@providerprotocol/ai 0.0.28 → 0.0.31

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.
Files changed (35) hide show
  1. package/dist/anthropic/index.d.ts +11 -0
  2. package/dist/anthropic/index.js +7 -7
  3. package/dist/anthropic/index.js.map +1 -1
  4. package/dist/{chunk-Z6DKC37J.js → chunk-3C7O2RNO.js} +2 -2
  5. package/dist/{chunk-55X3W2MN.js → chunk-3D6XGGVG.js} +9 -2
  6. package/dist/{chunk-55X3W2MN.js.map → chunk-3D6XGGVG.js.map} +1 -1
  7. package/dist/{chunk-QNJO7DSD.js → chunk-4J6OFUKX.js} +10 -1
  8. package/dist/chunk-4J6OFUKX.js.map +1 -0
  9. package/dist/{chunk-SBCATNHA.js → chunk-KUPF5KHT.js} +2 -2
  10. package/dist/{embedding-DtyOFIsS.d.ts → embedding-k7g-BcSY.d.ts} +14 -1
  11. package/dist/google/index.d.ts +4 -0
  12. package/dist/google/index.js +14 -10
  13. package/dist/google/index.js.map +1 -1
  14. package/dist/http/index.d.ts +7 -2
  15. package/dist/http/index.js +9 -5
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.js +12 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/ollama/index.js +24 -18
  20. package/dist/ollama/index.js.map +1 -1
  21. package/dist/openai/index.js +8 -11
  22. package/dist/openai/index.js.map +1 -1
  23. package/dist/openrouter/index.d.ts +1 -1
  24. package/dist/openrouter/index.js +6 -4
  25. package/dist/openrouter/index.js.map +1 -1
  26. package/dist/proxy/index.d.ts +1 -1
  27. package/dist/proxy/index.js +2 -2
  28. package/dist/{retry-DXLQnTuU.d.ts → retry-CyAwm_KZ.d.ts} +15 -1
  29. package/dist/xai/index.d.ts +15 -35
  30. package/dist/xai/index.js +11 -14
  31. package/dist/xai/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/dist/chunk-QNJO7DSD.js.map +0 -1
  34. /package/dist/{chunk-Z6DKC37J.js.map → chunk-3C7O2RNO.js.map} +0 -0
  35. /package/dist/{chunk-SBCATNHA.js.map → chunk-KUPF5KHT.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ErrorCode,
3
3
  UPPError
4
- } from "./chunk-QNJO7DSD.js";
4
+ } from "./chunk-4J6OFUKX.js";
5
5
 
6
6
  // src/http/keys.ts
7
7
  var RoundRobinKeys = class {
@@ -82,6 +82,12 @@ var DynamicKey = class {
82
82
  return this.selector();
83
83
  }
84
84
  };
85
+ function maskApiKey(key) {
86
+ if (key.length <= 8) {
87
+ return "***";
88
+ }
89
+ return `${key.slice(0, 4)}...${key.slice(-4)}`;
90
+ }
85
91
  function isKeyStrategy(value) {
86
92
  return typeof value === "object" && value !== null && "getKey" in value && typeof value.getKey === "function";
87
93
  }
@@ -116,6 +122,7 @@ export {
116
122
  RoundRobinKeys,
117
123
  WeightedKeys,
118
124
  DynamicKey,
125
+ maskApiKey,
119
126
  resolveApiKey
120
127
  };
121
- //# sourceMappingURL=chunk-55X3W2MN.js.map
128
+ //# sourceMappingURL=chunk-3D6XGGVG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/http/keys.ts"],"sourcesContent":["/**\n * API key management strategies for load balancing and dynamic key selection.\n * @module http/keys\n */\n\nimport type { ProviderConfig, KeyStrategy } from '../types/provider.ts';\nimport { ErrorCode, UPPError, type Modality } from '../types/errors.ts';\n\n/**\n * Distributes API requests across multiple keys using round-robin selection.\n *\n * Each call to {@link getKey} returns the next key in sequence, cycling back to\n * the first key after reaching the end. This provides even distribution of requests\n * across all available keys, which is useful for:\n * - Spreading rate limits across multiple API keys\n * - Load balancing between different accounts\n * - Maximizing throughput when multiple keys are available\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new RoundRobinKeys([\n * 'sk-key-1',\n * 'sk-key-2',\n * 'sk-key-3'\n * ]);\n *\n * keys.getKey(); // Returns 'sk-key-1'\n * keys.getKey(); // Returns 'sk-key-2'\n * keys.getKey(); // Returns 'sk-key-3'\n * keys.getKey(); // Returns 'sk-key-1' (cycles back)\n * ```\n */\nexport class RoundRobinKeys implements KeyStrategy {\n private keys: string[];\n private index = 0;\n\n /**\n * Creates a new RoundRobinKeys instance.\n *\n * @param keys - Array of API keys to rotate through\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: string[]) {\n if (keys.length === 0) {\n throw new Error('RoundRobinKeys requires at least one key');\n }\n this.keys = keys;\n }\n\n /**\n * Returns the next key in the rotation sequence.\n *\n * @returns The next API key in round-robin order\n */\n getKey(): string {\n const key = this.keys[this.index]!;\n this.index = (this.index + 1) % this.keys.length;\n return key;\n }\n}\n\n/**\n * Selects API keys using weighted random probability.\n *\n * Each key is assigned a weight that determines its probability of being selected.\n * Higher weights mean higher selection probability. This is useful for:\n * - Preferring higher-tier API keys with better rate limits\n * - Gradually migrating traffic between old and new keys\n * - A/B testing different API accounts\n * - Directing more traffic to keys with higher quotas\n *\n * The selection probability for each key is: weight / totalWeight\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new WeightedKeys([\n * { key: 'sk-premium', weight: 70 }, // 70% of requests\n * { key: 'sk-standard', weight: 20 }, // 20% of requests\n * { key: 'sk-backup', weight: 10 } // 10% of requests\n * ]);\n *\n * // Configure provider with weighted key selection\n * const provider = createOpenAI({\n * apiKey: keys\n * });\n * ```\n */\nexport class WeightedKeys implements KeyStrategy {\n private entries: Array<{ key: string; weight: number }>;\n private totalWeight: number;\n\n /**\n * Creates a new WeightedKeys instance.\n *\n * @param keys - Array of key-weight pairs defining selection probabilities\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: Array<{ key: string; weight: number }>) {\n if (keys.length === 0) {\n throw new Error('WeightedKeys requires at least one key');\n }\n this.entries = keys;\n this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);\n }\n\n /**\n * Returns a randomly selected key based on configured weights.\n *\n * @returns An API key selected with probability proportional to its weight\n */\n getKey(): string {\n const random = Math.random() * this.totalWeight;\n let cumulative = 0;\n\n for (const entry of this.entries) {\n cumulative += entry.weight;\n if (random <= cumulative) {\n return entry.key;\n }\n }\n\n return this.entries[this.entries.length - 1]!.key;\n }\n}\n\n/**\n * Provides dynamic key selection using custom logic.\n *\n * This strategy delegates key selection to a user-provided function, enabling\n * advanced scenarios such as:\n * - Fetching keys from a secrets manager (AWS Secrets Manager, HashiCorp Vault)\n * - Rotating keys based on external state or configuration\n * - Selecting keys based on request context or time of day\n * - Implementing custom load balancing algorithms\n *\n * The selector function can be synchronous or asynchronous.\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * // Fetch key from environment based on current mode\n * const dynamicKey = new DynamicKey(() => {\n * return process.env.NODE_ENV === 'production'\n * ? process.env.PROD_API_KEY!\n * : process.env.DEV_API_KEY!;\n * });\n *\n * // Async key fetching from a secrets manager\n * const vaultKey = new DynamicKey(async () => {\n * const secret = await vault.read('secret/openai');\n * return secret.data.apiKey;\n * });\n *\n * // Time-based key rotation\n * const timedKey = new DynamicKey(() => {\n * const hour = new Date().getHours();\n * return hour < 12 ? morningKey : afternoonKey;\n * });\n * ```\n */\nexport class DynamicKey implements KeyStrategy {\n private selector: () => string | Promise<string>;\n\n /**\n * Creates a new DynamicKey instance.\n *\n * @param selector - Function that returns an API key (sync or async)\n */\n constructor(selector: () => string | Promise<string>) {\n this.selector = selector;\n }\n\n /**\n * Invokes the selector function to retrieve the current key.\n *\n * @returns Promise resolving to the selected API key\n */\n async getKey(): Promise<string> {\n return this.selector();\n }\n}\n\n/**\n * Type guard to check if a value implements the KeyStrategy interface.\n *\n * @param value - The value to check\n * @returns True if the value has a getKey method\n */\nfunction isKeyStrategy(value: unknown): value is KeyStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'getKey' in value &&\n typeof (value as KeyStrategy).getKey === 'function'\n );\n}\n\n/**\n * Resolves an API key from provider configuration with multiple fallback options.\n *\n * This function handles various key specification methods in priority order:\n * 1. Direct string key in config.apiKey\n * 2. Function returning a key (sync or async) in config.apiKey\n * 3. KeyStrategy instance in config.apiKey (RoundRobinKeys, WeightedKeys, DynamicKey)\n * 4. Environment variable fallback (if envVar parameter is provided)\n *\n * @param config - Provider configuration containing the apiKey option\n * @param envVar - Optional environment variable name to check as fallback\n * @param provider - Provider identifier for error context (default: 'unknown')\n * @param modality - Request modality for error context (default: 'llm')\n * @returns The resolved API key string\n *\n * @throws {UPPError} AUTHENTICATION_FAILED - When no valid key is found\n *\n * @example\n * ```typescript\n * // Direct key in config\n * const key1 = await resolveApiKey({ apiKey: 'sk-...' }, 'OPENAI_API_KEY', 'openai');\n *\n * // Function-based key\n * const key2 = await resolveApiKey({ apiKey: () => getKeyFromVault() }, undefined, 'anthropic');\n *\n * // KeyStrategy instance\n * const key3 = await resolveApiKey({\n * apiKey: new RoundRobinKeys(['sk-1', 'sk-2', 'sk-3'])\n * }, 'OPENAI_API_KEY', 'openai');\n *\n * // Environment variable fallback\n * const key4 = await resolveApiKey({}, 'ANTHROPIC_API_KEY', 'anthropic');\n * ```\n */\nexport async function resolveApiKey(\n config: ProviderConfig,\n envVar?: string,\n provider = 'unknown',\n modality: Modality = 'llm'\n): Promise<string> {\n const { apiKey } = config;\n\n if (apiKey !== undefined) {\n if (typeof apiKey === 'string') {\n return apiKey;\n }\n\n if (typeof apiKey === 'function') {\n return apiKey();\n }\n\n if (isKeyStrategy(apiKey)) {\n return apiKey.getKey();\n }\n }\n\n if (envVar) {\n const envValue = process.env[envVar];\n if (envValue) {\n return envValue;\n }\n }\n\n throw new UPPError(\n envVar\n ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.`\n : 'API key not found. Provide apiKey in config.',\n ErrorCode.AuthenticationFailed,\n provider,\n modality\n );\n}\n"],"mappings":";;;;;;AAkCO,IAAM,iBAAN,MAA4C;AAAA,EACzC;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB,YAAY,MAAgB;AAC1B,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,SAAK,SAAS,KAAK,QAAQ,KAAK,KAAK,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AA8BO,IAAM,eAAN,MAA0C;AAAA,EACvC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQR,YAAY,MAA8C;AACxD,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,SAAK,UAAU;AACf,SAAK,cAAc,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,SAAS,KAAK,OAAO,IAAI,KAAK;AACpC,QAAI,aAAa;AAEjB,eAAW,SAAS,KAAK,SAAS;AAChC,oBAAc,MAAM;AACpB,UAAI,UAAU,YAAY;AACxB,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC,EAAG;AAAA,EAChD;AACF;AAsCO,IAAM,aAAN,MAAwC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,UAA0C;AACpD,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAA0B;AAC9B,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAQA,SAAS,cAAc,OAAsC;AAC3D,SACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,OAAQ,MAAsB,WAAW;AAE7C;AAoCA,eAAsB,cACpB,QACA,QACA,WAAW,WACX,WAAqB,OACJ;AACjB,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,WAAW,QAAW;AACxB,QAAI,OAAO,WAAW,UAAU;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,cAAc,MAAM,GAAG;AACzB,aAAO,OAAO,OAAO;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,QAAQ;AACV,UAAM,WAAW,QAAQ,IAAI,MAAM;AACnC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,SACI,0BAA0B,MAAM,uDAChC;AAAA,IACJ,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/http/keys.ts"],"sourcesContent":["/**\n * API key management strategies for load balancing and dynamic key selection.\n * @module http/keys\n */\n\nimport type { ProviderConfig, KeyStrategy } from '../types/provider.ts';\nimport { ErrorCode, UPPError, type Modality } from '../types/errors.ts';\n\n/**\n * Distributes API requests across multiple keys using round-robin selection.\n *\n * Each call to {@link getKey} returns the next key in sequence, cycling back to\n * the first key after reaching the end. This provides even distribution of requests\n * across all available keys, which is useful for:\n * - Spreading rate limits across multiple API keys\n * - Load balancing between different accounts\n * - Maximizing throughput when multiple keys are available\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new RoundRobinKeys([\n * 'sk-key-1',\n * 'sk-key-2',\n * 'sk-key-3'\n * ]);\n *\n * keys.getKey(); // Returns 'sk-key-1'\n * keys.getKey(); // Returns 'sk-key-2'\n * keys.getKey(); // Returns 'sk-key-3'\n * keys.getKey(); // Returns 'sk-key-1' (cycles back)\n * ```\n */\nexport class RoundRobinKeys implements KeyStrategy {\n private keys: string[];\n private index = 0;\n\n /**\n * Creates a new RoundRobinKeys instance.\n *\n * @param keys - Array of API keys to rotate through\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: string[]) {\n if (keys.length === 0) {\n throw new Error('RoundRobinKeys requires at least one key');\n }\n this.keys = keys;\n }\n\n /**\n * Returns the next key in the rotation sequence.\n *\n * @returns The next API key in round-robin order\n */\n getKey(): string {\n const key = this.keys[this.index]!;\n this.index = (this.index + 1) % this.keys.length;\n return key;\n }\n}\n\n/**\n * Selects API keys using weighted random probability.\n *\n * Each key is assigned a weight that determines its probability of being selected.\n * Higher weights mean higher selection probability. This is useful for:\n * - Preferring higher-tier API keys with better rate limits\n * - Gradually migrating traffic between old and new keys\n * - A/B testing different API accounts\n * - Directing more traffic to keys with higher quotas\n *\n * The selection probability for each key is: weight / totalWeight\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new WeightedKeys([\n * { key: 'sk-premium', weight: 70 }, // 70% of requests\n * { key: 'sk-standard', weight: 20 }, // 20% of requests\n * { key: 'sk-backup', weight: 10 } // 10% of requests\n * ]);\n *\n * // Configure provider with weighted key selection\n * const provider = createOpenAI({\n * apiKey: keys\n * });\n * ```\n */\nexport class WeightedKeys implements KeyStrategy {\n private entries: Array<{ key: string; weight: number }>;\n private totalWeight: number;\n\n /**\n * Creates a new WeightedKeys instance.\n *\n * @param keys - Array of key-weight pairs defining selection probabilities\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: Array<{ key: string; weight: number }>) {\n if (keys.length === 0) {\n throw new Error('WeightedKeys requires at least one key');\n }\n this.entries = keys;\n this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);\n }\n\n /**\n * Returns a randomly selected key based on configured weights.\n *\n * @returns An API key selected with probability proportional to its weight\n */\n getKey(): string {\n const random = Math.random() * this.totalWeight;\n let cumulative = 0;\n\n for (const entry of this.entries) {\n cumulative += entry.weight;\n if (random <= cumulative) {\n return entry.key;\n }\n }\n\n return this.entries[this.entries.length - 1]!.key;\n }\n}\n\n/**\n * Provides dynamic key selection using custom logic.\n *\n * This strategy delegates key selection to a user-provided function, enabling\n * advanced scenarios such as:\n * - Fetching keys from a secrets manager (AWS Secrets Manager, HashiCorp Vault)\n * - Rotating keys based on external state or configuration\n * - Selecting keys based on request context or time of day\n * - Implementing custom load balancing algorithms\n *\n * The selector function can be synchronous or asynchronous.\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * // Fetch key from environment based on current mode\n * const dynamicKey = new DynamicKey(() => {\n * return process.env.NODE_ENV === 'production'\n * ? process.env.PROD_API_KEY!\n * : process.env.DEV_API_KEY!;\n * });\n *\n * // Async key fetching from a secrets manager\n * const vaultKey = new DynamicKey(async () => {\n * const secret = await vault.read('secret/openai');\n * return secret.data.apiKey;\n * });\n *\n * // Time-based key rotation\n * const timedKey = new DynamicKey(() => {\n * const hour = new Date().getHours();\n * return hour < 12 ? morningKey : afternoonKey;\n * });\n * ```\n */\nexport class DynamicKey implements KeyStrategy {\n private selector: () => string | Promise<string>;\n\n /**\n * Creates a new DynamicKey instance.\n *\n * @param selector - Function that returns an API key (sync or async)\n */\n constructor(selector: () => string | Promise<string>) {\n this.selector = selector;\n }\n\n /**\n * Invokes the selector function to retrieve the current key.\n *\n * @returns Promise resolving to the selected API key\n */\n async getKey(): Promise<string> {\n return this.selector();\n }\n}\n\n/**\n * Masks an API key for safe logging.\n * Shows first 4 and last 4 characters with ellipsis, or '***' for short keys.\n *\n * @param key - The API key to mask\n * @returns Masked key like \"sk-ab...yz12\" or \"***\" for short keys\n *\n * @example\n * ```typescript\n * maskApiKey('sk-abc123def456xyz789'); // 'sk-a...z789'\n * maskApiKey('short'); // '***'\n * ```\n */\nexport function maskApiKey(key: string): string {\n if (key.length <= 8) {\n return '***';\n }\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n}\n\n/**\n * Type guard to check if a value implements the KeyStrategy interface.\n *\n * @param value - The value to check\n * @returns True if the value has a getKey method\n */\nfunction isKeyStrategy(value: unknown): value is KeyStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'getKey' in value &&\n typeof (value as KeyStrategy).getKey === 'function'\n );\n}\n\n/**\n * Resolves an API key from provider configuration with multiple fallback options.\n *\n * This function handles various key specification methods in priority order:\n * 1. Direct string key in config.apiKey\n * 2. Function returning a key (sync or async) in config.apiKey\n * 3. KeyStrategy instance in config.apiKey (RoundRobinKeys, WeightedKeys, DynamicKey)\n * 4. Environment variable fallback (if envVar parameter is provided)\n *\n * @param config - Provider configuration containing the apiKey option\n * @param envVar - Optional environment variable name to check as fallback\n * @param provider - Provider identifier for error context (default: 'unknown')\n * @param modality - Request modality for error context (default: 'llm')\n * @returns The resolved API key string\n *\n * @throws {UPPError} AUTHENTICATION_FAILED - When no valid key is found\n *\n * @example\n * ```typescript\n * // Direct key in config\n * const key1 = await resolveApiKey({ apiKey: 'sk-...' }, 'OPENAI_API_KEY', 'openai');\n *\n * // Function-based key\n * const key2 = await resolveApiKey({ apiKey: () => getKeyFromVault() }, undefined, 'anthropic');\n *\n * // KeyStrategy instance\n * const key3 = await resolveApiKey({\n * apiKey: new RoundRobinKeys(['sk-1', 'sk-2', 'sk-3'])\n * }, 'OPENAI_API_KEY', 'openai');\n *\n * // Environment variable fallback\n * const key4 = await resolveApiKey({}, 'ANTHROPIC_API_KEY', 'anthropic');\n * ```\n */\nexport async function resolveApiKey(\n config: ProviderConfig,\n envVar?: string,\n provider = 'unknown',\n modality: Modality = 'llm'\n): Promise<string> {\n const { apiKey } = config;\n\n if (apiKey !== undefined) {\n if (typeof apiKey === 'string') {\n return apiKey;\n }\n\n if (typeof apiKey === 'function') {\n return apiKey();\n }\n\n if (isKeyStrategy(apiKey)) {\n return apiKey.getKey();\n }\n }\n\n if (envVar) {\n const envValue = process.env[envVar];\n if (envValue) {\n return envValue;\n }\n }\n\n throw new UPPError(\n envVar\n ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.`\n : 'API key not found. Provide apiKey in config.',\n ErrorCode.AuthenticationFailed,\n provider,\n modality\n );\n}\n"],"mappings":";;;;;;AAkCO,IAAM,iBAAN,MAA4C;AAAA,EACzC;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB,YAAY,MAAgB;AAC1B,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,SAAK,SAAS,KAAK,QAAQ,KAAK,KAAK,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AA8BO,IAAM,eAAN,MAA0C;AAAA,EACvC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQR,YAAY,MAA8C;AACxD,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,SAAK,UAAU;AACf,SAAK,cAAc,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,SAAS,KAAK,OAAO,IAAI,KAAK;AACpC,QAAI,aAAa;AAEjB,eAAW,SAAS,KAAK,SAAS;AAChC,oBAAc,MAAM;AACpB,UAAI,UAAU,YAAY;AACxB,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC,EAAG;AAAA,EAChD;AACF;AAsCO,IAAM,aAAN,MAAwC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,UAA0C;AACpD,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAA0B;AAC9B,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAeO,SAAS,WAAW,KAAqB;AAC9C,MAAI,IAAI,UAAU,GAAG;AACnB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;AAC9C;AAQA,SAAS,cAAc,OAAsC;AAC3D,SACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,OAAQ,MAAsB,WAAW;AAE7C;AAoCA,eAAsB,cACpB,QACA,QACA,WAAW,WACX,WAAqB,OACJ;AACjB,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,WAAW,QAAW;AACxB,QAAI,OAAO,WAAW,UAAU;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,cAAc,MAAM,GAAG;AACzB,aAAO,OAAO,OAAO;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,QAAQ;AACV,UAAM,WAAW,QAAQ,IAAI,MAAM;AACnC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,SACI,0BAA0B,MAAM,uDAChC;AAAA,IACJ,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -207,6 +207,13 @@ function cancelledError(provider, modality) {
207
207
  // src/http/fetch.ts
