@kenkaiiii/gg-ai 4.4.0 → 4.5.0
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/dist/index.cjs +150 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +150 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -23,6 +23,11 @@ interface VideoContent {
|
|
|
23
23
|
type: "video";
|
|
24
24
|
mediaType: string;
|
|
25
25
|
data: string;
|
|
26
|
+
/** Moonshot/Kimi file id (e.g. "d4f0…") after uploading via the files API.
|
|
27
|
+
* Moonshot rejects inline base64 video; the provider uploads the clip once
|
|
28
|
+
* and caches the id here so later turns reference `ms://<fileId>` instead of
|
|
29
|
+
* re-sending the bytes. */
|
|
30
|
+
fileId?: string;
|
|
26
31
|
}
|
|
27
32
|
interface ToolCall {
|
|
28
33
|
type: "tool_call";
|
|
@@ -30,7 +35,7 @@ interface ToolCall {
|
|
|
30
35
|
name: string;
|
|
31
36
|
args: Record<string, unknown>;
|
|
32
37
|
}
|
|
33
|
-
type ToolResultContent = string | (TextContent | ImageContent)[];
|
|
38
|
+
type ToolResultContent = string | (TextContent | ImageContent | VideoContent)[];
|
|
34
39
|
interface ToolResult {
|
|
35
40
|
type: "tool_result";
|
|
36
41
|
toolCallId: string;
|
|
@@ -208,6 +213,11 @@ interface StreamOptions {
|
|
|
208
213
|
* version should pass it here. Ignored for non-Anthropic providers and for
|
|
209
214
|
* Anthropic requests using a regular API key. */
|
|
210
215
|
userAgent?: string;
|
|
216
|
+
/** Extra HTTP headers attached to every model request. Used by providers
|
|
217
|
+
* whose endpoint gates on client identity (e.g. Kimi For Coding requires a
|
|
218
|
+
* `User-Agent: kimi-code-cli/...` and `X-Msh-*` device headers). Merged
|
|
219
|
+
* into the underlying SDK's default headers. */
|
|
220
|
+
defaultHeaders?: Record<string, string>;
|
|
211
221
|
}
|
|
212
222
|
|
|
213
223
|
/**
|
|
@@ -330,7 +340,7 @@ declare const providerRegistry: ProviderRegistryImpl;
|
|
|
330
340
|
* Cannot read property 'foo' of undefined
|
|
331
341
|
* → This is a ggcoder bug — please report it.
|
|
332
342
|
*/
|
|
333
|
-
type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
|
|
343
|
+
type ErrorSource = "provider" | "ggcoder" | "network" | "auth" | "capability";
|
|
334
344
|
interface FormattedError {
|
|
335
345
|
/** Plain-English headline, e.g. "OpenAI returned an error." */
|
|
336
346
|
headline: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,11 @@ interface VideoContent {
|
|
|
23
23
|
type: "video";
|
|
24
24
|
mediaType: string;
|
|
25
25
|
data: string;
|
|
26
|
+
/** Moonshot/Kimi file id (e.g. "d4f0…") after uploading via the files API.
|
|
27
|
+
* Moonshot rejects inline base64 video; the provider uploads the clip once
|
|
28
|
+
* and caches the id here so later turns reference `ms://<fileId>` instead of
|
|
29
|
+
* re-sending the bytes. */
|
|
30
|
+
fileId?: string;
|
|
26
31
|
}
|
|
27
32
|
interface ToolCall {
|
|
28
33
|
type: "tool_call";
|
|
@@ -30,7 +35,7 @@ interface ToolCall {
|
|
|
30
35
|
name: string;
|
|
31
36
|
args: Record<string, unknown>;
|
|
32
37
|
}
|
|
33
|
-
type ToolResultContent = string | (TextContent | ImageContent)[];
|
|
38
|
+
type ToolResultContent = string | (TextContent | ImageContent | VideoContent)[];
|
|
34
39
|
interface ToolResult {
|
|
35
40
|
type: "tool_result";
|
|
36
41
|
toolCallId: string;
|
|
@@ -208,6 +213,11 @@ interface StreamOptions {
|
|
|
208
213
|
* version should pass it here. Ignored for non-Anthropic providers and for
|
|
209
214
|
* Anthropic requests using a regular API key. */
|
|
210
215
|
userAgent?: string;
|
|
216
|
+
/** Extra HTTP headers attached to every model request. Used by providers
|
|
217
|
+
* whose endpoint gates on client identity (e.g. Kimi For Coding requires a
|
|
218
|
+
* `User-Agent: kimi-code-cli/...` and `X-Msh-*` device headers). Merged
|
|
219
|
+
* into the underlying SDK's default headers. */
|
|
220
|
+
defaultHeaders?: Record<string, string>;
|
|
211
221
|
}
|
|
212
222
|
|
|
213
223
|
/**
|
|
@@ -330,7 +340,7 @@ declare const providerRegistry: ProviderRegistryImpl;
|
|
|
330
340
|
* Cannot read property 'foo' of undefined
|
|
331
341
|
* → This is a ggcoder bug — please report it.
|
|
332
342
|
*/
|
|
333
|
-
type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
|
|
343
|
+
type ErrorSource = "provider" | "ggcoder" | "network" | "auth" | "capability";
|
|
334
344
|
interface FormattedError {
|
|
335
345
|
/** Plain-English headline, e.g. "OpenAI returned an error." */
|
|
336
346
|
headline: string;
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,12 @@ var GGAIError = class extends Error {
|
|
|
25
25
|
this.hint = options?.hint;
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
|
+
var VideoUnsupportedError = class extends GGAIError {
|
|
29
|
+
constructor() {
|
|
30
|
+
super("This model can't analyze video.", { source: "capability" });
|
|
31
|
+
this.name = "VideoUnsupportedError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
28
34
|
var ProviderError = class extends GGAIError {
|
|
29
35
|
provider;
|
|
30
36
|
statusCode;
|
|
@@ -140,6 +146,14 @@ function finaliseBySource(source, message, requestId, hint) {
|
|
|
140
146
|
guidance: hint ?? providerGuidance(void 0, message, void 0),
|
|
141
147
|
...requestId ? { requestId } : {}
|
|
142
148
|
};
|
|
149
|
+
case "capability":
|
|
150
|
+
return {
|
|
151
|
+
headline: message,
|
|
152
|
+
source,
|
|
153
|
+
message: "",
|
|
154
|
+
guidance: hint ?? "Only Kimi, Gemini, and MiniMax can analyze video. Switch with /model.",
|
|
155
|
+
...requestId ? { requestId } : {}
|
|
156
|
+
};
|
|
143
157
|
case "ggcoder":
|
|
144
158
|
return {
|
|
145
159
|
headline: "ggcoder hit an unexpected error.",
|
|
@@ -500,6 +514,10 @@ function toolResultImages(content) {
|
|
|
500
514
|
if (typeof content === "string") return [];
|
|
501
515
|
return content.filter((b) => b.type === "image");
|
|
502
516
|
}
|
|
517
|
+
function toolResultVideos(content) {
|
|
518
|
+
if (typeof content === "string") return [];
|
|
519
|
+
return content.filter((b) => b.type === "video");
|
|
520
|
+
}
|
|
503
521
|
function toAnthropicCacheControl(retention, baseUrl) {
|
|
504
522
|
const resolved = retention ?? "short";
|
|
505
523
|
if (resolved === "none") return void 0;
|
|
@@ -510,6 +528,12 @@ function toAnthropicToolResultContent(content) {
|
|
|
510
528
|
if (typeof content === "string") return content;
|
|
511
529
|
return content.map((block) => {
|
|
512
530
|
if (block.type === "text") return { type: "text", text: block.text };
|
|
531
|
+
if (block.type === "video") {
|
|
532
|
+
return {
|
|
533
|
+
type: "video",
|
|
534
|
+
source: { type: "base64", media_type: block.mediaType, data: block.data }
|
|
535
|
+
};
|
|
536
|
+
}
|
|
513
537
|
return {
|
|
514
538
|
type: "image",
|
|
515
539
|
source: {
|
|
@@ -579,6 +603,8 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
579
603
|
if (msg.role === "tool") {
|
|
580
604
|
out.push({
|
|
581
605
|
role: "user",
|
|
606
|
+
// Cast covers the video block (used by the Anthropic-compatible MiniMax
|
|
607
|
+
// API), which isn't in the first-party Anthropic tool_result types.
|
|
582
608
|
content: msg.content.map((result) => ({
|
|
583
609
|
type: "tool_result",
|
|
584
610
|
tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
|
|
@@ -719,11 +745,10 @@ function toOpenAIMessages(messages, options) {
|
|
|
719
745
|
(part) => {
|
|
720
746
|
if (part.type === "text") return { type: "text", text: part.text };
|
|
721
747
|
if (part.type === "video") {
|
|
748
|
+
const videoUrl = part.fileId ? { url: `ms://${part.fileId}`, id: part.fileId } : { url: `data:${part.mediaType};base64,${part.data}` };
|
|
722
749
|
return {
|
|
723
750
|
type: "video_url",
|
|
724
|
-
video_url:
|
|
725
|
-
url: `data:${part.mediaType};base64,${part.data}`
|
|
726
|
-
}
|
|
751
|
+
video_url: videoUrl
|
|
727
752
|
};
|
|
728
753
|
}
|
|
729
754
|
return {
|
|
@@ -768,11 +793,27 @@ function toOpenAIMessages(messages, options) {
|
|
|
768
793
|
continue;
|
|
769
794
|
}
|
|
770
795
|
if (msg.role === "tool") {
|
|
796
|
+
const isMoonshot = options?.provider === "moonshot";
|
|
771
797
|
const imageBlocks = [];
|
|
772
798
|
for (const result of msg.content) {
|
|
773
799
|
const text = toolResultText(result.content);
|
|
774
800
|
const images = toolResultImages(result.content);
|
|
801
|
+
const videos = isMoonshot ? toolResultVideos(result.content) : [];
|
|
775
802
|
const hasText = text.length > 0;
|
|
803
|
+
if (videos.length > 0) {
|
|
804
|
+
const parts = [];
|
|
805
|
+
if (hasText) parts.push({ type: "text", text });
|
|
806
|
+
const videoParts = videos.map((v) => {
|
|
807
|
+
const videoUrl = v.fileId ? { url: `ms://${v.fileId}`, id: v.fileId } : { url: `data:${v.mediaType};base64,${v.data}` };
|
|
808
|
+
return { type: "video_url", video_url: videoUrl };
|
|
809
|
+
});
|
|
810
|
+
out.push({
|
|
811
|
+
role: "tool",
|
|
812
|
+
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
813
|
+
content: [...parts, ...videoParts]
|
|
814
|
+
});
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
776
817
|
out.push({
|
|
777
818
|
role: "tool",
|
|
778
819
|
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
@@ -1357,6 +1398,75 @@ function fnv1aHash(value) {
|
|
|
1357
1398
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1358
1399
|
}
|
|
1359
1400
|
|
|
1401
|
+
// src/utils/diag.ts
|
|
1402
|
+
var _diagFn = null;
|
|
1403
|
+
function setProviderDiagnostic(fn) {
|
|
1404
|
+
_diagFn = fn;
|
|
1405
|
+
}
|
|
1406
|
+
function providerDiag(phase, data) {
|
|
1407
|
+
_diagFn?.(phase, data);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
// src/providers/moonshot-video.ts
|
|
1411
|
+
async function uploadMoonshotVideos(client, messages, signal) {
|
|
1412
|
+
for (const msg of messages) {
|
|
1413
|
+
if (typeof msg.content === "string") continue;
|
|
1414
|
+
for (const part of msg.content) {
|
|
1415
|
+
if (part.type === "video") {
|
|
1416
|
+
await ensureUploaded(client, part, signal);
|
|
1417
|
+
continue;
|
|
1418
|
+
}
|
|
1419
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
1420
|
+
for (const inner of part.content) {
|
|
1421
|
+
if (inner.type === "video") {
|
|
1422
|
+
await ensureUploaded(client, inner, signal);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
async function ensureUploaded(client, video, signal) {
|
|
1430
|
+
if (video.fileId) {
|
|
1431
|
+
providerDiag("moonshot_video_cached", { fileId: video.fileId });
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (!video.data) {
|
|
1435
|
+
providerDiag("moonshot_video_skipped_no_data", {});
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
providerDiag("moonshot_video_upload_start", {
|
|
1439
|
+
mediaType: video.mediaType,
|
|
1440
|
+
bytes: Math.floor(video.data.length * 3 / 4)
|
|
1441
|
+
});
|
|
1442
|
+
video.fileId = await uploadOne(client, video, signal);
|
|
1443
|
+
providerDiag("moonshot_video_upload_done", { fileId: video.fileId });
|
|
1444
|
+
}
|
|
1445
|
+
async function uploadOne(client, video, signal) {
|
|
1446
|
+
const bytes = Buffer.from(video.data, "base64");
|
|
1447
|
+
const mediaType = video.mediaType || "video/mp4";
|
|
1448
|
+
const filename = `upload.${extForMime(mediaType)}`;
|
|
1449
|
+
const file = new File([new Uint8Array(bytes)], filename, { type: mediaType });
|
|
1450
|
+
const uploaded = await client.files.create(
|
|
1451
|
+
{ file, purpose: "video" },
|
|
1452
|
+
signal ? { signal } : void 0
|
|
1453
|
+
);
|
|
1454
|
+
return uploaded.id;
|
|
1455
|
+
}
|
|
1456
|
+
var MIME_TO_EXT = {
|
|
1457
|
+
"video/mp4": "mp4",
|
|
1458
|
+
"video/mpeg": "mpeg",
|
|
1459
|
+
"video/quicktime": "mov",
|
|
1460
|
+
"video/webm": "webm",
|
|
1461
|
+
"video/x-matroska": "mkv",
|
|
1462
|
+
"video/x-msvideo": "avi",
|
|
1463
|
+
"video/x-flv": "flv",
|
|
1464
|
+
"video/3gpp": "3gp"
|
|
1465
|
+
};
|
|
1466
|
+
function extForMime(mediaType) {
|
|
1467
|
+
return MIME_TO_EXT[mediaType.toLowerCase()] ?? "mp4";
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1360
1470
|
// src/utils/env.ts
|
|
1361
1471
|
function getEnvironment() {
|
|
1362
1472
|
return globalThis.process?.env;
|
|
@@ -1386,7 +1496,8 @@ function createClient2(options) {
|
|
|
1386
1496
|
return new OpenAI({
|
|
1387
1497
|
apiKey: options.apiKey,
|
|
1388
1498
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
1389
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
1499
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
1500
|
+
...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
|
|
1390
1501
|
});
|
|
1391
1502
|
}
|
|
1392
1503
|
function streamOpenAI(options) {
|
|
@@ -1399,6 +1510,13 @@ async function* runStream2(options) {
|
|
|
1399
1510
|
const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
|
|
1400
1511
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
1401
1512
|
const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
|
|
1513
|
+
if (options.provider === "moonshot") {
|
|
1514
|
+
try {
|
|
1515
|
+
await uploadMoonshotVideos(client, downgradedMessages, options.signal);
|
|
1516
|
+
} catch (err) {
|
|
1517
|
+
throw toError2(err, providerName);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1402
1520
|
const messages = toOpenAIMessages(downgradedMessages, {
|
|
1403
1521
|
provider: options.provider,
|
|
1404
1522
|
thinking: !!options.thinking,
|
|
@@ -1702,15 +1820,6 @@ function toError2(err, provider = "openai") {
|
|
|
1702
1820
|
// src/providers/openai-codex.ts
|
|
1703
1821
|
import os from "os";
|
|
1704
1822
|
|
|
1705
|
-
// src/utils/diag.ts
|
|
1706
|
-
var _diagFn = null;
|
|
1707
|
-
function setProviderDiagnostic(fn) {
|
|
1708
|
-
_diagFn = fn;
|
|
1709
|
-
}
|
|
1710
|
-
function providerDiag(phase, data) {
|
|
1711
|
-
_diagFn?.(phase, data);
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
1823
|
// src/utils/sse.ts
|
|
1715
1824
|
function parseSseBuffer(buffer) {
|
|
1716
1825
|
const events = [];
|
|
@@ -2359,6 +2468,13 @@ ${msg.content}` : msg.content;
|
|
|
2359
2468
|
}
|
|
2360
2469
|
}
|
|
2361
2470
|
});
|
|
2471
|
+
if (typeof result.content !== "string") {
|
|
2472
|
+
for (const block of result.content) {
|
|
2473
|
+
if (block.type === "video") {
|
|
2474
|
+
parts.push({ inlineData: { mimeType: block.mediaType, data: block.data } });
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2362
2478
|
}
|
|
2363
2479
|
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2364
2480
|
}
|
|
@@ -2756,6 +2872,7 @@ var providerRegistry = new ProviderRegistryImpl();
|
|
|
2756
2872
|
|
|
2757
2873
|
// src/stream.ts
|
|
2758
2874
|
var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
|
|
2875
|
+
var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
|
|
2759
2876
|
providerRegistry.register("anthropic", {
|
|
2760
2877
|
stream: (options) => streamAnthropic(options)
|
|
2761
2878
|
});
|
|
@@ -2784,10 +2901,11 @@ providerRegistry.register("glm", {
|
|
|
2784
2901
|
})
|
|
2785
2902
|
});
|
|
2786
2903
|
providerRegistry.register("moonshot", {
|
|
2787
|
-
stream: (options) =>
|
|
2788
|
-
|
|
2789
|
-
baseUrl: options.
|
|
2790
|
-
|
|
2904
|
+
stream: (options) => {
|
|
2905
|
+
const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
|
|
2906
|
+
const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
|
|
2907
|
+
return streamOpenAI({ ...options, baseUrl, defaultHeaders });
|
|
2908
|
+
}
|
|
2791
2909
|
});
|
|
2792
2910
|
providerRegistry.register("deepseek", {
|
|
2793
2911
|
stream: (options) => streamOpenAI({
|
|
@@ -2820,8 +2938,23 @@ function stream(options) {
|
|
|
2820
2938
|
`Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
|
|
2821
2939
|
);
|
|
2822
2940
|
}
|
|
2941
|
+
if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
|
|
2942
|
+
throw new VideoUnsupportedError();
|
|
2943
|
+
}
|
|
2823
2944
|
return entry.stream(options);
|
|
2824
2945
|
}
|
|
2946
|
+
function messagesContainVideo(messages) {
|
|
2947
|
+
for (const msg of messages) {
|
|
2948
|
+
if (typeof msg.content === "string" || !Array.isArray(msg.content)) continue;
|
|
2949
|
+
for (const part of msg.content) {
|
|
2950
|
+
if (part.type === "video") return true;
|
|
2951
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
2952
|
+
if (part.content.some((block) => block.type === "video")) return true;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
return false;
|
|
2957
|
+
}
|
|
2825
2958
|
|
|
2826
2959
|
// src/error-classification.ts
|
|
2827
2960
|
var CONTEXT_OVERFLOW_PATTERNS = [
|