@byfriends/kosong 0.1.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/LICENSE +28 -0
- package/README.md +11 -0
- package/dist/anthropic-Dm_GqFgS.d.mts +69 -0
- package/dist/capability-registry-CMBuEYcf.mjs +161 -0
- package/dist/chat-completions-stream-BuMu_xr9.mjs +62 -0
- package/dist/errors-DweKbIOf.d.mts +42 -0
- package/dist/errors-WFxxzL1B.mjs +80 -0
- package/dist/google-genai-hX0X6CF3.d.mts +98 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.mjs +287 -0
- package/dist/openai-common-08qin3UI.mjs +278 -0
- package/dist/openai-common-B6cK2ig3.d.mts +105 -0
- package/dist/openai-compat-CMrIk-ib.d.mts +132 -0
- package/dist/openai-compat-CWbwO4b7.mjs +801 -0
- package/dist/openai-legacy-B6CVfLlr.d.mts +71 -0
- package/dist/openai-responses-BxOwxtd3.d.mts +65 -0
- package/dist/provider-DiJKWMsQ.d.mts +371 -0
- package/dist/providers/anthropic.d.mts +2 -0
- package/dist/providers/anthropic.mjs +720 -0
- package/dist/providers/google-genai.d.mts +2 -0
- package/dist/providers/google-genai.mjs +562 -0
- package/dist/providers/openai-common.d.mts +2 -0
- package/dist/providers/openai-common.mjs +2 -0
- package/dist/providers/openai-compat.d.mts +2 -0
- package/dist/providers/openai-compat.mjs +2 -0
- package/dist/providers/openai-legacy.d.mts +2 -0
- package/dist/providers/openai-legacy.mjs +248 -0
- package/dist/providers/openai-responses.d.mts +2 -0
- package/dist/providers/openai-responses.mjs +623 -0
- package/dist/request-auth-DCWSyCKI.mjs +63 -0
- package/package.json +89 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BYF PROPRIETARY LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-2027 ByronFinn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted to copy and redistribute this software in its
|
|
6
|
+
unmodified form, without charge. This permission does not extend to modified
|
|
7
|
+
versions of the software.
|
|
8
|
+
|
|
9
|
+
Local modification of this software for personal use is permitted, provided
|
|
10
|
+
that such modifications are not distributed to third parties.
|
|
11
|
+
|
|
12
|
+
Restrictions:
|
|
13
|
+
1. You may NOT redistribute modified versions of this software.
|
|
14
|
+
2. You may NOT use this software for commercial purposes.
|
|
15
|
+
3. You may NOT sublicense, sell, or offer this software as a service.
|
|
16
|
+
|
|
17
|
+
This software is source-available but NOT open source. The source code is
|
|
18
|
+
made publicly visible for review purposes only.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
For questions about licensing, contact: https://github.com/ByronFinn/byf/issues
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @byfriends/kosong
|
|
2
|
+
|
|
3
|
+
LLM abstraction layer used by BYF.
|
|
4
|
+
|
|
5
|
+
Part of the [BYF](https://github.com/ByronFinn/byf) monorepo.
|
|
6
|
+
|
|
7
|
+
See the main repository for documentation, issues, and contribution guidelines.
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
Proprietary — see the root LICENSE file.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { a as StreamedMessage, b as Message, i as ProviderRequestAuth, m as ModelCapability, o as ThinkingEffort, p as Tool, r as GenerateOptions, t as ChatProvider } from "./provider-DiJKWMsQ.mjs";
|
|
2
|
+
import { o as ChatProviderError } from "./errors-DweKbIOf.mjs";
|
|
3
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
4
|
+
import { MessageCreateParams } from "@anthropic-ai/sdk/resources/messages/messages.js";
|
|
5
|
+
|
|
6
|
+
//#region src/providers/anthropic.d.ts
|
|
7
|
+
interface AnthropicOptions {
|
|
8
|
+
apiKey?: string | undefined;
|
|
9
|
+
baseUrl?: string | undefined;
|
|
10
|
+
model: string;
|
|
11
|
+
defaultMaxTokens?: number | undefined;
|
|
12
|
+
betaFeatures?: string[] | undefined;
|
|
13
|
+
defaultHeaders?: Record<string, string>;
|
|
14
|
+
metadata?: Record<string, string> | undefined;
|
|
15
|
+
/** Use streaming API. Defaults to true. Set to false for non-streaming (test/fallback). */
|
|
16
|
+
stream?: boolean | undefined;
|
|
17
|
+
clientFactory?: (auth: ProviderRequestAuth) => Anthropic;
|
|
18
|
+
}
|
|
19
|
+
interface AnthropicGenerationKwargs {
|
|
20
|
+
max_tokens?: number | undefined;
|
|
21
|
+
temperature?: number | undefined;
|
|
22
|
+
top_k?: number | undefined;
|
|
23
|
+
top_p?: number | undefined;
|
|
24
|
+
thinking?: MessageCreateParams['thinking'] | undefined;
|
|
25
|
+
output_config?: MessageCreateParams['output_config'] | undefined;
|
|
26
|
+
betaFeatures?: string[] | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the default `max_tokens` for an Anthropic request.
|
|
30
|
+
*
|
|
31
|
+
* Precedence:
|
|
32
|
+
* 1. Caller-provided `override` (e.g. `models.<alias>.maxOutputSize`
|
|
33
|
+
* from the harness config) — honored when present so users can
|
|
34
|
+
* intentionally lower the budget (handy for forcing truncation
|
|
35
|
+
* in tests) or raise it on a model we don't yet know about.
|
|
36
|
+
* 2. When the model id parses to a known Claude family + version,
|
|
37
|
+
* the override is clamped to the documented Messages-API ceiling
|
|
38
|
+
* so we never send a value the server would reject.
|
|
39
|
+
* 3. With no override and no recognized version, fall back to
|
|
40
|
+
* {@link FALLBACK_MAX_TOKENS}.
|
|
41
|
+
*/
|
|
42
|
+
declare function resolveDefaultMaxTokens(model: string, override?: number): number;
|
|
43
|
+
declare function convertAnthropicError(error: unknown): ChatProviderError;
|
|
44
|
+
declare class AnthropicChatProvider implements ChatProvider {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
private _model;
|
|
47
|
+
private _stream;
|
|
48
|
+
private _client;
|
|
49
|
+
private _generationKwargs;
|
|
50
|
+
private _metadata;
|
|
51
|
+
private _apiKey;
|
|
52
|
+
private _baseUrl;
|
|
53
|
+
private _defaultHeaders;
|
|
54
|
+
private _clientFactory;
|
|
55
|
+
constructor(options: AnthropicOptions);
|
|
56
|
+
get modelName(): string;
|
|
57
|
+
get thinkingEffort(): ThinkingEffort | null;
|
|
58
|
+
get modelParameters(): Record<string, unknown>;
|
|
59
|
+
getCapability(model?: string): ModelCapability;
|
|
60
|
+
generate(systemPrompt: string, tools: Tool[], history: Message[], options?: GenerateOptions): Promise<StreamedMessage>;
|
|
61
|
+
private _createClient;
|
|
62
|
+
private _buildClient;
|
|
63
|
+
withThinking(effort: ThinkingEffort): AnthropicChatProvider;
|
|
64
|
+
withGenerationKwargs(kwargs: Partial<AnthropicGenerationKwargs>): AnthropicChatProvider;
|
|
65
|
+
private _withGenerationKwargs;
|
|
66
|
+
private _clone;
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { resolveDefaultMaxTokens as i, AnthropicOptions as n, convertAnthropicError as r, AnthropicChatProvider as t };
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { i as UNKNOWN_CAPABILITY } from "./request-auth-DCWSyCKI.mjs";
|
|
2
|
+
//#region src/providers/capability-registry.ts
|
|
3
|
+
const OPENAI_RESPONSES_DEVELOPER_ROLE_MODELS = new Set([
|
|
4
|
+
"gpt-4.1",
|
|
5
|
+
"gpt-4.1-mini",
|
|
6
|
+
"gpt-4.1-nano",
|
|
7
|
+
"gpt-5-codex",
|
|
8
|
+
"o1",
|
|
9
|
+
"o1-mini",
|
|
10
|
+
"o1-pro",
|
|
11
|
+
"o3",
|
|
12
|
+
"o3-mini",
|
|
13
|
+
"o3-pro",
|
|
14
|
+
"o4-mini"
|
|
15
|
+
]);
|
|
16
|
+
const OPENAI_VISION_TOOL_PREFIXES = [
|
|
17
|
+
"gpt-4o",
|
|
18
|
+
"gpt-4-turbo",
|
|
19
|
+
"gpt-4.1",
|
|
20
|
+
"gpt-4.5"
|
|
21
|
+
];
|
|
22
|
+
const CLAUDE_3_PREFIXES = [
|
|
23
|
+
"claude-3-",
|
|
24
|
+
"claude-3.5-",
|
|
25
|
+
"claude-3.7-"
|
|
26
|
+
];
|
|
27
|
+
const CLAUDE_4_PREFIXES = [
|
|
28
|
+
"claude-opus-4",
|
|
29
|
+
"claude-sonnet-4",
|
|
30
|
+
"claude-haiku-4"
|
|
31
|
+
];
|
|
32
|
+
const GEMINI_CATALOGUED_PREFIXES = [
|
|
33
|
+
"gemini-1.5-pro",
|
|
34
|
+
"gemini-1.5-flash",
|
|
35
|
+
"gemini-2.0-flash",
|
|
36
|
+
"gemini-2.0-pro",
|
|
37
|
+
"gemini-2.5-pro",
|
|
38
|
+
"gemini-2.5-flash"
|
|
39
|
+
];
|
|
40
|
+
const OPENAI_REASONING_CAPABILITY = Object.freeze({
|
|
41
|
+
image_in: false,
|
|
42
|
+
video_in: false,
|
|
43
|
+
audio_in: false,
|
|
44
|
+
thinking: true,
|
|
45
|
+
tool_use: true,
|
|
46
|
+
max_context_tokens: 0
|
|
47
|
+
});
|
|
48
|
+
const OPENAI_VISION_TOOL_CAPABILITY = Object.freeze({
|
|
49
|
+
image_in: true,
|
|
50
|
+
video_in: false,
|
|
51
|
+
audio_in: false,
|
|
52
|
+
thinking: false,
|
|
53
|
+
tool_use: true,
|
|
54
|
+
max_context_tokens: 0
|
|
55
|
+
});
|
|
56
|
+
const OPENAI_TEXT_TOOL_CAPABILITY = Object.freeze({
|
|
57
|
+
image_in: false,
|
|
58
|
+
video_in: false,
|
|
59
|
+
audio_in: false,
|
|
60
|
+
thinking: false,
|
|
61
|
+
tool_use: true,
|
|
62
|
+
max_context_tokens: 0
|
|
63
|
+
});
|
|
64
|
+
const ANTHROPIC_VISION_TOOL_CAPABILITY = Object.freeze({
|
|
65
|
+
image_in: true,
|
|
66
|
+
video_in: false,
|
|
67
|
+
audio_in: false,
|
|
68
|
+
thinking: false,
|
|
69
|
+
tool_use: true,
|
|
70
|
+
max_context_tokens: 0
|
|
71
|
+
});
|
|
72
|
+
const ANTHROPIC_THINKING_VISION_TOOL_CAPABILITY = Object.freeze({
|
|
73
|
+
image_in: true,
|
|
74
|
+
video_in: false,
|
|
75
|
+
audio_in: false,
|
|
76
|
+
thinking: true,
|
|
77
|
+
tool_use: true,
|
|
78
|
+
max_context_tokens: 0
|
|
79
|
+
});
|
|
80
|
+
const GEMINI_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
|
|
81
|
+
image_in: true,
|
|
82
|
+
video_in: true,
|
|
83
|
+
audio_in: true,
|
|
84
|
+
thinking: false,
|
|
85
|
+
tool_use: true,
|
|
86
|
+
max_context_tokens: 0
|
|
87
|
+
});
|
|
88
|
+
const GEMINI_THINKING_MULTIMODAL_TOOL_CAPABILITY = Object.freeze({
|
|
89
|
+
image_in: true,
|
|
90
|
+
video_in: true,
|
|
91
|
+
audio_in: true,
|
|
92
|
+
thinking: true,
|
|
93
|
+
tool_use: true,
|
|
94
|
+
max_context_tokens: 0
|
|
95
|
+
});
|
|
96
|
+
const OPENAI_LEGACY_CAPABILITY_CATALOG = [
|
|
97
|
+
{
|
|
98
|
+
matches: isOpenAIReasoningModel,
|
|
99
|
+
capability: OPENAI_REASONING_CAPABILITY
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
matches: (name) => hasPrefix(name, OPENAI_VISION_TOOL_PREFIXES),
|
|
103
|
+
capability: OPENAI_VISION_TOOL_CAPABILITY
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
matches: (name) => name.startsWith("gpt-3.5-turbo"),
|
|
107
|
+
capability: OPENAI_TEXT_TOOL_CAPABILITY
|
|
108
|
+
}
|
|
109
|
+
];
|
|
110
|
+
const OPENAI_RESPONSES_CAPABILITY_CATALOG = [{
|
|
111
|
+
matches: isOpenAIReasoningModel,
|
|
112
|
+
capability: OPENAI_REASONING_CAPABILITY
|
|
113
|
+
}, {
|
|
114
|
+
matches: (name) => hasPrefix(name, OPENAI_VISION_TOOL_PREFIXES),
|
|
115
|
+
capability: OPENAI_VISION_TOOL_CAPABILITY
|
|
116
|
+
}];
|
|
117
|
+
const ANTHROPIC_CAPABILITY_CATALOG = [{
|
|
118
|
+
matches: (name) => hasPrefix(name, CLAUDE_3_PREFIXES),
|
|
119
|
+
capability: ANTHROPIC_VISION_TOOL_CAPABILITY
|
|
120
|
+
}, {
|
|
121
|
+
matches: (name) => hasPrefix(name, CLAUDE_4_PREFIXES),
|
|
122
|
+
capability: ANTHROPIC_THINKING_VISION_TOOL_CAPABILITY
|
|
123
|
+
}];
|
|
124
|
+
function normalizeModelName(modelName) {
|
|
125
|
+
return modelName.toLowerCase();
|
|
126
|
+
}
|
|
127
|
+
function hasPrefix(modelName, prefixes) {
|
|
128
|
+
return prefixes.some((prefix) => modelName.startsWith(prefix));
|
|
129
|
+
}
|
|
130
|
+
function isOpenAIReasoningModel(modelName) {
|
|
131
|
+
return /^o\d/.test(modelName);
|
|
132
|
+
}
|
|
133
|
+
function capabilityFromCatalog(modelName, catalog) {
|
|
134
|
+
const normalized = normalizeModelName(modelName);
|
|
135
|
+
for (const entry of catalog) if (entry.matches(normalized)) return entry.capability;
|
|
136
|
+
return UNKNOWN_CAPABILITY;
|
|
137
|
+
}
|
|
138
|
+
function getOpenAILegacyModelCapability(modelName) {
|
|
139
|
+
return capabilityFromCatalog(modelName, OPENAI_LEGACY_CAPABILITY_CATALOG);
|
|
140
|
+
}
|
|
141
|
+
function getOpenAIResponsesModelCapability(modelName) {
|
|
142
|
+
return capabilityFromCatalog(modelName, OPENAI_RESPONSES_CAPABILITY_CATALOG);
|
|
143
|
+
}
|
|
144
|
+
function getAnthropicModelCapability(modelName) {
|
|
145
|
+
return capabilityFromCatalog(modelName, ANTHROPIC_CAPABILITY_CATALOG);
|
|
146
|
+
}
|
|
147
|
+
function getGoogleGenAIModelCapability(modelName) {
|
|
148
|
+
const normalized = normalizeModelName(modelName);
|
|
149
|
+
if (!normalized.startsWith("gemini-")) return UNKNOWN_CAPABILITY;
|
|
150
|
+
if (!hasPrefix(normalized, GEMINI_CATALOGUED_PREFIXES)) return UNKNOWN_CAPABILITY;
|
|
151
|
+
if (normalized.startsWith("gemini-2.5-") || normalized.includes("thinking")) return GEMINI_THINKING_MULTIMODAL_TOOL_CAPABILITY;
|
|
152
|
+
return GEMINI_MULTIMODAL_TOOL_CAPABILITY;
|
|
153
|
+
}
|
|
154
|
+
function usesOpenAIResponsesDeveloperRole(modelName) {
|
|
155
|
+
const normalized = normalizeModelName(modelName);
|
|
156
|
+
if (OPENAI_RESPONSES_DEVELOPER_ROLE_MODELS.has(normalized)) return true;
|
|
157
|
+
for (const cataloguedModel of OPENAI_RESPONSES_DEVELOPER_ROLE_MODELS) if (normalized.startsWith(cataloguedModel + "-")) return true;
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
export { usesOpenAIResponsesDeveloperRole as a, getOpenAIResponsesModelCapability as i, getGoogleGenAIModelCapability as n, getOpenAILegacyModelCapability as r, getAnthropicModelCapability as t };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/providers/chat-completions-stream.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert an OpenAI Chat Completions-style streamed tool-call delta into the
|
|
4
|
+
* normalized kosong stream part protocol.
|
|
5
|
+
*
|
|
6
|
+
* OpenAI-compatible providers may emit argument chunks before the function name
|
|
7
|
+
* for a stream index. Buffer those early argument chunks until the first named
|
|
8
|
+
* header arrives, then emit subsequent chunks as indexed `tool_call_part`s so
|
|
9
|
+
* the shared generate loop can route interleaved parallel calls.
|
|
10
|
+
*/
|
|
11
|
+
function convertChatCompletionStreamToolCall(toolCall, bufferedByIndex) {
|
|
12
|
+
if (toolCall.function === void 0 || toolCall.function === null) return [];
|
|
13
|
+
const streamIndex = toolCall.index;
|
|
14
|
+
const functionName = toolCall.function.name;
|
|
15
|
+
const functionArguments = toolCall.function.arguments;
|
|
16
|
+
const hasConcreteName = typeof functionName === "string" && functionName.length > 0;
|
|
17
|
+
const hasArguments = typeof functionArguments === "string" && functionArguments.length > 0;
|
|
18
|
+
if (streamIndex === void 0) {
|
|
19
|
+
if (hasConcreteName) return [{
|
|
20
|
+
type: "function",
|
|
21
|
+
id: toolCall.id ?? crypto.randomUUID(),
|
|
22
|
+
name: functionName,
|
|
23
|
+
arguments: functionArguments ?? null
|
|
24
|
+
}];
|
|
25
|
+
if (hasArguments) return [{
|
|
26
|
+
type: "tool_call_part",
|
|
27
|
+
argumentsPart: functionArguments
|
|
28
|
+
}];
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const buffered = bufferedByIndex.get(streamIndex) ?? {
|
|
32
|
+
arguments: "",
|
|
33
|
+
emitted: false
|
|
34
|
+
};
|
|
35
|
+
if (toolCall.id !== void 0) buffered.id = toolCall.id;
|
|
36
|
+
if (!buffered.emitted) {
|
|
37
|
+
if (!hasConcreteName) {
|
|
38
|
+
if (hasArguments) buffered.arguments += functionArguments;
|
|
39
|
+
bufferedByIndex.set(streamIndex, buffered);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
buffered.emitted = true;
|
|
43
|
+
const initialArguments = buffered.arguments.length > 0 ? buffered.arguments + (functionArguments ?? "") : functionArguments ?? null;
|
|
44
|
+
buffered.arguments = "";
|
|
45
|
+
bufferedByIndex.set(streamIndex, buffered);
|
|
46
|
+
return [{
|
|
47
|
+
type: "function",
|
|
48
|
+
id: buffered.id ?? toolCall.id ?? crypto.randomUUID(),
|
|
49
|
+
name: functionName,
|
|
50
|
+
arguments: initialArguments,
|
|
51
|
+
_streamIndex: streamIndex
|
|
52
|
+
}];
|
|
53
|
+
}
|
|
54
|
+
if (!hasArguments) return [];
|
|
55
|
+
return [{
|
|
56
|
+
type: "tool_call_part",
|
|
57
|
+
argumentsPart: functionArguments,
|
|
58
|
+
index: streamIndex
|
|
59
|
+
}];
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { convertChatCompletionStreamToolCall as t };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base error for all chat provider errors.
|
|
4
|
+
*/
|
|
5
|
+
declare class ChatProviderError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Network-level connection failure.
|
|
10
|
+
*/
|
|
11
|
+
declare class APIConnectionError extends ChatProviderError {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Request timed out.
|
|
16
|
+
*/
|
|
17
|
+
declare class APITimeoutError extends ChatProviderError {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* HTTP status error from the API.
|
|
22
|
+
*/
|
|
23
|
+
declare class APIStatusError extends ChatProviderError {
|
|
24
|
+
readonly statusCode: number;
|
|
25
|
+
readonly requestId: string | null;
|
|
26
|
+
constructor(statusCode: number, message: string, requestId?: string | null);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* HTTP status error that specifically means the request exceeded the model
|
|
30
|
+
* context window.
|
|
31
|
+
*/
|
|
32
|
+
declare class APIContextOverflowError extends APIStatusError {
|
|
33
|
+
constructor(statusCode: number, message: string, requestId?: string | null);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The API returned an empty response (no content, no tool calls).
|
|
37
|
+
*/
|
|
38
|
+
declare class APIEmptyResponseError extends ChatProviderError {
|
|
39
|
+
constructor(message: string);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { APITimeoutError as a, APIStatusError as i, APIContextOverflowError as n, ChatProviderError as o, APIEmptyResponseError as r, APIConnectionError as t };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base error for all chat provider errors.
|
|
4
|
+
*/
|
|
5
|
+
var ChatProviderError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "ChatProviderError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Network-level connection failure.
|
|
13
|
+
*/
|
|
14
|
+
var APIConnectionError = class extends ChatProviderError {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "APIConnectionError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Request timed out.
|
|
22
|
+
*/
|
|
23
|
+
var APITimeoutError = class extends ChatProviderError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "APITimeoutError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* HTTP status error from the API.
|
|
31
|
+
*/
|
|
32
|
+
var APIStatusError = class extends ChatProviderError {
|
|
33
|
+
statusCode;
|
|
34
|
+
requestId;
|
|
35
|
+
constructor(statusCode, message, requestId) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "APIStatusError";
|
|
38
|
+
this.statusCode = statusCode;
|
|
39
|
+
this.requestId = requestId ?? null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* HTTP status error that specifically means the request exceeded the model
|
|
44
|
+
* context window.
|
|
45
|
+
*/
|
|
46
|
+
var APIContextOverflowError = class extends APIStatusError {
|
|
47
|
+
constructor(statusCode, message, requestId) {
|
|
48
|
+
super(statusCode, message, requestId);
|
|
49
|
+
this.name = "APIContextOverflowError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The API returned an empty response (no content, no tool calls).
|
|
54
|
+
*/
|
|
55
|
+
var APIEmptyResponseError = class extends ChatProviderError {
|
|
56
|
+
constructor(message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "APIEmptyResponseError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const CONTEXT_OVERFLOW_MESSAGE_PATTERNS = [
|
|
62
|
+
/context[ _-]?length/,
|
|
63
|
+
/(?:context[ _-]?window.*exceed|exceed.*context[ _-]?window)/,
|
|
64
|
+
/maximum context/,
|
|
65
|
+
/exceed(?:ed|s|ing)?\s+(?:the\s+)?max(?:imum)?\s+tokens?/,
|
|
66
|
+
/(?:too many tokens.*(?:prompt|input|context)|(?:prompt|input|context).*too many tokens)/,
|
|
67
|
+
/prompt is too long.*maximum/,
|
|
68
|
+
/input token count.*exceeds?.*maximum number of tokens/
|
|
69
|
+
];
|
|
70
|
+
function normalizeAPIStatusError(statusCode, message, requestId) {
|
|
71
|
+
if (isContextOverflowStatusError(statusCode, message)) return new APIContextOverflowError(statusCode, message, requestId);
|
|
72
|
+
return new APIStatusError(statusCode, message, requestId);
|
|
73
|
+
}
|
|
74
|
+
function isContextOverflowStatusError(statusCode, message) {
|
|
75
|
+
if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422) return false;
|
|
76
|
+
const lowerMessage = message.toLowerCase();
|
|
77
|
+
return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { APITimeoutError as a, APIStatusError as i, APIContextOverflowError as n, ChatProviderError as o, APIEmptyResponseError as r, normalizeAPIStatusError as s, APIConnectionError as t };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { S as StreamedMessagePart, a as StreamedMessage, b as Message, c as TokenUsage, i as ProviderRequestAuth, m as ModelCapability, n as FinishReason, o as ThinkingEffort, p as Tool, r as GenerateOptions, t as ChatProvider } from "./provider-DiJKWMsQ.mjs";
|
|
2
|
+
import { o as ChatProviderError } from "./errors-DweKbIOf.mjs";
|
|
3
|
+
import { GoogleGenAI } from "@google/genai";
|
|
4
|
+
|
|
5
|
+
//#region src/providers/google-genai.d.ts
|
|
6
|
+
interface GoogleGenAIOptions {
|
|
7
|
+
apiKey?: string | undefined;
|
|
8
|
+
model: string;
|
|
9
|
+
vertexai?: boolean | undefined;
|
|
10
|
+
project?: string | undefined;
|
|
11
|
+
location?: string | undefined;
|
|
12
|
+
stream?: boolean | undefined;
|
|
13
|
+
clientFactory?: (auth: ProviderRequestAuth) => GoogleGenAI;
|
|
14
|
+
}
|
|
15
|
+
interface GoogleGenAIGenerationKwargs {
|
|
16
|
+
max_output_tokens?: number | undefined;
|
|
17
|
+
temperature?: number | undefined;
|
|
18
|
+
top_k?: number | undefined;
|
|
19
|
+
top_p?: number | undefined;
|
|
20
|
+
thinking_config?: ThinkingConfig | undefined;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
interface ThinkingConfig {
|
|
24
|
+
include_thoughts?: boolean;
|
|
25
|
+
thinking_budget?: number;
|
|
26
|
+
thinking_level?: string;
|
|
27
|
+
}
|
|
28
|
+
interface GoogleContent {
|
|
29
|
+
role: string;
|
|
30
|
+
parts: GooglePart[];
|
|
31
|
+
}
|
|
32
|
+
interface GooglePart {
|
|
33
|
+
text?: string;
|
|
34
|
+
function_call?: {
|
|
35
|
+
name: string;
|
|
36
|
+
args: Record<string, unknown>;
|
|
37
|
+
};
|
|
38
|
+
function_response?: {
|
|
39
|
+
name: string;
|
|
40
|
+
response: Record<string, string>;
|
|
41
|
+
parts: unknown[];
|
|
42
|
+
};
|
|
43
|
+
thought_signature?: string;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
declare function messagesToGoogleGenAIContents(messages: Message[]): GoogleContent[];
|
|
47
|
+
declare class GoogleGenAIStreamedMessage implements StreamedMessage {
|
|
48
|
+
private _id;
|
|
49
|
+
private _usage;
|
|
50
|
+
private _finishReason;
|
|
51
|
+
private _rawFinishReason;
|
|
52
|
+
private readonly _iter;
|
|
53
|
+
constructor(response: AsyncIterable<Record<string, unknown>> | Record<string, unknown>, isStream: boolean, signal?: AbortSignal);
|
|
54
|
+
get id(): string | null;
|
|
55
|
+
get usage(): TokenUsage | null;
|
|
56
|
+
get finishReason(): FinishReason | null;
|
|
57
|
+
get rawFinishReason(): string | null;
|
|
58
|
+
[Symbol.asyncIterator](): AsyncIterator<StreamedMessagePart>;
|
|
59
|
+
private _captureFinishReason;
|
|
60
|
+
/** Yield parts from a single (non-streamed) GenerateContentResponse. */
|
|
61
|
+
private _extractChunkParts;
|
|
62
|
+
/** Extract usage metadata from a response chunk. */
|
|
63
|
+
private _extractUsage;
|
|
64
|
+
/** Extract response ID from a response chunk. */
|
|
65
|
+
private _extractId;
|
|
66
|
+
private _throwIfAborted;
|
|
67
|
+
private _convertNonStreamResponse;
|
|
68
|
+
private _convertStreamResponse;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Convert a Google GenAI SDK error (or raw Error) to a kosong `ChatProviderError`.
|
|
72
|
+
*/
|
|
73
|
+
declare function convertGoogleGenAIError(error: unknown): ChatProviderError;
|
|
74
|
+
declare class GoogleGenAIChatProvider implements ChatProvider {
|
|
75
|
+
readonly name: string;
|
|
76
|
+
private _model;
|
|
77
|
+
private _client;
|
|
78
|
+
private _generationKwargs;
|
|
79
|
+
private _vertexai;
|
|
80
|
+
private _stream;
|
|
81
|
+
private _apiKey;
|
|
82
|
+
private _project;
|
|
83
|
+
private _location;
|
|
84
|
+
private _clientFactory;
|
|
85
|
+
constructor(options: GoogleGenAIOptions);
|
|
86
|
+
private _buildClient;
|
|
87
|
+
get modelName(): string;
|
|
88
|
+
get thinkingEffort(): ThinkingEffort | null;
|
|
89
|
+
get modelParameters(): Record<string, unknown>;
|
|
90
|
+
getCapability(model?: string): ModelCapability;
|
|
91
|
+
generate(systemPrompt: string, tools: Tool[], history: Message[], options?: GenerateOptions): Promise<StreamedMessage>;
|
|
92
|
+
private _createClient;
|
|
93
|
+
withThinking(effort: ThinkingEffort): GoogleGenAIChatProvider;
|
|
94
|
+
withGenerationKwargs(kwargs: GoogleGenAIGenerationKwargs): GoogleGenAIChatProvider;
|
|
95
|
+
private _clone;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { convertGoogleGenAIError as a, GoogleGenAIStreamedMessage as i, GoogleGenAIGenerationKwargs as n, messagesToGoogleGenAIContents as o, GoogleGenAIOptions as r, GoogleGenAIChatProvider as t };
|