mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(ai): support Azure Foundry Chat Completions deployments (#9714)
* feat(ai): support Azure Foundry Chat Completions deployments The Azure provider only implemented the Responses API, so Foundry deployments that speak Chat Completions could not be used. On Chat Completions the deepseek/deepseek-v4-pro model sends `thinking` and `prompt_cache_key`, both of which Azure rejects with 400. The provider now dispatches openai-completions alongside azure-openai-responses. A stream wrapper resolves the Azure endpoint onto the model first, since the shared openai-completions implementation reads it off the model. The request-shape differences are catalog compat rather than api code: reasoning_effort instead of DeepSeek's thinking field, no prompt cache parameters, reasoning_content kept on assistant turns so the prefix Foundry cached stays byte-identical, and effort clamped to the low/medium/high the deployment accepts. The provider is renamed from azure-openai-responses to azure, since it now serves more than the Responses API. The azure-openai-responses api id is unchanged. Breaking: auth.json and models.json entries keyed on azure-openai-responses must move to azure, and existing sessions see a different provider on their next turn. closes #9645 * feat(ai): apply the Azure deployment name map to Chat Completions The Responses path already sent the deployment name from AZURE_OPENAI_DEPLOYMENT_NAME_MAP. The Chat Completions path sent the catalog model id, so any deployment not named after the model 404'd. The Azure provider now swaps the deployment name into the request via onPayload before dispatch, then runs the caller's own onPayload on it. model.id stays the catalog id. The map helpers move to azure-openai-config.ts so the provider can share them without importing the SDK. * docs(ai,coding-agent): add changelog entries for the Azure provider rename Breaking: the azure-openai-responses provider is now azure. Users rename the key in auth.json, models.json, and settings.json; SDK users switch to getModel("azure", ...) and providers/azure. Also note Azure Foundry Chat Completions support (DeepSeek V4 Pro). --------- Co-authored-by: David Brailovsky <davidbrai@gmail.com>
This commit is contained in:
parent
b2b5c42f61
commit
a37306d437
33 changed files with 559 additions and 188 deletions
|
|
@ -290,6 +290,15 @@ const DEEPSEEK_V4_FLASH_THINKING_LEVEL_MAP = {
|
|||
...DEEPSEEK_V4_THINKING_LEVEL_MAP,
|
||||
low: "low",
|
||||
} as const;
|
||||
// Azure Foundry rejects DeepSeek's own max effort.
|
||||
const AZURE_DEEPSEEK_V4_THINKING_LEVEL_MAP = {
|
||||
minimal: null,
|
||||
low: "low",
|
||||
medium: "medium",
|
||||
high: "high",
|
||||
xhigh: null,
|
||||
max: null,
|
||||
} as const;
|
||||
// Verified against Fireworks Messages raw_output on 2026-09-10 (#9323).
|
||||
// Fall back to verified support when models.dev omits effort metadata; this is
|
||||
// not an allowlist. Any Fireworks Messages model advertising effort uses adaptive thinking.
|
||||
|
|
@ -870,7 +879,7 @@ function applyStrictToolCompatMetadata(model: Model<Api>): void {
|
|||
const OPENAI_GRAMMAR_TOOL_PROVIDERS = new Set([
|
||||
"openai",
|
||||
"openai-codex",
|
||||
"azure-openai-responses",
|
||||
"azure",
|
||||
"github-copilot",
|
||||
"opencode",
|
||||
"cloudflare-ai-gateway",
|
||||
|
|
@ -1121,10 +1130,12 @@ function applyThinkingLevelMetadata(model: Model<any>): void {
|
|||
model,
|
||||
model.provider === "openrouter"
|
||||
? { ...DEEPSEEK_V4_THINKING_LEVEL_MAP, xhigh: "xhigh", max: null }
|
||||
: (model.provider === "deepseek" || model.provider === "opencode" || model.provider === "opencode-go") &&
|
||||
model.id.includes("deepseek-v4-flash")
|
||||
? DEEPSEEK_V4_FLASH_THINKING_LEVEL_MAP
|
||||
: DEEPSEEK_V4_THINKING_LEVEL_MAP,
|
||||
: model.provider === "azure"
|
||||
? AZURE_DEEPSEEK_V4_THINKING_LEVEL_MAP
|
||||
: (model.provider === "deepseek" || model.provider === "opencode" || model.provider === "opencode-go") &&
|
||||
model.id.includes("deepseek-v4-flash")
|
||||
? DEEPSEEK_V4_FLASH_THINKING_LEVEL_MAP
|
||||
: DEEPSEEK_V4_THINKING_LEVEL_MAP,
|
||||
);
|
||||
}
|
||||
if (model.provider === "groq" && model.id === "qwen/qwen3.6-27b") {
|
||||
|
|
@ -3427,7 +3438,7 @@ async function generateModels() {
|
|||
.map((model) => ({
|
||||
...model,
|
||||
api: "azure-openai-responses",
|
||||
provider: "azure-openai-responses",
|
||||
provider: "azure",
|
||||
baseUrl: "",
|
||||
cost: {
|
||||
input: model.cost.input,
|
||||
|
|
@ -3439,6 +3450,28 @@ async function generateModels() {
|
|||
}));
|
||||
allModels.push(...azureOpenAiModels);
|
||||
|
||||
// Azure resells DeepSeek at its own rates. US data zone, checked 2026-09-16.
|
||||
// https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/deepseek/
|
||||
const AZURE_DEEPSEEK_V4_PRO_COST: ModelCost = { input: 1.925, output: 3.828, cacheRead: 0.165, cacheWrite: 0 };
|
||||
// Azure 400s on DeepSeek's `thinking` field and on every prompt cache parameter, discards a
|
||||
// `developer` system message unbilled once reasoning_effort is set, and honours mid-convo ones (#9645).
|
||||
const azureDeepSeekModels: Model<Api>[] = allModels
|
||||
.filter((model) => model.provider === "deepseek" && model.id === "deepseek-v4-pro")
|
||||
.map((model) => ({
|
||||
...model,
|
||||
provider: "azure",
|
||||
baseUrl: "",
|
||||
cost: AZURE_DEEPSEEK_V4_PRO_COST,
|
||||
compat: {
|
||||
...(model.compat as OpenAICompletionsCompat),
|
||||
supportsDeveloperRole: false,
|
||||
supportsMidConvoSystemMessages: true,
|
||||
thinkingFormat: "openai",
|
||||
supportsLongCacheRetention: false,
|
||||
},
|
||||
}));
|
||||
allModels.push(...azureDeepSeekModels);
|
||||
|
||||
for (const model of allModels) {
|
||||
applyOpenAICompletionsCompatMetadata(model);
|
||||
applyAnthropicMessagesCompatMetadata(model);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue