@deepstrike/sdk 0.2.30 → 0.2.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/harness/harness.d.ts +3 -2
  2. package/dist/harness/harness.js +15 -35
  3. package/dist/harness/judge.d.ts +42 -0
  4. package/dist/harness/judge.js +58 -0
  5. package/dist/index.d.ts +8 -0
  6. package/dist/index.js +4 -0
  7. package/dist/kernel.d.ts +7 -1
  8. package/dist/providers/anthropic-compatible.d.ts +23 -0
  9. package/dist/providers/anthropic-compatible.js +29 -0
  10. package/dist/providers/catalog.js +5 -53
  11. package/dist/providers/deepseek.d.ts +28 -8
  12. package/dist/providers/deepseek.js +38 -157
  13. package/dist/providers/factories.js +10 -22
  14. package/dist/providers/glm.d.ts +5 -4
  15. package/dist/providers/glm.js +8 -23
  16. package/dist/providers/kimi.d.ts +5 -4
  17. package/dist/providers/kimi.js +8 -22
  18. package/dist/providers/minimax.d.ts +26 -12
  19. package/dist/providers/minimax.js +32 -158
  20. package/dist/providers/openai.d.ts +43 -0
  21. package/dist/providers/openai.js +128 -64
  22. package/dist/providers/qwen.d.ts +19 -19
  23. package/dist/providers/qwen.js +37 -176
  24. package/dist/providers/registry.d.ts +18 -0
  25. package/dist/providers/registry.js +35 -0
  26. package/dist/providers/vendor-profiles.d.ts +54 -0
  27. package/dist/providers/vendor-profiles.js +62 -0
  28. package/dist/runtime/event-stream.d.ts +44 -0
  29. package/dist/runtime/event-stream.js +39 -0
  30. package/dist/runtime/reactive-session.d.ts +125 -0
  31. package/dist/runtime/reactive-session.js +127 -0
  32. package/dist/runtime/run-group.d.ts +74 -0
  33. package/dist/runtime/run-group.js +72 -0
  34. package/dist/runtime/runner.d.ts +9 -0
  35. package/dist/runtime/runner.js +56 -7
  36. package/dist/runtime/session-log.d.ts +8 -0
  37. package/dist/runtime/turn-policy.d.ts +33 -0
  38. package/dist/runtime/turn-policy.js +58 -0
  39. package/dist/signals/gateway.d.ts +7 -2
  40. package/dist/signals/gateway.js +13 -3
  41. package/dist/signals/types.d.ts +10 -1
  42. package/package.json +2 -2
@@ -1,43 +1,31 @@
1
- import { DeepSeekProvider, DeepSeekAnthropicProvider } from "./deepseek.js";
2
- import { KimiProvider, KimiAnthropicProvider } from "./kimi.js";
3
- import { QwenProvider, QwenAnthropicProvider } from "./qwen.js";
4
- import { GLMProvider, GLMAnthropicProvider } from "./glm.js";
5
- import { MiniMaxOpenAIProvider, MiniMaxAnthropicProvider } from "./minimax.js";
6
- import { GeminiProvider } from "./gemini.js";
1
+ import { PROVIDER_REGISTRY } from "./registry.js";
7
2
  import { OllamaProvider } from "./ollama.js";
3
+ function build(providerId, protocol, o) {
4
+ return PROVIDER_REGISTRY[`${providerId}:${protocol}`](o.apiKey, o.model, o.retry, o.baseURL);
5
+ }
8
6
  /** DeepSeek. Defaults to the OpenAI-compatible wire (richer reasoning-replay handling). */
