@jaypie/llm 1.3.3 → 1.3.5

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 (177) hide show
  1. package/dist/cjs/index.cjs +906 -145
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.d.cts +13 -1
  4. package/dist/cjs/{index.d.ts → src/index.d.ts} +1 -0
  5. package/dist/cjs/{operate → src/operate}/adapters/AnthropicAdapter.d.ts +1 -1
  6. package/dist/{esm → cjs/src}/operate/adapters/OpenRouterAdapter.d.ts +8 -1
  7. package/dist/cjs/src/providers/anthropic/client.d.ts +54 -0
  8. package/dist/cjs/src/providers/anthropic/types.d.ts +129 -0
  9. package/dist/{esm → cjs/src}/providers/anthropic/utils.d.ts +5 -5
  10. package/dist/cjs/src/providers/google/client.d.ts +44 -0
  11. package/dist/cjs/{providers → src/providers}/google/utils.d.ts +2 -3
  12. package/dist/cjs/src/providers/openai/client.d.ts +74 -0
  13. package/dist/cjs/src/providers/openai/responseFormat.d.ts +17 -0
  14. package/dist/{esm → cjs/src}/providers/openai/utils.d.ts +4 -4
  15. package/dist/cjs/src/providers/openrouter/client.d.ts +40 -0
  16. package/dist/{esm → cjs/src}/providers/openrouter/utils.d.ts +2 -3
  17. package/dist/cjs/{providers → src/providers}/xai/utils.d.ts +2 -2
  18. package/dist/cjs/{util → src/util}/index.d.ts +2 -0
  19. package/dist/cjs/src/util/jsonSchema.d.ts +17 -0
  20. package/dist/cjs/src/util/sse.d.ts +12 -0
  21. package/dist/esm/index.d.ts +13 -1
  22. package/dist/esm/index.js +917 -158
  23. package/dist/esm/index.js.map +1 -1
  24. package/dist/esm/src/index.d.ts +17 -0
  25. package/dist/esm/{operate → src/operate}/adapters/AnthropicAdapter.d.ts +1 -1
  26. package/dist/{cjs → esm/src}/operate/adapters/OpenRouterAdapter.d.ts +8 -1
  27. package/dist/esm/src/providers/anthropic/client.d.ts +54 -0
  28. package/dist/esm/src/providers/anthropic/types.d.ts +129 -0
  29. package/dist/{cjs → esm/src}/providers/anthropic/utils.d.ts +5 -5
  30. package/dist/esm/src/providers/google/client.d.ts +44 -0
  31. package/dist/esm/{providers → src/providers}/google/utils.d.ts +2 -3
  32. package/dist/esm/src/providers/openai/client.d.ts +74 -0
  33. package/dist/esm/src/providers/openai/responseFormat.d.ts +17 -0
  34. package/dist/{cjs → esm/src}/providers/openai/utils.d.ts +4 -4
  35. package/dist/esm/src/providers/openrouter/client.d.ts +40 -0
  36. package/dist/{cjs → esm/src}/providers/openrouter/utils.d.ts +2 -3
  37. package/dist/esm/{providers → src/providers}/xai/utils.d.ts +2 -2
  38. package/dist/esm/{util → src/util}/index.d.ts +2 -0
  39. package/dist/esm/src/util/jsonSchema.d.ts +17 -0
  40. package/dist/esm/src/util/sse.d.ts +12 -0
  41. package/package.json +4 -19
  42. package/dist/cjs/providers/anthropic/types.d.ts +0 -2
  43. package/dist/esm/providers/anthropic/types.d.ts +0 -2
  44. /package/dist/cjs/{Llm.d.ts → src/Llm.d.ts} +0 -0
  45. /package/dist/cjs/{constants.d.ts → src/constants.d.ts} +0 -0
  46. /package/dist/cjs/{observability → src/observability}/llmobs.d.ts +0 -0
  47. /package/dist/cjs/{operate → src/operate}/OperateLoop.d.ts +0 -0
  48. /package/dist/cjs/{operate → src/operate}/StreamLoop.d.ts +0 -0
  49. /package/dist/cjs/{operate → src/operate}/adapters/BedrockAdapter.d.ts +0 -0
  50. /package/dist/cjs/{operate → src/operate}/adapters/GoogleAdapter.d.ts +0 -0
  51. /package/dist/cjs/{operate → src/operate}/adapters/OpenAiAdapter.d.ts +0 -0
  52. /package/dist/cjs/{operate → src/operate}/adapters/ProviderAdapter.interface.d.ts +0 -0
  53. /package/dist/cjs/{operate → src/operate}/adapters/XaiAdapter.d.ts +0 -0
  54. /package/dist/cjs/{operate → src/operate}/adapters/index.d.ts +0 -0
  55. /package/dist/cjs/{operate → src/operate}/hooks/HookRunner.d.ts +0 -0
  56. /package/dist/cjs/{operate → src/operate}/hooks/index.d.ts +0 -0
  57. /package/dist/cjs/{operate → src/operate}/index.d.ts +0 -0
  58. /package/dist/cjs/{operate → src/operate}/input/InputProcessor.d.ts +0 -0
  59. /package/dist/cjs/{operate → src/operate}/input/index.d.ts +0 -0
  60. /package/dist/cjs/{operate → src/operate}/response/ResponseBuilder.d.ts +0 -0
  61. /package/dist/cjs/{operate → src/operate}/response/index.d.ts +0 -0
  62. /package/dist/cjs/{operate → src/operate}/retry/RetryExecutor.d.ts +0 -0
  63. /package/dist/cjs/{operate → src/operate}/retry/RetryPolicy.d.ts +0 -0
  64. /package/dist/cjs/{operate → src/operate}/retry/createStaleRejectionGuard.d.ts +0 -0
  65. /package/dist/cjs/{operate → src/operate}/retry/index.d.ts +0 -0
  66. /package/dist/cjs/{operate → src/operate}/retry/isTransientNetworkError.d.ts +0 -0
  67. /package/dist/cjs/{operate → src/operate}/types.d.ts +0 -0
  68. /package/dist/cjs/{providers → src/providers}/anthropic/AnthropicProvider.class.d.ts +0 -0
  69. /package/dist/cjs/{providers → src/providers}/anthropic/index.d.ts +0 -0
  70. /package/dist/cjs/{providers → src/providers}/bedrock/BedrockProvider.class.d.ts +0 -0
  71. /package/dist/cjs/{providers → src/providers}/bedrock/index.d.ts +0 -0
  72. /package/dist/cjs/{providers → src/providers}/bedrock/utils.d.ts +0 -0
  73. /package/dist/cjs/{providers → src/providers}/google/GoogleProvider.class.d.ts +0 -0
  74. /package/dist/cjs/{providers → src/providers}/google/index.d.ts +0 -0
  75. /package/dist/cjs/{providers → src/providers}/google/types.d.ts +0 -0
  76. /package/dist/cjs/{providers → src/providers}/openai/OpenAiProvider.class.d.ts +0 -0
  77. /package/dist/cjs/{providers → src/providers}/openai/index.d.ts +0 -0
  78. /package/dist/cjs/{providers → src/providers}/openai/types.d.ts +0 -0
  79. /package/dist/cjs/{providers → src/providers}/openrouter/OpenRouterProvider.class.d.ts +0 -0
  80. /package/dist/cjs/{providers → src/providers}/openrouter/index.d.ts +0 -0
  81. /package/dist/cjs/{providers → src/providers}/xai/XaiProvider.class.d.ts +0 -0
  82. /package/dist/cjs/{providers → src/providers}/xai/index.d.ts +0 -0
  83. /package/dist/cjs/{tools → src/tools}/Toolkit.class.d.ts +0 -0
  84. /package/dist/cjs/{tools → src/tools}/index.d.ts +0 -0
  85. /package/dist/cjs/{tools → src/tools}/random.d.ts +0 -0
  86. /package/dist/cjs/{tools → src/tools}/roll.d.ts +0 -0
  87. /package/dist/cjs/{tools → src/tools}/time.d.ts +0 -0
  88. /package/dist/cjs/{tools → src/tools}/weather.d.ts +0 -0
  89. /package/dist/cjs/{types → src/types}/LlmOperateInput.guards.d.ts +0 -0
  90. /package/dist/cjs/{types → src/types}/LlmProvider.interface.d.ts +0 -0
  91. /package/dist/cjs/{types → src/types}/LlmStreamChunk.interface.d.ts +0 -0
  92. /package/dist/cjs/{types → src/types}/LlmTool.interface.d.ts +0 -0
  93. /package/dist/cjs/{upload → src/upload}/detectMimeType.d.ts +0 -0
  94. /package/dist/cjs/{upload → src/upload}/extractPdfPages.d.ts +0 -0
  95. /package/dist/cjs/{upload → src/upload}/index.d.ts +0 -0
  96. /package/dist/cjs/{upload → src/upload}/loadLocalFile.d.ts +0 -0
  97. /package/dist/cjs/{upload → src/upload}/loadS3File.d.ts +0 -0
  98. /package/dist/cjs/{upload → src/upload}/resolveOperateInput.d.ts +0 -0
  99. /package/dist/cjs/{util → src/util}/determineModelProvider.d.ts +0 -0
  100. /package/dist/cjs/{util → src/util}/extractReasoning.d.ts +0 -0
  101. /package/dist/cjs/{util → src/util}/fillFormatArrays.d.ts +0 -0
  102. /package/dist/cjs/{util → src/util}/formatOperateInput.d.ts +0 -0
  103. /package/dist/cjs/{util → src/util}/formatOperateMessage.d.ts +0 -0
  104. /package/dist/cjs/{util → src/util}/jsonSchemaToOpenApi3.d.ts +0 -0
  105. /package/dist/cjs/{util → src/util}/logger.d.ts +0 -0
  106. /package/dist/cjs/{util → src/util}/maxTurnsFromOptions.d.ts +0 -0
  107. /package/dist/cjs/{util → src/util}/naturalZodSchema.d.ts +0 -0
  108. /package/dist/cjs/{util → src/util}/random.d.ts +0 -0
  109. /package/dist/cjs/{util → src/util}/repairFormatKeys.d.ts +0 -0
  110. /package/dist/cjs/{util → src/util}/tryParseNumber.d.ts +0 -0
  111. /package/dist/esm/{Llm.d.ts → src/Llm.d.ts} +0 -0
  112. /package/dist/esm/{constants.d.ts → src/constants.d.ts} +0 -0
  113. /package/dist/esm/{observability → src/observability}/llmobs.d.ts +0 -0
  114. /package/dist/esm/{operate → src/operate}/OperateLoop.d.ts +0 -0
  115. /package/dist/esm/{operate → src/operate}/StreamLoop.d.ts +0 -0
  116. /package/dist/esm/{operate → src/operate}/adapters/BedrockAdapter.d.ts +0 -0
  117. /package/dist/esm/{operate → src/operate}/adapters/GoogleAdapter.d.ts +0 -0
  118. /package/dist/esm/{operate → src/operate}/adapters/OpenAiAdapter.d.ts +0 -0
  119. /package/dist/esm/{operate → src/operate}/adapters/ProviderAdapter.interface.d.ts +0 -0
  120. /package/dist/esm/{operate → src/operate}/adapters/XaiAdapter.d.ts +0 -0
  121. /package/dist/esm/{operate → src/operate}/adapters/index.d.ts +0 -0
  122. /package/dist/esm/{operate → src/operate}/hooks/HookRunner.d.ts +0 -0
  123. /package/dist/esm/{operate → src/operate}/hooks/index.d.ts +0 -0
  124. /package/dist/esm/{operate → src/operate}/index.d.ts +0 -0
  125. /package/dist/esm/{operate → src/operate}/input/InputProcessor.d.ts +0 -0
  126. /package/dist/esm/{operate → src/operate}/input/index.d.ts +0 -0
  127. /package/dist/esm/{operate → src/operate}/response/ResponseBuilder.d.ts +0 -0
  128. /package/dist/esm/{operate → src/operate}/response/index.d.ts +0 -0
  129. /package/dist/esm/{operate → src/operate}/retry/RetryExecutor.d.ts +0 -0
  130. /package/dist/esm/{operate → src/operate}/retry/RetryPolicy.d.ts +0 -0
  131. /package/dist/esm/{operate → src/operate}/retry/createStaleRejectionGuard.d.ts +0 -0
  132. /package/dist/esm/{operate → src/operate}/retry/index.d.ts +0 -0
  133. /package/dist/esm/{operate → src/operate}/retry/isTransientNetworkError.d.ts +0 -0
  134. /package/dist/esm/{operate → src/operate}/types.d.ts +0 -0
  135. /package/dist/esm/{providers → src/providers}/anthropic/AnthropicProvider.class.d.ts +0 -0
  136. /package/dist/esm/{providers → src/providers}/anthropic/index.d.ts +0 -0
  137. /package/dist/esm/{providers → src/providers}/bedrock/BedrockProvider.class.d.ts +0 -0
  138. /package/dist/esm/{providers → src/providers}/bedrock/index.d.ts +0 -0
  139. /package/dist/esm/{providers → src/providers}/bedrock/utils.d.ts +0 -0
  140. /package/dist/esm/{providers → src/providers}/google/GoogleProvider.class.d.ts +0 -0
  141. /package/dist/esm/{providers → src/providers}/google/index.d.ts +0 -0
  142. /package/dist/esm/{providers → src/providers}/google/types.d.ts +0 -0
  143. /package/dist/esm/{providers → src/providers}/openai/OpenAiProvider.class.d.ts +0 -0
  144. /package/dist/esm/{providers → src/providers}/openai/index.d.ts +0 -0
  145. /package/dist/esm/{providers → src/providers}/openai/types.d.ts +0 -0
  146. /package/dist/esm/{providers → src/providers}/openrouter/OpenRouterProvider.class.d.ts +0 -0
  147. /package/dist/esm/{providers → src/providers}/openrouter/index.d.ts +0 -0
  148. /package/dist/esm/{providers → src/providers}/xai/XaiProvider.class.d.ts +0 -0
  149. /package/dist/esm/{providers → src/providers}/xai/index.d.ts +0 -0
  150. /package/dist/esm/{tools → src/tools}/Toolkit.class.d.ts +0 -0
  151. /package/dist/esm/{tools → src/tools}/index.d.ts +0 -0
  152. /package/dist/esm/{tools → src/tools}/random.d.ts +0 -0
  153. /package/dist/esm/{tools → src/tools}/roll.d.ts +0 -0
  154. /package/dist/esm/{tools → src/tools}/time.d.ts +0 -0
  155. /package/dist/esm/{tools → src/tools}/weather.d.ts +0 -0
  156. /package/dist/esm/{types → src/types}/LlmOperateInput.guards.d.ts +0 -0
  157. /package/dist/esm/{types → src/types}/LlmProvider.interface.d.ts +0 -0
  158. /package/dist/esm/{types → src/types}/LlmStreamChunk.interface.d.ts +0 -0
  159. /package/dist/esm/{types → src/types}/LlmTool.interface.d.ts +0 -0
  160. /package/dist/esm/{upload → src/upload}/detectMimeType.d.ts +0 -0
  161. /package/dist/esm/{upload → src/upload}/extractPdfPages.d.ts +0 -0
  162. /package/dist/esm/{upload → src/upload}/index.d.ts +0 -0
  163. /package/dist/esm/{upload → src/upload}/loadLocalFile.d.ts +0 -0
  164. /package/dist/esm/{upload → src/upload}/loadS3File.d.ts +0 -0
  165. /package/dist/esm/{upload → src/upload}/resolveOperateInput.d.ts +0 -0
  166. /package/dist/esm/{util → src/util}/determineModelProvider.d.ts +0 -0
  167. /package/dist/esm/{util → src/util}/extractReasoning.d.ts +0 -0
  168. /package/dist/esm/{util → src/util}/fillFormatArrays.d.ts +0 -0
  169. /package/dist/esm/{util → src/util}/formatOperateInput.d.ts +0 -0
  170. /package/dist/esm/{util → src/util}/formatOperateMessage.d.ts +0 -0
  171. /package/dist/esm/{util → src/util}/jsonSchemaToOpenApi3.d.ts +0 -0
  172. /package/dist/esm/{util → src/util}/logger.d.ts +0 -0
  173. /package/dist/esm/{util → src/util}/maxTurnsFromOptions.d.ts +0 -0
  174. /package/dist/esm/{util → src/util}/naturalZodSchema.d.ts +0 -0
  175. /package/dist/esm/{util → src/util}/random.d.ts +0 -0
  176. /package/dist/esm/{util → src/util}/repairFormatKeys.d.ts +0 -0
  177. /package/dist/esm/{util → src/util}/tryParseNumber.d.ts +0 -0
