diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index fe4bb356e..d1487ef1a 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,11 +2,19 @@ ## [Unreleased] +### Breaking Changes + +- Removed the `thinkingFormat: \`"ant-ling"\`` compat value. Use `thinkingFormat: \`"deepseek"\`` for Ant Ling models ([#10943](https://github.com/earendil-works/pi/pull/10943) by [@lvisei](https://github.com/lvisei)) + ### Added - Anthropic requests with a `sessionId` (and `cacheRetention` not `"none"`) now use message threads (`message-threads-2026-08-12` beta): the server continues from the previous reply and pi sends only the new messages instead of the whole conversation. When anything earlier in the request changed, or the server can't continue the thread, the full request is sent. Token counts and prompt caching are unchanged. On by default only for the `anthropic` provider on `api.anthropic.com`; set `compat.supportsMessageThreads` to change that, for example for gateways that forward the `thread` field. Call `cleanupSessionResources(sessionId)` when a session ends to free the per-session state ([#11545](https://github.com/earendil-works/pi/pull/11545)) - Added `StreamOptions.disableContinuation`, which keeps a request from using or replacing server-side continuation state such as Anthropic message threads. Set it on requests that repeat an earlier one, such as cache warming ([#11545](https://github.com/earendil-works/pi/pull/11545)) +### Changed + +- Replaced the Ant Ling Ling 2.6 and Ring 2.6 models with Ling 3.1 Flash, Ling 3.0 Flash, and Ling 3.0 Flash VL. Ant Ling reasoning requests now use the DeepSeek-compatible `thinking` field instead of nested `reasoning`, with thinking either off or high and no reasoning effort parameter ([#10943](https://github.com/earendil-works/pi/pull/10943) by [@lvisei](https://github.com/lvisei)) + ### Fixed - Fixed OpenCode Go Claude Haiku 5.5 failing with a 400 (`Invalid signature in thinking block ... bound to a different conversation`) after the tool list, system prompt, or earlier history changed, which left the session failing on every retry. OpenCode Go and Zen Claude 5.5 models, Opus 5, and Fable 5.1 now use per-message effort and send `thinking.block_binding: drop_block`, like the Anthropic provider ([#10954](https://github.com/earendil-works/pi/issues/10954)) diff --git a/packages/ai/README.md b/packages/ai/README.md index d7a7a21e8..b6564098f 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -1461,7 +1461,7 @@ interface OpenAICompletionsCompat { requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false) requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false) requiresReasoningContentOnAssistantMessages?: boolean; // Whether all replayed assistant messages must include empty reasoning_content when reasoning is enabled (default: auto-detected for DeepSeek) - thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'baseten' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking' | 'ant-ling'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'baseten' uses configurable chat_template_args plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking, 'ant-ling' uses reasoning: { effort } only for mapped efforts (default: openai) + thinkingFormat?: 'openai' | 'openrouter' | 'deepseek' | 'together' | 'baseten' | 'zai' | 'qwen' | 'chat-template' | 'qwen-chat-template' | 'string-thinking'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'deepseek' uses thinking: { type } plus reasoning_effort when supported, 'together' uses reasoning: { enabled } plus reasoning_effort when supported, 'baseten' uses configurable chat_template_args plus reasoning_effort when supported, 'zai' uses thinking: { type }, 'qwen' uses enable_thinking, 'chat-template' uses configurable chat_template_kwargs, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking and preserve_thinking, 'string-thinking' uses top-level thinking (default: openai) chatTemplateKwargs?: Record; // chat_template_kwargs values; use $var for pi-controlled thinking values chatTemplateArgs?: Record; // chat_template_args values for thinkingFormat: 'baseten'; use $var for pi-controlled thinking values thinkingTokenBudgetField?: 'thinking_token_budget' | 'thinking_budget' | 'thinking_budget_tokens'; // Top-level field that caps reasoning tokens from thinkingBudgets (vLLM / Qwen / llama.cpp). Off by default. diff --git a/packages/ai/catalog/providers/ant-ling.ts b/packages/ai/catalog/providers/ant-ling.ts index b9cf58cc2..55d80993b 100644 --- a/packages/ai/catalog/providers/ant-ling.ts +++ b/packages/ai/catalog/providers/ant-ling.ts @@ -1,52 +1,69 @@ import type { Model, OpenAICompletionsCompat } from "../../src/types.ts"; -import { defineProvider, mergeThinking } from "../define.ts"; +import { defineProvider } from "../define.ts"; const COMPAT: OpenAICompletionsCompat = { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: false, maxTokensField: "max_tokens", + thinkingFormat: "deepseek", + supportsStrictMode: true, supportsLongCacheRetention: false, }; -function model(id: string, name: string, reasoning: boolean, cost: Model<"openai-completions">["cost"]) { +// Thinking is either off or high. Hiding the lower levels makes pi clamp them to +// high; off is unmapped, so a request without reasoning sends thinking disabled. +const ANT_LING_THINKING_LEVEL_MAP = { + minimal: null, + low: null, + medium: null, +} as const; + +function model( + id: string, + name: string, + input: ["text"] | ["text", "image"], + contextWindow: number, + cost: Model<"openai-completions">["cost"], +) { return { id, name, api: "openai-completions", baseUrl: "https://api.ant-ling.com/v1", provider: "ant-ling", - reasoning, - input: ["text"], + reasoning: true, + thinkingLevelMap: ANT_LING_THINKING_LEVEL_MAP, + input, cost, - contextWindow: 262144, - maxTokens: 65536, - compat: reasoning ? { ...COMPAT, thinkingFormat: "ant-ling" } : COMPAT, + contextWindow, + maxTokens: 262144, } satisfies Model<"openai-completions">; } export default defineProvider({ id: "ant-ling", models: () => [ - model("Ling-2.6-flash", "Ling 2.6 Flash", false, { input: 0.01, output: 0.02, cacheRead: 0, cacheWrite: 0 }), - model("Ling-2.6-1T", "Ling 2.6 1T", false, { input: 0.06, output: 0.25, cacheRead: 0, cacheWrite: 0 }), - model("Ring-2.6-1T", "Ring 2.6 1T", true, { input: 0.06, output: 0.25, cacheRead: 0, cacheWrite: 0 }), + model("Ling-3.1-flash", "Ling 3.1 Flash", ["text"], 1000000, { + input: 0.075, + output: 0.225, + cacheRead: 0.015, + cacheWrite: 0, + }), + model("Ling-3.0-flash-VL", "Ling 3.0 Flash VL", ["text", "image"], 262144, { + input: 0.021, + output: 0.062, + cacheRead: 0.0042, + cacheWrite: 0, + }), + model("Ling-3.0-flash", "Ling 3.0 Flash", ["text"], 262144, { + input: 0.0168, + output: 0.0504, + cacheRead: 0.0034, + cacheWrite: 0, + }), ], compat: { - "openai-completions": { - supportsStore: false, - supportsDeveloperRole: false, - supportsReasoningEffort: false, - maxTokensField: "max_tokens", - thinkingFormat: "ant-ling", - supportsStrictMode: true, - supportsLongCacheRetention: false, - }, - }, - finalize: (model) => { - // Ring reasons by default. Only high/xhigh have documented explicit effort controls. - if (model.reasoning) { - mergeThinking(model, { off: null, minimal: null, low: null, medium: null, high: "high", xhigh: "xhigh" }); - } + "openai-completions": COMPAT, }, }); diff --git a/packages/ai/src/api/openai-completions.ts b/packages/ai/src/api/openai-completions.ts index 0e5b606e6..038131135 100644 --- a/packages/ai/src/api/openai-completions.ts +++ b/packages/ai/src/api/openai-completions.ts @@ -930,11 +930,6 @@ function buildParams( } else if (model.thinkingLevelMap?.off !== null) { openRouterParams.reasoning = { effort: model.thinkingLevelMap?.off ?? "none" }; } - } else if (compat.thinkingFormat === "ant-ling" && model.reasoning && options?.reasoningEffort) { - const effort = model.thinkingLevelMap?.[options.reasoningEffort]; - if (typeof effort === "string") { - (params as typeof params & { reasoning?: { effort: string } }).reasoning = { effort }; - } } else if (compat.thinkingFormat === "together" && model.reasoning) { const togetherParams = params as Omit & { reasoning?: { enabled: boolean }; @@ -1638,8 +1633,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet return { supportsStore: !isNonStandard, supportsDeveloperRole: isOpenRouterDeveloperRoleModel || (!isNonStandard && !isOpenRouter), - supportsReasoningEffort: - !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia && !isAntLing, + supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway && !isNvidia, supportsUsageInStreaming: true, supportsFinishReason: true, maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens", @@ -1647,14 +1641,13 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAIComplet requiresAssistantAfterToolResult: false, requiresThinkingAsText: false, requiresReasoningContentOnAssistantMessages: isDeepSeek, - thinkingFormat: isDeepSeek - ? "deepseek" - : isZai - ? "zai" - : isTogether - ? "together" - : isAntLing - ? "ant-ling" + thinkingFormat: + isDeepSeek || isAntLing + ? "deepseek" + : isZai + ? "zai" + : isTogether + ? "together" : isOpenRouter ? "openrouter" : "openai", diff --git a/packages/ai/src/providers/compat-schema.ts b/packages/ai/src/providers/compat-schema.ts index 792b7a1b7..bc3ca8201 100644 --- a/packages/ai/src/providers/compat-schema.ts +++ b/packages/ai/src/providers/compat-schema.ts @@ -257,11 +257,10 @@ const OpenAICompletionsCompatProperties = { stringLiteral("chat-template"), stringLiteral("qwen-chat-template"), stringLiteral("string-thinking"), - stringLiteral("ant-ling"), ], { description: - 'Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. "openai" uses reasoning_effort, "openrouter" uses reasoning.effort, "deepseek" uses thinking.type plus reasoning_effort when supported, "together" uses reasoning.enabled plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking.type, "qwen" uses top-level enable_thinking, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, "string-thinking" uses top-level thinking, and "ant-ling" uses reasoning.effort only when the mapped effort is non-null.', + 'Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. "openai" uses reasoning_effort, "openrouter" uses reasoning.effort, "deepseek" uses thinking.type plus reasoning_effort when supported, "together" uses reasoning.enabled plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking.type, "qwen" uses top-level enable_thinking, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, and "string-thinking" uses top-level thinking.', }, ), ), diff --git a/packages/ai/test/openai-completions-tool-choice.test.ts b/packages/ai/test/openai-completions-tool-choice.test.ts index cf6aeebb5..c6ecb35ab 100644 --- a/packages/ai/test/openai-completions-tool-choice.test.ts +++ b/packages/ai/test/openai-completions-tool-choice.test.ts @@ -1,7 +1,8 @@ import { Type } from "typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { convertMessages } from "../src/api/openai-completions.ts"; -import { getModel, normalizeContext, stream, streamSimple } from "../src/compat.ts"; +import { getModel, normalizeContext, streamSimple } from "../src/compat.ts"; +import { getSupportedThinkingLevels } from "../src/models.ts"; import type { AssistantMessage, Model, SimpleStreamOptions, Tool, ToolResultMessage } from "../src/types.ts"; const mockState = vi.hoisted(() => ({ @@ -1399,7 +1400,7 @@ describe("openai-completions tool_choice", () => { ...model.compat, supportsStore: false, supportsDeveloperRole: false, - supportsReasoningEffort: true, + supportsReasoningEffort: false, supportsUsageInStreaming: true, supportsFinishReason: true, maxTokensField: "max_completion_tokens", @@ -1863,8 +1864,8 @@ describe("openai-completions tool_choice", () => { expect(params.reasoning_effort).toBeUndefined(); }); - it("uses Ant Ling compatibility metadata", async () => { - const model = getModel("ant-ling", "Ring-2.6-1T")!; + it("uses DeepSeek-compatible Ant Ling metadata", async () => { + const model = getModel("ant-ling", "Ling-3.1-flash")!; let payload: unknown; expect(model.compat).toMatchObject({ @@ -1872,7 +1873,7 @@ describe("openai-completions tool_choice", () => { supportsDeveloperRole: false, supportsReasoningEffort: false, maxTokensField: "max_tokens", - thinkingFormat: "ant-ling", + thinkingFormat: "deepseek", supportsLongCacheRetention: false, }); expect(model.compat?.supportsStrictMode).toBe(true); @@ -1900,7 +1901,7 @@ describe("openai-completions tool_choice", () => { max_tokens?: number; max_completion_tokens?: number; messages?: Array<{ role?: string }>; - reasoning?: { effort?: string }; + thinking?: { type?: string }; reasoning_effort?: string; store?: boolean; prompt_cache_key?: string; @@ -1909,48 +1910,36 @@ describe("openai-completions tool_choice", () => { expect(params.max_tokens).toBe(123); expect(params.max_completion_tokens).toBeUndefined(); expect(params.messages?.[0]?.role).toBe("system"); - expect(params.reasoning).toEqual({ effort: "high" }); + expect(params.thinking).toEqual({ type: "enabled" }); expect(params.reasoning_effort).toBeUndefined(); + expect(params).not.toHaveProperty("reasoning"); expect(params.store).toBeUndefined(); expect(params.prompt_cache_key).toBeUndefined(); expect(params.prompt_cache_retention).toBeUndefined(); }); - it("omits Ant Ling reasoning for unmapped direct reasoning efforts and non-reasoning models", async () => { - const ring = getModel("ant-ling", "Ring-2.6-1T")!; - let payload: unknown; + it("exposes only off and high for Ant Ling models", () => { + for (const id of ["Ling-3.1-flash", "Ling-3.0-flash", "Ling-3.0-flash-VL"] as const) { + expect(getSupportedThinkingLevels(getModel("ant-ling", id)!)).toEqual(["off", "high"]); + } + expect(getModel("ant-ling", "Ling-3.1-flash")!.contextWindow).toBe(1000000); + expect(getModel("ant-ling", "Ling-3.0-flash")!.contextWindow).toBe(262144); + expect(getModel("ant-ling", "Ling-3.0-flash-VL")!.contextWindow).toBe(262144); + }); - await stream( - ring, - { - messages: [{ role: "user", content: "Hi", timestamp: Date.now() }], - }, - { - apiKey: "test", - reasoningEffort: "medium", - onPayload: (params: unknown) => { - payload = params; - }, - }, - ).result(); + it("clamps every Ant Ling reasoning level to high", async () => { + const model = getModel("ant-ling", "Ling-3.0-flash")!; + for (const reasoning of ["minimal", "low", "medium", "high", "xhigh"] as const) { + const params = await captureSimpleParams(model, reasoning); + expect(params.thinking).toEqual({ type: "enabled" }); + expect(params.reasoning_effort).toBeUndefined(); + } + }); - expect((payload ?? mockState.lastParams) as { reasoning?: unknown }).not.toHaveProperty("reasoning"); - - const ling = getModel("ant-ling", "Ling-2.6-flash")!; - await streamSimple( - ling, - { - messages: [{ role: "user", content: "Hi", timestamp: Date.now() }], - }, - { - apiKey: "test", - reasoning: "high", - onPayload: (params: unknown) => { - payload = params; - }, - }, - ).result(); - - expect((payload ?? mockState.lastParams) as { reasoning?: unknown }).not.toHaveProperty("reasoning"); + it("disables Ant Ling reasoning when no reasoning level is requested", async () => { + const model = getModel("ant-ling", "Ling-3.0-flash-VL")!; + const params = await captureSimpleParams(model, undefined); + expect(params.thinking).toEqual({ type: "disabled" }); + expect(params.reasoning_effort).toBeUndefined(); }); }); diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 3cc02268b..5d038d507 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -1305,8 +1305,8 @@ describe("Generate E2E Tests", () => { }); }); - describe.skipIf(!process.env.ANT_LING_API_KEY)("Ant Ling Provider (Ling 2.6 Flash via OpenAI Completions)", () => { - const llm = getModel("ant-ling", "Ling-2.6-flash"); + describe.skipIf(!process.env.ANT_LING_API_KEY)("Ant Ling Provider (Ling 3.1 Flash via OpenAI Completions)", () => { + const llm = getModel("ant-ling", "Ling-3.1-flash"); it("should complete basic text generation", { retry: 3 }, async () => { await basicTextGeneration(llm); @@ -1321,8 +1321,8 @@ describe("Generate E2E Tests", () => { }); it("should handle thinking mode", { retry: 3 }, async () => { - const ringModel = getModel("ant-ling", "Ring-2.6-1T"); - await handleThinking(ringModel, { reasoningEffort: "high" }); + const model = getModel("ant-ling", "Ling-3.1-flash"); + await handleThinking(model, { reasoningEffort: "high" }); }); }); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index f9ca79449..8ec777744 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -12,6 +12,10 @@ - Anthropic sessions now use message threads: each request sends only the new messages and the server continues from the previous reply, falling back to the full request when the conversation changed (for example after compaction or a tool list change). On by default for the `anthropic` provider on `api.anthropic.com`; set `compat.supportsMessageThreads` in `models.json` to enable it for gateways that forward the `thread` field, or to turn it off. Cache warming does not touch the session's thread ([#11545](https://github.com/earendil-works/pi/pull/11545)) - `pi.registerMarkdownTransformer()` takes an optional `{ messageTypes }` that selects the Markdown surfaces a transformer runs on. Besides user, assistant, and thinking Markdown, transformers can now opt into `custom` (custom messages and skill blocks), `summary` (branch and compaction summaries), and `changelog` (What's New, `/changelog`, the Extension Updates notice, and the Update Available note). Without options a transformer runs on user, assistant, and thinking Markdown as before. The built-in Mermaid renderer now also draws diagrams in expanded branch and compaction summaries +### Changed + +- Changed the `ant-ling` default model to `Ling-3.1-flash`, replacing the removed Ling 2.6 and Ring 2.6 models ([#10943](https://github.com/earendil-works/pi/pull/10943) by [@lvisei](https://github.com/lvisei)) + ### Fixed - A failed token refresh of an MCP connection no longer registers an OAuth client or replaces the PKCE verifier of a sign-in in progress, which made that sign-in fail. MCP sign-in now refreshes the stored tokens with the stored client even when the loopback callback port changed, instead of registering a new client and opening the browser. Ctrl+C in `/mcp` and the `--timeout` of `pi mcp login` now also stop a sign-in while it waits up to 25 seconds for another pi process's token refresh, and a refresh lock that fails for another reason than that wait now fails the sign-in instead of opening the browser. diff --git a/packages/coding-agent/docs/models-configuration.md b/packages/coding-agent/docs/models-configuration.md index 2a25bdb48..e2369e966 100644 --- a/packages/coding-agent/docs/models-configuration.md +++ b/packages/coding-agent/docs/models-configuration.md @@ -120,7 +120,7 @@ See [Describe model input and caching](models.md#describe-model-input-and-cachin | `requiresAssistantAfterToolResult` | boolean | | Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. | | `requiresThinkingAsText` | boolean | | Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. | | `requiresReasoningContentOnAssistantMessages` | boolean | | Whether all replayed assistant messages must include an empty reasoning_content field when reasoning is enabled. Default: auto-detected from URL. | -| `thinkingFormat` | `"openai" \| "openrouter" \| "deepseek" \| "together" \| "baseten" \| "zai" \| "qwen" \| "chat-template" \| "qwen-chat-template" \| "string-thinking" \| "ant-ling"` | | Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. "openai" uses reasoning_effort, "openrouter" uses reasoning.effort, "deepseek" uses thinking.type plus reasoning_effort when supported, "together" uses reasoning.enabled plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking.type, "qwen" uses top-level enable_thinking, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, "string-thinking" uses top-level thinking, and "ant-ling" uses reasoning.effort only when the mapped effort is non-null. | +| `thinkingFormat` | `"openai" \| "openrouter" \| "deepseek" \| "together" \| "baseten" \| "zai" \| "qwen" \| "chat-template" \| "qwen-chat-template" \| "string-thinking"` | | Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. "openai" uses reasoning_effort, "openrouter" uses reasoning.effort, "deepseek" uses thinking.type plus reasoning_effort when supported, "together" uses reasoning.enabled plus reasoning_effort when supported, "baseten" uses configurable chat_template_args plus reasoning_effort when supported, "zai" uses thinking.type, "qwen" uses top-level enable_thinking, "qwen-chat-template" uses chat_template_kwargs.enable_thinking and preserve_thinking, "chat-template" uses configurable chat_template_kwargs, and "string-thinking" uses top-level thinking. | | `chatTemplateKwargs` | object | | Kwargs sent as chat_template_kwargs when thinkingFormat is "chat-template". Use $var with "thinking.enabled", "thinking.effort", or "thinking.budget" for pi-controlled values. | | `chatTemplateArgs` | object | | Arguments sent as chat_template_args when thinkingFormat is "baseten". Use $var with "thinking.enabled", "thinking.effort", or "thinking.budget" for pi-controlled values. | | `openRouterRouting` | object | | OpenRouter provider routing preferences. Controls which upstream providers OpenRouter routes requests to. Sent as the provider field in the OpenRouter API request body. See https://openrouter.ai/docs/guides/routing/provider-selection. | diff --git a/packages/coding-agent/schemas/models.schema.json b/packages/coding-agent/schemas/models.schema.json index 8861f8542..3e832af75 100644 --- a/packages/coding-agent/schemas/models.schema.json +++ b/packages/coding-agent/schemas/models.schema.json @@ -601,13 +601,9 @@ { "type": "string", "const": "string-thinking" - }, - { - "type": "string", - "const": "ant-ling" } ], - "description": "Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning.effort, \"deepseek\" uses thinking.type plus reasoning_effort when supported, \"together\" uses reasoning.enabled plus reasoning_effort when supported, \"baseten\" uses configurable chat_template_args plus reasoning_effort when supported, \"zai\" uses thinking.type, \"qwen\" uses top-level enable_thinking, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking and preserve_thinking, \"chat-template\" uses configurable chat_template_kwargs, \"string-thinking\" uses top-level thinking, and \"ant-ling\" uses reasoning.effort only when the mapped effort is non-null." + "description": "Format for reasoning or thinking parameters. When omitted, Pi auto-detects the format from the provider URL. \"openai\" uses reasoning_effort, \"openrouter\" uses reasoning.effort, \"deepseek\" uses thinking.type plus reasoning_effort when supported, \"together\" uses reasoning.enabled plus reasoning_effort when supported, \"baseten\" uses configurable chat_template_args plus reasoning_effort when supported, \"zai\" uses thinking.type, \"qwen\" uses top-level enable_thinking, \"qwen-chat-template\" uses chat_template_kwargs.enable_thinking and preserve_thinking, \"chat-template\" uses configurable chat_template_kwargs, and \"string-thinking\" uses top-level thinking." }, "chatTemplateKwargs": { "type": "object", diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index d1abd57cf..25212e041 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -19,7 +19,7 @@ import type { ModelRuntime } from "./model-runtime.ts"; /** Default chat model IDs for providers with built-in chat models. */ export const defaultModelPerProvider: Partial> = { "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1", - "ant-ling": "Ring-2.6-1T", + "ant-ling": "Ling-3.1-flash", anthropic: "claude-opus-4-8", openai: "gpt-6.1-sol", azure: "gpt-5.4", diff --git a/packages/coding-agent/test/model-resolver.test.ts b/packages/coding-agent/test/model-resolver.test.ts index 71cd27f0c..066c2fde3 100644 --- a/packages/coding-agent/test/model-resolver.test.ts +++ b/packages/coding-agent/test/model-resolver.test.ts @@ -708,7 +708,7 @@ describe("default model selection", () => { expect(defaultModelPerProvider.minimax).toBe("MiniMax-M2.7"); expect(defaultModelPerProvider["minimax-cn"]).toBe("MiniMax-M2.7"); expect(defaultModelPerProvider.cerebras).toBe("gpt-oss-120b"); - expect(defaultModelPerProvider["ant-ling"]).toBe("Ring-2.6-1T"); + expect(defaultModelPerProvider["ant-ling"]).toBe("Ling-3.1-flash"); }); test("built-in chat providers have defaults in their generated catalogs", () => {