@byfriends/kosong 0.2.2 → 0.3.2

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.
@@ -0,0 +1,151 @@
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
+ //#region src/providers/provider-common.ts
81
+ /**
82
+ * Build a finish-reason normalizer from a per-provider raw-string → FinishReason table.
83
+ *
84
+ * Mirrors the shape of the per-adapter `normalizeXxxFinishReason` functions:
85
+ * - `null` / `undefined` raw → `{ finishReason: null, rawFinishReason: null }`
86
+ * - raw present and in the table → mapped FinishReason, raw echoed back
87
+ * - raw present but not in the table → `'other'`, raw echoed back
88
+ *
89
+ * The returned function is stateless and safe to call repeatedly.
90
+ */
91
+ function makeFinishReasonNormalizer(mapping) {
92
+ return (raw) => {
93
+ if (raw === null || raw === void 0) return {
94
+ finishReason: null,
95
+ rawFinishReason: null
96
+ };
97
+ return {
98
+ finishReason: mapping[raw] ?? "other",
99
+ rawFinishReason: raw
100
+ };
101
+ };
102
+ }
103
+ /**
104
+ * Build the four-field `TokenUsage` from already-parsed per-provider numbers,
105
+ * applying the `inputOther = total - cached` formula shared by OpenAI-style and
106
+ * Google providers (which expose only a total prompt count and a cached subset).
107
+ *
108
+ * `inputOther` is clamped to ≥ 0 when `cached` exceeds `total` (defensive — a
109
+ * provider should never report more cached than total, but we never emit a
110
+ * negative usage field). Anthropic is excluded: it reports a real
111
+ * `inputCacheCreation` field that does not fit this formula.
112
+ */
113
+ function extractCacheUsage(total, cached, output) {
114
+ return {
115
+ inputOther: Math.max(0, total - cached),
116
+ output,
117
+ inputCacheRead: cached,
118
+ inputCacheCreation: 0
119
+ };
120
+ }
121
+ const NETWORK_RE = /network|connection|connect|disconnect/i;
122
+ const TIMEOUT_RE = /timed?\s*out|timeout|deadline/i;
123
+ /**
124
+ * Convert a raw thrown value into a kosong `ChatProviderError` using the
125
+ * shared message-based classification ladder:
126
+ *
127
+ * 1. already a `ChatProviderError` → returned as-is (identity)
128
+ * 2. `status` provided → `normalizeAPIStatusError` (status + message + requestId)
129
+ * 3. message matches `TIMEOUT_RE` → `APITimeoutError`
130
+ * 4. message matches `NETWORK_RE` or any `extraNetworkMatchers`, or the value
131
+ * is a `TypeError` matching `extraTypeErrorMatch` → `APIConnectionError`
132
+ * 5. otherwise → `ChatProviderError` wrapping the message
133
+ *
134
+ * Provider adapters that recognize SDK-specific error classes (e.g. OpenAI's
135
+ * `APIConnectionTimeoutError`, Google's `GoogleApiError`) should unwrap them
136
+ * into `(message, status?, requestId?)` before calling this function. The
137
+ * SDK-class detection itself is provider-specific and stays in the adapter.
138
+ */
139
+ function convertProviderError(error, opts = {}) {
140
+ if (error instanceof ChatProviderError) return error;
141
+ const message = error instanceof Error ? error.message : String(error);
142
+ if (typeof opts.status === "number") return normalizeAPIStatusError(opts.status, message, opts.requestId);
143
+ if (TIMEOUT_RE.test(message)) return new APITimeoutError(message);
144
+ if (NETWORK_RE.test(message)) return new APIConnectionError(message);
145
+ if (opts.extraNetworkMatchers?.some((re) => re.test(message))) return new APIConnectionError(message);
146
+ if (opts.extraTypeErrorMatch !== void 0 && error instanceof TypeError && message.includes(opts.extraTypeErrorMatch)) return new APIConnectionError(message);
147
+ if (error instanceof Error) return new ChatProviderError(`Error: ${message}`);
148
+ return new ChatProviderError(`Error: ${String(error)}`);
149
+ }
150
+ //#endregion
151
+ export { APIContextOverflowError as a, APITimeoutError as c, APIConnectionError as i, ChatProviderError as l, extractCacheUsage as n, APIEmptyResponseError as o, makeFinishReasonNormalizer as r, APIStatusError as s, convertProviderError as t, normalizeAPIStatusError as u };
@@ -1,2 +1,2 @@
1
- import { i as resolveDefaultMaxTokens, n as AnthropicOptions, r as convertAnthropicError, t as AnthropicChatProvider } from "../anthropic-0CVG5rBE.mjs";
1
+ import { i as resolveDefaultMaxTokens, n as AnthropicOptions, r as convertAnthropicError, t as AnthropicChatProvider } from "../anthropic-BYlmhwZs.mjs";
2
2
  export { AnthropicChatProvider, AnthropicOptions, convertAnthropicError, resolveDefaultMaxTokens };
@@ -1,5 +1,5 @@
1
- import { a as APITimeoutError, o as ChatProviderError, s as normalizeAPIStatusError, t as APIConnectionError$1 } from "../errors-WFxxzL1B.mjs";
2
- import { i as getAnthropicModelCapability, n as requireProviderApiKey, r as resolveAuthBackedClient, t as mergeRequestHeaders } from "../request-auth-BMXt8jRu.mjs";
1
+ import { c as APITimeoutError, i as APIConnectionError$1, l as ChatProviderError, r as makeFinishReasonNormalizer, u as normalizeAPIStatusError } from "../provider-common-CaxKVTTJ.mjs";
2
+ import { n as BaseChatProvider, o as getAnthropicModelCapability, r as mergeRequestHeaders, t as BaseStreamedMessage } from "../base-streamed-message-Kok6Drxr.mjs";
3
3
  import Anthropic, { APIConnectionError, APIConnectionTimeoutError, APIError, AnthropicError } from "@anthropic-ai/sdk";
4
4
  //#region src/providers/anthropic.ts
5
5
  /**
@@ -9,39 +9,14 @@ import Anthropic, { APIConnectionError, APIConnectionTimeoutError, APIError, Ant
9
9
  * Source: `message.stop_reason` (non-stream) or the last `message_delta`
10
10
  * event's `delta.stop_reason` (stream).
11
11
  */
12
- function normalizeAnthropicStopReason(raw) {
13
- if (raw === null || raw === void 0) return {
14
- finishReason: null,
15
- rawFinishReason: null
16
- };
17
- switch (raw) {
18
- case "end_turn":
19
- case "stop_sequence": return {
20
- finishReason: "completed",
21
- rawFinishReason: raw
22
- };
23
- case "max_tokens": return {
24
- finishReason: "truncated",
25
- rawFinishReason: raw
26
- };
27
- case "tool_use": return {
28
- finishReason: "tool_calls",
29
- rawFinishReason: raw
30
- };
31
- case "pause_turn": return {
32
- finishReason: "paused",
33
- rawFinishReason: raw
34
- };
35
- case "refusal": return {
36
- finishReason: "filtered",
37
- rawFinishReason: raw
38
- };
39
- default: return {
40
- finishReason: "other",
41
- rawFinishReason: raw
42
- };
43
- }
44
- }
12
+ const normalizeAnthropicStopReason = makeFinishReasonNormalizer({
13
+ end_turn: "completed",
14
+ stop_sequence: "completed",
15
+ max_tokens: "truncated",
16
+ tool_use: "tool_calls",
17
+ pause_turn: "paused",
18
+ refusal: "filtered"
19
+ });
45
20
  const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
46
21
  const FAMILY_VERSION_RE = /(?:opus|sonnet|haiku)[.-](\d+)[.-](\d{1,2})(?!\d)/;
47
22
  const OPUS_VERSION_RE = /opus[.-](\d+)[.-](\d{1,2})(?!\d)/;
@@ -355,35 +330,23 @@ function convertAnthropicError(error) {
355
330
  if (error instanceof Error) return new ChatProviderError(`Error: ${error.message}`);
356
331
  return new ChatProviderError(`Error: ${String(error)}`);
357
332
  }
358
- var AnthropicStreamedMessage = class {
359
- _id = null;
360
- _usage = {
361
- inputOther: 0,
362
- output: 0,
363
- inputCacheRead: 0,
364
- inputCacheCreation: 0
365
- };
366
- _finishReason = null;
367
- _rawFinishReason = null;
368
- _iter;
333
+ var AnthropicStreamedMessage = class extends BaseStreamedMessage {
334
+ _response;
335
+ _isStream;
369
336
  constructor(response, isStream) {
370
- if (isStream) this._iter = this._convertStreamResponse(response);
371
- else this._iter = this._convertNonStreamResponse(response);
372
- }
373
- get id() {
374
- return this._id;
375
- }
376
- get usage() {
377
- return this._usage;
378
- }
379
- get finishReason() {
380
- return this._finishReason;
381
- }
382
- get rawFinishReason() {
383
- return this._rawFinishReason;
337
+ super();
338
+ this._response = response;
339
+ this._isStream = isStream;
340
+ this._usage = {
341
+ inputOther: 0,
342
+ output: 0,
343
+ inputCacheRead: 0,
344
+ inputCacheCreation: 0
345
+ };
384
346
  }
385
- async *[Symbol.asyncIterator]() {
386
- yield* this._iter;
347
+ _buildIter() {
348
+ if (this._isStream) return this._convertStreamResponse(this._response);
349
+ return this._convertNonStreamResponse(this._response);
387
350
  }
388
351
  _captureStopReason(raw) {
389
352
  const normalized = normalizeAnthropicStopReason(raw);
@@ -391,11 +354,14 @@ var AnthropicStreamedMessage = class {
391
354
  this._rawFinishReason = normalized.rawFinishReason;
392
355
  }
393
356
  _extractUsage(usage) {
357
+ const inputTokens = usage.input_tokens ?? 0;
358
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
359
+ const cacheCreation = usage.cache_creation_input_tokens ?? 0;
394
360
  this._usage = {
395
- inputOther: usage.input_tokens ?? 0,
361
+ inputOther: Math.max(0, inputTokens - cacheRead - cacheCreation),
396
362
  output: usage.output_tokens ?? 0,
397
- inputCacheRead: usage.cache_read_input_tokens ?? 0,
398
- inputCacheCreation: usage.cache_creation_input_tokens ?? 0
363
+ inputCacheRead: cacheRead,
364
+ inputCacheCreation: cacheCreation
399
365
  };
400
366
  }
401
367
  async *_convertNonStreamResponse(response) {
@@ -520,9 +486,16 @@ var AnthropicStreamedMessage = class {
520
486
  const deltaUsage = evt.usage;
521
487
  if (deltaUsage !== void 0) {
522
488
  if (typeof deltaUsage["output_tokens"] === "number") this._usage.output = deltaUsage["output_tokens"];
489
+ const prevInputOther = this._usage.inputOther;
490
+ const prevCacheRead = this._usage.inputCacheRead;
491
+ const prevCacheCreation = this._usage.inputCacheCreation;
523
492
  if (typeof deltaUsage["cache_read_input_tokens"] === "number") this._usage.inputCacheRead = deltaUsage["cache_read_input_tokens"];
524
493
  if (typeof deltaUsage["cache_creation_input_tokens"] === "number") this._usage.inputCacheCreation = deltaUsage["cache_creation_input_tokens"];
525
- if (typeof deltaUsage["input_tokens"] === "number") this._usage.inputOther = deltaUsage["input_tokens"];
494
+ if (typeof deltaUsage["input_tokens"] === "number") this._usage.inputOther = Math.max(0, deltaUsage["input_tokens"] - this._usage.inputCacheRead - this._usage.inputCacheCreation);
495
+ else {
496
+ const totalInput = prevInputOther + prevCacheRead + prevCacheCreation;
497
+ this._usage.inputOther = Math.max(0, totalInput - this._usage.inputCacheRead - this._usage.inputCacheCreation);
498
+ }
526
499
  }
527
500
  const messageDeltaPayload = evt.delta;
528
501
  if (messageDeltaPayload !== void 0 && "stop_reason" in messageDeltaPayload) this._captureStopReason(messageDeltaPayload["stop_reason"]);
@@ -533,31 +506,22 @@ var AnthropicStreamedMessage = class {
533
506
  }
534
507
  }
535
508
  };
536
- var AnthropicChatProvider = class {
509
+ var AnthropicChatProvider = class AnthropicChatProvider extends BaseChatProvider {
537
510
  name = "anthropic";
538
- _model;
539
511
  _stream;
540
- _client;
541
- _generationKwargs;
542
512
  _metadata;
543
- _apiKey;
544
- _baseUrl;
545
- _defaultHeaders;
546
- _clientFactory;
547
513
  constructor(options) {
548
- this._model = options.model;
549
- this._stream = options.stream ?? true;
550
- this._metadata = options.metadata;
551
- const apiKey = options.apiKey ?? process.env["ANTHROPIC_API_KEY"];
552
- this._apiKey = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
553
- this._baseUrl = options.baseUrl;
554
- this._defaultHeaders = options.defaultHeaders;
555
- this._clientFactory = options.clientFactory;
556
- this._client = this._apiKey === void 0 ? void 0 : this._buildClient(this._apiKey);
557
- this._generationKwargs = {
514
+ const apiKey = options.apiKey === void 0 || options.apiKey.length === 0 ? process.env["ANTHROPIC_API_KEY"] ?? void 0 : options.apiKey;
515
+ const apiKeyResolved = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
516
+ const baseUrl = options.baseUrl;
517
+ const client = apiKeyResolved === void 0 ? void 0 : AnthropicChatProvider.buildClient(apiKeyResolved, baseUrl, options.defaultHeaders);
518
+ const generationKwargs = {
558
519
  max_tokens: resolveDefaultMaxTokens(options.model, options.defaultMaxTokens),
559
520
  betaFeatures: options.betaFeatures ?? [INTERLEAVED_THINKING_BETA]
560
521
  };
522
+ super(options.model, generationKwargs, apiKeyResolved, baseUrl ?? "", options.defaultHeaders, client, options.clientFactory);
523
+ this._stream = options.stream ?? true;
524
+ this._metadata = options.metadata;
561
525
  }
562
526
  get modelName() {
563
527
  return this._model;
@@ -650,24 +614,25 @@ var AnthropicChatProvider = class {
650
614
  throw convertAnthropicError(error);
651
615
  }
652
616
  }
653
- _createClient(auth) {
654
- return resolveAuthBackedClient({
655
- cachedClient: this._client,
656
- clientFactory: this._clientFactory
657
- }, auth, (a) => this._buildClient(requireProviderApiKey("AnthropicChatProvider", a, this._apiKey)));
658
- }
659
- _buildClient(apiKey) {
617
+ static buildClient(apiKey, baseUrl, defaultHeaders) {
660
618
  return new Anthropic({
661
619
  apiKey,
620
+ baseURL: baseUrl,
621
+ defaultHeaders
622
+ });
623
+ }
624
+ createRawClient(auth, defaultHeaders) {
625
+ return new Anthropic({
626
+ apiKey: auth.apiKey,
662
627
  baseURL: this._baseUrl,
663
- defaultHeaders: this._defaultHeaders
628
+ defaultHeaders
664
629
  });
665
630
  }
666
631
  withThinking(effort) {
667
632
  if (effort === "off") {
668
633
  let newBetas = [...this._generationKwargs.betaFeatures ?? []];
669
634
  newBetas = newBetas.filter((b) => b !== INTERLEAVED_THINKING_BETA);
670
- const clone = this._withGenerationKwargs({
635
+ const clone = this.withGenerationKwargs({
671
636
  thinking: { type: "disabled" },
672
637
  betaFeatures: newBetas
673
638
  });
@@ -678,7 +643,7 @@ var AnthropicChatProvider = class {
678
643
  if (effectiveEffort === "off") throw new Error("Non-off thinking effort unexpectedly clamped to off.");
679
644
  let newBetas = [...this._generationKwargs.betaFeatures ?? []];
680
645
  newBetas = newBetas.filter((b) => b !== INTERLEAVED_THINKING_BETA);
681
- return this._withGenerationKwargs({
646
+ return this.withGenerationKwargs({
682
647
  thinking: {
683
648
  type: "adaptive",
684
649
  display: "summarized"
@@ -687,22 +652,6 @@ var AnthropicChatProvider = class {
687
652
  betaFeatures: newBetas
688
653
  });
689
654
  }
690
- withGenerationKwargs(kwargs) {
691
- return this._withGenerationKwargs(kwargs);
692
- }
693
- _withGenerationKwargs(kwargs) {
694
- const clone = this._clone();
695
- clone._generationKwargs = {
696
- ...clone._generationKwargs,
697
- ...kwargs
698
- };
699
- return clone;
700
- }
701
- _clone() {
702
- const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
703
- clone._generationKwargs = { ...this._generationKwargs };
704
- return clone;
705
- }
706
655
  };
707
656
  //#endregion
708
657
  export { AnthropicChatProvider, convertAnthropicError, resolveDefaultMaxTokens };
@@ -1,2 +1,2 @@
1
- import { a as convertGoogleGenAIError, i as GoogleGenAIStreamedMessage, n as GoogleGenAIGenerationKwargs, o as messagesToGoogleGenAIContents, r as GoogleGenAIOptions, t as GoogleGenAIChatProvider } from "../google-genai-xKK8lI_R.mjs";
1
+ import { a as convertGoogleGenAIError, i as GoogleGenAIStreamedMessage, n as GoogleGenAIGenerationKwargs, o as messagesToGoogleGenAIContents, r as GoogleGenAIOptions, t as GoogleGenAIChatProvider } from "../google-genai-CSE4ZD3A.mjs";
2
2
  export { GoogleGenAIChatProvider, GoogleGenAIGenerationKwargs, GoogleGenAIOptions, GoogleGenAIStreamedMessage, convertGoogleGenAIError, messagesToGoogleGenAIContents };
@@ -1,5 +1,5 @@
1
- import { a as APITimeoutError, o as ChatProviderError, s as normalizeAPIStatusError, t as APIConnectionError } from "../errors-WFxxzL1B.mjs";
2
- import { a as getGoogleGenAIModelCapability, n as requireProviderApiKey, r as resolveAuthBackedClient } from "../request-auth-BMXt8jRu.mjs";
1
+ import { l as ChatProviderError, n as extractCacheUsage, t as convertProviderError, u as normalizeAPIStatusError } from "../provider-common-CaxKVTTJ.mjs";
2
+ import { a as resolveAuthBackedClient, i as requireProviderApiKey, n as BaseChatProvider, s as getGoogleGenAIModelCapability, t as BaseStreamedMessage } from "../base-streamed-message-Kok6Drxr.mjs";
3
3
  import { ApiError, GoogleGenAI } from "@google/genai";
4
4
  //#region src/providers/google-genai.ts
5
5
  /**
@@ -269,30 +269,19 @@ function messagesToGoogleGenAIContents(messages) {
269
269
  }
270
270
  return contents;
271
271
  }
272
- var GoogleGenAIStreamedMessage = class {
273
- _id = null;
274
- _usage = null;
275
- _finishReason = null;
276
- _rawFinishReason = null;
277
- _iter;
272
+ var GoogleGenAIStreamedMessage = class extends BaseStreamedMessage {
273
+ _response;
274
+ _isStream;
275
+ _signal;
278
276
  constructor(response, isStream, signal) {
279
- if (isStream) this._iter = this._convertStreamResponse(response, signal);
280
- else this._iter = this._convertNonStreamResponse(response, signal);
277
+ super();
278
+ this._response = response;
279
+ this._isStream = isStream;
280
+ this._signal = signal;
281
281
  }
282
- get id() {
283
- return this._id;
284
- }
285
- get usage() {
286
- return this._usage;
287
- }
288
- get finishReason() {
289
- return this._finishReason;
290
- }
291
- get rawFinishReason() {
292
- return this._rawFinishReason;
293
- }
294
- async *[Symbol.asyncIterator]() {
295
- yield* this._iter;
282
+ _buildIter() {
283
+ if (this._isStream) return this._convertStreamResponse(this._response, this._signal);
284
+ return this._convertNonStreamResponse(this._response, this._signal);
296
285
  }
297
286
  _captureFinishReason(response) {
298
287
  const candidates = response["candidates"];
@@ -348,12 +337,8 @@ var GoogleGenAIStreamedMessage = class {
348
337
  if (usageMetadata) {
349
338
  const promptTokenCount = typeof usageMetadata["promptTokenCount"] === "number" ? usageMetadata["promptTokenCount"] : 0;
350
339
  const cachedContentTokenCount = typeof usageMetadata["cachedContentTokenCount"] === "number" ? usageMetadata["cachedContentTokenCount"] : 0;
351
- this._usage = {
352
- inputOther: Math.max(promptTokenCount - cachedContentTokenCount, 0),
353
- output: usageMetadata["candidatesTokenCount"] ?? 0,
354
- inputCacheRead: cachedContentTokenCount,
355
- inputCacheCreation: 0
356
- };
340
+ const output = usageMetadata["candidatesTokenCount"] ?? 0;
341
+ this._usage = extractCacheUsage(promptTokenCount, cachedContentTokenCount, output);
357
342
  }
358
343
  }
359
344
  /** Extract response ID from a response chunk. */
@@ -391,59 +376,44 @@ var GoogleGenAIStreamedMessage = class {
391
376
  }
392
377
  }
393
378
  };
394
- const NETWORK_RE = /network|connection|connect|disconnect|fetch failed/i;
395
- const TIMEOUT_RE = /timed?\s*out|timeout|deadline/i;
396
379
  /**
397
380
  * Convert a Google GenAI SDK error (or raw Error) to a kosong `ChatProviderError`.
398
381
  */
399
382
  function convertGoogleGenAIError(error) {
400
383
  if (error instanceof ApiError) return normalizeAPIStatusError(error.status, error.message);
401
- if (error instanceof Error) {
402
- const msg = error.message;
403
- if (TIMEOUT_RE.test(msg)) return new APITimeoutError(msg);
404
- if (NETWORK_RE.test(msg) || error instanceof TypeError && msg.includes("fetch")) return new APIConnectionError(msg);
405
- const statusCode = error.code;
406
- if (typeof statusCode === "number") return normalizeAPIStatusError(statusCode, msg);
407
- return new ChatProviderError(`GoogleGenAI error: ${msg}`);
408
- }
409
- return new ChatProviderError(`GoogleGenAI error: ${String(error)}`);
384
+ const statusCode = error.code;
385
+ if (error instanceof Error && typeof statusCode === "number") return normalizeAPIStatusError(statusCode, error.message);
386
+ return convertProviderError(error, {
387
+ extraNetworkMatchers: [/^fetch failed$/i],
388
+ extraTypeErrorMatch: "fetch"
389
+ });
410
390
  }
411
- var GoogleGenAIChatProvider = class {
391
+ var GoogleGenAIChatProvider = class GoogleGenAIChatProvider extends BaseChatProvider {
412
392
  name = "google_genai";
413
- _model;
414
- _client;
415
- _generationKwargs;
416
- _vertexai;
417
393
  _stream;
418
- _apiKey;
394
+ _vertexai;
419
395
  _project;
420
396
  _location;
421
- _clientFactory;
422
397
  constructor(options) {
423
- this._model = options.model;
424
- this._vertexai = options.vertexai ?? false;
425
- this._stream = options.stream ?? true;
426
- this._generationKwargs = {};
427
398
  const apiKey = options.apiKey ?? process.env["GOOGLE_API_KEY"];
428
- this._apiKey = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
399
+ const apiKeyResolved = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
400
+ const client = (options.vertexai ?? false) || apiKeyResolved !== void 0 ? GoogleGenAIChatProvider.buildClient(apiKeyResolved, options.vertexai ?? false, options.project, options.location) : void 0;
401
+ super(options.model, {}, apiKeyResolved, "", void 0, client, options.clientFactory);
402
+ this._stream = options.stream ?? true;
403
+ this._vertexai = options.vertexai ?? false;
429
404
  this._project = options.project;
430
405
  this._location = options.location;
431
- this._clientFactory = options.clientFactory;
432
- this._client = this._vertexai || this._apiKey !== void 0 ? this._buildClient(this._apiKey) : void 0;
433
406
  }
434
- _buildClient(apiKey) {
407
+ static buildClient(apiKey, vertexai, project, location) {
435
408
  return new GoogleGenAI({
436
409
  apiKey,
437
- ...this._vertexai ? {
410
+ ...vertexai ? {
438
411
  vertexai: true,
439
- project: this._project,
440
- location: this._location
412
+ project,
413
+ location
441
414
  } : {}
442
415
  });
443
416
  }
444
- get modelName() {
445
- return this._model;
446
- }
447
417
  get thinkingEffort() {
448
418
  const thinkingConfig = this._generationKwargs.thinking_config;
449
419
  if (thinkingConfig === void 0) return null;
@@ -493,15 +463,26 @@ var GoogleGenAIChatProvider = class {
493
463
  throw convertGoogleGenAIError(error);
494
464
  }
495
465
  }
466
+ /**
467
+ * Override the base auth-resolution path to preserve the Vertex AI
468
+ * short-circuit: Vertex uses service credentials, not request-scoped keys
469
+ * or headers, so `requireProviderApiKey` must not be enforced there.
470
+ */
496
471
  _createClient(auth) {
497
472
  return resolveAuthBackedClient({
498
473
  cachedClient: this._client,
499
474
  clientFactory: this._clientFactory
500
475
  }, auth, (a) => {
501
- if (this._vertexai) return this._buildClient(this._apiKey);
502
- return this._buildClient(requireProviderApiKey("GoogleGenAIChatProvider", a, this._apiKey));
476
+ if (this._vertexai) return GoogleGenAIChatProvider.buildClient(this._apiKey, this._vertexai, this._project, this._location);
477
+ return this.createRawClient({
478
+ apiKey: requireProviderApiKey("GoogleGenAIChatProvider", a, this._apiKey),
479
+ headers: void 0
480
+ }, void 0);
503
481
  });
504
482
  }
483
+ createRawClient(auth, _defaultHeaders) {
484
+ return GoogleGenAIChatProvider.buildClient(auth.apiKey, this._vertexai, this._project, this._location);
485
+ }
505
486
  withThinking(effort) {
506
487
  const thinkingConfig = { include_thoughts: true };
507
488
  if (this._model.includes("gemini-3")) switch (effort) {
@@ -543,19 +524,6 @@ var GoogleGenAIChatProvider = class {
543
524
  }
544
525
  return this.withGenerationKwargs({ thinking_config: thinkingConfig });
545
526
  }
546
- withGenerationKwargs(kwargs) {
547
- const clone = this._clone();
548
- clone._generationKwargs = {
549
- ...clone._generationKwargs,
550
- ...kwargs
551
- };
552
- return clone;
553
- }
554
- _clone() {
555
- const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
556
- clone._generationKwargs = { ...this._generationKwargs };
557
- return clone;
558
- }
559
527
  };
560
528
  //#endregion
561
529
  export { GoogleGenAIChatProvider, GoogleGenAIStreamedMessage, convertGoogleGenAIError, messagesToGoogleGenAIContents };
@@ -1,2 +1,2 @@
1
- import { a as convertContentPart, c as extractUsage, d as reasoningEffortToThinkingEffort, f as thinkingEffortToReasoningEffort, i as ToolMessageConversion, l as isFunctionToolCall, n as OpenAIContentPart, o as convertOpenAIError, p as toolToOpenAI, r as OpenAIToolParam, s as convertToolMessageContent, t as FunctionToolCallShape, u as normalizeOpenAIFinishReason } from "../openai-common-DwkxUSyI.mjs";
1
+ import { a as convertContentPart, c as extractUsage, d as reasoningEffortToThinkingEffort, f as thinkingEffortToReasoningEffort, i as ToolMessageConversion, l as isFunctionToolCall, n as OpenAIContentPart, o as convertOpenAIError, p as toolToOpenAI, r as OpenAIToolParam, s as convertToolMessageContent, t as FunctionToolCallShape, u as normalizeOpenAIFinishReason } from "../openai-common-7D2LXUK2.mjs";
2
2
  export { FunctionToolCallShape, OpenAIContentPart, OpenAIToolParam, ToolMessageConversion, convertContentPart, convertOpenAIError, convertToolMessageContent, extractUsage, isFunctionToolCall, normalizeOpenAIFinishReason, reasoningEffortToThinkingEffort, thinkingEffortToReasoningEffort, toolToOpenAI };
@@ -1,2 +1,2 @@
1
- import { a as isFunctionToolCall, c as thinkingEffortToReasoningEffort, i as extractUsage, l as toolToOpenAI, n as convertOpenAIError, o as normalizeOpenAIFinishReason, r as convertToolMessageContent, s as reasoningEffortToThinkingEffort, t as convertContentPart } from "../openai-common-Dl42y_vn.mjs";
1
+ import { a as isFunctionToolCall, c as thinkingEffortToReasoningEffort, i as extractUsage, l as toolToOpenAI, n as convertOpenAIError, o as normalizeOpenAIFinishReason, r as convertToolMessageContent, s as reasoningEffortToThinkingEffort, t as convertContentPart } from "../openai-common-B7Ex3mNh.mjs";
2
2
  export { convertContentPart, convertOpenAIError, convertToolMessageContent, extractUsage, isFunctionToolCall, normalizeOpenAIFinishReason, reasoningEffortToThinkingEffort, thinkingEffortToReasoningEffort, toolToOpenAI };
@@ -1,2 +1,2 @@
1
- import { i as OpenAIResponsesStreamedMessage, n as OpenAIResponsesGenerationKwargs, r as OpenAIResponsesOptions, t as OpenAIResponsesChatProvider } from "../openai-responses-DZ9mQ5RA.mjs";
1
+ import { i as OpenAIResponsesStreamedMessage, n as OpenAIResponsesGenerationKwargs, r as OpenAIResponsesOptions, t as OpenAIResponsesChatProvider } from "../openai-responses-Bj6eg-S0.mjs";
2
2
  export { OpenAIResponsesChatProvider, OpenAIResponsesGenerationKwargs, OpenAIResponsesOptions, OpenAIResponsesStreamedMessage };