@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.
- package/dist/cjs/index.cjs +906 -145
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +13 -1
- package/dist/cjs/{index.d.ts → src/index.d.ts} +1 -0
- package/dist/cjs/{operate → src/operate}/adapters/AnthropicAdapter.d.ts +1 -1
- package/dist/{esm → cjs/src}/operate/adapters/OpenRouterAdapter.d.ts +8 -1
- package/dist/cjs/src/providers/anthropic/client.d.ts +54 -0
- package/dist/cjs/src/providers/anthropic/types.d.ts +129 -0
- package/dist/{esm → cjs/src}/providers/anthropic/utils.d.ts +5 -5
- package/dist/cjs/src/providers/google/client.d.ts +44 -0
- package/dist/cjs/{providers → src/providers}/google/utils.d.ts +2 -3
- package/dist/cjs/src/providers/openai/client.d.ts +74 -0
- package/dist/cjs/src/providers/openai/responseFormat.d.ts +17 -0
- package/dist/{esm → cjs/src}/providers/openai/utils.d.ts +4 -4
- package/dist/cjs/src/providers/openrouter/client.d.ts +40 -0
- package/dist/{esm → cjs/src}/providers/openrouter/utils.d.ts +2 -3
- package/dist/cjs/{providers → src/providers}/xai/utils.d.ts +2 -2
- package/dist/cjs/{util → src/util}/index.d.ts +2 -0
- package/dist/cjs/src/util/jsonSchema.d.ts +17 -0
- package/dist/cjs/src/util/sse.d.ts +12 -0
- package/dist/esm/index.d.ts +13 -1
- package/dist/esm/index.js +917 -158
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/src/index.d.ts +17 -0
- package/dist/esm/{operate → src/operate}/adapters/AnthropicAdapter.d.ts +1 -1
- package/dist/{cjs → esm/src}/operate/adapters/OpenRouterAdapter.d.ts +8 -1
- package/dist/esm/src/providers/anthropic/client.d.ts +54 -0
- package/dist/esm/src/providers/anthropic/types.d.ts +129 -0
- package/dist/{cjs → esm/src}/providers/anthropic/utils.d.ts +5 -5
- package/dist/esm/src/providers/google/client.d.ts +44 -0
- package/dist/esm/{providers → src/providers}/google/utils.d.ts +2 -3
- package/dist/esm/src/providers/openai/client.d.ts +74 -0
- package/dist/esm/src/providers/openai/responseFormat.d.ts +17 -0
- package/dist/{cjs → esm/src}/providers/openai/utils.d.ts +4 -4
- package/dist/esm/src/providers/openrouter/client.d.ts +40 -0
- package/dist/{cjs → esm/src}/providers/openrouter/utils.d.ts +2 -3
- package/dist/esm/{providers → src/providers}/xai/utils.d.ts +2 -2
- package/dist/esm/{util → src/util}/index.d.ts +2 -0
- package/dist/esm/src/util/jsonSchema.d.ts +17 -0
- package/dist/esm/src/util/sse.d.ts +12 -0
- package/package.json +4 -19
- package/dist/cjs/providers/anthropic/types.d.ts +0 -2
- package/dist/esm/providers/anthropic/types.d.ts +0 -2
- /package/dist/cjs/{Llm.d.ts → src/Llm.d.ts} +0 -0
- /package/dist/cjs/{constants.d.ts → src/constants.d.ts} +0 -0
- /package/dist/cjs/{observability → src/observability}/llmobs.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/OperateLoop.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/StreamLoop.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/BedrockAdapter.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/GoogleAdapter.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/OpenAiAdapter.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/ProviderAdapter.interface.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/XaiAdapter.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/adapters/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/hooks/HookRunner.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/hooks/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/input/InputProcessor.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/input/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/response/ResponseBuilder.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/response/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/retry/RetryExecutor.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/retry/RetryPolicy.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/retry/createStaleRejectionGuard.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/retry/index.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/retry/isTransientNetworkError.d.ts +0 -0
- /package/dist/cjs/{operate → src/operate}/types.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/anthropic/AnthropicProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/anthropic/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/bedrock/BedrockProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/bedrock/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/bedrock/utils.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/google/GoogleProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/google/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/google/types.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/openai/OpenAiProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/openai/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/openai/types.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/openrouter/OpenRouterProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/openrouter/index.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/xai/XaiProvider.class.d.ts +0 -0
- /package/dist/cjs/{providers → src/providers}/xai/index.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/Toolkit.class.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/index.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/random.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/roll.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/time.d.ts +0 -0
- /package/dist/cjs/{tools → src/tools}/weather.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/LlmOperateInput.guards.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/LlmProvider.interface.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/LlmStreamChunk.interface.d.ts +0 -0
- /package/dist/cjs/{types → src/types}/LlmTool.interface.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/detectMimeType.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/extractPdfPages.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/index.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/loadLocalFile.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/loadS3File.d.ts +0 -0
- /package/dist/cjs/{upload → src/upload}/resolveOperateInput.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/determineModelProvider.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/extractReasoning.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/fillFormatArrays.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/formatOperateInput.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/formatOperateMessage.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/jsonSchemaToOpenApi3.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/logger.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/maxTurnsFromOptions.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/naturalZodSchema.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/random.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/repairFormatKeys.d.ts +0 -0
- /package/dist/cjs/{util → src/util}/tryParseNumber.d.ts +0 -0
- /package/dist/esm/{Llm.d.ts → src/Llm.d.ts} +0 -0
- /package/dist/esm/{constants.d.ts → src/constants.d.ts} +0 -0
- /package/dist/esm/{observability → src/observability}/llmobs.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/OperateLoop.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/StreamLoop.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/BedrockAdapter.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/GoogleAdapter.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/OpenAiAdapter.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/ProviderAdapter.interface.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/XaiAdapter.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/adapters/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/hooks/HookRunner.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/hooks/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/input/InputProcessor.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/input/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/response/ResponseBuilder.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/response/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/retry/RetryExecutor.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/retry/RetryPolicy.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/retry/createStaleRejectionGuard.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/retry/index.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/retry/isTransientNetworkError.d.ts +0 -0
- /package/dist/esm/{operate → src/operate}/types.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/anthropic/AnthropicProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/anthropic/index.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/bedrock/BedrockProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/bedrock/index.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/bedrock/utils.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/google/GoogleProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/google/index.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/google/types.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/openai/OpenAiProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/openai/index.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/openai/types.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/openrouter/OpenRouterProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/openrouter/index.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/xai/XaiProvider.class.d.ts +0 -0
- /package/dist/esm/{providers → src/providers}/xai/index.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/Toolkit.class.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/index.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/random.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/roll.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/time.d.ts +0 -0
- /package/dist/esm/{tools → src/tools}/weather.d.ts +0 -0
- /package/dist/esm/{types → src/types}/LlmOperateInput.guards.d.ts +0 -0
- /package/dist/esm/{types → src/types}/LlmProvider.interface.d.ts +0 -0
- /package/dist/esm/{types → src/types}/LlmStreamChunk.interface.d.ts +0 -0
- /package/dist/esm/{types → src/types}/LlmTool.interface.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/detectMimeType.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/extractPdfPages.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/index.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/loadLocalFile.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/loadS3File.d.ts +0 -0
- /package/dist/esm/{upload → src/upload}/resolveOperateInput.d.ts +0 -0
- /package/dist/esm/{util → src/util}/determineModelProvider.d.ts +0 -0
- /package/dist/esm/{util → src/util}/extractReasoning.d.ts +0 -0
- /package/dist/esm/{util → src/util}/fillFormatArrays.d.ts +0 -0
- /package/dist/esm/{util → src/util}/formatOperateInput.d.ts +0 -0
- /package/dist/esm/{util → src/util}/formatOperateMessage.d.ts +0 -0
- /package/dist/esm/{util → src/util}/jsonSchemaToOpenApi3.d.ts +0 -0
- /package/dist/esm/{util → src/util}/logger.d.ts +0 -0
- /package/dist/esm/{util → src/util}/maxTurnsFromOptions.d.ts +0 -0
- /package/dist/esm/{util → src/util}/naturalZodSchema.d.ts +0 -0
- /package/dist/esm/{util → src/util}/random.d.ts +0 -0
- /package/dist/esm/{util → src/util}/repairFormatKeys.d.ts +0 -0
- /package/dist/esm/{util → src/util}/tryParseNumber.d.ts +0 -0
package/dist/cjs/index.cjs
CHANGED
|
@@ -5,8 +5,6 @@ var log$1 = require('@jaypie/logger');
|
|
|
5
5
|
var v4 = require('zod/v4');
|
|
6
6
|
var kit = require('@jaypie/kit');
|
|
7
7
|
var RandomLib = require('random');
|
|
8
|
-
var openai = require('openai');
|
|
9
|
-
var zod = require('openai/helpers/zod');
|
|
10
8
|
var module$1 = require('module');
|
|
11
9
|
var url = require('url');
|
|
12
10
|
var pdfLib = require('pdf-lib');
|
|
@@ -770,6 +768,119 @@ function formatOperateInput(input, options) {
|
|
|
770
768
|
return [input];
|
|
771
769
|
}
|
|
772
770
|
|
|
771
|
+
// Keywords a JSON Schema object node can carry that Natural Schema has no
|
|
772
|
+
// representation for. Anything present here (or any other unrecognized key)
|
|
773
|
+
// is dropped silently and logged, never thrown.
|
|
774
|
+
const LOSSY_OBJECT_KEYWORDS = [
|
|
775
|
+
"$defs",
|
|
776
|
+
"$id",
|
|
777
|
+
"$ref",
|
|
778
|
+
"$schema",
|
|
779
|
+
"additionalProperties",
|
|
780
|
+
"const",
|
|
781
|
+
"default",
|
|
782
|
+
"description",
|
|
783
|
+
"format",
|
|
784
|
+
"maxLength",
|
|
785
|
+
"maximum",
|
|
786
|
+
"minLength",
|
|
787
|
+
"minimum",
|
|
788
|
+
"multipleOf",
|
|
789
|
+
"nullable",
|
|
790
|
+
"allOf",
|
|
791
|
+
"anyOf",
|
|
792
|
+
"oneOf",
|
|
793
|
+
"pattern",
|
|
794
|
+
];
|
|
795
|
+
const KNOWN_KEYWORDS = new Set([
|
|
796
|
+
"type",
|
|
797
|
+
"properties",
|
|
798
|
+
"required",
|
|
799
|
+
"items",
|
|
800
|
+
"enum",
|
|
801
|
+
...LOSSY_OBJECT_KEYWORDS,
|
|
802
|
+
]);
|
|
803
|
+
/**
|
|
804
|
+
* Duck-type check for a bare JSON Schema object node: `{ type: "object", properties: {...} }`.
|
|
805
|
+
* Does not require the OpenAI-style `{ type: "json_schema", ... }` envelope.
|
|
806
|
+
*/
|
|
807
|
+
function isJsonSchema$1(value) {
|
|
808
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
const schema = value;
|
|
812
|
+
return (schema.type === "object" &&
|
|
813
|
+
typeof schema.properties === "object" &&
|
|
814
|
+
schema.properties !== null &&
|
|
815
|
+
!Array.isArray(schema.properties));
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Convert a Natural Schema to JSON Schema. Lossless: Natural Schema is a
|
|
819
|
+
* strict subset of what JSON Schema can express.
|
|
820
|
+
*/
|
|
821
|
+
function naturalSchemaToJsonSchema(schema) {
|
|
822
|
+
return v4.z.toJSONSchema(naturalZodSchema(schema));
|
|
823
|
+
}
|
|
824
|
+
function logDroppedKeywords(schema, path) {
|
|
825
|
+
for (const key of Object.keys(schema)) {
|
|
826
|
+
if (!KNOWN_KEYWORDS.has(key)) {
|
|
827
|
+
log$1.debug(`[jsonSchemaToNaturalSchema] Dropping unrecognized keyword "${key}" at "${path}"`);
|
|
828
|
+
}
|
|
829
|
+
else if (LOSSY_OBJECT_KEYWORDS.includes(key) &&
|
|
830
|
+
schema[key] !== undefined) {
|
|
831
|
+
log$1.debug(`[jsonSchemaToNaturalSchema] Dropping unsupported keyword "${key}" at "${path}"`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
function convertNode(schema, path) {
|
|
836
|
+
logDroppedKeywords(schema, path);
|
|
837
|
+
if (Array.isArray(schema.enum)) {
|
|
838
|
+
return schema.enum;
|
|
839
|
+
}
|
|
840
|
+
switch (schema.type) {
|
|
841
|
+
case "string":
|
|
842
|
+
return String;
|
|
843
|
+
case "number":
|
|
844
|
+
case "integer":
|
|
845
|
+
return Number;
|
|
846
|
+
case "boolean":
|
|
847
|
+
return Boolean;
|
|
848
|
+
case "array": {
|
|
849
|
+
const items = schema.items;
|
|
850
|
+
if (typeof items !== "object" || items === null || Array.isArray(items)) {
|
|
851
|
+
return Array;
|
|
852
|
+
}
|
|
853
|
+
return [convertNode(items, `${path}[]`)];
|
|
854
|
+
}
|
|
855
|
+
case "object": {
|
|
856
|
+
const properties = schema.properties;
|
|
857
|
+
if (!properties || Object.keys(properties).length === 0) {
|
|
858
|
+
return Object;
|
|
859
|
+
}
|
|
860
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
861
|
+
const result = {};
|
|
862
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
863
|
+
if (!required.has(key)) {
|
|
864
|
+
log$1.debug(`[jsonSchemaToNaturalSchema] Field "${path}.${key}" is optional; Natural Schema has no optional marker — treating as required`);
|
|
865
|
+
}
|
|
866
|
+
result[key] = convertNode(value, `${path}.${key}`);
|
|
867
|
+
}
|
|
868
|
+
return result;
|
|
869
|
+
}
|
|
870
|
+
default:
|
|
871
|
+
log$1.debug(`[jsonSchemaToNaturalSchema] Unsupported type "${String(schema.type)}" at "${path}"; defaulting to Object`);
|
|
872
|
+
return Object;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Convert a JSON Schema object node to Natural Schema. Lossy: constraints,
|
|
877
|
+
* descriptions, defaults, unions, and optionality have no Natural Schema
|
|
878
|
+
* representation. Dropped keywords are logged at debug level, never thrown.
|
|
879
|
+
*/
|
|
880
|
+
function jsonSchemaToNaturalSchema(schema) {
|
|
881
|
+
return convertNode(schema, "$");
|
|
882
|
+
}
|
|
883
|
+
|
|
773
884
|
/**
|
|
774
885
|
* Converts a JSON Schema (Draft 2020-12) object to the OpenAPI 3.0 schema subset
|
|
775
886
|
* that Gemini's `responseSchema` accepts. This constrains generation (not just validation)
|
|
@@ -1088,6 +1199,45 @@ function repairFormatKeys({ content, format, }) {
|
|
|
1088
1199
|
return repairFromSchema(schema, structuredClone(content));
|
|
1089
1200
|
}
|
|
1090
1201
|
|
|
1202
|
+
const DEFAULT_DONE_SENTINEL = "[DONE]";
|
|
1203
|
+
/**
|
|
1204
|
+
* Parse a Server-Sent Events stream into decoded JSON chunks. Buffers across
|
|
1205
|
+
* network reads, dispatches on newline-delimited `data:` lines, and stops at
|
|
1206
|
+
* the done sentinel (OpenAI-style `[DONE]`; Gemini omits it and simply ends
|
|
1207
|
+
* the stream). Comment lines (`: ...`, used for keep-alive) are ignored.
|
|
1208
|
+
*
|
|
1209
|
+
* Shared by the provider HTTP clients that replaced their vendor SDKs.
|
|
1210
|
+
*/
|
|
1211
|
+
async function* parseSseStream(body, { doneSentinel = DEFAULT_DONE_SENTINEL } = {}) {
|
|
1212
|
+
const reader = body.getReader();
|
|
1213
|
+
const decoder = new TextDecoder();
|
|
1214
|
+
let buffer = "";
|
|
1215
|
+
try {
|
|
1216
|
+
for (;;) {
|
|
1217
|
+
const { done, value } = await reader.read();
|
|
1218
|
+
if (done)
|
|
1219
|
+
break;
|
|
1220
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1221
|
+
let newlineIndex;
|
|
1222
|
+
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
1223
|
+
const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
|
|
1224
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
1225
|
+
if (line === "" || line.startsWith(":"))
|
|
1226
|
+
continue;
|
|
1227
|
+
if (!line.startsWith("data:"))
|
|
1228
|
+
continue;
|
|
1229
|
+
const data = line.slice("data:".length).trim();
|
|
1230
|
+
if (data === doneSentinel)
|
|
1231
|
+
return;
|
|
1232
|
+
yield JSON.parse(data);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
finally {
|
|
1237
|
+
reader.releaseLock();
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1091
1241
|
/**
|
|
1092
1242
|
* Helper function to safely call a function that might throw
|
|
1093
1243
|
* @param fn Function to call safely
|
|
@@ -1615,7 +1765,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1615
1765
|
? [...request.tools]
|
|
1616
1766
|
: [];
|
|
1617
1767
|
if (useFallbackStructuredOutput && request.format) {
|
|
1618
|
-
log$1.warn(`[AnthropicAdapter] Using legacy structured_output tool fallback for model ${anthropicRequest.model}; native output_config previously rejected for this model.`);
|
|
1768
|
+
log$1.log.warn(`[AnthropicAdapter] Using legacy structured_output tool fallback for model ${anthropicRequest.model}; native output_config previously rejected for this model.`);
|
|
1619
1769
|
allTools.push({
|
|
1620
1770
|
name: STRUCTURED_OUTPUT_TOOL_NAME$3,
|
|
1621
1771
|
description: "Output a structured JSON object, " +
|
|
@@ -1679,11 +1829,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1679
1829
|
}
|
|
1680
1830
|
formatOutputSchema(schema) {
|
|
1681
1831
|
let jsonSchema;
|
|
1682
|
-
// Check if schema is already a JsonObject
|
|
1683
|
-
|
|
1832
|
+
// Check if schema is already a JsonObject — either the OpenAI-style
|
|
1833
|
+
// `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
|
|
1834
|
+
if ((typeof schema === "object" &&
|
|
1684
1835
|
schema !== null &&
|
|
1685
1836
|
!Array.isArray(schema) &&
|
|
1686
|
-
schema.type === "json_schema")
|
|
1837
|
+
schema.type === "json_schema") ||
|
|
1838
|
+
isJsonSchema$1(schema)) {
|
|
1687
1839
|
jsonSchema = structuredClone(schema);
|
|
1688
1840
|
jsonSchema.type = "object"; // Validator does not recognize "json_schema"
|
|
1689
1841
|
}
|
|
@@ -1730,7 +1882,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
1730
1882
|
if (wantsStructuredOutput && isStructuredOutputUnsupportedError$1(error)) {
|
|
1731
1883
|
const model = anthropicRequest.model;
|
|
1732
1884
|
this.rememberModelRejectsStructuredOutput(model);
|
|
1733
|
-
log$1.warn(`[AnthropicAdapter] Model ${model} rejected native output_config; falling back to legacy structured_output tool emulation.`);
|
|
1885
|
+
log$1.log.warn(`[AnthropicAdapter] Model ${model} rejected native output_config; falling back to legacy structured_output tool emulation.`);
|
|
1734
1886
|
const fallbackRequest = this.toFallbackStructuredOutputRequest(anthropicRequest);
|
|
1735
1887
|
return (await anthropic.messages.create(fallbackRequest, signal ? { signal } : undefined));
|
|
1736
1888
|
}
|
|
@@ -2345,6 +2497,10 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2345
2497
|
}));
|
|
2346
2498
|
}
|
|
2347
2499
|
formatOutputSchema(schema) {
|
|
2500
|
+
// Bare JSON Schema node: `{ type: "object", properties: {...} }`
|
|
2501
|
+
if (isJsonSchema$1(schema)) {
|
|
2502
|
+
return structuredClone(schema);
|
|
2503
|
+
}
|
|
2348
2504
|
const zodSchema = schema instanceof v4.z.ZodType
|
|
2349
2505
|
? schema
|
|
2350
2506
|
: naturalZodSchema(schema);
|
|
@@ -2793,7 +2949,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
2793
2949
|
? [...request.tools]
|
|
2794
2950
|
: [];
|
|
2795
2951
|
if (request.format && hasUserTools && !useNativeCombo) {
|
|
2796
|
-
log$1.warn(`[GoogleAdapter] Using legacy structured_output tool fallback for model ${geminiRequest.model}; native responseJsonSchema + tools combo is only available on Gemini 3.`);
|
|
2952
|
+
log$1.log.warn(`[GoogleAdapter] Using legacy structured_output tool fallback for model ${geminiRequest.model}; native responseJsonSchema + tools combo is only available on Gemini 3.`);
|
|
2797
2953
|
allTools.push({
|
|
2798
2954
|
name: STRUCTURED_OUTPUT_TOOL_NAME$1,
|
|
2799
2955
|
description: "Output a structured JSON object, " +
|
|
@@ -2881,11 +3037,13 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
2881
3037
|
}
|
|
2882
3038
|
formatOutputSchema(schema) {
|
|
2883
3039
|
let jsonSchema;
|
|
2884
|
-
// Check if schema is already a JsonObject
|
|
2885
|
-
|
|
3040
|
+
// Check if schema is already a JsonObject — either the OpenAI-style
|
|
3041
|
+
// `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
|
|
3042
|
+
if ((typeof schema === "object" &&
|
|
2886
3043
|
schema !== null &&
|
|
2887
3044
|
!Array.isArray(schema) &&
|
|
2888
|
-
schema.type === "json_schema")
|
|
3045
|
+
schema.type === "json_schema") ||
|
|
3046
|
+
isJsonSchema$1(schema)) {
|
|
2889
3047
|
jsonSchema = structuredClone(schema);
|
|
2890
3048
|
jsonSchema.type = "object";
|
|
2891
3049
|
}
|
|
@@ -2924,7 +3082,7 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
2924
3082
|
if (wantsNativeCombo && isStructuredOutputComboUnsupportedError(error)) {
|
|
2925
3083
|
const model = geminiRequest.model;
|
|
2926
3084
|
this.rememberModelRejectsStructuredOutputCombo(model);
|
|
2927
|
-
log$1.warn(`[GoogleAdapter] Model ${model} rejected native responseJsonSchema + tools combo; falling back to legacy structured_output tool emulation.`);
|
|
3085
|
+
log$1.log.warn(`[GoogleAdapter] Model ${model} rejected native responseJsonSchema + tools combo; falling back to legacy structured_output tool emulation.`);
|
|
2928
3086
|
const fallbackRequest = this.toFallbackStructuredOutputRequest(geminiRequest);
|
|
2929
3087
|
const response = await genAI.models.generateContent({
|
|
2930
3088
|
model: fallbackRequest.model,
|
|
@@ -3508,6 +3666,206 @@ class GoogleAdapter extends BaseProviderAdapter {
|
|
|
3508
3666
|
// Export singleton instance
|
|
3509
3667
|
const googleAdapter = new GoogleAdapter();
|
|
3510
3668
|
|
|
3669
|
+
//
|
|
3670
|
+
//
|
|
3671
|
+
// Constants
|
|
3672
|
+
//
|
|
3673
|
+
const OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
3674
|
+
//
|
|
3675
|
+
//
|
|
3676
|
+
// Errors
|
|
3677
|
+
//
|
|
3678
|
+
// The adapter's `classifyError` uses `instanceof` against these classes, so the
|
|
3679
|
+
// client throws them directly. Status → class mapping mirrors the SDK; the
|
|
3680
|
+
// non-HTTP classes (connection/abort) are thrown when `fetch` itself rejects.
|
|
3681
|
+
//
|
|
3682
|
+
class OpenAiApiError extends Error {
|
|
3683
|
+
constructor(status, message, error) {
|
|
3684
|
+
super(message);
|
|
3685
|
+
this.name = this.constructor.name;
|
|
3686
|
+
this.status = status;
|
|
3687
|
+
this.error = error;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
let BadRequestError$1 = class BadRequestError extends OpenAiApiError {
|
|
3691
|
+
};
|
|
3692
|
+
let AuthenticationError$1 = class AuthenticationError extends OpenAiApiError {
|
|
3693
|
+
};
|
|
3694
|
+
let PermissionDeniedError$1 = class PermissionDeniedError extends OpenAiApiError {
|
|
3695
|
+
};
|
|
3696
|
+
let NotFoundError$1 = class NotFoundError extends OpenAiApiError {
|
|
3697
|
+
};
|
|
3698
|
+
class ConflictError extends OpenAiApiError {
|
|
3699
|
+
}
|
|
3700
|
+
class UnprocessableEntityError extends OpenAiApiError {
|
|
3701
|
+
}
|
|
3702
|
+
let RateLimitError$1 = class RateLimitError extends OpenAiApiError {
|
|
3703
|
+
};
|
|
3704
|
+
let InternalServerError$1 = class InternalServerError extends OpenAiApiError {
|
|
3705
|
+
};
|
|
3706
|
+
class APIConnectionError extends OpenAiApiError {
|
|
3707
|
+
}
|
|
3708
|
+
class APIConnectionTimeoutError extends APIConnectionError {
|
|
3709
|
+
}
|
|
3710
|
+
class APIUserAbortError extends OpenAiApiError {
|
|
3711
|
+
}
|
|
3712
|
+
function errorForStatus$1(status, message, error) {
|
|
3713
|
+
if (status === 400)
|
|
3714
|
+
return new BadRequestError$1(status, message, error);
|
|
3715
|
+
if (status === 401)
|
|
3716
|
+
return new AuthenticationError$1(status, message, error);
|
|
3717
|
+
if (status === 403)
|
|
3718
|
+
return new PermissionDeniedError$1(status, message, error);
|
|
3719
|
+
if (status === 404)
|
|
3720
|
+
return new NotFoundError$1(status, message, error);
|
|
3721
|
+
if (status === 409)
|
|
3722
|
+
return new ConflictError(status, message, error);
|
|
3723
|
+
if (status === 422) {
|
|
3724
|
+
return new UnprocessableEntityError(status, message, error);
|
|
3725
|
+
}
|
|
3726
|
+
if (status === 429)
|
|
3727
|
+
return new RateLimitError$1(status, message, error);
|
|
3728
|
+
if (status >= 500)
|
|
3729
|
+
return new InternalServerError$1(status, message, error);
|
|
3730
|
+
return new OpenAiApiError(status, message, error);
|
|
3731
|
+
}
|
|
3732
|
+
//
|
|
3733
|
+
//
|
|
3734
|
+
// Main
|
|
3735
|
+
//
|
|
3736
|
+
/**
|
|
3737
|
+
* Minimal `fetch`-based client for the OpenAI API. Replaces the `openai` SDK —
|
|
3738
|
+
* the adapters and provider utilities only need the Responses API
|
|
3739
|
+
* (`/responses`, streaming and non-streaming) for `operate`/`stream` and the
|
|
3740
|
+
* Chat Completions API (`/chat/completions`, plus a `parse` helper for
|
|
3741
|
+
* structured output) for `send`. Mirrors the SDK's `responses.create` and
|
|
3742
|
+
* `chat.completions.create` / `chat.completions.parse` surface so call sites are
|
|
3743
|
+
* unchanged. Also serves xAI via a custom `baseURL`.
|
|
3744
|
+
*/
|
|
3745
|
+
class OpenAIClient {
|
|
3746
|
+
constructor({ apiKey, baseURL = OPENAI_BASE_URL }) {
|
|
3747
|
+
this.apiKey = apiKey;
|
|
3748
|
+
this.baseURL = baseURL;
|
|
3749
|
+
this.responses = {
|
|
3750
|
+
create: ((params, options) => params.stream
|
|
3751
|
+
? this.createResponseStream(params, options)
|
|
3752
|
+
: this.createResponse(params, options)),
|
|
3753
|
+
};
|
|
3754
|
+
this.chat = {
|
|
3755
|
+
completions: {
|
|
3756
|
+
create: (params, options) => this.chatCompletion(params, options, { parse: false }),
|
|
3757
|
+
parse: (params, options) => this.chatCompletion(params, options, { parse: true }),
|
|
3758
|
+
},
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
headers(extra) {
|
|
3762
|
+
return {
|
|
3763
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
3764
|
+
"Content-Type": "application/json",
|
|
3765
|
+
...extra,
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
async post(path, body, { signal } = {}, { stream = false } = {}) {
|
|
3769
|
+
let response;
|
|
3770
|
+
try {
|
|
3771
|
+
response = await fetch(`${this.baseURL}${path}`, {
|
|
3772
|
+
method: "POST",
|
|
3773
|
+
headers: this.headers(stream ? { Accept: "text/event-stream" } : undefined),
|
|
3774
|
+
body: JSON.stringify(body),
|
|
3775
|
+
signal,
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3778
|
+
catch (cause) {
|
|
3779
|
+
if (signal?.aborted) {
|
|
3780
|
+
throw new APIUserAbortError(0, "Request was aborted");
|
|
3781
|
+
}
|
|
3782
|
+
const message = cause instanceof Error ? cause.message : "Connection error";
|
|
3783
|
+
const error = new APIConnectionError(0, message);
|
|
3784
|
+
error.cause = cause;
|
|
3785
|
+
throw error;
|
|
3786
|
+
}
|
|
3787
|
+
if (!response.ok)
|
|
3788
|
+
throw await this.toError(response);
|
|
3789
|
+
return response;
|
|
3790
|
+
}
|
|
3791
|
+
async toError(response) {
|
|
3792
|
+
let message = `OpenAI request failed with status ${response.status}`;
|
|
3793
|
+
let error;
|
|
3794
|
+
try {
|
|
3795
|
+
const body = (await response.json());
|
|
3796
|
+
if (body?.error?.message) {
|
|
3797
|
+
message = body.error.message;
|
|
3798
|
+
error = body.error;
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
catch {
|
|
3802
|
+
// Non-JSON error body; keep the status-based message.
|
|
3803
|
+
}
|
|
3804
|
+
return errorForStatus$1(response.status, message, error);
|
|
3805
|
+
}
|
|
3806
|
+
async createResponse(params, options) {
|
|
3807
|
+
const response = await this.post("/responses", params, options);
|
|
3808
|
+
return (await response.json());
|
|
3809
|
+
}
|
|
3810
|
+
async createResponseStream(params, options) {
|
|
3811
|
+
const response = await this.post("/responses", params, options, {
|
|
3812
|
+
stream: true,
|
|
3813
|
+
});
|
|
3814
|
+
if (!response.body)
|
|
3815
|
+
return (async function* () { })();
|
|
3816
|
+
// The Responses API streams typed events and does not emit a `[DONE]`
|
|
3817
|
+
// sentinel; the empty default sentinel never matches, so the stream ends
|
|
3818
|
+
// when the connection closes.
|
|
3819
|
+
return parseSseStream(response.body);
|
|
3820
|
+
}
|
|
3821
|
+
async chatCompletion(params, options, { parse }) {
|
|
3822
|
+
const response = await this.post("/chat/completions", params, options);
|
|
3823
|
+
const json = (await response.json());
|
|
3824
|
+
// `chat.completions.parse` surfaces parsed JSON on each message; replicate
|
|
3825
|
+
// the SDK by JSON-parsing the assistant content into `message.parsed`.
|
|
3826
|
+
if (parse) {
|
|
3827
|
+
const choices = json.choices;
|
|
3828
|
+
if (Array.isArray(choices)) {
|
|
3829
|
+
for (const choice of choices) {
|
|
3830
|
+
const message = choice.message;
|
|
3831
|
+
if (message &&
|
|
3832
|
+
typeof message.content === "string" &&
|
|
3833
|
+
!message.refusal) {
|
|
3834
|
+
try {
|
|
3835
|
+
message.parsed = JSON.parse(message.content);
|
|
3836
|
+
}
|
|
3837
|
+
catch {
|
|
3838
|
+
message.parsed = null;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
return json;
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
|
|
3848
|
+
//
|
|
3849
|
+
//
|
|
3850
|
+
// Main
|
|
3851
|
+
//
|
|
3852
|
+
/**
|
|
3853
|
+
* Local replacement for `openai/helpers/zod`'s `zodResponseFormat`. Builds the
|
|
3854
|
+
* `response_format` payload OpenAI's Chat Completions / Responses APIs expect
|
|
3855
|
+
* from a Zod schema. The SDK always emits `strict: true`; callers re-derive or
|
|
3856
|
+
* sanitize `schema` as needed (e.g. forcing `additionalProperties: false`).
|
|
3857
|
+
*/
|
|
3858
|
+
function zodResponseFormat(schema, name) {
|
|
3859
|
+
return {
|
|
3860
|
+
type: "json_schema",
|
|
3861
|
+
json_schema: {
|
|
3862
|
+
name,
|
|
3863
|
+
strict: true,
|
|
3864
|
+
schema: v4.z.toJSONSchema(schema),
|
|
3865
|
+
},
|
|
3866
|
+
};
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3511
3869
|
// Patterns for OpenAI reasoning models that support extended thinking
|
|
3512
3870
|
const REASONING_MODEL_PATTERNS = [
|
|
3513
3871
|
/^gpt-[5-9]/, // GPT-5 and above (gpt-5, gpt-5.1, gpt-5.2, gpt-6, etc.)
|
|
@@ -3524,19 +3882,19 @@ function isReasoningModel(model) {
|
|
|
3524
3882
|
// Constants
|
|
3525
3883
|
//
|
|
3526
3884
|
const RETRYABLE_ERROR_TYPES = [
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3885
|
+
APIConnectionError,
|
|
3886
|
+
APIConnectionTimeoutError,
|
|
3887
|
+
InternalServerError$1,
|
|
3530
3888
|
];
|
|
3531
3889
|
const NOT_RETRYABLE_ERROR_TYPES = [
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3890
|
+
APIUserAbortError,
|
|
3891
|
+
AuthenticationError$1,
|
|
3892
|
+
BadRequestError$1,
|
|
3893
|
+
ConflictError,
|
|
3894
|
+
NotFoundError$1,
|
|
3895
|
+
PermissionDeniedError$1,
|
|
3896
|
+
RateLimitError$1,
|
|
3897
|
+
UnprocessableEntityError,
|
|
3540
3898
|
];
|
|
3541
3899
|
// Models known not to accept `temperature`.
|
|
3542
3900
|
// Patterns (not exact names) so dated variants and future releases are covered
|
|
@@ -3549,7 +3907,7 @@ const MODELS_WITHOUT_TEMPERATURE$1 = [
|
|
|
3549
3907
|
function isTemperatureDeprecationError$1(error) {
|
|
3550
3908
|
if (!error || typeof error !== "object")
|
|
3551
3909
|
return false;
|
|
3552
|
-
if (!(error instanceof
|
|
3910
|
+
if (!(error instanceof BadRequestError$1) &&
|
|
3553
3911
|
error.status !== 400) {
|
|
3554
3912
|
return false;
|
|
3555
3913
|
}
|
|
@@ -3652,21 +4010,35 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
3652
4010
|
schema.type === "json_schema") {
|
|
3653
4011
|
return schema;
|
|
3654
4012
|
}
|
|
3655
|
-
//
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
4013
|
+
// Bare JSON Schema node: `{ type: "object", properties: {...} }`
|
|
4014
|
+
// Skip the zod round-trip and use it directly (still needs the
|
|
4015
|
+
// additionalProperties + envelope treatment below).
|
|
4016
|
+
let jsonSchema;
|
|
4017
|
+
let name = "response";
|
|
4018
|
+
let strict = true;
|
|
4019
|
+
if (isJsonSchema$1(schema)) {
|
|
4020
|
+
jsonSchema = { ...structuredClone(schema) };
|
|
4021
|
+
delete jsonSchema.$schema;
|
|
4022
|
+
}
|
|
4023
|
+
else {
|
|
4024
|
+
// Convert NaturalSchema to Zod schema if needed
|
|
4025
|
+
const zodSchema = schema instanceof v4.z.ZodType
|
|
4026
|
+
? schema
|
|
4027
|
+
: naturalZodSchema(schema);
|
|
4028
|
+
const responseFormat = zodResponseFormat(zodSchema, "response");
|
|
4029
|
+
// Re-spread to drop zod v4's non-enumerable `~standard` Standard-Schema
|
|
4030
|
+
// interop marker, then strip `$schema`: OpenAI's strict json_schema subset
|
|
4031
|
+
// does not recognize the draft-2020-12 `$schema` keyword, and shipping it
|
|
4032
|
+
// can leave strict structured-output enforcement silently disabled (the
|
|
4033
|
+
// model then free-forms, omitting required fields and corrupting keys).
|
|
4034
|
+
// Mirrors the OpenRouter adapter's sanitization.
|
|
4035
|
+
jsonSchema = {
|
|
4036
|
+
...v4.z.toJSONSchema(zodSchema),
|
|
4037
|
+
};
|
|
4038
|
+
delete jsonSchema.$schema;
|
|
4039
|
+
name = responseFormat.json_schema.name;
|
|
4040
|
+
strict = responseFormat.json_schema.strict;
|
|
4041
|
+
}
|
|
3670
4042
|
// OpenAI requires additionalProperties to be false on all objects
|
|
3671
4043
|
const checks = [jsonSchema];
|
|
3672
4044
|
while (checks.length > 0) {
|
|
@@ -3682,10 +4054,10 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
3682
4054
|
checks.shift();
|
|
3683
4055
|
}
|
|
3684
4056
|
return {
|
|
3685
|
-
name
|
|
4057
|
+
name,
|
|
3686
4058
|
schema: jsonSchema,
|
|
3687
|
-
strict
|
|
3688
|
-
type:
|
|
4059
|
+
strict,
|
|
4060
|
+
type: "json_schema",
|
|
3689
4061
|
};
|
|
3690
4062
|
}
|
|
3691
4063
|
//
|
|
@@ -3903,7 +4275,7 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
3903
4275
|
//
|
|
3904
4276
|
classifyError(error) {
|
|
3905
4277
|
// Check for rate limit error
|
|
3906
|
-
if (error instanceof
|
|
4278
|
+
if (error instanceof RateLimitError$1) {
|
|
3907
4279
|
return {
|
|
3908
4280
|
error,
|
|
3909
4281
|
category: ErrorCategory.RateLimit,
|
|
@@ -4121,7 +4493,49 @@ function convertContentToOpenRouter(content) {
|
|
|
4121
4493
|
}
|
|
4122
4494
|
return parts;
|
|
4123
4495
|
}
|
|
4124
|
-
|
|
4496
|
+
/**
|
|
4497
|
+
* Convert internal content parts to the OpenAI-compatible wire shape. The
|
|
4498
|
+
* internal representation uses camelCase keys (`imageUrl`, `fileData`) that the
|
|
4499
|
+
* SDK accepted; the REST API wants snake_case (`image_url`, `file_data`).
|
|
4500
|
+
*/
|
|
4501
|
+
function contentToWire(content) {
|
|
4502
|
+
if (content === null ||
|
|
4503
|
+
content === undefined ||
|
|
4504
|
+
typeof content === "string") {
|
|
4505
|
+
return content;
|
|
4506
|
+
}
|
|
4507
|
+
return content.map((part) => {
|
|
4508
|
+
if (part.type === "image_url") {
|
|
4509
|
+
return { type: "image_url", image_url: part.imageUrl };
|
|
4510
|
+
}
|
|
4511
|
+
if (part.type === "file") {
|
|
4512
|
+
return {
|
|
4513
|
+
type: "file",
|
|
4514
|
+
file: { filename: part.file.filename, file_data: part.file.fileData },
|
|
4515
|
+
};
|
|
4516
|
+
}
|
|
4517
|
+
return part;
|
|
4518
|
+
});
|
|
4519
|
+
}
|
|
4520
|
+
/**
|
|
4521
|
+
* Serialize an internal message to the OpenAI-compatible wire shape, mapping
|
|
4522
|
+
* camelCase tool fields (`toolCalls`, `toolCallId`) to snake_case. Tool-call
|
|
4523
|
+
* objects are already wire-shaped (`{ id, type, function: { name, arguments } }`).
|
|
4524
|
+
*/
|
|
4525
|
+
function messageToWire(message) {
|
|
4526
|
+
const wire = { role: message.role };
|
|
4527
|
+
if (message.content !== undefined) {
|
|
4528
|
+
wire.content = contentToWire(message.content);
|
|
4529
|
+
}
|
|
4530
|
+
if (message.toolCalls) {
|
|
4531
|
+
wire.tool_calls = message.toolCalls;
|
|
4532
|
+
}
|
|
4533
|
+
if (message.toolCallId) {
|
|
4534
|
+
wire.tool_call_id = message.toolCallId;
|
|
4535
|
+
}
|
|
4536
|
+
return wire;
|
|
4537
|
+
}
|
|
4538
|
+
// OpenRouter error types based on HTTP status codes
|
|
4125
4539
|
const RETRYABLE_STATUS_CODES = [408, 500, 502, 503, 524, 529];
|
|
4126
4540
|
const RATE_LIMIT_STATUS_CODE = 429;
|
|
4127
4541
|
/**
|
|
@@ -4285,11 +4699,13 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4285
4699
|
}
|
|
4286
4700
|
formatOutputSchema(schema) {
|
|
4287
4701
|
let jsonSchema;
|
|
4288
|
-
// Check if schema is already a JsonObject
|
|
4289
|
-
|
|
4702
|
+
// Check if schema is already a JsonObject — either the OpenAI-style
|
|
4703
|
+
// `{ type: "json_schema", ... }` envelope or a bare `{ type: "object", properties }` node
|
|
4704
|
+
if ((typeof schema === "object" &&
|
|
4290
4705
|
schema !== null &&
|
|
4291
4706
|
!Array.isArray(schema) &&
|
|
4292
|
-
schema.type === "json_schema")
|
|
4707
|
+
schema.type === "json_schema") ||
|
|
4708
|
+
isJsonSchema$1(schema)) {
|
|
4293
4709
|
jsonSchema = structuredClone(schema);
|
|
4294
4710
|
jsonSchema.type = "object"; // Normalize type
|
|
4295
4711
|
}
|
|
@@ -4324,7 +4740,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4324
4740
|
const openRouterRequest = request;
|
|
4325
4741
|
const wantsStructuredOutput = Boolean(openRouterRequest.response_format);
|
|
4326
4742
|
try {
|
|
4327
|
-
const response = (await openRouter.
|
|
4743
|
+
const response = (await openRouter.chatCompletion(this.toWireBody(openRouterRequest), signal ? { signal } : undefined));
|
|
4328
4744
|
if (wantsStructuredOutput) {
|
|
4329
4745
|
response.__jaypieStructuredOutput = true;
|
|
4330
4746
|
}
|
|
@@ -4341,7 +4757,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4341
4757
|
this.rememberModelRejectsTemperature(openRouterRequest.model);
|
|
4342
4758
|
const retryRequest = { ...openRouterRequest };
|
|
4343
4759
|
delete retryRequest.temperature;
|
|
4344
|
-
const response = (await openRouter.
|
|
4760
|
+
const response = (await openRouter.chatCompletion(this.toWireBody(retryRequest), signal ? { signal } : undefined));
|
|
4345
4761
|
if (wantsStructuredOutput) {
|
|
4346
4762
|
response.__jaypieStructuredOutput = true;
|
|
4347
4763
|
}
|
|
@@ -4354,7 +4770,7 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4354
4770
|
this.rememberModelRejectsStructuredOutput(model);
|
|
4355
4771
|
log$1.log.warn(`[OpenRouterAdapter] Model ${model} rejected native response_format; falling back to legacy structured_output tool emulation.`);
|
|
4356
4772
|
const fallbackRequest = this.toFallbackStructuredOutputRequest(openRouterRequest);
|
|
4357
|
-
return (await openRouter.
|
|
4773
|
+
return (await openRouter.chatCompletion(this.toWireBody(fallbackRequest), signal ? { signal } : undefined));
|
|
4358
4774
|
}
|
|
4359
4775
|
throw error;
|
|
4360
4776
|
}
|
|
@@ -4364,31 +4780,18 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4364
4780
|
* camelCase shape, forwarding only the fields we care about (the SDK
|
|
4365
4781
|
* silently strips unknown fields).
|
|
4366
4782
|
*/
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4783
|
+
/**
|
|
4784
|
+
* Serialize the internal request into the OpenAI-compatible wire body for
|
|
4785
|
+
* OpenRouter's Chat Completions endpoint. Top-level fields (model, tools,
|
|
4786
|
+
* tool_choice, response_format, user, temperature, and any providerOptions)
|
|
4787
|
+
* are already wire-shaped (snake_case); only messages carry camelCase tool
|
|
4788
|
+
* fields that must become snake_case on the wire.
|
|
4789
|
+
*/
|
|
4790
|
+
toWireBody(openRouterRequest) {
|
|
4791
|
+
return {
|
|
4792
|
+
...openRouterRequest,
|
|
4793
|
+
messages: openRouterRequest.messages.map(messageToWire),
|
|
4374
4794
|
};
|
|
4375
|
-
if (openRouterRequest.response_format) {
|
|
4376
|
-
const format = openRouterRequest.response_format;
|
|
4377
|
-
if (format.type === "json_schema") {
|
|
4378
|
-
params.responseFormat = {
|
|
4379
|
-
type: "json_schema",
|
|
4380
|
-
jsonSchema: format.json_schema,
|
|
4381
|
-
};
|
|
4382
|
-
}
|
|
4383
|
-
else {
|
|
4384
|
-
params.responseFormat = format;
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
4387
|
-
const temperature = openRouterRequest.temperature;
|
|
4388
|
-
if (temperature !== undefined) {
|
|
4389
|
-
params.temperature = temperature;
|
|
4390
|
-
}
|
|
4391
|
-
return params;
|
|
4392
4795
|
}
|
|
4393
4796
|
/**
|
|
4394
4797
|
* Rebuild a structured-output request without `response_format`, swapping in
|
|
@@ -4420,15 +4823,9 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
4420
4823
|
async *executeStreamRequest(client, request, signal) {
|
|
4421
4824
|
const openRouter = client;
|
|
4422
4825
|
const openRouterRequest = request;
|
|
4423
|
-
//
|
|
4424
|
-
//
|
|
4425
|
-
|
|
4426
|
-
// non-stream response.
|
|
4427
|
-
const streamParams = {
|
|
4428
|
-
...this.toSdkChatParams(openRouterRequest),
|
|
4429
|
-
stream: true,
|
|
4430
|
-
};
|
|
4431
|
-
const stream = (await openRouter.chat.send(streamParams, signal ? { signal } : undefined));
|
|
4826
|
+
// streamChatCompletion adds `stream: true` + `stream_options.include_usage`
|
|
4827
|
+
// and yields decoded SSE chunks in OpenAI-compatible (snake_case) shape.
|
|
4828
|
+
const stream = openRouter.streamChatCompletion(this.toWireBody(openRouterRequest), signal ? { signal } : undefined);
|
|
4432
4829
|
// Track current tool call being built
|
|
4433
4830
|
let currentToolCall = null;
|
|
4434
4831
|
// Track usage for final chunk
|
|
@@ -4877,7 +5274,7 @@ const TRANSIENT_INGEST_MESSAGE_PATTERNS = [
|
|
|
4877
5274
|
"failed to ingest inline file bytes",
|
|
4878
5275
|
];
|
|
4879
5276
|
function isTransientIngestError(error) {
|
|
4880
|
-
if (!(error instanceof
|
|
5277
|
+
if (!(error instanceof BadRequestError$1))
|
|
4881
5278
|
return false;
|
|
4882
5279
|
const message = (error.message ?? "").toLowerCase();
|
|
4883
5280
|
return TRANSIENT_INGEST_MESSAGE_PATTERNS.some((pattern) => message.includes(pattern));
|
|
@@ -5069,7 +5466,7 @@ const requireModule = typeof __filename !== "undefined"
|
|
|
5069
5466
|
? module$1.createRequire(url.pathToFileURL(__filename).href)
|
|
5070
5467
|
: module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
5071
5468
|
let resolved = false;
|
|
5072
|
-
let cachedSdk$
|
|
5469
|
+
let cachedSdk$1 = null;
|
|
5073
5470
|
/**
|
|
5074
5471
|
* Lazily resolve the dd-trace `llmobs` SDK. Returns null (and never throws)
|
|
5075
5472
|
* when dd-trace is absent or the SDK surface is unexpected. Cached after the
|
|
@@ -5077,7 +5474,7 @@ let cachedSdk$4 = null;
|
|
|
5077
5474
|
*/
|
|
5078
5475
|
function resolveLlmObs() {
|
|
5079
5476
|
if (resolved) {
|
|
5080
|
-
return cachedSdk$
|
|
5477
|
+
return cachedSdk$1;
|
|
5081
5478
|
}
|
|
5082
5479
|
resolved = true;
|
|
5083
5480
|
try {
|
|
@@ -5087,13 +5484,13 @@ function resolveLlmObs() {
|
|
|
5087
5484
|
if (llmobs &&
|
|
5088
5485
|
typeof llmobs.trace === "function" &&
|
|
5089
5486
|
typeof llmobs.annotate === "function") {
|
|
5090
|
-
cachedSdk$
|
|
5487
|
+
cachedSdk$1 = llmobs;
|
|
5091
5488
|
}
|
|
5092
5489
|
}
|
|
5093
5490
|
catch {
|
|
5094
|
-
cachedSdk$
|
|
5491
|
+
cachedSdk$1 = null;
|
|
5095
5492
|
}
|
|
5096
|
-
return cachedSdk$
|
|
5493
|
+
return cachedSdk$1;
|
|
5097
5494
|
}
|
|
5098
5495
|
//
|
|
5099
5496
|
//
|
|
@@ -7007,19 +7404,125 @@ function createStreamLoop(config) {
|
|
|
7007
7404
|
return new StreamLoop(config);
|
|
7008
7405
|
}
|
|
7009
7406
|
|
|
7010
|
-
//
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7407
|
+
//
|
|
7408
|
+
//
|
|
7409
|
+
// Constants
|
|
7410
|
+
//
|
|
7411
|
+
const ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1";
|
|
7412
|
+
const ANTHROPIC_VERSION = "2023-06-01";
|
|
7413
|
+
//
|
|
7414
|
+
//
|
|
7415
|
+
// Errors
|
|
7416
|
+
//
|
|
7417
|
+
// The adapter's `classifyError` keys off `error.constructor.name` (the SDK's
|
|
7418
|
+
// class names), so the client throws errors whose class names match. Status →
|
|
7419
|
+
// class mapping mirrors the SDK.
|
|
7420
|
+
//
|
|
7421
|
+
class AnthropicApiError extends Error {
|
|
7422
|
+
constructor(status, message, error) {
|
|
7423
|
+
super(message);
|
|
7424
|
+
this.name = this.constructor.name;
|
|
7425
|
+
this.status = status;
|
|
7426
|
+
this.error = error;
|
|
7018
7427
|
}
|
|
7019
|
-
|
|
7020
|
-
|
|
7428
|
+
}
|
|
7429
|
+
class BadRequestError extends AnthropicApiError {
|
|
7430
|
+
}
|
|
7431
|
+
class AuthenticationError extends AnthropicApiError {
|
|
7432
|
+
}
|
|
7433
|
+
class PermissionDeniedError extends AnthropicApiError {
|
|
7434
|
+
}
|
|
7435
|
+
class NotFoundError extends AnthropicApiError {
|
|
7436
|
+
}
|
|
7437
|
+
class RateLimitError extends AnthropicApiError {
|
|
7438
|
+
}
|
|
7439
|
+
class InternalServerError extends AnthropicApiError {
|
|
7440
|
+
}
|
|
7441
|
+
function errorForStatus(status, message, error) {
|
|
7442
|
+
if (status === 400)
|
|
7443
|
+
return new BadRequestError(status, message, error);
|
|
7444
|
+
if (status === 401)
|
|
7445
|
+
return new AuthenticationError(status, message, error);
|
|
7446
|
+
if (status === 403)
|
|
7447
|
+
return new PermissionDeniedError(status, message, error);
|
|
7448
|
+
if (status === 404)
|
|
7449
|
+
return new NotFoundError(status, message, error);
|
|
7450
|
+
if (status === 429)
|
|
7451
|
+
return new RateLimitError(status, message, error);
|
|
7452
|
+
if (status >= 500)
|
|
7453
|
+
return new InternalServerError(status, message, error);
|
|
7454
|
+
return new AnthropicApiError(status, message, error);
|
|
7455
|
+
}
|
|
7456
|
+
//
|
|
7457
|
+
//
|
|
7458
|
+
// Main
|
|
7459
|
+
//
|
|
7460
|
+
/**
|
|
7461
|
+
* Minimal `fetch`-based client for Anthropic's Messages API. Replaces
|
|
7462
|
+
* `@anthropic-ai/sdk` — the adapter only needs `messages.create` (streaming and
|
|
7463
|
+
* non-streaming), header auth, and HTTP errors whose class names drive
|
|
7464
|
+
* `classifyError`. Internal request params are already Anthropic's wire shape,
|
|
7465
|
+
* so they serialize verbatim. Exposes a `messages` facade mirroring the SDK so
|
|
7466
|
+
* the adapter call sites are unchanged.
|
|
7467
|
+
*/
|
|
7468
|
+
class AnthropicClient {
|
|
7469
|
+
constructor({ apiKey, baseURL = ANTHROPIC_BASE_URL, }) {
|
|
7470
|
+
this.apiKey = apiKey;
|
|
7471
|
+
this.baseURL = baseURL;
|
|
7472
|
+
this.messages = {
|
|
7473
|
+
create: ((params, options) => params.stream
|
|
7474
|
+
? this.createStream(params, options)
|
|
7475
|
+
: this.createMessage(params, options)),
|
|
7476
|
+
};
|
|
7477
|
+
}
|
|
7478
|
+
headers() {
|
|
7479
|
+
return {
|
|
7480
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
7481
|
+
"content-type": "application/json",
|
|
7482
|
+
"x-api-key": this.apiKey,
|
|
7483
|
+
};
|
|
7484
|
+
}
|
|
7485
|
+
async toError(response) {
|
|
7486
|
+
let message = `Anthropic request failed with status ${response.status}`;
|
|
7487
|
+
let error;
|
|
7488
|
+
try {
|
|
7489
|
+
const body = (await response.json());
|
|
7490
|
+
if (body?.error?.message) {
|
|
7491
|
+
message = body.error.message;
|
|
7492
|
+
error = body.error;
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
catch {
|
|
7496
|
+
// Non-JSON error body; keep the status-based message.
|
|
7497
|
+
}
|
|
7498
|
+
return errorForStatus(response.status, message, error);
|
|
7499
|
+
}
|
|
7500
|
+
async createMessage(params, { signal } = {}) {
|
|
7501
|
+
const response = await fetch(`${this.baseURL}/messages`, {
|
|
7502
|
+
method: "POST",
|
|
7503
|
+
headers: this.headers(),
|
|
7504
|
+
body: JSON.stringify(params),
|
|
7505
|
+
signal,
|
|
7506
|
+
});
|
|
7507
|
+
if (!response.ok)
|
|
7508
|
+
throw await this.toError(response);
|
|
7509
|
+
return (await response.json());
|
|
7510
|
+
}
|
|
7511
|
+
async createStream(params, { signal } = {}) {
|
|
7512
|
+
const response = await fetch(`${this.baseURL}/messages`, {
|
|
7513
|
+
method: "POST",
|
|
7514
|
+
headers: { ...this.headers(), accept: "text/event-stream" },
|
|
7515
|
+
body: JSON.stringify(params),
|
|
7516
|
+
signal,
|
|
7517
|
+
});
|
|
7518
|
+
if (!response.ok)
|
|
7519
|
+
throw await this.toError(response);
|
|
7520
|
+
if (!response.body)
|
|
7521
|
+
return (async function* () { })();
|
|
7522
|
+
return parseSseStream(response.body);
|
|
7021
7523
|
}
|
|
7022
7524
|
}
|
|
7525
|
+
|
|
7023
7526
|
// Logger
|
|
7024
7527
|
const getLogger$5 = () => log$1.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
7025
7528
|
// Client initialization
|
|
@@ -7029,8 +7532,7 @@ async function initializeClient$5({ apiKey, } = {}) {
|
|
|
7029
7532
|
if (!resolvedApiKey) {
|
|
7030
7533
|
throw new errors.ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
|
|
7031
7534
|
}
|
|
7032
|
-
const
|
|
7033
|
-
const client = new sdk.default({ apiKey: resolvedApiKey });
|
|
7535
|
+
const client = new AnthropicClient({ apiKey: resolvedApiKey });
|
|
7034
7536
|
logger.trace("Initialized Anthropic client");
|
|
7035
7537
|
return client;
|
|
7036
7538
|
}
|
|
@@ -7130,6 +7632,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
7130
7632
|
[exports.LlmMessageRole.Assistant]: PROVIDER.ANTHROPIC.ROLE.ASSISTANT,
|
|
7131
7633
|
[exports.LlmMessageRole.Developer]: PROVIDER.ANTHROPIC.ROLE.SYSTEM,
|
|
7132
7634
|
});
|
|
7635
|
+
/* eslint-enable @typescript-eslint/no-namespace */
|
|
7133
7636
|
|
|
7134
7637
|
// Main class implementation
|
|
7135
7638
|
class AnthropicProvider {
|
|
@@ -7218,13 +7721,13 @@ class AnthropicProvider {
|
|
|
7218
7721
|
}
|
|
7219
7722
|
}
|
|
7220
7723
|
|
|
7221
|
-
let cachedSdk
|
|
7222
|
-
async function loadSdk
|
|
7223
|
-
if (cachedSdk
|
|
7224
|
-
return cachedSdk
|
|
7724
|
+
let cachedSdk = null;
|
|
7725
|
+
async function loadSdk() {
|
|
7726
|
+
if (cachedSdk)
|
|
7727
|
+
return cachedSdk;
|
|
7225
7728
|
try {
|
|
7226
|
-
cachedSdk
|
|
7227
|
-
return cachedSdk
|
|
7729
|
+
cachedSdk = await import('@aws-sdk/client-bedrock-runtime');
|
|
7730
|
+
return cachedSdk;
|
|
7228
7731
|
}
|
|
7229
7732
|
catch {
|
|
7230
7733
|
throw new errors.ConfigurationError("@aws-sdk/client-bedrock-runtime is required but not installed. Run: npm install @aws-sdk/client-bedrock-runtime");
|
|
@@ -7234,7 +7737,7 @@ const getLogger$4 = () => log$1.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
|
7234
7737
|
async function initializeClient$4({ region, } = {}) {
|
|
7235
7738
|
const logger = getLogger$4();
|
|
7236
7739
|
const resolvedRegion = region || process.env.AWS_REGION || "us-east-1";
|
|
7237
|
-
const sdk = await loadSdk
|
|
7740
|
+
const sdk = await loadSdk();
|
|
7238
7741
|
const client = new sdk.BedrockRuntimeClient({ region: resolvedRegion });
|
|
7239
7742
|
logger.trace("Initialized Bedrock client");
|
|
7240
7743
|
return client;
|
|
@@ -7299,19 +7802,156 @@ class BedrockProvider {
|
|
|
7299
7802
|
}
|
|
7300
7803
|
}
|
|
7301
7804
|
|
|
7302
|
-
//
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7805
|
+
//
|
|
7806
|
+
//
|
|
7807
|
+
// Constants
|
|
7808
|
+
//
|
|
7809
|
+
const GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
7810
|
+
// Config keys the REST API expects at the top level of the request body. Every
|
|
7811
|
+
// other key in the SDK-style `config` object belongs under `generationConfig`.
|
|
7812
|
+
const TOP_LEVEL_CONFIG_KEYS = new Set([
|
|
7813
|
+
"systemInstruction",
|
|
7814
|
+
"tools",
|
|
7815
|
+
"toolConfig",
|
|
7816
|
+
"safetySettings",
|
|
7817
|
+
"cachedContent",
|
|
7818
|
+
]);
|
|
7819
|
+
/**
|
|
7820
|
+
* HTTP error carrying the upstream status and parsed API message. The
|
|
7821
|
+
* GoogleAdapter classifies errors by reading `.status` / `.code` and
|
|
7822
|
+
* `.message`, so this shape keeps `classifyError` working unchanged after
|
|
7823
|
+
* dropping the SDK.
|
|
7824
|
+
*/
|
|
7825
|
+
class GoogleHttpError extends Error {
|
|
7826
|
+
constructor(status, message) {
|
|
7827
|
+
super(message);
|
|
7828
|
+
this.name = "GoogleHttpError";
|
|
7829
|
+
this.status = status;
|
|
7830
|
+
this.code = status;
|
|
7310
7831
|
}
|
|
7311
|
-
|
|
7312
|
-
|
|
7832
|
+
}
|
|
7833
|
+
//
|
|
7834
|
+
//
|
|
7835
|
+
// Helpers
|
|
7836
|
+
//
|
|
7837
|
+
/**
|
|
7838
|
+
* Translate the SDK-style `{ model, contents, config }` request into the REST
|
|
7839
|
+
* `generateContent` body. `systemInstruction` (a string) becomes a Content;
|
|
7840
|
+
* top-level config keys pass through; all remaining config keys (temperature,
|
|
7841
|
+
* responseMimeType, responseSchema, responseJsonSchema, ...) move under
|
|
7842
|
+
* `generationConfig`.
|
|
7843
|
+
*/
|
|
7844
|
+
function toRestBody(params) {
|
|
7845
|
+
const body = {
|
|
7846
|
+
contents: params.contents,
|
|
7847
|
+
};
|
|
7848
|
+
const config = params.config;
|
|
7849
|
+
if (!config)
|
|
7850
|
+
return body;
|
|
7851
|
+
const generationConfig = {};
|
|
7852
|
+
for (const [key, value] of Object.entries(config)) {
|
|
7853
|
+
if (value === undefined)
|
|
7854
|
+
continue;
|
|
7855
|
+
if (key === "systemInstruction") {
|
|
7856
|
+
body.systemInstruction =
|
|
7857
|
+
typeof value === "string"
|
|
7858
|
+
? { parts: [{ text: value }] }
|
|
7859
|
+
: value;
|
|
7860
|
+
}
|
|
7861
|
+
else if (TOP_LEVEL_CONFIG_KEYS.has(key)) {
|
|
7862
|
+
body[key] = value;
|
|
7863
|
+
}
|
|
7864
|
+
else {
|
|
7865
|
+
generationConfig[key] = value;
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
7869
|
+
body.generationConfig = generationConfig;
|
|
7870
|
+
}
|
|
7871
|
+
return body;
|
|
7872
|
+
}
|
|
7873
|
+
/**
|
|
7874
|
+
* Concatenate the non-thought text parts of the first candidate, matching the
|
|
7875
|
+
* SDK's `response.text` convenience getter (consumed by `GoogleProvider.send`).
|
|
7876
|
+
*/
|
|
7877
|
+
function responseText(response) {
|
|
7878
|
+
const parts = response.candidates?.[0]?.content?.parts;
|
|
7879
|
+
if (!parts)
|
|
7880
|
+
return undefined;
|
|
7881
|
+
const text = parts
|
|
7882
|
+
.filter((part) => part.text && !part.thought)
|
|
7883
|
+
.map((part) => part.text)
|
|
7884
|
+
.join("");
|
|
7885
|
+
return text.length > 0 ? text : undefined;
|
|
7886
|
+
}
|
|
7887
|
+
//
|
|
7888
|
+
//
|
|
7889
|
+
// Main
|
|
7890
|
+
//
|
|
7891
|
+
/**
|
|
7892
|
+
* Minimal `fetch`-based client for Google's Generative Language (Gemini) REST
|
|
7893
|
+
* API. Replaces `@google/genai` — the adapter only needs `generateContent`
|
|
7894
|
+
* (streaming and non-streaming), an api-key header, and HTTP error surfacing.
|
|
7895
|
+
* Exposes a `models` facade mirroring the SDK so the adapter call sites are
|
|
7896
|
+
* unchanged.
|
|
7897
|
+
*/
|
|
7898
|
+
class GoogleClient {
|
|
7899
|
+
constructor({ apiKey, baseURL = GOOGLE_BASE_URL }) {
|
|
7900
|
+
this.apiKey = apiKey;
|
|
7901
|
+
this.baseURL = baseURL;
|
|
7902
|
+
this.models = {
|
|
7903
|
+
generateContent: (params, options) => this.generateContent(params, options),
|
|
7904
|
+
generateContentStream: (params, options) => this.generateContentStream(params, options),
|
|
7905
|
+
};
|
|
7906
|
+
}
|
|
7907
|
+
headers() {
|
|
7908
|
+
return {
|
|
7909
|
+
"Content-Type": "application/json",
|
|
7910
|
+
"x-goog-api-key": this.apiKey,
|
|
7911
|
+
};
|
|
7912
|
+
}
|
|
7913
|
+
async toError(response) {
|
|
7914
|
+
let message = `Google request failed with status ${response.status}`;
|
|
7915
|
+
try {
|
|
7916
|
+
const body = (await response.json());
|
|
7917
|
+
if (body?.error?.message)
|
|
7918
|
+
message = body.error.message;
|
|
7919
|
+
}
|
|
7920
|
+
catch {
|
|
7921
|
+
// Non-JSON error body; keep the status-based message.
|
|
7922
|
+
}
|
|
7923
|
+
return new GoogleHttpError(response.status, message);
|
|
7924
|
+
}
|
|
7925
|
+
async generateContent(params, { signal } = {}) {
|
|
7926
|
+
const url = `${this.baseURL}/models/${params.model}:generateContent`;
|
|
7927
|
+
const response = await fetch(url, {
|
|
7928
|
+
method: "POST",
|
|
7929
|
+
headers: this.headers(),
|
|
7930
|
+
body: JSON.stringify(toRestBody(params)),
|
|
7931
|
+
signal,
|
|
7932
|
+
});
|
|
7933
|
+
if (!response.ok)
|
|
7934
|
+
throw await this.toError(response);
|
|
7935
|
+
const json = (await response.json());
|
|
7936
|
+
json.text = responseText(json);
|
|
7937
|
+
return json;
|
|
7938
|
+
}
|
|
7939
|
+
async generateContentStream(params, { signal } = {}) {
|
|
7940
|
+
const url = `${this.baseURL}/models/${params.model}:streamGenerateContent?alt=sse`;
|
|
7941
|
+
const response = await fetch(url, {
|
|
7942
|
+
method: "POST",
|
|
7943
|
+
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
7944
|
+
body: JSON.stringify(toRestBody(params)),
|
|
7945
|
+
signal,
|
|
7946
|
+
});
|
|
7947
|
+
if (!response.ok)
|
|
7948
|
+
throw await this.toError(response);
|
|
7949
|
+
if (!response.body)
|
|
7950
|
+
return (async function* () { })();
|
|
7951
|
+
return parseSseStream(response.body);
|
|
7313
7952
|
}
|
|
7314
7953
|
}
|
|
7954
|
+
|
|
7315
7955
|
// Logger
|
|
7316
7956
|
const getLogger$3 = () => log$1.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
7317
7957
|
// Client initialization
|
|
@@ -7321,8 +7961,7 @@ async function initializeClient$3({ apiKey, } = {}) {
|
|
|
7321
7961
|
if (!resolvedApiKey) {
|
|
7322
7962
|
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
7323
7963
|
}
|
|
7324
|
-
const
|
|
7325
|
-
const client = new sdk.GoogleGenAI({ apiKey: resolvedApiKey });
|
|
7964
|
+
const client = new GoogleClient({ apiKey: resolvedApiKey });
|
|
7326
7965
|
logger.trace("Initialized Gemini client");
|
|
7327
7966
|
return client;
|
|
7328
7967
|
}
|
|
@@ -7471,7 +8110,7 @@ async function initializeClient$2({ apiKey, } = {}) {
|
|
|
7471
8110
|
if (!resolvedApiKey) {
|
|
7472
8111
|
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
7473
8112
|
}
|
|
7474
|
-
const client = new
|
|
8113
|
+
const client = new OpenAIClient({ apiKey: resolvedApiKey });
|
|
7475
8114
|
logger.trace("Initialized OpenAI client");
|
|
7476
8115
|
return client;
|
|
7477
8116
|
}
|
|
@@ -7513,7 +8152,7 @@ async function createStructuredCompletion(client, { messages, responseSchema, mo
|
|
|
7513
8152
|
const zodSchema = responseSchema instanceof v4.z.ZodType
|
|
7514
8153
|
? responseSchema
|
|
7515
8154
|
: naturalZodSchema(responseSchema);
|
|
7516
|
-
const responseFormat =
|
|
8155
|
+
const responseFormat = zodResponseFormat(zodSchema, "response");
|
|
7517
8156
|
const jsonSchema = v4.z.toJSONSchema(zodSchema);
|
|
7518
8157
|
// Temporary hack because OpenAI requires additional_properties to be false on all objects
|
|
7519
8158
|
const checks = [jsonSchema];
|
|
@@ -7530,23 +8169,23 @@ async function createStructuredCompletion(client, { messages, responseSchema, mo
|
|
|
7530
8169
|
checks.shift();
|
|
7531
8170
|
}
|
|
7532
8171
|
responseFormat.json_schema.schema = jsonSchema;
|
|
7533
|
-
const completion = await client.chat.completions.parse({
|
|
8172
|
+
const completion = (await client.chat.completions.parse({
|
|
7534
8173
|
messages,
|
|
7535
8174
|
model,
|
|
7536
8175
|
response_format: responseFormat,
|
|
7537
|
-
});
|
|
8176
|
+
}));
|
|
7538
8177
|
logger.var({ assistantReply: completion.choices[0].message.parsed });
|
|
7539
8178
|
return completion.choices[0].message.parsed;
|
|
7540
8179
|
}
|
|
7541
8180
|
async function createTextCompletion(client, { messages, model, }) {
|
|
7542
8181
|
const logger = getLogger$2();
|
|
7543
8182
|
logger.trace("Using text output (unstructured)");
|
|
7544
|
-
const completion = await client.chat.completions.create({
|
|
8183
|
+
const completion = (await client.chat.completions.create({
|
|
7545
8184
|
messages,
|
|
7546
8185
|
model,
|
|
7547
|
-
});
|
|
7548
|
-
logger.trace(`Assistant reply: ${completion.choices[0]?.message?.content?.length} characters`);
|
|
7549
|
-
return completion.choices[0]?.message?.content || "";
|
|
8186
|
+
}));
|
|
8187
|
+
logger.trace(`Assistant reply: ${completion.choices?.[0]?.message?.content?.length} characters`);
|
|
8188
|
+
return completion.choices?.[0]?.message?.content || "";
|
|
7550
8189
|
}
|
|
7551
8190
|
|
|
7552
8191
|
class OpenAiProvider {
|
|
@@ -7633,19 +8272,139 @@ class OpenAiProvider {
|
|
|
7633
8272
|
}
|
|
7634
8273
|
}
|
|
7635
8274
|
|
|
7636
|
-
//
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
8275
|
+
//
|
|
8276
|
+
//
|
|
8277
|
+
// Constants
|
|
8278
|
+
//
|
|
8279
|
+
const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
8280
|
+
/**
|
|
8281
|
+
* HTTP error carrying the upstream status and parsed API message. The
|
|
8282
|
+
* OpenRouterAdapter classifies errors by reading `.status` / `.statusCode` and
|
|
8283
|
+
* `.message` / `.error.message`, so this shape keeps `classifyError`,
|
|
8284
|
+
* `isTemperatureDeprecationError`, and `isStructuredOutputUnsupportedError`
|
|
8285
|
+
* working unchanged after dropping the SDK.
|
|
8286
|
+
*/
|
|
8287
|
+
class OpenRouterHttpError extends Error {
|
|
8288
|
+
constructor(status, message, error) {
|
|
8289
|
+
super(message);
|
|
8290
|
+
this.name = "OpenRouterHttpError";
|
|
8291
|
+
this.status = status;
|
|
8292
|
+
this.statusCode = status;
|
|
8293
|
+
this.error = error;
|
|
7644
8294
|
}
|
|
7645
|
-
|
|
7646
|
-
|
|
8295
|
+
}
|
|
8296
|
+
//
|
|
8297
|
+
//
|
|
8298
|
+
// Helpers
|
|
8299
|
+
//
|
|
8300
|
+
/**
|
|
8301
|
+
* Normalize the snake_case wire response into the camelCase shape the adapter
|
|
8302
|
+
* readers expect (the SDK previously returned camelCase). Only protocol fields
|
|
8303
|
+
* are touched — user content (schema property names, tool argument JSON) is
|
|
8304
|
+
* left untouched.
|
|
8305
|
+
*/
|
|
8306
|
+
function normalizeResponse(json) {
|
|
8307
|
+
const choices = json.choices;
|
|
8308
|
+
if (Array.isArray(choices)) {
|
|
8309
|
+
for (const choice of choices) {
|
|
8310
|
+
if (choice.finish_reason !== undefined &&
|
|
8311
|
+
choice.finishReason === undefined) {
|
|
8312
|
+
choice.finishReason = choice.finish_reason;
|
|
8313
|
+
}
|
|
8314
|
+
const message = choice.message;
|
|
8315
|
+
if (message?.tool_calls !== undefined &&
|
|
8316
|
+
message.toolCalls === undefined) {
|
|
8317
|
+
message.toolCalls = message.tool_calls;
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
const usage = json.usage;
|
|
8322
|
+
if (usage) {
|
|
8323
|
+
if (usage.promptTokens === undefined)
|
|
8324
|
+
usage.promptTokens = usage.prompt_tokens;
|
|
8325
|
+
if (usage.completionTokens === undefined) {
|
|
8326
|
+
usage.completionTokens = usage.completion_tokens;
|
|
8327
|
+
}
|
|
8328
|
+
if (usage.totalTokens === undefined)
|
|
8329
|
+
usage.totalTokens = usage.total_tokens;
|
|
8330
|
+
const details = usage.completion_tokens_details;
|
|
8331
|
+
if (details?.reasoning_tokens !== undefined &&
|
|
8332
|
+
!usage.completionTokensDetails) {
|
|
8333
|
+
usage.completionTokensDetails = {
|
|
8334
|
+
reasoningTokens: details.reasoning_tokens,
|
|
8335
|
+
};
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8338
|
+
return json;
|
|
8339
|
+
}
|
|
8340
|
+
//
|
|
8341
|
+
//
|
|
8342
|
+
// Main
|
|
8343
|
+
//
|
|
8344
|
+
/**
|
|
8345
|
+
* Minimal `fetch`-based client for OpenRouter's OpenAI-compatible Chat
|
|
8346
|
+
* Completions endpoint. Replaces `@openrouter/sdk` — the adapter only needs a
|
|
8347
|
+
* single POST (streaming and non-streaming), header auth, and HTTP error
|
|
8348
|
+
* surfacing.
|
|
8349
|
+
*/
|
|
8350
|
+
class OpenRouterClient {
|
|
8351
|
+
constructor({ apiKey, baseURL = OPENROUTER_BASE_URL, }) {
|
|
8352
|
+
this.apiKey = apiKey;
|
|
8353
|
+
this.baseURL = baseURL;
|
|
8354
|
+
}
|
|
8355
|
+
headers() {
|
|
8356
|
+
return {
|
|
8357
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
8358
|
+
"Content-Type": "application/json",
|
|
8359
|
+
};
|
|
8360
|
+
}
|
|
8361
|
+
async toError(response) {
|
|
8362
|
+
let message = `OpenRouter request failed with status ${response.status}`;
|
|
8363
|
+
let error;
|
|
8364
|
+
try {
|
|
8365
|
+
const body = (await response.json());
|
|
8366
|
+
if (body?.error?.message) {
|
|
8367
|
+
message = body.error.message;
|
|
8368
|
+
error = body.error;
|
|
8369
|
+
}
|
|
8370
|
+
}
|
|
8371
|
+
catch {
|
|
8372
|
+
// Non-JSON error body; keep the status-based message.
|
|
8373
|
+
}
|
|
8374
|
+
return new OpenRouterHttpError(response.status, message, error);
|
|
8375
|
+
}
|
|
8376
|
+
async chatCompletion(body, { signal } = {}) {
|
|
8377
|
+
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
8378
|
+
method: "POST",
|
|
8379
|
+
headers: this.headers(),
|
|
8380
|
+
body: JSON.stringify(body),
|
|
8381
|
+
signal,
|
|
8382
|
+
});
|
|
8383
|
+
if (!response.ok)
|
|
8384
|
+
throw await this.toError(response);
|
|
8385
|
+
const json = (await response.json());
|
|
8386
|
+
return normalizeResponse(json);
|
|
8387
|
+
}
|
|
8388
|
+
async *streamChatCompletion(body, { signal } = {}) {
|
|
8389
|
+
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
8390
|
+
method: "POST",
|
|
8391
|
+
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
8392
|
+
// OpenAI-style streams only include usage when explicitly requested.
|
|
8393
|
+
body: JSON.stringify({
|
|
8394
|
+
...body,
|
|
8395
|
+
stream: true,
|
|
8396
|
+
stream_options: { include_usage: true },
|
|
8397
|
+
}),
|
|
8398
|
+
signal,
|
|
8399
|
+
});
|
|
8400
|
+
if (!response.ok)
|
|
8401
|
+
throw await this.toError(response);
|
|
8402
|
+
if (!response.body)
|
|
8403
|
+
return;
|
|
8404
|
+
yield* parseSseStream(response.body);
|
|
7647
8405
|
}
|
|
7648
8406
|
}
|
|
8407
|
+
|
|
7649
8408
|
// Logger
|
|
7650
8409
|
const getLogger$1 = () => log$1.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
7651
8410
|
// Client initialization
|
|
@@ -7655,8 +8414,7 @@ async function initializeClient$1({ apiKey, } = {}) {
|
|
|
7655
8414
|
if (!resolvedApiKey) {
|
|
7656
8415
|
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
7657
8416
|
}
|
|
7658
|
-
const
|
|
7659
|
-
const client = new sdk.OpenRouter({ apiKey: resolvedApiKey });
|
|
8417
|
+
const client = new OpenRouterClient({ apiKey: resolvedApiKey });
|
|
7660
8418
|
logger.trace("Initialized OpenRouter client");
|
|
7661
8419
|
return client;
|
|
7662
8420
|
}
|
|
@@ -7736,12 +8494,13 @@ class OpenRouterProvider {
|
|
|
7736
8494
|
const client = await this.getClient();
|
|
7737
8495
|
const messages = prepareMessages(message, options);
|
|
7738
8496
|
const modelToUse = options?.model || this.model;
|
|
7739
|
-
//
|
|
7740
|
-
const response = await client.
|
|
8497
|
+
// OpenAI-compatible Chat Completions body; messages are already wire-shaped.
|
|
8498
|
+
const response = await client.chatCompletion({
|
|
7741
8499
|
model: modelToUse,
|
|
7742
|
-
messages
|
|
8500
|
+
messages,
|
|
7743
8501
|
});
|
|
7744
|
-
const
|
|
8502
|
+
const choices = response.choices;
|
|
8503
|
+
const rawContent = choices?.[0]?.message?.content;
|
|
7745
8504
|
// Extract text content - content could be string or array of content items
|
|
7746
8505
|
const content = typeof rawContent === "string"
|
|
7747
8506
|
? rawContent
|
|
@@ -7804,7 +8563,7 @@ async function initializeClient({ apiKey, } = {}) {
|
|
|
7804
8563
|
if (!resolvedApiKey) {
|
|
7805
8564
|
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
7806
8565
|
}
|
|
7807
|
-
const client = new
|
|
8566
|
+
const client = new OpenAIClient({
|
|
7808
8567
|
apiKey: resolvedApiKey,
|
|
7809
8568
|
baseURL: PROVIDER.XAI.BASE_URL,
|
|
7810
8569
|
});
|
|
@@ -8418,6 +9177,8 @@ exports.isLlmOperateInput = isLlmOperateInput;
|
|
|
8418
9177
|
exports.isLlmOperateInputContent = isLlmOperateInputContent;
|
|
8419
9178
|
exports.isLlmOperateInputFile = isLlmOperateInputFile;
|
|
8420
9179
|
exports.isLlmOperateInputImage = isLlmOperateInputImage;
|
|
9180
|
+
exports.jsonSchemaToNaturalSchema = jsonSchemaToNaturalSchema;
|
|
9181
|
+
exports.naturalSchemaToJsonSchema = naturalSchemaToJsonSchema;
|
|
8421
9182
|
exports.toolkit = toolkit;
|
|
8422
9183
|
exports.tools = tools;
|
|
8423
9184
|
//# sourceMappingURL=index.cjs.map
|