@kenkaiiii/gg-ai 4.3.165 → 4.3.167
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 +244 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +63 -2
- package/dist/index.d.ts +63 -2
- package/dist/index.js +242 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -157,6 +157,10 @@ interface StreamOptions {
|
|
|
157
157
|
signal?: AbortSignal;
|
|
158
158
|
/** Prompt cache retention preference. Providers map this to their supported values. Default: "short". */
|
|
159
159
|
cacheRetention?: CacheRetention;
|
|
160
|
+
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot). */
|
|
161
|
+
promptCacheKey?: string;
|
|
162
|
+
/** OpenAI service tier for latency-sensitive requests. Only sent to first-party OpenAI API calls. */
|
|
163
|
+
serviceTier?: "auto" | "default" | "flex" | "priority";
|
|
160
164
|
/** OpenAI ChatGPT account ID (from OAuth JWT) for codex endpoint */
|
|
161
165
|
accountId?: string;
|
|
162
166
|
/** Enable provider-native web search. Each provider uses its own format:
|
|
@@ -292,17 +296,74 @@ declare class ProviderRegistryImpl {
|
|
|
292
296
|
/** Global provider registry. Import this to register custom providers. */
|
|
293
297
|
declare const providerRegistry: ProviderRegistryImpl;
|
|
294
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Error model for gg-ai and downstream consumers.
|
|
301
|
+
*
|
|
302
|
+
* Every error users see should answer one question: "is this me or them?"
|
|
303
|
+
* That answer drives whether they retry, switch model, log in, or report a
|
|
304
|
+
* ggcoder bug. The `FormattedError` shape captures it in plain English:
|
|
305
|
+
*
|
|
306
|
+
* ✗ OpenAI returned an error.
|
|
307
|
+
* An error occurred while processing your request...
|
|
308
|
+
* → This is an OpenAI issue, not ggcoder. Retry — if it persists, check status.openai.com.
|
|
309
|
+
*
|
|
310
|
+
* ✗ ggcoder hit an unexpected error.
|
|
311
|
+
* Cannot read property 'foo' of undefined
|
|
312
|
+
* → This is a ggcoder bug — please report it.
|
|
313
|
+
*/
|
|
314
|
+
type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
|
|
315
|
+
interface FormattedError {
|
|
316
|
+
/** Plain-English headline, e.g. "OpenAI returned an error." */
|
|
317
|
+
headline: string;
|
|
318
|
+
/** Machine-readable classification. */
|
|
319
|
+
source: ErrorSource;
|
|
320
|
+
/** Detailed message body from the underlying error (no JSON, no tag prefix). */
|
|
321
|
+
message: string;
|
|
322
|
+
/** Action line — tells the user whether to retry, switch model, log in, or report a bug. */
|
|
323
|
+
guidance: string;
|
|
324
|
+
/** Provider name when source === "provider". */
|
|
325
|
+
provider?: string;
|
|
326
|
+
/** HTTP status code if known. */
|
|
327
|
+
statusCode?: number;
|
|
328
|
+
/** Provider request ID, kept for telemetry / debug — not shown by default. */
|
|
329
|
+
requestId?: string;
|
|
330
|
+
}
|
|
295
331
|
declare class GGAIError extends Error {
|
|
296
|
-
|
|
332
|
+
readonly source: ErrorSource;
|
|
333
|
+
readonly requestId?: string;
|
|
334
|
+
readonly hint?: string;
|
|
335
|
+
constructor(message: string, options?: {
|
|
336
|
+
source?: ErrorSource;
|
|
337
|
+
requestId?: string;
|
|
338
|
+
hint?: string;
|
|
339
|
+
cause?: unknown;
|
|
340
|
+
});
|
|
297
341
|
}
|
|
298
342
|
declare class ProviderError extends GGAIError {
|
|
299
343
|
readonly provider: string;
|
|
300
344
|
readonly statusCode?: number;
|
|
301
345
|
constructor(provider: string, message: string, options?: {
|
|
302
346
|
statusCode?: number;
|
|
347
|
+
requestId?: string;
|
|
348
|
+
hint?: string;
|
|
303
349
|
cause?: unknown;
|
|
304
350
|
});
|
|
305
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Normalise any thrown value into a structured display object. Always returns
|
|
354
|
+
* a non-empty `headline` and `guidance` so the UI never has to second-guess
|
|
355
|
+
* what to show the user.
|
|
356
|
+
*/
|
|
357
|
+
declare function formatError(err: unknown): FormattedError;
|
|
358
|
+
/**
|
|
359
|
+
* Render a FormattedError as a multi-line string for terminal display.
|
|
360
|
+
*
|
|
361
|
+
* Format:
|
|
362
|
+
* <headline>
|
|
363
|
+
* <message>
|
|
364
|
+
* → <guidance>
|
|
365
|
+
*/
|
|
366
|
+
declare function formatErrorForDisplay(err: unknown): string;
|
|
306
367
|
|
|
307
368
|
interface PalsuProviderState {
|
|
308
369
|
callCount: number;
|
|
@@ -373,4 +434,4 @@ interface PalsuProviderConfig {
|
|
|
373
434
|
*/
|
|
374
435
|
declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
|
|
375
436
|
|
|
376
|
-
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, EventStream, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
|
|
437
|
+
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
|
package/dist/index.d.ts
CHANGED
|
@@ -157,6 +157,10 @@ interface StreamOptions {
|
|
|
157
157
|
signal?: AbortSignal;
|
|
158
158
|
/** Prompt cache retention preference. Providers map this to their supported values. Default: "short". */
|
|
159
159
|
cacheRetention?: CacheRetention;
|
|
160
|
+
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot). */
|
|
161
|
+
promptCacheKey?: string;
|
|
162
|
+
/** OpenAI service tier for latency-sensitive requests. Only sent to first-party OpenAI API calls. */
|
|
163
|
+
serviceTier?: "auto" | "default" | "flex" | "priority";
|
|
160
164
|
/** OpenAI ChatGPT account ID (from OAuth JWT) for codex endpoint */
|
|
161
165
|
accountId?: string;
|
|
162
166
|
/** Enable provider-native web search. Each provider uses its own format:
|
|
@@ -292,17 +296,74 @@ declare class ProviderRegistryImpl {
|
|
|
292
296
|
/** Global provider registry. Import this to register custom providers. */
|
|
293
297
|
declare const providerRegistry: ProviderRegistryImpl;
|
|
294
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Error model for gg-ai and downstream consumers.
|
|
301
|
+
*
|
|
302
|
+
* Every error users see should answer one question: "is this me or them?"
|
|
303
|
+
* That answer drives whether they retry, switch model, log in, or report a
|
|
304
|
+
* ggcoder bug. The `FormattedError` shape captures it in plain English:
|
|
305
|
+
*
|
|
306
|
+
* ✗ OpenAI returned an error.
|
|
307
|
+
* An error occurred while processing your request...
|
|
308
|
+
* → This is an OpenAI issue, not ggcoder. Retry — if it persists, check status.openai.com.
|
|
309
|
+
*
|
|
310
|
+
* ✗ ggcoder hit an unexpected error.
|
|
311
|
+
* Cannot read property 'foo' of undefined
|
|
312
|
+
* → This is a ggcoder bug — please report it.
|
|
313
|
+
*/
|
|
314
|
+
type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
|
|
315
|
+
interface FormattedError {
|
|
316
|
+
/** Plain-English headline, e.g. "OpenAI returned an error." */
|
|
317
|
+
headline: string;
|
|
318
|
+
/** Machine-readable classification. */
|
|
319
|
+
source: ErrorSource;
|
|
320
|
+
/** Detailed message body from the underlying error (no JSON, no tag prefix). */
|
|
321
|
+
message: string;
|
|
322
|
+
/** Action line — tells the user whether to retry, switch model, log in, or report a bug. */
|
|
323
|
+
guidance: string;
|
|
324
|
+
/** Provider name when source === "provider". */
|
|
325
|
+
provider?: string;
|
|
326
|
+
/** HTTP status code if known. */
|
|
327
|
+
statusCode?: number;
|
|
328
|
+
/** Provider request ID, kept for telemetry / debug — not shown by default. */
|
|
329
|
+
requestId?: string;
|
|
330
|
+
}
|
|
295
331
|
declare class GGAIError extends Error {
|
|
296
|
-
|
|
332
|
+
readonly source: ErrorSource;
|
|
333
|
+
readonly requestId?: string;
|
|
334
|
+
readonly hint?: string;
|
|
335
|
+
constructor(message: string, options?: {
|
|
336
|
+
source?: ErrorSource;
|
|
337
|
+
requestId?: string;
|
|
338
|
+
hint?: string;
|
|
339
|
+
cause?: unknown;
|
|
340
|
+
});
|
|
297
341
|
}
|
|
298
342
|
declare class ProviderError extends GGAIError {
|
|
299
343
|
readonly provider: string;
|
|
300
344
|
readonly statusCode?: number;
|
|
301
345
|
constructor(provider: string, message: string, options?: {
|
|
302
346
|
statusCode?: number;
|
|
347
|
+
requestId?: string;
|
|
348
|
+
hint?: string;
|
|
303
349
|
cause?: unknown;
|
|
304
350
|
});
|
|
305
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Normalise any thrown value into a structured display object. Always returns
|
|
354
|
+
* a non-empty `headline` and `guidance` so the UI never has to second-guess
|
|
355
|
+
* what to show the user.
|
|
356
|
+
*/
|
|
357
|
+
declare function formatError(err: unknown): FormattedError;
|
|
358
|
+
/**
|
|
359
|
+
* Render a FormattedError as a multi-line string for terminal display.
|
|
360
|
+
*
|
|
361
|
+
* Format:
|
|
362
|
+
* <headline>
|
|
363
|
+
* <message>
|
|
364
|
+
* → <guidance>
|
|
365
|
+
*/
|
|
366
|
+
declare function formatErrorForDisplay(err: unknown): string;
|
|
306
367
|
|
|
307
368
|
interface PalsuProviderState {
|
|
308
369
|
callCount: number;
|
|
@@ -373,4 +434,4 @@ interface PalsuProviderConfig {
|
|
|
373
434
|
*/
|
|
374
435
|
declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
|
|
375
436
|
|
|
376
|
-
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, EventStream, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
|
|
437
|
+
export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,164 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var GGAIError = class extends Error {
|
|
3
|
+
source;
|
|
4
|
+
requestId;
|
|
5
|
+
hint;
|
|
3
6
|
constructor(message, options) {
|
|
4
|
-
super(message, options);
|
|
7
|
+
super(message, { cause: options?.cause });
|
|
5
8
|
this.name = "GGAIError";
|
|
9
|
+
this.source = options?.source ?? "ggcoder";
|
|
10
|
+
this.requestId = options?.requestId;
|
|
11
|
+
this.hint = options?.hint;
|
|
6
12
|
}
|
|
7
13
|
};
|
|
8
14
|
var ProviderError = class extends GGAIError {
|
|
9
15
|
provider;
|
|
10
16
|
statusCode;
|
|
11
17
|
constructor(provider, message, options) {
|
|
12
|
-
super(
|
|
18
|
+
super(message, {
|
|
19
|
+
source: "provider",
|
|
20
|
+
requestId: options?.requestId,
|
|
21
|
+
hint: options?.hint,
|
|
22
|
+
cause: options?.cause
|
|
23
|
+
});
|
|
13
24
|
this.name = "ProviderError";
|
|
14
25
|
this.provider = provider;
|
|
15
26
|
this.statusCode = options?.statusCode;
|
|
16
27
|
}
|
|
17
28
|
};
|
|
29
|
+
var PROVIDER_DISPLAY = {
|
|
30
|
+
openai: "OpenAI",
|
|
31
|
+
anthropic: "Anthropic",
|
|
32
|
+
glm: "Z.AI (GLM)",
|
|
33
|
+
moonshot: "Moonshot",
|
|
34
|
+
deepseek: "DeepSeek",
|
|
35
|
+
openrouter: "OpenRouter",
|
|
36
|
+
xiaomi: "Xiaomi (MiMo)",
|
|
37
|
+
minimax: "MiniMax"
|
|
38
|
+
};
|
|
39
|
+
var PROVIDER_STATUS_URL = {
|
|
40
|
+
openai: "status.openai.com",
|
|
41
|
+
anthropic: "status.anthropic.com"
|
|
42
|
+
};
|
|
43
|
+
function providerDisplayName(provider) {
|
|
44
|
+
return PROVIDER_DISPLAY[provider] ?? provider;
|
|
45
|
+
}
|
|
46
|
+
function formatError(err) {
|
|
47
|
+
if (err instanceof ProviderError) {
|
|
48
|
+
const name = providerDisplayName(err.provider);
|
|
49
|
+
const cleanMessage = cleanProviderMessage(err.message);
|
|
50
|
+
return {
|
|
51
|
+
headline: `${name} returned an error.`,
|
|
52
|
+
source: "provider",
|
|
53
|
+
message: cleanMessage,
|
|
54
|
+
provider: err.provider,
|
|
55
|
+
statusCode: err.statusCode,
|
|
56
|
+
requestId: err.requestId,
|
|
57
|
+
guidance: err.hint ?? providerGuidance(err.provider, cleanMessage, err.statusCode)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (err instanceof GGAIError) {
|
|
61
|
+
return finaliseBySource(err.source, err.message, err.requestId, err.hint);
|
|
62
|
+
}
|
|
63
|
+
if (err instanceof Error) {
|
|
64
|
+
const source = inferSource(err);
|
|
65
|
+
return finaliseBySource(source, err.message, void 0, void 0);
|
|
66
|
+
}
|
|
67
|
+
return finaliseBySource("ggcoder", String(err), void 0, void 0);
|
|
68
|
+
}
|
|
69
|
+
function finaliseBySource(source, message, requestId, hint) {
|
|
70
|
+
switch (source) {
|
|
71
|
+
case "network":
|
|
72
|
+
return {
|
|
73
|
+
headline: "Network error \u2014 couldn't reach the provider.",
|
|
74
|
+
source,
|
|
75
|
+
message,
|
|
76
|
+
guidance: hint ?? "Check your internet connection. Not a ggcoder issue \u2014 retry shortly.",
|
|
77
|
+
...requestId ? { requestId } : {}
|
|
78
|
+
};
|
|
79
|
+
case "auth":
|
|
80
|
+
return {
|
|
81
|
+
headline: "Authentication issue.",
|
|
82
|
+
source,
|
|
83
|
+
message,
|
|
84
|
+
guidance: hint ?? "Run `ggcoder login` to refresh your credentials.",
|
|
85
|
+
...requestId ? { requestId } : {}
|
|
86
|
+
};
|
|
87
|
+
case "provider":
|
|
88
|
+
return {
|
|
89
|
+
headline: "Provider returned an error.",
|
|
90
|
+
source,
|
|
91
|
+
message,
|
|
92
|
+
guidance: hint ?? providerGuidance(void 0, message, void 0),
|
|
93
|
+
...requestId ? { requestId } : {}
|
|
94
|
+
};
|
|
95
|
+
case "ggcoder":
|
|
96
|
+
return {
|
|
97
|
+
headline: "ggcoder hit an unexpected error.",
|
|
98
|
+
source,
|
|
99
|
+
message,
|
|
100
|
+
guidance: hint ?? "This looks like a ggcoder bug \u2014 please report it to the developer (see /help).",
|
|
101
|
+
...requestId ? { requestId } : {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function formatErrorForDisplay(err) {
|
|
106
|
+
const f = formatError(err);
|
|
107
|
+
const lines = [f.headline];
|
|
108
|
+
if (f.message && f.message !== f.headline) lines.push(` ${f.message}`);
|
|
109
|
+
lines.push(` \u2192 ${f.guidance}`);
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
function cleanProviderMessage(message) {
|
|
113
|
+
return message.replace(/^\[[^\]]+\]\s*/, "").trim();
|
|
114
|
+
}
|
|
115
|
+
function inferSource(err) {
|
|
116
|
+
const msg = err.message.toLowerCase();
|
|
117
|
+
const code = err.code ?? "";
|
|
118
|
+
if (code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "ENOTFOUND" || code === "ECONNRESET" || msg.includes("fetch failed") || msg.includes("network request failed")) {
|
|
119
|
+
return "network";
|
|
120
|
+
}
|
|
121
|
+
if (msg.includes("not logged in") || msg.includes("token exchange failed") || msg.includes("token refresh failed") || msg.includes("invalid_grant")) {
|
|
122
|
+
return "auth";
|
|
123
|
+
}
|
|
124
|
+
return "ggcoder";
|
|
125
|
+
}
|
|
126
|
+
function providerGuidance(provider, message, statusCode) {
|
|
127
|
+
const name = provider ? providerDisplayName(provider) : "the provider";
|
|
128
|
+
const status = provider ? PROVIDER_STATUS_URL[provider] : void 0;
|
|
129
|
+
const lower = message.toLowerCase();
|
|
130
|
+
if (statusCode === 401 || lower.includes("unauthorized") || lower.includes("invalid api key")) {
|
|
131
|
+
return `Authentication failed with ${name}. Run \`ggcoder login\` to refresh your credentials.`;
|
|
132
|
+
}
|
|
133
|
+
if (lower.includes("overloaded") || lower.includes("engine_overloaded")) {
|
|
134
|
+
return `${name}'s servers are overloaded right now. Retry in a moment \u2014 not a ggcoder issue.`;
|
|
135
|
+
}
|
|
136
|
+
if (lower.includes("insufficient balance") || lower.includes("quota exceeded") || lower.includes("recharge") || lower.includes("no resource package")) {
|
|
137
|
+
return `Your ${name} account has a billing or quota issue \u2014 check your balance. Not a ggcoder issue.`;
|
|
138
|
+
}
|
|
139
|
+
if (statusCode === 429 || lower.includes("rate limit") || lower.includes("too many requests")) {
|
|
140
|
+
return `${name} rate limit hit. Wait a moment then retry \u2014 not a ggcoder issue.`;
|
|
141
|
+
}
|
|
142
|
+
if (statusCode === 502 || lower.includes("bad gateway")) {
|
|
143
|
+
return `${name} returned a bad gateway. Retry \u2014 this is on their side, not ggcoder.`;
|
|
144
|
+
}
|
|
145
|
+
if (statusCode === 503 || lower.includes("service unavailable")) {
|
|
146
|
+
return `${name} is temporarily unavailable. Retry shortly \u2014 not a ggcoder issue.`;
|
|
147
|
+
}
|
|
148
|
+
if (statusCode === 500 || lower.includes("server_error") || lower.includes("500") && lower.includes("internal server error")) {
|
|
149
|
+
return status ? `This is an error from ${name}, not ggcoder. Retry \u2014 if it keeps happening, check ${status}.` : `This is an error from ${name}, not ggcoder. Retry \u2014 if it keeps happening, try a different model with /model.`;
|
|
150
|
+
}
|
|
151
|
+
if (lower.includes("timeout") || lower.includes("timed out")) {
|
|
152
|
+
return `Request to ${name} timed out. Their servers may be slow \u2014 retry. Not a ggcoder issue.`;
|
|
153
|
+
}
|
|
154
|
+
if (lower.includes("does not recognize the requested model") || lower.includes("model") && (lower.includes("not exist") || lower.includes("not found") || lower.includes("no access"))) {
|
|
155
|
+
return `${name} doesn't recognise this model on your account. Use /model to switch, or check your subscription tier.`;
|
|
156
|
+
}
|
|
157
|
+
if (lower.includes("context_length_exceeded") || lower.includes("prompt is too long")) {
|
|
158
|
+
return `Context window for this ${name} model is full. Run /compact to shrink history, or start a new session.`;
|
|
159
|
+
}
|
|
160
|
+
return status ? `This is an error from ${name}, not ggcoder. Retry \u2014 if it persists, check ${status}.` : `This is an error from ${name}, not ggcoder. Retry \u2014 if it persists, try a different model with /model.`;
|
|
161
|
+
}
|
|
18
162
|
|
|
19
163
|
// src/providers/anthropic.ts
|
|
20
164
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -246,9 +390,18 @@ function toAnthropicToolResultContent(content) {
|
|
|
246
390
|
};
|
|
247
391
|
});
|
|
248
392
|
}
|
|
393
|
+
function remapAnthropicToolCallId(id, idMap) {
|
|
394
|
+
if (/^[a-zA-Z0-9_-]+$/.test(id)) return id;
|
|
395
|
+
const existing = idMap.get(id);
|
|
396
|
+
if (existing) return existing;
|
|
397
|
+
const mapped = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
398
|
+
idMap.set(id, mapped);
|
|
399
|
+
return mapped;
|
|
400
|
+
}
|
|
249
401
|
function toAnthropicMessages(messages, cacheControl) {
|
|
250
402
|
let systemText;
|
|
251
403
|
const out = [];
|
|
404
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
252
405
|
for (const msg of messages) {
|
|
253
406
|
if (msg.role === "system") {
|
|
254
407
|
systemText = msg.content;
|
|
@@ -283,7 +436,7 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
283
436
|
if (part.type === "tool_call")
|
|
284
437
|
return {
|
|
285
438
|
type: "tool_use",
|
|
286
|
-
id: part.id,
|
|
439
|
+
id: remapAnthropicToolCallId(part.id, idMap),
|
|
287
440
|
name: part.name,
|
|
288
441
|
input: part.args
|
|
289
442
|
};
|
|
@@ -308,7 +461,7 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
308
461
|
role: "user",
|
|
309
462
|
content: msg.content.map((result) => ({
|
|
310
463
|
type: "tool_result",
|
|
311
|
-
tool_use_id: result.toolCallId,
|
|
464
|
+
tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
|
|
312
465
|
content: toAnthropicToolResultContent(result.content),
|
|
313
466
|
is_error: result.isError
|
|
314
467
|
}))
|
|
@@ -364,12 +517,19 @@ function toAnthropicMessages(messages, cacheControl) {
|
|
|
364
517
|
}
|
|
365
518
|
return { system, messages: out };
|
|
366
519
|
}
|
|
367
|
-
function toAnthropicTools(tools) {
|
|
368
|
-
return tools.map((tool) =>
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
520
|
+
function toAnthropicTools(tools, options) {
|
|
521
|
+
return tools.map((tool, index) => {
|
|
522
|
+
const anthropicTool = {
|
|
523
|
+
name: tool.name,
|
|
524
|
+
description: tool.description,
|
|
525
|
+
input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
|
|
526
|
+
...options?.enableFineGrainedToolStreaming ? { eager_input_streaming: true } : {}
|
|
527
|
+
};
|
|
528
|
+
if (options?.cacheControl && index === tools.length - 1) {
|
|
529
|
+
anthropicTool.cache_control = options.cacheControl;
|
|
530
|
+
}
|
|
531
|
+
return anthropicTool;
|
|
532
|
+
});
|
|
373
533
|
}
|
|
374
534
|
function toAnthropicToolChoice(choice) {
|
|
375
535
|
if (choice === "auto") return { type: "auto" };
|
|
@@ -588,6 +748,7 @@ async function* runStream(options) {
|
|
|
588
748
|
const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
|
|
589
749
|
const useStreaming = options.streaming !== false;
|
|
590
750
|
const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
|
|
751
|
+
const supportsFirstPartyToolExtras = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
|
|
591
752
|
const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
592
753
|
const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl);
|
|
593
754
|
const system = isOAuth ? [
|
|
@@ -620,7 +781,10 @@ async function* runStream(options) {
|
|
|
620
781
|
...options.stop ? { stop_sequences: options.stop } : {},
|
|
621
782
|
...options.tools?.length || options.serverTools?.length || options.webSearch ? {
|
|
622
783
|
tools: [
|
|
623
|
-
...options.tools?.length ? toAnthropicTools(options.tools
|
|
784
|
+
...options.tools?.length ? toAnthropicTools(options.tools, {
|
|
785
|
+
...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
|
|
786
|
+
...supportsFirstPartyToolExtras ? { enableFineGrainedToolStreaming: true } : {}
|
|
787
|
+
}) : [],
|
|
624
788
|
...options.serverTools ?? [],
|
|
625
789
|
...options.webSearch ? [{ type: "web_search_20250305", name: "web_search" }] : []
|
|
626
790
|
]
|
|
@@ -953,8 +1117,10 @@ function messageToResponse(message) {
|
|
|
953
1117
|
}
|
|
954
1118
|
function toError(err) {
|
|
955
1119
|
if (err instanceof Anthropic.APIError) {
|
|
1120
|
+
const requestId = err.request_id ?? err.error?.request_id;
|
|
956
1121
|
return new ProviderError("anthropic", err.message, {
|
|
957
1122
|
statusCode: err.status,
|
|
1123
|
+
...requestId ? { requestId } : {},
|
|
958
1124
|
cause: err
|
|
959
1125
|
});
|
|
960
1126
|
}
|
|
@@ -1004,12 +1170,15 @@ async function* runStream2(options) {
|
|
|
1004
1170
|
};
|
|
1005
1171
|
if (options.provider === "openai" || options.provider === "moonshot") {
|
|
1006
1172
|
const paramsAny = params;
|
|
1007
|
-
paramsAny.prompt_cache_key = "ggcoder";
|
|
1173
|
+
paramsAny.prompt_cache_key = options.promptCacheKey ?? "ggcoder";
|
|
1008
1174
|
const retention = options.cacheRetention ?? "short";
|
|
1009
1175
|
if (retention === "long") {
|
|
1010
1176
|
paramsAny.prompt_cache_retention = "24h";
|
|
1011
1177
|
}
|
|
1012
1178
|
}
|
|
1179
|
+
if (options.provider === "openai" && options.serviceTier) {
|
|
1180
|
+
params.service_tier = options.serviceTier;
|
|
1181
|
+
}
|
|
1013
1182
|
if (usesThinkingParam) {
|
|
1014
1183
|
if (options.thinking) {
|
|
1015
1184
|
params.thinking = { type: "enabled" };
|
|
@@ -1246,19 +1415,19 @@ function completionToResponse(completion) {
|
|
|
1246
1415
|
}
|
|
1247
1416
|
function toError2(err, provider = "openai") {
|
|
1248
1417
|
if (err instanceof OpenAI.APIError) {
|
|
1249
|
-
let msg = err.message;
|
|
1250
1418
|
const body = err.error;
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
return new ProviderError(provider, msg, {
|
|
1419
|
+
const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
|
|
1420
|
+
const modelName = typeof body?.model === "string" ? body.model : "";
|
|
1421
|
+
const cleanMessage = bodyMessage ?? err.message;
|
|
1422
|
+
let hint;
|
|
1423
|
+
if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
|
|
1424
|
+
hint = "codex-mini-latest requires an OpenAI Pro or Max subscription. Your account currently has access to GPT-5.4 and GPT-5.4 Mini.";
|
|
1425
|
+
}
|
|
1426
|
+
const requestId = err.request_id ?? (typeof body?.request_id === "string" ? body.request_id : void 0);
|
|
1427
|
+
return new ProviderError(provider, cleanMessage, {
|
|
1261
1428
|
statusCode: err.status,
|
|
1429
|
+
...requestId ? { requestId } : {},
|
|
1430
|
+
...hint ? { hint } : {},
|
|
1262
1431
|
cause: err
|
|
1263
1432
|
});
|
|
1264
1433
|
}
|
|
@@ -1292,6 +1461,9 @@ async function* runStream3(options) {
|
|
|
1292
1461
|
if (options.tools?.length) {
|
|
1293
1462
|
body.tools = toCodexTools(options.tools);
|
|
1294
1463
|
}
|
|
1464
|
+
if (options.promptCacheKey) {
|
|
1465
|
+
body.prompt_cache_key = options.promptCacheKey;
|
|
1466
|
+
}
|
|
1295
1467
|
if (options.temperature != null && !options.thinking) {
|
|
1296
1468
|
body.temperature = options.temperature;
|
|
1297
1469
|
}
|
|
@@ -1320,19 +1492,19 @@ async function* runStream3(options) {
|
|
|
1320
1492
|
});
|
|
1321
1493
|
if (!response.ok) {
|
|
1322
1494
|
const text = await response.text().catch(() => "");
|
|
1323
|
-
|
|
1495
|
+
const parsed = parseCodexErrorBody(text);
|
|
1496
|
+
const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
|
|
1497
|
+
const requestId = parsed.requestId ?? response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? void 0;
|
|
1498
|
+
let hint;
|
|
1324
1499
|
if (response.status === 400 && text.includes("not supported")) {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
}
|
|
1329
|
-
if (response.status === 404 && text.includes("does not exist")) {
|
|
1330
|
-
message += `
|
|
1331
|
-
|
|
1332
|
-
Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GPT-5.4 and GPT-5.4 Mini work with any active ChatGPT plan.`;
|
|
1500
|
+
hint = 'Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription. The "codex-spark" variants require ChatGPT Pro. Check your subscription at https://chatgpt.com/settings.';
|
|
1501
|
+
} else if (response.status === 404 && text.includes("does not exist")) {
|
|
1502
|
+
hint = "codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GPT-5.4 and GPT-5.4 Mini work with any active ChatGPT plan.";
|
|
1333
1503
|
}
|
|
1334
1504
|
throw new ProviderError("openai", message, {
|
|
1335
|
-
statusCode: response.status
|
|
1505
|
+
statusCode: response.status,
|
|
1506
|
+
...requestId ? { requestId } : {},
|
|
1507
|
+
...hint ? { hint } : {}
|
|
1336
1508
|
});
|
|
1337
1509
|
}
|
|
1338
1510
|
if (!response.body) {
|
|
@@ -1343,16 +1515,27 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
|
|
|
1343
1515
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
1344
1516
|
let inputTokens = 0;
|
|
1345
1517
|
let outputTokens = 0;
|
|
1518
|
+
let cacheRead = 0;
|
|
1346
1519
|
for await (const event of parseSSE(response.body)) {
|
|
1347
1520
|
const type = event.type;
|
|
1348
1521
|
if (!type) continue;
|
|
1349
1522
|
if (type === "error") {
|
|
1350
|
-
const
|
|
1351
|
-
|
|
1523
|
+
const nested = event.error ?? void 0;
|
|
1524
|
+
const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
|
|
1525
|
+
const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
|
|
1526
|
+
const requestId = extractCodexRequestId(message) ?? event.request_id;
|
|
1527
|
+
throw new ProviderError("openai", message, {
|
|
1528
|
+
...requestId != null ? { requestId } : {},
|
|
1529
|
+
...code === "server_error" ? { statusCode: 500 } : {}
|
|
1530
|
+
});
|
|
1352
1531
|
}
|
|
1353
1532
|
if (type === "response.failed") {
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1533
|
+
const nested = event.error;
|
|
1534
|
+
const message = nested?.message ?? "Codex response failed.";
|
|
1535
|
+
const requestId = extractCodexRequestId(message) ?? event.request_id;
|
|
1536
|
+
throw new ProviderError("openai", message, {
|
|
1537
|
+
...requestId != null ? { requestId } : {}
|
|
1538
|
+
});
|
|
1356
1539
|
}
|
|
1357
1540
|
if (type === "response.output_text.delta") {
|
|
1358
1541
|
const delta = event.delta;
|
|
@@ -1425,7 +1608,8 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
|
|
|
1425
1608
|
const resp = event.response;
|
|
1426
1609
|
const usage = resp?.usage;
|
|
1427
1610
|
if (usage) {
|
|
1428
|
-
|
|
1611
|
+
cacheRead = usage.input_tokens_details?.cached_tokens ?? 0;
|
|
1612
|
+
inputTokens = (usage.input_tokens ?? 0) - cacheRead;
|
|
1429
1613
|
outputTokens = usage.output_tokens ?? 0;
|
|
1430
1614
|
}
|
|
1431
1615
|
}
|
|
@@ -1455,7 +1639,7 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
|
|
|
1455
1639
|
content: contentParts.length > 0 ? contentParts : textAccum || ""
|
|
1456
1640
|
},
|
|
1457
1641
|
stopReason,
|
|
1458
|
-
usage: { inputTokens, outputTokens }
|
|
1642
|
+
usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
|
|
1459
1643
|
};
|
|
1460
1644
|
yield { type: "done", stopReason };
|
|
1461
1645
|
return streamResponse;
|
|
@@ -1597,6 +1781,23 @@ function toCodexTools(tools) {
|
|
|
1597
1781
|
strict: null
|
|
1598
1782
|
}));
|
|
1599
1783
|
}
|
|
1784
|
+
function extractCodexRequestId(message) {
|
|
1785
|
+
const match = message.match(/request ID ([a-z0-9-]{8,})/i);
|
|
1786
|
+
return match?.[1];
|
|
1787
|
+
}
|
|
1788
|
+
function parseCodexErrorBody(text) {
|
|
1789
|
+
if (!text) return {};
|
|
1790
|
+
try {
|
|
1791
|
+
const parsed = JSON.parse(text);
|
|
1792
|
+
const error = parsed.error;
|
|
1793
|
+
const message = error?.message ?? parsed.message;
|
|
1794
|
+
const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
|
|
1795
|
+
return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
|
|
1796
|
+
} catch {
|
|
1797
|
+
const trimmed = text.trim().slice(0, 240);
|
|
1798
|
+
return trimmed ? { message: trimmed } : {};
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1600
1801
|
|
|
1601
1802
|
// src/provider-registry.ts
|
|
1602
1803
|
var ProviderRegistryImpl = class {
|
|
@@ -1856,6 +2057,8 @@ export {
|
|
|
1856
2057
|
GGAIError,
|
|
1857
2058
|
ProviderError,
|
|
1858
2059
|
StreamResult,
|
|
2060
|
+
formatError,
|
|
2061
|
+
formatErrorForDisplay,
|
|
1859
2062
|
palsuAssistantMessage,
|
|
1860
2063
|
palsuText,
|
|
1861
2064
|
palsuThinking,
|