@kenkaiiii/gg-ai 4.3.243 → 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 +242 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +241 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
GGAIError: () => GGAIError,
|
|
35
35
|
ProviderError: () => ProviderError,
|
|
36
36
|
StreamResult: () => StreamResult,
|
|
37
|
+
classifyProviderError: () => classifyProviderError,
|
|
37
38
|
formatError: () => formatError,
|
|
38
39
|
formatErrorForDisplay: () => formatErrorForDisplay,
|
|
39
40
|
isHardBillingMessage: () => isHardBillingMessage,
|
|
@@ -78,6 +79,12 @@ var GGAIError = class extends Error {
|
|
|
78
79
|
this.hint = options?.hint;
|
|
79
80
|
}
|
|
80
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
|
+
};
|
|
81
88
|
var ProviderError = class extends GGAIError {
|
|
82
89
|
provider;
|
|
83
90
|
statusCode;
|
|
@@ -193,6 +200,14 @@ function finaliseBySource(source, message, requestId, hint) {
|
|
|
193
200
|
guidance: hint ?? providerGuidance(void 0, message, void 0),
|
|
194
201
|
...requestId ? { requestId } : {}
|
|
195
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
|
+
};
|
|
196
211
|
case "ggcoder":
|
|
197
212
|
return {
|
|
198
213
|
headline: "ggcoder hit an unexpected error.",
|
|
@@ -553,6 +568,10 @@ function toolResultImages(content) {
|
|
|
553
568
|
if (typeof content === "string") return [];
|
|
554
569
|
return content.filter((b) => b.type === "image");
|
|
555
570
|
}
|
|
571
|
+
function toolResultVideos(content) {
|
|
572
|
+
if (typeof content === "string") return [];
|
|
573
|
+
return content.filter((b) => b.type === "video");
|
|
574
|
+
}
|
|
556
575
|
function toAnthropicCacheControl(retention, baseUrl) {
|
|
557
576
|
const resolved = retention ?? "short";
|
|
558
577
|
if (resolved === "none") return void 0;
|
|
@@ -563,6 +582,12 @@ function toAnthropicToolResultContent(content) {
|
|
|
563
582
|
if (typeof content === "string") return content;
|
|
564
583
|
return content.map((block) => {
|
|
565
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
|
+
}
|
|
566
591
|
return {
|
|
567
592
|
type: "image",
|
|
568
593
|
source: {
|
|
@@ -632,6 +657,8 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
632
657
|
if (msg.role === "tool") {
|
|
633
658
|
out.push({
|
|
634
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.
|
|
635
662
|
content: msg.content.map((result) => ({
|
|
636
663
|
type: "tool_result",
|
|
637
664
|
tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
|
|
@@ -772,11 +799,10 @@ function toOpenAIMessages(messages, options) {
|
|
|
772
799
|
(part) => {
|
|
773
800
|
if (part.type === "text") return { type: "text", text: part.text };
|
|
774
801
|
if (part.type === "video") {
|
|
802
|
+
const videoUrl = part.fileId ? { url: `ms://${part.fileId}`, id: part.fileId } : { url: `data:${part.mediaType};base64,${part.data}` };
|
|
775
803
|
return {
|
|
776
804
|
type: "video_url",
|
|
777
|
-
video_url:
|
|
778
|
-
url: `data:${part.mediaType};base64,${part.data}`
|
|
779
|
-
}
|
|
805
|
+
video_url: videoUrl
|
|
780
806
|
};
|
|
781
807
|
}
|
|
782
808
|
return {
|
|
@@ -821,11 +847,27 @@ function toOpenAIMessages(messages, options) {
|
|
|
821
847
|
continue;
|
|
822
848
|
}
|
|
823
849
|
if (msg.role === "tool") {
|
|
850
|
+
const isMoonshot = options?.provider === "moonshot";
|
|
824
851
|
const imageBlocks = [];
|
|
825
852
|
for (const result of msg.content) {
|
|
826
853
|
const text = toolResultText(result.content);
|
|
827
854
|
const images = toolResultImages(result.content);
|
|
855
|
+
const videos = isMoonshot ? toolResultVideos(result.content) : [];
|
|
828
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
|
+
}
|
|
829
871
|
out.push({
|
|
830
872
|
role: "tool",
|
|
831
873
|
tool_call_id: remapToolCallId(result.toolCallId, idMap),
|
|
@@ -1410,6 +1452,75 @@ function fnv1aHash(value) {
|
|
|
1410
1452
|
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
1411
1453
|
}
|
|
1412
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
|
+
|
|
1413
1524
|
// src/utils/env.ts
|
|
1414
1525
|
function getEnvironment() {
|
|
1415
1526
|
return globalThis.process?.env;
|
|
@@ -1439,7 +1550,8 @@ function createClient2(options) {
|
|
|
1439
1550
|
return new import_openai.default({
|
|
1440
1551
|
apiKey: options.apiKey,
|
|
1441
1552
|
...options.baseUrl ? { baseURL: options.baseUrl } : {},
|
|
1442
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
1553
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
1554
|
+
...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
|
|
1443
1555
|
});
|
|
1444
1556
|
}
|
|
1445
1557
|
function streamOpenAI(options) {
|
|
@@ -1452,6 +1564,13 @@ async function* runStream2(options) {
|
|
|
1452
1564
|
const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
|
|
1453
1565
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
1454
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
|
+
}
|
|
1455
1574
|
const messages = toOpenAIMessages(downgradedMessages, {
|
|
1456
1575
|
provider: options.provider,
|
|
1457
1576
|
thinking: !!options.thinking,
|
|
@@ -1755,15 +1874,6 @@ function toError2(err, provider = "openai") {
|
|
|
1755
1874
|
// src/providers/openai-codex.ts
|
|
1756
1875
|
var import_node_os = __toESM(require("os"), 1);
|
|
1757
1876
|
|
|
1758
|
-
// src/utils/diag.ts
|
|
1759
|
-
var _diagFn = null;
|
|
1760
|
-
function setProviderDiagnostic(fn) {
|
|
1761
|
-
_diagFn = fn;
|
|
1762
|
-
}
|
|
1763
|
-
function providerDiag(phase, data) {
|
|
1764
|
-
_diagFn?.(phase, data);
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
1877
|
// src/utils/sse.ts
|
|
1768
1878
|
function parseSseBuffer(buffer) {
|
|
1769
1879
|
const events = [];
|
|
@@ -2412,6 +2522,13 @@ ${msg.content}` : msg.content;
|
|
|
2412
2522
|
}
|
|
2413
2523
|
}
|
|
2414
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
|
+
}
|
|
2415
2532
|
}
|
|
2416
2533
|
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2417
2534
|
}
|
|
@@ -2809,6 +2926,7 @@ var providerRegistry = new ProviderRegistryImpl();
|
|
|
2809
2926
|
|
|
2810
2927
|
// src/stream.ts
|
|
2811
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"}`;
|
|
2812
2930
|
providerRegistry.register("anthropic", {
|
|
2813
2931
|
stream: (options) => streamAnthropic(options)
|
|
2814
2932
|
});
|
|
@@ -2837,10 +2955,11 @@ providerRegistry.register("glm", {
|
|
|
2837
2955
|
})
|
|
2838
2956
|
});
|
|
2839
2957
|
providerRegistry.register("moonshot", {
|
|
2840
|
-
stream: (options) =>
|
|
2841
|
-
|
|
2842
|
-
baseUrl: options.
|
|
2843
|
-
|
|
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
|
+
}
|
|
2844
2963
|
});
|
|
2845
2964
|
providerRegistry.register("deepseek", {
|
|
2846
2965
|
stream: (options) => streamOpenAI({
|
|
@@ -2873,8 +2992,113 @@ function stream(options) {
|
|
|
2873
2992
|
`Unknown provider: "${options.provider}". Registered: ${providerRegistry.list().join(", ")}`
|
|
2874
2993
|
);
|
|
2875
2994
|
}
|
|
2995
|
+
if (options.supportsVideo !== true && messagesContainVideo(options.messages)) {
|
|
2996
|
+
throw new VideoUnsupportedError();
|
|
2997
|
+
}
|
|
2876
2998
|
return entry.stream(options);
|
|
2877
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
|
+
}
|
|
3012
|
+
|
|
3013
|
+
// src/error-classification.ts
|
|
3014
|
+
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
3015
|
+
/context_length_exceeded/i,
|
|
3016
|
+
/context length exceeded/i,
|
|
3017
|
+
/context window/i,
|
|
3018
|
+
// OpenAI Codex / Responses
|
|
3019
|
+
/maximum context length/i,
|
|
3020
|
+
// OpenAI / OpenRouter / Mistral
|
|
3021
|
+
/prompt is too long/i,
|
|
3022
|
+
// Anthropic
|
|
3023
|
+
/request_too_large/i,
|
|
3024
|
+
// Anthropic HTTP 413
|
|
3025
|
+
/input is too long/i,
|
|
3026
|
+
// Bedrock
|
|
3027
|
+
/input token count.*exceeds the maximum/i,
|
|
3028
|
+
// Gemini
|
|
3029
|
+
/maximum prompt length/i,
|
|
3030
|
+
// xAI / Grok
|
|
3031
|
+
/reduce the length of the messages/i,
|
|
3032
|
+
// Groq
|
|
3033
|
+
/too large for model/i,
|
|
3034
|
+
// Mistral
|
|
3035
|
+
/token limit/i
|
|
3036
|
+
// generic
|
|
3037
|
+
];
|
|
3038
|
+
var RATE_LIMIT_PATTERNS = [
|
|
3039
|
+
/rate[ _-]?limit/i,
|
|
3040
|
+
/\b429\b/,
|
|
3041
|
+
/too many requests/i,
|
|
3042
|
+
/tokens per minute/i,
|
|
3043
|
+
/requests per minute/i
|
|
3044
|
+
];
|
|
3045
|
+
var PROVIDER_TRANSIENT_PATTERNS = [
|
|
3046
|
+
/\b5\d\d\b/,
|
|
3047
|
+
/api_error/i,
|
|
3048
|
+
/server_error/i,
|
|
3049
|
+
/internal server error/i,
|
|
3050
|
+
/bad gateway/i,
|
|
3051
|
+
/service unavailable/i,
|
|
3052
|
+
/gateway timeout/i,
|
|
3053
|
+
/overloaded/i,
|
|
3054
|
+
/\b529\b/
|
|
3055
|
+
];
|
|
3056
|
+
var BILLING_PATTERNS = [
|
|
3057
|
+
/payment required/i,
|
|
3058
|
+
/\b402\b/,
|
|
3059
|
+
/quota_exceeded/i,
|
|
3060
|
+
// underscore variant not in isHardBillingMessage
|
|
3061
|
+
/credit balance/i
|
|
3062
|
+
];
|
|
3063
|
+
var AUTH_PATTERNS = [
|
|
3064
|
+
/invalid[ _]api[ _]key/i,
|
|
3065
|
+
/unauthorized/i,
|
|
3066
|
+
/\b401\b/,
|
|
3067
|
+
/authentication[ _]failed/i,
|
|
3068
|
+
/please run \/login/i
|
|
3069
|
+
// Anthropic Claude Code-style hint
|
|
3070
|
+
];
|
|
3071
|
+
function matchesAny(message, patterns) {
|
|
3072
|
+
return patterns.some((p) => p.test(message));
|
|
3073
|
+
}
|
|
3074
|
+
function classifyProviderError(message) {
|
|
3075
|
+
if (matchesAny(message, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
3076
|
+
return `[context_overflow] Worker context window exceeded \u2014 the conversation is too large to continue. Recovery: call reset_worker(project) to wipe history, then re-prompt with the task. Re-prompting WITHOUT reset will fail the same way.
|
|
3077
|
+
|
|
3078
|
+
Original: ${message}`;
|
|
3079
|
+
}
|
|
3080
|
+
if (isHardBillingMessage(message) || matchesAny(message, BILLING_PATTERNS)) {
|
|
3081
|
+
return `[billing] Provider billing/quota issue. Recovery: surface to the user \u2014 they need to top up or switch providers. Do NOT retry.
|
|
3082
|
+
|
|
3083
|
+
Original: ${message}`;
|
|
3084
|
+
}
|
|
3085
|
+
if (matchesAny(message, AUTH_PATTERNS)) {
|
|
3086
|
+
return `[auth] Provider authentication failed. Recovery: surface to the user \u2014 they need to re-login. Do NOT retry.
|
|
3087
|
+
|
|
3088
|
+
Original: ${message}`;
|
|
3089
|
+
}
|
|
3090
|
+
if (matchesAny(message, RATE_LIMIT_PATTERNS)) {
|
|
3091
|
+
return `[rate_limited] Provider rate limit hit. Recovery: wait ~30s, then re-prompt the same worker (no reset needed).
|
|
3092
|
+
|
|
3093
|
+
Original: ${message}`;
|
|
3094
|
+
}
|
|
3095
|
+
if (matchesAny(message, PROVIDER_TRANSIENT_PATTERNS)) {
|
|
3096
|
+
return `[provider_transient] Provider server-side/transient error. Recovery: wait briefly, then re-prompt the same worker (no reset needed). If it keeps happening, switch models/providers or check provider status.
|
|
3097
|
+
|
|
3098
|
+
Original: ${message}`;
|
|
3099
|
+
}
|
|
3100
|
+
return message;
|
|
3101
|
+
}
|
|
2878
3102
|
|
|
2879
3103
|
// src/providers/palsu.ts
|
|
2880
3104
|
function palsuText(text) {
|
|
@@ -3033,6 +3257,7 @@ function registerPalsuProvider(config) {
|
|
|
3033
3257
|
GGAIError,
|
|
3034
3258
|
ProviderError,
|
|
3035
3259
|
StreamResult,
|
|
3260
|
+
classifyProviderError,
|
|
3036
3261
|
formatError,
|
|
3037
3262
|
formatErrorForDisplay,
|
|
3038
3263
|
isHardBillingMessage,
|