pi-provider-kimi-code 0.5.0 → 0.5.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/package.json +23 -21
- package/src/constants.ts +3 -4
- package/src/oauth.ts +1 -0
- package/src/payload.ts +8 -4
- package/src/stream.ts +34 -7
- package/src/tools/moonshot.ts +39 -19
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-kimi-code",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Use Kimi Code in Pi without losing the Kimi-specific bits",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"pi-package",
|
|
7
|
-
"pi-extension",
|
|
8
|
-
"pi-provider",
|
|
9
|
-
"pi",
|
|
10
|
-
"pi-coding-agent",
|
|
11
|
-
"pi-mono",
|
|
12
|
-
"coding-agent",
|
|
13
6
|
"ai-provider",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
7
|
+
"coding-agent",
|
|
8
|
+
"device-code",
|
|
16
9
|
"kimi",
|
|
17
|
-
"kimi-for-coding",
|
|
18
10
|
"kimi-coder",
|
|
11
|
+
"kimi-for-coding",
|
|
19
12
|
"kimi-k2",
|
|
20
|
-
"kimi-k2.6",
|
|
21
13
|
"kimi-k2.5",
|
|
14
|
+
"kimi-k2.6",
|
|
15
|
+
"llm",
|
|
16
|
+
"llm-provider",
|
|
22
17
|
"moonshot",
|
|
23
18
|
"moonshotai",
|
|
24
19
|
"oauth",
|
|
25
|
-
"
|
|
20
|
+
"pi",
|
|
21
|
+
"pi-coding-agent",
|
|
22
|
+
"pi-extension",
|
|
23
|
+
"pi-mono",
|
|
24
|
+
"pi-package",
|
|
25
|
+
"pi-provider",
|
|
26
26
|
"subscription"
|
|
27
27
|
],
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Leechael/pi-provider-kimi-code/issues"
|
|
30
|
+
},
|
|
28
31
|
"license": "MIT",
|
|
32
|
+
"author": "Leechael",
|
|
29
33
|
"repository": {
|
|
30
34
|
"type": "git",
|
|
31
35
|
"url": "https://github.com/Leechael/pi-provider-kimi-code"
|
|
32
36
|
},
|
|
33
|
-
"bugs": {
|
|
34
|
-
"url": "https://github.com/Leechael/pi-provider-kimi-code/issues"
|
|
35
|
-
},
|
|
36
|
-
"author": "Leechael",
|
|
37
37
|
"files": [
|
|
38
38
|
"index.ts",
|
|
39
39
|
"src",
|
|
@@ -51,17 +51,19 @@
|
|
|
51
51
|
"format:check": "oxfmt . --check",
|
|
52
52
|
"pre-commit": "prek run"
|
|
53
53
|
},
|
|
54
|
-
"peerDependencies": {
|
|
55
|
-
"@earendil-works/pi-coding-agent": "*",
|
|
56
|
-
"@earendil-works/pi-ai": "*"
|
|
57
|
-
},
|
|
58
54
|
"devDependencies": {
|
|
55
|
+
"@earendil-works/pi-ai": "^0.78.0",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
59
57
|
"@j178/prek": "^0.3.8",
|
|
60
58
|
"@types/node": "^24.0.0",
|
|
61
59
|
"oxfmt": "^0.44.0",
|
|
62
60
|
"oxlint": "^1.59.0",
|
|
63
61
|
"typescript": "^5.9.0"
|
|
64
62
|
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"@earendil-works/pi-ai": "*",
|
|
65
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
66
|
+
},
|
|
65
67
|
"pi": {
|
|
66
68
|
"extensions": [
|
|
67
69
|
"./index.ts"
|
package/src/constants.ts
CHANGED
|
@@ -24,10 +24,9 @@ export const DEFAULT_BASE_URL = IS_OPENAI_PROTOCOL
|
|
|
24
24
|
? "https://api.kimi.com/coding/v1"
|
|
25
25
|
: "https://api.kimi.com/coding";
|
|
26
26
|
|
|
27
|
-
export const KIMI_CODE_VERSION = "0.
|
|
28
|
-
export const
|
|
29
|
-
export const
|
|
30
|
-
export const KIMI_PLATFORM = "kimi-code-cli";
|
|
27
|
+
export const KIMI_CODE_VERSION = "0.6.0";
|
|
28
|
+
export const KIMI_CODE_USER_AGENT = `kimi-code-cli/${KIMI_CODE_VERSION}`;
|
|
29
|
+
export const KIMI_PLATFORM = "kimi_code_cli";
|
|
31
30
|
|
|
32
31
|
export function getKimiCodeHome(): string {
|
|
33
32
|
const value = process.env.KIMI_CODE_HOME?.trim();
|
package/src/oauth.ts
CHANGED
|
@@ -388,6 +388,7 @@ export async function refreshKimiAuthToken(currentKey: string): Promise<string |
|
|
|
388
388
|
console.error("[kimi-coding] auth refresh: requesting new access token");
|
|
389
389
|
const refreshed = await refreshAccessToken(cred.refresh);
|
|
390
390
|
const newCred: OAuthCredential = {
|
|
391
|
+
...cred,
|
|
391
392
|
type: "oauth",
|
|
392
393
|
access: refreshed.access_token,
|
|
393
394
|
refresh: refreshed.refresh_token,
|
package/src/payload.ts
CHANGED
|
@@ -87,8 +87,7 @@ export function readEnvOverrides(): KimiEnvOverrides {
|
|
|
87
87
|
if (temp) out.temperature = parseFloat(temp);
|
|
88
88
|
const topP = process.env.KIMI_MODEL_TOP_P;
|
|
89
89
|
if (topP) out.topP = parseFloat(topP);
|
|
90
|
-
const maxCompletionTokens =
|
|
91
|
-
process.env.KIMI_MODEL_MAX_COMPLETION_TOKENS ?? process.env.KIMI_MODEL_MAX_TOKENS;
|
|
90
|
+
const maxCompletionTokens = process.env.KIMI_MODEL_MAX_COMPLETION_TOKENS;
|
|
92
91
|
if (maxCompletionTokens) out.maxCompletionTokens = parseInt(maxCompletionTokens, 10);
|
|
93
92
|
return out;
|
|
94
93
|
}
|
|
@@ -421,13 +420,18 @@ export async function applyKimiPayloadMutations(
|
|
|
421
420
|
if (resolved) payload.prompt_cache_key = resolved;
|
|
422
421
|
}
|
|
423
422
|
|
|
424
|
-
// 4.
|
|
423
|
+
// 4. Normalize deprecated max_tokens and apply env-level hyperparameter
|
|
424
|
+
// overrides (pre-parsed into numbers by caller).
|
|
425
|
+
if (payload.max_completion_tokens === undefined && typeof payload.max_tokens === "number") {
|
|
426
|
+
payload.max_completion_tokens = payload.max_tokens;
|
|
427
|
+
}
|
|
428
|
+
delete payload.max_tokens;
|
|
429
|
+
|
|
425
430
|
const { temperature, topP, maxCompletionTokens } = ctx.envOverrides;
|
|
426
431
|
if (temperature !== undefined) payload.temperature = temperature;
|
|
427
432
|
if (topP !== undefined) payload.top_p = topP;
|
|
428
433
|
if (maxCompletionTokens !== undefined) {
|
|
429
434
|
payload.max_completion_tokens = maxCompletionTokens;
|
|
430
|
-
delete payload.max_tokens;
|
|
431
435
|
}
|
|
432
436
|
|
|
433
437
|
// 5. Reasoning effort mapping.
|
package/src/stream.ts
CHANGED
|
@@ -46,24 +46,50 @@ export async function* filterEmptyResponseStream(
|
|
|
46
46
|
): AsyncIterable<AssistantMessageEvent> {
|
|
47
47
|
const suppressedIndices = new Set<number>();
|
|
48
48
|
let textBuffer: AssistantMessageEvent[] = [];
|
|
49
|
+
let bufferedText = "";
|
|
49
50
|
let bufferingIndex: number | null = null;
|
|
50
51
|
|
|
52
|
+
const suppressBufferedTextBlock = (): void => {
|
|
53
|
+
if (bufferingIndex !== null) suppressedIndices.add(bufferingIndex);
|
|
54
|
+
textBuffer = [];
|
|
55
|
+
bufferedText = "";
|
|
56
|
+
bufferingIndex = null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const flushBufferedTextBlock = async function* (): AsyncIterable<AssistantMessageEvent> {
|
|
60
|
+
for (const buffered of textBuffer) yield buffered;
|
|
61
|
+
textBuffer = [];
|
|
62
|
+
bufferedText = "";
|
|
63
|
+
bufferingIndex = null;
|
|
64
|
+
};
|
|
65
|
+
|
|
51
66
|
for await (const event of upstream) {
|
|
52
|
-
// Start buffering
|
|
67
|
+
// Start buffering only long enough to decide whether this text block is
|
|
68
|
+
// Kimi's synthetic "(Empty response: ...)" wrapper. Normal text is flushed
|
|
69
|
+
// as soon as it diverges from the marker so output still streams.
|
|
53
70
|
if (event.type === "text_start") {
|
|
54
71
|
bufferingIndex = event.contentIndex;
|
|
55
72
|
textBuffer = [event];
|
|
73
|
+
bufferedText = "";
|
|
56
74
|
continue;
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
// Accumulate text deltas + detect the empty-response marker on text_end.
|
|
60
77
|
if (
|
|
61
78
|
bufferingIndex !== null &&
|
|
62
79
|
"contentIndex" in event &&
|
|
63
80
|
event.contentIndex === bufferingIndex
|
|
64
81
|
) {
|
|
65
82
|
if (event.type === "text_delta") {
|
|
83
|
+
bufferedText += event.delta;
|
|
66
84
|
textBuffer.push(event);
|
|
85
|
+
if (bufferedText.startsWith(EMPTY_RESPONSE_PREFIX)) {
|
|
86
|
+
suppressBufferedTextBlock();
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (EMPTY_RESPONSE_PREFIX.startsWith(bufferedText)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
yield* flushBufferedTextBlock();
|
|
67
93
|
continue;
|
|
68
94
|
}
|
|
69
95
|
if (event.type === "text_end") {
|
|
@@ -72,14 +98,11 @@ export async function* filterEmptyResponseStream(
|
|
|
72
98
|
// array: it is a shared reference into session state, and mutating
|
|
73
99
|
// it would shift subsequent contentIndex values, corrupting the
|
|
74
100
|
// stream.
|
|
75
|
-
|
|
101
|
+
suppressBufferedTextBlock();
|
|
76
102
|
} else {
|
|
77
|
-
|
|
78
|
-
for (const buffered of textBuffer) yield buffered;
|
|
103
|
+
yield* flushBufferedTextBlock();
|
|
79
104
|
yield event;
|
|
80
105
|
}
|
|
81
|
-
textBuffer = [];
|
|
82
|
-
bufferingIndex = null;
|
|
83
106
|
continue;
|
|
84
107
|
}
|
|
85
108
|
}
|
|
@@ -103,6 +126,10 @@ export async function* filterEmptyResponseStream(
|
|
|
103
126
|
|
|
104
127
|
yield event;
|
|
105
128
|
}
|
|
129
|
+
|
|
130
|
+
if (bufferingIndex !== null) {
|
|
131
|
+
yield* flushBufferedTextBlock();
|
|
132
|
+
}
|
|
106
133
|
}
|
|
107
134
|
|
|
108
135
|
// =============================================================================
|
package/src/tools/moonshot.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Type, type Static } from "@earendil-works/pi-ai";
|
|
|
3
3
|
|
|
4
4
|
import { PROVIDER_ID } from "../constants.ts";
|
|
5
5
|
import { getCommonHeaders } from "../device.ts";
|
|
6
|
+
import { refreshKimiAuthToken } from "../oauth.ts";
|
|
6
7
|
|
|
7
8
|
const MOONSHOT_BASE_V1 = "https://api.kimi.com/coding/v1";
|
|
8
9
|
const MOONSHOT_TIMEOUT_MS = 180_000;
|
|
@@ -47,6 +48,7 @@ export interface MoonshotFetchResult {
|
|
|
47
48
|
interface MoonshotToolDeps {
|
|
48
49
|
fetch: typeof fetch;
|
|
49
50
|
getAccessToken: () => string | null;
|
|
51
|
+
refreshAccessToken: (currentToken: string) => Promise<string | null>;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export interface BuildMoonshotToolOptions {
|
|
@@ -68,6 +70,7 @@ function buildDeps(options: BuildMoonshotToolOptions = {}): MoonshotToolDeps {
|
|
|
68
70
|
return {
|
|
69
71
|
fetch: options.deps?.fetch ?? fetch,
|
|
70
72
|
getAccessToken: options.deps?.getAccessToken ?? defaultGetAccessToken,
|
|
73
|
+
refreshAccessToken: options.deps?.refreshAccessToken ?? refreshKimiAuthToken,
|
|
71
74
|
};
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -196,6 +199,19 @@ async function readErrorBody(response: Response): Promise<string> {
|
|
|
196
199
|
return response.text().catch(() => "");
|
|
197
200
|
}
|
|
198
201
|
|
|
202
|
+
async function fetchWithAuthRetry(
|
|
203
|
+
deps: MoonshotToolDeps,
|
|
204
|
+
accessToken: string,
|
|
205
|
+
request: (token: string) => Promise<Response>,
|
|
206
|
+
): Promise<Response> {
|
|
207
|
+
const response = await request(accessToken);
|
|
208
|
+
if (response.status !== 401) return response;
|
|
209
|
+
|
|
210
|
+
const refreshed = await deps.refreshAccessToken(accessToken);
|
|
211
|
+
if (!refreshed || refreshed === accessToken) return response;
|
|
212
|
+
return request(refreshed);
|
|
213
|
+
}
|
|
214
|
+
|
|
199
215
|
export function buildMoonshotSearchTool(options: BuildMoonshotToolOptions = {}) {
|
|
200
216
|
const deps = buildDeps(options);
|
|
201
217
|
const defaultCollapsed = options.defaultCollapsed ?? true;
|
|
@@ -219,17 +235,19 @@ export function buildMoonshotSearchTool(options: BuildMoonshotToolOptions = {})
|
|
|
219
235
|
const includeContent = params.include_content === true;
|
|
220
236
|
const timeout = buildTimeoutSignal(signal);
|
|
221
237
|
try {
|
|
222
|
-
const response = await deps
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
238
|
+
const response = await fetchWithAuthRetry(deps, accessToken, (token) =>
|
|
239
|
+
deps.fetch(`${MOONSHOT_BASE_V1}/search`, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: buildHeaders(token, toolCallId),
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
text_query: params.query,
|
|
244
|
+
limit,
|
|
245
|
+
enable_page_crawling: includeContent,
|
|
246
|
+
timeout_seconds: 30,
|
|
247
|
+
}),
|
|
248
|
+
signal: timeout.signal,
|
|
230
249
|
}),
|
|
231
|
-
|
|
232
|
-
});
|
|
250
|
+
);
|
|
233
251
|
|
|
234
252
|
if (!response.ok) {
|
|
235
253
|
const body = await readErrorBody(response);
|
|
@@ -279,15 +297,17 @@ export function buildMoonshotFetchTool(options: BuildMoonshotToolOptions = {}) {
|
|
|
279
297
|
|
|
280
298
|
const timeout = buildTimeoutSignal(signal);
|
|
281
299
|
try {
|
|
282
|
-
const response = await deps
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
300
|
+
const response = await fetchWithAuthRetry(deps, accessToken, (token) =>
|
|
301
|
+
deps.fetch(`${MOONSHOT_BASE_V1}/fetch`, {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: {
|
|
304
|
+
...buildHeaders(token, toolCallId),
|
|
305
|
+
Accept: "text/markdown",
|
|
306
|
+
},
|
|
307
|
+
body: JSON.stringify({ url: params.url }),
|
|
308
|
+
signal: timeout.signal,
|
|
309
|
+
}),
|
|
310
|
+
);
|
|
291
311
|
|
|
292
312
|
if (!response.ok) {
|
|
293
313
|
const body = await readErrorBody(response);
|