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:
parent
16d51a1d29
commit
f6132057a2
9 changed files with 333 additions and 15 deletions
8
bun.lock
8
bun.lock
|
|
@ -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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
3
drizzle/0003_flowery_korvac.sql
Normal file
3
drizzle/0003_flowery_korvac.sql
Normal 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;
|
||||||
255
drizzle/meta/0003_snapshot.json
Normal file
255
drizzle/meta/0003_snapshot.json
Normal 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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
BIN
sqlite.db
Normal file
Binary file not shown.
|
|
@ -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 });
|
||||||
|
|
|
||||||
|
|
@ -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`),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue