From f6132057a215e85de017ba897249233ed90a7a66 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:34:14 +0000 Subject: [PATCH] I've made some improvements to how I learn from our interactions. I've updated the way I log our conversations so I can better remember your posts, my responses, and whether a particular conversation is muted. To give you more relevant and personalized responses, I'll also review our last five interactions to get better context for your requests. I'll be sure to exclude messages from our current conversation to avoid repeating myself. --- bun.lock | 8 +- drizzle/0003_flowery_korvac.sql | 3 + drizzle/meta/0003_snapshot.json | 255 ++++++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + sqlite.db | Bin 0 -> 45056 bytes src/db/index.ts | 3 +- src/db/schema.ts | 3 + src/handlers/posts.ts | 26 +++- src/utils/interactions.ts | 43 +++++- 9 files changed, 333 insertions(+), 15 deletions(-) create mode 100644 drizzle/0003_flowery_korvac.sql create mode 100644 drizzle/meta/0003_snapshot.json create mode 100644 sqlite.db diff --git a/bun.lock b/bun.lock index 6580c1d..ae7e23e 100644 --- a/bun.lock +++ b/bun.lock @@ -5,20 +5,20 @@ "name": "bsky-echo", "dependencies": { "@atproto/syntax": "^0.4.0", - "@google/genai": "^1.10.0", + "@google/genai": "^1.11.0", "@skyware/bot": "^0.3.12", "@types/js-yaml": "^4.0.9", "consola": "^3.4.2", - "drizzle-orm": "^0.44.3", + "drizzle-orm": "^0.44.4", "js-yaml": "^4.1.0", - "zod": "^4.0.5", + "zod": "^4.0.14", }, "devDependencies": { "@types/bun": "^1.2.19", "drizzle-kit": "^0.31.4", }, "peerDependencies": { - "typescript": "^5", + "typescript": "^5.8.3", }, }, }, diff --git a/drizzle/0003_flowery_korvac.sql b/drizzle/0003_flowery_korvac.sql new file mode 100644 index 0000000..d9fe9ba --- /dev/null +++ b/drizzle/0003_flowery_korvac.sql @@ -0,0 +1,3 @@ +ALTER TABLE `interactions` ADD `post` text;--> statement-breakpoint +ALTER TABLE `interactions` ADD `response` text;--> statement-breakpoint +ALTER TABLE `interactions` ADD `muted` integer; \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..21cce7e --- /dev/null +++ b/drizzle/meta/0003_snapshot.json @@ -0,0 +1,255 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "30d38111-8e11-4d7d-99e8-cbafd962ca62", + "prevId": "11e8b31f-8e38-4013-8d50-bec6177b015a", + "tables": { + "interactions": { + "name": "interactions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "did": { + "name": "did", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "post": { + "name": "post", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "response": { + "name": "response", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "muted": { + "name": "muted", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "interactions_uri_unique": { + "name": "interactions_uri_unique", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "memory_block_entries": { + "name": "memory_block_entries", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "block_id": { + "name": "block_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "added_by": { + "name": "added_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "memory_block_entries_block_id_memory_blocks_id_fk": { + "name": "memory_block_entries_block_id_memory_blocks_id_fk", + "tableFrom": "memory_block_entries", + "tableTo": "memory_blocks", + "columnsFrom": [ + "block_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "memory_blocks": { + "name": "memory_blocks", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "did": { + "name": "did", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'memory'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'User memory'" + }, + "mutable": { + "name": "mutable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "muted_threads": { + "name": "muted_threads", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rkey": { + "name": "rkey", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "muted_at": { + "name": "muted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "muted_threads_uri_unique": { + "name": "muted_threads_uri_unique", + "columns": [ + "uri" + ], + "isUnique": true + }, + "muted_threads_rkey_unique": { + "name": "muted_threads_rkey_unique", + "columns": [ + "rkey" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index e6a1257..bb4556e 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1753682242260, "tag": "0002_green_millenium_guard", "breakpoints": true + }, + { + "idx": 3, + "version": "6", + "when": 1754166687687, + "tag": "0003_flowery_korvac", + "breakpoints": true } ] } \ No newline at end of file diff --git a/sqlite.db b/sqlite.db new file mode 100644 index 0000000000000000000000000000000000000000..de6eb41d4964cee790fd1c5167a093fc11dfbec6 GIT binary patch literal 45056 zcmeI*PjB1i9S3kyk}X?_qwP?5Sq^)eAaK+=86}FMlwoMpM9Bm@c4AA|j2)zhBT1H}JA_D*l;N)n{54G%`GUxsCV9H>9e*>@ zt>3+|cJXs5mwhhf)A>)c&)1%>4KI9nv3~I%tKY2r?K@&o1OW&@00Izzz<)(xw;H5Xe|x(@y2+w0xshA#I#!p6+(QDTFr-z{Z{?qqueIx4*ZZ;jl)SdVBvuGp3O4)L*Ce|rNZfJx$OGJ zhWyltN8D$9KRD?au5Z2@&aGUSAM;$oPpRo z zyc5LK?L<9(Kv-cskEbiym)MgP&+0vQM2@%iy$`|QT{e*|e2~qq|8hef{mk(!c0AYX z4&Ov)=s8b@Y%YIgGMe^I^KrXDnmhLzzu}b0K_x=F!fw8l4>>-e6fWJ7ba8Z_PG)hk ziMjU1fXlh%_%!;_N_PD%-sfuz`YeXFsMD+8qtl7lZ|QXmS2$YAuK(h?Jh~EZKY4mh zj60dVyl~^m8i~O1rjvy;uSc2X^*7&?58~+-K5_Z2UngXI|9B>QX?1CC%Dq&0L!84l zd1N8~jr66sqMY81+e~gXx%>mZq7+62IX)GR#^=sX#|oVH-*;K3?;Hj+zBn$*OU(gY zT~y_&xLES!SY*Gk*Q{?RFP8ii!&8rjz4%%-J2#K>U!1LoH|(>(F-7&lgVgf+Z#7wp zC(dN2&|&1q>zze?=oA;_rulKOB94VM{>ohXla&7~Z|5)b1tbVS00Izz00bZa0SG_< z0uX?}4_sg?vm}kymJ~~4hE=t#TFqd3O}Dgah1s@R)rv)(8WvMc+b)%iic+aCrZU5< zGD}fZy~?z5(Jsr++z*caxRs6@(`}_{6l=O68ZDc8xmvcZibl0sxh6twqgXXcrlQw0 z-7HmTRaF(cL~BK}rrO1dAwS#wxluf%-u2y(+Owo!)Nz;m~rdX<}nY3ssys=L{&E@z$&E&t9^8d>JGyiwKfCK>u zKmY;|fB*y_009U<00Izz!1)%)q$N2c-qPYNl}<~m;`4w0rGF#{KmY;|fB*y_009U< z00Izz00jPr1;qV-D*qQL|22O@f&c^{009U<00Izz00bZa0SG|gya@axmDc3s50gxP znI-dMIlVDg5@y8r|MB_%dD*q-Cj=k>0SG_<0uX=z1Rwwb2teR$0{H&_+1wBX0SG_< z0uX=z1Rwwb2tWV=5I9c)`27Do?Je{b0uX=z1Rwwb2tWV=5P$##AaFJT-2b1=4N(w) z00bZa0SG_<0uX=z1Rwx`^CTd?|DRdo|Nr0b^A9c!`2rFIAOHafKmY;|fB*y_009V` ze}U0bW<`>(j2=sBX262b_h>t00SVZXAq%6BocKdR2Lt9=B$)nftQGJl2aJr+=x)LWu3@=f2J)!HP!$B4lk?~q*O6cu0e zxB7lKh&$tW#M#{^`#nF1%wZHR7*R8omE_l`q^2Sc+3GnV;r}QRYYC9PADj@A4yfU{ zPUNsfO}X-lwEFYWnxs;z?|4oa1vK&l-sR{((9h@gDBp}H3SV@+M?K4BVzYO6{w>F| z*zvI#{DO|h_y75yO8GDHQT|ta0SN*SfB*y_009U<00Izz00bcLe^XvCk2*rLri Nd?O^2BK`>C { return; } - await logInteraction(post); - - if (await threadUtils.isThreadMuted(post)) { + const isMuted = await threadUtils.isThreadMuted(post); + if (isMuted) { logger.warn("Thread is muted."); + await logInteraction(post, { + responseText: null, + wasMuted: true, + }); return; } @@ -151,11 +158,17 @@ export async function handler(post: Post): Promise { await MemoryHandler.getBlocks(post.author.did), ); + const recentInteractions = await getRecentInteractions( + post.author.did, + thread, + ); + const memory = yaml.dump({ users_with_memory_blocks: { [env.HANDLE]: botMemory.parseBlocks(), [post.author.handle]: userMemory.parseBlocks(), }, + recent_interactions: recentInteractions, }); logger.log("Parsed memory blocks: ", memory); @@ -167,6 +180,11 @@ export async function handler(post: Post): Promise { if (responseText) { await sendResponse(post, responseText); } + + await logInteraction(post, { + responseText: responseText ?? null, + wasMuted: false, + }); } catch (error) { logger.error("Error in post handler:", error); diff --git a/src/utils/interactions.ts b/src/utils/interactions.ts index 16d61bb..31a78b3 100644 --- a/src/utils/interactions.ts +++ b/src/utils/interactions.ts @@ -1,5 +1,6 @@ import { interactions } from "../db/schema"; import type { Post } from "@skyware/bot"; +import { desc, notInArray } from "drizzle-orm"; import { env } from "../env"; import db from "../db"; @@ -9,11 +10,41 @@ export function isAuthorizedUser(did: string) { : env.AUTHORIZED_USERS.includes(did as any); } -export async function logInteraction(post: Post): Promise { - await db.insert(interactions).values([{ - uri: post.uri, - did: post.author.did, - }]); +export async function logInteraction( + post: Post, + options: { + responseText: string | null; + wasMuted: boolean; + }, +): Promise { + await db.insert(interactions).values([ + { + uri: post.uri, + did: post.author.did, + post: post.text, + response: options.responseText, + muted: options.wasMuted, + }, + ]); - console.log(`Logged interaction, initiated by @${post.author.handle}`); + console.log(`Logged interaction, initiated by @${post.author.handle}`); +} + +export async function getRecentInteractions(did: string, thread: Post[]) { + const threadUris = thread.map((p) => p.uri); + + const recentInteractions = await db.query.interactions.findMany({ + where: (interactions, { eq, and, notInArray }) => + and( + eq(interactions.did, did), + notInArray(interactions.uri, threadUris), + ), + orderBy: (interactions, { desc }) => [desc(interactions.created_at)], + limit: 5, + }); + + return recentInteractions.map((i) => ({ + post: i.post, + response: i.response, + })); }