@charming_groot/providers 0.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.
- package/dist/auth/auth-resolver.d.ts +9 -0
- package/dist/auth/auth-resolver.d.ts.map +1 -0
- package/dist/auth/auth-resolver.js +200 -0
- package/dist/auth/auth-resolver.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/base-provider.d.ts +10 -0
- package/dist/base-provider.d.ts.map +1 -0
- package/dist/base-provider.js +8 -0
- package/dist/base-provider.js.map +1 -0
- package/dist/circuit-breaker.d.ts +42 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +116 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/claude-provider.d.ts +15 -0
- package/dist/claude-provider.d.ts.map +1 -0
- package/dist/claude-provider.js +171 -0
- package/dist/claude-provider.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/openai-provider.d.ts +16 -0
- package/dist/openai-provider.d.ts.map +1 -0
- package/dist/openai-provider.js +196 -0
- package/dist/openai-provider.js.map +1 -0
- package/dist/provider-factory.d.ts +17 -0
- package/dist/provider-factory.d.ts.map +1 -0
- package/dist/provider-factory.js +36 -0
- package/dist/provider-factory.js.map +1 -0
- package/dist/retry-provider.d.ts +25 -0
- package/dist/retry-provider.d.ts.map +1 -0
- package/dist/retry-provider.js +92 -0
- package/dist/retry-provider.js.map +1 -0
- package/dist/thinking-parser.d.ts +28 -0
- package/dist/thinking-parser.d.ts.map +1 -0
- package/dist/thinking-parser.js +40 -0
- package/dist/thinking-parser.js.map +1 -0
- package/package.json +34 -0
- package/src/auth/auth-resolver.ts +261 -0
- package/src/auth/index.ts +1 -0
- package/src/base-provider.ts +28 -0
- package/src/circuit-breaker.ts +157 -0
- package/src/claude-provider.ts +215 -0
- package/src/index.ts +13 -0
- package/src/openai-provider.ts +239 -0
- package/src/provider-factory.ts +48 -0
- package/src/retry-provider.ts +135 -0
- package/src/thinking-parser.ts +50 -0
- package/tests/auth-resolver.test.ts +204 -0
- package/tests/circuit-breaker.test.ts +220 -0
- package/tests/claude-provider.test.ts +35 -0
- package/tests/openai-provider.test.ts +35 -0
- package/tests/provider-factory.test.ts +73 -0
- package/tests/retry-provider-new.test.ts +166 -0
- package/tests/retry-provider.test.ts +118 -0
- package/tests/thinking-parser.test.ts +73 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +15 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { BaseProvider } from './base-provider.js';
|
|
2
|
+
export { ClaudeProvider } from './claude-provider.js';
|
|
3
|
+
export { OpenAIProvider } from './openai-provider.js';
|
|
4
|
+
export { createProvider, registerProvider, getProviderRegistry, } from './provider-factory.js';
|
|
5
|
+
export { RetryProvider, type RetryConfig } from './retry-provider.js';
|
|
6
|
+
export { CircuitBreakerProvider, type CircuitBreakerConfig } from './circuit-breaker.js';
|
|
7
|
+
export { resolveAuth, extractToken } from './auth/index.js';
|
|
8
|
+
export { extractThinkTag, estimateThinkingMs } from './thinking-parser.js';
|
|
9
|
+
export type { ThinkTagResult } from './thinking-parser.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC3E,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { BaseProvider } from './base-provider.js';
|
|
2
|
+
export { ClaudeProvider } from './claude-provider.js';
|
|
3
|
+
export { OpenAIProvider } from './openai-provider.js';
|
|
4
|
+
export { createProvider, registerProvider, getProviderRegistry, } from './provider-factory.js';
|
|
5
|
+
export { RetryProvider } from './retry-provider.js';
|
|
6
|
+
export { CircuitBreakerProvider } from './circuit-breaker.js';
|
|
7
|
+
export { resolveAuth, extractToken } from './auth/index.js';
|
|
8
|
+
export { extractThinkTag, estimateThinkingMs } from './thinking-parser.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAoB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Message, LlmResponse, StreamEvent, ToolDescription, ProviderConfig } from '@charming_groot/core';
|
|
2
|
+
import { BaseProvider } from './base-provider.js';
|
|
3
|
+
export declare class OpenAIProvider extends BaseProvider {
|
|
4
|
+
readonly providerId = "openai";
|
|
5
|
+
private readonly client;
|
|
6
|
+
private readonly model;
|
|
7
|
+
private readonly maxTokens;
|
|
8
|
+
private readonly temperature;
|
|
9
|
+
constructor(config: ProviderConfig);
|
|
10
|
+
chat(messages: readonly Message[], tools?: readonly ToolDescription[]): Promise<LlmResponse>;
|
|
11
|
+
stream(messages: readonly Message[], tools?: readonly ToolDescription[]): AsyncIterable<StreamEvent>;
|
|
12
|
+
private toOpenAIMessages;
|
|
13
|
+
private toOpenAITools;
|
|
14
|
+
private parseResponse;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=openai-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-provider.d.ts","sourceRoot":"","sources":["../src/openai-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAe,EAEf,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,QAAQ,CAAC,UAAU,YAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,EAAE,cAAc;IAS5B,IAAI,CACR,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,GACjC,OAAO,CAAC,WAAW,CAAC;IAmBhB,MAAM,CACX,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,GACjC,aAAa,CAAC,WAAW,CAAC;IAkF7B,OAAO,CAAC,gBAAgB;IAoCxB,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,aAAa;CAyCtB"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { ProviderError } from '@charming_groot/core';
|
|
3
|
+
import { BaseProvider } from './base-provider.js';
|
|
4
|
+
import { extractToken } from './auth/auth-resolver.js';
|
|
5
|
+
import { extractThinkTag, estimateThinkingMs } from './thinking-parser.js';
|
|
6
|
+
export class OpenAIProvider extends BaseProvider {
|
|
7
|
+
providerId = 'openai';
|
|
8
|
+
client;
|
|
9
|
+
model;
|
|
10
|
+
maxTokens;
|
|
11
|
+
temperature;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super('openai-provider');
|
|
14
|
+
const apiKey = extractToken(config.auth);
|
|
15
|
+
this.client = new OpenAI({ apiKey, baseURL: config.baseUrl });
|
|
16
|
+
this.model = config.model;
|
|
17
|
+
this.maxTokens = config.maxTokens;
|
|
18
|
+
this.temperature = config.temperature;
|
|
19
|
+
}
|
|
20
|
+
async chat(messages, tools) {
|
|
21
|
+
try {
|
|
22
|
+
const response = await this.client.chat.completions.create({
|
|
23
|
+
model: this.model,
|
|
24
|
+
max_tokens: this.maxTokens,
|
|
25
|
+
temperature: this.temperature,
|
|
26
|
+
messages: this.toOpenAIMessages(messages),
|
|
27
|
+
tools: tools && tools.length > 0 ? this.toOpenAITools(tools) : undefined,
|
|
28
|
+
});
|
|
29
|
+
return this.parseResponse(response);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new ProviderError(`OpenAI API error: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async *stream(messages, tools) {
|
|
36
|
+
try {
|
|
37
|
+
const stream = await this.client.chat.completions.create({
|
|
38
|
+
model: this.model,
|
|
39
|
+
max_tokens: this.maxTokens,
|
|
40
|
+
temperature: this.temperature,
|
|
41
|
+
messages: this.toOpenAIMessages(messages),
|
|
42
|
+
tools: tools && tools.length > 0 ? this.toOpenAITools(tools) : undefined,
|
|
43
|
+
stream: true,
|
|
44
|
+
});
|
|
45
|
+
let content = '';
|
|
46
|
+
const toolCalls = [];
|
|
47
|
+
let inputTokens = 0;
|
|
48
|
+
let outputTokens = 0;
|
|
49
|
+
for await (const chunk of stream) {
|
|
50
|
+
const delta = chunk.choices[0]?.delta;
|
|
51
|
+
if (!delta)
|
|
52
|
+
continue;
|
|
53
|
+
if (delta.content) {
|
|
54
|
+
content += delta.content;
|
|
55
|
+
yield { type: 'text_delta', content: delta.content };
|
|
56
|
+
}
|
|
57
|
+
if (delta.tool_calls) {
|
|
58
|
+
for (const tc of delta.tool_calls) {
|
|
59
|
+
if (tc.id) {
|
|
60
|
+
toolCalls.push({
|
|
61
|
+
id: tc.id,
|
|
62
|
+
name: tc.function?.name ?? '',
|
|
63
|
+
arguments: tc.function?.arguments ?? '',
|
|
64
|
+
});
|
|
65
|
+
yield {
|
|
66
|
+
type: 'tool_call_start',
|
|
67
|
+
toolCall: { id: tc.id, name: tc.function?.name },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else if (tc.function?.arguments) {
|
|
71
|
+
const last = toolCalls[toolCalls.length - 1];
|
|
72
|
+
if (last) {
|
|
73
|
+
toolCalls[toolCalls.length - 1] = {
|
|
74
|
+
...last,
|
|
75
|
+
arguments: last.arguments + tc.function.arguments,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
yield { type: 'tool_call_delta', content: tc.function.arguments };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (chunk.usage) {
|
|
83
|
+
inputTokens = chunk.usage.prompt_tokens;
|
|
84
|
+
outputTokens = chunk.usage.completion_tokens;
|
|
85
|
+
}
|
|
86
|
+
if (chunk.choices[0]?.finish_reason) {
|
|
87
|
+
const stopReason = chunk.choices[0].finish_reason === 'tool_calls'
|
|
88
|
+
? 'tool_use'
|
|
89
|
+
: chunk.choices[0].finish_reason === 'length'
|
|
90
|
+
? 'max_tokens'
|
|
91
|
+
: 'end_turn';
|
|
92
|
+
yield {
|
|
93
|
+
type: 'done',
|
|
94
|
+
response: {
|
|
95
|
+
content,
|
|
96
|
+
stopReason,
|
|
97
|
+
toolCalls,
|
|
98
|
+
usage: { inputTokens, outputTokens },
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new ProviderError(`OpenAI stream error: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? error : undefined);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
toOpenAIMessages(messages) {
|
|
109
|
+
const result = [];
|
|
110
|
+
for (const msg of messages) {
|
|
111
|
+
if (msg.role === 'system') {
|
|
112
|
+
result.push({ role: 'system', content: msg.content });
|
|
113
|
+
}
|
|
114
|
+
else if (msg.toolResults && msg.toolResults.length > 0) {
|
|
115
|
+
for (const tr of msg.toolResults) {
|
|
116
|
+
result.push({
|
|
117
|
+
role: 'tool',
|
|
118
|
+
tool_call_id: tr.toolCallId,
|
|
119
|
+
content: tr.content,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (msg.role === 'assistant' && msg.toolCalls && msg.toolCalls.length > 0) {
|
|
124
|
+
result.push({
|
|
125
|
+
role: 'assistant',
|
|
126
|
+
content: msg.content || null,
|
|
127
|
+
tool_calls: msg.toolCalls.map((tc) => ({
|
|
128
|
+
id: tc.id,
|
|
129
|
+
type: 'function',
|
|
130
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
131
|
+
})),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else if (msg.role === 'assistant') {
|
|
135
|
+
result.push({ role: 'assistant', content: msg.content });
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
result.push({ role: 'user', content: msg.content });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
toOpenAITools(tools) {
|
|
144
|
+
return tools.map((tool) => ({
|
|
145
|
+
type: 'function',
|
|
146
|
+
function: {
|
|
147
|
+
name: tool.name,
|
|
148
|
+
description: tool.description,
|
|
149
|
+
parameters: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: Object.fromEntries(tool.parameters.map((p) => [
|
|
152
|
+
p.name,
|
|
153
|
+
{ type: p.type, description: p.description },
|
|
154
|
+
])),
|
|
155
|
+
required: tool.parameters
|
|
156
|
+
.filter((p) => p.required)
|
|
157
|
+
.map((p) => p.name),
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
parseResponse(response) {
|
|
163
|
+
const choice = response.choices[0];
|
|
164
|
+
if (!choice) {
|
|
165
|
+
throw new ProviderError('No choices in OpenAI response');
|
|
166
|
+
}
|
|
167
|
+
const toolCalls = (choice.message.tool_calls ?? []).map((tc) => ({
|
|
168
|
+
id: tc.id,
|
|
169
|
+
name: tc.function.name,
|
|
170
|
+
arguments: tc.function.arguments,
|
|
171
|
+
}));
|
|
172
|
+
const stopReason = choice.finish_reason === 'tool_calls'
|
|
173
|
+
? 'tool_use'
|
|
174
|
+
: choice.finish_reason === 'length'
|
|
175
|
+
? 'max_tokens'
|
|
176
|
+
: 'end_turn';
|
|
177
|
+
let content = choice.message.content ?? '';
|
|
178
|
+
let thinkingMs;
|
|
179
|
+
const parsed = extractThinkTag(content);
|
|
180
|
+
if (parsed.thinkContent) {
|
|
181
|
+
content = parsed.cleanContent;
|
|
182
|
+
thinkingMs = estimateThinkingMs(parsed.thinkContent);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
content,
|
|
186
|
+
stopReason,
|
|
187
|
+
toolCalls,
|
|
188
|
+
usage: {
|
|
189
|
+
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
190
|
+
outputTokens: response.usage?.completion_tokens ?? 0,
|
|
191
|
+
thinkingMs,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=openai-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-provider.js","sourceRoot":"","sources":["../src/openai-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ILlmProvider, ProviderConfig } from '@charming_groot/core';
|
|
2
|
+
import { Registry } from '@charming_groot/core';
|
|
3
|
+
type ProviderConstructor = new (config: ProviderConfig) => ILlmProvider;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a provider wrapped with RetryProvider → CircuitBreakerProvider.
|
|
6
|
+
*
|
|
7
|
+
* Request flow:
|
|
8
|
+
* AgentLoop → CircuitBreakerProvider → RetryProvider → actual provider
|
|
9
|
+
*
|
|
10
|
+
* CircuitBreaker is outermost so it sees already-retried failures,
|
|
11
|
+
* preventing the circuit from tripping on transient single errors.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createProvider(config: ProviderConfig): ILlmProvider;
|
|
14
|
+
export declare function registerProvider(id: string, constructor: ProviderConstructor): void;
|
|
15
|
+
export declare function getProviderRegistry(): Registry<ProviderConstructor>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=provider-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-factory.d.ts","sourceRoot":"","sources":["../src/provider-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAiB,MAAM,sBAAsB,CAAC;AAM/D,KAAK,mBAAmB,GAAG,KAAK,MAAM,EAAE,cAAc,KAAK,YAAY,CAAC;AAUxE;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,YAAY,CAUnE;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,mBAAmB,GAC/B,IAAI,CAEN;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,mBAAmB,CAAC,CAEnE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Registry, ProviderError } from '@charming_groot/core';
|
|
2
|
+
import { ClaudeProvider } from './claude-provider.js';
|
|
3
|
+
import { OpenAIProvider } from './openai-provider.js';
|
|
4
|
+
import { RetryProvider } from './retry-provider.js';
|
|
5
|
+
import { CircuitBreakerProvider } from './circuit-breaker.js';
|
|
6
|
+
const providerRegistry = new Registry('Provider');
|
|
7
|
+
providerRegistry.register('claude', ClaudeProvider);
|
|
8
|
+
providerRegistry.register('openai', OpenAIProvider);
|
|
9
|
+
providerRegistry.register('vllm', OpenAIProvider);
|
|
10
|
+
providerRegistry.register('ollama', OpenAIProvider);
|
|
11
|
+
providerRegistry.register('custom', OpenAIProvider);
|
|
12
|
+
/**
|
|
13
|
+
* Creates a provider wrapped with RetryProvider → CircuitBreakerProvider.
|
|
14
|
+
*
|
|
15
|
+
* Request flow:
|
|
16
|
+
* AgentLoop → CircuitBreakerProvider → RetryProvider → actual provider
|
|
17
|
+
*
|
|
18
|
+
* CircuitBreaker is outermost so it sees already-retried failures,
|
|
19
|
+
* preventing the circuit from tripping on transient single errors.
|
|
20
|
+
*/
|
|
21
|
+
export function createProvider(config) {
|
|
22
|
+
const Constructor = providerRegistry.tryGet(config.providerId);
|
|
23
|
+
if (!Constructor) {
|
|
24
|
+
throw new ProviderError(`Unknown provider: '${config.providerId}'. Available: ${providerRegistry.getAllNames().join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
const provider = new Constructor(config);
|
|
27
|
+
const withRetry = new RetryProvider(provider);
|
|
28
|
+
return new CircuitBreakerProvider(withRetry);
|
|
29
|
+
}
|
|
30
|
+
export function registerProvider(id, constructor) {
|
|
31
|
+
providerRegistry.register(id, constructor);
|
|
32
|
+
}
|
|
33
|
+
export function getProviderRegistry() {
|
|
34
|
+
return providerRegistry;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=provider-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-factory.js","sourceRoot":"","sources":["../src/provider-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAI9D,MAAM,gBAAgB,GAAG,IAAI,QAAQ,CAAsB,UAAU,CAAC,CAAC;AAEvE,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACpD,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACpD,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAClD,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACpD,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,aAAa,CACrB,sBAAsB,MAAM,CAAC,UAAU,iBAAiB,gBAAgB,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpG,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,IAAI,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,EAAU,EACV,WAAgC;IAEhC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ILlmProvider, Message, LlmResponse, StreamEvent, ToolDescription } from '@charming_groot/core';
|
|
2
|
+
export interface RetryConfig {
|
|
3
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
4
|
+
readonly maxRetries?: number;
|
|
5
|
+
/** Base delay in ms for exponential backoff (default: 1000) */
|
|
6
|
+
readonly baseDelayMs?: number;
|
|
7
|
+
/** Maximum delay in ms (default: 30000) */
|
|
8
|
+
readonly maxDelayMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Wraps an ILlmProvider with retry logic and exponential backoff.
|
|
12
|
+
* Retries on rate limits, transient network errors, and server errors.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RetryProvider implements ILlmProvider {
|
|
15
|
+
readonly providerId: string;
|
|
16
|
+
private readonly inner;
|
|
17
|
+
private readonly maxRetries;
|
|
18
|
+
private readonly baseDelayMs;
|
|
19
|
+
private readonly maxDelayMs;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
constructor(provider: ILlmProvider, config?: RetryConfig);
|
|
22
|
+
chat(messages: readonly Message[], tools?: readonly ToolDescription[]): Promise<LlmResponse>;
|
|
23
|
+
stream(messages: readonly Message[], tools?: readonly ToolDescription[]): AsyncIterable<StreamEvent>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=retry-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-provider.d.ts","sourceRoot":"","sources":["../src/retry-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAO9B,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAsCD;;;GAGG;AACH,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW;IASlD,IAAI,CACR,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,GACjC,OAAO,CAAC,WAAW,CAAC;IAwBhB,MAAM,CACX,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,GACjC,aAAa,CAAC,WAAW,CAAC;CAwB9B"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ProviderError, createChildLogger } from '@charming_groot/core';
|
|
2
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
3
|
+
const DEFAULT_BASE_DELAY_MS = 1000;
|
|
4
|
+
const DEFAULT_MAX_DELAY_MS = 30000;
|
|
5
|
+
function isRetryable(error) {
|
|
6
|
+
if (error instanceof ProviderError) {
|
|
7
|
+
const msg = error.message.toLowerCase();
|
|
8
|
+
return (msg.includes('rate limit') ||
|
|
9
|
+
msg.includes('429') ||
|
|
10
|
+
msg.includes('too many requests') ||
|
|
11
|
+
msg.includes('overloaded') ||
|
|
12
|
+
msg.includes('529') ||
|
|
13
|
+
msg.includes('timeout') ||
|
|
14
|
+
msg.includes('econnreset') ||
|
|
15
|
+
msg.includes('socket hang up') ||
|
|
16
|
+
msg.includes('503') ||
|
|
17
|
+
msg.includes('500'));
|
|
18
|
+
}
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
const msg = error.message.toLowerCase();
|
|
21
|
+
return (msg.includes('econnreset') ||
|
|
22
|
+
msg.includes('etimedout') ||
|
|
23
|
+
msg.includes('socket hang up') ||
|
|
24
|
+
msg.includes('fetch failed'));
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
function computeDelay(attempt, baseMs, maxMs) {
|
|
29
|
+
const exponential = baseMs * Math.pow(2, attempt);
|
|
30
|
+
const jitter = Math.random() * baseMs;
|
|
31
|
+
return Math.min(exponential + jitter, maxMs);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Wraps an ILlmProvider with retry logic and exponential backoff.
|
|
35
|
+
* Retries on rate limits, transient network errors, and server errors.
|
|
36
|
+
*/
|
|
37
|
+
export class RetryProvider {
|
|
38
|
+
providerId;
|
|
39
|
+
inner;
|
|
40
|
+
maxRetries;
|
|
41
|
+
baseDelayMs;
|
|
42
|
+
maxDelayMs;
|
|
43
|
+
logger;
|
|
44
|
+
constructor(provider, config) {
|
|
45
|
+
this.inner = provider;
|
|
46
|
+
this.providerId = provider.providerId;
|
|
47
|
+
this.maxRetries = config?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
48
|
+
this.baseDelayMs = config?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
49
|
+
this.maxDelayMs = config?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
50
|
+
this.logger = createChildLogger('retry-provider');
|
|
51
|
+
}
|
|
52
|
+
async chat(messages, tools) {
|
|
53
|
+
let lastError;
|
|
54
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
55
|
+
try {
|
|
56
|
+
return await this.inner.chat(messages, tools);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
lastError = error;
|
|
60
|
+
if (attempt < this.maxRetries && isRetryable(error)) {
|
|
61
|
+
const delay = computeDelay(attempt, this.baseDelayMs, this.maxDelayMs);
|
|
62
|
+
this.logger.warn({ attempt: attempt + 1, maxRetries: this.maxRetries, delayMs: Math.round(delay) }, 'Retrying after transient error');
|
|
63
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw lastError;
|
|
70
|
+
}
|
|
71
|
+
async *stream(messages, tools) {
|
|
72
|
+
let lastError;
|
|
73
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
yield* this.inner.stream(messages, tools);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
lastError = error;
|
|
80
|
+
if (attempt < this.maxRetries && isRetryable(error)) {
|
|
81
|
+
const delay = computeDelay(attempt, this.baseDelayMs, this.maxDelayMs);
|
|
82
|
+
this.logger.warn({ attempt: attempt + 1, maxRetries: this.maxRetries, delayMs: Math.round(delay) }, 'Retrying stream after transient error');
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw lastError;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=retry-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-provider.js","sourceRoot":"","sources":["../src/retry-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing utilities for extended thinking / <think> blocks.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - <think>...</think> tags (DeepSeek, Qwen, etc.)
|
|
6
|
+
* - Anthropic extended thinking content blocks (handled in provider)
|
|
7
|
+
*
|
|
8
|
+
* The thinkingMs is estimated from content length since the API
|
|
9
|
+
* does not provide wall-clock timing for the thinking phase.
|
|
10
|
+
*/
|
|
11
|
+
export interface ThinkTagResult {
|
|
12
|
+
/** The content inside <think> tags, or undefined if no tags found */
|
|
13
|
+
readonly thinkContent: string | undefined;
|
|
14
|
+
/** The remaining content after removing <think> tags */
|
|
15
|
+
readonly cleanContent: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Extract <think>...</think> block from the beginning of content.
|
|
19
|
+
* Returns the thinking content and cleaned content separately.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractThinkTag(content: string): ThinkTagResult;
|
|
22
|
+
/**
|
|
23
|
+
* Estimate thinking duration in milliseconds from thinking content.
|
|
24
|
+
* Uses a rough tokens-per-second heuristic since APIs don't report
|
|
25
|
+
* wall-clock thinking time.
|
|
26
|
+
*/
|
|
27
|
+
export declare function estimateThinkingMs(thinkContent: string): number;
|
|
28
|
+
//# sourceMappingURL=thinking-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking-parser.d.ts","sourceRoot":"","sources":["../src/thinking-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAS/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing utilities for extended thinking / <think> blocks.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - <think>...</think> tags (DeepSeek, Qwen, etc.)
|
|
6
|
+
* - Anthropic extended thinking content blocks (handled in provider)
|
|
7
|
+
*
|
|
8
|
+
* The thinkingMs is estimated from content length since the API
|
|
9
|
+
* does not provide wall-clock timing for the thinking phase.
|
|
10
|
+
*/
|
|
11
|
+
const THINK_TAG_REGEX = /^<think>([\s\S]*?)<\/think>\s*/;
|
|
12
|
+
/** Tokens per second estimate for thinking content */
|
|
13
|
+
const THINKING_TOKENS_PER_SEC = 80;
|
|
14
|
+
/** Characters per token estimate */
|
|
15
|
+
const CHARS_PER_TOKEN = 4;
|
|
16
|
+
/**
|
|
17
|
+
* Extract <think>...</think> block from the beginning of content.
|
|
18
|
+
* Returns the thinking content and cleaned content separately.
|
|
19
|
+
*/
|
|
20
|
+
export function extractThinkTag(content) {
|
|
21
|
+
const match = content.match(THINK_TAG_REGEX);
|
|
22
|
+
if (!match) {
|
|
23
|
+
return { thinkContent: undefined, cleanContent: content };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
thinkContent: match[1].trim(),
|
|
27
|
+
cleanContent: content.slice(match[0].length),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Estimate thinking duration in milliseconds from thinking content.
|
|
32
|
+
* Uses a rough tokens-per-second heuristic since APIs don't report
|
|
33
|
+
* wall-clock thinking time.
|
|
34
|
+
*/
|
|
35
|
+
export function estimateThinkingMs(thinkContent) {
|
|
36
|
+
const estimatedTokens = Math.ceil(thinkContent.length / CHARS_PER_TOKEN);
|
|
37
|
+
const seconds = estimatedTokens / THINKING_TOKENS_PER_SEC;
|
|
38
|
+
return Math.round(seconds * 1000);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=thinking-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking-parser.js","sourceRoot":"","sources":["../src/thinking-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEzD,sDAAsD;AACtD,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,oCAAoC;AACpC,MAAM,eAAe,GAAG,CAAC,CAAC;AAS1B;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IAC5D,CAAC;IACD,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QAC7B,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,eAAe,GAAG,uBAAuB,CAAC;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@charming_groot/providers",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "LLM provider adapters for CLI Agent (Anthropic Claude, OpenAI, retry, circuit-breaker)",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"clean": "rm -rf dist"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@charming_groot/core": "workspace:*",
|
|
24
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
25
|
+
"openai": "^4.73.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"typescript": "^5.4.0",
|
|
29
|
+
"vitest": "^1.6.0",
|
|
30
|
+
"@types/node": "^20.11.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": ["agent", "cli", "llm", "anthropic", "openai", "provider"],
|
|
33
|
+
"license": "MIT"
|
|
34
|
+
}
|