208
208
  var DEFAULT_TIMEOUT = 12e4;
209
209
  var MAX_RETRY_AFTER_SECONDS = 3600;
210
+ function warnInsecureUrl(url, provider) {
211
+ if (process.env.NODE_ENV !== "production" && url.startsWith("http://") && !url.includes("localhost") && !url.includes("127.0.0.1") && !url.includes("[::1]")) {
212
+ console.warn(
213
+ `[UPP] Provider "${provider}" using non-TLS URL: ${url}. API keys may be exposed to network interception.`
214
+ );
215
+ }
216
+ }
210
217
  function hasFork(strategy) {
211
218
  return !!strategy && typeof strategy.fork === "function";
212
219
  }
@@ -215,6 +222,7 @@ async function doFetch(url, init, config, provider, modality) {
215
222
  const timeout = config.timeout ?? DEFAULT_TIMEOUT;
216
223
  const baseStrategy = config.retryStrategy;
217
224
  const strategy = hasFork(baseStrategy) ? baseStrategy.fork() : baseStrategy;
225
+ warnInsecureUrl(url, provider);
218
226
  let attempt = 0;
219
227
  while (true) {
220
228
  attempt++;
@@ -372,7 +380,8 @@ export {
372
380
  networkError,
373
381
  timeoutError,
374
382
  cancelledError,
383
+ warnInsecureUrl,
375
384
  doFetch,
376
385
  doStreamFetch
377
386
  };
378
- //# sourceMappingURL=chunk-QNJO7DSD.js.map
387
+ //# sourceMappingURL=chunk-4J6OFUKX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/errors.ts","../src/utils/error.ts","../src/http/errors.ts","../src/http/fetch.ts"],"sourcesContent":["/**\n * @fileoverview Error types for the Unified Provider Protocol.\n *\n * Provides normalized error codes and a unified error class for handling\n * errors across different AI providers in a consistent manner.\n *\n * @module types/errors\n */\n\n/**\n * Error code constants for cross-provider error handling.\n *\n * Use these constants instead of raw strings for type-safe error handling:\n *\n * @example\n * ```typescript\n * import { ErrorCode } from 'upp';\n *\n * try {\n * await llm.generate('Hello');\n * } catch (error) {\n * if (error instanceof UPPError) {\n * switch (error.code) {\n * case ErrorCode.RateLimited:\n * await delay(error.retryAfter);\n * break;\n * case ErrorCode.AuthenticationFailed:\n * throw new Error('Invalid API key');\n * }\n * }\n * }\n * ```\n */\nexport const ErrorCode = {\n /** API key is invalid or expired */\n AuthenticationFailed: 'AUTHENTICATION_FAILED',\n /** Rate limit exceeded, retry after delay */\n RateLimited: 'RATE_LIMITED',\n /** Input exceeds model's context window */\n ContextLengthExceeded: 'CONTEXT_LENGTH_EXCEEDED',\n /** Requested model does not exist */\n ModelNotFound: 'MODEL_NOT_FOUND',\n /** Request parameters are malformed */\n InvalidRequest: 'INVALID_REQUEST',\n /** Provider returned an unexpected response format */\n InvalidResponse: 'INVALID_RESPONSE',\n /** Content was blocked by safety filters */\n ContentFiltered: 'CONTENT_FILTERED',\n /** Account quota or credits exhausted */\n QuotaExceeded: 'QUOTA_EXCEEDED',\n /** Provider-specific error not covered by other codes */\n ProviderError: 'PROVIDER_ERROR',\n /** Network connectivity issue */\n NetworkError: 'NETWORK_ERROR',\n /** Request exceeded timeout limit */\n Timeout: 'TIMEOUT',\n /** Request was cancelled via AbortSignal */\n Cancelled: 'CANCELLED',\n} as const;\n\n/**\n * Error code discriminator union.\n *\n * This type is derived from {@link ErrorCode} constants. Use `ErrorCode.RateLimited`\n * for constants or `type MyCode = ErrorCode` for type annotations.\n */\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n/**\n * Modality type constants.\n *\n * Use these constants for type-safe modality handling:\n *\n * @example\n * ```typescript\n * import { ModalityType } from 'upp';\n *\n * if (provider.modality === ModalityType.LLM) {\n * // Handle LLM provider\n * }\n * ```\n */\nexport const ModalityType = {\n /** Large language model for text generation */\n LLM: 'llm',\n /** Text/image embedding model */\n Embedding: 'embedding',\n /** Image generation model */\n Image: 'image',\n /** Audio processing/generation model */\n Audio: 'audio',\n /** Video processing/generation model */\n Video: 'video',\n} as const;\n\n/**\n * Modality type discriminator union.\n *\n * This type is derived from {@link ModalityType} constants. The name `Modality`\n * is kept for backward compatibility; `ModalityType` works as both the const\n * object and this type.\n */\nexport type Modality = (typeof ModalityType)[keyof typeof ModalityType];\n\n/**\n * Type alias for Modality, allowing `ModalityType` to work as both const and type.\n */\nexport type ModalityType = Modality;\n\n/**\n * Unified Provider Protocol Error.\n *\n * All provider-specific errors are normalized to this type, providing\n * a consistent interface for error handling across different AI providers.\n *\n * @example\n * ```typescript\n * import { ErrorCode, ModalityType } from 'upp';\n *\n * throw new UPPError(\n * 'API key is invalid',\n * ErrorCode.AuthenticationFailed,\n * 'openai',\n * ModalityType.LLM,\n * 401\n * );\n * ```\n *\n * @example\n * ```typescript\n * import { ErrorCode, ModalityType } from 'upp';\n *\n * // Wrapping a provider error\n * try {\n * await openai.chat.completions.create({ ... });\n * } catch (err) {\n * throw new UPPError(\n * 'OpenAI request failed',\n * ErrorCode.ProviderError,\n * 'openai',\n * ModalityType.LLM,\n * err.status,\n * err\n * );\n * }\n * ```\n */\nexport class UPPError extends Error {\n /** Normalized error code for programmatic handling */\n readonly code: ErrorCode;\n\n /** Name of the provider that generated the error */\n readonly provider: string;\n\n /** The modality that was being used when the error occurred */\n readonly modality: Modality;\n\n /** HTTP status code from the provider's response, if available */\n readonly statusCode?: number;\n\n /** The original error that caused this UPPError, if wrapping another error */\n override readonly cause?: Error;\n\n /** Error class name, always 'UPPError' */\n override readonly name = 'UPPError';\n\n /**\n * Creates a new UPPError instance.\n *\n * @param message - Human-readable error description\n * @param code - Normalized error code for programmatic handling\n * @param provider - Name of the provider that generated the error\n * @param modality - The modality that was being used\n * @param statusCode - HTTP status code from the provider's response\n * @param cause - The original error being wrapped\n */\n constructor(\n message: string,\n code: ErrorCode,\n provider: string,\n modality: Modality,\n statusCode?: number,\n cause?: Error\n ) {\n super(message);\n this.code = code;\n this.provider = provider;\n this.modality = modality;\n this.statusCode = statusCode;\n this.cause = cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UPPError);\n }\n }\n\n /**\n * Creates a string representation of the error.\n *\n * @returns Formatted error string including code, message, provider, and modality\n */\n override toString(): string {\n let str = `UPPError [${this.code}]: ${this.message}`;\n str += ` (provider: ${this.provider}, modality: ${this.modality}`;\n if (this.statusCode) {\n str += `, status: ${this.statusCode}`;\n }\n str += ')';\n return str;\n }\n\n /**\n * Converts the error to a JSON-serializable object.\n *\n * @returns Plain object representation suitable for logging or transmission\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n provider: this.provider,\n modality: this.modality,\n statusCode: this.statusCode,\n cause: this.cause?.message,\n };\n }\n}\n","/**\n * @fileoverview Error normalization utilities.\n *\n * @module utils/error\n */\n\n/**\n * Converts an unknown thrown value into an Error instance.\n *\n * @param value - Unknown error value\n * @returns An Error instance\n */\nexport function toError(value: unknown): Error {\n if (value instanceof Error) {\n return value;\n }\n if (typeof value === 'string') {\n return new Error(value);\n }\n if (typeof value === 'object' && value !== null && 'message' in value) {\n const message = (value as { message?: unknown }).message;\n if (typeof message === 'string') {\n return new Error(message);\n }\n }\n return new Error(String(value));\n}\n","/**\n * HTTP error handling and normalization utilities.\n * @module http/errors\n */\n\nimport {\n UPPError,\n ErrorCode,\n type Modality,\n} from '../types/errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/**\n * Maps HTTP status codes to standardized UPP error codes.\n *\n * This function provides consistent error categorization across all providers:\n * - 400 -> INVALID_REQUEST (bad request format or parameters)\n * - 401, 403 -> AUTHENTICATION_FAILED (invalid or missing credentials)\n * - 404 -> MODEL_NOT_FOUND (requested model does not exist)\n * - 408 -> TIMEOUT (request timed out)\n * - 413 -> CONTEXT_LENGTH_EXCEEDED (input too long)\n * - 429 -> RATE_LIMITED (too many requests)\n * - 5xx -> PROVIDER_ERROR (server-side issues)\n *\n * @param status - HTTP status code from the response\n * @returns The corresponding UPP ErrorCode\n *\n * @example\n * ```typescript\n * const errorCode = statusToErrorCode(429);\n * // Returns 'RATE_LIMITED'\n *\n * const serverError = statusToErrorCode(503);\n * // Returns 'PROVIDER_ERROR'\n * ```\n */\nexport function statusToErrorCode(status: number): ErrorCode {\n switch (status) {\n case 400:\n return ErrorCode.InvalidRequest;\n case 402:\n return ErrorCode.QuotaExceeded;\n case 401:\n case 403:\n return ErrorCode.AuthenticationFailed;\n case 404:\n return ErrorCode.ModelNotFound;\n case 408:\n return ErrorCode.Timeout;\n case 409:\n return ErrorCode.InvalidRequest;\n case 422:\n return ErrorCode.InvalidRequest;\n case 413:\n return ErrorCode.ContextLengthExceeded;\n case 451:\n return ErrorCode.ContentFiltered;\n case 429:\n return ErrorCode.RateLimited;\n case 500:\n case 502:\n case 503:\n case 504:\n return ErrorCode.ProviderError;\n default:\n return ErrorCode.ProviderError;\n }\n}\n\n/**\n * Normalizes HTTP error responses into standardized UPPError objects.\n *\n * This function performs several operations:\n * 1. Maps the HTTP status code to an appropriate ErrorCode\n * 2. Attempts to extract a meaningful error message from the response body\n * 3. Handles various provider-specific error response formats\n *\n * Supported error message formats:\n * - `{ error: { message: \"...\" } }` (OpenAI, Anthropic)\n * - `{ message: \"...\" }` (simple format)\n * - `{ error: { error: { message: \"...\" } } }` (nested format)\n * - `{ detail: \"...\" }` (FastAPI style)\n * - Plain text body (if under 200 characters)\n *\n * @param response - The HTTP Response object with non-2xx status\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with normalized code and message\n *\n * @example\n * ```typescript\n * if (!response.ok) {\n * const error = await normalizeHttpError(response, 'openai', 'llm');\n * // error.code might be 'RATE_LIMITED' for 429\n * // error.message contains provider's error message\n * throw error;\n * }\n * ```\n */\nexport async function normalizeHttpError(\n response: Response,\n provider: string,\n modality: Modality\n): Promise<UPPError> {\n const code = statusToErrorCode(response.status);\n let message = `HTTP ${response.status}: ${response.statusText}`;\n let bodyReadError: Error | undefined;\n\n try {\n const body = await response.text();\n if (body) {\n try {\n const json = JSON.parse(body);\n const extractedMessage =\n json.error?.message ||\n json.message ||\n json.error?.error?.message ||\n json.detail;\n\n if (extractedMessage) {\n message = extractedMessage;\n }\n } catch {\n if (body.length < 200) {\n message = body;\n }\n }\n }\n } catch (error) {\n bodyReadError = toError(error);\n }\n\n return new UPPError(message, code, provider, modality, response.status, bodyReadError);\n}\n\n/**\n * Creates a UPPError for network failures (DNS, connection, etc.).\n *\n * Use this when the request fails before receiving any HTTP response,\n * such as DNS resolution failures, connection refused, or network unreachable.\n *\n * @param error - The underlying Error that caused the failure\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with NETWORK_ERROR code and the original error attached\n */\nexport function networkError(\n error: Error,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Network error: ${error.message}`,\n ErrorCode.NetworkError,\n provider,\n modality,\n undefined,\n error\n );\n}\n\n/**\n * Creates a UPPError for request timeout.\n *\n * Use this when the request exceeds the configured timeout duration\n * and is aborted by the AbortController.\n *\n * @param timeout - The timeout duration in milliseconds that was exceeded\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with TIMEOUT code\n */\nexport function timeoutError(\n timeout: number,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Request timed out after ${timeout}ms`,\n ErrorCode.Timeout,\n provider,\n modality\n );\n}\n\n/**\n * Creates a UPPError for user-initiated request cancellation.\n *\n * Use this when the request is aborted via a user-provided AbortSignal,\n * distinct from timeout-based cancellation.\n *\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with CANCELLED code\n */\nexport function cancelledError(provider: string, modality: Modality): UPPError {\n return new UPPError(\n 'Request was cancelled',\n ErrorCode.Cancelled,\n provider,\n modality\n );\n}\n","/**\n * HTTP fetch utilities with retry, timeout, and error normalization.\n * @module http/fetch\n */\n\nimport type { ProviderConfig, RetryStrategy } from '../types/provider.ts';\nimport type { Modality } from '../types/errors.ts';\nimport { UPPError } from '../types/errors.ts';\nimport {\n normalizeHttpError,\n networkError,\n timeoutError,\n cancelledError,\n} from './errors.ts';\nimport { toError } from '../utils/error.ts';\n\n/** Default request timeout in milliseconds (2 minutes). */\nconst DEFAULT_TIMEOUT = 120000;\nconst MAX_RETRY_AFTER_SECONDS = 3600;\n\n/**\n * Warns when a non-TLS URL is used with a provider.\n * Only warns in non-production, excludes localhost for local development.\n */\nexport function warnInsecureUrl(url: string, provider: string): void {\n if (\n process.env.NODE_ENV !== 'production' &&\n url.startsWith('http://') &&\n !url.includes('localhost') &&\n !url.includes('127.0.0.1') &&\n !url.includes('[::1]')\n ) {\n console.warn(\n `[UPP] Provider \"${provider}\" using non-TLS URL: ${url}. ` +\n 'API keys may be exposed to network interception.'\n );\n }\n}\n\ntype ForkableRetryStrategy = RetryStrategy & {\n fork: () => RetryStrategy | undefined;\n};\n\nfunction hasFork(strategy: RetryStrategy | undefined): strategy is ForkableRetryStrategy {\n return !!strategy && typeof (strategy as { fork?: unknown }).fork === 'function';\n}\n\n/**\n * Executes an HTTP fetch request with automatic retry, timeout handling, and error normalization.\n *\n * This function wraps the standard fetch API with additional capabilities:\n * - Configurable timeout with automatic request cancellation (per attempt)\n * - Retry strategy support (exponential backoff, linear, token bucket, etc.)\n * - Pre-request delay support for rate limiting strategies\n * - Automatic Retry-After header parsing and handling\n * - Error normalization to UPPError format\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options (method, headers, body, etc.)\n * @param config - Provider configuration containing fetch customization, timeout, and retry strategy\n * @param provider - Provider identifier for error context (e.g., 'openai', 'anthropic')\n * @param modality - Request modality for error context (e.g., 'llm', 'embedding', 'image')\n * @returns The successful Response object\n *\n * @throws {UPPError} RATE_LIMITED - When rate limited and retries exhausted\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n * @throws {UPPError} Various codes based on HTTP status (see statusToErrorCode)\n *\n * @example\n * ```typescript\n * const response = await doFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [] })\n * },\n * { timeout: 30000, retryStrategy: new ExponentialBackoff() },\n * 'openai',\n * 'llm'\n * );\n * ```\n */\nexport async function doFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const baseStrategy = config.retryStrategy;\n const strategy = hasFork(baseStrategy) ? baseStrategy.fork() : baseStrategy;\n\n // Warn about potential security issue with non-TLS URLs\n warnInsecureUrl(url, provider);\n\n let attempt = 0;\n\n while (true) {\n attempt++;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n let response: Response;\n try {\n response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n } catch (error) {\n if (error instanceof UPPError) {\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n throw error;\n }\n\n const uppError = networkError(toError(error), provider, modality);\n\n if (strategy) {\n const delay = await strategy.onRetry(uppError, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n\n throw uppError;\n }\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, provider, modality);\n\n const retryAfterSeconds = parseRetryAfter(\n response.headers.get('Retry-After'),\n config.retryAfterMaxSeconds ?? MAX_RETRY_AFTER_SECONDS\n );\n if (retryAfterSeconds !== null && strategy && 'setRetryAfter' in strategy) {\n (strategy as { setRetryAfter: (s: number) => void }).setRetryAfter(\n retryAfterSeconds\n );\n }\n\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n continue;\n }\n }\n\n throw error;\n }\n\n strategy?.reset?.();\n\n return response;\n }\n}\n\n/**\n * Executes a fetch request with configurable timeout.\n *\n * Creates an AbortController to cancel the request if it exceeds the timeout.\n * Properly handles both user-provided abort signals and timeout-based cancellation,\n * throwing appropriate error types for each case.\n *\n * @param fetchFn - The fetch function to use (allows custom implementations)\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param timeout - Maximum time in milliseconds before aborting\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response from the fetch call\n *\n * @throws {UPPError} TIMEOUT - When the timeout is exceeded\n * @throws {UPPError} CANCELLED - When cancelled via user-provided signal\n * @throws {Error} Network errors are passed through unchanged\n */\nasync function fetchWithTimeout(\n fetchFn: typeof fetch,\n url: string,\n init: RequestInit,\n timeout: number,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const existingSignal = init.signal;\n\n // Check if already aborted before starting\n if (existingSignal?.aborted) {\n throw cancelledError(provider, modality);\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const onAbort = () => controller.abort();\n if (existingSignal) {\n existingSignal.addEventListener('abort', onAbort, { once: true });\n }\n\n try {\n const response = await fetchFn(url, {\n ...init,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n if (toError(error).name === 'AbortError') {\n if (existingSignal?.aborted) {\n throw cancelledError(provider, modality);\n }\n throw timeoutError(timeout, provider, modality);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n if (existingSignal) {\n existingSignal.removeEventListener('abort', onAbort);\n }\n }\n}\n\n/**\n * Delays execution for a specified duration.\n *\n * @param ms - Duration to sleep in milliseconds\n * @returns Promise that resolves after the specified delay\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Executes an HTTP fetch request for streaming responses.\n *\n * Unlike {@link doFetch}, this function returns the response immediately without\n * checking the HTTP status. This is necessary for Server-Sent Events (SSE) and\n * other streaming protocols where error information may be embedded in the stream.\n *\n * The caller is responsible for:\n * - Checking response.ok and handling HTTP errors\n * - Parsing the response stream (e.g., using parseSSEStream)\n * - Handling stream-specific error conditions\n *\n * Retries are not performed for streaming requests since partial data may have\n * already been consumed by the caller.\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param config - Provider configuration containing fetch customization and timeout\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response object (may have non-2xx status)\n *\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n *\n * @example\n * ```typescript\n * const response = await doStreamFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [], stream: true })\n * },\n * { timeout: 120000 },\n * 'openai',\n * 'llm'\n * );\n *\n * if (!response.ok) {\n * throw await normalizeHttpError(response, 'openai', 'llm');\n * }\n *\n * for await (const event of parseSSEStream(response.body!)) {\n * console.log(event);\n * }\n * ```\n */\nexport async function doStreamFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const baseStrategy = config.retryStrategy;\n const strategy = hasFork(baseStrategy) ? baseStrategy.fork() : baseStrategy;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n try {\n const response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n return response;\n } catch (error) {\n if (error instanceof UPPError) {\n throw error;\n }\n throw networkError(toError(error), provider, modality);\n }\n}\n\n/**\n * Parses Retry-After header values into seconds.\n *\n * Supports both delta-seconds and HTTP-date formats.\n */\nfunction parseRetryAfter(headerValue: string | null, maxSeconds: number): number | null {\n if (!headerValue) {\n return null;\n }\n\n const seconds = parseInt(headerValue, 10);\n if (!Number.isNaN(seconds)) {\n return Math.min(maxSeconds, Math.max(0, seconds));\n }\n\n const dateMillis = Date.parse(headerValue);\n if (Number.isNaN(dateMillis)) {\n return null;\n }\n\n const deltaMs = dateMillis - Date.now();\n if (deltaMs <= 0) {\n return 0;\n }\n\n const deltaSeconds = Math.ceil(deltaMs / 1000);\n return Math.min(maxSeconds, Math.max(0, deltaSeconds));\n}\n"],"mappings":";AAiCO,IAAM,YAAY;AAAA;AAAA,EAEvB,sBAAsB;AAAA;AAAA,EAEtB,aAAa;AAAA;AAAA,EAEb,uBAAuB;AAAA;AAAA,EAEvB,eAAe;AAAA;AAAA,EAEf,gBAAgB;AAAA;AAAA,EAEhB,iBAAiB;AAAA;AAAA,EAEjB,iBAAiB;AAAA;AAAA,EAEjB,eAAe;AAAA;AAAA,EAEf,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,SAAS;AAAA;AAAA,EAET,WAAW;AACb;AAwBO,IAAM,eAAe;AAAA;AAAA,EAE1B,KAAK;AAAA;AAAA,EAEL,WAAW;AAAA;AAAA,EAEX,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AACT;AAsDO,IAAM,WAAN,MAAM,kBAAiB,MAAM;AAAA;AAAA,EAEzB;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGS;AAAA;AAAA,EAGA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYzB,YACE,SACA,MACA,UACA,UACA,YACA,OACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,WAAW;AAChB,SAAK,aAAa;AAClB,SAAK,QAAQ;AAEb,QAAI,MAAM,mBAAmB;AAC3B,YAAM,kBAAkB,MAAM,SAAQ;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,WAAmB;AAC1B,QAAI,MAAM,aAAa,KAAK,IAAI,MAAM,KAAK,OAAO;AAClD,WAAO,eAAe,KAAK,QAAQ,eAAe,KAAK,QAAQ;AAC/D,QAAI,KAAK,YAAY;AACnB,aAAO,aAAa,KAAK,UAAU;AAAA,IACrC;AACA,WAAO;AACP,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,OAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACF;;;ACvNO,SAAS,QAAQ,OAAuB;AAC7C,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,IAAI,MAAM,KAAK;AAAA,EACxB;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa,OAAO;AACrE,UAAM,UAAW,MAAgC;AACjD,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO,IAAI,MAAM,OAAO;AAAA,IAC1B;AAAA,EACF;AACA,SAAO,IAAI,MAAM,OAAO,KAAK,CAAC;AAChC;;;ACUO,SAAS,kBAAkB,QAA2B;AAC3D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AACH,aAAO,UAAU;AAAA,IACnB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,UAAU;AAAA,IACnB;AACE,aAAO,UAAU;AAAA,EACrB;AACF;AAgCA,eAAsB,mBACpB,UACA,UACA,UACmB;AACnB,QAAM,OAAO,kBAAkB,SAAS,MAAM;AAC9C,MAAI,UAAU,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AAC7D,MAAI;AAEJ,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,MAAM;AACR,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,cAAM,mBACJ,KAAK,OAAO,WACZ,KAAK,WACL,KAAK,OAAO,OAAO,WACnB,KAAK;AAEP,YAAI,kBAAkB;AACpB,oBAAU;AAAA,QACZ;AAAA,MACF,QAAQ;AACN,YAAI,KAAK,SAAS,KAAK;AACrB,oBAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,oBAAgB,QAAQ,KAAK;AAAA,EAC/B;AAEA,SAAO,IAAI,SAAS,SAAS,MAAM,UAAU,UAAU,SAAS,QAAQ,aAAa;AACvF;AAaO,SAAS,aACd,OACA,UACA,UACU;AACV,SAAO,IAAI;AAAA,IACT,kBAAkB,MAAM,OAAO;AAAA,IAC/B,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAaO,SAAS,aACd,SACA,UACA,UACU;AACV,SAAO,IAAI;AAAA,IACT,2BAA2B,OAAO;AAAA,IAClC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,eAAe,UAAkB,UAA8B;AAC7E,SAAO,IAAI;AAAA,IACT;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AACF;;;ACzLA,IAAM,kBAAkB;AACxB,IAAM,0BAA0B;AAMzB,SAAS,gBAAgB,KAAa,UAAwB;AACnE,MACE,QAAQ,IAAI,aAAa,gBACzB,IAAI,WAAW,SAAS,KACxB,CAAC,IAAI,SAAS,WAAW,KACzB,CAAC,IAAI,SAAS,WAAW,KACzB,CAAC,IAAI,SAAS,OAAO,GACrB;AACA,YAAQ;AAAA,MACN,mBAAmB,QAAQ,wBAAwB,GAAG;AAAA,IAExD;AAAA,EACF;AACF;AAMA,SAAS,QAAQ,UAAwE;AACvF,SAAO,CAAC,CAAC,YAAY,OAAQ,SAAgC,SAAS;AACxE;AAwCA,eAAsB,QACpB,KACA,MACA,QACA,UACA,UACmB;AACnB,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,eAAe,OAAO;AAC5B,QAAM,WAAW,QAAQ,YAAY,IAAI,aAAa,KAAK,IAAI;AAG/D,kBAAgB,KAAK,QAAQ;AAE7B,MAAI,UAAU;AAEd,SAAO,MAAM;AACX;AAEA,QAAI,UAAU,eAAe;AAC3B,YAAM,QAAQ,MAAM,SAAS,cAAc;AAC3C,UAAI,QAAQ,GAAG;AACb,cAAM,MAAM,KAAK;AAAA,MACnB;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,UAAU;AAC7B,YAAI,UAAU;AACZ,gBAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO,OAAO;AACnD,cAAI,UAAU,MAAM;AAClB,kBAAM,MAAM,KAAK;AACjB;AAAA,UACF;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAEA,YAAM,WAAW,aAAa,QAAQ,KAAK,GAAG,UAAU,QAAQ;AAEhE,UAAI,UAAU;AACZ,cAAM,QAAQ,MAAM,SAAS,QAAQ,UAAU,OAAO;AACtD,YAAI,UAAU,MAAM;AAClB,gBAAM,MAAM,KAAK;AACjB;AAAA,QACF;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QAAQ,MAAM,mBAAmB,UAAU,UAAU,QAAQ;AAEnE,YAAM,oBAAoB;AAAA,QACxB,SAAS,QAAQ,IAAI,aAAa;AAAA,QAClC,OAAO,wBAAwB;AAAA,MACjC;AACA,UAAI,sBAAsB,QAAQ,YAAY,mBAAmB,UAAU;AACzE,QAAC,SAAoD;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,UAAU;AACZ,cAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO,OAAO;AACnD,YAAI,UAAU,MAAM;AAClB,gBAAM,MAAM,KAAK;AACjB;AAAA,QACF;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAEA,cAAU,QAAQ;AAElB,WAAO;AAAA,EACT;AACF;AAqBA,eAAe,iBACb,SACA,KACA,MACA,SACA,UACA,UACmB;AACnB,QAAM,iBAAiB,KAAK;AAG5B,MAAI,gBAAgB,SAAS;AAC3B,UAAM,eAAe,UAAU,QAAQ;AAAA,EACzC;AAEA,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,QAAM,UAAU,MAAM,WAAW,MAAM;AACvC,MAAI,gBAAgB;AAClB,mBAAe,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAClE;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,MAClC,GAAG;AAAA,MACH,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,QAAQ,KAAK,EAAE,SAAS,cAAc;AACxC,UAAI,gBAAgB,SAAS;AAC3B,cAAM,eAAe,UAAU,QAAQ;AAAA,MACzC;AACA,YAAM,aAAa,SAAS,UAAU,QAAQ;AAAA,IAChD;AACA,UAAM;AAAA,EACR,UAAE;AACA,iBAAa,SAAS;AACtB,QAAI,gBAAgB;AAClB,qBAAe,oBAAoB,SAAS,OAAO;AAAA,IACrD;AAAA,EACF;AACF;AAQA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAmDA,eAAsB,cACpB,KACA,MACA,QACA,UACA,UACmB;AACnB,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,eAAe,OAAO;AAC5B,QAAM,WAAW,QAAQ,YAAY,IAAI,aAAa,KAAK,IAAI;AAE/D,MAAI,UAAU,eAAe;AAC3B,UAAM,QAAQ,MAAM,SAAS,cAAc;AAC3C,QAAI,QAAQ,GAAG;AACb,YAAM,MAAM,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,YAAM;AAAA,IACR;AACA,UAAM,aAAa,QAAQ,KAAK,GAAG,UAAU,QAAQ;AAAA,EACvD;AACF;AAOA,SAAS,gBAAgB,aAA4B,YAAmC;AACtF,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,SAAS,aAAa,EAAE;AACxC,MAAI,CAAC,OAAO,MAAM,OAAO,GAAG;AAC1B,WAAO,KAAK,IAAI,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,EAClD;AAEA,QAAM,aAAa,KAAK,MAAM,WAAW;AACzC,MAAI,OAAO,MAAM,UAAU,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,aAAa,KAAK,IAAI;AACtC,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,KAAK,KAAK,UAAU,GAAI;AAC7C,SAAO,KAAK,IAAI,YAAY,KAAK,IAAI,GAAG,YAAY,CAAC;AACvD;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ErrorCode
3
- } from "./chunk-QNJO7DSD.js";
3
+ } from "./chunk-4J6OFUKX.js";
4
4
 
