@contentgrowth/llm-service 1.2.4 → 1.3.0
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/README.md +232 -0
- package/dist/index.cjs +180 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -13
- package/dist/index.d.ts +57 -13
- package/dist/index.js +180 -68
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -1
- package/dist/ui/react/components/index.d.cts +2 -3
- package/dist/ui/react/components/index.d.ts +2 -3
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
3
|
import * as _google_genai from '@google/genai';
|
|
3
4
|
import { GoogleGenAI } from '@google/genai';
|
|
4
5
|
|
|
@@ -158,35 +159,71 @@ declare class OpenAIProvider extends BaseLLMProvider {
|
|
|
158
159
|
declare class AnthropicProvider extends BaseLLMProvider {
|
|
159
160
|
models: any;
|
|
160
161
|
defaultModel: any;
|
|
162
|
+
client: Anthropic;
|
|
161
163
|
chat(userMessage: any, systemPrompt?: string, options?: {}): Promise<{
|
|
162
|
-
text:
|
|
164
|
+
text: string;
|
|
163
165
|
}>;
|
|
164
166
|
chatCompletion(messages: any, systemPrompt: any, tools?: any, options?: {}): Promise<{
|
|
165
|
-
content:
|
|
166
|
-
tool_calls:
|
|
167
|
+
content: string;
|
|
168
|
+
tool_calls: {
|
|
169
|
+
id: string;
|
|
170
|
+
type: string;
|
|
171
|
+
function: {
|
|
172
|
+
name: string;
|
|
173
|
+
arguments: string;
|
|
174
|
+
};
|
|
175
|
+
}[];
|
|
167
176
|
finishReason: string;
|
|
168
|
-
_rawFinishReason:
|
|
177
|
+
_rawFinishReason: Anthropic.Messages.StopReason;
|
|
169
178
|
_responseFormat: any;
|
|
170
179
|
usage: {
|
|
171
|
-
prompt_tokens:
|
|
172
|
-
completion_tokens:
|
|
173
|
-
total_tokens:
|
|
180
|
+
prompt_tokens: number;
|
|
181
|
+
completion_tokens: number;
|
|
182
|
+
total_tokens: number;
|
|
174
183
|
};
|
|
175
184
|
model: any;
|
|
176
185
|
}>;
|
|
177
186
|
_chatCompletionWithModel(messages: any, systemPrompt: any, tools: any, modelName: any, maxTokens: any, temperature: any, options?: {}): Promise<{
|
|
178
|
-
content:
|
|
179
|
-
tool_calls:
|
|
187
|
+
content: string;
|
|
188
|
+
tool_calls: {
|
|
189
|
+
id: string;
|
|
190
|
+
type: string;
|
|
191
|
+
function: {
|
|
192
|
+
name: string;
|
|
193
|
+
arguments: string;
|
|
194
|
+
};
|
|
195
|
+
}[];
|
|
180
196
|
finishReason: string;
|
|
181
|
-
_rawFinishReason:
|
|
197
|
+
_rawFinishReason: Anthropic.Messages.StopReason;
|
|
182
198
|
_responseFormat: any;
|
|
183
199
|
usage: {
|
|
184
|
-
prompt_tokens:
|
|
185
|
-
completion_tokens:
|
|
186
|
-
total_tokens:
|
|
200
|
+
prompt_tokens: number;
|
|
201
|
+
completion_tokens: number;
|
|
202
|
+
total_tokens: number;
|
|
187
203
|
};
|
|
188
204
|
model: any;
|
|
189
205
|
}>;
|
|
206
|
+
/**
|
|
207
|
+
* Convert standard {role, content, tool_calls, tool_call_id} messages
|
|
208
|
+
* to Anthropic's message format.
|
|
209
|
+
*
|
|
210
|
+
* Key differences from OpenAI:
|
|
211
|
+
* - No 'system' role in messages array (handled via top-level `system` param)
|
|
212
|
+
* - Tool results go as role:'user' with content type:'tool_result'
|
|
213
|
+
* - Assistant messages with tool calls use an array of content blocks
|
|
214
|
+
* - Roles must strictly alternate user/assistant
|
|
215
|
+
*/
|
|
216
|
+
_formatMessages(messages: any): {
|
|
217
|
+
role: string;
|
|
218
|
+
content: any;
|
|
219
|
+
}[];
|
|
220
|
+
/**
|
|
221
|
+
* Convert OpenAI-style tool definitions to Anthropic format.
|
|
222
|
+
* OpenAI: { type: 'function', function: { name, description, parameters } }
|
|
223
|
+
* Anthropic: { name, description, input_schema }
|
|
224
|
+
*/
|
|
225
|
+
_formatTools(tools: any): any;
|
|
226
|
+
executeTools(tool_calls: any, messages: any, tenantId: any, toolImplementations: any, env: any): Promise<void>;
|
|
190
227
|
_getModelForTier(tier: any): any;
|
|
191
228
|
_shouldAutoParse(options: any): boolean;
|
|
192
229
|
_safeJsonParse(text: any): any;
|
|
@@ -365,6 +402,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
365
402
|
getConfig(tenantId: any, env: any): Promise<{
|
|
366
403
|
provider: any;
|
|
367
404
|
apiKey: any;
|
|
405
|
+
baseURL: any;
|
|
368
406
|
project: any;
|
|
369
407
|
location: any;
|
|
370
408
|
models: any;
|
|
@@ -373,6 +411,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
373
411
|
} | {
|
|
374
412
|
provider: any;
|
|
375
413
|
apiKey: any;
|
|
414
|
+
baseURL: any;
|
|
376
415
|
models: any;
|
|
377
416
|
temperature: number;
|
|
378
417
|
maxTokens: number;
|
|
@@ -384,6 +423,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
384
423
|
provider: any;
|
|
385
424
|
models: any;
|
|
386
425
|
apiKey: any;
|
|
426
|
+
baseURL: any;
|
|
387
427
|
project: any;
|
|
388
428
|
location: any;
|
|
389
429
|
temperature: number;
|
|
@@ -394,6 +434,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
394
434
|
_getSystemConfig(env: any): {
|
|
395
435
|
provider: any;
|
|
396
436
|
apiKey: any;
|
|
437
|
+
baseURL: any;
|
|
397
438
|
project: any;
|
|
398
439
|
location: any;
|
|
399
440
|
models: any;
|
|
@@ -402,6 +443,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
402
443
|
} | {
|
|
403
444
|
provider: any;
|
|
404
445
|
apiKey: any;
|
|
446
|
+
baseURL: any;
|
|
405
447
|
models: any;
|
|
406
448
|
temperature: number;
|
|
407
449
|
maxTokens: number;
|
|
@@ -472,6 +514,7 @@ declare class ConfigManager {
|
|
|
472
514
|
static getConfig(tenantId: any, env: any): Promise<{
|
|
473
515
|
provider: any;
|
|
474
516
|
apiKey: any;
|
|
517
|
+
baseURL: any;
|
|
475
518
|
project: any;
|
|
476
519
|
location: any;
|
|
477
520
|
models: any;
|
|
@@ -480,6 +523,7 @@ declare class ConfigManager {
|
|
|
480
523
|
} | {
|
|
481
524
|
provider: any;
|
|
482
525
|
apiKey: any;
|
|
526
|
+
baseURL: any;
|
|
483
527
|
models: any;
|
|
484
528
|
temperature: number;
|
|
485
529
|
maxTokens: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
3
|
import * as _google_genai from '@google/genai';
|
|
3
4
|
import { GoogleGenAI } from '@google/genai';
|
|
4
5
|
|
|
@@ -158,35 +159,71 @@ declare class OpenAIProvider extends BaseLLMProvider {
|
|
|
158
159
|
declare class AnthropicProvider extends BaseLLMProvider {
|
|
159
160
|
models: any;
|
|
160
161
|
defaultModel: any;
|
|
162
|
+
client: Anthropic;
|
|
161
163
|
chat(userMessage: any, systemPrompt?: string, options?: {}): Promise<{
|
|
162
|
-
text:
|
|
164
|
+
text: string;
|
|
163
165
|
}>;
|
|
164
166
|
chatCompletion(messages: any, systemPrompt: any, tools?: any, options?: {}): Promise<{
|
|
165
|
-
content:
|
|
166
|
-
tool_calls:
|
|
167
|
+
content: string;
|
|
168
|
+
tool_calls: {
|
|
169
|
+
id: string;
|
|
170
|
+
type: string;
|
|
171
|
+
function: {
|
|
172
|
+
name: string;
|
|
173
|
+
arguments: string;
|
|
174
|
+
};
|
|
175
|
+
}[];
|
|
167
176
|
finishReason: string;
|
|
168
|
-
_rawFinishReason:
|
|
177
|
+
_rawFinishReason: Anthropic.Messages.StopReason;
|
|
169
178
|
_responseFormat: any;
|
|
170
179
|
usage: {
|
|
171
|
-
prompt_tokens:
|
|
172
|
-
completion_tokens:
|
|
173
|
-
total_tokens:
|
|
180
|
+
prompt_tokens: number;
|
|
181
|
+
completion_tokens: number;
|
|
182
|
+
total_tokens: number;
|
|
174
183
|
};
|
|
175
184
|
model: any;
|
|
176
185
|
}>;
|
|
177
186
|
_chatCompletionWithModel(messages: any, systemPrompt: any, tools: any, modelName: any, maxTokens: any, temperature: any, options?: {}): Promise<{
|
|
178
|
-
content:
|
|
179
|
-
tool_calls:
|
|
187
|
+
content: string;
|
|
188
|
+
tool_calls: {
|
|
189
|
+
id: string;
|
|
190
|
+
type: string;
|
|
191
|
+
function: {
|
|
192
|
+
name: string;
|
|
193
|
+
arguments: string;
|
|
194
|
+
};
|
|
195
|
+
}[];
|
|
180
196
|
finishReason: string;
|
|
181
|
-
_rawFinishReason:
|
|
197
|
+
_rawFinishReason: Anthropic.Messages.StopReason;
|
|
182
198
|
_responseFormat: any;
|
|
183
199
|
usage: {
|
|
184
|
-
prompt_tokens:
|
|
185
|
-
completion_tokens:
|
|
186
|
-
total_tokens:
|
|
200
|
+
prompt_tokens: number;
|
|
201
|
+
completion_tokens: number;
|
|
202
|
+
total_tokens: number;
|
|
187
203
|
};
|
|
188
204
|
model: any;
|
|
189
205
|
}>;
|
|
206
|
+
/**
|
|
207
|
+
* Convert standard {role, content, tool_calls, tool_call_id} messages
|
|
208
|
+
* to Anthropic's message format.
|
|
209
|
+
*
|
|
210
|
+
* Key differences from OpenAI:
|
|
211
|
+
* - No 'system' role in messages array (handled via top-level `system` param)
|
|
212
|
+
* - Tool results go as role:'user' with content type:'tool_result'
|
|
213
|
+
* - Assistant messages with tool calls use an array of content blocks
|
|
214
|
+
* - Roles must strictly alternate user/assistant
|
|
215
|
+
*/
|
|
216
|
+
_formatMessages(messages: any): {
|
|
217
|
+
role: string;
|
|
218
|
+
content: any;
|
|
219
|
+
}[];
|
|
220
|
+
/**
|
|
221
|
+
* Convert OpenAI-style tool definitions to Anthropic format.
|
|
222
|
+
* OpenAI: { type: 'function', function: { name, description, parameters } }
|
|
223
|
+
* Anthropic: { name, description, input_schema }
|
|
224
|
+
*/
|
|
225
|
+
_formatTools(tools: any): any;
|
|
226
|
+
executeTools(tool_calls: any, messages: any, tenantId: any, toolImplementations: any, env: any): Promise<void>;
|
|
190
227
|
_getModelForTier(tier: any): any;
|
|
191
228
|
_shouldAutoParse(options: any): boolean;
|
|
192
229
|
_safeJsonParse(text: any): any;
|
|
@@ -365,6 +402,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
365
402
|
getConfig(tenantId: any, env: any): Promise<{
|
|
366
403
|
provider: any;
|
|
367
404
|
apiKey: any;
|
|
405
|
+
baseURL: any;
|
|
368
406
|
project: any;
|
|
369
407
|
location: any;
|
|
370
408
|
models: any;
|
|
@@ -373,6 +411,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
373
411
|
} | {
|
|
374
412
|
provider: any;
|
|
375
413
|
apiKey: any;
|
|
414
|
+
baseURL: any;
|
|
376
415
|
models: any;
|
|
377
416
|
temperature: number;
|
|
378
417
|
maxTokens: number;
|
|
@@ -384,6 +423,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
384
423
|
provider: any;
|
|
385
424
|
models: any;
|
|
386
425
|
apiKey: any;
|
|
426
|
+
baseURL: any;
|
|
387
427
|
project: any;
|
|
388
428
|
location: any;
|
|
389
429
|
temperature: number;
|
|
@@ -394,6 +434,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
394
434
|
_getSystemConfig(env: any): {
|
|
395
435
|
provider: any;
|
|
396
436
|
apiKey: any;
|
|
437
|
+
baseURL: any;
|
|
397
438
|
project: any;
|
|
398
439
|
location: any;
|
|
399
440
|
models: any;
|
|
@@ -402,6 +443,7 @@ declare class DefaultConfigProvider extends BaseConfigProvider$1 {
|
|
|
402
443
|
} | {
|
|
403
444
|
provider: any;
|
|
404
445
|
apiKey: any;
|
|
446
|
+
baseURL: any;
|
|
405
447
|
models: any;
|
|
406
448
|
temperature: number;
|
|
407
449
|
maxTokens: number;
|
|
@@ -472,6 +514,7 @@ declare class ConfigManager {
|
|
|
472
514
|
static getConfig(tenantId: any, env: any): Promise<{
|
|
473
515
|
provider: any;
|
|
474
516
|
apiKey: any;
|
|
517
|
+
baseURL: any;
|
|
475
518
|
project: any;
|
|
476
519
|
location: any;
|
|
477
520
|
models: any;
|
|
@@ -480,6 +523,7 @@ declare class ConfigManager {
|
|
|
480
523
|
} | {
|
|
481
524
|
provider: any;
|
|
482
525
|
apiKey: any;
|
|
526
|
+
baseURL: any;
|
|
483
527
|
models: any;
|
|
484
528
|
temperature: number;
|
|
485
529
|
maxTokens: number;
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
74
74
|
models: MODEL_CONFIGS[tenantConfig.provider],
|
|
75
75
|
// for backward compatibility, api_key is deprecated and shall never be used!!
|
|
76
76
|
apiKey: tenantConfig.apiKey || tenantConfig.api_key,
|
|
77
|
+
baseURL: tenantConfig.baseURL || tenantConfig.base_url || void 0,
|
|
77
78
|
project: tenantConfig.project,
|
|
78
79
|
location: tenantConfig.location,
|
|
79
80
|
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
@@ -87,9 +88,11 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
87
88
|
const provider = ((_a = env.LLM_PROVIDER) == null ? void 0 : _a.toLowerCase()) || "openai";
|
|
88
89
|
const providerDefaults = MODEL_CONFIGS[provider] || MODEL_CONFIGS["openai"];
|
|
89
90
|
let apiKey;
|
|
91
|
+
let baseURL;
|
|
90
92
|
let models = { ...providerDefaults };
|
|
91
93
|
if (provider === "openai") {
|
|
92
94
|
apiKey = env.OPENAI_API_KEY;
|
|
95
|
+
baseURL = env.OPENAI_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
93
96
|
models = {
|
|
94
97
|
default: env.OPENAI_MODEL || providerDefaults.default,
|
|
95
98
|
edge: env.OPENAI_MODEL_EDGE || providerDefaults.edge,
|
|
@@ -99,6 +102,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
99
102
|
};
|
|
100
103
|
} else if (provider === "gemini") {
|
|
101
104
|
apiKey = env.GEMINI_API_KEY;
|
|
105
|
+
baseURL = env.GEMINI_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
102
106
|
models = {
|
|
103
107
|
default: env.GEMINI_MODEL || providerDefaults.default,
|
|
104
108
|
edge: env.GEMINI_MODEL_EDGE || providerDefaults.edge,
|
|
@@ -108,8 +112,19 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
108
112
|
image: env.GEMINI_IMAGE_MODEL || providerDefaults.image,
|
|
109
113
|
video: env.GEMINI_VIDEO_MODEL || providerDefaults.video
|
|
110
114
|
};
|
|
115
|
+
} else if (provider === "anthropic") {
|
|
116
|
+
apiKey = env.ANTHROPIC_API_KEY;
|
|
117
|
+
baseURL = env.ANTHROPIC_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
118
|
+
models = {
|
|
119
|
+
default: env.ANTHROPIC_MODEL || providerDefaults.default,
|
|
120
|
+
edge: env.ANTHROPIC_MODEL_EDGE || providerDefaults.edge,
|
|
121
|
+
fast: env.ANTHROPIC_MODEL_FAST || providerDefaults.fast,
|
|
122
|
+
cost: env.ANTHROPIC_MODEL_COST || providerDefaults.cost,
|
|
123
|
+
free: env.ANTHROPIC_MODEL_FREE || providerDefaults.free
|
|
124
|
+
};
|
|
111
125
|
} else if (provider === "vertex") {
|
|
112
126
|
apiKey = env.VERTEX_API_KEY;
|
|
127
|
+
baseURL = env.VERTEX_BASE_URL || env.LLM_BASE_URL || void 0;
|
|
113
128
|
const project = env.VERTEX_PROJECT || env.GOOGLE_CLOUD_PROJECT;
|
|
114
129
|
const location = env.VERTEX_LOCATION || env.GOOGLE_CLOUD_LOCATION || "us-central1";
|
|
115
130
|
models = {
|
|
@@ -124,6 +139,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
124
139
|
return {
|
|
125
140
|
provider,
|
|
126
141
|
apiKey,
|
|
142
|
+
baseURL,
|
|
127
143
|
project,
|
|
128
144
|
location,
|
|
129
145
|
models,
|
|
@@ -134,6 +150,7 @@ var DefaultConfigProvider = class extends BaseConfigProvider {
|
|
|
134
150
|
return {
|
|
135
151
|
provider,
|
|
136
152
|
apiKey,
|
|
153
|
+
baseURL,
|
|
137
154
|
models,
|
|
138
155
|
temperature: parseFloat(env.DEFAULT_TEMPERATURE || "0.7"),
|
|
139
156
|
maxTokens: parseInt(env.DEFAULT_MAX_TOKENS || "65536")
|
|
@@ -397,7 +414,11 @@ function extractTextAndJson(input) {
|
|
|
397
414
|
var OpenAIProvider = class extends BaseLLMProvider {
|
|
398
415
|
constructor(config) {
|
|
399
416
|
super(config);
|
|
400
|
-
|
|
417
|
+
const clientOptions = { apiKey: config.apiKey };
|
|
418
|
+
if (config.baseURL) {
|
|
419
|
+
clientOptions.baseURL = config.baseURL;
|
|
420
|
+
}
|
|
421
|
+
this.client = new OpenAI(clientOptions);
|
|
401
422
|
this.models = config.models;
|
|
402
423
|
this.defaultModel = config.models.default;
|
|
403
424
|
}
|
|
@@ -579,11 +600,13 @@ var GoogleProvider = class extends BaseLLMProvider {
|
|
|
579
600
|
this.models = config.models;
|
|
580
601
|
this.defaultModel = config.models.default;
|
|
581
602
|
this._pendingOperations = /* @__PURE__ */ new Map();
|
|
603
|
+
const httpOptions = config.baseURL ? { baseUrl: config.baseURL } : void 0;
|
|
582
604
|
if (config.provider === "vertex") {
|
|
583
605
|
if (config.apiKey) {
|
|
584
606
|
this.client = new GoogleGenAI({
|
|
585
607
|
vertexai: true,
|
|
586
|
-
apiKey: config.apiKey
|
|
608
|
+
apiKey: config.apiKey,
|
|
609
|
+
...httpOptions ? { httpOptions } : {}
|
|
587
610
|
});
|
|
588
611
|
} else {
|
|
589
612
|
if (!config.project) {
|
|
@@ -592,12 +615,14 @@ var GoogleProvider = class extends BaseLLMProvider {
|
|
|
592
615
|
this.client = new GoogleGenAI({
|
|
593
616
|
vertexai: true,
|
|
594
617
|
project: config.project,
|
|
595
|
-
location: config.location || "us-central1"
|
|
618
|
+
location: config.location || "us-central1",
|
|
619
|
+
...httpOptions ? { httpOptions } : {}
|
|
596
620
|
});
|
|
597
621
|
}
|
|
598
622
|
} else {
|
|
599
623
|
this.client = new GoogleGenAI({
|
|
600
|
-
apiKey: config.apiKey
|
|
624
|
+
apiKey: config.apiKey,
|
|
625
|
+
...httpOptions ? { httpOptions } : {}
|
|
601
626
|
});
|
|
602
627
|
}
|
|
603
628
|
}
|
|
@@ -1149,11 +1174,17 @@ ${prompt}` : prompt;
|
|
|
1149
1174
|
};
|
|
1150
1175
|
|
|
1151
1176
|
// src/llm/providers/anthropic-provider.js
|
|
1177
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
1152
1178
|
var AnthropicProvider = class extends BaseLLMProvider {
|
|
1153
1179
|
constructor(config) {
|
|
1154
1180
|
super(config);
|
|
1155
1181
|
this.models = config.models || {};
|
|
1156
1182
|
this.defaultModel = this.models.default || "claude-3-5-sonnet-20241022";
|
|
1183
|
+
const clientOptions = { apiKey: config.apiKey || "" };
|
|
1184
|
+
if (config.baseURL) {
|
|
1185
|
+
clientOptions.baseURL = config.baseURL;
|
|
1186
|
+
}
|
|
1187
|
+
this.client = new Anthropic(clientOptions);
|
|
1157
1188
|
}
|
|
1158
1189
|
async chat(userMessage, systemPrompt = "", options = {}) {
|
|
1159
1190
|
var _a;
|
|
@@ -1190,82 +1221,69 @@ var AnthropicProvider = class extends BaseLLMProvider {
|
|
|
1190
1221
|
);
|
|
1191
1222
|
}
|
|
1192
1223
|
async _chatCompletionWithModel(messages, systemPrompt, tools, modelName, maxTokens, temperature, options = {}) {
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const formattedMessages = messages.map((m) => ({
|
|
1196
|
-
role: m.role === "assistant" ? "assistant" : "user",
|
|
1197
|
-
content: m.content
|
|
1198
|
-
}));
|
|
1199
|
-
const headers = {
|
|
1200
|
-
"Content-Type": "application/json",
|
|
1201
|
-
"x-api-key": this.config.apiKey || "",
|
|
1202
|
-
"anthropic-version": "2023-06-01"
|
|
1203
|
-
};
|
|
1204
|
-
const payload = {
|
|
1205
|
-
model: modelName,
|
|
1206
|
-
messages: formattedMessages,
|
|
1207
|
-
max_tokens: maxTokens,
|
|
1208
|
-
temperature
|
|
1209
|
-
};
|
|
1210
|
-
if (systemPrompt) {
|
|
1211
|
-
payload.system = systemPrompt;
|
|
1212
|
-
}
|
|
1224
|
+
var _a, _b, _c, _d;
|
|
1225
|
+
let effectiveSystem = systemPrompt || void 0;
|
|
1213
1226
|
if (options.responseFormat) {
|
|
1214
1227
|
const formatType = typeof options.responseFormat === "string" ? options.responseFormat : options.responseFormat.type;
|
|
1215
1228
|
if (formatType === "json" || formatType === "json_schema") {
|
|
1216
|
-
const
|
|
1229
|
+
const schema = options.responseFormat.schema || options.responseSchema;
|
|
1230
|
+
const schemaText = schema ? `
|
|
1217
1231
|
|
|
1218
|
-
Your output must comply strictly with this JSON Schema: ${JSON.stringify(
|
|
1232
|
+
Your output must comply strictly with this JSON Schema: ${JSON.stringify(schema)}` : "";
|
|
1219
1233
|
const jsonPrompt = `IMPORTANT: You must respond ONLY in raw JSON format. Do not write any conversational text, explanations, or markdown code blocks (e.g. do NOT wrap your answer in \`\`\`json ... \`\`\`). Your response must start with '{' and end with '}'.${schemaText}`;
|
|
1220
|
-
|
|
1221
|
-
payload.system = `${payload.system}
|
|
1234
|
+
effectiveSystem = effectiveSystem ? `${effectiveSystem}
|
|
1222
1235
|
|
|
1223
|
-
${jsonPrompt}
|
|
1224
|
-
} else {
|
|
1225
|
-
payload.system = jsonPrompt;
|
|
1226
|
-
}
|
|
1236
|
+
${jsonPrompt}` : jsonPrompt;
|
|
1227
1237
|
}
|
|
1228
1238
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1239
|
+
const anthropicMessages = this._formatMessages(messages);
|
|
1240
|
+
const anthropicTools = tools ? this._formatTools(tools) : void 0;
|
|
1241
|
+
const requestParams = {
|
|
1242
|
+
model: modelName,
|
|
1243
|
+
max_tokens: maxTokens,
|
|
1244
|
+
temperature,
|
|
1245
|
+
messages: anthropicMessages,
|
|
1246
|
+
...effectiveSystem ? { system: effectiveSystem } : {},
|
|
1247
|
+
...anthropicTools ? { tools: anthropicTools } : {}
|
|
1248
|
+
};
|
|
1249
|
+
let response;
|
|
1231
1250
|
try {
|
|
1232
|
-
|
|
1233
|
-
method: "POST",
|
|
1234
|
-
headers,
|
|
1235
|
-
body: JSON.stringify(payload)
|
|
1236
|
-
});
|
|
1237
|
-
responseText = await res.text();
|
|
1238
|
-
if (!res.ok) {
|
|
1239
|
-
console.error(`[AnthropicProvider] API request failed with status ${res.status}:`, responseText);
|
|
1240
|
-
throw new LLMServiceException(`Anthropic API Error: ${responseText}`, res.status);
|
|
1241
|
-
}
|
|
1242
|
-
responseData = JSON.parse(responseText);
|
|
1251
|
+
response = await this.client.messages.create(requestParams);
|
|
1243
1252
|
} catch (error) {
|
|
1244
1253
|
console.error(`[AnthropicProvider] request failed (API Key: ${this._getMaskedApiKey()}):`, error);
|
|
1245
1254
|
throw error;
|
|
1246
1255
|
}
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1256
|
+
const textBlocks = response.content.filter((b) => b.type === "text");
|
|
1257
|
+
const toolUseBlocks = response.content.filter((b) => b.type === "tool_use");
|
|
1258
|
+
const contentText = textBlocks.map((b) => b.text).join("");
|
|
1259
|
+
if (!contentText && toolUseBlocks.length === 0) {
|
|
1260
|
+
console.error("[AnthropicProvider] Model returned empty response (no text, no tool calls)");
|
|
1251
1261
|
throw new LLMServiceException(
|
|
1252
1262
|
"Model returned empty response. This usually means the prompt or schema is confusing the model.",
|
|
1253
1263
|
500
|
|
1254
1264
|
);
|
|
1255
1265
|
}
|
|
1256
|
-
const
|
|
1266
|
+
const toolCalls = toolUseBlocks.length > 0 ? toolUseBlocks.map((block) => ({
|
|
1267
|
+
id: block.id,
|
|
1268
|
+
type: "function",
|
|
1269
|
+
function: {
|
|
1270
|
+
name: block.name,
|
|
1271
|
+
// Anthropic returns args as a parsed object; serialize to string to match OpenAI shape
|
|
1272
|
+
arguments: JSON.stringify(block.input || {})
|
|
1273
|
+
}
|
|
1274
|
+
})) : null;
|
|
1275
|
+
const rawFinishReason = response.stop_reason;
|
|
1257
1276
|
const normalizedFinishReason = this.normalizeFinishReason(rawFinishReason);
|
|
1258
1277
|
const result = {
|
|
1259
1278
|
content: contentText,
|
|
1260
|
-
tool_calls:
|
|
1261
|
-
// REST client tool calls mapping not required for playbook studio
|
|
1279
|
+
tool_calls: toolCalls,
|
|
1262
1280
|
finishReason: normalizedFinishReason,
|
|
1263
1281
|
_rawFinishReason: rawFinishReason,
|
|
1264
1282
|
_responseFormat: options.responseFormat,
|
|
1265
1283
|
usage: {
|
|
1266
|
-
prompt_tokens:
|
|
1267
|
-
completion_tokens:
|
|
1268
|
-
total_tokens:
|
|
1284
|
+
prompt_tokens: ((_a = response.usage) == null ? void 0 : _a.input_tokens) || 0,
|
|
1285
|
+
completion_tokens: ((_b = response.usage) == null ? void 0 : _b.output_tokens) || 0,
|
|
1286
|
+
total_tokens: (((_c = response.usage) == null ? void 0 : _c.input_tokens) || 0) + (((_d = response.usage) == null ? void 0 : _d.output_tokens) || 0)
|
|
1269
1287
|
},
|
|
1270
1288
|
model: modelName
|
|
1271
1289
|
};
|
|
@@ -1274,11 +1292,110 @@ ${jsonPrompt}`;
|
|
|
1274
1292
|
}
|
|
1275
1293
|
return result;
|
|
1276
1294
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1295
|
+
/**
|
|
1296
|
+
* Convert standard {role, content, tool_calls, tool_call_id} messages
|
|
1297
|
+
* to Anthropic's message format.
|
|
1298
|
+
*
|
|
1299
|
+
* Key differences from OpenAI:
|
|
1300
|
+
* - No 'system' role in messages array (handled via top-level `system` param)
|
|
1301
|
+
* - Tool results go as role:'user' with content type:'tool_result'
|
|
1302
|
+
* - Assistant messages with tool calls use an array of content blocks
|
|
1303
|
+
* - Roles must strictly alternate user/assistant
|
|
1304
|
+
*/
|
|
1305
|
+
_formatMessages(messages) {
|
|
1306
|
+
const result = [];
|
|
1307
|
+
for (const msg of messages) {
|
|
1308
|
+
if (msg.role === "system") {
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
if (msg.role === "tool") {
|
|
1312
|
+
const toolResultBlock = {
|
|
1313
|
+
type: "tool_result",
|
|
1314
|
+
tool_use_id: msg.tool_call_id,
|
|
1315
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1316
|
+
};
|
|
1317
|
+
const last = result[result.length - 1];
|
|
1318
|
+
if (last && last.role === "user" && Array.isArray(last.content)) {
|
|
1319
|
+
last.content.push(toolResultBlock);
|
|
1320
|
+
} else {
|
|
1321
|
+
result.push({ role: "user", content: [toolResultBlock] });
|
|
1322
|
+
}
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
if (msg.role === "assistant") {
|
|
1326
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
1327
|
+
const contentBlocks = [];
|
|
1328
|
+
if (msg.content) {
|
|
1329
|
+
contentBlocks.push({ type: "text", text: msg.content });
|
|
1330
|
+
}
|
|
1331
|
+
for (const tc of msg.tool_calls) {
|
|
1332
|
+
let inputArgs = tc.function.arguments;
|
|
1333
|
+
if (typeof inputArgs === "string") {
|
|
1334
|
+
try {
|
|
1335
|
+
inputArgs = JSON.parse(inputArgs);
|
|
1336
|
+
} catch (_) {
|
|
1337
|
+
inputArgs = {};
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
contentBlocks.push({
|
|
1341
|
+
type: "tool_use",
|
|
1342
|
+
id: tc.id,
|
|
1343
|
+
name: tc.function.name,
|
|
1344
|
+
input: inputArgs
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
result.push({ role: "assistant", content: contentBlocks });
|
|
1348
|
+
} else {
|
|
1349
|
+
result.push({ role: "assistant", content: msg.content || "" });
|
|
1350
|
+
}
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
result.push({ role: "user", content: msg.content || "" });
|
|
1280
1354
|
}
|
|
1281
|
-
return
|
|
1355
|
+
return result;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Convert OpenAI-style tool definitions to Anthropic format.
|
|
1359
|
+
* OpenAI: { type: 'function', function: { name, description, parameters } }
|
|
1360
|
+
* Anthropic: { name, description, input_schema }
|
|
1361
|
+
*/
|
|
1362
|
+
_formatTools(tools) {
|
|
1363
|
+
return tools.map((tool) => {
|
|
1364
|
+
const fn = tool.function || tool;
|
|
1365
|
+
return {
|
|
1366
|
+
name: fn.name,
|
|
1367
|
+
description: fn.description || "",
|
|
1368
|
+
input_schema: fn.parameters || fn.input_schema || { type: "object", properties: {} }
|
|
1369
|
+
};
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
async executeTools(tool_calls, messages, tenantId, toolImplementations, env) {
|
|
1373
|
+
const toolResults = await Promise.all(
|
|
1374
|
+
tool_calls.map(async (toolCall) => {
|
|
1375
|
+
const toolName = toolCall.function.name;
|
|
1376
|
+
const tool = toolImplementations[toolName];
|
|
1377
|
+
console.log(`[Tool Call] ${toolName} with arguments:`, toolCall.function.arguments);
|
|
1378
|
+
if (!tool) {
|
|
1379
|
+
console.error(`[Tool Error] Tool '${toolName}' not found`);
|
|
1380
|
+
return { tool_call_id: toolCall.id, output: JSON.stringify({ error: `Tool '${toolName}' not found.` }) };
|
|
1381
|
+
}
|
|
1382
|
+
try {
|
|
1383
|
+
const output = await tool(toolCall.function.arguments, { env, tenantId });
|
|
1384
|
+
const preview = typeof output === "string" ? output.length > 200 ? output.substring(0, 200) + "..." : output : JSON.stringify(output).substring(0, 200) + "...";
|
|
1385
|
+
console.log(`[Tool Result] ${toolName} returned:`, preview);
|
|
1386
|
+
return { tool_call_id: toolCall.id, output };
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
console.error(`[Tool Error] ${toolName} failed:`, error.message);
|
|
1389
|
+
return { tool_call_id: toolCall.id, output: JSON.stringify({ error: `Error executing tool '${toolName}': ${error.message}` }) };
|
|
1390
|
+
}
|
|
1391
|
+
})
|
|
1392
|
+
);
|
|
1393
|
+
toolResults.forEach(
|
|
1394
|
+
(result) => messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.output })
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
_getModelForTier(tier) {
|
|
1398
|
+
return this.models[tier] || this.defaultModel;
|
|
1282
1399
|
}
|
|
1283
1400
|
_shouldAutoParse(options) {
|
|
1284
1401
|
if (!options.responseFormat) return false;
|
|
@@ -1299,15 +1416,10 @@ ${jsonPrompt}`;
|
|
|
1299
1416
|
}
|
|
1300
1417
|
normalizeFinishReason(providerReason) {
|
|
1301
1418
|
const lower = (providerReason || "").toLowerCase();
|
|
1302
|
-
if (lower === "end_turn")
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
if (lower === "
|
|
1306
|
-
return "truncated";
|
|
1307
|
-
}
|
|
1308
|
-
if (lower === "stop_sequence") {
|
|
1309
|
-
return "completed";
|
|
1310
|
-
}
|
|
1419
|
+
if (lower === "end_turn") return "completed";
|
|
1420
|
+
if (lower === "max_tokens") return "truncated";
|
|
1421
|
+
if (lower === "stop_sequence") return "completed";
|
|
1422
|
+
if (lower === "tool_use") return "tool_call";
|
|
1311
1423
|
return super.normalizeFinishReason(providerReason);
|
|
1312
1424
|
}
|
|
1313
1425
|
};
|