@openclaw/kimi-provider 0.0.0 → 2026.6.9-beta.1
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.
- package/README.md +11 -2
- package/dist/api.js +3 -0
- package/dist/index.js +98 -0
- package/dist/onboard.js +34 -0
- package/dist/provider-catalog.js +46 -0
- package/dist/replay-policy.js +4 -0
- package/dist/stream.js +261 -0
- package/npm-shrinkwrap.json +12 -0
- package/openclaw.plugin.json +72 -0
- package/package.json +44 -8
package/README.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OpenClaw Kimi Coding Provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official OpenClaw provider plugin for Kimi Coding.
|
|
4
|
+
|
|
5
|
+
Install from OpenClaw:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
openclaw plugins install @openclaw/kimi-provider
|
|
9
|
+
openclaw gateway restart
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
See <https://docs.openclaw.ai/providers/moonshot> for setup and configuration.
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { KIMI_CODING_BASE_URL, KIMI_CODING_DEFAULT_MODEL_ID, KIMI_CODING_LEGACY_MODEL_IDS, buildKimiCodingProvider, normalizeKimiCodingModelId } from "./provider-catalog.js";
|
|
2
|
+
import { KIMI_CODING_MODEL_REF, KIMI_MODEL_REF } from "./onboard.js";
|
|
3
|
+
export { KIMI_CODING_BASE_URL, KIMI_CODING_DEFAULT_MODEL_ID, KIMI_CODING_LEGACY_MODEL_IDS, KIMI_CODING_MODEL_REF, KIMI_MODEL_REF, buildKimiCodingProvider, normalizeKimiCodingModelId };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { buildKimiCodingProvider, normalizeKimiCodingModelId } from "./provider-catalog.js";
|
|
2
|
+
import { KIMI_CODING_MODEL_REF, applyKimiCodeConfig } from "./onboard.js";
|
|
3
|
+
import { KIMI_REPLAY_POLICY } from "./replay-policy.js";
|
|
4
|
+
import { wrapKimiProviderStream } from "./stream.js";
|
|
5
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
6
|
+
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
|
7
|
+
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
|
|
8
|
+
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
|
+
//#region extensions/kimi-coding/index.ts
|
|
10
|
+
const PLUGIN_ID = "kimi";
|
|
11
|
+
const PROVIDER_ID = "kimi";
|
|
12
|
+
function findExplicitProviderConfig(providers, providerId) {
|
|
13
|
+
if (!providers) return;
|
|
14
|
+
const normalizedProviderId = normalizeProviderId(providerId);
|
|
15
|
+
const match = Object.entries(providers).find(([configuredProviderId]) => normalizeProviderId(configuredProviderId) === normalizedProviderId);
|
|
16
|
+
return isRecord(match?.[1]) ? match[1] : void 0;
|
|
17
|
+
}
|
|
18
|
+
var kimi_coding_default = definePluginEntry({
|
|
19
|
+
id: PLUGIN_ID,
|
|
20
|
+
name: "Kimi Provider",
|
|
21
|
+
description: "Bundled Kimi provider plugin",
|
|
22
|
+
register(api) {
|
|
23
|
+
api.registerProvider({
|
|
24
|
+
id: PROVIDER_ID,
|
|
25
|
+
label: "Kimi",
|
|
26
|
+
aliases: ["kimi-code", "kimi-coding"],
|
|
27
|
+
docsPath: "/providers/moonshot",
|
|
28
|
+
envVars: ["KIMI_API_KEY", "KIMICODE_API_KEY"],
|
|
29
|
+
auth: [createProviderApiKeyAuthMethod({
|
|
30
|
+
providerId: PROVIDER_ID,
|
|
31
|
+
methodId: "api-key",
|
|
32
|
+
label: "Kimi Code API key (subscription)",
|
|
33
|
+
hint: "Kimi K2.6 + Kimi",
|
|
34
|
+
optionKey: "kimiCodeApiKey",
|
|
35
|
+
flagName: "--kimi-code-api-key",
|
|
36
|
+
envVar: "KIMI_API_KEY",
|
|
37
|
+
promptMessage: "Enter Kimi API key",
|
|
38
|
+
defaultModel: KIMI_CODING_MODEL_REF,
|
|
39
|
+
expectedProviders: [
|
|
40
|
+
"kimi",
|
|
41
|
+
"kimi-code",
|
|
42
|
+
"kimi-coding"
|
|
43
|
+
],
|
|
44
|
+
applyConfig: (cfg) => applyKimiCodeConfig(cfg),
|
|
45
|
+
noteMessage: ["Kimi uses a dedicated coding endpoint and API key.", "Get your API key at: https://www.kimi.com/code/en"].join("\n"),
|
|
46
|
+
noteTitle: "Kimi",
|
|
47
|
+
wizard: {
|
|
48
|
+
choiceId: "kimi-code-api-key",
|
|
49
|
+
choiceLabel: "Kimi Code API key (subscription)",
|
|
50
|
+
groupId: "moonshot",
|
|
51
|
+
groupLabel: "Moonshot AI (Kimi K2.6)",
|
|
52
|
+
groupHint: "Kimi K2.6"
|
|
53
|
+
}
|
|
54
|
+
})],
|
|
55
|
+
catalog: {
|
|
56
|
+
order: "simple",
|
|
57
|
+
run: async (ctx) => {
|
|
58
|
+
const apiKey = ctx.resolveProviderApiKey(PROVIDER_ID).apiKey;
|
|
59
|
+
if (!apiKey) return null;
|
|
60
|
+
const explicitProvider = findExplicitProviderConfig(ctx.config.models?.providers, PROVIDER_ID);
|
|
61
|
+
const builtInProvider = buildKimiCodingProvider();
|
|
62
|
+
const explicitBaseUrl = normalizeOptionalString(explicitProvider?.baseUrl) ?? "";
|
|
63
|
+
const explicitHeaders = isRecord(explicitProvider?.headers) ? explicitProvider.headers : void 0;
|
|
64
|
+
return { provider: {
|
|
65
|
+
...builtInProvider,
|
|
66
|
+
...explicitBaseUrl ? { baseUrl: explicitBaseUrl } : {},
|
|
67
|
+
...explicitHeaders ? { headers: {
|
|
68
|
+
...builtInProvider.headers,
|
|
69
|
+
...explicitHeaders
|
|
70
|
+
} } : {},
|
|
71
|
+
apiKey
|
|
72
|
+
} };
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
buildReplayPolicy: () => KIMI_REPLAY_POLICY,
|
|
76
|
+
normalizeResolvedModel: ({ model }) => {
|
|
77
|
+
const normalizedId = normalizeKimiCodingModelId(model.id);
|
|
78
|
+
return normalizedId === model.id ? void 0 : {
|
|
79
|
+
...model,
|
|
80
|
+
id: normalizedId
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
resolveThinkingProfile: () => ({
|
|
84
|
+
levels: [{
|
|
85
|
+
id: "off",
|
|
86
|
+
label: "off"
|
|
87
|
+
}, {
|
|
88
|
+
id: "low",
|
|
89
|
+
label: "on"
|
|
90
|
+
}],
|
|
91
|
+
defaultLevel: "off"
|
|
92
|
+
}),
|
|
93
|
+
wrapStreamFn: wrapKimiProviderStream
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
//#endregion
|
|
98
|
+
export { kimi_coding_default as default };
|
package/dist/onboard.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { KIMI_CODING_BASE_URL, KIMI_CODING_DEFAULT_MODEL_ID, buildKimiCodingProvider } from "./provider-catalog.js";
|
|
2
|
+
import { createDefaultModelPresetAppliers } from "openclaw/plugin-sdk/provider-onboard";
|
|
3
|
+
//#region extensions/kimi-coding/onboard.ts
|
|
4
|
+
const KIMI_MODEL_REF = `kimi/${KIMI_CODING_DEFAULT_MODEL_ID}`;
|
|
5
|
+
const KIMI_CODING_MODEL_REF = KIMI_MODEL_REF;
|
|
6
|
+
function resolveKimiCodingDefaultModel() {
|
|
7
|
+
return buildKimiCodingProvider().models[0];
|
|
8
|
+
}
|
|
9
|
+
const kimiCodingPresetAppliers = createDefaultModelPresetAppliers({
|
|
10
|
+
primaryModelRef: KIMI_MODEL_REF,
|
|
11
|
+
resolveParams: (_cfg) => {
|
|
12
|
+
const defaultModel = resolveKimiCodingDefaultModel();
|
|
13
|
+
if (!defaultModel) return null;
|
|
14
|
+
return {
|
|
15
|
+
providerId: "kimi",
|
|
16
|
+
api: "anthropic-messages",
|
|
17
|
+
baseUrl: KIMI_CODING_BASE_URL,
|
|
18
|
+
defaultModel,
|
|
19
|
+
defaultModelId: KIMI_CODING_DEFAULT_MODEL_ID,
|
|
20
|
+
aliases: [{
|
|
21
|
+
modelRef: KIMI_MODEL_REF,
|
|
22
|
+
alias: "Kimi"
|
|
23
|
+
}]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
function applyKimiCodeProviderConfig(cfg) {
|
|
28
|
+
return kimiCodingPresetAppliers.applyProviderConfig(cfg);
|
|
29
|
+
}
|
|
30
|
+
function applyKimiCodeConfig(cfg) {
|
|
31
|
+
return kimiCodingPresetAppliers.applyConfig(cfg);
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { KIMI_CODING_MODEL_REF, KIMI_MODEL_REF, applyKimiCodeConfig, applyKimiCodeProviderConfig };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region extensions/kimi-coding/provider-catalog.ts
|
|
2
|
+
const KIMI_BASE_URL = "https://api.kimi.com/coding/";
|
|
3
|
+
const KIMI_CODING_USER_AGENT = "claude-code/0.1.0";
|
|
4
|
+
const KIMI_DEFAULT_MODEL_ID = "kimi-for-coding";
|
|
5
|
+
const KIMI_LEGACY_MODEL_IDS = ["kimi-code", "k2p5"];
|
|
6
|
+
const KIMI_CODING_DEFAULT_CONTEXT_WINDOW = 262144;
|
|
7
|
+
const KIMI_CODING_DEFAULT_MAX_TOKENS = 32768;
|
|
8
|
+
const KIMI_CODING_DEFAULT_COST = {
|
|
9
|
+
input: 0,
|
|
10
|
+
output: 0,
|
|
11
|
+
cacheRead: 0,
|
|
12
|
+
cacheWrite: 0
|
|
13
|
+
};
|
|
14
|
+
const KIMI_CODING_INPUT = ["text", "image"];
|
|
15
|
+
function buildKimiCodingProvider() {
|
|
16
|
+
return {
|
|
17
|
+
baseUrl: KIMI_BASE_URL,
|
|
18
|
+
api: "anthropic-messages",
|
|
19
|
+
headers: { "User-Agent": KIMI_CODING_USER_AGENT },
|
|
20
|
+
models: [{
|
|
21
|
+
id: KIMI_DEFAULT_MODEL_ID,
|
|
22
|
+
name: "Kimi Code",
|
|
23
|
+
reasoning: true,
|
|
24
|
+
input: [...KIMI_CODING_INPUT],
|
|
25
|
+
cost: KIMI_CODING_DEFAULT_COST,
|
|
26
|
+
contextWindow: KIMI_CODING_DEFAULT_CONTEXT_WINDOW,
|
|
27
|
+
maxTokens: KIMI_CODING_DEFAULT_MAX_TOKENS
|
|
28
|
+
}, ...KIMI_LEGACY_MODEL_IDS.map((id) => ({
|
|
29
|
+
id,
|
|
30
|
+
name: `Kimi Code (legacy ${id})`,
|
|
31
|
+
reasoning: true,
|
|
32
|
+
input: [...KIMI_CODING_INPUT],
|
|
33
|
+
cost: KIMI_CODING_DEFAULT_COST,
|
|
34
|
+
contextWindow: KIMI_CODING_DEFAULT_CONTEXT_WINDOW,
|
|
35
|
+
maxTokens: KIMI_CODING_DEFAULT_MAX_TOKENS
|
|
36
|
+
}))]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function normalizeKimiCodingModelId(modelId) {
|
|
40
|
+
return KIMI_LEGACY_MODEL_IDS.includes(modelId) ? KIMI_DEFAULT_MODEL_ID : modelId;
|
|
41
|
+
}
|
|
42
|
+
const KIMI_CODING_BASE_URL = KIMI_BASE_URL;
|
|
43
|
+
const KIMI_CODING_DEFAULT_MODEL_ID = KIMI_DEFAULT_MODEL_ID;
|
|
44
|
+
const KIMI_CODING_LEGACY_MODEL_IDS = KIMI_LEGACY_MODEL_IDS;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { KIMI_CODING_BASE_URL, KIMI_CODING_DEFAULT_MODEL_ID, KIMI_CODING_LEGACY_MODEL_IDS, buildKimiCodingProvider, normalizeKimiCodingModelId };
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { streamSimple } from "openclaw/plugin-sdk/llm";
|
|
3
|
+
import { streamWithPayloadPatch } from "openclaw/plugin-sdk/provider-stream-shared";
|
|
4
|
+
//#region extensions/kimi-coding/stream.ts
|
|
5
|
+
const TOOL_CALLS_SECTION_BEGIN = "<|tool_calls_section_begin|>";
|
|
6
|
+
const TOOL_CALLS_SECTION_END = "<|tool_calls_section_end|>";
|
|
7
|
+
const TOOL_CALL_BEGIN = "<|tool_call_begin|>";
|
|
8
|
+
const TOOL_CALL_ARGUMENT_BEGIN = "<|tool_call_argument_begin|>";
|
|
9
|
+
const TOOL_CALL_END = "<|tool_call_end|>";
|
|
10
|
+
const KIMI_ANTHROPIC_THINKING_BUDGETS = {
|
|
11
|
+
minimal: 1024,
|
|
12
|
+
low: 1024,
|
|
13
|
+
medium: 4096,
|
|
14
|
+
high: 8192,
|
|
15
|
+
adaptive: 8192,
|
|
16
|
+
xhigh: 8192,
|
|
17
|
+
max: 8192
|
|
18
|
+
};
|
|
19
|
+
const KIMI_ANTHROPIC_VISIBLE_OUTPUT_RESERVE_TOKENS = 1024;
|
|
20
|
+
const KIMI_ANTHROPIC_MIN_OUTPUT_TOKENS = 16e3;
|
|
21
|
+
function normalizeKimiThinkingBudgetTokens(value) {
|
|
22
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return;
|
|
23
|
+
const normalized = Math.floor(value);
|
|
24
|
+
return normalized >= 1024 ? normalized : void 0;
|
|
25
|
+
}
|
|
26
|
+
function normalizeKimiAnthropicMaxTokens(value) {
|
|
27
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return;
|
|
28
|
+
const normalized = Math.floor(value);
|
|
29
|
+
return normalized > 0 ? normalized : void 0;
|
|
30
|
+
}
|
|
31
|
+
function ensureKimiAnthropicMaxTokens(payloadObj, thinkingConfig) {
|
|
32
|
+
if (thinkingConfig.type !== "enabled" || thinkingConfig.budget_tokens === void 0) return;
|
|
33
|
+
const required = Math.max(KIMI_ANTHROPIC_MIN_OUTPUT_TOKENS, thinkingConfig.budget_tokens + KIMI_ANTHROPIC_VISIBLE_OUTPUT_RESERVE_TOKENS);
|
|
34
|
+
const current = normalizeKimiAnthropicMaxTokens(payloadObj.max_tokens);
|
|
35
|
+
payloadObj.max_tokens = current === void 0 ? required : Math.max(current, required);
|
|
36
|
+
}
|
|
37
|
+
function messageHasOpenAIToolCalls(message) {
|
|
38
|
+
return Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
39
|
+
}
|
|
40
|
+
function ensureKimiOpenAIReasoningContent(payloadObj) {
|
|
41
|
+
if (!Array.isArray(payloadObj.messages)) return;
|
|
42
|
+
for (const message of payloadObj.messages) {
|
|
43
|
+
if (!message || typeof message !== "object") continue;
|
|
44
|
+
const record = message;
|
|
45
|
+
if (record.role !== "assistant" || !messageHasOpenAIToolCalls(record)) continue;
|
|
46
|
+
if (!("reasoning_content" in record)) record.reasoning_content = "";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function stripKimiOpenAIReasoningContent(payloadObj) {
|
|
50
|
+
if (!Array.isArray(payloadObj.messages)) return;
|
|
51
|
+
for (const message of payloadObj.messages) if (message && typeof message === "object") delete message.reasoning_content;
|
|
52
|
+
}
|
|
53
|
+
function normalizeKimiThinkingType(value) {
|
|
54
|
+
if (typeof value === "boolean") return value ? "enabled" : "disabled";
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
const normalized = normalizeOptionalLowercaseString(value);
|
|
57
|
+
if (!normalized) return;
|
|
58
|
+
if ([
|
|
59
|
+
"enabled",
|
|
60
|
+
"enable",
|
|
61
|
+
"on",
|
|
62
|
+
"true"
|
|
63
|
+
].includes(normalized)) return "enabled";
|
|
64
|
+
if ([
|
|
65
|
+
"disabled",
|
|
66
|
+
"disable",
|
|
67
|
+
"off",
|
|
68
|
+
"false"
|
|
69
|
+
].includes(normalized)) return "disabled";
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (value && typeof value === "object" && !Array.isArray(value)) return normalizeKimiThinkingType(value.type);
|
|
73
|
+
}
|
|
74
|
+
function normalizeKimiThinkingConfig(value) {
|
|
75
|
+
const type = normalizeKimiThinkingType(value);
|
|
76
|
+
if (!type) return;
|
|
77
|
+
if (type === "disabled") return { type: "disabled" };
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { type: "enabled" };
|
|
79
|
+
const record = value;
|
|
80
|
+
const budgetTokens = normalizeKimiThinkingBudgetTokens(record.budget_tokens ?? record.budgetTokens);
|
|
81
|
+
return budgetTokens === void 0 ? { type: "enabled" } : {
|
|
82
|
+
type: "enabled",
|
|
83
|
+
budget_tokens: budgetTokens
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function resolveKimiAnthropicThinkingBudgetTokens(thinkingLevel) {
|
|
87
|
+
if (!thinkingLevel || thinkingLevel === "off") return;
|
|
88
|
+
return KIMI_ANTHROPIC_THINKING_BUDGETS[thinkingLevel];
|
|
89
|
+
}
|
|
90
|
+
function resolveKimiThinkingConfig(params) {
|
|
91
|
+
const configured = normalizeKimiThinkingConfig(params.configuredThinking);
|
|
92
|
+
const levelBudgetTokens = resolveKimiAnthropicThinkingBudgetTokens(params.thinkingLevel);
|
|
93
|
+
if (configured) return configured.type === "enabled" && configured.budget_tokens === void 0 ? {
|
|
94
|
+
type: "enabled",
|
|
95
|
+
budget_tokens: levelBudgetTokens ?? 1024
|
|
96
|
+
} : configured;
|
|
97
|
+
if (!params.thinkingLevel || params.thinkingLevel === "off") return { type: "disabled" };
|
|
98
|
+
return levelBudgetTokens === void 0 ? { type: "enabled" } : {
|
|
99
|
+
type: "enabled",
|
|
100
|
+
budget_tokens: levelBudgetTokens
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function resolveKimiThinkingType(params) {
|
|
104
|
+
return resolveKimiThinkingConfig(params).type;
|
|
105
|
+
}
|
|
106
|
+
function stripTaggedToolCallCounter(value) {
|
|
107
|
+
return value.trim().replace(/:\d+$/, "");
|
|
108
|
+
}
|
|
109
|
+
function parseKimiTaggedToolCalls(text) {
|
|
110
|
+
const trimmed = text.trim();
|
|
111
|
+
if (!trimmed.startsWith(TOOL_CALLS_SECTION_BEGIN) || !trimmed.endsWith(TOOL_CALLS_SECTION_END)) return null;
|
|
112
|
+
let cursor = 28;
|
|
113
|
+
const sectionEndIndex = trimmed.length - 26;
|
|
114
|
+
const toolCalls = [];
|
|
115
|
+
while (cursor < sectionEndIndex) {
|
|
116
|
+
while (cursor < sectionEndIndex && /\s/.test(trimmed[cursor] ?? "")) cursor += 1;
|
|
117
|
+
if (cursor >= sectionEndIndex) break;
|
|
118
|
+
if (!trimmed.startsWith(TOOL_CALL_BEGIN, cursor)) return null;
|
|
119
|
+
const nameStart = cursor + 19;
|
|
120
|
+
const argMarkerIndex = trimmed.indexOf(TOOL_CALL_ARGUMENT_BEGIN, nameStart);
|
|
121
|
+
if (argMarkerIndex < 0 || argMarkerIndex >= sectionEndIndex) return null;
|
|
122
|
+
const rawId = trimmed.slice(nameStart, argMarkerIndex).trim();
|
|
123
|
+
if (!rawId) return null;
|
|
124
|
+
const argsStart = argMarkerIndex + 28;
|
|
125
|
+
const callEndIndex = trimmed.indexOf(TOOL_CALL_END, argsStart);
|
|
126
|
+
if (callEndIndex < 0 || callEndIndex > sectionEndIndex) return null;
|
|
127
|
+
const rawArgs = trimmed.slice(argsStart, callEndIndex).trim();
|
|
128
|
+
let parsedArgs;
|
|
129
|
+
try {
|
|
130
|
+
parsedArgs = JSON.parse(rawArgs);
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (!parsedArgs || typeof parsedArgs !== "object" || Array.isArray(parsedArgs)) return null;
|
|
135
|
+
const name = stripTaggedToolCallCounter(rawId);
|
|
136
|
+
if (!name) return null;
|
|
137
|
+
toolCalls.push({
|
|
138
|
+
type: "toolCall",
|
|
139
|
+
id: rawId,
|
|
140
|
+
name,
|
|
141
|
+
arguments: parsedArgs
|
|
142
|
+
});
|
|
143
|
+
cursor = callEndIndex + 17;
|
|
144
|
+
}
|
|
145
|
+
return toolCalls.length > 0 ? toolCalls : null;
|
|
146
|
+
}
|
|
147
|
+
function rewriteKimiTaggedToolCallsInMessage(message) {
|
|
148
|
+
if (!message || typeof message !== "object") return;
|
|
149
|
+
const content = message.content;
|
|
150
|
+
if (!Array.isArray(content)) return;
|
|
151
|
+
let changed = false;
|
|
152
|
+
const nextContent = [];
|
|
153
|
+
for (const block of content) {
|
|
154
|
+
if (!block || typeof block !== "object") {
|
|
155
|
+
nextContent.push(block);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const typedBlock = block;
|
|
159
|
+
if (typedBlock.type !== "text" || typeof typedBlock.text !== "string") {
|
|
160
|
+
nextContent.push(block);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const parsed = parseKimiTaggedToolCalls(typedBlock.text);
|
|
164
|
+
if (!parsed) {
|
|
165
|
+
nextContent.push(block);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
nextContent.push(...parsed);
|
|
169
|
+
changed = true;
|
|
170
|
+
}
|
|
171
|
+
if (!changed) return;
|
|
172
|
+
message.content = nextContent;
|
|
173
|
+
const typedMessage = message;
|
|
174
|
+
if (typedMessage.stopReason === "stop") typedMessage.stopReason = "toolUse";
|
|
175
|
+
}
|
|
176
|
+
function transformKimiStreamEvent(value, transformMessage) {
|
|
177
|
+
const event = value && typeof value === "object" ? value : void 0;
|
|
178
|
+
if (!event) return;
|
|
179
|
+
for (const message of [event.partial, event.message]) transformMessage(message);
|
|
180
|
+
}
|
|
181
|
+
function wrapStreamMessageObjects(stream, transformMessage) {
|
|
182
|
+
const readFinalMessage = stream.result.bind(stream);
|
|
183
|
+
Object.assign(stream, { async result() {
|
|
184
|
+
const message = await readFinalMessage();
|
|
185
|
+
transformMessage(message);
|
|
186
|
+
return message;
|
|
187
|
+
} });
|
|
188
|
+
const createIterator = stream[Symbol.asyncIterator].bind(stream);
|
|
189
|
+
stream[Symbol.asyncIterator] = () => {
|
|
190
|
+
const iterator = createIterator();
|
|
191
|
+
return {
|
|
192
|
+
async next() {
|
|
193
|
+
const step = await iterator.next();
|
|
194
|
+
if (!step.done) transformKimiStreamEvent(step.value, transformMessage);
|
|
195
|
+
return step;
|
|
196
|
+
},
|
|
197
|
+
async return(value) {
|
|
198
|
+
return iterator.return?.(value) ?? {
|
|
199
|
+
done: true,
|
|
200
|
+
value: void 0
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
async throw(error) {
|
|
204
|
+
return iterator.throw?.(error) ?? {
|
|
205
|
+
done: true,
|
|
206
|
+
value: void 0
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
return stream;
|
|
212
|
+
}
|
|
213
|
+
function createKimiToolCallMarkupWrapper(baseStreamFn) {
|
|
214
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
215
|
+
return (model, context, options) => {
|
|
216
|
+
const maybeStream = underlying(model, context, options);
|
|
217
|
+
if (maybeStream && typeof maybeStream === "object" && "then" in maybeStream) return Promise.resolve(maybeStream).then((stream) => wrapStreamMessageObjects(stream, rewriteKimiTaggedToolCallsInMessage));
|
|
218
|
+
return wrapStreamMessageObjects(maybeStream, rewriteKimiTaggedToolCallsInMessage);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function createKimiThinkingWrapper(baseStreamFn, thinkingConfig) {
|
|
222
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
223
|
+
return (model, context, options) => streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
|
|
224
|
+
const normalized = typeof thinkingConfig === "string" ? { type: thinkingConfig } : thinkingConfig;
|
|
225
|
+
payloadObj.thinking = model.api === "anthropic-messages" ? { ...normalized } : { type: normalized.type };
|
|
226
|
+
if (model.api === "anthropic-messages") ensureKimiAnthropicMaxTokens(payloadObj, normalized);
|
|
227
|
+
else if (normalized.type === "enabled") ensureKimiOpenAIReasoningContent(payloadObj);
|
|
228
|
+
else stripKimiOpenAIReasoningContent(payloadObj);
|
|
229
|
+
delete payloadObj.reasoning;
|
|
230
|
+
delete payloadObj.reasoning_effort;
|
|
231
|
+
delete payloadObj.reasoningEffort;
|
|
232
|
+
stripAnthropicCacheControlMarkers(payloadObj);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
function stripContentBlockCacheControl(block) {
|
|
236
|
+
if (!block || typeof block !== "object") return;
|
|
237
|
+
const record = block;
|
|
238
|
+
delete record.cache_control;
|
|
239
|
+
if (record.type === "tool_result" && Array.isArray(record.content)) for (const nestedBlock of record.content) stripContentBlockCacheControl(nestedBlock);
|
|
240
|
+
}
|
|
241
|
+
function stripContentArrayCacheControl(value) {
|
|
242
|
+
if (!Array.isArray(value)) return;
|
|
243
|
+
for (const block of value) stripContentBlockCacheControl(block);
|
|
244
|
+
}
|
|
245
|
+
function stripAnthropicCacheControlMarkers(payloadObj) {
|
|
246
|
+
stripContentArrayCacheControl(payloadObj.system);
|
|
247
|
+
if (!Array.isArray(payloadObj.messages)) return;
|
|
248
|
+
for (const message of payloadObj.messages) {
|
|
249
|
+
if (!message || typeof message !== "object") continue;
|
|
250
|
+
stripContentArrayCacheControl(message.content);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function wrapKimiProviderStream(ctx) {
|
|
254
|
+
const thinkingConfig = resolveKimiThinkingConfig({
|
|
255
|
+
configuredThinking: ctx.extraParams?.thinking,
|
|
256
|
+
thinkingLevel: ctx.thinkingLevel
|
|
257
|
+
});
|
|
258
|
+
return createKimiToolCallMarkupWrapper(createKimiThinkingWrapper(ctx.streamFn, thinkingConfig));
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
export { createKimiThinkingWrapper, createKimiToolCallMarkupWrapper, resolveKimiThinkingConfig, resolveKimiThinkingType, wrapKimiProviderStream };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "kimi",
|
|
3
|
+
"activation": {
|
|
4
|
+
"onStartup": false
|
|
5
|
+
},
|
|
6
|
+
"enabledByDefault": true,
|
|
7
|
+
"providers": ["kimi", "kimi-coding"],
|
|
8
|
+
"providerRequest": {
|
|
9
|
+
"providers": {
|
|
10
|
+
"kimi": {
|
|
11
|
+
"family": "moonshot",
|
|
12
|
+
"compatibilityFamily": "moonshot"
|
|
13
|
+
},
|
|
14
|
+
"kimi-coding": {
|
|
15
|
+
"family": "moonshot",
|
|
16
|
+
"compatibilityFamily": "moonshot"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"modelPricing": {
|
|
21
|
+
"providers": {
|
|
22
|
+
"kimi": {
|
|
23
|
+
"openRouter": {
|
|
24
|
+
"provider": "moonshotai"
|
|
25
|
+
},
|
|
26
|
+
"liteLLM": {
|
|
27
|
+
"provider": "moonshot"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"kimi-coding": {
|
|
31
|
+
"openRouter": {
|
|
32
|
+
"provider": "moonshotai"
|
|
33
|
+
},
|
|
34
|
+
"liteLLM": {
|
|
35
|
+
"provider": "moonshot"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"setup": {
|
|
41
|
+
"providers": [
|
|
42
|
+
{
|
|
43
|
+
"id": "kimi",
|
|
44
|
+
"envVars": ["KIMI_API_KEY", "KIMICODE_API_KEY"]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "kimi-coding",
|
|
48
|
+
"envVars": ["KIMI_API_KEY", "KIMICODE_API_KEY"]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"providerAuthChoices": [
|
|
53
|
+
{
|
|
54
|
+
"provider": "kimi",
|
|
55
|
+
"method": "api-key",
|
|
56
|
+
"choiceId": "kimi-code-api-key",
|
|
57
|
+
"choiceLabel": "Kimi Code API key (subscription)",
|
|
58
|
+
"groupId": "moonshot",
|
|
59
|
+
"groupLabel": "Moonshot AI (Kimi K2.6)",
|
|
60
|
+
"groupHint": "Kimi K2.6",
|
|
61
|
+
"optionKey": "kimiCodeApiKey",
|
|
62
|
+
"cliFlag": "--kimi-code-api-key",
|
|
63
|
+
"cliOption": "--kimi-code-api-key <key>",
|
|
64
|
+
"cliDescription": "Kimi Code API key (subscription)"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"configSchema": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"properties": {}
|
|
71
|
+
}
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/kimi-provider",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"license": "MIT",
|
|
3
|
+
"version": "2026.6.9-beta.1",
|
|
4
|
+
"description": "OpenClaw Kimi provider plugin.",
|
|
6
5
|
"repository": {
|
|
7
6
|
"type": "git",
|
|
8
|
-
"url": "
|
|
7
|
+
"url": "https://github.com/openclaw/openclaw"
|
|
9
8
|
},
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
9
|
+
"type": "module",
|
|
10
|
+
"openclaw": {
|
|
11
|
+
"extensions": [
|
|
12
|
+
"./index.ts"
|
|
13
|
+
],
|
|
14
|
+
"install": {
|
|
15
|
+
"clawhubSpec": "clawhub:@openclaw/kimi-provider",
|
|
16
|
+
"npmSpec": "@openclaw/kimi-provider",
|
|
17
|
+
"defaultChoice": "npm",
|
|
18
|
+
"minHostVersion": ">=2026.6.8"
|
|
19
|
+
},
|
|
20
|
+
"compat": {
|
|
21
|
+
"pluginApi": ">=2026.6.9-beta.1"
|
|
22
|
+
},
|
|
23
|
+
"build": {
|
|
24
|
+
"openclawVersion": "2026.6.9-beta.1",
|
|
25
|
+
"bundledDist": false
|
|
26
|
+
},
|
|
27
|
+
"release": {
|
|
28
|
+
"publishToClawHub": true,
|
|
29
|
+
"publishToNpm": true
|
|
30
|
+
},
|
|
31
|
+
"runtimeExtensions": [
|
|
32
|
+
"./dist/index.js"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/**",
|
|
37
|
+
"openclaw.plugin.json",
|
|
38
|
+
"npm-shrinkwrap.json",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"openclaw": ">=2026.6.9-beta.1"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"openclaw": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"bundledDependencies": []
|
|
14
50
|
}
|