5
5
  // src/http/retry.ts
6
6
  var ExponentialBackoff = class {
@@ -260,4 +260,4 @@ export {
260
260
  TokenBucket,
261
261
  RetryAfterStrategy
262
262
  };
263
- //# sourceMappingURL=chunk-SBCATNHA.js.map
263
+ //# sourceMappingURL=chunk-KUPF5KHT.js.map
@@ -9,6 +9,17 @@ import { ap as ProviderIdentity, aq as ProviderConfig, aB as EmbeddingInput, aA
9
9
  * @module types/embedding
10
10
  */
11
11
 
12
+ /**
13
+ * Input type hints for provider-specific embedding optimization.
14
+ * Some providers optimize embeddings differently for queries vs documents.
15
+ */
16
+ declare const EmbeddingInputType: {
17
+ /** Input is a document to be stored/indexed */
18
+ readonly Document: "document";
19
+ /** Input is a query for retrieval/search */
20
+ readonly Query: "query";
21
+ };
22
+ type EmbeddingInputType = (typeof EmbeddingInputType)[keyof typeof EmbeddingInputType];
12
23
  /**
13
24
  * Structural type for embedding model input.
14
25
  * Uses structural typing to avoid generic variance issues with Provider generics.
@@ -62,6 +73,8 @@ interface EmbedOptions {
62
73
  concurrency?: number;
63
74
  /** Abort signal for cancellation */
64
75
  signal?: AbortSignal;
76
+ /** Hint for embedding optimization (provider-specific) */
77
+ inputType?: EmbeddingInputType;
65
78
  }
66
79
  /**
67
80
  * Single embedding vector result.
@@ -155,4 +168,4 @@ interface EmbeddingInstance<TParams = unknown> {
155
168
  readonly params: TParams | undefined;
156
169
  }
157
170
 
158
- export type { EmbeddingOptions as E, EmbeddingInstance as a, EmbedOptions as b, Embedding as c, EmbeddingResult as d, EmbeddingProgress as e, EmbeddingStream as f, EmbeddingModelInput as g };
171
+ export { type EmbeddingOptions as E, type EmbeddingInstance as a, type EmbedOptions as b, type Embedding as c, type EmbeddingResult as d, type EmbeddingProgress as e, type EmbeddingStream as f, type EmbeddingModelInput as g, EmbeddingInputType as h };
@@ -246,6 +246,8 @@ interface GoogleImagePart {
246
246
  interface GoogleFunctionCallPart {
247
247
  /** Function call details. */
248
248
  functionCall: {
249
+ /** Unique identifier for this function call (native ID from API). */
250
+ id?: string;
249
251
  /** Name of the function to call. */
250
252
  name: string;
251
253
  /** Arguments to pass to the function. */
@@ -263,6 +265,8 @@ interface GoogleFunctionCallPart {
263
265
  interface GoogleFunctionResponsePart {
264
266
  /** Function response details. */
265
267
  functionResponse: {
268
+ /** Unique identifier to correlate with the function call (matches functionCall.id if provided). */
269
+ id?: string;
266
270
  /** Name of the function that was called. */
267
271
  name: string;
268
272
  /** Response data from the function execution. */
@@ -3,7 +3,7 @@ import {
3
3
  } from "../chunk-WAKD3OO5.js";
4
4
  import {
5
5
  parseJsonResponse
6
- } from "../chunk-Z6DKC37J.js";
6
+ } from "../chunk-3C7O2RNO.js";
7
7
  import {
8
8
  StreamEventType
9
9
  } from "../chunk-73IIE3QT.js";
@@ -19,7 +19,7 @@ import {
19
19
  } from "../chunk-TOJCZMVU.js";
20
20
  import {
21
21
  resolveApiKey
22
- } from "../chunk-55X3W2MN.js";
22
+ } from "../chunk-3D6XGGVG.js";
23
23
  import {
24
24
  ErrorCode,
25
25
  ModalityType,
@@ -28,7 +28,7 @@ import {
28
28
  doStreamFetch,
29
29
  normalizeHttpError,
30
30
  toError
31
- } from "../chunk-QNJO7DSD.js";
31
+ } from "../chunk-4J6OFUKX.js";
32
32
 
33
33
  // src/providers/google/transform.ts
34
34
  function transformRequest(request, modelId) {
@@ -163,6 +163,7 @@ function transformMessages(messages) {
163
163
  for (const fc of googleMeta.functionCallParts) {
164
164
  const part = {
165
165
  functionCall: {
166
+ id: fc.id,
166
167
  name: fc.name,
167
168
  args: fc.args
168
169
  }
@@ -204,9 +205,7 @@ function transformMessages(messages) {
204
205
  return contents;
205
206
  }
206
207
  function uint8ArrayToBase64(bytes) {
207
- return btoa(
208
- Array.from(bytes).map((b) => String.fromCharCode(b)).join("")
209
- );
208
+ return Buffer.from(bytes).toString("base64");
210
209
  }
211
210
  function transformContentBlock(block) {
212
211
  switch (block.type) {
@@ -306,13 +305,14 @@ function transformResponse(data) {
306
305
  }
307
306
  } else if ("functionCall" in part) {
308
307
  const fc = part;
309
- const toolCallId = createGoogleToolCallId(fc.functionCall.name, toolCalls.length);
308
+ const toolCallId = fc.functionCall.id ?? createGoogleToolCallId(fc.functionCall.name, toolCalls.length);
310
309
  toolCalls.push({
311
310
  toolCallId,
312
311
  toolName: fc.functionCall.name,
313
312
  arguments: fc.functionCall.args
314
313
  });
315
314
  functionCallParts.push({
315
+ id: fc.functionCall.id,
316
316
  name: fc.functionCall.name,
317
317
  args: fc.functionCall.args,
318
318
  thoughtSignature: fc.thoughtSignature
@@ -418,9 +418,10 @@ function transformStreamChunk(chunk, state) {
418
418
  }
419
419
  } else if ("functionCall" in part) {
420
420
  const fc = part;
421
- const toolCallId = createGoogleToolCallId(fc.functionCall.name, state.toolCalls.length);
421
+ const toolCallId = fc.functionCall.id ?? createGoogleToolCallId(fc.functionCall.name, state.toolCalls.length);
422
422
  state.toolCalls.push({
423
423
  id: toolCallId,
424
+ nativeId: fc.functionCall.id,
424
425
  name: fc.functionCall.name,
425
426
  args: fc.functionCall.args,
426
427
  thoughtSignature: fc.thoughtSignature
@@ -500,6 +501,7 @@ function buildResponseFromState(state) {
500
501
  arguments: tc.args
501
502
  });
502
503
  functionCallParts.push({
504
+ id: tc.nativeId,
503
505
  name: tc.name,
504
506
  args: tc.args,
505
507
  thoughtSignature: tc.thoughtSignature
@@ -541,6 +543,8 @@ function normalizeStopReason(reason) {
541
543
  case "SAFETY":
542
544
  case "RECITATION":
543
545
  return "content_filter";
546
+ case "TOOL_USE":
547
+ return "tool_use";
544
548
  case "OTHER":
545
549
  return "end_turn";
546
550
  default:
@@ -802,9 +806,9 @@ function createEmbeddingHandler() {
802
806
  vector: e.values,
803
807
  index,
804
808
  tokens: e.statistics?.tokenCount,
805
- // Per-embedding metadata (NOT redacted)
809
+ // Per-embedding metadata namespaced under provider (Spec 15.4)
806
810
  metadata: e.statistics ? {
807
- truncated: e.statistics.truncated
811
+ google: { truncated: e.statistics.truncated }
808
812
  } : void 0
809
813
  })),
810
814
  usage: { totalTokens }