Skip to content

Commit eea69a0

Browse files
committed
refactor: inject memory via system prompt hook, use markdown format, and remove double casts
1 parent e77bd0f commit eea69a0

11 files changed

Lines changed: 185 additions & 230 deletions

File tree

src/handlers/chat.ts

Lines changed: 12 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { Hooks } from "@opencode-ai/plugin";
2-
import type { Part } from "@opencode-ai/sdk";
32
import type { GraphitiClient } from "../services/client.ts";
43
import { calculateInjectionBudget } from "../services/context-limit.ts";
54
import {
@@ -26,28 +25,17 @@ export interface ChatHandlerDeps {
2625
export function createChatHandler(deps: ChatHandlerDeps) {
2726
const { sessionManager, driftThreshold, factStaleDays, client } = deps;
2827

29-
const removeSyntheticMemoryParts = (parts: Part[]): Part[] =>
30-
parts.filter((part) => {
31-
if (part.type !== "text") return true;
32-
if (part.id?.startsWith("graphiti-memory-")) return false;
33-
if (part.id?.startsWith("graphiti-refresh-")) return false;
34-
35-
return true;
36-
});
37-
38-
const injectMemoryContext = async (
28+
const searchAndCacheMemoryContext = async (
3929
state: {
4030
groupId: string;
4131
userGroupId: string;
4232
contextLimit: number;
4333
lastInjectionFactUuids: Set<string>;
34+
cachedMemoryContext?: string;
4435
},
4536
messageText: string,
46-
output: ChatMessageOutput,
47-
prefix: string,
4837
useUserScope: boolean,
4938
characterBudget: number,
50-
shouldReinject: boolean,
5139
seedFactUuids?: Set<string> | null,
5240
) => {
5341
const userGroupId = state.userGroupId;
@@ -125,10 +113,10 @@ export function createChatHandler(deps: ChatHandlerDeps) {
125113
if (snapshot?.content) {
126114
const snapshotBudget = Math.min(characterBudget, 1200);
127115
snapshotPrimer = [
128-
'<memory source="snapshot">',
129-
"<instruction>Most recent session snapshot; use to restore active strategy and open questions.</instruction>",
130-
`<snapshot>${snapshot.content.slice(0, snapshotBudget)}</snapshot>`,
131-
"</memory>",
116+
"## Session Snapshot",
117+
"> Most recent session snapshot; use to restore active strategy and open questions.",
118+
"",
119+
snapshot.content.slice(0, snapshotBudget),
132120
].join("\n");
133121
}
134122
} catch (err) {
@@ -150,41 +138,18 @@ export function createChatHandler(deps: ChatHandlerDeps) {
150138
.slice(0, characterBudget);
151139
if (!memoryContext) return;
152140

153-
if (shouldReinject) {
154-
output.parts = removeSyntheticMemoryParts(output.parts);
155-
}
156-
157-
const allFactUuids = seedFactUuids ??
141+
const factUuids = seedFactUuids ??
158142
new Set<string>([
159143
...projectContext.facts.map((fact) => fact.uuid),
160144
...userContext.facts.map((fact) => fact.uuid),
161145
]);
162-
163-
if ("system" in output.message) {
164-
try {
165-
output.message.system = memoryContext;
166-
return;
167-
} catch (_err) {
168-
// Fall through to synthetic injection.
169-
}
170-
}
171-
172-
{
173-
output.parts.unshift({
174-
type: "text",
175-
text: memoryContext,
176-
id: `${prefix}${Date.now()}`,
177-
sessionID: output.message.sessionID,
178-
messageID: output.message.id,
179-
synthetic: true,
180-
} as Part);
181-
}
146+
state.cachedMemoryContext = memoryContext;
182147
logger.info(
183-
`Injected ${projectFacts.length + userFacts.length} facts and ${
148+
`Cached ${projectFacts.length + userFacts.length} facts and ${
184149
projectNodes.length + userNodes.length
185-
} nodes`,
150+
} nodes for system prompt injection`,
186151
);
187-
state.lastInjectionFactUuids = allFactUuids;
152+
state.lastInjectionFactUuids = factUuids;
188153
};
189154

190155
const computeJaccardSimilarity = (
@@ -254,20 +219,14 @@ export function createChatHandler(deps: ChatHandlerDeps) {
254219
}
255220
}
256221

257-
if (!shouldInjectOnFirst && !shouldReinject) return;
258-
259222
try {
260-
const prefix = shouldReinject ? "graphiti-refresh-" : "graphiti-memory-";
261223
const useUserScope = shouldInjectOnFirst;
262224
const characterBudget = calculateInjectionBudget(state.contextLimit);
263-
await injectMemoryContext(
225+
await searchAndCacheMemoryContext(
264226
state,
265227
messageText,
266-
output,
267-
prefix,
268228
useUserScope,
269229
characterBudget,
270-
shouldReinject,
271230
currentFactUuids,
272231
);
273232
state.injectedMemories = true;

src/handlers/event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Hooks } from "@opencode-ai/plugin";
2+
import type { OpencodeClient } from "@opencode-ai/sdk";
23
import type { GraphitiClient } from "../services/client.ts";
34
import { handleCompaction } from "../services/compaction.ts";
4-
import type { ProviderListClient } from "../services/context-limit.ts";
55
import { resolveContextLimit } from "../services/context-limit.ts";
66
import { logger } from "../services/logger.ts";
77
import type { SessionManager } from "../session.ts";
@@ -15,7 +15,7 @@ export interface EventHandlerDeps {
1515
sessionManager: SessionManager;
1616
client: GraphitiClient;
1717
defaultGroupId: string;
18-
sdkClient: ProviderListClient;
18+
sdkClient: OpencodeClient;
1919
directory: string;
2020
groupIdPrefix: string;
2121
}

src/handlers/system.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Hooks } from "@opencode-ai/plugin";
2+
import { logger } from "../services/logger.ts";
3+
import type { SessionManager } from "../session.ts";
4+
5+
type SystemTransformHook = NonNullable<
6+
Hooks["experimental.chat.system.transform"]
7+
>;
8+
type SystemTransformInput = Parameters<SystemTransformHook>[0];
9+
type SystemTransformOutput = Parameters<SystemTransformHook>[1];
10+
11+
export interface SystemHandlerDeps {
12+
sessionManager: SessionManager;
13+
}
14+
15+
export function createSystemHandler(deps: SystemHandlerDeps) {
16+
const { sessionManager } = deps;
17+
18+
// Assumes chat.message hook completes before system.transform fires for the same turn.
19+
// deno-lint-ignore require-await
20+
return async (
21+
{ sessionID }: SystemTransformInput,
22+
output: SystemTransformOutput,
23+
) => {
24+
if (!sessionID) return;
25+
26+
const state = sessionManager.getState(sessionID);
27+
if (!state?.isMain) return;
28+
if (!state.cachedMemoryContext) return;
29+
30+
output.system.push(state.cachedMemoryContext);
31+
state.cachedMemoryContext = undefined;
32+
logger.info("Injected memory context into system prompt");
33+
};
34+
}

src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { loadConfig } from "./config.ts";
33
import { createChatHandler } from "./handlers/chat.ts";
44
import { createCompactingHandler } from "./handlers/compacting.ts";
55
import { createEventHandler } from "./handlers/event.ts";
6+
import { createSystemHandler } from "./handlers/system.ts";
67
import { GraphitiClient } from "./services/client.ts";
7-
import type { ProviderListClient } from "./services/context-limit.ts";
88
import { logger } from "./services/logger.ts";
9-
import { type SdkSessionClient, SessionManager } from "./session.ts";
9+
import { SessionManager } from "./session.ts";
1010
import { makeGroupId, makeUserGroupId } from "./utils.ts";
1111

1212
/**
@@ -38,7 +38,7 @@ export const graphiti: Plugin = async (input: PluginInput) => {
3838
const sessionManager = new SessionManager(
3939
defaultGroupId,
4040
defaultUserGroupId,
41-
sdkClient as unknown as SdkSessionClient,
41+
sdkClient,
4242
client,
4343
);
4444

@@ -47,7 +47,7 @@ export const graphiti: Plugin = async (input: PluginInput) => {
4747
sessionManager,
4848
client,
4949
defaultGroupId,
50-
sdkClient: sdkClient as unknown as ProviderListClient,
50+
sdkClient,
5151
directory: input.directory,
5252
groupIdPrefix: config.groupIdPrefix,
5353
}),
@@ -63,5 +63,8 @@ export const graphiti: Plugin = async (input: PluginInput) => {
6363
defaultGroupId,
6464
factStaleDays: config.factStaleDays,
6565
}),
66+
"experimental.chat.system.transform": createSystemHandler({
67+
sessionManager,
68+
}),
6669
};
6770
};

0 commit comments

Comments
 (0)