agents-library 1.1.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.
Potentially problematic release.
This version of agents-library might be problematic. Click here for more details.
- package/dist/base-agent.d.ts +172 -0
- package/dist/base-agent.d.ts.map +1 -0
- package/dist/base-agent.js +255 -0
- package/dist/base-agent.js.map +1 -0
- package/dist/base-bot.d.ts +282 -0
- package/dist/base-bot.d.ts.map +1 -0
- package/dist/base-bot.js +375 -0
- package/dist/base-bot.js.map +1 -0
- package/dist/common/result.d.ts +51 -0
- package/dist/common/result.d.ts.map +1 -0
- package/dist/common/result.js +45 -0
- package/dist/common/result.js.map +1 -0
- package/dist/common/types.d.ts +57 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/common/types.js +42 -0
- package/dist/common/types.js.map +1 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/kadi-event-publisher.d.ts +163 -0
- package/dist/kadi-event-publisher.d.ts.map +1 -0
- package/dist/kadi-event-publisher.js +286 -0
- package/dist/kadi-event-publisher.js.map +1 -0
- package/dist/memory/arcadedb-adapter.d.ts +159 -0
- package/dist/memory/arcadedb-adapter.d.ts.map +1 -0
- package/dist/memory/arcadedb-adapter.js +314 -0
- package/dist/memory/arcadedb-adapter.js.map +1 -0
- package/dist/memory/file-storage-adapter.d.ts +122 -0
- package/dist/memory/file-storage-adapter.d.ts.map +1 -0
- package/dist/memory/file-storage-adapter.js +352 -0
- package/dist/memory/file-storage-adapter.js.map +1 -0
- package/dist/memory/memory-service.d.ts +208 -0
- package/dist/memory/memory-service.d.ts.map +1 -0
- package/dist/memory/memory-service.js +410 -0
- package/dist/memory/memory-service.js.map +1 -0
- package/dist/memory/types.d.ts +126 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +41 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/producer-tool-utils.d.ts +474 -0
- package/dist/producer-tool-utils.d.ts.map +1 -0
- package/dist/producer-tool-utils.js +664 -0
- package/dist/producer-tool-utils.js.map +1 -0
- package/dist/providers/anthropic-provider.d.ts +160 -0
- package/dist/providers/anthropic-provider.d.ts.map +1 -0
- package/dist/providers/anthropic-provider.js +527 -0
- package/dist/providers/anthropic-provider.js.map +1 -0
- package/dist/providers/model-manager-provider.d.ts +91 -0
- package/dist/providers/model-manager-provider.d.ts.map +1 -0
- package/dist/providers/model-manager-provider.js +355 -0
- package/dist/providers/model-manager-provider.js.map +1 -0
- package/dist/providers/provider-manager.d.ts +111 -0
- package/dist/providers/provider-manager.d.ts.map +1 -0
- package/dist/providers/provider-manager.js +337 -0
- package/dist/providers/provider-manager.js.map +1 -0
- package/dist/providers/types.d.ts +145 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +23 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/shadow-agent-factory.d.ts +623 -0
- package/dist/shadow-agent-factory.d.ts.map +1 -0
- package/dist/shadow-agent-factory.js +1117 -0
- package/dist/shadow-agent-factory.js.map +1 -0
- package/dist/types/agent-config.d.ts +307 -0
- package/dist/types/agent-config.d.ts.map +1 -0
- package/dist/types/agent-config.js +15 -0
- package/dist/types/agent-config.js.map +1 -0
- package/dist/types/event-schemas.d.ts +358 -0
- package/dist/types/event-schemas.d.ts.map +1 -0
- package/dist/types/event-schemas.js +188 -0
- package/dist/types/event-schemas.js.map +1 -0
- package/dist/types/tool-schemas.d.ts +498 -0
- package/dist/types/tool-schemas.d.ts.map +1 -0
- package/dist/types/tool-schemas.js +457 -0
- package/dist/types/tool-schemas.js.map +1 -0
- package/dist/utils/logger.d.ts +135 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +205 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/timer.d.ts +186 -0
- package/dist/utils/timer.d.ts.map +1 -0
- package/dist/utils/timer.js +211 -0
- package/dist/utils/timer.js.map +1 -0
- package/dist/worker-agent-factory.d.ts +688 -0
- package/dist/worker-agent-factory.d.ts.map +1 -0
- package/dist/worker-agent-factory.js +1517 -0
- package/dist/worker-agent-factory.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Provider Implementation
|
|
3
|
+
*
|
|
4
|
+
* Implements LLMProvider interface for Anthropic Claude models.
|
|
5
|
+
* Provides chat completion, streaming, health checks, and model discovery.
|
|
6
|
+
*/
|
|
7
|
+
import type { LLMProvider, Message, ChatOptions, ProviderError } from './types.js';
|
|
8
|
+
import type { Result } from '../common/result.js';
|
|
9
|
+
/**
|
|
10
|
+
* Anthropic Claude Provider
|
|
11
|
+
*
|
|
12
|
+
* Wraps the Anthropic SDK to provide standardized LLM provider interface.
|
|
13
|
+
* Supports both standard and streaming chat completions.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AnthropicProvider implements LLMProvider {
|
|
16
|
+
readonly name = "anthropic";
|
|
17
|
+
private client;
|
|
18
|
+
private consecutiveFailures;
|
|
19
|
+
private readonly maxConsecutiveFailures;
|
|
20
|
+
/**
|
|
21
|
+
* Default model configuration
|
|
22
|
+
*/
|
|
23
|
+
private readonly defaultModel;
|
|
24
|
+
private readonly defaultMaxTokens;
|
|
25
|
+
/**
|
|
26
|
+
* Model name mapping for user-friendly aliases
|
|
27
|
+
* Maps shorthand names to full Anthropic model identifiers
|
|
28
|
+
*/
|
|
29
|
+
private readonly modelAliases;
|
|
30
|
+
/**
|
|
31
|
+
* Model-specific maximum output token limits
|
|
32
|
+
* Maps model identifiers to their maximum allowed output tokens
|
|
33
|
+
*/
|
|
34
|
+
private readonly modelMaxTokens;
|
|
35
|
+
/**
|
|
36
|
+
* Create Anthropic provider instance
|
|
37
|
+
*
|
|
38
|
+
* @param apiKey - Anthropic API key
|
|
39
|
+
*/
|
|
40
|
+
constructor(apiKey: string);
|
|
41
|
+
/**
|
|
42
|
+
* Normalize model name using aliases
|
|
43
|
+
*
|
|
44
|
+
* Converts user-friendly model names to actual Anthropic model identifiers.
|
|
45
|
+
* Returns the input model name if no mapping is found (assumes it's already valid).
|
|
46
|
+
*
|
|
47
|
+
* @param model - User-provided model name
|
|
48
|
+
* @returns Normalized Anthropic model identifier
|
|
49
|
+
*/
|
|
50
|
+
private normalizeModelName;
|
|
51
|
+
/**
|
|
52
|
+
* Get appropriate max tokens for a model
|
|
53
|
+
*
|
|
54
|
+
* Returns model-specific token limit or falls back to default.
|
|
55
|
+
* Prevents exceeding model's maximum output token capacity.
|
|
56
|
+
*
|
|
57
|
+
* @param normalizedModel - Normalized model identifier
|
|
58
|
+
* @param requestedMaxTokens - User-requested max tokens (optional)
|
|
59
|
+
* @returns Safe max tokens value for the model
|
|
60
|
+
*/
|
|
61
|
+
private getMaxTokensForModel;
|
|
62
|
+
/**
|
|
63
|
+
* Generate chat completion
|
|
64
|
+
*
|
|
65
|
+
* @param messages - Conversation messages
|
|
66
|
+
* @param options - Optional chat configuration
|
|
67
|
+
* @returns Result with response text or error
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Convert OpenAI tool format to Anthropic tool format
|
|
71
|
+
*
|
|
72
|
+
* OpenAI format:
|
|
73
|
+
* {
|
|
74
|
+
* type: "function",
|
|
75
|
+
* function: {
|
|
76
|
+
* name: "tool_name",
|
|
77
|
+
* description: "...",
|
|
78
|
+
* parameters: { type: "object", properties: {...}, required: [...] }
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
*
|
|
82
|
+
* Anthropic format:
|
|
83
|
+
* {
|
|
84
|
+
* name: "tool_name",
|
|
85
|
+
* description: "...",
|
|
86
|
+
* input_schema: { type: "object", properties: {...}, required: [...] }
|
|
87
|
+
* }
|
|
88
|
+
*/
|
|
89
|
+
private convertOpenAIToolsToAnthropic;
|
|
90
|
+
/**
|
|
91
|
+
* Convert OpenAI tool_choice to Anthropic tool_choice
|
|
92
|
+
*
|
|
93
|
+
* OpenAI: "auto" | "none" | { type: "function", function: { name: string } }
|
|
94
|
+
* Anthropic: { type: "auto" } | { type: "any" } | { type: "tool", name: string }
|
|
95
|
+
*/
|
|
96
|
+
private convertOpenAIToolChoiceToAnthropic;
|
|
97
|
+
/**
|
|
98
|
+
* Convert Anthropic tool use response to OpenAI format
|
|
99
|
+
*
|
|
100
|
+
* Returns special format: __TOOL_CALLS__<JSON>
|
|
101
|
+
* This allows the bot to parse and execute tools
|
|
102
|
+
*/
|
|
103
|
+
private convertAnthropicToolCallsToOpenAI;
|
|
104
|
+
/**
|
|
105
|
+
* Convert OpenAI message format to Anthropic message format
|
|
106
|
+
*
|
|
107
|
+
* Handles conversion of tool messages (role: 'tool') to Anthropic's tool_result format.
|
|
108
|
+
* OpenAI uses separate tool messages, Anthropic embeds tool results in user messages.
|
|
109
|
+
*
|
|
110
|
+
* OpenAI format:
|
|
111
|
+
* { role: 'tool', content: '{"result": "..."}', tool_call_id: 'call_123' }
|
|
112
|
+
*
|
|
113
|
+
* Anthropic format:
|
|
114
|
+
* { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_123', content: '{"result": "..."}' }] }
|
|
115
|
+
*/
|
|
116
|
+
private convertMessagesToAnthropicFormat;
|
|
117
|
+
chat(messages: Message[], options?: ChatOptions): Promise<Result<string, ProviderError>>;
|
|
118
|
+
/**
|
|
119
|
+
* Generate streaming chat completion
|
|
120
|
+
*
|
|
121
|
+
* @param messages - Conversation messages
|
|
122
|
+
* @param options - Optional chat configuration
|
|
123
|
+
* @returns Result with async iterator of text chunks or error
|
|
124
|
+
*/
|
|
125
|
+
streamChat(messages: Message[], options?: ChatOptions): Promise<Result<AsyncIterable<string>, ProviderError>>;
|
|
126
|
+
/**
|
|
127
|
+
* Check if provider is healthy
|
|
128
|
+
*
|
|
129
|
+
* Uses passive health monitoring based on recent failures
|
|
130
|
+
* instead of making actual API calls to avoid costs and rate limits.
|
|
131
|
+
*
|
|
132
|
+
* @returns True if provider is responding correctly
|
|
133
|
+
*/
|
|
134
|
+
isHealthy(): Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Reset provider health status
|
|
137
|
+
*
|
|
138
|
+
* Clears consecutive failure counter and marks provider as healthy.
|
|
139
|
+
*/
|
|
140
|
+
resetHealth(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Get list of available models
|
|
143
|
+
*
|
|
144
|
+
* @returns Result with array of model IDs or error
|
|
145
|
+
*/
|
|
146
|
+
getAvailableModels(): Promise<Result<string[], ProviderError>>;
|
|
147
|
+
/**
|
|
148
|
+
* Create async iterator from Anthropic stream
|
|
149
|
+
*/
|
|
150
|
+
private createStreamIterator;
|
|
151
|
+
/**
|
|
152
|
+
* Handle API errors and convert to ProviderError
|
|
153
|
+
*/
|
|
154
|
+
private handleError;
|
|
155
|
+
/**
|
|
156
|
+
* Create standardized ProviderError
|
|
157
|
+
*/
|
|
158
|
+
private createError;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=anthropic-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,WAAW,EACX,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,WAAW;IACnD,SAAgB,IAAI,eAAe;IACnC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IAE5C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAEzC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAoC3B;IAEF;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAsB7B;IAEF;;;;OAIG;gBACS,MAAM,EAAE,MAAM;IAa1B;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;;OAMG;IACH;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,6BAA6B;IAQrC;;;;;OAKG;IACH,OAAO,CAAC,kCAAkC;IAqB1C;;;;;OAKG;IACH,OAAO,CAAC,iCAAiC;IAkCzC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gCAAgC;IAwGlC,IAAI,CACR,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IA8EzC;;;;;;OAMG;IACG,UAAU,CACd,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC;IA+BxD;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAMnC;;;;OAIG;IACH,WAAW,IAAI,IAAI;IAInB;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC;IA0BpE;;OAEG;YACY,oBAAoB;IAkBnC;;OAEG;IACH,OAAO,CAAC,WAAW;IAkCnB;;OAEG;IACH,OAAO,CAAC,WAAW;CAOpB"}
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Provider Implementation
|
|
3
|
+
*
|
|
4
|
+
* Implements LLMProvider interface for Anthropic Claude models.
|
|
5
|
+
* Provides chat completion, streaming, health checks, and model discovery.
|
|
6
|
+
*/
|
|
7
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
8
|
+
import { ProviderErrorType } from './types.js';
|
|
9
|
+
import { ok, err } from '../common/result.js';
|
|
10
|
+
/**
|
|
11
|
+
* Anthropic Claude Provider
|
|
12
|
+
*
|
|
13
|
+
* Wraps the Anthropic SDK to provide standardized LLM provider interface.
|
|
14
|
+
* Supports both standard and streaming chat completions.
|
|
15
|
+
*/
|
|
16
|
+
export class AnthropicProvider {
|
|
17
|
+
name = 'anthropic';
|
|
18
|
+
client;
|
|
19
|
+
consecutiveFailures = 0;
|
|
20
|
+
maxConsecutiveFailures = 3;
|
|
21
|
+
/**
|
|
22
|
+
* Default model configuration
|
|
23
|
+
*/
|
|
24
|
+
defaultModel = 'claude-sonnet-4-5-20250929';
|
|
25
|
+
defaultMaxTokens = 8096;
|
|
26
|
+
/**
|
|
27
|
+
* Model name mapping for user-friendly aliases
|
|
28
|
+
* Maps shorthand names to full Anthropic model identifiers
|
|
29
|
+
*/
|
|
30
|
+
modelAliases = {
|
|
31
|
+
// Claude 4 models (NEW - available in Tier 1)
|
|
32
|
+
'claude-4-sonnet': 'claude-sonnet-4-20250514',
|
|
33
|
+
'claude-sonnet-4': 'claude-sonnet-4-20250514',
|
|
34
|
+
'sonnet-4': 'claude-sonnet-4-20250514',
|
|
35
|
+
'claude-4.5-sonnet': 'claude-sonnet-4-5-20250929',
|
|
36
|
+
'claude-sonnet-4.5': 'claude-sonnet-4-5-20250929',
|
|
37
|
+
'sonnet-4.5': 'claude-sonnet-4-5-20250929',
|
|
38
|
+
'claude-4-opus': 'claude-opus-4-20250514',
|
|
39
|
+
'claude-opus-4': 'claude-opus-4-20250514',
|
|
40
|
+
'opus-4': 'claude-opus-4-20250514',
|
|
41
|
+
'claude-4.5-opus': 'claude-opus-4-5-20251101',
|
|
42
|
+
'claude-opus-4.5': 'claude-opus-4-5-20251101',
|
|
43
|
+
'opus-4.5': 'claude-opus-4-5-20251101',
|
|
44
|
+
'claude-4-haiku': 'claude-haiku-4-20250514',
|
|
45
|
+
'claude-haiku-4': 'claude-haiku-4-20250514',
|
|
46
|
+
'haiku-4': 'claude-haiku-4-20250514',
|
|
47
|
+
// Claude 3.5 models (NOT available in Tier 1)
|
|
48
|
+
'claude-3-5-sonnet': 'claude-3-5-sonnet-20241022',
|
|
49
|
+
'claude-3.5-sonnet': 'claude-3-5-sonnet-20241022',
|
|
50
|
+
'sonnet-3.5': 'claude-3-5-sonnet-20241022',
|
|
51
|
+
'claude-3-5-haiku': 'claude-3-5-haiku-20241022',
|
|
52
|
+
'claude-3.5-haiku': 'claude-3-5-haiku-20241022',
|
|
53
|
+
'haiku-3.5': 'claude-3-5-haiku-20241022',
|
|
54
|
+
// Claude 3 models
|
|
55
|
+
'claude-3-opus': 'claude-3-opus-20240229',
|
|
56
|
+
'claude-3.0-opus': 'claude-3-opus-20240229',
|
|
57
|
+
'opus': 'claude-3-opus-20240229',
|
|
58
|
+
'claude-3-sonnet': 'claude-3-sonnet-20240229',
|
|
59
|
+
'claude-3.0-sonnet': 'claude-3-sonnet-20240229',
|
|
60
|
+
'sonnet': 'claude-3-sonnet-20240229',
|
|
61
|
+
'claude-3-haiku': 'claude-3-haiku-20240307',
|
|
62
|
+
'claude-3.0-haiku': 'claude-3-haiku-20240307',
|
|
63
|
+
'haiku': 'claude-3-haiku-20240307',
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Model-specific maximum output token limits
|
|
67
|
+
* Maps model identifiers to their maximum allowed output tokens
|
|
68
|
+
*/
|
|
69
|
+
modelMaxTokens = {
|
|
70
|
+
// Claude 4 models (8192 max output tokens) - TESTED WORKING
|
|
71
|
+
'claude-sonnet-4-20250514': 8192,
|
|
72
|
+
'claude-4-sonnet-20250514': 8192,
|
|
73
|
+
'claude-sonnet-4-5-20250929': 8192,
|
|
74
|
+
'claude-opus-4-20250514': 8192,
|
|
75
|
+
'claude-4-opus-20250514': 8192,
|
|
76
|
+
'claude-opus-4-5-20251101': 8192,
|
|
77
|
+
'claude-opus-4-5': 8192,
|
|
78
|
+
'claude-haiku-4-20250514': 8192,
|
|
79
|
+
// Claude 3.7 models (8192 max output tokens) - TESTED WORKING
|
|
80
|
+
'claude-3-7-sonnet-20250219': 8192,
|
|
81
|
+
// Claude 3.5 models (8192 max output tokens) - NOT AVAILABLE
|
|
82
|
+
'claude-3-5-sonnet-20241022': 8192,
|
|
83
|
+
'claude-3-5-haiku-20241022': 8192,
|
|
84
|
+
// Claude 3 models (4096 max output tokens)
|
|
85
|
+
'claude-3-opus-20240229': 4096,
|
|
86
|
+
'claude-3-sonnet-20240229': 4096,
|
|
87
|
+
'claude-3-haiku-20240307': 4096,
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Create Anthropic provider instance
|
|
91
|
+
*
|
|
92
|
+
* @param apiKey - Anthropic API key
|
|
93
|
+
*/
|
|
94
|
+
constructor(apiKey) {
|
|
95
|
+
if (!apiKey || apiKey.trim() === '') {
|
|
96
|
+
throw new Error('Anthropic API key is required');
|
|
97
|
+
}
|
|
98
|
+
// IMPORTANT: Explicitly set baseURL to official Anthropic API
|
|
99
|
+
// This prevents proxies/gateways from intercepting the requests
|
|
100
|
+
this.client = new Anthropic({
|
|
101
|
+
apiKey,
|
|
102
|
+
baseURL: 'https://api.anthropic.com',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Normalize model name using aliases
|
|
107
|
+
*
|
|
108
|
+
* Converts user-friendly model names to actual Anthropic model identifiers.
|
|
109
|
+
* Returns the input model name if no mapping is found (assumes it's already valid).
|
|
110
|
+
*
|
|
111
|
+
* @param model - User-provided model name
|
|
112
|
+
* @returns Normalized Anthropic model identifier
|
|
113
|
+
*/
|
|
114
|
+
normalizeModelName(model) {
|
|
115
|
+
const normalized = this.modelAliases[model.toLowerCase()];
|
|
116
|
+
return normalized || model;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get appropriate max tokens for a model
|
|
120
|
+
*
|
|
121
|
+
* Returns model-specific token limit or falls back to default.
|
|
122
|
+
* Prevents exceeding model's maximum output token capacity.
|
|
123
|
+
*
|
|
124
|
+
* @param normalizedModel - Normalized model identifier
|
|
125
|
+
* @param requestedMaxTokens - User-requested max tokens (optional)
|
|
126
|
+
* @returns Safe max tokens value for the model
|
|
127
|
+
*/
|
|
128
|
+
getMaxTokensForModel(normalizedModel, requestedMaxTokens) {
|
|
129
|
+
// Get model's maximum allowed tokens
|
|
130
|
+
const modelLimit = this.modelMaxTokens[normalizedModel] || this.defaultMaxTokens;
|
|
131
|
+
// If user didn't specify, use model's limit
|
|
132
|
+
if (!requestedMaxTokens) {
|
|
133
|
+
return modelLimit;
|
|
134
|
+
}
|
|
135
|
+
// If user specified, ensure it doesn't exceed model's limit
|
|
136
|
+
return Math.min(requestedMaxTokens, modelLimit);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generate chat completion
|
|
140
|
+
*
|
|
141
|
+
* @param messages - Conversation messages
|
|
142
|
+
* @param options - Optional chat configuration
|
|
143
|
+
* @returns Result with response text or error
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Convert OpenAI tool format to Anthropic tool format
|
|
147
|
+
*
|
|
148
|
+
* OpenAI format:
|
|
149
|
+
* {
|
|
150
|
+
* type: "function",
|
|
151
|
+
* function: {
|
|
152
|
+
* name: "tool_name",
|
|
153
|
+
* description: "...",
|
|
154
|
+
* parameters: { type: "object", properties: {...}, required: [...] }
|
|
155
|
+
* }
|
|
156
|
+
* }
|
|
157
|
+
*
|
|
158
|
+
* Anthropic format:
|
|
159
|
+
* {
|
|
160
|
+
* name: "tool_name",
|
|
161
|
+
* description: "...",
|
|
162
|
+
* input_schema: { type: "object", properties: {...}, required: [...] }
|
|
163
|
+
* }
|
|
164
|
+
*/
|
|
165
|
+
convertOpenAIToolsToAnthropic(openaiTools) {
|
|
166
|
+
return openaiTools.map((tool) => ({
|
|
167
|
+
name: tool.function.name,
|
|
168
|
+
description: tool.function.description || '',
|
|
169
|
+
input_schema: tool.function.parameters,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Convert OpenAI tool_choice to Anthropic tool_choice
|
|
174
|
+
*
|
|
175
|
+
* OpenAI: "auto" | "none" | { type: "function", function: { name: string } }
|
|
176
|
+
* Anthropic: { type: "auto" } | { type: "any" } | { type: "tool", name: string }
|
|
177
|
+
*/
|
|
178
|
+
convertOpenAIToolChoiceToAnthropic(toolChoice) {
|
|
179
|
+
if (!toolChoice || toolChoice === 'auto') {
|
|
180
|
+
return { type: 'auto' };
|
|
181
|
+
}
|
|
182
|
+
if (toolChoice === 'none') {
|
|
183
|
+
return undefined; // Anthropic doesn't have explicit "none", just omit tools
|
|
184
|
+
}
|
|
185
|
+
if (typeof toolChoice === 'object' && toolChoice.type === 'function') {
|
|
186
|
+
return {
|
|
187
|
+
type: 'tool',
|
|
188
|
+
name: toolChoice.function.name,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
return { type: 'auto' }; // Fallback
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Convert Anthropic tool use response to OpenAI format
|
|
195
|
+
*
|
|
196
|
+
* Returns special format: __TOOL_CALLS__<JSON>
|
|
197
|
+
* This allows the bot to parse and execute tools
|
|
198
|
+
*/
|
|
199
|
+
convertAnthropicToolCallsToOpenAI(content) {
|
|
200
|
+
const toolUseBlocks = content.filter((block) => block.type === 'tool_use');
|
|
201
|
+
if (toolUseBlocks.length === 0) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
// Convert to OpenAI format
|
|
205
|
+
const toolCalls = toolUseBlocks.map((block) => ({
|
|
206
|
+
id: block.id,
|
|
207
|
+
type: 'function',
|
|
208
|
+
function: {
|
|
209
|
+
name: block.name,
|
|
210
|
+
arguments: JSON.stringify(block.input),
|
|
211
|
+
},
|
|
212
|
+
}));
|
|
213
|
+
// Get text content if any
|
|
214
|
+
const textBlock = content.find((block) => block.type === 'text');
|
|
215
|
+
const message = textBlock ? textBlock.text : '';
|
|
216
|
+
// Return in the format the bot expects
|
|
217
|
+
const toolCallsData = {
|
|
218
|
+
tool_calls: toolCalls,
|
|
219
|
+
message,
|
|
220
|
+
};
|
|
221
|
+
return `__TOOL_CALLS__${JSON.stringify(toolCallsData)}`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Convert OpenAI message format to Anthropic message format
|
|
225
|
+
*
|
|
226
|
+
* Handles conversion of tool messages (role: 'tool') to Anthropic's tool_result format.
|
|
227
|
+
* OpenAI uses separate tool messages, Anthropic embeds tool results in user messages.
|
|
228
|
+
*
|
|
229
|
+
* OpenAI format:
|
|
230
|
+
* { role: 'tool', content: '{"result": "..."}', tool_call_id: 'call_123' }
|
|
231
|
+
*
|
|
232
|
+
* Anthropic format:
|
|
233
|
+
* { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_123', content: '{"result": "..."}' }] }
|
|
234
|
+
*/
|
|
235
|
+
convertMessagesToAnthropicFormat(messages) {
|
|
236
|
+
const anthropicMessages = [];
|
|
237
|
+
for (let i = 0; i < messages.length; i++) {
|
|
238
|
+
const msg = messages[i];
|
|
239
|
+
// Skip system messages (Anthropic doesn't support them in messages array)
|
|
240
|
+
if (msg.role === 'system') {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
// Convert tool messages to Anthropic's tool_result format
|
|
244
|
+
if (msg.role === 'tool') {
|
|
245
|
+
// Tool results should only be included if we're in the same request as the tool_use
|
|
246
|
+
// In subsequent requests (like streaming after tool execution), skip tool results
|
|
247
|
+
// because the corresponding tool_use is not in this request's messages
|
|
248
|
+
// Check if there's a previous assistant message with __TOOL_CALLS__ marker
|
|
249
|
+
// If so, this is a subsequent request and we should skip the tool result
|
|
250
|
+
let skipToolResult = false;
|
|
251
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
252
|
+
if (anthropicMessages[j] && anthropicMessages[j].role === 'assistant') {
|
|
253
|
+
const assistantContent = anthropicMessages[j].content;
|
|
254
|
+
if (typeof assistantContent === 'string' && assistantContent.includes('__TOOL_CALLS__')) {
|
|
255
|
+
// This tool result corresponds to a tool call that was already processed
|
|
256
|
+
// Skip it in subsequent requests
|
|
257
|
+
skipToolResult = true;
|
|
258
|
+
}
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (skipToolResult) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const toolContent = (msg.content || '').trim();
|
|
266
|
+
const toolCallId = msg.tool_call_id || '';
|
|
267
|
+
// Skip tool messages with missing content or ID
|
|
268
|
+
if (!toolContent || !toolCallId) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
anthropicMessages.push({
|
|
272
|
+
role: 'user',
|
|
273
|
+
content: [
|
|
274
|
+
{
|
|
275
|
+
type: 'tool_result',
|
|
276
|
+
tool_use_id: toolCallId,
|
|
277
|
+
content: toolContent,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
});
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
// Handle assistant messages with tool_calls
|
|
284
|
+
if (msg.role === 'assistant' && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
285
|
+
// Convert OpenAI tool_calls to Anthropic tool_use format
|
|
286
|
+
const toolUseBlocks = msg.tool_calls.map((toolCall) => ({
|
|
287
|
+
type: 'tool_use',
|
|
288
|
+
id: toolCall.id,
|
|
289
|
+
name: toolCall.function.name,
|
|
290
|
+
input: JSON.parse(toolCall.function.arguments),
|
|
291
|
+
}));
|
|
292
|
+
// Include text content if present, otherwise just tool_use blocks
|
|
293
|
+
const content = (msg.content || '').trim();
|
|
294
|
+
const contentBlocks = [];
|
|
295
|
+
if (content) {
|
|
296
|
+
contentBlocks.push({
|
|
297
|
+
type: 'text',
|
|
298
|
+
text: content,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
contentBlocks.push(...toolUseBlocks);
|
|
302
|
+
anthropicMessages.push({
|
|
303
|
+
role: 'assistant',
|
|
304
|
+
content: contentBlocks,
|
|
305
|
+
});
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
// Regular user/assistant messages without tool calls
|
|
309
|
+
const content = (msg.content || '').trim();
|
|
310
|
+
// Skip messages with empty content
|
|
311
|
+
if (!content) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
anthropicMessages.push({
|
|
315
|
+
role: msg.role,
|
|
316
|
+
content,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
return anthropicMessages;
|
|
320
|
+
}
|
|
321
|
+
async chat(messages, options) {
|
|
322
|
+
try {
|
|
323
|
+
// Normalize model name to handle user-friendly aliases
|
|
324
|
+
const requestedModel = options?.model || this.defaultModel;
|
|
325
|
+
const normalizedModel = this.normalizeModelName(requestedModel);
|
|
326
|
+
// Get appropriate max tokens for this model
|
|
327
|
+
const maxTokens = this.getMaxTokensForModel(normalizedModel, options?.maxTokens);
|
|
328
|
+
// Convert tools from OpenAI format to Anthropic format if provided
|
|
329
|
+
const anthropicTools = options?.tools
|
|
330
|
+
? this.convertOpenAIToolsToAnthropic(options.tools)
|
|
331
|
+
: undefined;
|
|
332
|
+
const anthropicToolChoice = options?.tool_choice
|
|
333
|
+
? this.convertOpenAIToolChoiceToAnthropic(options.tool_choice)
|
|
334
|
+
: undefined;
|
|
335
|
+
const response = await this.client.messages.create({
|
|
336
|
+
model: normalizedModel,
|
|
337
|
+
max_tokens: maxTokens,
|
|
338
|
+
temperature: options?.temperature,
|
|
339
|
+
stop_sequences: options?.stopSequences,
|
|
340
|
+
messages: this.convertMessagesToAnthropicFormat(messages),
|
|
341
|
+
...(options?.system && { system: options.system }),
|
|
342
|
+
...(anthropicTools && anthropicTools.length > 0 && { tools: anthropicTools }),
|
|
343
|
+
...(anthropicToolChoice && { tool_choice: anthropicToolChoice }),
|
|
344
|
+
});
|
|
345
|
+
// Handle error responses that SDK didn't throw
|
|
346
|
+
// Some SDK versions or proxies return errors as response objects
|
|
347
|
+
if (response.type === 'error' || response.error) {
|
|
348
|
+
const errorObj = response.error;
|
|
349
|
+
if (errorObj?.type === 'authentication_error') {
|
|
350
|
+
return err(this.createError(ProviderErrorType.AUTH_FAILED, errorObj.message || 'Authentication failed'));
|
|
351
|
+
}
|
|
352
|
+
if (errorObj?.type === 'rate_limit_error') {
|
|
353
|
+
return err(this.createError(ProviderErrorType.RATE_LIMIT, errorObj.message || 'Rate limit exceeded'));
|
|
354
|
+
}
|
|
355
|
+
if (errorObj?.type === 'invalid_request_error') {
|
|
356
|
+
return err(this.createError(ProviderErrorType.INVALID_REQUEST, errorObj.message || 'Invalid request'));
|
|
357
|
+
}
|
|
358
|
+
return err(this.createError(ProviderErrorType.UNKNOWN, errorObj?.message || 'Unknown error from API'));
|
|
359
|
+
}
|
|
360
|
+
// Validate response structure
|
|
361
|
+
if (!response || !response.content || !Array.isArray(response.content)) {
|
|
362
|
+
return err(this.createError(ProviderErrorType.INVALID_REQUEST, 'Invalid response structure from Anthropic API'));
|
|
363
|
+
}
|
|
364
|
+
// Check if response contains tool calls
|
|
365
|
+
const toolCallsResponse = this.convertAnthropicToolCallsToOpenAI(response.content);
|
|
366
|
+
if (toolCallsResponse) {
|
|
367
|
+
// Reset failure counter on success
|
|
368
|
+
this.consecutiveFailures = 0;
|
|
369
|
+
return ok(toolCallsResponse);
|
|
370
|
+
}
|
|
371
|
+
// Extract text from response
|
|
372
|
+
const textContent = response.content.find((c) => c.type === 'text');
|
|
373
|
+
if (!textContent || textContent.type !== 'text') {
|
|
374
|
+
return err(this.createError(ProviderErrorType.INVALID_REQUEST, 'No text content in response'));
|
|
375
|
+
}
|
|
376
|
+
// Reset failure counter on success
|
|
377
|
+
this.consecutiveFailures = 0;
|
|
378
|
+
return ok(textContent.text);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
this.consecutiveFailures++;
|
|
382
|
+
return err(this.handleError(error));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Generate streaming chat completion
|
|
387
|
+
*
|
|
388
|
+
* @param messages - Conversation messages
|
|
389
|
+
* @param options - Optional chat configuration
|
|
390
|
+
* @returns Result with async iterator of text chunks or error
|
|
391
|
+
*/
|
|
392
|
+
async streamChat(messages, options) {
|
|
393
|
+
try {
|
|
394
|
+
// Normalize model name to handle user-friendly aliases
|
|
395
|
+
const requestedModel = options?.model || this.defaultModel;
|
|
396
|
+
const normalizedModel = this.normalizeModelName(requestedModel);
|
|
397
|
+
// Get appropriate max tokens for this model
|
|
398
|
+
const maxTokens = this.getMaxTokensForModel(normalizedModel, options?.maxTokens);
|
|
399
|
+
const stream = await this.client.messages.stream({
|
|
400
|
+
model: normalizedModel,
|
|
401
|
+
max_tokens: maxTokens,
|
|
402
|
+
temperature: options?.temperature,
|
|
403
|
+
stop_sequences: options?.stopSequences,
|
|
404
|
+
messages: this.convertMessagesToAnthropicFormat(messages),
|
|
405
|
+
...(options?.system && { system: options.system }),
|
|
406
|
+
});
|
|
407
|
+
// Create async iterator from stream
|
|
408
|
+
const iterator = this.createStreamIterator(stream);
|
|
409
|
+
// Reset failure counter on success
|
|
410
|
+
this.consecutiveFailures = 0;
|
|
411
|
+
return ok(iterator);
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
this.consecutiveFailures++;
|
|
415
|
+
return err(this.handleError(error));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Check if provider is healthy
|
|
420
|
+
*
|
|
421
|
+
* Uses passive health monitoring based on recent failures
|
|
422
|
+
* instead of making actual API calls to avoid costs and rate limits.
|
|
423
|
+
*
|
|
424
|
+
* @returns True if provider is responding correctly
|
|
425
|
+
*/
|
|
426
|
+
async isHealthy() {
|
|
427
|
+
// Passive health check: only monitor consecutive failures
|
|
428
|
+
// Don't make actual API calls to avoid costs and rate limits
|
|
429
|
+
return this.consecutiveFailures < this.maxConsecutiveFailures;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Reset provider health status
|
|
433
|
+
*
|
|
434
|
+
* Clears consecutive failure counter and marks provider as healthy.
|
|
435
|
+
*/
|
|
436
|
+
resetHealth() {
|
|
437
|
+
this.consecutiveFailures = 0;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Get list of available models
|
|
441
|
+
*
|
|
442
|
+
* @returns Result with array of model IDs or error
|
|
443
|
+
*/
|
|
444
|
+
async getAvailableModels() {
|
|
445
|
+
// Return models confirmed to work with the current API key
|
|
446
|
+
// Note: Anthropic SDK doesn't have a models.list() method
|
|
447
|
+
const knownModels = [
|
|
448
|
+
// Claude 4 Opus models (TESTED - WORKING - MOST POWERFUL)
|
|
449
|
+
'claude-opus-4-5-20251101', // Claude Opus 4.5 (MOST CAPABLE)
|
|
450
|
+
'claude-opus-4-5', // Claude Opus 4.5 (alias)
|
|
451
|
+
'claude-opus-4-20250514', // Claude Opus 4
|
|
452
|
+
'claude-4-opus-20250514', // Claude 4 Opus (alt format)
|
|
453
|
+
// Claude 4 Sonnet models (TESTED - WORKING)
|
|
454
|
+
'claude-sonnet-4-5-20250929', // Claude Sonnet 4.5
|
|
455
|
+
'claude-sonnet-4-20250514', // Claude Sonnet 4
|
|
456
|
+
'claude-4-sonnet-20250514', // Claude 4 Sonnet (alt format)
|
|
457
|
+
// Claude 3.7 Sonnet (TESTED - WORKING)
|
|
458
|
+
'claude-3-7-sonnet-20250219',
|
|
459
|
+
// Claude 3 Haiku (TESTED - WORKING - FASTEST)
|
|
460
|
+
'claude-3-haiku-20240307',
|
|
461
|
+
// Note: Claude 3.5 models and Claude 4 Haiku are NOT available
|
|
462
|
+
];
|
|
463
|
+
return ok(knownModels);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Create async iterator from Anthropic stream
|
|
467
|
+
*/
|
|
468
|
+
async *createStreamIterator(stream) {
|
|
469
|
+
try {
|
|
470
|
+
for await (const chunk of stream) {
|
|
471
|
+
if (chunk.type === 'content_block_delta') {
|
|
472
|
+
const delta = chunk.delta;
|
|
473
|
+
if (delta.type === 'text_delta') {
|
|
474
|
+
yield delta.text;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
// Stream error - will be caught by caller
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Handle API errors and convert to ProviderError
|
|
486
|
+
*/
|
|
487
|
+
handleError(error) {
|
|
488
|
+
// Anthropic SDK errors - check for APIError instance or status property
|
|
489
|
+
// The status is passed as first parameter to APIError constructor
|
|
490
|
+
const statusCode = error.status;
|
|
491
|
+
if (statusCode !== undefined) {
|
|
492
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
493
|
+
return this.createError(ProviderErrorType.AUTH_FAILED, 'Authentication failed');
|
|
494
|
+
}
|
|
495
|
+
if (statusCode === 429) {
|
|
496
|
+
return this.createError(ProviderErrorType.RATE_LIMIT, 'Rate limit exceeded');
|
|
497
|
+
}
|
|
498
|
+
if (statusCode === 404) {
|
|
499
|
+
return this.createError(ProviderErrorType.MODEL_NOT_FOUND, 'Model not found');
|
|
500
|
+
}
|
|
501
|
+
if (statusCode === 400) {
|
|
502
|
+
return this.createError(ProviderErrorType.INVALID_REQUEST, error.message || 'Invalid request');
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Network errors
|
|
506
|
+
if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
|
|
507
|
+
return this.createError(ProviderErrorType.NETWORK_ERROR, 'Network connection failed');
|
|
508
|
+
}
|
|
509
|
+
// Timeout errors
|
|
510
|
+
if (error.code === 'ETIMEDOUT' || error.name === 'TimeoutError') {
|
|
511
|
+
return this.createError(ProviderErrorType.TIMEOUT, 'Request timeout');
|
|
512
|
+
}
|
|
513
|
+
// Unknown errors
|
|
514
|
+
return this.createError(ProviderErrorType.UNKNOWN, error.message || 'Unknown error occurred');
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Create standardized ProviderError
|
|
518
|
+
*/
|
|
519
|
+
createError(type, message) {
|
|
520
|
+
return {
|
|
521
|
+
type,
|
|
522
|
+
message,
|
|
523
|
+
provider: this.name,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
//# sourceMappingURL=anthropic-provider.js.map
|