feat: add alibaba-coding-plan provider support (#295)

This commit is contained in:
Kassie Povinelli 2026-03-14 10:09:54 -05:00 committed by GitHub
parent 6a39f7226b
commit c3ceb077d9
5 changed files with 155 additions and 6 deletions

View file

@ -122,6 +122,7 @@ export function getEnvApiKey(provider: any): string | undefined {
opencode: "OPENCODE_API_KEY",
"opencode-go": "OPENCODE_API_KEY",
"kimi-coding": "KIMI_API_KEY",
"alibaba-coding-plan": "ALIBABA_API_KEY",
};
const envVar = envMap[provider];

View file

@ -13384,4 +13384,142 @@ export const MODELS = {
maxTokens: 131072,
} satisfies Model<"openai-completions">,
},
"alibaba-coding-plan": {
"qwen3.5-plus": {
id: "qwen3.5-plus",
name: "Qwen3.5 Plus",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"qwen3-max-2026-01-23": {
id: "qwen3-max-2026-01-23",
name: "Qwen3 Max 2026-01-23",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: false,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 32768,
} satisfies Model<"anthropic-messages">,
"qwen3-coder-next": {
id: "qwen3-coder-next",
name: "Qwen3 Coder Next",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: false,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"qwen3-coder-plus": {
id: "qwen3-coder-plus",
name: "Qwen3 Coder Plus",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: false,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"MiniMax-M2.5": {
id: "MiniMax-M2.5",
name: "MiniMax M2.5",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 196608,
maxTokens: 24576,
} satisfies Model<"anthropic-messages">,
"glm-5": {
id: "glm-5",
name: "GLM-5",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 202752,
maxTokens: 16384,
} satisfies Model<"anthropic-messages">,
"glm-4.7": {
id: "glm-4.7",
name: "GLM-4.7",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 202752,
maxTokens: 16384,
} satisfies Model<"anthropic-messages">,
"kimi-k2.5": {
id: "kimi-k2.5",
name: "Kimi K2.5",
api: "anthropic-messages",
provider: "alibaba-coding-plan",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 32768,
} satisfies Model<"anthropic-messages">,
},
} as const;

View file

@ -452,6 +452,9 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
for (const block of output.content) delete (block as any).index;
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
if (model.provider === "alibaba-coding-plan") {
output.errorMessage = `[alibaba-coding-plan] ${output.errorMessage}`;
}
if (error instanceof Anthropic.APIError && error.headers) {
const retryAfterMs = extractRetryAfterMs(error.headers, error.message);
if (retryAfterMs !== undefined) {
@ -583,8 +586,10 @@ function createClient(
return { client, isOAuthToken: false };
}
const betaFeatures = ["fine-grained-tool-streaming-2025-05-14"];
if (needsInterleavedBeta) {
// Skip beta headers for providers that don't support them (e.g., Alibaba Coding Plan)
const skipBetaHeaders = model.provider === "alibaba-coding-plan";
const betaFeatures = skipBetaHeaders ? [] : ["fine-grained-tool-streaming-2025-05-14"];
if (needsInterleavedBeta && !skipBetaHeaders) {
betaFeatures.push("interleaved-thinking-2025-05-14");
}
@ -599,7 +604,7 @@ function createClient(
{
accept: "application/json",
"anthropic-dangerous-direct-browser-access": "true",
"anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}`,
...(betaFeatures.length > 0 ? { "anthropic-beta": `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}` } : {}),
"user-agent": `claude-cli/${claudeCodeVersion}`,
"x-app": "cli",
},
@ -612,15 +617,18 @@ function createClient(
}
// API key auth
// Alibaba Coding Plan uses Bearer token auth instead of x-api-key
const isAlibabaProvider = model.provider === "alibaba-coding-plan";
const client = new Anthropic({
apiKey,
apiKey: isAlibabaProvider ? null : apiKey,
authToken: isAlibabaProvider ? apiKey : undefined,
baseURL: model.baseUrl,
dangerouslyAllowBrowser: true,
defaultHeaders: mergeHeaders(
{
accept: "application/json",
"anthropic-dangerous-direct-browser-access": "true",
"anthropic-beta": betaFeatures.join(","),
...(betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}),
},
model.headers,
optionsHeaders,

View file

@ -39,7 +39,8 @@ export type KnownProvider =
| "huggingface"
| "opencode"
| "opencode-go"
| "kimi-coding";
| "kimi-coding"
| "alibaba-coding-plan";
export type Provider = KnownProvider | string;
export type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh";

View file

@ -35,6 +35,7 @@ export const defaultModelPerProvider: Record<KnownProvider, string> = {
opencode: "claude-opus-4-6",
"opencode-go": "kimi-k2.5",
"kimi-coding": "kimi-k2-thinking",
"alibaba-coding-plan": "qwen3.5-plus",
};
export interface ScopedModel {