@deepstrike/sdk 0.2.30 → 0.2.32

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 (48) 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/gemini.d.ts +12 -0
  15. package/dist/providers/gemini.js +38 -3
  16. package/dist/providers/glm.d.ts +7 -4
  17. package/dist/providers/glm.js +27 -24
  18. package/dist/providers/kimi.d.ts +5 -4
  19. package/dist/providers/kimi.js +8 -22
  20. package/dist/providers/minimax.d.ts +26 -12
  21. package/dist/providers/minimax.js +33 -159
  22. package/dist/providers/openai-responses.d.ts +6 -0
  23. package/dist/providers/openai-responses.js +22 -2
  24. package/dist/providers/openai.d.ts +52 -0
  25. package/dist/providers/openai.js +145 -66
  26. package/dist/providers/profiles.d.ts +60 -0
  27. package/dist/providers/profiles.js +22 -0
  28. package/dist/providers/qwen.d.ts +20 -19
  29. package/dist/providers/qwen.js +49 -176
  30. package/dist/providers/registry.d.ts +18 -0
  31. package/dist/providers/registry.js +35 -0
  32. package/dist/providers/vendor-profiles.d.ts +54 -0
  33. package/dist/providers/vendor-profiles.js +66 -0
  34. package/dist/runtime/event-stream.d.ts +44 -0
  35. package/dist/runtime/event-stream.js +39 -0
  36. package/dist/runtime/reactive-session.d.ts +125 -0
  37. package/dist/runtime/reactive-session.js +127 -0
  38. package/dist/runtime/run-group.d.ts +74 -0
  39. package/dist/runtime/run-group.js +72 -0
  40. package/dist/runtime/runner.d.ts +9 -0
  41. package/dist/runtime/runner.js +56 -7
  42. package/dist/runtime/session-log.d.ts +8 -0
  43. package/dist/runtime/turn-policy.d.ts +33 -0
  44. package/dist/runtime/turn-policy.js +58 -0
  45. package/dist/signals/gateway.d.ts +7 -2
  46. package/dist/signals/gateway.js +13 -3
  47. package/dist/signals/types.d.ts +10 -1
  48. 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 = {}) {
@@ -16,4 +16,16 @@ export declare class GeminiProvider implements LLMProvider {
16
16
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
17
17
  stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
18
18
  private modelExtensions;
19
+ /**
20
+ * Gemini vendor features from extensions, mapped to the Node SDK shape (mirrors the Python provider's
21
+ * extension keys for a consistent cross-SDK API):
22
+ * - `google_search` (truthy → default, object → config): Google Search grounding server tool
23
+ * (gemini-2.0+), appended to tools[].
24
+ * - `response_mime_type` / `response_schema`: structured output → `generationConfig` (the API rejects
25
+ * pairing this with google_search).
26
+ */
27
+ vendorConfig(extensions?: Record<string, unknown>): {
28
+ tools?: unknown[];
29
+ generationConfig?: Record<string, unknown>;
30
+ };
19
31
  }
@@ -116,11 +116,14 @@ export class GeminiProvider {
116
116
  let lastErr;
117
117
  for (let i = 0; i < this.maxRetries; i++) {
118
118
  try {
119
+ const vc = this.vendorConfig(extensions);
120
+ const allTools = [...geminiTools, ...(vc.tools ?? [])];
119
121
  const m = this.genAI.getGenerativeModel({
120
122
  ...this.modelExtensions(extensions),
121
123
  model: this.model,
122
124
  ...(system ? { systemInstruction: system } : {}),
123
- ...(geminiTools.length ? { tools: geminiTools } : {}),
125
+ ...(allTools.length ? { tools: allTools } : {}),
126
+ ...(vc.generationConfig ? { generationConfig: vc.generationConfig } : {}),
124
127
  }, this.requestOptions);
125
128
  const resp = await m.generateContent({ contents });
126
129
  this.circuit.recordSuccess();
@@ -157,11 +160,14 @@ export class GeminiProvider {
157
160
  const system = context.systemText || undefined;
158
161
  const contents = buildContents(turnsWithStateAppended(context));
159
162
  const geminiTools = buildTools(tools);
163
+ const vc = this.vendorConfig(extensions);
164
+ const allTools = [...geminiTools, ...(vc.tools ?? [])];
160
165
  const m = this.genAI.getGenerativeModel({
161
166
  ...this.modelExtensions(extensions),
162
167
  model: this.model,
163
168
  ...(system ? { systemInstruction: system } : {}),
164
- ...(geminiTools.length ? { tools: geminiTools } : {}),
169
+ ...(allTools.length ? { tools: allTools } : {}),
170
+ ...(vc.generationConfig ? { generationConfig: vc.generationConfig } : {}),
165
171
  }, this.requestOptions);
166
172
  const result = await m.generateContentStream({ contents });
167
173
  const toolCalls = [];
@@ -195,7 +201,36 @@ export class GeminiProvider {
195
201
  modelExtensions(extensions) {
196
202
  if (!extensions)
197
203
  return {};
198
- const { model: _model, systemInstruction: _systemInstruction, tools: _tools, ...rest } = extensions;
204
+ // Strip keys handled explicitly elsewhere (incl. the vendor server-tool / structured-output keys
205
+ // consumed by `vendorConfig`) so they never leak raw into getGenerativeModel.
206
+ // Strip keys handled explicitly: the SDK fields set below + the named vendor keys consumed by
207
+ // `vendorConfig`. A caller-provided raw `generationConfig` still passes through (and is merged with
208
+ // any structured-output config at the call site).
209
+ const { model: _model, systemInstruction: _systemInstruction, tools: _tools, google_search: _gs, response_mime_type: _rmt, response_schema: _rs, ...rest } = extensions;
199
210
  return rest;
200
211
  }
212
+ /**
213
+ * Gemini vendor features from extensions, mapped to the Node SDK shape (mirrors the Python provider's
214
+ * extension keys for a consistent cross-SDK API):
215
+ * - `google_search` (truthy → default, object → config): Google Search grounding server tool
216
+ * (gemini-2.0+), appended to tools[].
217
+ * - `response_mime_type` / `response_schema`: structured output → `generationConfig` (the API rejects
218
+ * pairing this with google_search).
219
+ */
220
+ vendorConfig(extensions) {
221
+ const ext = extensions ?? {};
222
+ const tools = [];
223
+ if (ext.google_search)
224
+ tools.push({ googleSearch: typeof ext.google_search === "object" ? ext.google_search : {} });
225
+ // Seed from any caller-provided raw generationConfig, then layer the named structured-output keys.
226
+ const gc = { ...ext.generationConfig };
227
+ if (ext.response_mime_type != null)
228
+ gc.responseMimeType = ext.response_mime_type;
229
+ if (ext.response_schema != null)
230
+ gc.responseSchema = ext.response_schema;
231
+ return {
232
+ ...(tools.length ? { tools } : {}),
233
+ ...(Object.keys(gc).length ? { generationConfig: gc } : {}),
234
+ };
235
+ }
201
236
  }
@@ -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?: {
@@ -19,4 +20,6 @@ export declare class GLMProvider extends OpenAIChatProvider {
19
20
  }, baseURL?: string);
20
21
  runtimePolicy(): RuntimePolicy;
21
22
  descriptor(): ProviderDescriptor;
23
+ protected serverTools(extensions?: Record<string, unknown>): unknown[];
24
+ protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown> | undefined;
22
25
  }
@@ -1,35 +1,20 @@
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 {
32
- constructor(apiKey, model = "glm-5.1", retry, baseURL = endpointProfiles["glm.openai"].baseURL) {
17
+ constructor(apiKey, model = "glm-5.2", retry, baseURL = endpointProfiles["glm.openai"].baseURL) {
33
18
  super(apiKey, model, retry, baseURL);
34
19
  }
35
20
  runtimePolicy() {
@@ -42,4 +27,22 @@ export class GLMProvider extends OpenAIChatProvider {
42
27
  model: this.model,
43
28
  };
44
29
  }
30
+ // ── GLM web_search (Zhipu vendor server tool; OpenAI-wire only) ──────────────
31
+ // Enable with `extensions={ web_search: true }` (default config) or `{ web_search: {...} }`
32
+ // (passthrough: search_engine, search_recency_filter, search_domain_filter, count, …). Injected as a
33
+ // `{ type: "web_search", web_search: {...} }` entry in tools[]; the model searches server-side and
34
+ // the results come back inline (no client tool-loop). Mirrors the Python GLM provider.
35
+ serverTools(extensions) {
36
+ const ws = extensions?.web_search;
37
+ if (!ws)
38
+ return [];
39
+ return [{ type: "web_search", web_search: typeof ws === "object" ? ws : {} }];
40
+ }
41
+ // Strip `web_search` from the passthrough so it shapes tools[] only, never leaks as a body field.
42
+ prepareExtensions(extensions) {
43
+ if (!extensions || !("web_search" in extensions))
44
+ return extensions;
45
+ const { web_search: _omit, ...rest } = extensions;
46
+ return rest;
47
+ }
45
48
  }
@@ -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,44 +1,31 @@
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
- constructor(apiKey, model = "MiniMax-M2.7", retry, baseURL = endpointProfiles["minimax.openai"].baseURL) {
28
+ constructor(apiKey, model = "MiniMax-M3", retry, baseURL = endpointProfiles["minimax.openai"].baseURL) {
42
29
  super(apiKey, model, retry, baseURL);
43
30
  }
44
31
  runtimePolicy() {
@@ -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
- }
@@ -40,5 +40,11 @@ export declare class OpenAIResponsesProvider implements LLMProvider {
40
40
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
41
41
  stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState): AsyncIterable<StreamEvent>;
42
42
  private requestExtensions;
43
+ /** Responses API built-in server tools from extensions (live in the same tools[] as function tools):
44
+ * `web_search: true` (or a config object), plus a `builtin_tools` list passed through verbatim for
45
+ * file_search / code_interpreter. They run server-side; results return inline. Mirrors py. */
46
+ private builtinTools;
47
+ /** Function tools + built-in server tools merged into the wire tools[] (undefined when empty). */
48
+ private allTools;
43
49
  private asRunState;
44
50
  }
@@ -176,7 +176,7 @@ export class OpenAIResponsesProvider {
176
176
  model: this.model,
177
177
  input: this.responses.buildInput(context),
178
178
  ...(instructions ? { instructions } : {}),
179
- ...(tools.length ? { tools: this.responses.buildTools(tools) } : {}),
179
+ ...((t => t ? { tools: t } : {})(this.allTools(tools, extensions))),
180
180
  });
181
181
  this.circuit.recordSuccess();
182
182
  const decoded = this.responses.decodeOutput(resp.output);
@@ -206,7 +206,7 @@ export class OpenAIResponsesProvider {
206
206
  input: this.responses.buildInput(context, runState),
207
207
  ...(instructions ? { instructions } : {}),
208
208
  ...(runState.previousResponseId ? { previous_response_id: runState.previousResponseId } : {}),
209
- ...(tools.length ? { tools: this.responses.buildTools(tools) } : {}),
209
+ ...((t => t ? { tools: t } : {})(this.allTools(tools, extensions))),
210
210
  stream: true,
211
211
  });
212
212
  for await (const evt of stream) {
@@ -266,8 +266,28 @@ export class OpenAIResponsesProvider {
266
266
  requestExtensions(extensions) {
267
267
  return omitExtensionKeys(extensions, [
268
268
  "model", "input", "instructions", "tools", "stream", "previous_response_id",
269
+ "web_search", "builtin_tools",
269
270
  ]);
270
271
  }
272
+ /** Responses API built-in server tools from extensions (live in the same tools[] as function tools):
273
+ * `web_search: true` (or a config object), plus a `builtin_tools` list passed through verbatim for
274
+ * file_search / code_interpreter. They run server-side; results return inline. Mirrors py. */
275
+ builtinTools(extensions) {
276
+ const ext = extensions ?? {};
277
+ const out = [];
278
+ const ws = ext.web_search;
279
+ if (ws)
280
+ out.push(typeof ws === "object" ? { type: "web_search", ...ws } : { type: "web_search" });
281
+ if (Array.isArray(ext.builtin_tools))
282
+ out.push(...ext.builtin_tools);
283
+ return out;
284
+ }
285
+ /** Function tools + built-in server tools merged into the wire tools[] (undefined when empty). */
286
+ allTools(tools, extensions) {
287
+ const fnTools = tools.length ? this.responses.buildTools(tools) : [];
288
+ const all = [...fnTools, ...this.builtinTools(extensions)];
289
+ return all.length ? all : undefined;
290
+ }
271
291
  asRunState(state) {
272
292
  if (!state)
273
293
  return this.createRunState();