9
7
  export function deepseek(o) {
10
- return o.protocol === "anthropic"
11
- ? new DeepSeekAnthropicProvider(o.apiKey, o.model, o.retry, o.baseURL)
12
- : new DeepSeekProvider(o.apiKey, o.model, o.retry, o.baseURL);
8
+ return build("deepseek", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
13
9
  }
14
10
  /** Moonshot Kimi. Defaults to the OpenAI-compatible wire. */
15
11
  export function kimi(o) {
16
- return o.protocol === "anthropic"
17
- ? new KimiAnthropicProvider(o.apiKey, o.model, o.retry, o.baseURL)
18
- : new KimiProvider(o.apiKey, o.model, o.retry, o.baseURL);
12
+ return build("kimi", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
19
13
  }
20
14
  /** Alibaba Qwen / DashScope. Defaults to the OpenAI-compatible (DashScope) wire. */
21
15
  export function qwen(o) {
22
- return o.protocol === "anthropic"
23
- ? new QwenAnthropicProvider(o.apiKey, o.model, o.retry, o.baseURL)
24
- : new QwenProvider(o.apiKey, o.model, o.retry, o.baseURL);
16
+ return build("qwen", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
25
17
  }
26
18
  /** Zhipu GLM. Defaults to the OpenAI-compatible wire. */
27
19
  export function glm(o) {
28
- return o.protocol === "anthropic"
29
- ? new GLMAnthropicProvider(o.apiKey, o.model, o.retry, o.baseURL)
30
- : new GLMProvider(o.apiKey, o.model, o.retry, o.baseURL);
20
+ return build("glm", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
31
21
  }
32
22
  /** MiniMax. Defaults to the Anthropic-compatible wire (the primary M2.x path). */
33
23
  export function minimax(o) {
34
- return o.protocol === "openai"
35
- ? new MiniMaxOpenAIProvider(o.apiKey, o.model, o.retry, o.baseURL)
36
- : new MiniMaxAnthropicProvider(o.apiKey, o.model, o.retry, o.baseURL);
24
+ return build("minimax", o.protocol === "openai" ? "openai-chat" : "anthropic-messages", o);
37
25
  }
38
26
  /** Google Gemini (single wire). */
39
27
  export function gemini(o) {
40
- return new GeminiProvider(o.apiKey, o.model, o.retry, o.baseURL);
28
+ return PROVIDER_REGISTRY["gemini:gemini"](o.apiKey, o.model, o.retry, o.baseURL);
41
29
  }
42
30
  /** Local Ollama (single wire, no API key). */
43
31
  export function ollama(o = {}) {
@@ -1,16 +1,17 @@
1
1
  import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
2
  import { OpenAIChatProvider } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * GLM over its Anthropic-compatible endpoint.
6
+ * @deprecated Prefer `glm({ protocol: "anthropic" })`. Behavior is now fully
7
+ * data-driven via `anthropicVendorProfiles.glm`; this thin shim is kept for
8
+ * backward compatibility and `instanceof` checks.
6
9
  */
7
- export declare class GLMAnthropicProvider extends AnthropicProvider {
10
+ export declare class GLMAnthropicProvider extends AnthropicCompatibleProvider {
8
11
  constructor(apiKey: string, model?: string, retry?: {
9
12
  maxRetries: number;
10
13
  baseDelay: number;
11
14
  }, baseURL?: string);
12
- protected providerName(): string;
13
- runtimePolicy(): RuntimePolicy;
14
15
  }
15
16
  export declare class GLMProvider extends OpenAIChatProvider {
16
17
  constructor(apiKey: string, model?: string, retry?: {
@@ -1,31 +1,16 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
1
  import { OpenAIChatProvider } from "./openai.js";
2
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
3
3
  import { endpointProfiles } from "./profiles.js";
4
- const GLM_POLICIES = {
5
- "glm-5.1": { maxTurns: 50 },
6
- "glm/glm-5.1": { maxTurns: 50 },
7
- "glm-4-plus": { maxTurns: 35 },
8
- "glm/glm-4-plus": { maxTurns: 35 },
9
- "glm-4-flash": { maxTurns: 15 },
10
- "glm/glm-4-flash": { maxTurns: 15 },
11
- "glm-4-air": { maxTurns: 20 },
12
- "glm/glm-4-air": { maxTurns: 20 },
13
- };
4
+ import { GLM_POLICIES, anthropicVendorProfiles } from "./vendor-profiles.js";
14
5
  /**
15
6
  * GLM over its Anthropic-compatible endpoint.
7
+ * @deprecated Prefer `glm({ protocol: "anthropic" })`. Behavior is now fully
8
+ * data-driven via `anthropicVendorProfiles.glm`; this thin shim is kept for
9
+ * backward compatibility and `instanceof` checks.
16
10
  */
17
- export class GLMAnthropicProvider extends AnthropicProvider {
18
- constructor(apiKey, model = "glm-5.1", retry, baseURL = endpointProfiles["glm.anthropic"].baseURL) {
19
- super(apiKey, model, retry, {
20
- baseURL,
21
- authMode: "api-key",
22
- });
23
- }
24
- providerName() {
25
- return "glm";
26
- }
27
- runtimePolicy() {
28
- return GLM_POLICIES[this.model] ?? {};
11
+ export class GLMAnthropicProvider extends AnthropicCompatibleProvider {
12
+ constructor(apiKey, model, retry, baseURL) {
13
+ super(anthropicVendorProfiles.glm, apiKey, model, retry, baseURL);
29
14
  }
30
15
  }
31
16
  export class GLMProvider extends OpenAIChatProvider {
@@ -1,16 +1,17 @@
1
1
  import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
2
  import { OpenAIChatProvider } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * Kimi over its Anthropic-compatible endpoint.
6
+ * @deprecated Prefer `kimi({ protocol: "anthropic" })`. Behavior is now fully
7
+ * data-driven via `anthropicVendorProfiles.kimi`; this thin shim is kept for
8
+ * backward compatibility and `instanceof` checks.
6
9
  */
7
- export declare class KimiAnthropicProvider extends AnthropicProvider {
10
+ export declare class KimiAnthropicProvider extends AnthropicCompatibleProvider {
8
11
  constructor(apiKey: string, model?: string, retry?: {
9
12
  maxRetries: number;
10
13
  baseDelay: number;
11
14
  }, baseURL?: string);
12
- protected providerName(): string;
13
- runtimePolicy(): RuntimePolicy;
14
15
  }
15
16
  export declare class KimiProvider extends OpenAIChatProvider {
16
17
  constructor(apiKey: string, model?: string, retry?: {
@@ -1,30 +1,16 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
1
  import { OpenAIChatProvider } from "./openai.js";
2
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
3
3
  import { endpointProfiles } from "./profiles.js";
4
- const KIMI_POLICIES = {
5
- "moonshot-v1-8k": { maxTurns: 15 },
6
- "moonshot-v1-32k": { maxTurns: 20 },
7
- "moonshot-v1-128k": { maxTurns: 30 },
8
- "kimi-k2.5": { maxTurns: 30 },
9
- "kimi-k2.6": { maxTurns: 35 },
10
- "kimi-k2-thinking": { maxTurns: 50 },
11
- "kimi-k2-thinking-turbo": { maxTurns: 40 },
12
- };
4
+ import { KIMI_POLICIES, anthropicVendorProfiles } from "./vendor-profiles.js";
13
5
  /**
14
6
  * Kimi over its Anthropic-compatible endpoint.
7
+ * @deprecated Prefer `kimi({ protocol: "anthropic" })`. Behavior is now fully
8
+ * data-driven via `anthropicVendorProfiles.kimi`; this thin shim is kept for
9
+ * backward compatibility and `instanceof` checks.
15
10
  */
16
- export class KimiAnthropicProvider extends AnthropicProvider {
17
- constructor(apiKey, model = "kimi-k2.6", retry, baseURL = endpointProfiles["kimi.anthropic"].baseURL) {
18
- super(apiKey, model, retry, {
19
- baseURL,
20
- authMode: "api-key",
21
- });
22
- }
23
- providerName() {
24
- return "kimi";
25
- }
26
- runtimePolicy() {
27
- return KIMI_POLICIES[this.model] ?? {};
11
+ export class KimiAnthropicProvider extends AnthropicCompatibleProvider {
12
+ constructor(apiKey, model, retry, baseURL) {
13
+ super(anthropicVendorProfiles.kimi, apiKey, model, retry, baseURL);
28
14
  }
29
15
  }
30
16
  export class KimiProvider extends OpenAIChatProvider {
@@ -1,24 +1,27 @@
1
- import type { Message, ProviderDescriptor, RenderedContext, RuntimePolicy, StreamEvent, ToolSchema } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
- import { OpenAIChatProvider } from "./openai.js";
1
+ import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
+ import { OpenAIChatProvider, type OpenAIChatTurnReasoning } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * MiniMax over its Anthropic-compatible endpoint. Replay is carried as Anthropic
6
6
  * `native_blocks` (thinking/text/tool_use), identical to the first-party
7
7
  * Anthropic provider.
8
+ * @deprecated Prefer `minimax({ protocol: "anthropic" })`. Behavior is now fully
9
+ * data-driven via `anthropicVendorProfiles.minimax`; this thin shim is kept for
10
+ * backward compatibility and `instanceof` checks.
8
11
  */
9
- export declare class MiniMaxAnthropicProvider extends AnthropicProvider {
12
+ export declare class MiniMaxAnthropicProvider extends AnthropicCompatibleProvider {
10
13
  constructor(apiKey: string, model?: string, retry?: {
11
14
  maxRetries: number;
12
15
  baseDelay: number;
13
16
  }, baseURL?: string);
14
- protected providerName(): string;
15
- runtimePolicy(): RuntimePolicy;
16
17
  }
17
18
  /**
18
19
  * MiniMax over its OpenAI-compatible endpoint. Replay is carried as
19
- * `reasoning_content` / `reasoning_details` (split reasoning), and requests
20
- * default to `reasoning_split: true` so reasoning is returned out-of-band rather
21
- * than embedded in the message content.
20
+ * `reasoning_content` / `reasoning_details` (split reasoning) in a schema_version-2
21
+ * envelope; requests default to `reasoning_split: true` so reasoning is returned
22
+ * out-of-band rather than embedded in the message content. Request shaping and replay
23
+ * are supplied via the OpenAIChatProvider Template-Method hooks; the streaming /
24
+ * tool-call machinery is inherited from the base class.
22
25
  */
23
26
  export declare class MiniMaxOpenAIProvider extends OpenAIChatProvider {
24
27
  constructor(apiKey: string, model?: string, retry?: {
@@ -28,8 +31,19 @@ export declare class MiniMaxOpenAIProvider extends OpenAIChatProvider {
28
31
  runtimePolicy(): RuntimePolicy;
29
32
  descriptor(): ProviderDescriptor;
30
33
  protected requireNonEmptyReasoningReplayForToolTurns(extensions?: Record<string, unknown>): boolean;
31
- private buildRequestExtensions;
32
- complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
33
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
34
+ protected cacheKeyParams(): Record<string, unknown>;
35
+ protected usesInlineThinkingTags(): boolean;
36
+ protected exposeReasoningDelta(extensions?: Record<string, unknown>): boolean;
37
+ protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
38
+ protected rememberCompleteReplay(content: string, toolCalls: Array<{
39
+ id: string;
40
+ name: string;
41
+ arguments: string;
42
+ }>, r: OpenAIChatTurnReasoning): void;
43
+ protected rememberStreamReplay(content: string, toolCalls: Array<{
44
+ id: string;
45
+ name: string;
46
+ arguments: string;
47
+ }>, r: OpenAIChatTurnReasoning): void;
34
48
  private rememberMiniMaxReplay;
35
49
  }
@@ -1,41 +1,28 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
1
  import { OpenAIChatProvider } from "./openai.js";
2
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
3
3
  import { endpointProfiles } from "./profiles.js";
4
- import { omitExtensionKeys, openAICachedPromptTokens } from "./base.js";
5
- const MINIMAX_POLICIES = {
6
- "MiniMax-M2.7": { maxTurns: 35 },
7
- "MiniMax-M2.7-highspeed": { maxTurns: 35 },
8
- "MiniMax-M2.5": { maxTurns: 25 },
9
- "MiniMax-M2.5-highspeed": { maxTurns: 25 },
10
- "MiniMax-M2.1": { maxTurns: 25 },
11
- "MiniMax-M2.1-highspeed": { maxTurns: 25 },
12
- "MiniMax-M2": { maxTurns: 20 },
13
- "MiniMax-Text-01": { maxTurns: 20 },
14
- };
4
+ import { omitExtensionKeys } from "./base.js";
5
+ import { MINIMAX_POLICIES, anthropicVendorProfiles } from "./vendor-profiles.js";
15
6
  /**
16
7
  * MiniMax over its Anthropic-compatible endpoint. Replay is carried as Anthropic
17
8
  * `native_blocks` (thinking/text/tool_use), identical to the first-party
18
9
  * Anthropic provider.
10
+ * @deprecated Prefer `minimax({ protocol: "anthropic" })`. Behavior is now fully
11
+ * data-driven via `anthropicVendorProfiles.minimax`; this thin shim is kept for
12
+ * backward compatibility and `instanceof` checks.
19
13
  */
20
- export class MiniMaxAnthropicProvider extends AnthropicProvider {
21
- constructor(apiKey, model = "MiniMax-M2.7", retry, baseURL = endpointProfiles["minimax.anthropic"].baseURL) {
22
- super(apiKey, model, retry, {
23
- baseURL,
24
- authMode: "api-key",
25
- });
26
- }
27
- providerName() {
28
- return "minimax";
29
- }
30
- runtimePolicy() {
31
- return MINIMAX_POLICIES[this.model] ?? {};
14
+ export class MiniMaxAnthropicProvider extends AnthropicCompatibleProvider {
15
+ constructor(apiKey, model, retry, baseURL) {
16
+ super(anthropicVendorProfiles.minimax, apiKey, model, retry, baseURL);
32
17
  }
33
18
  }
34
19
  /**
35
20
  * MiniMax over its OpenAI-compatible endpoint. Replay is carried as
36
- * `reasoning_content` / `reasoning_details` (split reasoning), and requests
37
- * default to `reasoning_split: true` so reasoning is returned out-of-band rather
38
- * than embedded in the message content.
21
+ * `reasoning_content` / `reasoning_details` (split reasoning) in a schema_version-2
22
+ * envelope; requests default to `reasoning_split: true` so reasoning is returned
23
+ * out-of-band rather than embedded in the message content. Request shaping and replay
24
+ * are supplied via the OpenAIChatProvider Template-Method hooks; the streaming /
25
+ * tool-call machinery is inherited from the base class.
39
26
  */
40
27
  export class MiniMaxOpenAIProvider extends OpenAIChatProvider {
41
28
  constructor(apiKey, model = "MiniMax-M2.7", retry, baseURL = endpointProfiles["minimax.openai"].baseURL) {
@@ -65,139 +52,33 @@ export class MiniMaxOpenAIProvider extends OpenAIChatProvider {
65
52
  return false;
66
53
  return extensions?.reasoning_split !== false;
67
54
  }
68
- buildRequestExtensions(extensions) {
55
+ // MiniMax auto prefix-caches and does not accept OpenAI's `prompt_cache_key`; omit it.
56
+ cacheKeyParams() {
57
+ return {};
58
+ }
59
+ // Reasoning arrives out-of-band (reasoning_content / reasoning_details), never inline tags.
60
+ usesInlineThinkingTags() {
61
+ return false;
62
+ }
63
+ exposeReasoningDelta(extensions) {
64
+ return (extensions?.exposeReasoning ?? false);
65
+ }
66
+ prepareExtensions(extensions) {
69
67
  const reasoningSplit = extensions?.reasoning_split !== false;
70
68
  return {
71
69
  ...omitExtensionKeys(extensions, ["reasoning_split", "exposeReasoning"]),
72
70
  __deepstrikeThinkingEnabled: reasoningSplit,
73
- // Re-thread the degrade control flag (omitExtensionKeys strips internal
74
- // keys) so buildChatMessages can honor it; the wire-request omit drops it.
71
+ // Re-thread the degrade control flag (omitExtensionKeys strips internal keys) so
72
+ // buildChatMessages can honor it; the wire-request omit drops it.
75
73
  ...(extensions?.degradeMissingReasoningReplay === true ? { degradeMissingReasoningReplay: true } : {}),
76
74
  reasoning_split: reasoningSplit,
77
75
  };
78
76
  }
79
- async complete(context, tools, extensions) {
80
- const requestExtensions = this.buildRequestExtensions(extensions);
81
- if (this.circuit.isOpen())
82
- throw new Error("Circuit breaker open");
83
- const msgs = this.buildChatMessages(context, requestExtensions);
84
- let lastErr;
85
- for (let i = 0; i < this.maxRetries; i++) {
86
- try {
87
- const resp = await this.client.chat.completions.create({
88
- ...this.requestExtensions(requestExtensions),
89
- model: this.model,
90
- messages: msgs,
91
- ...(tools.length ? { tools: this.chat.buildTools(tools) } : {}),
92
- });
93
- this.circuit.recordSuccess();
94
- const choice = resp.choices[0].message;
95
- const nativeToolCalls = choice.tool_calls ?? [];
96
- const toolCalls = this.chat.normalizeToolCalls(nativeToolCalls);
97
- const content = choice.content ?? "";
98
- this.rememberMiniMaxReplay(content, toolCalls, choice.reasoning_content, choice.reasoning_details, nativeToolCalls);
99
- return { role: "assistant", content, tokenCount: resp.usage?.completion_tokens ?? resp.usage?.total_tokens, toolCalls };
100
- }
101
- catch (err) {
102
- lastErr = err;
103
- this.circuit.recordFailure();
104
- if (i < this.maxRetries - 1)
105
- await new Promise(r => setTimeout(r, this.baseDelay * 2 ** i));
106
- }
107
- }
108
- throw lastErr;
77
+ rememberCompleteReplay(content, toolCalls, r) {
78
+ this.rememberMiniMaxReplay(content, toolCalls, r.reasoningContent, r.reasoningDetails, r.nativeToolCalls);
109
79
  }
110
- async *stream(context, tools, extensions) {
111
- const exposeReasoning = extensions?.exposeReasoning ?? false;
112
- const requestExtensions = this.buildRequestExtensions(extensions);
113
- const msgs = this.buildChatMessages(context, requestExtensions);
114
- const toolCallBufs = {};
115
- const emittedToolCallIndexes = new Set();
116
- let reasoningContent = "";
117
- let reasoningDetails;
118
- let finalText = "";
119
- const stream = await this.client.chat.completions.create({
120
- ...this.requestExtensions(requestExtensions),
121
- model: this.model,
122
- messages: msgs,
123
- ...(tools.length ? { tools: this.chat.buildTools(tools) } : {}),
124
- stream: true,
125
- stream_options: { include_usage: true },
126
- });
127
- let totalTokens = 0;
128
- let inputTokens = 0;
129
- let outputTokens = 0;
130
- let cacheReadTokens = 0;
131
- for await (const chunk of stream) {
132
- if (chunk.usage) {
133
- totalTokens = chunk.usage.total_tokens;
134
- inputTokens = chunk.usage.prompt_tokens ?? 0;
135
- outputTokens = chunk.usage.completion_tokens ?? 0;
136
- cacheReadTokens = openAICachedPromptTokens(chunk.usage);
137
- continue;
138
- }
139
- const choice = chunk.choices[0];
140
- if (!choice)
141
- continue;
142
- const delta = choice.delta;
143
- if (!delta)
144
- continue;
145
- if (exposeReasoning && delta.reasoning_content) {
146
- yield { type: "thinking_delta", delta: String(delta.reasoning_content) };
147
- }
148
- if (delta.reasoning_content)
149
- reasoningContent += String(delta.reasoning_content);
150
- if (delta.reasoning_details !== undefined && delta.reasoning_details !== null)
151
- reasoningDetails = delta.reasoning_details;
152
- if (delta.content) {
153
- finalText += String(delta.content);
154
- yield { type: "text_delta", delta: delta.content };
155
- }
156
- for (const tc of delta.tool_calls ?? []) {
157
- const idx = tc.index;
158
- if (!toolCallBufs[idx])
159
- toolCallBufs[idx] = { id: tc.id ?? "", name: "", argsBuf: "" };
160
- if (tc.function?.name)
161
- toolCallBufs[idx].name += tc.function.name;
162
- toolCallBufs[idx].argsBuf += tc.function?.arguments ?? "";
163
- }
164
- if (choice.finish_reason === "tool_calls") {
165
- const toolCalls = Object.values(toolCallBufs).map(tb => ({ id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}" }));
166
- this.rememberMiniMaxReplay(finalText, toolCalls, reasoningContent, reasoningDetails, nativeToolCallsFromBuffers(toolCallBufs));
167
- for (const [index, tb] of Object.entries(toolCallBufs)) {
168
- const idx = Number(index);
169
- if (emittedToolCallIndexes.has(idx))
170
- continue;
171
- let args = {};
172
- try {
173
- args = JSON.parse(tb.argsBuf || "{}");
174
- }
175
- catch {
176
- args = {};
177
- }
178
- emittedToolCallIndexes.add(idx);
179
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
180
- }
181
- }
182
- }
183
- const toolCalls = Object.values(toolCallBufs).map(tb => ({ id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}" }));
184
- this.rememberMiniMaxReplay(finalText, toolCalls, reasoningContent, reasoningDetails, nativeToolCallsFromBuffers(toolCallBufs));
185
- for (const [index, tb] of Object.entries(toolCallBufs)) {
186
- const idx = Number(index);
187
- if (emittedToolCallIndexes.has(idx))
188
- continue;
189
- let args = {};
190
- try {
191
- args = JSON.parse(tb.argsBuf || "{}");
192
- }
193
- catch {
194
- args = {};
195
- }
196
- emittedToolCallIndexes.add(idx);
197
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
198
- }
199
- if (totalTokens > 0)
200
- yield { type: "usage", totalTokens, inputTokens, outputTokens, ...(cacheReadTokens > 0 ? { cacheReadInputTokens: cacheReadTokens } : {}) };
80
+ rememberStreamReplay(content, toolCalls, r) {
81
+ this.rememberMiniMaxReplay(content, toolCalls, r.reasoningContent, r.reasoningDetails, r.nativeToolCalls);
201
82
  }
202
83
  rememberMiniMaxReplay(content, toolCalls, reasoningContent, reasoningDetails, nativeToolCalls) {
203
84
  const hasReasoning = typeof reasoningContent === "string" && reasoningContent.trim().length > 0;
@@ -215,10 +96,3 @@ export class MiniMaxOpenAIProvider extends OpenAIChatProvider {
215
96
  });
216
97
  }
217
98
  }
218
- function nativeToolCallsFromBuffers(toolCallBufs) {
219
- return Object.values(toolCallBufs).map(tb => ({
220
- id: tb.id,
221
- type: "function",
222
- function: { name: tb.name, arguments: tb.argsBuf || "{}" },
223
- }));
224
- }
@@ -15,6 +15,20 @@ export interface OpenAIProviderOptions {
15
15
  /** Custom OpenAI-compatible endpoint (MiMo, DeepSeek, Kimi, …). Defaults to the OpenAI API. */
16
16
  baseURL?: string;
17
17
  }
18
+ /** Reasoning captured from a single model turn, handed to the replay-remember hooks so an
19
+ * OpenAI-compatible subclass can persist whatever replay envelope its wire requires. */
20
+ export interface OpenAIChatTurnReasoning {
21
+ reasoningContent: string;
22
+ reasoningDetails?: unknown;
23
+ nativeToolCalls: unknown[];
24
+ }
25
+ /** Rebuild OpenAI-native `tool_calls` blocks from the streamed buffers — needed by reasoning
26
+ * vendors (DeepSeek/MiniMax) that persist the native blocks in their replay envelope. */
27
+ export declare function nativeToolCallsFromBuffers(toolCallBufs: Record<number, {
28
+ id: string;
29
+ name: string;
30
+ argsBuf: string;
31
+ }>): Array<Record<string, unknown>>;
18
32
  export declare class OpenAIChatProvider implements LLMProvider {
19
33
  protected client: OpenAI;
20
34
  protected circuit: CircuitBreaker;
@@ -31,6 +45,35 @@ export declare class OpenAIChatProvider implements LLMProvider {
31
45
  protected requireNonEmptyReasoningReplayForToolTurns(_extensions?: Record<string, unknown>): boolean;
32
46
  protected degradeMissingReasoningReplay(extensions?: Record<string, unknown>): boolean;
33
47
  protected buildChatMessages(context: RenderedContext, extensions?: Record<string, unknown>): OpenAI.Chat.Completions.ChatCompletionMessageParam[];
48
+ /** Pre-process caller extensions before they reach buildChatMessages + the wire request
49
+ * (e.g. set `__deepstrikeThinkingEnabled`). Default: pass through unchanged. */
50
+ protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown> | undefined;
51
+ /** Extra top-level request-body fields merged into the chat.completions call (vendor thinking
52
+ * knobs like `reasoning_effort`, `extra_body`, `reasoning_split`). Default: none. */
53
+ protected requestBodyExtras(_extensions?: Record<string, unknown>): Record<string, unknown>;
54
+ /** Request-body params controlling prompt caching. Default sends OpenAI's `prompt_cache_key`;
55
+ * vendors whose endpoints reject unknown params (e.g. DeepSeek 400s) override to `{}`. */
56
+ protected cacheKeyParams(context: RenderedContext, tools: ToolSchema[]): Record<string, unknown>;
57
+ /** Whether streamed `content` may carry inline `<thinking>…</thinking>` tags to split out.
58
+ * Default true (OpenAI). Reasoning vendors emit reasoning out-of-band, so they return false. */
59
+ protected usesInlineThinkingTags(): boolean;
60
+ /** Whether to surface streamed `reasoning_content` as thinking_delta events. Default true;
61
+ * vendors gate this behind an `exposeReasoning` extension. */
62
+ protected exposeReasoningDelta(_extensions?: Record<string, unknown>): boolean;
63
+ /** Persist replay after a non-streaming turn. Default: nothing (plain OpenAI has no reasoning
64
+ * to replay). Reasoning vendors override to store their envelope. */
65
+ protected rememberCompleteReplay(_content: string, _toolCalls: Array<{
66
+ id: string;
67
+ name: string;
68
+ arguments: string;
69
+ }>, _reasoning: OpenAIChatTurnReasoning): void;
70
+ /** Persist replay after a streamed turn. Default: store `{ reasoning_content }` when there is a
71
+ * tool-call turn or captured reasoning (the prior base behavior). Vendors override. */
72
+ protected rememberStreamReplay(content: string, toolCalls: Array<{
73
+ id: string;
74
+ name: string;
75
+ arguments: string;
76
+ }>, reasoning: OpenAIChatTurnReasoning): void;
34
77
  /**
35
78
  * Pre-flight query: would this history validate against this provider with the
36
79
  * given extensions, without sending the request? Lets an embedder route around