@jaypie/llm 1.1.22 → 1.1.24

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/index.js CHANGED
@@ -2,19 +2,40 @@ import { ConfigurationError, BadGatewayError, TooManyRequestsError, NotImplement
2
2
  import { log as log$2, JAYPIE, resolveValue, placeholders, sleep, ConfigurationError as ConfigurationError$1 } from '@jaypie/core';
3
3
  import { OpenAI, APIUserAbortError, AuthenticationError, BadRequestError, ConflictError, NotFoundError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, APIConnectionError, APIConnectionTimeoutError, InternalServerError } from 'openai';
4
4
  import { zodResponseFormat } from 'openai/helpers/zod';
5
- import { z } from 'zod';
5
+ import { z } from 'zod/v4';
6
6
  import RandomLib from 'random';
7
7
  import { getEnvSecret } from '@jaypie/aws';
8
- import Anthropic from '@anthropic-ai/sdk';
8
+ import Anthropic$1, { Anthropic } from '@anthropic-ai/sdk';
9
+ import ZSchema from 'z-schema';
9
10
  import { fetchWeatherApi } from 'openmeteo';
10
11
 
11
12
  const PROVIDER = {
12
13
  ANTHROPIC: {
14
+ // https://docs.anthropic.com/en/docs/about-claude/models/overview
13
15
  MODEL: {
16
+ // Jaypie Aliases
17
+ DEFAULT: "claude-opus-4-1",
18
+ SMALL: "claude-sonnet-4-0",
19
+ TINY: "claude-3-5-haiku-latest",
20
+ LARGE: "claude-opus-4-1",
21
+ // Latests
22
+ CLAUDE_OPUS_4: "claude-opus-4-1",
23
+ CLAUDE_SONNET_4: "claude-sonnet-4-0",
14
24
  CLAUDE_3_HAIKU: "claude-3-5-haiku-latest",
15
25
  CLAUDE_3_OPUS: "claude-3-opus-latest",
16
- CLAUDE_3_SONNET: "claude-3-5-sonnet-latest",
17
- DEFAULT: "claude-3-5-sonnet-latest",
26
+ CLAUDE_3_SONNET: "claude-3-7-sonnet-latest",
27
+ // Specifics
28
+ CLAUDE_OPUS_4_1: "claude-opus-4-1",
29
+ CLAUDE_OPUS_4_0: "claude-opus-4-0",
30
+ CLAUDE_SONNET_4_0: "claude-sonnet-4-0",
31
+ CLAUDE_3_7_SONNET: "claude-3-7-sonnet-latest ",
32
+ CLAUDE_3_5_SONNET: "claude-3-5-sonnet-latest",
33
+ CLAUDE_3_5_HAIKU: "claude-3-5-haiku-latest",
34
+ // _Note: Claude reversed the order of model name and version in 4_
35
+ // Backward compatibility
36
+ CLAUDE_HAIKU_3: "claude-3-5-haiku-latest",
37
+ CLAUDE_OPUS_3: "claude-3-opus-latest",
38
+ CLAUDE_SONNET_3: "claude-3-7-sonnet-latest",
18
39
  },
19
40
  NAME: "anthropic",
20
41
  PROMPT: {
@@ -34,8 +55,20 @@ const PROVIDER = {
34
55
  },
35
56
  },
36
57
  OPENAI: {
58
+ // https://platform.openai.com/docs/models
37
59
  MODEL: {
38
- DEFAULT: "gpt-4o",
60
+ // Jaypie Aliases
61
+ DEFAULT: "gpt-5",
62
+ SMALL: "gpt-5-mini",
63
+ LARGE: "gpt-5",
64
+ TINY: "gpt-5-nano",
65
+ // OpenAI Official
66
+ GPT_5: "gpt-5",
67
+ GPT_5_MINI: "gpt-5-mini",
68
+ GPT_5_NANO: "gpt-5-nano",
69
+ GPT_4_1: "gpt-4.1",
70
+ GPT_4_1_MINI: "gpt-4.1-mini",
71
+ GPT_4_1_NANO: "gpt-4.1-nano",
39
72
  GPT_4: "gpt-4",
40
73
  GPT_4_O_MINI: "gpt-4o-mini",
41
74
  GPT_4_O: "gpt-4o",
@@ -45,6 +78,9 @@ const PROVIDER = {
45
78
  O1_PRO: "o1-pro",
46
79
  O3_MINI: "o3-mini",
47
80
  O3_MINI_HIGH: "o3-mini-high",
81
+ O3: "o3",
82
+ O3_PRO: "o3-pro",
83
+ O4_MINI: "o4-mini",
48
84
  },
49
85
  NAME: "openai",
50
86
  },
@@ -610,6 +646,10 @@ function extractContentFromResponse(currentResponse, options) {
610
646
  if (output.type === LlmMessageType.FunctionCall) {
611
647
  return createFunctionCallContent(output);
612
648
  }
649
+ // Skip reasoning items when extracting content
650
+ if (output.type === "reasoning") {
651
+ continue;
652
+ }
613
653
  }
614
654
  return "";
615
655
  }
@@ -668,11 +708,26 @@ function createRequestOptions(input, options = {}) {
668
708
  ? options.format
669
709
  : naturalZodSchema(options.format);
670
710
  const responseFormat = zodResponseFormat(zodSchema, "response");
711
+ const jsonSchema = z.toJSONSchema(zodSchema);
712
+ // Temporary hack because of OpenAI requires additional_properties to be false on all objects
713
+ const checks = [jsonSchema];
714
+ while (checks.length > 0) {
715
+ const current = checks[0];
716
+ if (current.type == "object") {
717
+ current.additionalProperties = false;
718
+ }
719
+ Object.keys(current).forEach((key) => {
720
+ if (typeof current[key] == "object") {
721
+ checks.push(current[key]);
722
+ }
723
+ });
724
+ checks.shift();
725
+ }
671
726
  // Set up structured output format in the format expected by the test
672
727
  requestOptions.text = {
673
728
  format: {
674
729
  name: responseFormat.json_schema.name,
675
- schema: responseFormat.json_schema.schema,
730
+ schema: jsonSchema, // Replace this with responseFormat.json_schema.schema once OpenAI supports Zod v4
676
731
  strict: responseFormat.json_schema.strict,
677
732
  type: responseFormat.type,
678
733
  },
@@ -701,7 +756,7 @@ function createRequestOptions(input, options = {}) {
701
756
  //
702
757
  // Main
703
758
  //
704
- async function operate(input, options = {}, context = {
759
+ async function operate$1(input, options = {}, context = {
705
760
  client: new OpenAI(),
706
761
  }) {
707
762
  //
@@ -829,12 +884,19 @@ async function operate(input, options = {}, context = {
829
884
  }
830
885
  // Check if we need to process function calls for multi-turn conversations
831
886
  let hasFunctionCall = false;
887
+ const pendingReasoningItems = []; // Track reasoning items that need to be paired
832
888
  try {
833
889
  if (currentResponse.output && Array.isArray(currentResponse.output)) {
834
890
  // New OpenAI API format with output array
835
891
  for (const output of currentResponse.output) {
836
892
  returnResponse.output.push(output);
837
893
  returnResponse.history.push(output);
894
+ // Handle reasoning items (GPT-5)
895
+ if (output.type === "reasoning") {
896
+ // Store reasoning items to be added with their paired function calls
897
+ pendingReasoningItems.push(output);
898
+ continue;
899
+ }
838
900
  if (output.type === LlmMessageType.FunctionCall) {
839
901
  hasFunctionCall = true;
840
902
  let toolkit;
@@ -891,6 +953,13 @@ async function operate(input, options = {}, context = {
891
953
  }
892
954
  // Add model's function call and result
893
955
  if (Array.isArray(currentInput)) {
956
+ // Add any pending reasoning items before the function call
957
+ for (const reasoningItem of pendingReasoningItems) {
958
+ currentInput.push(reasoningItem);
959
+ }
960
+ // Clear the pending reasoning items after adding them
961
+ pendingReasoningItems.length = 0;
962
+ // Add the function call
894
963
  currentInput.push(output);
895
964
  // Add function call result
896
965
  const functionCallOutput = {
@@ -1080,21 +1149,38 @@ function prepareMessages$1(message, { system, data, placeholders } = {}) {
1080
1149
  return messages;
1081
1150
  }
1082
1151
  // Completion requests
1083
- async function createStructuredCompletion(client, { messages, responseSchema, model, }) {
1152
+ async function createStructuredCompletion$1(client, { messages, responseSchema, model, }) {
1084
1153
  const logger = getLogger$1();
1085
1154
  logger.trace("Using structured output");
1086
1155
  const zodSchema = responseSchema instanceof z.ZodType
1087
1156
  ? responseSchema
1088
1157
  : naturalZodSchema(responseSchema);
1158
+ const responseFormat = zodResponseFormat(zodSchema, "response");
1159
+ const jsonSchema = z.toJSONSchema(zodSchema);
1160
+ // Temporary hack because OpenAI requires additional_properties to be false on all objects
1161
+ const checks = [jsonSchema];
1162
+ while (checks.length > 0) {
1163
+ const current = checks[0];
1164
+ if (current.type == "object") {
1165
+ current.additionalProperties = false;
1166
+ }
1167
+ Object.keys(current).forEach((key) => {
1168
+ if (typeof current[key] == "object") {
1169
+ checks.push(current[key]);
1170
+ }
1171
+ });
1172
+ checks.shift();
1173
+ }
1174
+ responseFormat.json_schema.schema = jsonSchema;
1089
1175
  const completion = await client.beta.chat.completions.parse({
1090
1176
  messages,
1091
1177
  model,
1092
- response_format: zodResponseFormat(zodSchema, "response"),
1178
+ response_format: responseFormat,
1093
1179
  });
1094
1180
  logger.var({ assistantReply: completion.choices[0].message.parsed });
1095
1181
  return completion.choices[0].message.parsed;
1096
1182
  }
1097
- async function createTextCompletion(client, { messages, model, }) {
1183
+ async function createTextCompletion$1(client, { messages, model, }) {
1098
1184
  const logger = getLogger$1();
1099
1185
  logger.trace("Using text output (unstructured)");
1100
1186
  const completion = await client.chat.completions.create({
@@ -1124,13 +1210,13 @@ class OpenAiProvider {
1124
1210
  const messages = prepareMessages$1(message, options || {});
1125
1211
  const modelToUse = options?.model || this.model;
1126
1212
  if (options?.response) {
1127
- return createStructuredCompletion(client, {
1213
+ return createStructuredCompletion$1(client, {
1128
1214
  messages,
1129
1215
  responseSchema: options.response,
1130
1216
  model: modelToUse,
1131
1217
  });
1132
1218
  }
1133
- return createTextCompletion(client, {
1219
+ return createTextCompletion$1(client, {
1134
1220
  messages,
1135
1221
  model: modelToUse,
1136
1222
  });
@@ -1147,7 +1233,7 @@ class OpenAiProvider {
1147
1233
  : [...this.conversationHistory];
1148
1234
  }
1149
1235
  // Call operate with the updated options
1150
- const response = await operate(input, mergedOptions, { client });
1236
+ const response = await operate$1(input, mergedOptions, { client });
1151
1237
  // Update conversation history with the new history from the response
1152
1238
  if (response.history && response.history.length > 0) {
1153
1239
  this.conversationHistory = response.history;
@@ -1156,6 +1242,267 @@ class OpenAiProvider {
1156
1242
  }
1157
1243
  }
1158
1244
 
1245
+ // Handle placeholder logic
1246
+ // Convert string input to array format if needed
1247
+ // Apply placeholders to fields if data is provided and placeholders.* is undefined or true
1248
+ function handleInputAndPlaceholders(input, options) {
1249
+ let history = formatOperateInput(input);
1250
+ let llmInstructions;
1251
+ let systemPrompt;
1252
+ if (options?.data &&
1253
+ (options.placeholders?.input === undefined || options.placeholders?.input)) {
1254
+ history = formatOperateInput(input, {
1255
+ data: options?.data,
1256
+ });
1257
+ }
1258
+ if (options?.instructions) {
1259
+ llmInstructions =
1260
+ options.data && options.placeholders?.instructions !== false
1261
+ ? placeholders(options.instructions, options.data)
1262
+ : options.instructions;
1263
+ }
1264
+ if (options?.system) {
1265
+ systemPrompt =
1266
+ options.data && options.placeholders?.system !== false
1267
+ ? placeholders(options.system, options.data)
1268
+ : options.system;
1269
+ }
1270
+ return { history, systemPrompt, llmInstructions };
1271
+ }
1272
+ function updateUsage(usage, totalUsage) {
1273
+ totalUsage.input += usage.input_tokens;
1274
+ totalUsage.output += usage.output_tokens;
1275
+ totalUsage.reasoning += usage.prompt_tokens;
1276
+ totalUsage.total += usage.input_tokens + usage.output_tokens;
1277
+ }
1278
+ function handleMaxTurns(maxTurns, history, inputMessages, response, totalUsage) {
1279
+ const error = new TooManyRequestsError();
1280
+ const detail = `Model requested function call but exceeded ${maxTurns} turns`;
1281
+ log.warn(detail);
1282
+ return {
1283
+ //model: model,
1284
+ //provider: PROVIDER.ANTHROPIC,
1285
+ error: {
1286
+ detail,
1287
+ status: error.status,
1288
+ title: error.title,
1289
+ },
1290
+ history,
1291
+ output: inputMessages.slice(-1),
1292
+ responses: response.content,
1293
+ status: LlmResponseStatus.Incomplete,
1294
+ usage: [totalUsage],
1295
+ };
1296
+ }
1297
+ function handleOutputSchema(format) {
1298
+ let schema;
1299
+ if (format) {
1300
+ // Check if format is a JsonObject with type "json_schema"
1301
+ if (typeof format === "object" &&
1302
+ format !== null &&
1303
+ !Array.isArray(format) &&
1304
+ format.type === "json_schema") {
1305
+ // Direct pass-through for JsonObject with type "json_schema"
1306
+ schema = structuredClone(format);
1307
+ schema.type = "object"; // Validator does not recognise "json_schema" as a type
1308
+ }
1309
+ else {
1310
+ // Convert NaturalSchema to JSON schema through Zod
1311
+ const zodSchema = format instanceof z.ZodType
1312
+ ? format
1313
+ : naturalZodSchema(format);
1314
+ schema = z.toJSONSchema(zodSchema);
1315
+ }
1316
+ if (schema.$schema) {
1317
+ delete schema.$schema; // Hack to fix issue with validator
1318
+ }
1319
+ return schema;
1320
+ }
1321
+ }
1322
+ // Register tools and process them to work with Anthropic
1323
+ function bundleTools(tools, explain, schema) {
1324
+ let toolkit;
1325
+ let processedTools = [];
1326
+ if (tools instanceof Toolkit) {
1327
+ toolkit = tools;
1328
+ }
1329
+ else if (Array.isArray(tools)) {
1330
+ toolkit = new Toolkit(tools, { explain });
1331
+ }
1332
+ if (toolkit) {
1333
+ toolkit.tools.forEach((tool) => {
1334
+ processedTools.push({
1335
+ ...tool,
1336
+ input_schema: {
1337
+ ...tool.parameters,
1338
+ type: "object",
1339
+ },
1340
+ type: "custom",
1341
+ });
1342
+ delete processedTools[processedTools.length - 1].parameters;
1343
+ });
1344
+ }
1345
+ if (schema) {
1346
+ processedTools.push({
1347
+ name: "structured_output",
1348
+ description: "Output a structured JSON object, " +
1349
+ "use this before your final response to give structured outputs to the user",
1350
+ input_schema: schema,
1351
+ type: "custom",
1352
+ });
1353
+ }
1354
+ return { processedTools, toolkit };
1355
+ }
1356
+ // Handles individual tool calls. Returns true for break, false for continue.
1357
+ async function callTool(inputMessages, response, hooks, toolkit) {
1358
+ inputMessages.push({
1359
+ role: PROVIDER.ANTHROPIC.ROLE.ASSISTANT,
1360
+ content: response.content,
1361
+ });
1362
+ // Get the tool use
1363
+ const toolUse = response.content[response.content.length - 1];
1364
+ // If the tool use is structured output (magic tool), break
1365
+ if (toolUse.name === "structured_output") {
1366
+ return true;
1367
+ }
1368
+ if (hooks?.beforeEachTool) {
1369
+ await hooks.beforeEachTool({
1370
+ toolName: toolUse.name,
1371
+ args: JSON.stringify(toolUse.input),
1372
+ });
1373
+ }
1374
+ let result;
1375
+ try {
1376
+ result = await toolkit?.call({
1377
+ name: toolUse.name,
1378
+ arguments: JSON.stringify(toolUse.input),
1379
+ });
1380
+ }
1381
+ catch (error) {
1382
+ if (hooks?.onToolError) {
1383
+ await hooks.onToolError({
1384
+ error: error,
1385
+ toolName: toolUse.name,
1386
+ args: JSON.stringify(toolUse.input),
1387
+ });
1388
+ }
1389
+ throw error;
1390
+ }
1391
+ if (hooks?.afterEachTool) {
1392
+ await hooks.afterEachTool({
1393
+ result,
1394
+ toolName: toolUse.name,
1395
+ args: JSON.stringify(toolUse.input),
1396
+ });
1397
+ }
1398
+ inputMessages.push({
1399
+ role: PROVIDER.ANTHROPIC.ROLE.USER,
1400
+ content: [
1401
+ {
1402
+ type: "tool_result",
1403
+ content: JSON.stringify(result),
1404
+ tool_use_id: toolUse.id,
1405
+ },
1406
+ ],
1407
+ });
1408
+ return false;
1409
+ }
1410
+ //
1411
+ //
1412
+ // Main
1413
+ //
1414
+ async function operate(input, options = {}, context = {
1415
+ client: new Anthropic(),
1416
+ }) {
1417
+ // Set model
1418
+ const model = options?.model || PROVIDER.ANTHROPIC.MODEL.DEFAULT;
1419
+ let schema = handleOutputSchema(options.format);
1420
+ let { processedTools, toolkit } = bundleTools(options.tools, options.explain, schema);
1421
+ let { history, systemPrompt, llmInstructions } = handleInputAndPlaceholders(input, options);
1422
+ // If history is provided, merge it with the input
1423
+ if (options.history) {
1424
+ history = [...options.history, ...history];
1425
+ }
1426
+ // Avoid Anthropic error by removing type property
1427
+ const inputMessages = structuredClone(history);
1428
+ inputMessages.forEach((message) => {
1429
+ delete message.type;
1430
+ });
1431
+ // Add instruction to the input message
1432
+ if (llmInstructions) {
1433
+ inputMessages[inputMessages.length - 1].content += "\n\n" + llmInstructions;
1434
+ }
1435
+ // Setup usage tracking
1436
+ let totalUsage = {
1437
+ input: 0,
1438
+ output: 0,
1439
+ reasoning: 0,
1440
+ total: 0,
1441
+ };
1442
+ // Determine max turns from options
1443
+ const maxTurns = maxTurnsFromOptions(options);
1444
+ const enableMultipleTurns = maxTurns > 1;
1445
+ let currentTurn = 0;
1446
+ let response;
1447
+ while (true) {
1448
+ // Loop for tool use
1449
+ response = await context.client.messages.create({
1450
+ model: model,
1451
+ system: systemPrompt,
1452
+ messages: inputMessages,
1453
+ max_tokens: PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
1454
+ stream: false,
1455
+ tools: processedTools,
1456
+ tool_choice: processedTools.length > 0
1457
+ ? { type: schema ? "any" : "auto" }
1458
+ : undefined,
1459
+ ...options?.providerOptions,
1460
+ });
1461
+ // Update usage
1462
+ updateUsage(response.usage, totalUsage);
1463
+ // If the response is not a tool use, break
1464
+ if (response.stop_reason !== "tool_use") {
1465
+ break;
1466
+ }
1467
+ const breakLoop = await callTool(inputMessages, response, options.hooks, toolkit);
1468
+ if (breakLoop) {
1469
+ break;
1470
+ }
1471
+ // Handle turn limit
1472
+ if (!enableMultipleTurns || currentTurn >= maxTurns) {
1473
+ return handleMaxTurns(maxTurns, history, inputMessages, response, totalUsage);
1474
+ }
1475
+ currentTurn++;
1476
+ }
1477
+ let jsonResult;
1478
+ if (schema) {
1479
+ const validator = new ZSchema({});
1480
+ jsonResult = response.content[response.content.length - 1].input;
1481
+ if (!validator.validate(jsonResult, schema)) {
1482
+ throw new Error("Model returned invalid JSON");
1483
+ }
1484
+ }
1485
+ history.push({
1486
+ content: schema
1487
+ ? JSON.stringify(jsonResult)
1488
+ : response.content[0].text,
1489
+ role: PROVIDER.ANTHROPIC.ROLE.ASSISTANT,
1490
+ type: LlmMessageType.Message,
1491
+ });
1492
+ return {
1493
+ //model: model,
1494
+ //provider: PROVIDER.ANTHROPIC,
1495
+ content: schema
1496
+ ? jsonResult
1497
+ : response.content[0].text,
1498
+ responses: [response],
1499
+ output: history.slice(-1),
1500
+ history,
1501
+ status: LlmResponseStatus.Completed,
1502
+ usage: [totalUsage],
1503
+ };
1504
+ }
1505
+
1159
1506
  // Logger
1160
1507
  const getLogger = () => log$2.lib({ lib: JAYPIE.LIB.LLM });
1161
1508
  // Client initialization
@@ -1165,7 +1512,7 @@ async function initializeClient({ apiKey, } = {}) {
1165
1512
  if (!resolvedApiKey) {
1166
1513
  throw new ConfigurationError$1("The application could not resolve the required API key: ANTHROPIC_API_KEY");
1167
1514
  }
1168
- const client = new Anthropic({ apiKey: resolvedApiKey });
1515
+ const client = new Anthropic$1({ apiKey: resolvedApiKey });
1169
1516
  logger.trace("Initialized Anthropic client");
1170
1517
  return client;
1171
1518
  }
@@ -1193,6 +1540,72 @@ function prepareMessages(message, { data, placeholders } = {}) {
1193
1540
  logger.trace(`User message: ${userMessage.content.length} characters`);
1194
1541
  return messages;
1195
1542
  }
1543
+ // Basic text completion
1544
+ async function createTextCompletion(client, messages, model, systemMessage) {
1545
+ log$2.trace("Using text output (unstructured)");
1546
+ const params = {
1547
+ model,
1548
+ messages,
1549
+ max_tokens: PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
1550
+ };
1551
+ // Add system instruction if provided
1552
+ if (systemMessage) {
1553
+ params.system = systemMessage;
1554
+ log$2.trace(`System message: ${systemMessage.length} characters`);
1555
+ }
1556
+ const response = await client.messages.create(params);
1557
+ log$2.trace(`Assistant reply: ${response.content[0]?.text?.length || 0} characters`);
1558
+ return response.content[0]?.text || "";
1559
+ }
1560
+ // Structured output completion
1561
+ async function createStructuredCompletion(client, messages, model, responseSchema, systemMessage) {
1562
+ log$2.trace("Using structured output");
1563
+ // Get the JSON schema for the response
1564
+ const schema = responseSchema instanceof z.ZodType
1565
+ ? responseSchema
1566
+ : naturalZodSchema(responseSchema);
1567
+ // Set system message with JSON instructions
1568
+ const defaultSystemPrompt = "You will be responding with structured JSON data. " +
1569
+ "Format your entire response as a valid JSON object with the following structure: " +
1570
+ JSON.stringify(z.toJSONSchema(schema));
1571
+ const systemPrompt = systemMessage || defaultSystemPrompt;
1572
+ try {
1573
+ // Use standard Anthropic API to get response
1574
+ const params = {
1575
+ model,
1576
+ messages,
1577
+ max_tokens: PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
1578
+ system: systemPrompt,
1579
+ };
1580
+ const response = await client.messages.create(params);
1581
+ // Extract text from response
1582
+ const responseText = response.content[0]?.text || "";
1583
+ // Find JSON in response
1584
+ const jsonMatch = responseText.match(/```json\s*([\s\S]*?)\s*```/) ||
1585
+ responseText.match(/\{[\s\S]*\}/);
1586
+ if (jsonMatch) {
1587
+ try {
1588
+ // Parse the JSON response
1589
+ const jsonStr = jsonMatch[1] || jsonMatch[0];
1590
+ const result = JSON.parse(jsonStr);
1591
+ if (!schema.parse(result)) {
1592
+ throw new Error(`JSON response from Anthropic does not match schema: ${responseText}`);
1593
+ }
1594
+ log$2.trace("Received structured response", { result });
1595
+ return result;
1596
+ }
1597
+ catch {
1598
+ throw new Error(`Failed to parse JSON response from Anthropic: ${responseText}`);
1599
+ }
1600
+ }
1601
+ // If we can't extract JSON
1602
+ throw new Error("Failed to parse structured response from Anthropic");
1603
+ }
1604
+ catch (error) {
1605
+ log$2.error("Error creating structured completion", { error });
1606
+ throw error;
1607
+ }
1608
+ }
1196
1609
 
1197
1610
  // Maps Jaypie roles to Anthropic roles
1198
1611
  ({
@@ -1217,89 +1630,6 @@ class AnthropicProvider {
1217
1630
  this._client = await initializeClient({ apiKey: this.apiKey });
1218
1631
  return this._client;
1219
1632
  }
1220
- // Basic text completion
1221
- async createTextCompletion(client, messages, model, systemMessage) {
1222
- this.log.trace("Using text output (unstructured)");
1223
- const params = {
1224
- model,
1225
- messages,
1226
- max_tokens: PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
1227
- };
1228
- // Add system instruction if provided
1229
- if (systemMessage) {
1230
- params.system = systemMessage;
1231
- this.log.trace(`System message: ${systemMessage.length} characters`);
1232
- }
1233
- const response = await client.messages.create(params);
1234
- this.log.trace(`Assistant reply: ${response.content[0]?.text?.length || 0} characters`);
1235
- return response.content[0]?.text || "";
1236
- }
1237
- // Structured output completion
1238
- async createStructuredCompletion(client, messages, model, responseSchema, systemMessage) {
1239
- this.log.trace("Using structured output");
1240
- // Get the JSON schema for the response
1241
- const schema = responseSchema instanceof z.ZodType
1242
- ? responseSchema
1243
- : naturalZodSchema(responseSchema);
1244
- // Set system message with JSON instructions
1245
- const defaultSystemPrompt = "You will be responding with structured JSON data. " +
1246
- "Format your entire response as a valid JSON object with the following structure: " +
1247
- (responseSchema instanceof z.ZodType
1248
- ? JSON.stringify(this.simplifyZodSchema(schema))
1249
- : JSON.stringify(responseSchema));
1250
- const systemPrompt = systemMessage || defaultSystemPrompt;
1251
- try {
1252
- // Use standard Anthropic API to get response
1253
- const params = {
1254
- model,
1255
- messages,
1256
- max_tokens: PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
1257
- system: systemPrompt,
1258
- };
1259
- const response = await client.messages.create(params);
1260
- // Extract text from response
1261
- const responseText = response.content[0]?.text || "";
1262
- // Find JSON in response
1263
- const jsonMatch = responseText.match(/```json\s*([\s\S]*?)\s*```/) ||
1264
- responseText.match(/\{[\s\S]*\}/);
1265
- if (jsonMatch) {
1266
- try {
1267
- // Parse the JSON response
1268
- const jsonStr = jsonMatch[1] || jsonMatch[0];
1269
- const result = JSON.parse(jsonStr);
1270
- this.log.trace("Received structured response", { result });
1271
- return result;
1272
- }
1273
- catch {
1274
- throw new Error(`Failed to parse JSON response from Anthropic: ${responseText}`);
1275
- }
1276
- }
1277
- // If we can't extract JSON
1278
- throw new Error("Failed to parse structured response from Anthropic");
1279
- }
1280
- catch (error) {
1281
- this.log.error("Error creating structured completion", { error });
1282
- throw error;
1283
- }
1284
- }
1285
- // Helper method to simplify Zod schema to a plain object for system prompt
1286
- simplifyZodSchema(schema) {
1287
- try {
1288
- const zodSchema = schema;
1289
- if (typeof zodSchema._def?.shape === "object") {
1290
- const result = {};
1291
- Object.keys(zodSchema._def.shape || {}).forEach((key) => {
1292
- result[key] = "string";
1293
- });
1294
- return result;
1295
- }
1296
- return { result: "string" };
1297
- }
1298
- catch (error) {
1299
- this.log.error(`Error simplifying schema: ${error}`);
1300
- return { result: "string" };
1301
- }
1302
- }
1303
1633
  // Main send method
1304
1634
  async send(message, options) {
1305
1635
  const client = await this.getClient();
@@ -1314,17 +1644,28 @@ class AnthropicProvider {
1314
1644
  });
1315
1645
  }
1316
1646
  if (options?.response) {
1317
- const schema = options.response instanceof z.ZodType
1318
- ? options.response
1319
- : naturalZodSchema(options.response);
1320
- return this.createStructuredCompletion(client, messages, modelToUse, schema, systemMessage);
1647
+ return createStructuredCompletion(client, messages, modelToUse, options.response, systemMessage);
1321
1648
  }
1322
- return this.createTextCompletion(client, messages, modelToUse, systemMessage);
1649
+ return createTextCompletion(client, messages, modelToUse, systemMessage);
1323
1650
  }
1324
- // Placeholder for operate method - will be implemented in a future task
1325
- // This will be properly implemented in the "Tool Calling Implementation" task
1326
- async operate(input, _options = {}) {
1327
- throw new Error("The operate method is not yet implemented for AnthropicProvider");
1651
+ async operate(input, options = {}) {
1652
+ const client = await this.getClient();
1653
+ options.model = options?.model || this.model;
1654
+ // Create a merged history including both the tracked history and any explicitly provided history
1655
+ const mergedOptions = { ...options };
1656
+ if (this.conversationHistory.length > 0) {
1657
+ // If options.history exists, merge with instance history, otherwise use instance history
1658
+ mergedOptions.history = options.history
1659
+ ? [...this.conversationHistory, ...options.history]
1660
+ : [...this.conversationHistory];
1661
+ }
1662
+ // Call operate with the updated options
1663
+ const response = await operate(input, mergedOptions, { client });
1664
+ // Update conversation history with the new history from the response
1665
+ if (response.history && response.history.length > 0) {
1666
+ this.conversationHistory = response.history;
1667
+ }
1668
+ return response;
1328
1669
  }
1329
1670
  }
1330
1671
 
@@ -1636,7 +1977,16 @@ const weather = {
1636
1977
  };
1637
1978
 
1638
1979
  const tools = [random, roll, time, weather];
1639
- const toolkit = new Toolkit(tools);
1980
+ class JaypieToolkit extends Toolkit {
1981
+ constructor(tools, options) {
1982
+ super(tools, options);
1983
+ this.random = random;
1984
+ this.roll = roll;
1985
+ this.time = time;
1986
+ this.weather = weather;
1987
+ }
1988
+ }
1989
+ const toolkit = new JaypieToolkit(tools);
1640
1990
 
1641
- export { constants as LLM, Llm, Toolkit, toolkit, tools };
1991
+ export { JaypieToolkit, constants as LLM, Llm, Toolkit, toolkit, tools };
1642
1992
  //# sourceMappingURL=index.js.map