diff options
| author | Adam Malczewski <[email protected]> | 2026-06-12 20:13:55 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-12 20:13:55 +0900 |
| commit | 020e051040001320955a70d6dcaab2d833013196 (patch) | |
| tree | 1a0921487ae3c89befdbccc1754cd399c07ce1b9 /packages/conversation-store/src | |
| parent | 35197ed933044d322d0a653c4e88a5f3e475fe76 (diff) | |
| download | dispatch-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.ts | 4 | ||||
| -rw-r--r-- | packages/conversation-store/src/store.test.ts | 75 | ||||
| -rw-r--r-- | packages/conversation-store/src/store.ts | 17 |
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 }); + } + }, }; } |
