mirror of https://github.com/openclaw/openclaw.git
Kimi Coding: set default subscription user agent (#44248)
* Providers: set default Kimi coding user agent * Tests: cover Kimi coding header overrides * Changelog: note Kimi coding user agent * Tests: satisfy Kimi provider fixture type * Update CHANGELOG.md * Providers: preserve Kimi headers through models merge
This commit is contained in:
parent
33ba3ce951
commit
86135d5889
|
|
@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
|||
|
||||
### Fixes
|
||||
|
||||
- Models/Kimi Coding: send the built-in `User-Agent: claude-code/0.1.0` header by default for `kimi-coding` while still allowing explicit provider headers to override it, so Kimi Code subscription auth can work without a local header-injection proxy. (#30099) Thanks @Amineelfarssi and @vincentkoc.
|
||||
- Security/plugins: disable implicit workspace plugin auto-load so cloned repositories cannot execute workspace plugin code without an explicit trust decision. (`GHSA-99qw-6mr3-36qr`)(#44174) Thanks @lintsinghua and @vincentkoc.
|
||||
- Moonshot CN API: respect explicit `baseUrl` (api.moonshot.cn) in implicit provider resolution so platform.moonshot.cn API keys authenticate correctly instead of returning HTTP 401. (#33637) Thanks @chengzhichao-xydt.
|
||||
- Kimi Coding/provider config: respect explicit `models.providers["kimi-coding"].baseUrl` when resolving the implicit provider so custom Kimi Coding endpoints no longer get overwritten by the built-in default. (#36353) Thanks @2233admin.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,40 @@ describe("models-config merge helpers", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("preserves implicit provider headers when explicit config adds extra headers", () => {
|
||||
const merged = mergeProviderModels(
|
||||
{
|
||||
api: "anthropic-messages",
|
||||
headers: { "User-Agent": "claude-code/0.1.0" },
|
||||
models: [
|
||||
{
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
},
|
||||
],
|
||||
} as ProviderConfig,
|
||||
{
|
||||
api: "anthropic-messages",
|
||||
headers: { "X-Kimi-Tenant": "tenant-a" },
|
||||
models: [
|
||||
{
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
},
|
||||
],
|
||||
} as ProviderConfig,
|
||||
);
|
||||
|
||||
expect(merged.headers).toEqual({
|
||||
"User-Agent": "claude-code/0.1.0",
|
||||
"X-Kimi-Tenant": "tenant-a",
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces stale baseUrl when model api surface changes", () => {
|
||||
const merged = mergeWithExistingProviderSecrets({
|
||||
nextProviders: {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,27 @@ export function mergeProviderModels(
|
|||
): ProviderConfig {
|
||||
const implicitModels = Array.isArray(implicit.models) ? implicit.models : [];
|
||||
const explicitModels = Array.isArray(explicit.models) ? explicit.models : [];
|
||||
const implicitHeaders =
|
||||
implicit.headers && typeof implicit.headers === "object" && !Array.isArray(implicit.headers)
|
||||
? implicit.headers
|
||||
: undefined;
|
||||
const explicitHeaders =
|
||||
explicit.headers && typeof explicit.headers === "object" && !Array.isArray(explicit.headers)
|
||||
? explicit.headers
|
||||
: undefined;
|
||||
if (implicitModels.length === 0) {
|
||||
return { ...implicit, ...explicit };
|
||||
return {
|
||||
...implicit,
|
||||
...explicit,
|
||||
...(implicitHeaders || explicitHeaders
|
||||
? {
|
||||
headers: {
|
||||
...implicitHeaders,
|
||||
...explicitHeaders,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
const implicitById = new Map(
|
||||
|
|
@ -93,6 +112,14 @@ export function mergeProviderModels(
|
|||
return {
|
||||
...implicit,
|
||||
...explicit,
|
||||
...(implicitHeaders || explicitHeaders
|
||||
? {
|
||||
headers: {
|
||||
...implicitHeaders,
|
||||
...explicitHeaders,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
models: mergedModels,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ describe("kimi-coding implicit provider (#22409)", () => {
|
|||
const provider = buildKimiCodingProvider();
|
||||
expect(provider.api).toBe("anthropic-messages");
|
||||
expect(provider.baseUrl).toBe("https://api.kimi.com/coding/");
|
||||
expect(provider.headers).toEqual({ "User-Agent": "claude-code/0.1.0" });
|
||||
expect(provider.models).toBeDefined();
|
||||
expect(provider.models.length).toBeGreaterThan(0);
|
||||
expect(provider.models[0].id).toBe("k2p5");
|
||||
|
|
@ -65,4 +66,33 @@ describe("kimi-coding implicit provider (#22409)", () => {
|
|||
envSnapshot.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it("merges explicit kimi-coding headers on top of the built-in user agent", async () => {
|
||||
const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-"));
|
||||
const envSnapshot = captureEnv(["KIMI_API_KEY"]);
|
||||
process.env.KIMI_API_KEY = "test-key";
|
||||
|
||||
try {
|
||||
const providers = await resolveImplicitProvidersForTest({
|
||||
agentDir,
|
||||
explicitProviders: {
|
||||
"kimi-coding": {
|
||||
baseUrl: "https://api.kimi.com/coding/",
|
||||
api: "anthropic-messages",
|
||||
headers: {
|
||||
"User-Agent": "custom-kimi-client/1.0",
|
||||
"X-Kimi-Tenant": "tenant-a",
|
||||
},
|
||||
models: buildKimiCodingProvider().models,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(providers?.["kimi-coding"]?.headers).toEqual({
|
||||
"User-Agent": "custom-kimi-client/1.0",
|
||||
"X-Kimi-Tenant": "tenant-a",
|
||||
});
|
||||
} finally {
|
||||
envSnapshot.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ const MOONSHOT_DEFAULT_COST = {
|
|||
};
|
||||
|
||||
const KIMI_CODING_BASE_URL = "https://api.kimi.com/coding/";
|
||||
const KIMI_CODING_USER_AGENT = "claude-code/0.1.0";
|
||||
const KIMI_CODING_DEFAULT_MODEL_ID = "k2p5";
|
||||
const KIMI_CODING_DEFAULT_CONTEXT_WINDOW = 262144;
|
||||
const KIMI_CODING_DEFAULT_MAX_TOKENS = 32768;
|
||||
|
|
@ -308,6 +309,9 @@ export function buildKimiCodingProvider(): ProviderConfig {
|
|||
return {
|
||||
baseUrl: KIMI_CODING_BASE_URL,
|
||||
api: "anthropic-messages",
|
||||
headers: {
|
||||
"User-Agent": KIMI_CODING_USER_AGENT,
|
||||
},
|
||||
models: [
|
||||
{
|
||||
id: KIMI_CODING_DEFAULT_MODEL_ID,
|
||||
|
|
|
|||
|
|
@ -667,12 +667,24 @@ const SIMPLE_IMPLICIT_PROVIDER_LOADERS: ImplicitProviderLoader[] = [
|
|||
};
|
||||
}),
|
||||
withApiKey("kimi-coding", async ({ apiKey, explicitProvider }) => {
|
||||
const builtInProvider = buildKimiCodingProvider();
|
||||
const explicitBaseUrl = explicitProvider?.baseUrl;
|
||||
const explicitHeaders = isRecord(explicitProvider?.headers)
|
||||
? (explicitProvider.headers as ProviderConfig["headers"])
|
||||
: undefined;
|
||||
return {
|
||||
...buildKimiCodingProvider(),
|
||||
...builtInProvider,
|
||||
...(typeof explicitBaseUrl === "string" && explicitBaseUrl.trim()
|
||||
? { baseUrl: explicitBaseUrl.trim() }
|
||||
: {}),
|
||||
...(explicitHeaders
|
||||
? {
|
||||
headers: {
|
||||
...builtInProvider.headers,
|
||||
...explicitHeaders,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
apiKey,
|
||||
};
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -915,6 +915,43 @@ describe("resolveModel", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("lets provider config override registry-found kimi user agent headers", () => {
|
||||
mockDiscoveredModel({
|
||||
provider: "kimi-coding",
|
||||
modelId: "k2p5",
|
||||
templateModel: {
|
||||
...buildForwardCompatTemplate({
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
provider: "kimi-coding",
|
||||
api: "anthropic-messages",
|
||||
baseUrl: "https://api.kimi.com/coding/",
|
||||
}),
|
||||
headers: { "User-Agent": "claude-code/0.1.0" },
|
||||
},
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
models: {
|
||||
providers: {
|
||||
"kimi-coding": {
|
||||
headers: {
|
||||
"User-Agent": "custom-kimi-client/1.0",
|
||||
"X-Kimi-Tenant": "tenant-a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
const result = resolveModel("kimi-coding", "k2p5", "/tmp/agent", cfg);
|
||||
expect(result.error).toBeUndefined();
|
||||
expect((result.model as unknown as { headers?: Record<string, string> }).headers).toEqual({
|
||||
"User-Agent": "custom-kimi-client/1.0",
|
||||
"X-Kimi-Tenant": "tenant-a",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not override when no provider config exists", () => {
|
||||
mockDiscoveredModel({
|
||||
provider: "anthropic",
|
||||
|
|
|
|||
Loading…
Reference in New Issue