summaryrefslogtreecommitdiffhomepage
path: root/packages/conversation-store/src
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-12 20:13:55 +0900
committerAdam Malczewski <[email protected]>2026-06-12 20:13:55 +0900
commit020e051040001320955a70d6dcaab2d833013196 (patch)
tree1a0921487ae3c89befdbccc1754cd399c07ce1b9 /packages/conversation-store/src
parent35197ed933044d322d0a653c4e88a5f3e475fe76 (diff)
downloaddispatch-020e051040001320955a70d6dcaab2d833013196.tar.gz
dispatch-020e051040001320955a70d6dcaab2d833013196.zip
feat(reasoning-effort): persisted per-conversation + per-turn override, threaded to providers
- conversation-store: get/setReasoningEffort (own key space, mirrors cwd) - session-orchestrator: resolveReasoningEffort (override -> stored -> 'high'), StartTurnInput.reasoningEffort, warm() parity (cache-safe) - transport-http: /chat validation (400 on bad level) + GET/PUT /conversations/:id/reasoning-effort - transport-ws: chat.send threading + validation - cli: --effort <low|medium|high|xhigh|max> 993 vitest + 189 bun tests green; typecheck + biome clean.
Diffstat (limited to 'packages/conversation-store/src')
-rw-r--r--packages/conversation-store/src/keys.ts4
-rw-r--r--packages/conversation-store/src/store.test.ts75
-rw-r--r--packages/conversation-store/src/store.ts17
3 files changed, 96 insertions, 0 deletions
diff --git a/packages/conversation-store/src/keys.ts b/packages/conversation-store/src/keys.ts
index e7204db..5eaed70 100644
--- a/packages/conversation-store/src/keys.ts
+++ b/packages/conversation-store/src/keys.ts
@@ -49,3 +49,7 @@ export function parseMetricsOrdinal(key: string): number {
export function cwdKey(conversationId: string): string {
return `conv:${conversationId}:cwd`;
}
+
+export function reasoningEffortKey(conversationId: string): string {
+ return `conv:${conversationId}:reasoning-effort`;
+}
diff --git a/packages/conversation-store/src/store.test.ts b/packages/conversation-store/src/store.test.ts
index 5b07eca..65c6aed 100644
--- a/packages/conversation-store/src/store.test.ts
+++ b/packages/conversation-store/src/store.test.ts
@@ -884,3 +884,78 @@ describe("ConversationStore cwd", () => {
expect(await store.getCwd("convB")).toBe("/path/b");
});
});
+
+describe("ConversationStore reasoning effort", () => {
+ let storage: StorageNamespace;
+
+ beforeEach(() => {
+ storage = createMemoryStorage();
+ });
+
+ it("setReasoningEffort then getReasoningEffort returns the level", async () => {
+ const store = createConversationStore(storage);
+ await store.setReasoningEffort("conv1", "high");
+ const result = await store.getReasoningEffort("conv1");
+ expect(result).toBe("high");
+ });
+
+ it("getReasoningEffort returns null when never set", async () => {
+ const store = createConversationStore(storage);
+ const result = await store.getReasoningEffort("conv_unknown");
+ expect(result).toBeNull();
+ });
+
+ it("reasoning effort of one conversation does not leak into another", async () => {
+ const store = createConversationStore(storage);
+ await store.setReasoningEffort("convA", "low");
+ await store.setReasoningEffort("convB", "max");
+ expect(await store.getReasoningEffort("convA")).toBe("low");
+ expect(await store.getReasoningEffort("convB")).toBe("max");
+ });
+
+ it("setReasoningEffort is an upsert (second set overwrites)", async () => {
+ const store = createConversationStore(storage);
+ await store.setReasoningEffort("conv1", "medium");
+ await store.setReasoningEffort("conv1", "xhigh");
+ const result = await store.getReasoningEffort("conv1");
+ expect(result).toBe("xhigh");
+ });
+
+ it("reasoning effort persists across a fresh store instance on the same storage", async () => {
+ const store1 = createConversationStore(storage);
+ await store1.setReasoningEffort("conv1", "max");
+
+ const store2 = createConversationStore(storage);
+ const result = await store2.getReasoningEffort("conv1");
+ expect(result).toBe("max");
+ });
+
+ it("reasoning-effort keys do not collide with chunk/cwd/metrics key spaces", async () => {
+ const store = createConversationStore(storage);
+ const msg: ChatMessage = { role: "user", chunks: [{ type: "text", text: "hello" }] };
+ await store.append("conv1", [msg]);
+ await store.setCwd("conv1", "/some/path");
+ await store.setReasoningEffort("conv1", "low");
+
+ const metrics: TurnMetrics = {
+ turnId: "turn_iso",
+ usage: { inputTokens: 100, outputTokens: 50 },
+ steps: [],
+ };
+ await store.appendMetrics("conv1", metrics);
+
+ const messages = await store.load("conv1");
+ expect(messages).toEqual([msg]);
+
+ const chunks = await store.loadSince("conv1");
+ expect(chunks).toHaveLength(1);
+ expect(chunks[0]?.chunk).toEqual({ type: "text", text: "hello" });
+
+ expect(await store.getCwd("conv1")).toBe("/some/path");
+ expect(await store.getReasoningEffort("conv1")).toBe("low");
+
+ const metricsResult = await store.loadMetrics("conv1");
+ expect(metricsResult).toHaveLength(1);
+ expect(metricsResult[0]).toEqual(metrics);
+ });
+});
diff --git a/packages/conversation-store/src/store.ts b/packages/conversation-store/src/store.ts
index 0a42917..fdbb2fb 100644
--- a/packages/conversation-store/src/store.ts
+++ b/packages/conversation-store/src/store.ts
@@ -2,6 +2,7 @@ import type {
ChatMessage,
Chunk,
Logger,
+ ReasoningEffort,
Role,
StorageNamespace,
StoredChunk,
@@ -16,6 +17,7 @@ import {
metricsPrefix,
metricsSeqKey,
parseSeq,
+ reasoningEffortKey,
seqKey,
} from "./keys.js";
import { reconcileWithReport } from "./reconcile.js";
@@ -57,6 +59,10 @@ export interface ConversationStore {
readonly getCwd: (conversationId: string) => Promise<string | null>;
/** Persist (upsert) the working directory for a conversation. */
readonly setCwd: (conversationId: string, cwd: string) => Promise<void>;
+ /** The persisted reasoning-effort level for a conversation, or null if never set. */
+ readonly getReasoningEffort: (conversationId: string) => Promise<ReasoningEffort | null>;
+ /** Persist (upsert) the reasoning-effort level for a conversation. */
+ readonly setReasoningEffort: (conversationId: string, effort: ReasoningEffort) => Promise<void>;
}
export const conversationStoreHandle = defineService<ConversationStore>("conversation-store/store");
@@ -213,5 +219,16 @@ export function createConversationStore(
logger.debug("cwd set", { conversationId });
}
},
+
+ async getReasoningEffort(conversationId) {
+ return (await storage.get(reasoningEffortKey(conversationId))) as ReasoningEffort | null;
+ },
+
+ async setReasoningEffort(conversationId, effort) {
+ await storage.set(reasoningEffortKey(conversationId), effort);
+ if (logger !== undefined) {
+ logger.debug("reasoning-effort set", { conversationId });
+ }
+ },
};
}