package/dist/esm/index.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import { ConfigurationError, BadGatewayError, TooManyRequestsError, NotImplementedError } from '@jaypie/errors';
2
- import log$2, { log as log$1 } from '@jaypie/logger';
2
+ import log$1, { log as log$2 } from '@jaypie/logger';
3
3
  import { z } from 'zod/v4';
4
4
  import { placeholders, JAYPIE, resolveValue, sleep } from '@jaypie/kit';
5
5
  import RandomLib from 'random';
6
- import { RateLimitError, APIConnectionError, APIConnectionTimeoutError, InternalServerError, APIUserAbortError, AuthenticationError, BadRequestError, ConflictError, NotFoundError, PermissionDeniedError, UnprocessableEntityError, OpenAI } from 'openai';
7
- import { zodResponseFormat } from 'openai/helpers/zod';
8
6
  import { createRequire } from 'module';
9
7
  import { pathToFileURL } from 'url';
10
8
  import { PDFDocument } from 'pdf-lib';
@@ -767,6 +765,119 @@ function formatOperateInput(input, options) {
767
765
  return [input];
768
766
  }
769
767
 
768
+ // Keywords a JSON Schema object node can carry that Natural Schema has no
769
+ // representation for. Anything present here (or any other unrecognized key)
770
+ // is dropped silently and logged, never thrown.
771
+ const LOSSY_OBJECT_KEYWORDS = [
772
+ "$defs",
773
+ "$id",
774
+ "$ref",
775
+ "$schema",
776
+ "additionalProperties",
777
+ "const",
778
+ "default",
779
+ "description",
780
+ "format",
781
+ "maxLength",
782
+ "maximum",
783
+ "minLength",
784
+ "minimum",
785
+ "multipleOf",
786
+ "nullable",
787
+ "allOf",
788
+ "anyOf",
789
+ "oneOf",
790
+ "pattern",
791
+ ];
792
+ const KNOWN_KEYWORDS = new Set([
793
+ "type",
794
+ "properties",
795
+ "required",
796
+ "items",
797
+ "enum",
798
+ ...LOSSY_OBJECT_KEYWORDS,
799
+ ]);
800
+ /**
801
+ * Duck-type check for a bare JSON Schema object node: `{ type: "object", properties: {...} }`.
802
+ * Does not require the OpenAI-style `{ type: "json_schema", ... }` envelope.
803
+ */
804
+ function isJsonSchema$1(value) {
805
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
806
+ return false;
807
+ }
808
+ const schema = value;
809
+ return (schema.type === "object" &&
810
+ typeof schema.properties === "object" &&
811
+ schema.properties !== null &&
812
+ !Array.isArray(schema.properties));
813
+ }
814
+ /**
815
+ * Convert a Natural Schema to JSON Schema. Lossless: Natural Schema is a
816
+ * strict subset of what JSON Schema can express.
817
+ */
818
+ function naturalSchemaToJsonSchema(schema) {
819
+ return z.toJSONSchema(naturalZodSchema(schema));
820
+ }
821
+ function logDroppedKeywords(schema, path) {
822
+ for (const key of Object.keys(schema)) {
823
+ if (!KNOWN_KEYWORDS.has(key)) {
824
+ log$1.debug(`[jsonSchemaToNaturalSchema] Dropping unrecognized keyword "${key}" at "${path}"`);
825
+ }
826
+ else if (LOSSY_OBJECT_KEYWORDS.includes(key) &&
827
+ schema[key] !== undefined) {
828
+ log$1.debug(`[jsonSchemaToNaturalSchema] Dropping unsupported keyword "${key}" at "${path}"`);
829
+ }
830
+ }
831
+ }
832
+ function convertNode(schema, path) {
833
+ logDroppedKeywords(schema, path);
834
+ if (Array.isArray(schema.enum)) {
835
+ return schema.enum;
836
+ }
837
+ switch (schema.type) {
838
+ case "string":
839
+ return String;
840
+ case "number":
841
+ case "integer":
842
+ return Number;
843
+ case "boolean":
844
+ return Boolean;
845
+ case "array": {
846
+ const items = schema.items;
847
+ if (typeof items !== "object" || items === null || Array.isArray(items)) {
848
+ return Array;
849
+ }
850
+ return [convertNode(items, `${path}[]`)];
851
+ }
852
+ case "object": {
853
+ const properties = schema.properties;
854
+ if (!properties || Object.keys(properties).length === 0) {
855
+ return Object;
856
+ }
857
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
858
+ const result = {};
859
+ for (const [key, value] of Object.entries(properties)) {
860
+ if (!required.has(key)) {
861
+ log$1.debug(`[jsonSchemaToNaturalSchema] Field "${path}.${key}" is optional; Natural Schema has no optional marker — treating as required`);
862
+ }
863
+ result[key] = convertNode(value, `${path}.${key}`);
864
+ }
865
+ return result;
866
+ }
867
+ default:
868
+ log$1.debug(`[jsonSchemaToNaturalSchema] Unsupported type "${String(schema.type)}" at "${path}"; defaulting to Object`);
869
+ return Object;
870
+ }
871
+ }
872
+ /**
873
+ * Convert a JSON Schema object node to Natural Schema. Lossy: constraints,
874
+ * descriptions, defaults, unions, and optionality have no Natural Schema
875
+ * representation. Dropped keywords are logged at debug level, never thrown.
876
+ */
877
+ function jsonSchemaToNaturalSchema(schema) {
878
+ return convertNode(schema, "$");
879
+ }
880
+
770
881
  /**
771
882
  * Converts a JSON Schema (Draft 2020-12) object to the OpenAPI 3.0 schema subset
772
883
  * that Gemini's `responseSchema` accepts. This constrains generation (not just validation)
@@ -809,7 +920,7 @@ function jsonSchemaToOpenApi3(schema) {
809
920
  return result;
810
921
  }
811
922
 
812
- const getLogger$6 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
923
+ const getLogger$6 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
813
924
 
814
925
  // Turn policy constants
815
926
  const MAX_TURNS_ABSOLUTE_LIMIT = 72;
@@ -1085,6 +1196,45 @@ function repairFormatKeys({ content, format, }) {
1085
1196
  return repairFromSchema(schema, structuredClone(content));
1086
1197
  }
1087
1198
 
1199
+ const DEFAULT_DONE_SENTINEL = "[DONE]";
1200
+ /**
1201
+ * Parse a Server-Sent Events stream into decoded JSON chunks. Buffers across
1202
+ * network reads, dispatches on newline-delimited `data:` lines, and stops at
1203
+ * the done sentinel (OpenAI-style `[DONE]`; Gemini omits it and simply ends
1204
+ * the stream). Comment lines (`: ...`, used for keep-alive) are ignored.
1205
+ *
1206
+ * Shared by the provider HTTP clients that replaced their vendor SDKs.
1207
+ */
1208
+ async function* parseSseStream(body, { doneSentinel = DEFAULT_DONE_SENTINEL } = {}) {
1209
+ const reader = body.getReader();
1210
+ const decoder = new TextDecoder();
1211
+ let buffer = "";
1212
+ try {
1213
+ for (;;) {
1214
+ const { done, value } = await reader.read();
1215
+ if (done)
1216
+ break;
1217
+ buffer += decoder.decode(value, { stream: true });
1218
+ let newlineIndex;
1219
+ while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
1220
+ const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
1221
+ buffer = buffer.slice(newlineIndex + 1);
1222
+ if (line === "" || line.startsWith(":"))
1223
+ continue;
1224
+ if (!line.startsWith("data:"))
1225
+ continue;
1226
+ const data = line.slice("data:".length).trim();
1227
+ if (data === doneSentinel)
1228
+ return;
1229
+ yield JSON.parse(data);
1230
+ }
1231
+ }
1232
+ }
1233
+ finally {
1234
+ reader.releaseLock();
1235
+ }
1236
+ }
1237
+
1088
1238
  /**
1089
1239
  * Helper function to safely call a function that might throw
1090
1240
  * @param fn Function to call safely
@@ -1676,11 +1826,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
1676
1826
  }
1677
1827
  formatOutputSchema(schema) {
1678
1828
  let jsonSchema;
1679
- // Check if schema is already a JsonObject with type "json_schema"
1680
- if (typeof schema === "object" &&
1829
+ // Check if schema is already a JsonObject either the OpenAI-style
1830
+ // `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
1831
+ if ((typeof schema === "object" &&
1681
1832
  schema !== null &&
1682
1833
  !Array.isArray(schema) &&
1683
- schema.type === "json_schema") {
1834
+ schema.type === "json_schema") ||
1835
+ isJsonSchema$1(schema)) {
1684
1836
  jsonSchema = structuredClone(schema);
1685
1837
  jsonSchema.type = "object"; // Validator does not recognize "json_schema"
1686
1838
  }
@@ -2342,6 +2494,10 @@ class BedrockAdapter extends BaseProviderAdapter {
2342
2494
  }));
2343
2495
  }
2344
2496
  formatOutputSchema(schema) {
2497
+ // Bare JSON Schema node: `{ type: "object", properties: {...} }`
2498
+ if (isJsonSchema$1(schema)) {
2499
+ return structuredClone(schema);
2500
+ }
2345
2501
  const zodSchema = schema instanceof z.ZodType
2346
2502
  ? schema
2347
2503
  : naturalZodSchema(schema);
@@ -2878,11 +3034,13 @@ class GoogleAdapter extends BaseProviderAdapter {
2878
3034
  }
2879
3035
  formatOutputSchema(schema) {
2880
3036
  let jsonSchema;
2881
- // Check if schema is already a JsonObject with type "json_schema"
2882
- if (typeof schema === "object" &&
3037
+ // Check if schema is already a JsonObject either the OpenAI-style
3038
+ // `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
3039
+ if ((typeof schema === "object" &&
2883
3040
  schema !== null &&
2884
3041
  !Array.isArray(schema) &&
2885
- schema.type === "json_schema") {
3042
+ schema.type === "json_schema") ||
3043
+ isJsonSchema$1(schema)) {
2886
3044
  jsonSchema = structuredClone(schema);
2887
3045
  jsonSchema.type = "object";
2888
3046
  }
@@ -3505,6 +3663,206 @@ class GoogleAdapter extends BaseProviderAdapter {
3505
3663
  // Export singleton instance
3506
3664
  const googleAdapter = new GoogleAdapter();
3507
3665
 
3666
+ //
3667
+ //
3668
+ // Constants
3669
+ //
3670
+ const OPENAI_BASE_URL = "https://api.openai.com/v1";
3671
+ //
3672
+ //
3673
+ // Errors
3674
+ //
3675
+ // The adapter's `classifyError` uses `instanceof` against these classes, so the
3676
+ // client throws them directly. Status → class mapping mirrors the SDK; the
3677
+ // non-HTTP classes (connection/abort) are thrown when `fetch` itself rejects.
3678
+ //
3679
+ class OpenAiApiError extends Error {
3680
+ constructor(status, message, error) {
3681
+ super(message);
3682
+ this.name = this.constructor.name;
3683
+ this.status = status;
3684
+ this.error = error;
3685
+ }
3686
+ }
3687
+ let BadRequestError$1 = class BadRequestError extends OpenAiApiError {
3688
+ };
3689
+ let AuthenticationError$1 = class AuthenticationError extends OpenAiApiError {
3690
+ };
3691
+ let PermissionDeniedError$1 = class PermissionDeniedError extends OpenAiApiError {
3692
+ };
3693
+ let NotFoundError$1 = class NotFoundError extends OpenAiApiError {
3694
+ };
3695
+ class ConflictError extends OpenAiApiError {
3696
+ }
3697
+ class UnprocessableEntityError extends OpenAiApiError {
3698
+ }
3699
+ let RateLimitError$1 = class RateLimitError extends OpenAiApiError {
3700
+ };
3701
+ let InternalServerError$1 = class InternalServerError extends OpenAiApiError {
3702
+ };
3703
+ class APIConnectionError extends OpenAiApiError {
3704
+ }
3705
+ class APIConnectionTimeoutError extends APIConnectionError {
3706
+ }
3707
+ class APIUserAbortError extends OpenAiApiError {
3708
+ }
3709
+ function errorForStatus$1(status, message, error) {
3710
+ if (status === 400)
3711
+ return new BadRequestError$1(status, message, error);
3712
+ if (status === 401)
3713
+ return new AuthenticationError$1(status, message, error);
3714
+ if (status === 403)
3715
+ return new PermissionDeniedError$1(status, message, error);
3716
+ if (status === 404)
3717
+ return new NotFoundError$1(status, message, error);
3718
+ if (status === 409)
3719
+ return new ConflictError(status, message, error);
3720
+ if (status === 422) {
3721
+ return new UnprocessableEntityError(status, message, error);
3722
+ }
3723
+ if (status === 429)
3724
+ return new RateLimitError$1(status, message, error);
3725
+ if (status >= 500)
3726
+ return new InternalServerError$1(status, message, error);
3727
+ return new OpenAiApiError(status, message, error);
3728
+ }
3729
+ //
3730
+ //
3731
+ // Main
3732
+ //
3733
+ /**
3734
+ * Minimal `fetch`-based client for the OpenAI API. Replaces the `openai` SDK —
3735
+ * the adapters and provider utilities only need the Responses API
3736
+ * (`/responses`, streaming and non-streaming) for `operate`/`stream` and the
3737
+ * Chat Completions API (`/chat/completions`, plus a `parse` helper for
3738
+ * structured output) for `send`. Mirrors the SDK's `responses.create` and
3739
+ * `chat.completions.create` / `chat.completions.parse` surface so call sites are
3740
+ * unchanged. Also serves xAI via a custom `baseURL`.
3741
+ */
3742
+ class OpenAIClient {
3743
+ constructor({ apiKey, baseURL = OPENAI_BASE_URL }) {
3744
+ this.apiKey = apiKey;
3745
+ this.baseURL = baseURL;
3746
+ this.responses = {
3747
+ create: ((params, options) => params.stream
3748
+ ? this.createResponseStream(params, options)
3749
+ : this.createResponse(params, options)),
3750
+ };
3751
+ this.chat = {
3752
+ completions: {
3753
+ create: (params, options) => this.chatCompletion(params, options, { parse: false }),
3754
+ parse: (params, options) => this.chatCompletion(params, options, { parse: true }),
3755
+ },
3756
+ };
3757
+ }
3758
+ headers(extra) {
3759
+ return {
3760
+ Authorization: `Bearer ${this.apiKey}`,
3761
+ "Content-Type": "application/json",
3762
+ ...extra,
3763
+ };
3764
+ }
3765
+ async post(path, body, { signal } = {}, { stream = false } = {}) {
3766
+ let response;
3767
+ try {
3768
+ response = await fetch(`${this.baseURL}${path}`, {
3769
+ method: "POST",
3770
+ headers: this.headers(stream ? { Accept: "text/event-stream" } : undefined),
3771
+ body: JSON.stringify(body),
3772
+ signal,
3773
+ });
3774
+ }
3775
+ catch (cause) {
3776
+ if (signal?.aborted) {
3777
+ throw new APIUserAbortError(0, "Request was aborted");
3778
+ }
3779
+ const message = cause instanceof Error ? cause.message : "Connection error";
3780
+ const error = new APIConnectionError(0, message);
3781
+ error.cause = cause;
3782
+ throw error;
3783
+ }
3784
+ if (!response.ok)
3785
+ throw await this.toError(response);
3786
+ return response;
3787
+ }
3788
+ async toError(response) {
3789
+ let message = `OpenAI request failed with status ${response.status}`;
3790
+ let error;
3791
+ try {
3792
+ const body = (await response.json());
3793
+ if (body?.error?.message) {
3794
+ message = body.error.message;
3795
+ error = body.error;
3796
+ }
3797
+ }
3798
+ catch {
3799
+ // Non-JSON error body; keep the status-based message.
3800
+ }
3801
+ return errorForStatus$1(response.status, message, error);
3802
+ }
3803
+ async createResponse(params, options) {
3804
+ const response = await this.post("/responses", params, options);
3805
+ return (await response.json());
3806
+ }
3807
+ async createResponseStream(params, options) {
3808
+ const response = await this.post("/responses", params, options, {
3809
+ stream: true,
3810
+ });
3811
+ if (!response.body)
3812
+ return (async function* () { })();
3813
+ // The Responses API streams typed events and does not emit a `[DONE]`
3814
+ // sentinel; the empty default sentinel never matches, so the stream ends
3815
+ // when the connection closes.
3816
+ return parseSseStream(response.body);
3817
+ }
3818
+ async chatCompletion(params, options, { parse }) {
3819
+ const response = await this.post("/chat/completions", params, options);
3820
+ const json = (await response.json());
3821
+ // `chat.completions.parse` surfaces parsed JSON on each message; replicate
3822
+ // the SDK by JSON-parsing the assistant content into `message.parsed`.
3823
+ if (parse) {
3824
+ const choices = json.choices;
3825
+ if (Array.isArray(choices)) {
3826
+ for (const choice of choices) {
3827
+ const message = choice.message;
3828
+ if (message &&
3829
+ typeof message.content === "string" &&
3830
+ !message.refusal) {
3831
+ try {
3832
+ message.parsed = JSON.parse(message.content);
3833
+ }
3834
+ catch {
3835
+ message.parsed = null;
3836
+ }
3837
+ }
3838
+ }
3839
+ }
3840
+ }
3841
+ return json;
3842
+ }
3843
+ }
3844
+
3845
+ //
3846
+ //
3847
+ // Main
3848
+ //
3849
+ /**
3850
+ * Local replacement for `openai/helpers/zod`'s `zodResponseFormat`. Builds the
3851
+ * `response_format` payload OpenAI's Chat Completions / Responses APIs expect
3852
+ * from a Zod schema. The SDK always emits `strict: true`; callers re-derive or
3853
+ * sanitize `schema` as needed (e.g. forcing `additionalProperties: false`).
3854
+ */
3855
+ function zodResponseFormat(schema, name) {
3856
+ return {
3857
+ type: "json_schema",
3858
+ json_schema: {
3859
+ name,
3860
+ strict: true,
3861
+ schema: z.toJSONSchema(schema),
3862
+ },
3863
+ };
3864
+ }
3865
+
3508
3866
  // Patterns for OpenAI reasoning models that support extended thinking
