Skip to content

Commit e19e0c8

Browse files
authored
cache llm calls to disk (#126)
1 parent 9b456a9 commit e19e0c8

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

typescript/packages/planning-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"private": true,
88
"type": "module",
99
"scripts": {
10-
"start": "deno run --allow-env --allow-read --allow-net src/index.ts",
10+
"start": "deno run --allow-env --allow-read --allow-write --allow-net src/index.ts",
1111
"test": "deno test --no-check",
1212
"build": "wireit",
1313
"clean": "wireit"

typescript/packages/planning-server/src/index.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import {
66
} from "./conversation.ts";
77
import { CoreMessage, CoreTool } from "npm:ai";
88
import { CoreAssistantMessage } from "npm:ai";
9+
import { ensureDir } from "https://deno.land/std/fs/mod.ts";
10+
import { crypto } from "https://deno.land/std/crypto/mod.ts";
911

1012
const threadManager = new InMemoryConversationThreadManager();
1113

14+
const CACHE_DIR = "./cache";
15+
1216
type CreateConversationThreadRequest = {
1317
action: "create";
1418
message: string;
@@ -58,7 +62,32 @@ const handler = async (request: Request): Promise<Response> => {
5862
}
5963
};
6064

61-
const cache: Record<string, any> = {};
65+
async function hashKey(key: string): Promise<string> {
66+
const encoder = new TextEncoder();
67+
const data = encoder.encode(key);
68+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
69+
const hashArray = Array.from(new Uint8Array(hashBuffer));
70+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
71+
}
72+
73+
async function loadCacheItem(key: string): Promise<any | null> {
74+
const hash = await hashKey(key);
75+
const filePath = `${CACHE_DIR}/${hash}.json`;
76+
try {
77+
await ensureDir(CACHE_DIR);
78+
const cacheData = await Deno.readTextFile(filePath);
79+
return JSON.parse(cacheData);
80+
} catch {
81+
return null;
82+
}
83+
}
84+
85+
async function saveCacheItem(key: string, data: any): Promise<void> {
86+
const hash = await hashKey(key);
87+
const filePath = `${CACHE_DIR}/${hash}.json`;
88+
await ensureDir(CACHE_DIR);
89+
await Deno.writeTextFile(filePath, JSON.stringify(data, null, 2));
90+
}
6291

6392
async function handleCreateConversationThread(
6493
system: string,
@@ -67,12 +96,13 @@ async function handleCreateConversationThread(
6796
): Promise<Response> {
6897
const cacheKey = `${system}:${message}`;
6998

70-
if (cache[cacheKey]) {
99+
const cachedResult = await loadCacheItem(cacheKey);
100+
if (cachedResult) {
71101
console.log(
72102
"Cache hit!",
73103
(cacheKey.slice(0, 20) + "..." + cacheKey.slice(-20)).replaceAll("\n", "")
74104
);
75-
return new Response(JSON.stringify(cache[cacheKey]), {
105+
return new Response(JSON.stringify(cachedResult), {
76106
headers: { "Content-Type": "application/json" },
77107
});
78108
}
@@ -90,7 +120,7 @@ async function handleCreateConversationThread(
90120
threadManager.update(thread.id, [result.assistantResponse]);
91121
}
92122

93-
// cache[cacheKey] = result;
123+
await saveCacheItem(cacheKey, result);
94124

95125
return new Response(JSON.stringify(result), {
96126
headers: { "Content-Type": "application/json" },

0 commit comments

Comments
 (0)