@kenkaiiii/gg-ai 4.4.0 → 4.6.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 +166 -22
- 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 +166 -22
- 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, MiniMax, and MiMo-V2.5 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,29 +793,56 @@ function toOpenAIMessages(messages, options) {
|
|
|
768
793
|
continue;
|
|
769
794
|
}
|
|
770
795
|
if (msg.role === "tool") {
|
|
771
|
-
const
|
|
796
|
+
const isMoonshot = options?.provider === "moonshot";
|
|
797
|
+
const followUpMediaBlocks = [];
|
|
798
|
+
let followUpHasVideo = false;
|
|
772
799
|
for (const result of msg.content) {
|
|
773
800
|
const text = toolResultText(result.content);
|
|
774
801
|
const images = toolResultImages(result.content);
|
|
802
|
+
const videos = toolResultVideos(result.content);
|
|
775
803
|
const hasText = text.length > 0;
|
|
804
|
+
if (isMoonshot && videos.length > 0) {
|
|
805
|
+
const parts = [];
|
|
806
|
+
if (hasText) parts.push({ type: "text", text });
|
|
807
|
+
const videoParts = videos.map((v) => {
|
|
808
|
+
const videoUrl = v.fileId ? { url: `ms://${v.fileId}`, id: v.fileId } : { url: `data:${v.mediaType};base64,${v.data}` };
|
|
809
|
+
return { type: "video_url", video_url: videoUrl };
|
|
810
|
+
});
|
|
811
|
+
out.push({
|
|
812
|
+
role: "tool",
|
|
813
|
+
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
814
|
+
content: [...parts, ...videoParts]
|
|
815
|
+
});
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
776
818
|
out.push({
|
|
777
819
|
role: "tool",
|
|
778
820
|
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
779
|
-
content: hasText ? text : "(see attached
|
|
821
|
+
content: hasText ? text : "(see attached media)"
|
|
780
822
|
});
|
|
781
823
|
if (images.length > 0 && options?.supportsImages !== false) {
|
|
782
824
|
for (const img of images) {
|
|
783
|
-
|
|
825
|
+
followUpMediaBlocks.push({
|
|
784
826
|
type: "image_url",
|
|
785
827
|
image_url: { url: `data:${img.mediaType};base64,${img.data}` }
|
|
786
828
|
});
|
|
787
829
|
}
|
|
788
830
|
}
|
|
831
|
+
if (!isMoonshot && videos.length > 0) {
|
|
832
|
+
for (const v of videos) {
|
|
833
|
+
followUpMediaBlocks.push({
|
|
834
|
+
type: "video_url",
|
|
835
|
+
video_url: { url: `data:${v.mediaType};base64,${v.data}` }
|
|
836
|
+
});
|
|
837
|
+
followUpHasVideo = true;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
789
840
|
}
|
|
790
|
-
if (
|
|
841
|
+
if (followUpMediaBlocks.length > 0) {
|
|
842
|
+
const label = followUpHasVideo ? "Attached media from tool result:" : "Attached image(s) from tool result:";
|
|
791
843
|
out.push({
|
|
792
844
|
role: "user",
|
|
793
|
-
content: [{ type: "text", text:
|
|
845
|
+
content: [{ type: "text", text: label }, ...followUpMediaBlocks]
|
|
794
846
|
});
|
|
795
847
|
}
|
|
796
848
|
}
|
|
@@ -1357,6 +1409,75 @@ function fnv1aHash(value) {
|
|
|
1357
1409
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1358
1410
|
}
|
|
1359
1411
|
|
|
1412
|
+
// src/utils/diag.ts
|
|
1413
|
+
var _diagFn = null;
|
|
1414
|
+
function setProviderDiagnostic(fn) {
|
|
1415
|
+
_diagFn = fn;
|
|
1416
|
+
}
|
|
1417
|
+
function providerDiag(phase, data) {
|
|
1418
|
+
_diagFn?.(phase, data);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// src/providers/moonshot-video.ts
|
|
1422
|
+
async function uploadMoonshotVideos(client, messages, signal) {
|
|
1423
|
+
for (const msg of messages) {
|
|
1424
|
+
if (typeof msg.content === "string") continue;
|
|
1425
|
+
for (const part of msg.content) {
|
|
1426
|
+
if (part.type === "video") {
|
|
1427
|
+
await ensureUploaded(client, part, signal);
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
1431
|
+
for (const inner of part.content) {
|
|
1432
|
+
if (inner.type === "video") {
|
|
1433
|
+
await ensureUploaded(client, inner, signal);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
async function ensureUploaded(client, video, signal) {
|
|
1441
|
+
if (video.fileId) {
|
|
1442
|
+
providerDiag("moonshot_video_cached", { fileId: video.fileId });
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
if (!video.data) {
|
|
1446
|
+
providerDiag("moonshot_video_skipped_no_data", {});
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
providerDiag("moonshot_video_upload_start", {
|
|
1450
|
+
mediaType: video.mediaType,
|
|
1451
|
+
bytes: Math.floor(video.data.length * 3 / 4)
|
|
1452
|
+
});
|
|
1453
|
+
video.fileId = await uploadOne(client, video, signal);
|
|
1454
|
+
providerDiag("moonshot_video_upload_done", { fileId: video.fileId });
|
|
1455
|
+
}
|
|
1456
|
+
async function uploadOne(client, video, signal) {
|
|
1457
|
+
const bytes = Buffer.from(video.data, "base64");
|
|
1458
|
+
const mediaType = video.mediaType || "video/mp4";
|
|
1459
|
+
const filename = `upload.${extForMime(mediaType)}`;
|
|
1460
|
+
const file = new File([new Uint8Array(bytes)], filename, { type: mediaType });
|
|
1461
|
+
const uploaded = await client.files.create(
|
|
1462
|
+
{ file, purpose: "video" },
|
|
1463
|
+
signal ? { signal } : void 0
|
|
1464
|
+
);
|
|
1465
|
+
return uploaded.id;
|
|
1466
|
+
}
|
|
1467
|
+
var MIME_TO_EXT = {
|
|
1468
|
+
"video/mp4": "mp4",
|
|
1469
|
+
"video/mpeg": "mpeg",
|
|
1470
|
+
"video/quicktime": "mov",
|
|
1471
|
+
"video/webm": "webm",
|
|
1472
|
+
"video/x-matroska": "mkv",
|
|
1473
|
+
"video/x-msvideo": "avi",
|
|
1474
|
+
"video/x-flv": "flv",
|
|
1475
|
+
"video/3gpp": "3gp"
|
|
1476
|
+
};
|
|
1477
|
+
function extForMime(mediaType) {
|
|
1478
|
+
return MIME_TO_EXT[mediaType.toLowerCase()] ?? "mp4";
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1360
1481
|
// src/utils/env.ts
|
|
1361
1482
|
function getEnvironment() {
|
|
1362
1483
|
return globalThis.process?.env;
|
|
@@ -1386,7 +1507,8 @@ function createClient2(options) {
|
|
|
1386
1507
|
return new OpenAI({
|
|
1387
1508
|
apiKey: options.apiKey,
|
|
1388
1509
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
1389
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
1510
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
1511
|
+
...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
|
|
1390
1512
|
});
|
|
1391
1513
|
}
|
|
1392
1514
|
function streamOpenAI(options) {
|
|
@@ -1399,6 +1521,13 @@ async function* runStream2(options) {
|
|
|
1399
1521
|
const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
|
|
1400
1522
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
1401
1523
|
const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
|
|
1524
|
+
if (options.provider === "moonshot") {
|
|
1525
|
+
try {
|
|
1526
|
+
await uploadMoonshotVideos(client, downgradedMessages, options.signal);
|
|
1527
|
+
} catch (err) {
|
|
1528
|
+
throw toError2(err, providerName);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1402
1531
|
const messages = toOpenAIMessages(downgradedMessages, {
|
|
1403
1532
|
provider: options.provider,
|
|
1404
1533
|
thinking: !!options.thinking,
|
|
@@ -1702,15 +1831,6 @@ function toError2(err, provider = "openai") {
|
|
|
1702
1831
|
// src/providers/openai-codex.ts
|
|
1703
1832
|
import os from "os";
|
|
1704
1833
|
|
|
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
1834
|
// src/utils/sse.ts
|
|
1715
1835
|
function parseSseBuffer(buffer) {
|
|
1716
1836
|
const events = [];
|
|
@@ -2359,6 +2479,13 @@ ${msg.content}` : msg.content;
|
|
|
2359
2479
|
}
|
|
2360
2480
|
}
|
|
2361
2481
|
});
|
|
2482
|
+
if (typeof result.content !== "string") {
|
|
2483
|
+
for (const block of result.content) {
|
|
2484
|
+
if (block.type === "video") {
|
|
2485
|
+
parts.push({ inlineData: { mimeType: block.mediaType, data: block.data } });
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2362
2489
|
}
|
|
2363
2490
|
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2364
2491
|
}
|
|
@@ -2756,6 +2883,7 @@ var providerRegistry = new ProviderRegistryImpl();
|
|
|
2756
2883
|
|
|
2757
2884
|
// src/stream.ts
|
|
2758
2885
|
var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
|
|
2886
|
+
var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
|
|
2759
2887
|
providerRegistry.register("anthropic", {
|
|
2760
2888
|
stream: (options) => streamAnthropic(options)
|
|
2761
2889
|
});
|
|
@@ -2784,10 +2912,11 @@ providerRegistry.register("glm", {
|
|
|
2784
2912
|
})
|
|
2785
2913
|
});
|
|
2786
2914
|
providerRegistry.register("moonshot", {
|
|
2787
|
-
stream: (options) =>
|
|
2788
|
-
|
|
2789
|
-
baseUrl: options.
|
|
2790
|
-
|
|
2915
|
+
stream: (options) => {
|
|
2916
|
+
const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
|
|
2917
|
+
const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
|
|
2918
|
+
return streamOpenAI({ ...options, baseUrl, defaultHeaders });
|
|
2919
|
+
}
|
|
2791
2920
|
});
|
|
2792
2921
|
providerRegistry.register("deepseek", {
|
|
2793
2922
|
stream: (options) => streamOpenAI({
|
|
@@ -2820,8 +2949,23 @@ function stream(options) {
|
|
|
2820
2949
|
`Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
|
|
2821
2950
|
);
|
|
2822
2951
|
}
|
|
2952
|
+
if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
|
|
2953
|
+
throw new VideoUnsupportedError();
|
|
2954
|
+
}
|
|
2823
2955
|
return entry.stream(options);
|
|
2824
2956
|
}
|
|
2957
|
+
function messagesContainVideo(messages) {
|
|
2958
|
+
for (const msg of messages) {
|
|
2959
|
+
if (typeof msg.content === "string" || !Array.isArray(msg.content)) continue;
|
|
2960
|
+
for (const part of msg.content) {
|
|
2961
|
+
if (part.type === "video") return true;
|
|
2962
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
2963
|
+
if (part.content.some((block) => block.type === "video")) return true;
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
return false;
|
|
2968
|
+
}
|
|
2825
2969
|
|
|
2826
2970
|
// src/error-classification.ts
|
|
2827
2971
|
var CONTEXT_OVERFLOW_PATTERNS = [
|