3509
3867
  const REASONING_MODEL_PATTERNS = [
3510
3868
  /^gpt-[5-9]/, // GPT-5 and above (gpt-5, gpt-5.1, gpt-5.2, gpt-6, etc.)
@@ -3523,16 +3881,16 @@ function isReasoningModel(model) {
3523
3881
  const RETRYABLE_ERROR_TYPES = [
3524
3882
  APIConnectionError,
3525
3883
  APIConnectionTimeoutError,
3526
- InternalServerError,
3884
+ InternalServerError$1,
3527
3885
  ];
3528
3886
  const NOT_RETRYABLE_ERROR_TYPES = [
3529
3887
  APIUserAbortError,
3530
- AuthenticationError,
3531
- BadRequestError,
3888
+ AuthenticationError$1,
3889
+ BadRequestError$1,
3532
3890
  ConflictError,
3533
- NotFoundError,
3534
- PermissionDeniedError,
3535
- RateLimitError,
3891
+ NotFoundError$1,
3892
+ PermissionDeniedError$1,
3893
+ RateLimitError$1,
3536
3894
  UnprocessableEntityError,
3537
3895
  ];
3538
3896
  // Models known not to accept `temperature`.
@@ -3546,7 +3904,7 @@ const MODELS_WITHOUT_TEMPERATURE$1 = [
3546
3904
  function isTemperatureDeprecationError$1(error) {
3547
3905
  if (!error || typeof error !== "object")
3548
3906
  return false;
3549
- if (!(error instanceof BadRequestError) &&
3907
+ if (!(error instanceof BadRequestError$1) &&
3550
3908
  error.status !== 400) {
3551
3909
  return false;
3552
3910
  }
@@ -3649,21 +4007,35 @@ class OpenAiAdapter extends BaseProviderAdapter {
3649
4007
  schema.type === "json_schema") {
3650
4008
  return schema;
3651
4009
  }
3652
- // Convert NaturalSchema to Zod schema if needed
3653
- const zodSchema = schema instanceof z.ZodType
3654
- ? schema
3655
- : naturalZodSchema(schema);
3656
- const responseFormat = zodResponseFormat(zodSchema, "response");
3657
- // Re-spread to drop zod v4's non-enumerable `~standard` Standard-Schema
3658
- // interop marker, then strip `$schema`: OpenAI's strict json_schema subset
3659
- // does not recognize the draft-2020-12 `$schema` keyword, and shipping it
3660
- // can leave strict structured-output enforcement silently disabled (the
3661
- // model then free-forms, omitting required fields and corrupting keys).
3662
- // Mirrors the OpenRouter adapter's sanitization.
3663
- const jsonSchema = {
3664
- ...z.toJSONSchema(zodSchema),
3665
- };
3666
- delete jsonSchema.$schema;
4010
+ // Bare JSON Schema node: `{ type: "object", properties: {...} }`
4011
+ // Skip the zod round-trip and use it directly (still needs the
4012
+ // additionalProperties + envelope treatment below).
4013
+ let jsonSchema;
4014
+ let name = "response";
4015
+ let strict = true;
4016
+ if (isJsonSchema$1(schema)) {
4017
+ jsonSchema = { ...structuredClone(schema) };
4018
+ delete jsonSchema.$schema;
4019
+ }
4020
+ else {
4021
+ // Convert NaturalSchema to Zod schema if needed
4022
+ const zodSchema = schema instanceof z.ZodType
4023
+ ? schema
4024
+ : naturalZodSchema(schema);
4025
+ const responseFormat = zodResponseFormat(zodSchema, "response");
4026
+ // Re-spread to drop zod v4's non-enumerable `~standard` Standard-Schema
4027
+ // interop marker, then strip `$schema`: OpenAI's strict json_schema subset
4028
+ // does not recognize the draft-2020-12 `$schema` keyword, and shipping it
4029
+ // can leave strict structured-output enforcement silently disabled (the
4030
+ // model then free-forms, omitting required fields and corrupting keys).
4031
+ // Mirrors the OpenRouter adapter's sanitization.
4032
+ jsonSchema = {
4033
+ ...z.toJSONSchema(zodSchema),
4034
+ };
4035
+ delete jsonSchema.$schema;
4036
+ name = responseFormat.json_schema.name;
4037
+ strict = responseFormat.json_schema.strict;
4038
+ }
3667
4039
  // OpenAI requires additionalProperties to be false on all objects
3668
4040
  const checks = [jsonSchema];
3669
4041
  while (checks.length > 0) {
@@ -3679,10 +4051,10 @@ class OpenAiAdapter extends BaseProviderAdapter {
3679
4051
  checks.shift();
3680
4052
  }
3681
4053
  return {
3682
- name: responseFormat.json_schema.name,
4054
+ name,
3683
4055
  schema: jsonSchema,
3684
- strict: responseFormat.json_schema.strict,
3685
- type: responseFormat.type,
4056
+ strict,
4057
+ type: "json_schema",
3686
4058
  };
3687
4059
  }
3688
4060
  //
@@ -3900,7 +4272,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
3900
4272
  //
3901
4273
  classifyError(error) {
3902
4274
  // Check for rate limit error
3903
- if (error instanceof RateLimitError) {
4275
+ if (error instanceof RateLimitError$1) {
3904
4276
  return {
3905
4277
  error,
3906
4278
  category: ErrorCategory.RateLimit,
@@ -4088,7 +4460,7 @@ function convertContentToOpenRouter(content) {
4088
4460
  if (item.type === LlmMessageType.InputImage) {
4089
4461
  const url = item.image_url ?? "";
4090
4462
  if (!url) {
4091
- log$1.warn("OpenRouter image content missing image_url; image discarded");
4463
+ log$2.warn("OpenRouter image content missing image_url; image discarded");
4092
4464
  continue;
4093
4465
  }
4094
4466
  parts.push({ type: "image_url", imageUrl: { url } });
@@ -4097,7 +4469,7 @@ function convertContentToOpenRouter(content) {
4097
4469
  if (item.type === LlmMessageType.InputFile) {
4098
4470
  const fileData = typeof item.file_data === "string" ? item.file_data : "";
4099
4471
  if (!fileData) {
4100
- log$1.warn({ filename: item.filename }, "OpenRouter file content missing file_data; file discarded");
4472
+ log$2.warn({ filename: item.filename }, "OpenRouter file content missing file_data; file discarded");
4101
4473
  continue;
4102
4474
  }
4103
4475
  parts.push({
@@ -4110,7 +4482,7 @@ function convertContentToOpenRouter(content) {
4110
4482
  continue;
4111
4483
  }
4112
4484
  // Unknown type - warn and skip
4113
- log$1.warn({ item }, "Unknown content type for OpenRouter; discarded");
4485
+ log$2.warn({ item }, "Unknown content type for OpenRouter; discarded");
4114
4486
  }
4115
4487
  // If no parts remain, return empty string to avoid empty array
4116
4488
  if (parts.length === 0) {
@@ -4118,7 +4490,49 @@ function convertContentToOpenRouter(content) {
4118
4490
  }
4119
4491
  return parts;
4120
4492
  }
4121
- // OpenRouter SDK error types based on HTTP status codes
4493
+ /**
4494
+ * Convert internal content parts to the OpenAI-compatible wire shape. The
4495
+ * internal representation uses camelCase keys (`imageUrl`, `fileData`) that the
4496
+ * SDK accepted; the REST API wants snake_case (`image_url`, `file_data`).
4497
+ */
4498
+ function contentToWire(content) {
4499
+ if (content === null ||
4500
+ content === undefined ||
4501
+ typeof content === "string") {
4502
+ return content;
4503
+ }
4504
+ return content.map((part) => {
4505
+ if (part.type === "image_url") {
4506
+ return { type: "image_url", image_url: part.imageUrl };
4507
+ }
4508
+ if (part.type === "file") {
4509
+ return {
4510
+ type: "file",
4511
+ file: { filename: part.file.filename, file_data: part.file.fileData },
4512
+ };
4513
+ }
4514
+ return part;
4515
+ });
4516
+ }
4517
+ /**
4518
+ * Serialize an internal message to the OpenAI-compatible wire shape, mapping
4519
+ * camelCase tool fields (`toolCalls`, `toolCallId`) to snake_case. Tool-call
4520
+ * objects are already wire-shaped (`{ id, type, function: { name, arguments } }`).
4521
+ */
4522
+ function messageToWire(message) {
4523
+ const wire = { role: message.role };
4524
+ if (message.content !== undefined) {
4525
+ wire.content = contentToWire(message.content);
4526
+ }
4527
+ if (message.toolCalls) {
4528
+ wire.tool_calls = message.toolCalls;
4529
+ }
4530
+ if (message.toolCallId) {
4531
+ wire.tool_call_id = message.toolCallId;
4532
+ }
4533
+ return wire;
4534
+ }
4535
+ // OpenRouter error types based on HTTP status codes
4122
4536
  const RETRYABLE_STATUS_CODES = [408, 500, 502, 503, 524, 529];
4123
4537
  const RATE_LIMIT_STATUS_CODE = 429;
4124
4538
  /**
@@ -4216,7 +4630,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4216
4630
  ? [...request.tools]
4217
4631
  : [];
4218
4632
  if (useFallbackStructuredOutput && request.format) {
4219
- log$1.warn(`[OpenRouterAdapter] Using legacy structured_output tool fallback for model ${openRouterRequest.model}; native response_format previously rejected for this model.`);
4633
+ log$2.warn(`[OpenRouterAdapter] Using legacy structured_output tool fallback for model ${openRouterRequest.model}; native response_format previously rejected for this model.`);
4220
4634
  allTools.push({
4221
4635
  name: STRUCTURED_OUTPUT_TOOL_NAME,
4222
4636
  description: "REQUIRED: You MUST call this tool to provide your final response. " +
@@ -4282,11 +4696,13 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4282
4696
  }
4283
4697
  formatOutputSchema(schema) {
4284
4698
  let jsonSchema;
4285
- // Check if schema is already a JsonObject with type "json_schema"
4286
- if (typeof schema === "object" &&
4699
+ // Check if schema is already a JsonObject either the OpenAI-style
4700
+ // `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
4701
+ if ((typeof schema === "object" &&
4287
4702
  schema !== null &&
4288
4703
  !Array.isArray(schema) &&
4289
- schema.type === "json_schema") {
4704
+ schema.type === "json_schema") ||
4705
+ isJsonSchema$1(schema)) {
4290
4706
  jsonSchema = structuredClone(schema);
4291
4707
  jsonSchema.type = "object"; // Normalize type
4292
4708
  }
@@ -4321,7 +4737,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4321
4737
  const openRouterRequest = request;
4322
4738
  const wantsStructuredOutput = Boolean(openRouterRequest.response_format);
4323
4739
  try {
4324
- const response = (await openRouter.chat.send(this.toSdkChatParams(openRouterRequest), signal ? { signal } : undefined));
4740
+ const response = (await openRouter.chatCompletion(this.toWireBody(openRouterRequest), signal ? { signal } : undefined));
4325
4741
  if (wantsStructuredOutput) {
4326
4742
  response.__jaypieStructuredOutput = true;
4327
4743
  }
@@ -4338,7 +4754,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4338
4754
  this.rememberModelRejectsTemperature(openRouterRequest.model);
4339
4755
  const retryRequest = { ...openRouterRequest };
4340
4756
  delete retryRequest.temperature;
4341
- const response = (await openRouter.chat.send(this.toSdkChatParams(retryRequest), signal ? { signal } : undefined));
4757
+ const response = (await openRouter.chatCompletion(this.toWireBody(retryRequest), signal ? { signal } : undefined));
4342
4758
  if (wantsStructuredOutput) {
4343
4759
  response.__jaypieStructuredOutput = true;
4344
4760
  }
@@ -4349,9 +4765,9 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4349
4765
  if (wantsStructuredOutput && isStructuredOutputUnsupportedError(error)) {
4350
4766
  const model = openRouterRequest.model;
4351
4767
  this.rememberModelRejectsStructuredOutput(model);
4352
- log$1.warn(`[OpenRouterAdapter] Model ${model} rejected native response_format; falling back to legacy structured_output tool emulation.`);
4768
+ log$2.warn(`[OpenRouterAdapter] Model ${model} rejected native response_format; falling back to legacy structured_output tool emulation.`);
4353
4769
  const fallbackRequest = this.toFallbackStructuredOutputRequest(openRouterRequest);
4354
- return (await openRouter.chat.send(this.toSdkChatParams(fallbackRequest), signal ? { signal } : undefined));
4770
+ return (await openRouter.chatCompletion(this.toWireBody(fallbackRequest), signal ? { signal } : undefined));
4355
4771
  }
4356
4772
  throw error;
4357
4773
  }
@@ -4361,31 +4777,18 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4361
4777
  * camelCase shape, forwarding only the fields we care about (the SDK
4362
4778
  * silently strips unknown fields).
4363
4779
  */
4364
- toSdkChatParams(openRouterRequest) {
4365
- const params = {
4366
- model: openRouterRequest.model,
4367
- messages: openRouterRequest.messages,
4368
- tools: openRouterRequest.tools,
4369
- toolChoice: openRouterRequest.tool_choice,
4370
- user: openRouterRequest.user,
4780
+ /**
4781
+ * Serialize the internal request into the OpenAI-compatible wire body for
4782
+ * OpenRouter's Chat Completions endpoint. Top-level fields (model, tools,
4783
+ * tool_choice, response_format, user, temperature, and any providerOptions)
4784
+ * are already wire-shaped (snake_case); only messages carry camelCase tool
4785
+ * fields that must become snake_case on the wire.
4786
+ */
4787
+ toWireBody(openRouterRequest) {
4788
+ return {
4789
+ ...openRouterRequest,
4790
+ messages: openRouterRequest.messages.map(messageToWire),
4371
4791
  };
4372
- if (openRouterRequest.response_format) {
4373
- const format = openRouterRequest.response_format;
4374
- if (format.type === "json_schema") {
4375
- params.responseFormat = {
4376
- type: "json_schema",
4377
- jsonSchema: format.json_schema,
4378
- };
4379
- }
4380
- else {
4381
- params.responseFormat = format;
4382
- }
4383
- }
4384
- const temperature = openRouterRequest.temperature;
4385
- if (temperature !== undefined) {
4386
- params.temperature = temperature;
4387
- }
4388
- return params;
4389
4792
  }
4390
4793
  /**
4391
4794
  * Rebuild a structured-output request without `response_format`, swapping in
@@ -4417,15 +4820,9 @@ class OpenRouterAdapter extends BaseProviderAdapter {
4417
4820
  async *executeStreamRequest(client, request, signal) {
4418
4821
  const openRouter = client;
4419
4822
  const openRouterRequest = request;
4420
- // Use chat.send with stream: true for streaming responses.
4421
- // Cast the result to AsyncIterable: when stream: true, the SDK returns a
4422
- // stream we can iterate, but the typed result is the union with the
4423
- // non-stream response.
4424
- const streamParams = {
4425
- ...this.toSdkChatParams(openRouterRequest),
4426
- stream: true,
4427
- };
4428
- const stream = (await openRouter.chat.send(streamParams, signal ? { signal } : undefined));
4823
+ // streamChatCompletion adds `stream: true` + `stream_options.include_usage`
4824
+ // and yields decoded SSE chunks in OpenAI-compatible (snake_case) shape.
4825
+ const stream = openRouter.streamChatCompletion(this.toWireBody(openRouterRequest), signal ? { signal } : undefined);
4429
4826
  // Track current tool call being built
4430
4827
  let currentToolCall = null;
4431
4828
  // Track usage for final chunk
@@ -4874,7 +5271,7 @@ const TRANSIENT_INGEST_MESSAGE_PATTERNS = [
4874
5271
  "failed to ingest inline file bytes",
4875
5272
  ];
4876
5273
  function isTransientIngestError(error) {
4877
- if (!(error instanceof BadRequestError))
5274
+ if (!(error instanceof BadRequestError$1))
4878
5275
  return false;
4879
5276
  const message = (error.message ?? "").toLowerCase();
4880
5277
  return TRANSIENT_INGEST_MESSAGE_PATTERNS.some((pattern) => message.includes(pattern));
@@ -4908,7 +5305,7 @@ class XaiAdapter extends OpenAiAdapter {
4908
5305
  const xaiAdapter = new XaiAdapter();
4909
5306
 
4910
5307
  const DEFAULT_TOOL_TYPE = "function";
4911
- const log = log$1.lib({ lib: JAYPIE.LIB.LLM });
5308
+ const log = log$2.lib({ lib: JAYPIE.LIB.LLM });
4912
5309
  function logToolMessage(message, context) {
4913
5310
  log.trace.var({ [context.name]: message });
4914
5311
  }
@@ -5066,7 +5463,7 @@ const requireModule = typeof __filename !== "undefined"
5066
5463
  ? createRequire(pathToFileURL(__filename).href)
5067
5464
  : createRequire(import.meta.url);
5068
5465
  let resolved = false;
5069
- let cachedSdk$4 = null;
5466
+ let cachedSdk$1 = null;
5070
5467
  /**
5071
5468
  * Lazily resolve the dd-trace `llmobs` SDK. Returns null (and never throws)
5072
5469
  * when dd-trace is absent or the SDK surface is unexpected. Cached after the
@@ -5074,7 +5471,7 @@ let cachedSdk$4 = null;
5074
5471
  */
5075
5472
  function resolveLlmObs() {
5076
5473
  if (resolved) {
5077
- return cachedSdk$4;
5474
+ return cachedSdk$1;
5078
5475
  }
5079
5476
  resolved = true;
5080
5477
  try {
@@ -5084,13 +5481,13 @@ function resolveLlmObs() {
5084
5481
  if (llmobs &&
5085
5482
  typeof llmobs.trace === "function" &&
5086
5483
  typeof llmobs.annotate === "function") {
5087
- cachedSdk$4 = llmobs;
5484
+ cachedSdk$1 = llmobs;
5088
5485
  }
5089
5486
  }
5090
5487
  catch {
5091
- cachedSdk$4 = null;
5488
+ cachedSdk$1 = null;
5092
5489
  }
5093
- return cachedSdk$4;
5490
+ return cachedSdk$1;
5094
5491
  }
5095
5492
  //
5096
5493
  //
@@ -7004,21 +7401,127 @@ function createStreamLoop(config) {
7004
7401
  return new StreamLoop(config);
7005
7402
  }
7006
7403
 
7007
- // SDK loader with caching
7008
- let cachedSdk$3 = null;
7009
- async function loadSdk$3() {
7010
- if (cachedSdk$3)
7011
- return cachedSdk$3;
7012
- try {
7013
- cachedSdk$3 = await import('@anthropic-ai/sdk');
7014
- return cachedSdk$3;
7404
+ //
7405
+ //
7406
+ // Constants
7407
+ //
7408
+ const ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1";
7409
+ const ANTHROPIC_VERSION = "2023-06-01";
7410
+ //
7411
+ //
7412
+ // Errors
7413
+ //
7414
+ // The adapter's `classifyError` keys off `error.constructor.name` (the SDK's
7415
+ // class names), so the client throws errors whose class names match. Status →
7416
+ // class mapping mirrors the SDK.
7417
+ //
7418
+ class AnthropicApiError extends Error {
7419
+ constructor(status, message, error) {
7420
+ super(message);
7421
+ this.name = this.constructor.name;
7422
+ this.status = status;
7423
+ this.error = error;
7015
7424
  }
7016
- catch {
7017
- throw new ConfigurationError("@anthropic-ai/sdk is required but not installed. Run: npm install @anthropic-ai/sdk");
7425
+ }
7426
+ class BadRequestError extends AnthropicApiError {
7427
+ }
7428
+ class AuthenticationError extends AnthropicApiError {
7429
+ }
7430
+ class PermissionDeniedError extends AnthropicApiError {
7431
+ }
7432
+ class NotFoundError extends AnthropicApiError {
7433
+ }
7434
+ class RateLimitError extends AnthropicApiError {
7435
+ }
7436
+ class InternalServerError extends AnthropicApiError {
7437
+ }
7438
+ function errorForStatus(status, message, error) {
7439
+ if (status === 400)
7440
+ return new BadRequestError(status, message, error);
7441
+ if (status === 401)
7442
+ return new AuthenticationError(status, message, error);
7443
+ if (status === 403)
7444
+ return new PermissionDeniedError(status, message, error);
7445
+ if (status === 404)
7446
+ return new NotFoundError(status, message, error);
7447
+ if (status === 429)
7448
+ return new RateLimitError(status, message, error);
7449
+ if (status >= 500)
7450
+ return new InternalServerError(status, message, error);
7451
+ return new AnthropicApiError(status, message, error);
7452
+ }
7453
+ //
7454
+ //
7455
+ // Main
7456
+ //
7457
+ /**
7458
+ * Minimal `fetch`-based client for Anthropic's Messages API. Replaces
7459
+ * `@anthropic-ai/sdk` — the adapter only needs `messages.create` (streaming and
7460
+ * non-streaming), header auth, and HTTP errors whose class names drive
7461
+ * `classifyError`. Internal request params are already Anthropic's wire shape,
7462
+ * so they serialize verbatim. Exposes a `messages` facade mirroring the SDK so
7463
+ * the adapter call sites are unchanged.
7464
+ */
7465
+ class AnthropicClient {
7466
+ constructor({ apiKey, baseURL = ANTHROPIC_BASE_URL, }) {
7467
+ this.apiKey = apiKey;
7468
+ this.baseURL = baseURL;
7469
+ this.messages = {
7470
+ create: ((params, options) => params.stream
7471
+ ? this.createStream(params, options)
7472
+ : this.createMessage(params, options)),
7473
+ };
7474
+ }
7475
+ headers() {
7476
+ return {
7477
+ "anthropic-version": ANTHROPIC_VERSION,
7478
+ "content-type": "application/json",
7479
+ "x-api-key": this.apiKey,
7480
+ };
7481
+ }
7482
+ async toError(response) {
7483
+ let message = `Anthropic request failed with status ${response.status}`;
7484
+ let error;
7485
+ try {
7486
+ const body = (await response.json());
7487
+ if (body?.error?.message) {
7488
+ message = body.error.message;
7489
+ error = body.error;
7490
+ }
7491
+ }
7492
+ catch {
7493
+ // Non-JSON error body; keep the status-based message.
7494
+ }
7495
+ return errorForStatus(response.status, message, error);
7496
+ }
7497
+ async createMessage(params, { signal } = {}) {
7498
+ const response = await fetch(`${this.baseURL}/messages`, {
7499
+ method: "POST",
7500
+ headers: this.headers(),
7501
+ body: JSON.stringify(params),
7502
+ signal,
7503
+ });
7504
+ if (!response.ok)
7505
+ throw await this.toError(response);
7506
+ return (await response.json());
7507
+ }
7508
+ async createStream(params, { signal } = {}) {
7509
+ const response = await fetch(`${this.baseURL}/messages`, {
7510
+ method: "POST",
7511
+ headers: { ...this.headers(), accept: "text/event-stream" },
7512
+ body: JSON.stringify(params),
7513
+ signal,
7514
+ });
7515
+ if (!response.ok)
7516
+ throw await this.toError(response);
7517
+ if (!response.body)
7518
+ return (async function* () { })();
7519
+ return parseSseStream(response.body);
7018
7520
  }
7019
7521
  }
7522
+
7020
7523
  // Logger
7021
- const getLogger$5 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
7524
+ const getLogger$5 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7022
7525
  // Client initialization
7023
7526
  async function initializeClient$5({ apiKey, } = {}) {
7024
7527
  const logger = getLogger$5();
@@ -7026,8 +7529,7 @@ async function initializeClient$5({ apiKey, } = {}) {
7026
7529
  if (!resolvedApiKey) {
7027
7530
  throw new ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
7028
7531
  }
7029
- const sdk = await loadSdk$3();
7030
- const client = new sdk.default({ apiKey: resolvedApiKey });
7532
+ const client = new AnthropicClient({ apiKey: resolvedApiKey });
7031
7533
  logger.trace("Initialized Anthropic client");
7032
7534
  return client;
7033
7535
  }
@@ -7057,7 +7559,7 @@ function prepareMessages$3(message, { data, placeholders } = {}) {
7057
7559
  }
7058
7560
  // Basic text completion
7059
7561
  async function createTextCompletion$1(client, messages, model, systemMessage) {
7060
- log$1.trace("Using text output (unstructured)");
7562
+ log$2.trace("Using text output (unstructured)");
7061
7563
  const params = {
7062
7564
  model,
7063
7565
  messages,
@@ -7066,19 +7568,19 @@ async function createTextCompletion$1(client, messages, model, systemMessage) {
7066
7568
  // Add system instruction if provided
7067
7569
  if (systemMessage) {
7068
7570
  params.system = systemMessage;
7069
- log$1.trace(`System message: ${systemMessage.length} characters`);
7571
+ log$2.trace(`System message: ${systemMessage.length} characters`);
7070
7572
  }
7071
7573
  const response = await client.messages.create(params);
7072
7574
  const firstContent = response.content[0];
7073
7575
  const text = firstContent && "text" in firstContent ? firstContent.text : "";
7074
- log$1.trace(`Assistant reply: ${text.length} characters`);
7576
+ log$2.trace(`Assistant reply: ${text.length} characters`);
7075
7577
  return text;
7076
7578
  }
7077
7579
  // Structured output completion using Anthropic's native `output_config.format`
7078
7580
  // field. Returns a JsonObject parsed and validated against the caller's
7079
7581
  // Zod schema.
7080
7582
  async function createStructuredCompletion$1(client, messages, model, responseSchema, systemMessage) {
7081
- log$1.trace("Using native structured output");
7583
+ log$2.trace("Using native structured output");
7082
7584
  const schema = responseSchema instanceof z.ZodType
7083
7585
  ? responseSchema
7084
7586
  : naturalZodSchema(responseSchema);
@@ -7116,7 +7618,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
7116
7618
  if (!validation.success) {
7117
7619
  throw new Error(`JSON response from Anthropic does not match schema: ${textBlock.text}`);
7118
7620
  }
7119
- log$1.trace("Received structured response", { result: validation.data });
7621
+ log$2.trace("Received structured response", { result: validation.data });
7120
7622
  return validation.data;
7121
7623
  }
7122
7624
 
@@ -7127,6 +7629,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
7127
7629
  [LlmMessageRole.Assistant]: PROVIDER.ANTHROPIC.ROLE.ASSISTANT,
7128
7630
  [LlmMessageRole.Developer]: PROVIDER.ANTHROPIC.ROLE.SYSTEM,
7129
7631
  });
7632
+ /* eslint-enable @typescript-eslint/no-namespace */
7130
7633
 
7131
7634
  // Main class implementation
7132
7635
  class AnthropicProvider {
@@ -7215,23 +7718,23 @@ class AnthropicProvider {
7215
7718
  }
7216
7719
  }
7217
7720
 
7218
- let cachedSdk$2 = null;
7219
- async function loadSdk$2() {
7220
- if (cachedSdk$2)
7221
- return cachedSdk$2;
7721
+ let cachedSdk = null;
7722
+ async function loadSdk() {
7723
+ if (cachedSdk)
7724
+ return cachedSdk;
7222
7725
  try {
7223
- cachedSdk$2 = await import('@aws-sdk/client-bedrock-runtime');
7224
- return cachedSdk$2;
7726
+ cachedSdk = await import('@aws-sdk/client-bedrock-runtime');
7727
+ return cachedSdk;
7225
7728
  }
7226
7729
  catch {
7227
7730
  throw new ConfigurationError("@aws-sdk/client-bedrock-runtime is required but not installed. Run: npm install @aws-sdk/client-bedrock-runtime");
7228
7731
  }
7229
7732
  }
7230
- const getLogger$4 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
7733
+ const getLogger$4 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7231
7734
  async function initializeClient$4({ region, } = {}) {
7232
7735
  const logger = getLogger$4();
7233
7736
  const resolvedRegion = region || process.env.AWS_REGION || "us-east-1";
7234
- const sdk = await loadSdk$2();
7737
+ const sdk = await loadSdk();
7235
7738
  const client = new sdk.BedrockRuntimeClient({ region: resolvedRegion });
7236
7739
  logger.trace("Initialized Bedrock client");
7237
7740
  return client;
@@ -7296,21 +7799,158 @@ class BedrockProvider {
7296
7799
  }
7297
7800
  }
7298
7801
 
7299
- // SDK loader with caching
7300
- let cachedSdk$1 = null;
7301
- async function loadSdk$1() {
7302
- if (cachedSdk$1)
7303
- return cachedSdk$1;
7304
- try {
7305
- cachedSdk$1 = await import('@google/genai');
7306
- return cachedSdk$1;
7802
+ //
7803
+ //
7804
+ // Constants
7805
+ //
7806
+ const GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
7807
+ // Config keys the REST API expects at the top level of the request body. Every
7808
+ // other key in the SDK-style `config` object belongs under `generationConfig`.
7809
+ const TOP_LEVEL_CONFIG_KEYS = new Set([
7810
+ "systemInstruction",
7811
+ "tools",
7812
+ "toolConfig",
7813
+ "safetySettings",
7814
+ "cachedContent",
7815
+ ]);
7816
+ /**
7817
+ * HTTP error carrying the upstream status and parsed API message. The
7818
+ * GoogleAdapter classifies errors by reading `.status` / `.code` and
7819
+ * `.message`, so this shape keeps `classifyError` working unchanged after
7820
+ * dropping the SDK.
7821
+ */
7822
+ class GoogleHttpError extends Error {
7823
+ constructor(status, message) {
7824
+ super(message);
7825
+ this.name = "GoogleHttpError";
7826
+ this.status = status;
7827
+ this.code = status;
7307
7828
  }
7308
- catch {
7309
- throw new ConfigurationError("@google/genai is required but not installed. Run: npm install @google/genai");
7829
+ }
7830
+ //
7831
+ //
7832
+ // Helpers
7833
+ //
7834
+ /**
7835
+ * Translate the SDK-style `{ model, contents, config }` request into the REST
7836
+ * `generateContent` body. `systemInstruction` (a string) becomes a Content;
7837
+ * top-level config keys pass through; all remaining config keys (temperature,
7838
+ * responseMimeType, responseSchema, responseJsonSchema, ...) move under
7839
+ * `generationConfig`.
7840
+ */
7841
+ function toRestBody(params) {
7842
+ const body = {
7843
+ contents: params.contents,
7844
+ };
7845
+ const config = params.config;
7846
+ if (!config)
7847
+ return body;
7848
+ const generationConfig = {};
7849
+ for (const [key, value] of Object.entries(config)) {
7850
+ if (value === undefined)
7851
+ continue;
7852
+ if (key === "systemInstruction") {
7853
+ body.systemInstruction =
7854
+ typeof value === "string"
7855
+ ? { parts: [{ text: value }] }
7856
+ : value;
7857
+ }
7858
+ else if (TOP_LEVEL_CONFIG_KEYS.has(key)) {
7859
+ body[key] = value;
7860
+ }
7861
+ else {
7862
+ generationConfig[key] = value;
7863
+ }
7864
+ }
7865
+ if (Object.keys(generationConfig).length > 0) {
7866
+ body.generationConfig = generationConfig;
7867
+ }
7868
+ return body;
7869
+ }
7870
+ /**
7871
+ * Concatenate the non-thought text parts of the first candidate, matching the
7872
+ * SDK's `response.text` convenience getter (consumed by `GoogleProvider.send`).
7873
+ */
7874
+ function responseText(response) {
7875
+ const parts = response.candidates?.[0]?.content?.parts;
7876
+ if (!parts)
7877
+ return undefined;
7878
+ const text = parts
7879
+ .filter((part) => part.text && !part.thought)
7880
+ .map((part) => part.text)
7881
+ .join("");
7882
+ return text.length > 0 ? text : undefined;
7883
+ }
7884
+ //
7885
+ //
7886
+ // Main
7887
+ //
7888
+ /**
7889
+ * Minimal `fetch`-based client for Google's Generative Language (Gemini) REST
7890
+ * API. Replaces `@google/genai` — the adapter only needs `generateContent`
7891
+ * (streaming and non-streaming), an api-key header, and HTTP error surfacing.
7892
+ * Exposes a `models` facade mirroring the SDK so the adapter call sites are
7893
+ * unchanged.
7894
+ */
7895
+ class GoogleClient {
7896
+ constructor({ apiKey, baseURL = GOOGLE_BASE_URL }) {
7897
+ this.apiKey = apiKey;
7898
+ this.baseURL = baseURL;
7899
+ this.models = {
7900
+ generateContent: (params, options) => this.generateContent(params, options),
7901
+ generateContentStream: (params, options) => this.generateContentStream(params, options),
7902
+ };
7903
+ }
7904
+ headers() {
7905
+ return {
7906
+ "Content-Type": "application/json",
7907
+ "x-goog-api-key": this.apiKey,
7908
+ };
7909
+ }
7910
+ async toError(response) {
7911
+ let message = `Google request failed with status ${response.status}`;
7912
+ try {
7913
+ const body = (await response.json());
7914
+ if (body?.error?.message)
7915
+ message = body.error.message;
7916
+ }
7917
+ catch {
7918
+ // Non-JSON error body; keep the status-based message.
7919
+ }
7920
+ return new GoogleHttpError(response.status, message);
7921
+ }
7922
+ async generateContent(params, { signal } = {}) {
7923
+ const url = `${this.baseURL}/models/${params.model}:generateContent`;
7924
+ const response = await fetch(url, {
7925
+ method: "POST",
7926
+ headers: this.headers(),
7927
+ body: JSON.stringify(toRestBody(params)),
7928
+ signal,
7929
+ });
7930
+ if (!response.ok)
7931
+ throw await this.toError(response);
7932
+ const json = (await response.json());
7933
+ json.text = responseText(json);
7934
+ return json;
7935
+ }
7936
+ async generateContentStream(params, { signal } = {}) {
7937
+ const url = `${this.baseURL}/models/${params.model}:streamGenerateContent?alt=sse`;
7938
+ const response = await fetch(url, {
7939
+ method: "POST",
7940
+ headers: { ...this.headers(), Accept: "text/event-stream" },
7941
+ body: JSON.stringify(toRestBody(params)),
7942
+ signal,
7943
+ });
7944
+ if (!response.ok)
7945
+ throw await this.toError(response);
7946
+ if (!response.body)
7947
+ return (async function* () { })();
7948
+ return parseSseStream(response.body);
7310
7949
  }
7311
7950
  }
7951
+
7312
7952
  // Logger
7313
- const getLogger$3 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
7953
+ const getLogger$3 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7314
7954
  // Client initialization
7315
7955
  async function initializeClient$3({ apiKey, } = {}) {
7316
7956
  const logger = getLogger$3();
@@ -7318,8 +7958,7 @@ async function initializeClient$3({ apiKey, } = {}) {
7318
7958
  if (!resolvedApiKey) {
7319
7959
  throw new ConfigurationError("The application could not resolve the requested keys");
7320
7960
  }
7321
- const sdk = await loadSdk$1();
7322
- const client = new sdk.GoogleGenAI({ apiKey: resolvedApiKey });
7961
+ const client = new GoogleClient({ apiKey: resolvedApiKey });
7323
7962
  logger.trace("Initialized Gemini client");
7324
7963
  return client;
7325
7964
  }
@@ -7460,7 +8099,7 @@ class GoogleProvider {
7460
8099
  }
7461
8100
 
7462
8101
  // Logger
7463
- const getLogger$2 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
8102
+ const getLogger$2 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7464
8103
  // Client initialization
7465
8104
  async function initializeClient$2({ apiKey, } = {}) {
7466
8105
  const logger = getLogger$2();
@@ -7468,7 +8107,7 @@ async function initializeClient$2({ apiKey, } = {}) {
7468
8107
  if (!resolvedApiKey) {
7469
8108
  throw new ConfigurationError("The application could not resolve the requested keys");
7470
8109
  }
7471
- const client = new OpenAI({ apiKey: resolvedApiKey });
8110
+ const client = new OpenAIClient({ apiKey: resolvedApiKey });
7472
8111
  logger.trace("Initialized OpenAI client");
7473
8112
  return client;
7474
8113
  }
@@ -7527,23 +8166,23 @@ async function createStructuredCompletion(client, { messages, responseSchema, mo
7527
8166
  checks.shift();
7528
8167
  }
7529
8168
  responseFormat.json_schema.schema = jsonSchema;
7530
- const completion = await client.chat.completions.parse({
8169
+ const completion = (await client.chat.completions.parse({
7531
8170
  messages,
7532
8171
  model,
7533
8172
  response_format: responseFormat,
7534
- });
8173
+ }));
7535
8174
  logger.var({ assistantReply: completion.choices[0].message.parsed });
7536
8175
  return completion.choices[0].message.parsed;
7537
8176
  }
7538
8177
  async function createTextCompletion(client, { messages, model, }) {
7539
8178
  const logger = getLogger$2();
7540
8179
  logger.trace("Using text output (unstructured)");
7541
- const completion = await client.chat.completions.create({
8180
+ const completion = (await client.chat.completions.create({
7542
8181
  messages,
7543
8182
  model,
7544
- });
7545
- logger.trace(`Assistant reply: ${completion.choices[0]?.message?.content?.length} characters`);
7546
- return completion.choices[0]?.message?.content || "";
8183
+ }));
8184
+ logger.trace(`Assistant reply: ${completion.choices?.[0]?.message?.content?.length} characters`);
8185
+ return completion.choices?.[0]?.message?.content || "";
7547
8186
  }
7548
8187
 
7549
8188
  class OpenAiProvider {
@@ -7630,21 +8269,141 @@ class OpenAiProvider {
7630
8269
  }
7631
8270
  }
7632
8271
 
7633
- // SDK loader with caching
7634
- let cachedSdk = null;
7635
- async function loadSdk() {
7636
- if (cachedSdk)
7637
- return cachedSdk;
7638
- try {
7639
- cachedSdk = await import('@openrouter/sdk');
7640
- return cachedSdk;
8272
+ //
8273
+ //
8274
+ // Constants
8275
+ //
8276
+ const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
8277
+ /**
8278
+ * HTTP error carrying the upstream status and parsed API message. The
8279
+ * OpenRouterAdapter classifies errors by reading `.status` / `.statusCode` and
8280
+ * `.message` / `.error.message`, so this shape keeps `classifyError`,
8281
+ * `isTemperatureDeprecationError`, and `isStructuredOutputUnsupportedError`
8282
+ * working unchanged after dropping the SDK.
8283
+ */
8284
+ class OpenRouterHttpError extends Error {
8285
+ constructor(status, message, error) {
8286
+ super(message);
8287
+ this.name = "OpenRouterHttpError";
8288
+ this.status = status;
8289
+ this.statusCode = status;
8290
+ this.error = error;
7641
8291
  }
7642
- catch {
7643
- throw new ConfigurationError("@openrouter/sdk is required but not installed. Run: npm install @openrouter/sdk");
8292
+ }
8293
+ //
8294
+ //
8295
+ // Helpers
8296
+ //
8297
+ /**
8298
+ * Normalize the snake_case wire response into the camelCase shape the adapter
8299
+ * readers expect (the SDK previously returned camelCase). Only protocol fields
8300
+ * are touched — user content (schema property names, tool argument JSON) is
8301
+ * left untouched.
8302
+ */
8303
+ function normalizeResponse(json) {
8304
+ const choices = json.choices;
8305
+ if (Array.isArray(choices)) {
8306
+ for (const choice of choices) {
8307
+ if (choice.finish_reason !== undefined &&
8308
+ choice.finishReason === undefined) {
8309
+ choice.finishReason = choice.finish_reason;
8310
+ }
8311
+ const message = choice.message;
8312
+ if (message?.tool_calls !== undefined &&
8313
+ message.toolCalls === undefined) {
8314
+ message.toolCalls = message.tool_calls;
8315
+ }
8316
+ }
8317
+ }
8318
+ const usage = json.usage;
8319
+ if (usage) {
8320
+ if (usage.promptTokens === undefined)
8321
+ usage.promptTokens = usage.prompt_tokens;
8322
+ if (usage.completionTokens === undefined) {
8323
+ usage.completionTokens = usage.completion_tokens;
8324
+ }
8325
+ if (usage.totalTokens === undefined)
8326
+ usage.totalTokens = usage.total_tokens;
8327
+ const details = usage.completion_tokens_details;
8328
+ if (details?.reasoning_tokens !== undefined &&
8329
+ !usage.completionTokensDetails) {
8330
+ usage.completionTokensDetails = {
8331
+ reasoningTokens: details.reasoning_tokens,
8332
+ };
8333
+ }
8334
+ }
8335
+ return json;
8336
+ }
8337
+ //
8338
+ //
8339
+ // Main
8340
+ //
8341
+ /**
8342
+ * Minimal `fetch`-based client for OpenRouter's OpenAI-compatible Chat
8343
+ * Completions endpoint. Replaces `@openrouter/sdk` — the adapter only needs a
8344
+ * single POST (streaming and non-streaming), header auth, and HTTP error
8345
+ * surfacing.
8346
+ */
8347
+ class OpenRouterClient {
8348
+ constructor({ apiKey, baseURL = OPENROUTER_BASE_URL, }) {
8349
+ this.apiKey = apiKey;
8350
+ this.baseURL = baseURL;
8351
+ }
8352
+ headers() {
8353
+ return {
8354
+ Authorization: `Bearer ${this.apiKey}`,
8355
+ "Content-Type": "application/json",
8356
+ };
8357
+ }
8358
+ async toError(response) {
8359
+ let message = `OpenRouter request failed with status ${response.status}`;
8360
+ let error;
8361
+ try {
8362
+ const body = (await response.json());
8363
+ if (body?.error?.message) {
8364
+ message = body.error.message;
8365
+ error = body.error;
8366
+ }
8367
+ }
8368
+ catch {
8369
+ // Non-JSON error body; keep the status-based message.
8370
+ }
8371
+ return new OpenRouterHttpError(response.status, message, error);
8372
+ }
8373
+ async chatCompletion(body, { signal } = {}) {
8374
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
8375
+ method: "POST",
8376
+ headers: this.headers(),
8377
+ body: JSON.stringify(body),
8378
+ signal,
8379
+ });
8380
+ if (!response.ok)
8381
+ throw await this.toError(response);
8382
+ const json = (await response.json());
8383
+ return normalizeResponse(json);
8384
+ }
8385
+ async *streamChatCompletion(body, { signal } = {}) {
8386
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
8387
+ method: "POST",
8388
+ headers: { ...this.headers(), Accept: "text/event-stream" },
8389
+ // OpenAI-style streams only include usage when explicitly requested.
8390
+ body: JSON.stringify({
8391
+ ...body,
8392
+ stream: true,
8393
+ stream_options: { include_usage: true },
8394
+ }),
8395
+ signal,
8396
+ });
8397
+ if (!response.ok)
8398
+ throw await this.toError(response);
8399
+ if (!response.body)
8400
+ return;
8401
+ yield* parseSseStream(response.body);
7644
8402
  }
7645
8403
  }
8404
+
7646
8405
  // Logger
7647
- const getLogger$1 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
8406
+ const getLogger$1 = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7648
8407
  // Client initialization
7649
8408
  async function initializeClient$1({ apiKey, } = {}) {
7650
8409
  const logger = getLogger$1();
@@ -7652,8 +8411,7 @@ async function initializeClient$1({ apiKey, } = {}) {
7652
8411
  if (!resolvedApiKey) {
7653
8412
  throw new ConfigurationError("The application could not resolve the requested keys");
7654
8413
  }
7655
- const sdk = await loadSdk();
7656
- const client = new sdk.OpenRouter({ apiKey: resolvedApiKey });
8414
+ const client = new OpenRouterClient({ apiKey: resolvedApiKey });
7657
8415
  logger.trace("Initialized OpenRouter client");
7658
8416
  return client;
7659
8417
  }
@@ -7733,12 +8491,13 @@ class OpenRouterProvider {
7733
8491
  const client = await this.getClient();
7734
8492
  const messages = prepareMessages(message, options);
7735
8493
  const modelToUse = options?.model || this.model;
7736
- // Build the request
7737
- const response = await client.chat.send({
8494
+ // OpenAI-compatible Chat Completions body; messages are already wire-shaped.
8495
+ const response = await client.chatCompletion({
7738
8496
  model: modelToUse,
7739
- messages: messages,
8497
+ messages,
7740
8498
  });
7741
- const rawContent = response.choices?.[0]?.message?.content;
8499
+ const choices = response.choices;
8500
+ const rawContent = choices?.[0]?.message?.content;
7742
8501
  // Extract text content - content could be string or array of content items
7743
8502
  const content = typeof rawContent === "string"
7744
8503
  ? rawContent
@@ -7793,7 +8552,7 @@ class OpenRouterProvider {
7793
8552
  }
7794
8553
 
7795
8554
  // Logger
7796
- const getLogger = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
8555
+ const getLogger = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
7797
8556
  // Client initialization
7798
8557
  async function initializeClient({ apiKey, } = {}) {
7799
8558
  const logger = getLogger();
@@ -7801,7 +8560,7 @@ async function initializeClient({ apiKey, } = {}) {
7801
8560
  if (!resolvedApiKey) {
7802
8561
  throw new ConfigurationError("The application could not resolve the requested keys");
7803
8562
  }
7804
- const client = new OpenAI({
8563
+ const client = new OpenAIClient({
7805
8564
  apiKey: resolvedApiKey,
7806
8565
  baseURL: PROVIDER.XAI.BASE_URL,
7807
8566
  });
@@ -7900,7 +8659,7 @@ class Llm {
7900
8659
  let finalModel = model;
7901
8660
  // Legacy: accept "gemini" but warn
7902
8661
  if (providerName === "gemini") {
7903
- log$2.warn(`Provider "gemini" is deprecated, use "${PROVIDER.GOOGLE.NAME}" instead`);
8662
+ log$1.warn(`Provider "gemini" is deprecated, use "${PROVIDER.GOOGLE.NAME}" instead`);
7904
8663
  }
7905
8664
  if (model) {
7906
8665
  const modelDetermined = determineModelProvider(model);
@@ -8014,7 +8773,7 @@ class Llm {
8014
8773
  }
8015
8774
  catch (error) {
8016
8775
  lastError = error;
8017
- log$2.warn(`Provider ${this._provider} failed`, {
8776
+ log$1.warn(`Provider ${this._provider} failed`, {
8018
8777
  error: lastError.message,
8019
8778
  fallbacksRemaining: fallbackChain.length,
8020
8779
  });
@@ -8034,7 +8793,7 @@ class Llm {
8034
8793
  }
8035
8794
  catch (error) {
8036
8795
  lastError = error;
8037
- log$2.warn(`Fallback provider ${fallbackConfig.provider} failed`, {
8796
+ log$1.warn(`Fallback provider ${fallbackConfig.provider} failed`, {
8038
8797
  error: lastError.message,
8039
8798
  fallbacksRemaining: fallbackChain.length - attempts + 1,
8040
8799
  });
@@ -8401,5 +9160,5 @@ const toolkit = new JaypieToolkit(tools);
8401
9160
  [LlmMessageRole.Developer]: "user",
8402
9161
  });
8403
9162
 
8404
- export { BedrockProvider, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmMessageRole, LlmMessageType, LlmStreamChunkType, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, toolkit, tools };
9163
+ export { BedrockProvider, GoogleProvider as GeminiProvider, GoogleProvider, JaypieToolkit, constants as LLM, Llm, LlmMessageRole, LlmMessageType, LlmStreamChunkType, OpenRouterProvider, Toolkit, XaiProvider, extractReasoning, isLlmOperateInput, isLlmOperateInputContent, isLlmOperateInputFile, isLlmOperateInputImage, jsonSchemaToNaturalSchema, naturalSchemaToJsonSchema, toolkit, tools };
8405
9164
  //# sourceMappingURL=index.js.map