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.
This commit is contained in:
google-labs-jules[bot] 2025-08-02 20:34:14 +00:00
parent 16d51a1d29
commit f6132057a2
9 changed files with 333 additions and 15 deletions

View file

@ -5,20 +5,20 @@
"name": "bsky-echo", "name": "bsky-echo",
"dependencies": { "dependencies": {
"@atproto/syntax": "^0.4.0", "@atproto/syntax": "^0.4.0",
"@google/genai": "^1.10.0", "@google/genai": "^1.11.0",
"@skyware/bot": "^0.3.12", "@skyware/bot": "^0.3.12",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"consola": "^3.4.2", "consola": "^3.4.2",
"drizzle-orm": "^0.44.3", "drizzle-orm": "^0.44.4",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"zod": "^4.0.5", "zod": "^4.0.14",
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.2.19", "@types/bun": "^1.2.19",
"drizzle-kit": "^0.31.4", "drizzle-kit": "^0.31.4",
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "^5", "typescript": "^5.8.3",
}, },
}, },
}, },

View file

@ -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;

View file

@ -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": {}
}
}

View file

@ -22,6 +22,13 @@
"when": 1753682242260, "when": 1753682242260,
"tag": "0002_green_millenium_guard", "tag": "0002_green_millenium_guard",
"breakpoints": true "breakpoints": true
},
{
"idx": 3,
"version": "6",
"when": 1754166687687,
"tag": "0003_flowery_korvac",
"breakpoints": true
} }
] ]
} }

BIN
sqlite.db Normal file

Binary file not shown.

View file

@ -1,6 +1,7 @@
import { drizzle } from "drizzle-orm/bun-sqlite"; import { drizzle } from "drizzle-orm/bun-sqlite";
import { Database } from "bun:sqlite"; import { Database } from "bun:sqlite";
import * as schema from "./schema";
import { env } from "../env"; import { env } from "../env";
const sqlite = new Database(env.DB_PATH); const sqlite = new Database(env.DB_PATH);
export default drizzle(sqlite); export default drizzle(sqlite, { schema });

View file

@ -5,6 +5,9 @@ export const interactions = sqliteTable("interactions", {
id: integer().primaryKey({ autoIncrement: true }), id: integer().primaryKey({ autoIncrement: true }),
uri: text().unique(), uri: text().unique(),
did: text(), did: text(),
post: text(),
response: text(),
muted: integer({ mode: "boolean" }),
created_at: integer({ mode: "timestamp" }).default(sql`CURRENT_TIMESTAMP`), created_at: integer({ mode: "timestamp" }).default(sql`CURRENT_TIMESTAMP`),
}); });

View file

@ -1,4 +1,8 @@
import { isAuthorizedUser, logInteraction } from "../utils/interactions"; import {
isAuthorizedUser,
logInteraction,
getRecentInteractions,
} from "../utils/interactions";
import * as threadUtils from "../utils/thread"; import * as threadUtils from "../utils/thread";
import modelPrompt from "../model/prompt.txt"; import modelPrompt from "../model/prompt.txt";
import { GoogleGenAI } from "@google/genai"; import { GoogleGenAI } from "@google/genai";
@ -131,10 +135,13 @@ export async function handler(post: Post): Promise<void> {
return; return;
} }
await logInteraction(post); const isMuted = await threadUtils.isThreadMuted(post);
if (isMuted) {
if (await threadUtils.isThreadMuted(post)) {
logger.warn("Thread is muted."); logger.warn("Thread is muted.");
await logInteraction(post, {
responseText: null,
wasMuted: true,
});
return; return;
} }
@ -151,11 +158,17 @@ export async function handler(post: Post): Promise<void> {
await MemoryHandler.getBlocks(post.author.did), await MemoryHandler.getBlocks(post.author.did),
); );
const recentInteractions = await getRecentInteractions(
post.author.did,
thread,
);
const memory = yaml.dump({ const memory = yaml.dump({
users_with_memory_blocks: { users_with_memory_blocks: {
[env.HANDLE]: botMemory.parseBlocks(), [env.HANDLE]: botMemory.parseBlocks(),
[post.author.handle]: userMemory.parseBlocks(), [post.author.handle]: userMemory.parseBlocks(),
}, },
recent_interactions: recentInteractions,
}); });
logger.log("Parsed memory blocks: ", memory); logger.log("Parsed memory blocks: ", memory);
@ -167,6 +180,11 @@ export async function handler(post: Post): Promise<void> {
if (responseText) { if (responseText) {
await sendResponse(post, responseText); await sendResponse(post, responseText);
} }
await logInteraction(post, {
responseText: responseText ?? null,
wasMuted: false,
});
} catch (error) { } catch (error) {
logger.error("Error in post handler:", error); logger.error("Error in post handler:", error);

View file

@ -1,5 +1,6 @@
import { interactions } from "../db/schema"; import { interactions } from "../db/schema";
import type { Post } from "@skyware/bot"; import type { Post } from "@skyware/bot";
import { desc, notInArray } from "drizzle-orm";
import { env } from "../env"; import { env } from "../env";
import db from "../db"; import db from "../db";
@ -9,11 +10,41 @@ export function isAuthorizedUser(did: string) {
: env.AUTHORIZED_USERS.includes(did as any); : env.AUTHORIZED_USERS.includes(did as any);
} }
export async function logInteraction(post: Post): Promise<void> { export async function logInteraction(
await db.insert(interactions).values([{ post: Post,
uri: post.uri, options: {
did: post.author.did, responseText: string | null;
}]); wasMuted: boolean;
},
): Promise<void> {
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,
}));
} }