@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.cjs
CHANGED
|
@@ -79,6 +79,12 @@ var GGAIError = class extends Error {
|
|
|
79
79
|
this.hint = options?.hint;
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
+
var VideoUnsupportedError = class extends GGAIError {
|
|
83
|
+
constructor() {
|
|
84
|
+
super("This model can't analyze video.", { source: "capability" });
|
|
85
|
+
this.name = "VideoUnsupportedError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
82
88
|
var ProviderError = class extends GGAIError {
|
|
83
89
|
provider;
|
|
84
90
|
statusCode;
|
|
@@ -194,6 +200,14 @@ function finaliseBySource(source, message, requestId, hint) {
|
|
|
194
200
|
guidance: hint ?? providerGuidance(void 0, message, void 0),
|
|
195
201
|
...requestId ? { requestId } : {}
|
|
196
202
|
};
|
|
203
|
+
case "capability":
|
|
204
|
+
return {
|
|
205
|
+
headline: message,
|
|
206
|
+
source,
|
|
207
|
+
message: "",
|
|
208
|
+
guidance: hint ?? "Only Kimi, Gemini, and MiniMax can analyze video. Switch with /model.",
|
|
209
|
+
...requestId ? { requestId } : {}
|
|
210
|
+
};
|
|
197
211
|
case "ggcoder":
|
|
198
212
|
return {
|
|
199
213
|
headline: "ggcoder hit an unexpected error.",
|
|
@@ -554,6 +568,10 @@ function toolResultImages(content) {
|
|
|
554
568
|
if (typeof content === "string") return [];
|
|
555
569
|
return content.filter((b) => b.type === "image");
|
|
556
570
|
}
|
|
571
|
+
function toolResultVideos(content) {
|
|
572
|
+
if (typeof content === "string") return [];
|
|
573
|
+
return content.filter((b) => b.type === "video");
|
|
574
|
+
}
|
|
557
575
|
function toAnthropicCacheControl(retention, baseUrl) {
|
|
558
576
|
const resolved = retention ?? "short";
|
|
559
577
|
if (resolved === "none") return void 0;
|
|
@@ -564,6 +582,12 @@ function toAnthropicToolResultContent(content) {
|
|
|
564
582
|
if (typeof content === "string") return content;
|
|
565
583
|
return content.map((block) => {
|
|
566
584
|
if (block.type === "text") return { type: "text", text: block.text };
|
|
585
|
+
if (block.type === "video") {
|
|
586
|
+
return {
|
|
587
|
+
type: "video",
|
|
588
|
+
source: { type: "base64", media_type: block.mediaType, data: block.data }
|
|
589
|
+
};
|
|
590
|
+
}
|
|
567
591
|
return {
|
|
568
592
|
type: "image",
|
|
569
593
|
source: {
|
|
@@ -633,6 +657,8 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
633
657
|
if (msg.role === "tool") {
|
|
634
658
|
out.push({
|
|
635
659
|
role: "user",
|
|
660
|
+
// Cast covers the video block (used by the Anthropic-compatible MiniMax
|
|
661
|
+
// API), which isn't in the first-party Anthropic tool_result types.
|
|
636
662
|
content: msg.content.map((result) => ({
|
|
637
663
|
type: "tool_result",
|
|
638
664
|
tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
|
|
@@ -773,11 +799,10 @@ function toOpenAIMessages(messages, options) {
|
|
|
773
799
|
(part) => {
|
|
774
800
|
if (part.type === "text") return { type: "text", text: part.text };
|
|
775
801
|
if (part.type === "video") {
|
|
802
|
+
const videoUrl = part.fileId ? { url: `ms://${part.fileId}`, id: part.fileId } : { url: `data:${part.mediaType};base64,${part.data}` };
|
|
776
803
|
return {
|
|
777
804
|
type: "video_url",
|
|
778
|
-
video_url:
|
|
779
|
-
url: `data:${part.mediaType};base64,${part.data}`
|
|
780
|
-
}
|
|
805
|
+
video_url: videoUrl
|
|
781
806
|
};
|
|
782
807
|
}
|
|
783
808
|
return {
|
|
@@ -822,11 +847,27 @@ function toOpenAIMessages(messages, options) {
|
|
|
822
847
|
continue;
|
|
823
848
|
}
|
|
824
849
|
if (msg.role === "tool") {
|
|
850
|
+
const isMoonshot = options?.provider === "moonshot";
|
|
825
851
|
const imageBlocks = [];
|
|
826
852
|
for (const result of msg.content) {
|
|
827
853
|
const text = toolResultText(result.content);
|
|
828
854
|
const images = toolResultImages(result.content);
|
|
855
|
+
const videos = isMoonshot ? toolResultVideos(result.content) : [];
|
|
829
856
|
const hasText = text.length > 0;
|
|
857
|
+
if (videos.length > 0) {
|
|
858
|
+
const parts = [];
|
|
859
|
+
if (hasText) parts.push({ type: "text", text });
|
|
860
|
+
const videoParts = videos.map((v) => {
|
|
861
|
+
const videoUrl = v.fileId ? { url: `ms://${v.fileId}`, id: v.fileId } : { url: `data:${v.mediaType};base64,${v.data}` };
|
|
862
|
+
return { type: "video_url", video_url: videoUrl };
|
|
863
|
+
});
|
|
864
|
+
out.push({
|
|
865
|
+
role: "tool",
|
|
866
|
+
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
867
|
+
content: [...parts, ...videoParts]
|
|
868
|
+
});
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
830
871
|
out.push({
|
|
831
872
|
role: "tool",
|
|
832
873
|
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
@@ -1411,6 +1452,75 @@ function fnv1aHash(value) {
|
|
|
1411
1452
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1412
1453
|
}
|
|
1413
1454
|
|
|
1455
|
+
// src/utils/diag.ts
|
|
1456
|
+
var _diagFn = null;
|
|
1457
|
+
function setProviderDiagnostic(fn) {
|
|
1458
|
+
_diagFn = fn;
|
|
1459
|
+
}
|
|
1460
|
+
function providerDiag(phase, data) {
|
|
1461
|
+
_diagFn?.(phase, data);
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// src/providers/moonshot-video.ts
|
|
1465
|
+
async function uploadMoonshotVideos(client, messages, signal) {
|
|
1466
|
+
for (const msg of messages) {
|
|
1467
|
+
if (typeof msg.content === "string") continue;
|
|
1468
|
+
for (const part of msg.content) {
|
|
1469
|
+
if (part.type === "video") {
|
|
1470
|
+
await ensureUploaded(client, part, signal);
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
1474
|
+
for (const inner of part.content) {
|
|
1475
|
+
if (inner.type === "video") {
|
|
1476
|
+
await ensureUploaded(client, inner, signal);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
async function ensureUploaded(client, video, signal) {
|
|
1484
|
+
if (video.fileId) {
|
|
1485
|
+
providerDiag("moonshot_video_cached", { fileId: video.fileId });
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (!video.data) {
|
|
1489
|
+
providerDiag("moonshot_video_skipped_no_data", {});
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
providerDiag("moonshot_video_upload_start", {
|
|
1493
|
+
mediaType: video.mediaType,
|
|
1494
|
+
bytes: Math.floor(video.data.length * 3 / 4)
|
|
1495
|
+
});
|
|
1496
|
+
video.fileId = await uploadOne(client, video, signal);
|
|
1497
|
+
providerDiag("moonshot_video_upload_done", { fileId: video.fileId });
|
|
1498
|
+
}
|
|
1499
|
+
async function uploadOne(client, video, signal) {
|
|
1500
|
+
const bytes = Buffer.from(video.data, "base64");
|
|
1501
|
+
const mediaType = video.mediaType || "video/mp4";
|
|
1502
|
+
const filename = `upload.${extForMime(mediaType)}`;
|
|
1503
|
+
const file = new File([new Uint8Array(bytes)], filename, { type: mediaType });
|
|
1504
|
+
const uploaded = await client.files.create(
|
|
1505
|
+
{ file, purpose: "video" },
|
|
1506
|
+
signal ? { signal } : void 0
|
|
1507
|
+
);
|
|
1508
|
+
return uploaded.id;
|
|
1509
|
+
}
|
|
1510
|
+
var MIME_TO_EXT = {
|
|
1511
|
+
"video/mp4": "mp4",
|
|
1512
|
+
"video/mpeg": "mpeg",
|
|
1513
|
+
"video/quicktime": "mov",
|
|
1514
|
+
"video/webm": "webm",
|
|
1515
|
+
"video/x-matroska": "mkv",
|
|
1516
|
+
"video/x-msvideo": "avi",
|
|
1517
|
+
"video/x-flv": "flv",
|
|
1518
|
+
"video/3gpp": "3gp"
|
|
1519
|
+
};
|
|
1520
|
+
function extForMime(mediaType) {
|
|
1521
|
+
return MIME_TO_EXT[mediaType.toLowerCase()] ?? "mp4";
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1414
1524
|
// src/utils/env.ts
|
|
1415
1525
|
function getEnvironment() {
|
|
1416
1526
|
return globalThis.process?.env;
|
|
@@ -1440,7 +1550,8 @@ function createClient2(options) {
|
|
|
1440
1550
|
return new import_openai.default({
|
|
1441
1551
|
apiKey: options.apiKey,
|
|
1442
1552
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
1443
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
1553
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
1554
|
+
...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
|
|
1444
1555
|
});
|
|
1445
1556
|
}
|
|
1446
1557
|
function streamOpenAI(options) {
|
|
@@ -1453,6 +1564,13 @@ async function* runStream2(options) {
|
|
|
1453
1564
|
const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
|
|
1454
1565
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
1455
1566
|
const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
|
|
1567
|
+
if (options.provider === "moonshot") {
|
|
1568
|
+
try {
|
|
1569
|
+
await uploadMoonshotVideos(client, downgradedMessages, options.signal);
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
throw toError2(err, providerName);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1456
1574
|
const messages = toOpenAIMessages(downgradedMessages, {
|
|
1457
1575
|
provider: options.provider,
|
|
1458
1576
|
thinking: !!options.thinking,
|
|
@@ -1756,15 +1874,6 @@ function toError2(err, provider = "openai") {
|
|
|
1756
1874
|
// src/providers/openai-codex.ts
|
|
1757
1875
|
var import_node_os = __toESM(require("os"), 1);
|
|
1758
1876
|
|
|
1759
|
-
// src/utils/diag.ts
|
|
1760
|
-
var _diagFn = null;
|
|
1761
|
-
function setProviderDiagnostic(fn) {
|
|
1762
|
-
_diagFn = fn;
|
|
1763
|
-
}
|
|
1764
|
-
function providerDiag(phase, data) {
|
|
1765
|
-
_diagFn?.(phase, data);
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
1877
|
// src/utils/sse.ts
|
|
1769
1878
|
function parseSseBuffer(buffer) {
|
|
1770
1879
|
const events = [];
|
|
@@ -2413,6 +2522,13 @@ ${msg.content}` : msg.content;
|
|
|
2413
2522
|
}
|
|
2414
2523
|
}
|
|
2415
2524
|
});
|
|
2525
|
+
if (typeof result.content !== "string") {
|
|
2526
|
+
for (const block of result.content) {
|
|
2527
|
+
if (block.type === "video") {
|
|
2528
|
+
parts.push({ inlineData: { mimeType: block.mediaType, data: block.data } });
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2416
2532
|
}
|
|
2417
2533
|
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2418
2534
|
}
|
|
@@ -2810,6 +2926,7 @@ var providerRegistry = new ProviderRegistryImpl();
|
|
|
2810
2926
|
|
|
2811
2927
|
// src/stream.ts
|
|
2812
2928
|
var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
|
|
2929
|
+
var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
|
|
2813
2930
|
providerRegistry.register("anthropic", {
|
|
2814
2931
|
stream: (options) => streamAnthropic(options)
|
|
2815
2932
|
});
|
|
@@ -2838,10 +2955,11 @@ providerRegistry.register("glm", {
|
|
|
2838
2955
|
})
|
|
2839
2956
|
});
|
|
2840
2957
|
providerRegistry.register("moonshot", {
|
|
2841
|
-
stream: (options) =>
|
|
2842
|
-
|
|
2843
|
-
baseUrl: options.
|
|
2844
|
-
|
|
2958
|
+
stream: (options) => {
|
|
2959
|
+
const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
|
|
2960
|
+
const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
|
|
2961
|
+
return streamOpenAI({ ...options, baseUrl, defaultHeaders });
|
|
2962
|
+
}
|
|
2845
2963
|
});
|
|
2846
2964
|
providerRegistry.register("deepseek", {
|
|
2847
2965
|
stream: (options) => streamOpenAI({
|
|
@@ -2874,8 +2992,23 @@ function stream(options) {
|
|
|
2874
2992
|
`Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
|
|
2875
2993
|
);
|
|
2876
2994
|
}
|
|
2995
|
+
if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
|
|
2996
|
+
throw new VideoUnsupportedError();
|
|
2997
|
+
}
|
|
2877
2998
|
return entry.stream(options);
|
|
2878
2999
|
}
|
|
3000
|
+
function messagesContainVideo(messages) {
|
|
3001
|
+
for (const msg of messages) {
|
|
3002
|
+
if (typeof msg.content === "string" || !Array.isArray(msg.content)) continue;
|
|
3003
|
+
for (const part of msg.content) {
|
|
3004
|
+
if (part.type === "video") return true;
|
|
3005
|
+
if (part.type === "tool_result" && Array.isArray(part.content)) {
|
|
3006
|
+
if (part.content.some((block) => block.type === "video")) return true;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
return false;
|
|
3011
|
+
}
|
|
2879
3012
|
|
|
2880
3013
|
// src/error-classification.ts
|
|
2881
3014
|
var CONTEXT_OVERFLOW_PATTERNS = [
|