@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/constants.d.ts +42 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +458 -108
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.class.d.ts +15 -0
- package/dist/providers/anthropic/operate.d.ts +16 -0
- package/dist/providers/anthropic/utils.d.ts +7 -3
- package/dist/providers/openai/types.d.ts +9 -1
- package/dist/providers/openai/utils.d.ts +1 -1
- package/dist/tools/index.d.ts +10 -2
- package/dist/util/naturalZodSchema.d.ts +1 -1
- package/package.json +5 -4
- package/dist/providers/AnthropicProvider.class.d.ts +0 -20
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-
|
|
17
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
1649
|
+
return createTextCompletion(client, messages, modelToUse, systemMessage);
|
|
1323
1650
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
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
|
-
|
|
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
|