diff options
| author | Adam Malczewski <[email protected]> | 2026-05-19 20:44:09 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-19 20:44:09 +0900 |
| commit | b3d16aa62a1b8724fe57ab3846a83f03a4a3fbc0 (patch) | |
| tree | 480a1fa2c95bf87ab214f709b1b4414aa64e8929 /packages/api | |
| parent | f78a91c20f658dd404277919a0b872b352c99bb6 (diff) | |
| download | dispatch-b3d16aa62a1b8724fe57ab3846a83f03a4a3fbc0.tar.gz dispatch-b3d16aa62a1b8724fe57ab3846a83f03a4a3fbc0.zip | |
fix: DeepSeek reasoning_content dropped on multi-step tool calls
- Base URL corrected: zen/v1 -> zen/go/v1 (opencode-go provider)
- Model changed: deepseek-v4-flash-free -> deepseek-v4-flash
- Added wrapLanguageModel middleware to inject reasoning_content via
providerMetadata.openaiCompatible before each stream call
- Fixed test mocks: removed vi.importActual (unsupported in Bun), added
tool factory mocks, preserved real tool export in ai mock
- Added 11 tests for the normalizeMessages middleware
Diffstat (limited to 'packages/api')
| -rw-r--r-- | packages/api/src/agent-manager.ts | 4 | ||||
| -rw-r--r-- | packages/api/tests/agent-manager.test.ts | 64 | ||||
| -rw-r--r-- | packages/api/tests/routes.test.ts | 65 |
3 files changed, 87 insertions, 46 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts index f60b8d3..2991ee1 100644 --- a/packages/api/src/agent-manager.ts +++ b/packages/api/src/agent-manager.ts @@ -24,7 +24,7 @@ export class AgentManager { private getOrCreateAgent(): Agent { if (!this.agent) { const apiKey = process.env.OPENCODE_API_KEY ?? ""; - const model = process.env.DISPATCH_MODEL ?? "deepseek-v4-flash-free"; + const model = process.env.DISPATCH_MODEL ?? "deepseek-v4-flash"; const workingDirectory = process.env.DISPATCH_WORKING_DIR ?? process.cwd(); const tools = [ @@ -36,7 +36,7 @@ export class AgentManager { this.agent = new Agent({ model, apiKey, - baseURL: "https://opencode.ai/zen/v1", + baseURL: "https://opencode.ai/zen/go/v1", systemPrompt: SYSTEM_PROMPT, tools, workingDirectory, diff --git a/packages/api/tests/agent-manager.test.ts b/packages/api/tests/agent-manager.test.ts index 17b0bff..56cb818 100644 --- a/packages/api/tests/agent-manager.test.ts +++ b/packages/api/tests/agent-manager.test.ts @@ -1,28 +1,48 @@ -import type { AgentEvent } from "@dispatch/core"; +import type { AgentEvent, ToolDefinition } from "@dispatch/core"; import { describe, expect, it, vi } from "vitest"; // Mock @dispatch/core's Agent to avoid real LLM calls -vi.mock("@dispatch/core", async () => { - const actual = await vi.importActual<typeof import("@dispatch/core")>("@dispatch/core"); - return { - ...actual, - Agent: class MockAgent { - status = "idle"; - messages: unknown[] = []; - async *run(_message: string) { - yield { type: "status", status: "running" } as const; - await new Promise<void>((r) => setTimeout(r, 10)); - yield { type: "text-delta", delta: "Hello " } as const; - yield { type: "text-delta", delta: "world" } as const; - yield { - type: "done", - message: { role: "assistant", content: "Hello world" }, - } as const; - yield { type: "status", status: "idle" } as const; - } - }, - }; -}); +vi.mock("@dispatch/core", () => ({ + Agent: class MockAgent { + status = "idle"; + messages: unknown[] = []; + async *run(_message: string) { + yield { type: "status", status: "running" } as const; + await new Promise<void>((r) => setTimeout(r, 10)); + yield { type: "text-delta", delta: "Hello " } as const; + yield { type: "text-delta", delta: "world" } as const; + yield { + type: "done", + message: { role: "assistant", content: "Hello world" }, + } as const; + yield { type: "status", status: "idle" } as const; + } + }, + createReadFileTool(_wd: string): ToolDefinition { + return { + name: "read_file", + description: "read a file", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => "mock file content", + }; + }, + createWriteFileTool(_wd: string): ToolDefinition { + return { + name: "write_file", + description: "write a file", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => true, + }; + }, + createListFilesTool(_wd: string): ToolDefinition { + return { + name: "list_files", + description: "list files", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => ["file1.ts"], + }; + }, +})); // Import after mock is defined (Vitest hoists vi.mock automatically) const { AgentManager } = await import("../src/agent-manager.js"); diff --git a/packages/api/tests/routes.test.ts b/packages/api/tests/routes.test.ts index d5384b3..9f852ee 100644 --- a/packages/api/tests/routes.test.ts +++ b/packages/api/tests/routes.test.ts @@ -1,28 +1,49 @@ +import type { ToolDefinition } from "@dispatch/core"; import { describe, expect, it, vi } from "vitest"; // Mock @dispatch/core's Agent to avoid real LLM calls -vi.mock("@dispatch/core", async () => { - const actual = await vi.importActual<typeof import("@dispatch/core")>("@dispatch/core"); - return { - ...actual, - Agent: class MockAgent { - status = "idle"; - messages: unknown[] = []; - async *run(_message: string) { - yield { type: "status", status: "running" } as const; - // Simulate some processing time so status stays "running" - await new Promise<void>((r) => setTimeout(r, 100)); - yield { type: "text-delta", delta: "Hello " } as const; - yield { type: "text-delta", delta: "world" } as const; - yield { - type: "done", - message: { role: "assistant", content: "Hello world" }, - } as const; - yield { type: "status", status: "idle" } as const; - } - }, - }; -}); +vi.mock("@dispatch/core", () => ({ + Agent: class MockAgent { + status = "idle"; + messages: unknown[] = []; + async *run(_message: string) { + yield { type: "status", status: "running" } as const; + // Simulate some processing time so status stays "running" + await new Promise<void>((r) => setTimeout(r, 100)); + yield { type: "text-delta", delta: "Hello " } as const; + yield { type: "text-delta", delta: "world" } as const; + yield { + type: "done", + message: { role: "assistant", content: "Hello world" }, + } as const; + yield { type: "status", status: "idle" } as const; + } + }, + createReadFileTool(_wd: string): ToolDefinition { + return { + name: "read_file", + description: "read a file", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => "mock file content", + }; + }, + createWriteFileTool(_wd: string): ToolDefinition { + return { + name: "write_file", + description: "write a file", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => true, + }; + }, + createListFilesTool(_wd: string): ToolDefinition { + return { + name: "list_files", + description: "list files", + parameters: { _type: "z.ZodObject", shape: {} } as unknown as ToolDefinition["parameters"], + execute: async () => ["file1.ts"], + }; + }, +})); const { app } = await import("../src/app.js"); |
