@jaypie/llm 1.2.24 → 1.2.26

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.
@@ -14,6 +14,10 @@ import { BaseProviderAdapter } from "./ProviderAdapter.interface.js";
14
14
  export declare class AnthropicAdapter extends BaseProviderAdapter {
15
15
  readonly name: "anthropic";
16
16
  readonly defaultModel: "claude-sonnet-4-6";
17
+ private runtimeNoTemperatureModels;
18
+ rememberModelRejectsTemperature(model: string): void;
19
+ clearRuntimeNoTemperatureModels(): void;
20
+ private supportsTemperature;
17
21
  buildRequest(request: OperateRequest): Anthropic.MessageCreateParams;
18
22
  formatTools(toolkit: Toolkit, outputSchema?: JsonObject): ProviderToolDefinition[];
19
23
  formatOutputSchema(schema: JsonObject | NaturalSchema | z.ZodType): JsonObject;
@@ -72,6 +72,7 @@ export interface GeminiGenerateContentConfig {
72
72
  };
73
73
  responseMimeType?: string;
74
74
  responseJsonSchema?: JsonObject;
75
+ responseSchema?: JsonObject;
75
76
  temperature?: number;
76
77
  topP?: number;
77
78
  topK?: number;
@@ -2,6 +2,7 @@ export * from "./determineModelProvider.js";
2
2
  export * from "./extractReasoning.js";
3
3
  export * from "./formatOperateInput.js";
4
4
  export * from "./formatOperateMessage.js";
5
+ export * from "./jsonSchemaToOpenApi3.js";
5
6
  export * from "./logger.js";
6
7
  export * from "./maxTurnsFromOptions.js";
7
8
  export * from "./naturalZodSchema.js";
@@ -0,0 +1,10 @@
1
+ import { JsonObject } from "@jaypie/types";
2
+ /**
3
+ * Converts a JSON Schema (Draft 2020-12) object to the OpenAPI 3.0 schema subset
4
+ * that Gemini's `responseSchema` accepts. This constrains generation (not just validation)
5
+ * and avoids the `items`-keyword leakage bug in `responseJsonSchema`.
6
+ *
7
+ * Strips: $schema, additionalProperties, $defs, $ref (inlines where possible), const
8
+ * Preserves: type, properties, required, items, enum, description, nullable
9
+ */
10
+ export declare function jsonSchemaToOpenApi3(schema: JsonObject): JsonObject;
package/dist/esm/index.js CHANGED
@@ -552,6 +552,50 @@ function formatOperateInput(input, options) {
552
552
  return [input];
553
553
  }
554
554
 
555
+ /**
556
+ * Converts a JSON Schema (Draft 2020-12) object to the OpenAPI 3.0 schema subset
557
+ * that Gemini's `responseSchema` accepts. This constrains generation (not just validation)
558
+ * and avoids the `items`-keyword leakage bug in `responseJsonSchema`.
559
+ *
560
+ * Strips: $schema, additionalProperties, $defs, $ref (inlines where possible), const
561
+ * Preserves: type, properties, required, items, enum, description, nullable
562
+ */
563
+ function jsonSchemaToOpenApi3(schema) {
564
+ if (typeof schema !== "object" || schema === null || Array.isArray(schema)) {
565
+ return schema;
566
+ }
567
+ const result = {};
568
+ for (const [key, value] of Object.entries(schema)) {
569
+ // Strip JSON Schema keywords not in OpenAPI 3.0 subset
570
+ if (key === "$schema" ||
571
+ key === "$defs" ||
572
+ key === "additionalProperties" ||
573
+ key === "const" ||
574
+ key === "$ref") {
575
+ continue;
576
+ }
577
+ if (key === "properties" &&
578
+ typeof value === "object" &&
579
+ value !== null &&
580
+ !Array.isArray(value)) {
581
+ const convertedProps = {};
582
+ for (const [propKey, propValue] of Object.entries(value)) {
583
+ convertedProps[propKey] = jsonSchemaToOpenApi3(propValue);
584
+ }
585
+ result[key] = convertedProps;
586
+ }
587
+ else if (key === "items" &&
588
+ typeof value === "object" &&
589
+ value !== null) {
590
+ result[key] = jsonSchemaToOpenApi3(value);
591
+ }
592
+ else {
593
+ result[key] = value;
594
+ }
595
+ }
596
+ return result;
597
+ }
598
+
555
599
  const getLogger$5 = () => log$1.lib({ lib: JAYPIE.LIB.LLM });
556
600
 
557
601
  // Turn policy constants
@@ -1019,6 +1063,24 @@ const NOT_RETRYABLE_ERROR_NAMES = [
1019
1063
  "NotFoundError",
1020
1064
  "PermissionDeniedError",
1021
1065
  ];
1066
+ // Models known not to accept `temperature`.
1067
+ // Patterns (not exact names) so dated variants and future releases are covered
1068
+ // without code changes — Anthropic is trending toward removing temperature on
1069
+ // newer Claude models.
1070
+ const MODELS_WITHOUT_TEMPERATURE = [
1071
+ /^claude-opus-4-[789]/,
1072
+ /^claude-opus-[5-9]/,
1073
+ ];
1074
+ function isTemperatureDeprecationError(error) {
1075
+ if (!error || typeof error !== "object")
1076
+ return false;
1077
+ const err = error;
1078
+ const name = error?.constructor?.name;
1079
+ if (name !== "BadRequestError" && err.status !== 400)
1080
+ return false;
1081
+ const messages = [err.message, err.error?.message].filter((m) => typeof m === "string");
1082
+ return messages.some((m) => m.toLowerCase().includes("temperature"));
1083
+ }
1022
1084
  //
1023
1085
  //
1024
1086
  // Main
@@ -1033,6 +1095,20 @@ class AnthropicAdapter extends BaseProviderAdapter {
1033
1095
  super(...arguments);
1034
1096
  this.name = PROVIDER.ANTHROPIC.NAME;
1035
1097
  this.defaultModel = PROVIDER.ANTHROPIC.MODEL.DEFAULT;
1098
+ // Session-level cache of models observed to reject `temperature` at runtime.
1099
+ // Populated by executeRequest on 400 errors so repeat calls skip the param.
1100
+ this.runtimeNoTemperatureModels = new Set();
1101
+ }
1102
+ rememberModelRejectsTemperature(model) {
1103
+ this.runtimeNoTemperatureModels.add(model);
1104
+ }
1105
+ clearRuntimeNoTemperatureModels() {
1106
+ this.runtimeNoTemperatureModels.clear();
1107
+ }
1108
+ supportsTemperature(model) {
1109
+ if (this.runtimeNoTemperatureModels.has(model))
1110
+ return false;
1111
+ return !MODELS_WITHOUT_TEMPERATURE.some((pattern) => pattern.test(model));
1036
1112
  }
1037
1113
  //
1038
1114
  // Request Building
@@ -1124,6 +1200,11 @@ class AnthropicAdapter extends BaseProviderAdapter {
1124
1200
  if (request.temperature !== undefined) {
1125
1201
  anthropicRequest.temperature = request.temperature;
1126
1202
  }
1203
+ // Strip temperature for models that don't support it (denylist + runtime cache)
1204
+ if (anthropicRequest.temperature !== undefined &&
1205
+ !this.supportsTemperature(anthropicRequest.model)) {
1206
+ delete anthropicRequest.temperature;
1207
+ }
1127
1208
  return anthropicRequest;
1128
1209
  }
1129
1210
  formatTools(toolkit, outputSchema) {
@@ -1174,22 +1255,48 @@ class AnthropicAdapter extends BaseProviderAdapter {
1174
1255
  //
1175
1256
  async executeRequest(client, request, signal) {
1176
1257
  const anthropic = client;
1258
+ const anthropicRequest = request;
1177
1259
  try {
1178
- return (await anthropic.messages.create(request, signal ? { signal } : undefined));
1260
+ return (await anthropic.messages.create(anthropicRequest, signal ? { signal } : undefined));
1179
1261
  }
1180
1262
  catch (error) {
1181
1263
  if (signal?.aborted)
1182
1264
  return undefined;
1265
+ // If the model rejected `temperature`, cache it and retry without the param
1266
+ if (anthropicRequest.temperature !== undefined &&
1267
+ isTemperatureDeprecationError(error)) {
1268
+ this.rememberModelRejectsTemperature(anthropicRequest.model);
1269
+ const retryRequest = { ...anthropicRequest };
1270
+ delete retryRequest.temperature;
1271
+ return (await anthropic.messages.create(retryRequest, signal ? { signal } : undefined));
1272
+ }
1183
1273
  throw error;
1184
1274
  }
1185
1275
  }
1186
1276
  async *executeStreamRequest(client, request, signal) {
1187
1277
  const anthropic = client;
1188
- const streamRequest = {
1278
+ let streamRequest = {
1189
1279
  ...request,
1190
1280
  stream: true,
1191
1281
  };
1192
- const stream = await anthropic.messages.create(streamRequest, signal ? { signal } : undefined);
1282
+ let stream;
1283
+ try {
1284
+ stream = await anthropic.messages.create(streamRequest, signal ? { signal } : undefined);
1285
+ }
1286
+ catch (error) {
1287
+ if (streamRequest.temperature !== undefined &&
1288
+ isTemperatureDeprecationError(error)) {
1289
+ this.rememberModelRejectsTemperature(streamRequest.model);
1290
+ streamRequest = {
1291
+ ...streamRequest,
1292
+ };
1293
+ delete streamRequest.temperature;
1294
+ stream = await anthropic.messages.create(streamRequest, signal ? { signal } : undefined);
1295
+ }
1296
+ else {
1297
+ throw error;
1298
+ }
1299
+ }
1193
1300
  // Track current tool call being built
1194
1301
  let currentToolCall = null;
1195
1302
  // Track usage for final chunk
@@ -1538,11 +1645,21 @@ class GeminiAdapter extends BaseProviderAdapter {
1538
1645
  // Gemini doesn't support combining function calling with responseMimeType: 'application/json'
1539
1646
  // When tools are present, structured output is handled via the structured_output tool
1540
1647
  if (request.format && !(request.tools && request.tools.length > 0)) {
1541
- geminiRequest.config = {
1542
- ...geminiRequest.config,
1543
- responseMimeType: "application/json",
1544
- responseJsonSchema: request.format,
1545
- };
1648
+ const useJsonSchema = request.providerOptions?.useJsonSchema === true;
1649
+ if (useJsonSchema) {
1650
+ geminiRequest.config = {
1651
+ ...geminiRequest.config,
1652
+ responseMimeType: "application/json",
1653
+ responseJsonSchema: request.format,
1654
+ };
1655
+ }
1656
+ else {
1657
+ geminiRequest.config = {
1658
+ ...geminiRequest.config,
1659
+ responseMimeType: "application/json",
1660
+ responseSchema: jsonSchemaToOpenApi3(request.format),
1661
+ };
1662
+ }
1546
1663
  }
1547
1664
  // When format is specified with tools, add instruction to use structured_output tool
1548
1665
  if (request.format && request.tools && request.tools.length > 0) {
@@ -1610,11 +1727,7 @@ class GeminiAdapter extends BaseProviderAdapter {
1610
1727
  : naturalZodSchema(schema);
1611
1728
  jsonSchema = z.toJSONSchema(zodSchema);
1612
1729
  }
1613
- // Remove $schema property (Gemini doesn't need it)
1614
- if (jsonSchema.$schema) {
1615
- delete jsonSchema.$schema;
1616
- }
1617
- return jsonSchema;
1730
+ return jsonSchemaToOpenApi3(jsonSchema);
1618
1731
  }
1619
1732
  //
1620
1733
  // API Execution