recker 1.0.4 → 1.0.6
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 +1 -1
- package/dist/ai/adaptive-timeout.d.ts +51 -0
- package/dist/ai/adaptive-timeout.d.ts.map +1 -0
- package/dist/ai/adaptive-timeout.js +208 -0
- package/dist/ai/client.d.ts +24 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +289 -0
- package/dist/ai/index.d.ts +10 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +6 -0
- package/dist/ai/providers/anthropic.d.ts +64 -0
- package/dist/ai/providers/anthropic.d.ts.map +1 -0
- package/dist/ai/providers/anthropic.js +367 -0
- package/dist/ai/providers/base.d.ts +49 -0
- package/dist/ai/providers/base.d.ts.map +1 -0
- package/dist/ai/providers/base.js +145 -0
- package/dist/ai/providers/index.d.ts +7 -0
- package/dist/ai/providers/index.d.ts.map +1 -0
- package/dist/ai/providers/index.js +3 -0
- package/dist/ai/providers/openai.d.ts +65 -0
- package/dist/ai/providers/openai.d.ts.map +1 -0
- package/dist/ai/providers/openai.js +298 -0
- package/dist/ai/rate-limiter.d.ts +44 -0
- package/dist/ai/rate-limiter.d.ts.map +1 -0
- package/dist/ai/rate-limiter.js +212 -0
- package/dist/bench/generator.d.ts +19 -0
- package/dist/bench/generator.d.ts.map +1 -0
- package/dist/bench/generator.js +86 -0
- package/dist/bench/stats.d.ts +35 -0
- package/dist/bench/stats.d.ts.map +1 -0
- package/dist/bench/stats.js +60 -0
- package/dist/cache/memory-storage.d.ts.map +1 -1
- package/dist/cache/memory-storage.js +0 -5
- package/dist/cli/handler.d.ts +11 -0
- package/dist/cli/handler.d.ts.map +1 -0
- package/dist/cli/handler.js +92 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +255 -0
- package/dist/cli/presets.d.ts +2 -0
- package/dist/cli/presets.d.ts.map +1 -0
- package/dist/cli/presets.js +67 -0
- package/dist/cli/tui/ai-chat.d.ts +3 -0
- package/dist/cli/tui/ai-chat.d.ts.map +1 -0
- package/dist/cli/tui/ai-chat.js +100 -0
- package/dist/cli/tui/load-dashboard.d.ts +3 -0
- package/dist/cli/tui/load-dashboard.d.ts.map +1 -0
- package/dist/cli/tui/load-dashboard.js +117 -0
- package/dist/cli/tui/shell.d.ts +27 -0
- package/dist/cli/tui/shell.d.ts.map +1 -0
- package/dist/cli/tui/shell.js +386 -0
- package/dist/cli/tui/websocket.d.ts +2 -0
- package/dist/cli/tui/websocket.d.ts.map +1 -0
- package/dist/cli/tui/websocket.js +87 -0
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/core/client.d.ts +1 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +4 -2
- package/dist/core/request-promise.d.ts +1 -1
- package/dist/core/request-promise.d.ts.map +1 -1
- package/dist/mcp/contract.d.ts +1 -1
- package/dist/mcp/contract.d.ts.map +1 -1
- package/dist/plugins/cache.d.ts.map +1 -1
- package/dist/plugins/cache.js +0 -7
- package/dist/plugins/scrape.d.ts.map +1 -1
- package/dist/plugins/scrape.js +9 -14
- package/dist/protocols/ftp.d.ts +28 -5
- package/dist/protocols/ftp.d.ts.map +1 -1
- package/dist/protocols/ftp.js +549 -136
- package/dist/protocols/sftp.d.ts +4 -2
- package/dist/protocols/sftp.d.ts.map +1 -1
- package/dist/protocols/sftp.js +16 -2
- package/dist/protocols/telnet.d.ts +37 -5
- package/dist/protocols/telnet.d.ts.map +1 -1
- package/dist/protocols/telnet.js +434 -58
- package/dist/runner/request-runner.d.ts.map +1 -1
- package/dist/runner/request-runner.js +0 -1
- package/dist/scrape/document.d.ts +3 -2
- package/dist/scrape/document.d.ts.map +1 -1
- package/dist/scrape/document.js +15 -3
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/mock-udp-server.d.ts +44 -0
- package/dist/testing/mock-udp-server.d.ts.map +1 -0
- package/dist/testing/mock-udp-server.js +188 -0
- package/dist/transport/base-udp.d.ts +36 -0
- package/dist/transport/base-udp.d.ts.map +1 -0
- package/dist/transport/base-udp.js +188 -0
- package/dist/transport/udp-response.d.ts +65 -0
- package/dist/transport/udp-response.d.ts.map +1 -0
- package/dist/transport/udp-response.js +269 -0
- package/dist/transport/udp.d.ts +22 -0
- package/dist/transport/udp.d.ts.map +1 -0
- package/dist/transport/udp.js +260 -0
- package/dist/types/ai.d.ts +268 -0
- package/dist/types/ai.d.ts.map +1 -0
- package/dist/types/ai.js +1 -0
- package/dist/types/udp.d.ts +138 -0
- package/dist/types/udp.d.ts.map +1 -0
- package/dist/types/udp.js +1 -0
- package/dist/udp/index.d.ts +6 -0
- package/dist/udp/index.d.ts.map +1 -0
- package/dist/udp/index.js +3 -0
- package/dist/utils/chart.d.ts +15 -0
- package/dist/utils/chart.d.ts.map +1 -0
- package/dist/utils/chart.js +94 -0
- package/dist/utils/colors.d.ts +27 -0
- package/dist/utils/colors.d.ts.map +1 -0
- package/dist/utils/colors.js +50 -0
- package/dist/utils/optional-require.d.ts +20 -0
- package/dist/utils/optional-require.d.ts.map +1 -0
- package/dist/utils/optional-require.js +105 -0
- package/package.json +53 -12
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://www.npmjs.com/package/recker)
|
|
13
13
|
[](https://www.typescriptlang.org/)
|
|
14
14
|
[](https://nodejs.org/)
|
|
15
|
-
[](https://github.com/forattini-dev/recker)
|
|
16
16
|
[](https://github.com/forattini-dev/recker/blob/main/LICENSE)
|
|
17
17
|
|
|
18
18
|
[Documentation](https://forattini-dev.github.io/recker) · [Examples](./docs/examples/README.md) · [Migration](./docs/migration.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AITimeoutOptions } from '../types/ai.js';
|
|
2
|
+
interface LatencySample {
|
|
3
|
+
ttft: number;
|
|
4
|
+
betweenTokens: number;
|
|
5
|
+
total: number;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
}
|
|
8
|
+
interface ModelProfile {
|
|
9
|
+
samples: LatencySample[];
|
|
10
|
+
avgTtft: number;
|
|
11
|
+
avgBetweenTokens: number;
|
|
12
|
+
avgTotal: number;
|
|
13
|
+
p95Ttft: number;
|
|
14
|
+
p95BetweenTokens: number;
|
|
15
|
+
p95Total: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class AdaptiveTimeoutManager {
|
|
18
|
+
private profiles;
|
|
19
|
+
private maxSamples;
|
|
20
|
+
private sampleTtl;
|
|
21
|
+
getTimeouts(model: string, options?: AITimeoutOptions): Required<AITimeoutOptions>;
|
|
22
|
+
recordSample(model: string, ttft: number, betweenTokens: number, total: number): void;
|
|
23
|
+
getProfile(model: string): ModelProfile | undefined;
|
|
24
|
+
clear(): void;
|
|
25
|
+
export(): Record<string, LatencySample[]>;
|
|
26
|
+
import(data: Record<string, LatencySample[]>): void;
|
|
27
|
+
private findPreset;
|
|
28
|
+
private recalculateStats;
|
|
29
|
+
private percentile;
|
|
30
|
+
}
|
|
31
|
+
export declare class StreamTimeoutController {
|
|
32
|
+
private timeouts;
|
|
33
|
+
private abortController;
|
|
34
|
+
private firstTokenTimer;
|
|
35
|
+
private betweenTokenTimer;
|
|
36
|
+
private totalTimer;
|
|
37
|
+
private receivedFirstToken;
|
|
38
|
+
private startTime;
|
|
39
|
+
constructor(timeouts: Required<AITimeoutOptions>);
|
|
40
|
+
get signal(): AbortSignal;
|
|
41
|
+
start(): void;
|
|
42
|
+
recordToken(): void;
|
|
43
|
+
complete(): void;
|
|
44
|
+
abort(reason: string): void;
|
|
45
|
+
get elapsed(): number;
|
|
46
|
+
get ttft(): number | null;
|
|
47
|
+
private clearTimers;
|
|
48
|
+
}
|
|
49
|
+
export declare const adaptiveTimeouts: AdaptiveTimeoutManager;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=adaptive-timeout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive-timeout.d.ts","sourceRoot":"","sources":["../../src/ai/adaptive-timeout.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKvD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,UAAU,YAAY;IACpB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqCD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,SAAS,CAA+B;IAKhD,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,QAAQ,CAAC,gBAAgB,CAAC;IAgClF,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAyCrF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAOnD,KAAK,IAAI,IAAI;IAOb,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IAWzC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,GAAG,IAAI;IAmBnD,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,UAAU;CAMnB;AAOD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,eAAe,CAA8C;IACrE,OAAO,CAAC,iBAAiB,CAA8C;IACvE,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,SAAS,CAAS;gBAEd,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IAShD,IAAI,MAAM,IAAI,WAAW,CAExB;IAKD,KAAK,IAAI,IAAI;IAqBb,WAAW,IAAI,IAAI;IAyBnB,QAAQ,IAAI,IAAI;IAOhB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ3B,IAAI,OAAO,IAAI,MAAM,CAEpB;IAKD,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAExB;IAKD,OAAO,CAAC,WAAW;CAcpB;AAKD,eAAO,MAAM,gBAAgB,wBAA+B,CAAC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUTS = {
|
|
2
|
+
firstToken: 60000,
|
|
3
|
+
betweenTokens: 10000,
|
|
4
|
+
total: 300000,
|
|
5
|
+
};
|
|
6
|
+
const MODEL_PRESETS = {
|
|
7
|
+
'gpt-5.1-mini': { firstToken: 10000, betweenTokens: 2000, total: 60000 },
|
|
8
|
+
'gpt-5.1-nano': { firstToken: 5000, betweenTokens: 1000, total: 30000 },
|
|
9
|
+
'claude-haiku': { firstToken: 10000, betweenTokens: 2000, total: 60000 },
|
|
10
|
+
'gpt-5.1': { firstToken: 30000, betweenTokens: 5000, total: 120000 },
|
|
11
|
+
'claude-sonnet': { firstToken: 30000, betweenTokens: 5000, total: 120000 },
|
|
12
|
+
'o3': { firstToken: 120000, betweenTokens: 30000, total: 600000 },
|
|
13
|
+
'o3-mini': { firstToken: 60000, betweenTokens: 15000, total: 300000 },
|
|
14
|
+
'o4-mini': { firstToken: 60000, betweenTokens: 15000, total: 300000 },
|
|
15
|
+
'claude-opus': { firstToken: 60000, betweenTokens: 10000, total: 300000 },
|
|
16
|
+
'text-embedding': { firstToken: 10000, betweenTokens: 0, total: 30000 },
|
|
17
|
+
};
|
|
18
|
+
export class AdaptiveTimeoutManager {
|
|
19
|
+
profiles = new Map();
|
|
20
|
+
maxSamples = 100;
|
|
21
|
+
sampleTtl = 24 * 60 * 60 * 1000;
|
|
22
|
+
getTimeouts(model, options) {
|
|
23
|
+
let timeouts = { ...DEFAULT_TIMEOUTS, adaptive: true };
|
|
24
|
+
const preset = this.findPreset(model);
|
|
25
|
+
if (preset) {
|
|
26
|
+
timeouts = { ...timeouts, ...preset };
|
|
27
|
+
}
|
|
28
|
+
if (options?.adaptive !== false) {
|
|
29
|
+
const profile = this.profiles.get(model);
|
|
30
|
+
if (profile && profile.samples.length >= 5) {
|
|
31
|
+
timeouts.firstToken = Math.ceil(profile.p95Ttft * 1.2);
|
|
32
|
+
timeouts.betweenTokens = Math.ceil(profile.p95BetweenTokens * 1.2);
|
|
33
|
+
timeouts.total = Math.ceil(profile.p95Total * 1.2);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (options?.firstToken !== undefined)
|
|
37
|
+
timeouts.firstToken = options.firstToken;
|
|
38
|
+
if (options?.betweenTokens !== undefined)
|
|
39
|
+
timeouts.betweenTokens = options.betweenTokens;
|
|
40
|
+
if (options?.total !== undefined)
|
|
41
|
+
timeouts.total = options.total;
|
|
42
|
+
return timeouts;
|
|
43
|
+
}
|
|
44
|
+
recordSample(model, ttft, betweenTokens, total) {
|
|
45
|
+
const sample = {
|
|
46
|
+
ttft,
|
|
47
|
+
betweenTokens,
|
|
48
|
+
total,
|
|
49
|
+
timestamp: Date.now(),
|
|
50
|
+
};
|
|
51
|
+
let profile = this.profiles.get(model);
|
|
52
|
+
if (!profile) {
|
|
53
|
+
profile = {
|
|
54
|
+
samples: [],
|
|
55
|
+
avgTtft: 0,
|
|
56
|
+
avgBetweenTokens: 0,
|
|
57
|
+
avgTotal: 0,
|
|
58
|
+
p95Ttft: 0,
|
|
59
|
+
p95BetweenTokens: 0,
|
|
60
|
+
p95Total: 0,
|
|
61
|
+
};
|
|
62
|
+
this.profiles.set(model, profile);
|
|
63
|
+
}
|
|
64
|
+
profile.samples.push(sample);
|
|
65
|
+
const cutoff = Date.now() - this.sampleTtl;
|
|
66
|
+
profile.samples = profile.samples.filter((s) => s.timestamp > cutoff);
|
|
67
|
+
if (profile.samples.length > this.maxSamples) {
|
|
68
|
+
profile.samples = profile.samples.slice(-this.maxSamples);
|
|
69
|
+
}
|
|
70
|
+
this.recalculateStats(profile);
|
|
71
|
+
}
|
|
72
|
+
getProfile(model) {
|
|
73
|
+
return this.profiles.get(model);
|
|
74
|
+
}
|
|
75
|
+
clear() {
|
|
76
|
+
this.profiles.clear();
|
|
77
|
+
}
|
|
78
|
+
export() {
|
|
79
|
+
const data = {};
|
|
80
|
+
for (const [model, profile] of this.profiles) {
|
|
81
|
+
data[model] = profile.samples;
|
|
82
|
+
}
|
|
83
|
+
return data;
|
|
84
|
+
}
|
|
85
|
+
import(data) {
|
|
86
|
+
for (const [model, samples] of Object.entries(data)) {
|
|
87
|
+
const profile = {
|
|
88
|
+
samples,
|
|
89
|
+
avgTtft: 0,
|
|
90
|
+
avgBetweenTokens: 0,
|
|
91
|
+
avgTotal: 0,
|
|
92
|
+
p95Ttft: 0,
|
|
93
|
+
p95BetweenTokens: 0,
|
|
94
|
+
p95Total: 0,
|
|
95
|
+
};
|
|
96
|
+
this.recalculateStats(profile);
|
|
97
|
+
this.profiles.set(model, profile);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
findPreset(model) {
|
|
101
|
+
if (MODEL_PRESETS[model]) {
|
|
102
|
+
return MODEL_PRESETS[model];
|
|
103
|
+
}
|
|
104
|
+
const modelLower = model.toLowerCase();
|
|
105
|
+
for (const [key, preset] of Object.entries(MODEL_PRESETS)) {
|
|
106
|
+
if (modelLower.includes(key.toLowerCase())) {
|
|
107
|
+
return preset;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
recalculateStats(profile) {
|
|
113
|
+
if (profile.samples.length === 0)
|
|
114
|
+
return;
|
|
115
|
+
const ttfts = profile.samples.map((s) => s.ttft);
|
|
116
|
+
const betweens = profile.samples.map((s) => s.betweenTokens);
|
|
117
|
+
const totals = profile.samples.map((s) => s.total);
|
|
118
|
+
profile.avgTtft = ttfts.reduce((a, b) => a + b, 0) / ttfts.length;
|
|
119
|
+
profile.avgBetweenTokens = betweens.reduce((a, b) => a + b, 0) / betweens.length;
|
|
120
|
+
profile.avgTotal = totals.reduce((a, b) => a + b, 0) / totals.length;
|
|
121
|
+
profile.p95Ttft = this.percentile(ttfts, 95);
|
|
122
|
+
profile.p95BetweenTokens = this.percentile(betweens, 95);
|
|
123
|
+
profile.p95Total = this.percentile(totals, 95);
|
|
124
|
+
}
|
|
125
|
+
percentile(values, p) {
|
|
126
|
+
if (values.length === 0)
|
|
127
|
+
return 0;
|
|
128
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
129
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1;
|
|
130
|
+
return sorted[Math.max(0, index)];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export class StreamTimeoutController {
|
|
134
|
+
timeouts;
|
|
135
|
+
abortController;
|
|
136
|
+
firstTokenTimer = null;
|
|
137
|
+
betweenTokenTimer = null;
|
|
138
|
+
totalTimer = null;
|
|
139
|
+
receivedFirstToken = false;
|
|
140
|
+
startTime;
|
|
141
|
+
constructor(timeouts) {
|
|
142
|
+
this.timeouts = timeouts;
|
|
143
|
+
this.abortController = new AbortController();
|
|
144
|
+
this.startTime = Date.now();
|
|
145
|
+
}
|
|
146
|
+
get signal() {
|
|
147
|
+
return this.abortController.signal;
|
|
148
|
+
}
|
|
149
|
+
start() {
|
|
150
|
+
if (this.timeouts.firstToken > 0) {
|
|
151
|
+
this.firstTokenTimer = setTimeout(() => {
|
|
152
|
+
if (!this.receivedFirstToken) {
|
|
153
|
+
this.abort('First token timeout exceeded');
|
|
154
|
+
}
|
|
155
|
+
}, this.timeouts.firstToken);
|
|
156
|
+
}
|
|
157
|
+
if (this.timeouts.total > 0) {
|
|
158
|
+
this.totalTimer = setTimeout(() => {
|
|
159
|
+
this.abort('Total timeout exceeded');
|
|
160
|
+
}, this.timeouts.total);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
recordToken() {
|
|
164
|
+
if (!this.receivedFirstToken) {
|
|
165
|
+
this.receivedFirstToken = true;
|
|
166
|
+
if (this.firstTokenTimer) {
|
|
167
|
+
clearTimeout(this.firstTokenTimer);
|
|
168
|
+
this.firstTokenTimer = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (this.betweenTokenTimer) {
|
|
172
|
+
clearTimeout(this.betweenTokenTimer);
|
|
173
|
+
}
|
|
174
|
+
if (this.timeouts.betweenTokens > 0) {
|
|
175
|
+
this.betweenTokenTimer = setTimeout(() => {
|
|
176
|
+
this.abort('Token stream stalled');
|
|
177
|
+
}, this.timeouts.betweenTokens);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
complete() {
|
|
181
|
+
this.clearTimers();
|
|
182
|
+
}
|
|
183
|
+
abort(reason) {
|
|
184
|
+
this.clearTimers();
|
|
185
|
+
this.abortController.abort(new Error(reason));
|
|
186
|
+
}
|
|
187
|
+
get elapsed() {
|
|
188
|
+
return Date.now() - this.startTime;
|
|
189
|
+
}
|
|
190
|
+
get ttft() {
|
|
191
|
+
return this.receivedFirstToken ? Date.now() - this.startTime : null;
|
|
192
|
+
}
|
|
193
|
+
clearTimers() {
|
|
194
|
+
if (this.firstTokenTimer) {
|
|
195
|
+
clearTimeout(this.firstTokenTimer);
|
|
196
|
+
this.firstTokenTimer = null;
|
|
197
|
+
}
|
|
198
|
+
if (this.betweenTokenTimer) {
|
|
199
|
+
clearTimeout(this.betweenTokenTimer);
|
|
200
|
+
this.betweenTokenTimer = null;
|
|
201
|
+
}
|
|
202
|
+
if (this.totalTimer) {
|
|
203
|
+
clearTimeout(this.totalTimer);
|
|
204
|
+
this.totalTimer = null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export const adaptiveTimeouts = new AdaptiveTimeoutManager();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AIClient, AIClientConfig, ChatOptions, AIResponse, AIStream, EmbedOptions, EmbedResponse, AIMetrics } from '../types/ai.js';
|
|
2
|
+
export declare class AIClientImpl implements AIClient {
|
|
3
|
+
private config;
|
|
4
|
+
private providers;
|
|
5
|
+
private _metrics;
|
|
6
|
+
constructor(config?: AIClientConfig);
|
|
7
|
+
get metrics(): AIMetrics;
|
|
8
|
+
chat(optionsOrPrompt: ChatOptions | string): Promise<AIResponse>;
|
|
9
|
+
stream(options: ChatOptions): Promise<AIStream>;
|
|
10
|
+
embed(options: EmbedOptions): Promise<EmbedResponse>;
|
|
11
|
+
extend(defaults: Partial<ChatOptions>): AIClient;
|
|
12
|
+
private initializeProviders;
|
|
13
|
+
private getProvider;
|
|
14
|
+
private normalizeOptions;
|
|
15
|
+
private executeWithRetry;
|
|
16
|
+
private shouldRetry;
|
|
17
|
+
private calculateBackoff;
|
|
18
|
+
private reduceContext;
|
|
19
|
+
private logResponse;
|
|
20
|
+
private sleep;
|
|
21
|
+
}
|
|
22
|
+
export declare function createAIClient(config?: AIClientConfig): AIClient;
|
|
23
|
+
export declare const ai: AIClient;
|
|
24
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ai/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,SAAS,EAGV,MAAM,gBAAgB,CAAC;AAgIxB,qBAAa,YAAa,YAAW,QAAQ;IAC3C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,QAAQ,CAAsC;gBAE1C,MAAM,GAAE,cAAmB;IAevC,IAAI,OAAO,IAAI,SAAS,CAEvB;IAKK,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA8BhE,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ/C,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAQ1D,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,QAAQ;IAwBhD,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,gBAAgB;YAYV,gBAAgB;IAwE9B,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,KAAK;CAGd;AAKD,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,QAAQ,CAEhE;AAKD,eAAO,MAAM,EAAE,UAAmB,CAAC"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { AIError, RateLimitError, ContextLengthError, OverloadedError } from './providers/base.js';
|
|
2
|
+
import { OpenAIProvider } from './providers/openai.js';
|
|
3
|
+
import { AnthropicProvider } from './providers/anthropic.js';
|
|
4
|
+
class AIMetricsImpl {
|
|
5
|
+
data = {
|
|
6
|
+
totalRequests: 0,
|
|
7
|
+
totalTokens: 0,
|
|
8
|
+
totalCost: 0,
|
|
9
|
+
totalLatencyTtft: 0,
|
|
10
|
+
totalLatencyTotal: 0,
|
|
11
|
+
errorCount: 0,
|
|
12
|
+
cacheHits: 0,
|
|
13
|
+
byModel: new Map(),
|
|
14
|
+
byProvider: new Map(),
|
|
15
|
+
};
|
|
16
|
+
get totalRequests() {
|
|
17
|
+
return this.data.totalRequests;
|
|
18
|
+
}
|
|
19
|
+
get totalTokens() {
|
|
20
|
+
return this.data.totalTokens;
|
|
21
|
+
}
|
|
22
|
+
get totalCost() {
|
|
23
|
+
return this.data.totalCost;
|
|
24
|
+
}
|
|
25
|
+
get avgLatency() {
|
|
26
|
+
const count = this.data.totalRequests || 1;
|
|
27
|
+
return {
|
|
28
|
+
ttft: this.data.totalLatencyTtft / count,
|
|
29
|
+
total: this.data.totalLatencyTotal / count,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
get errorRate() {
|
|
33
|
+
const total = this.data.totalRequests + this.data.errorCount;
|
|
34
|
+
return total > 0 ? this.data.errorCount / total : 0;
|
|
35
|
+
}
|
|
36
|
+
get cacheHitRate() {
|
|
37
|
+
return this.data.totalRequests > 0
|
|
38
|
+
? this.data.cacheHits / this.data.totalRequests
|
|
39
|
+
: 0;
|
|
40
|
+
}
|
|
41
|
+
record(response) {
|
|
42
|
+
this.data.totalRequests++;
|
|
43
|
+
this.data.totalTokens += response.usage.totalTokens;
|
|
44
|
+
this.data.totalCost += response.cost?.totalCost || 0;
|
|
45
|
+
this.data.totalLatencyTtft += response.latency.ttft;
|
|
46
|
+
this.data.totalLatencyTotal += response.latency.total;
|
|
47
|
+
if (response.cached) {
|
|
48
|
+
this.data.cacheHits++;
|
|
49
|
+
}
|
|
50
|
+
const modelStats = this.data.byModel.get(response.model) || { requests: 0, tokens: 0, cost: 0 };
|
|
51
|
+
modelStats.requests++;
|
|
52
|
+
modelStats.tokens += response.usage.totalTokens;
|
|
53
|
+
modelStats.cost += response.cost?.totalCost || 0;
|
|
54
|
+
this.data.byModel.set(response.model, modelStats);
|
|
55
|
+
const providerStats = this.data.byProvider.get(response.provider) || { requests: 0, tokens: 0, cost: 0 };
|
|
56
|
+
providerStats.requests++;
|
|
57
|
+
providerStats.tokens += response.usage.totalTokens;
|
|
58
|
+
providerStats.cost += response.cost?.totalCost || 0;
|
|
59
|
+
this.data.byProvider.set(response.provider, providerStats);
|
|
60
|
+
}
|
|
61
|
+
recordError() {
|
|
62
|
+
this.data.errorCount++;
|
|
63
|
+
}
|
|
64
|
+
summary() {
|
|
65
|
+
return {
|
|
66
|
+
totalRequests: this.totalRequests,
|
|
67
|
+
totalTokens: this.totalTokens,
|
|
68
|
+
totalCost: this.totalCost,
|
|
69
|
+
avgLatency: this.avgLatency,
|
|
70
|
+
errorRate: this.errorRate,
|
|
71
|
+
cacheHitRate: this.cacheHitRate,
|
|
72
|
+
byModel: Object.fromEntries(this.data.byModel),
|
|
73
|
+
byProvider: Object.fromEntries(this.data.byProvider),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
reset() {
|
|
77
|
+
this.data = {
|
|
78
|
+
totalRequests: 0,
|
|
79
|
+
totalTokens: 0,
|
|
80
|
+
totalCost: 0,
|
|
81
|
+
totalLatencyTtft: 0,
|
|
82
|
+
totalLatencyTotal: 0,
|
|
83
|
+
errorCount: 0,
|
|
84
|
+
cacheHits: 0,
|
|
85
|
+
byModel: new Map(),
|
|
86
|
+
byProvider: new Map(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class AIClientImpl {
|
|
91
|
+
config;
|
|
92
|
+
providers = new Map();
|
|
93
|
+
_metrics = new AIMetricsImpl();
|
|
94
|
+
constructor(config = {}) {
|
|
95
|
+
this.config = {
|
|
96
|
+
defaultProvider: 'openai',
|
|
97
|
+
observability: true,
|
|
98
|
+
debug: false,
|
|
99
|
+
...config,
|
|
100
|
+
};
|
|
101
|
+
this.initializeProviders();
|
|
102
|
+
}
|
|
103
|
+
get metrics() {
|
|
104
|
+
return this._metrics;
|
|
105
|
+
}
|
|
106
|
+
async chat(optionsOrPrompt) {
|
|
107
|
+
const options = this.normalizeOptions(optionsOrPrompt);
|
|
108
|
+
const provider = this.getProvider(options.provider);
|
|
109
|
+
try {
|
|
110
|
+
const response = await this.executeWithRetry(() => provider.chat(options), options);
|
|
111
|
+
if (this.config.observability) {
|
|
112
|
+
this._metrics.record(response);
|
|
113
|
+
}
|
|
114
|
+
if (this.config.debug) {
|
|
115
|
+
this.logResponse(response);
|
|
116
|
+
}
|
|
117
|
+
return response;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (this.config.observability) {
|
|
121
|
+
this._metrics.recordError();
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async stream(options) {
|
|
127
|
+
const provider = this.getProvider(options.provider);
|
|
128
|
+
return provider.stream(options);
|
|
129
|
+
}
|
|
130
|
+
async embed(options) {
|
|
131
|
+
const provider = this.getProvider(options.provider);
|
|
132
|
+
return provider.embed(options);
|
|
133
|
+
}
|
|
134
|
+
extend(defaults) {
|
|
135
|
+
const parent = this;
|
|
136
|
+
return {
|
|
137
|
+
chat: async (optionsOrPrompt) => {
|
|
138
|
+
const options = parent.normalizeOptions(optionsOrPrompt);
|
|
139
|
+
return parent.chat({ ...defaults, ...options, messages: [...(defaults.messages || []), ...options.messages] });
|
|
140
|
+
},
|
|
141
|
+
stream: async (options) => {
|
|
142
|
+
return parent.stream({ ...defaults, ...options, messages: [...(defaults.messages || []), ...options.messages] });
|
|
143
|
+
},
|
|
144
|
+
embed: async (options) => {
|
|
145
|
+
return parent.embed({ provider: defaults.provider, ...options });
|
|
146
|
+
},
|
|
147
|
+
extend: (moreDefaults) => {
|
|
148
|
+
return parent.extend({ ...defaults, ...moreDefaults });
|
|
149
|
+
},
|
|
150
|
+
metrics: parent.metrics,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
initializeProviders() {
|
|
154
|
+
const openaiConfig = this.config.providers?.openai || {};
|
|
155
|
+
this.providers.set('openai', new OpenAIProvider(openaiConfig));
|
|
156
|
+
const anthropicConfig = this.config.providers?.anthropic || {};
|
|
157
|
+
this.providers.set('anthropic', new AnthropicProvider(anthropicConfig));
|
|
158
|
+
}
|
|
159
|
+
getProvider(name) {
|
|
160
|
+
const providerName = name || this.config.defaultProvider || 'openai';
|
|
161
|
+
const provider = this.providers.get(providerName);
|
|
162
|
+
if (!provider) {
|
|
163
|
+
throw new AIError(`Provider not found: ${providerName}`, providerName, 'provider_not_found');
|
|
164
|
+
}
|
|
165
|
+
return provider;
|
|
166
|
+
}
|
|
167
|
+
normalizeOptions(optionsOrPrompt) {
|
|
168
|
+
if (typeof optionsOrPrompt === 'string') {
|
|
169
|
+
return {
|
|
170
|
+
messages: [{ role: 'user', content: optionsOrPrompt }],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return optionsOrPrompt;
|
|
174
|
+
}
|
|
175
|
+
async executeWithRetry(fn, options) {
|
|
176
|
+
const retryConfig = options.retry || this.config.retry;
|
|
177
|
+
if (!retryConfig) {
|
|
178
|
+
return fn();
|
|
179
|
+
}
|
|
180
|
+
const maxAttempts = retryConfig.maxAttempts || 3;
|
|
181
|
+
const retryOn = retryConfig.on || ['rate_limit', 'overloaded', 'timeout'];
|
|
182
|
+
let lastError = null;
|
|
183
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
184
|
+
try {
|
|
185
|
+
return await fn();
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
lastError = error;
|
|
189
|
+
if (!this.shouldRetry(error, retryOn, attempt, maxAttempts)) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
const delay = this.calculateBackoff(attempt, retryConfig.backoff);
|
|
193
|
+
if (retryConfig.onRetry) {
|
|
194
|
+
retryConfig.onRetry(attempt, error);
|
|
195
|
+
}
|
|
196
|
+
if (this.config.debug) {
|
|
197
|
+
console.log(`[recker/ai] Retry ${attempt}/${maxAttempts} after ${delay}ms`);
|
|
198
|
+
}
|
|
199
|
+
await this.sleep(delay);
|
|
200
|
+
if (retryConfig.fallback && options.model) {
|
|
201
|
+
const fallbackModel = retryConfig.fallback[options.model];
|
|
202
|
+
if (fallbackModel) {
|
|
203
|
+
options = { ...options, model: fallbackModel };
|
|
204
|
+
if (this.config.debug) {
|
|
205
|
+
console.log(`[recker/ai] Falling back to model: ${fallbackModel}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (retryConfig.reduceContext &&
|
|
210
|
+
error instanceof ContextLengthError &&
|
|
211
|
+
options.messages.length > 2) {
|
|
212
|
+
options = {
|
|
213
|
+
...options,
|
|
214
|
+
messages: this.reduceContext(options.messages),
|
|
215
|
+
};
|
|
216
|
+
if (this.config.debug) {
|
|
217
|
+
console.log(`[recker/ai] Reduced context to ${options.messages.length} messages`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
throw lastError || new Error('Retry exhausted');
|
|
223
|
+
}
|
|
224
|
+
shouldRetry(error, retryOn, attempt, maxAttempts) {
|
|
225
|
+
if (attempt >= maxAttempts)
|
|
226
|
+
return false;
|
|
227
|
+
if (error instanceof RateLimitError && retryOn.includes('rate_limit'))
|
|
228
|
+
return true;
|
|
229
|
+
if (error instanceof OverloadedError && retryOn.includes('overloaded'))
|
|
230
|
+
return true;
|
|
231
|
+
if (error instanceof ContextLengthError && retryOn.includes('context_length_exceeded'))
|
|
232
|
+
return true;
|
|
233
|
+
if (error.name === 'TimeoutError' && retryOn.includes('timeout'))
|
|
234
|
+
return true;
|
|
235
|
+
if (error instanceof AIError && error.retryable && retryOn.includes('server_error'))
|
|
236
|
+
return true;
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
calculateBackoff(attempt, strategy = 'exponential') {
|
|
240
|
+
const baseDelay = 1000;
|
|
241
|
+
const maxDelay = 30000;
|
|
242
|
+
let delay;
|
|
243
|
+
switch (strategy) {
|
|
244
|
+
case 'linear':
|
|
245
|
+
delay = baseDelay * attempt;
|
|
246
|
+
break;
|
|
247
|
+
case 'exponential':
|
|
248
|
+
delay = baseDelay * Math.pow(2, attempt - 1);
|
|
249
|
+
break;
|
|
250
|
+
case 'decorrelated':
|
|
251
|
+
delay = Math.random() * Math.min(maxDelay, baseDelay * Math.pow(3, attempt - 1));
|
|
252
|
+
break;
|
|
253
|
+
default:
|
|
254
|
+
delay = baseDelay * attempt;
|
|
255
|
+
}
|
|
256
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
257
|
+
return Math.min(maxDelay, delay + jitter);
|
|
258
|
+
}
|
|
259
|
+
reduceContext(messages) {
|
|
260
|
+
if (messages.length <= 2)
|
|
261
|
+
return messages;
|
|
262
|
+
const systemMessages = messages.filter((m) => m.role === 'system');
|
|
263
|
+
const nonSystemMessages = messages.filter((m) => m.role !== 'system');
|
|
264
|
+
if (nonSystemMessages.length <= 4) {
|
|
265
|
+
return messages;
|
|
266
|
+
}
|
|
267
|
+
const reduced = [
|
|
268
|
+
...systemMessages,
|
|
269
|
+
nonSystemMessages[0],
|
|
270
|
+
...nonSystemMessages.slice(-2),
|
|
271
|
+
];
|
|
272
|
+
return reduced;
|
|
273
|
+
}
|
|
274
|
+
logResponse(response) {
|
|
275
|
+
console.log(`[recker/ai] ${response.provider}/${response.model}`);
|
|
276
|
+
console.log(` Tokens: ${response.usage.inputTokens} in / ${response.usage.outputTokens} out`);
|
|
277
|
+
console.log(` Latency: TTFT=${response.latency.ttft.toFixed(0)}ms, Total=${response.latency.total.toFixed(0)}ms`);
|
|
278
|
+
if (response.cost) {
|
|
279
|
+
console.log(` Cost: $${response.cost.totalCost.toFixed(6)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
sleep(ms) {
|
|
283
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
export function createAIClient(config) {
|
|
287
|
+
return new AIClientImpl(config);
|
|
288
|
+
}
|
|
289
|
+
export const ai = createAIClient();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AIClientImpl, createAIClient, ai } from './client.js';
|
|
2
|
+
export { BaseAIProvider, AIError, RateLimitError, ContextLengthError, OverloadedError, AuthenticationError } from './providers/base.js';
|
|
3
|
+
export { OpenAIProvider } from './providers/openai.js';
|
|
4
|
+
export { AnthropicProvider } from './providers/anthropic.js';
|
|
5
|
+
export { AdaptiveTimeoutManager, StreamTimeoutController, adaptiveTimeouts } from './adaptive-timeout.js';
|
|
6
|
+
export { TokenRateLimiter, RateLimitExceededError, createRateLimiter, tokenEstimators, PROVIDER_RATE_LIMITS, } from './rate-limiter.js';
|
|
7
|
+
export type { AIProvider, MessageRole, ChatMessage, ContentPart, ToolDefinition, ToolCall, TokenUsage, CostInfo, AILatency, AITimeoutOptions, AIRetryOptions, ResponseFormat, ChatOptions, EmbedOptions, AIResponse, EmbedResponse, StreamEventType, StreamEvent, TextStreamEvent, ToolCallStreamEvent, ToolCallDeltaStreamEvent, UsageStreamEvent, DoneStreamEvent, ErrorStreamEvent, AIStream, ProviderConfig, AIClientConfig, TokenRateLimitConfig, SemanticCacheConfig, CacheStorage, AIClient, AIMetrics, AIMetricsSummary, AgentTool, AgentConfig, AgentEventType, AgentStreamEvent, Agent, } from '../types/ai.js';
|
|
8
|
+
export type { OpenAIConfig } from './providers/openai.js';
|
|
9
|
+
export type { AnthropicConfig } from './providers/anthropic.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AA2DA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACxI,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG1G,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EAEV,UAAU,EACV,WAAW,EACX,WAAW,EACX,WAAW,EACX,cAAc,EACd,QAAQ,EAGR,UAAU,EACV,QAAQ,EACR,SAAS,EAGT,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EAGZ,UAAU,EACV,aAAa,EAGb,eAAe,EACf,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EAGR,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,YAAY,EAGZ,QAAQ,EACR,SAAS,EACT,gBAAgB,EAGhB,SAAS,EACT,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,KAAK,GACN,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/ai/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AIClientImpl, createAIClient, ai } from './client.js';
|
|
2
|
+
export { BaseAIProvider, AIError, RateLimitError, ContextLengthError, OverloadedError, AuthenticationError } from './providers/base.js';
|
|
3
|
+
export { OpenAIProvider } from './providers/openai.js';
|
|
4
|
+
export { AnthropicProvider } from './providers/anthropic.js';
|
|
5
|
+
export { AdaptiveTimeoutManager, StreamTimeoutController, adaptiveTimeouts } from './adaptive-timeout.js';
|
|
6
|
+
export { TokenRateLimiter, RateLimitExceededError, createRateLimiter, tokenEstimators, PROVIDER_RATE_LIMITS, } from './rate-limiter.js';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ChatOptions, ChatMessage, AIResponse, AIStream, StreamEvent, EmbedOptions, EmbedResponse, ProviderConfig } from '../../types/ai.js';
|
|
2
|
+
import { BaseAIProvider, ProviderRequestContext } from './base.js';
|
|
3
|
+
export interface AnthropicConfig extends ProviderConfig {
|
|
4
|
+
version?: string;
|
|
5
|
+
}
|
|
6
|
+
interface AnthropicMessage {
|
|
7
|
+
role: 'user' | 'assistant';
|
|
8
|
+
content: string | AnthropicContentPart[];
|
|
9
|
+
}
|
|
10
|
+
type AnthropicContentPart = {
|
|
11
|
+
type: 'text';
|
|
12
|
+
text: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'image';
|
|
15
|
+
source: {
|
|
16
|
+
type: 'base64';
|
|
17
|
+
media_type: string;
|
|
18
|
+
data: string;
|
|
19
|
+
};
|
|
20
|
+
} | {
|
|
21
|
+
type: 'tool_use';
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
input: unknown;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'tool_result';
|
|
27
|
+
tool_use_id: string;
|
|
28
|
+
content: string;
|
|
29
|
+
};
|
|
30
|
+
interface AnthropicCompletion {
|
|
31
|
+
id: string;
|
|
32
|
+
type: 'message';
|
|
33
|
+
role: 'assistant';
|
|
34
|
+
content: AnthropicContentPart[];
|
|
35
|
+
model: string;
|
|
36
|
+
stop_reason: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';
|
|
37
|
+
stop_sequence?: string;
|
|
38
|
+
usage: {
|
|
39
|
+
input_tokens: number;
|
|
40
|
+
output_tokens: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export declare class AnthropicProvider extends BaseAIProvider {
|
|
44
|
+
private anthropicConfig;
|
|
45
|
+
constructor(config?: AnthropicConfig);
|
|
46
|
+
protected getEnvApiKey(): string | undefined;
|
|
47
|
+
protected getBaseUrl(): string;
|
|
48
|
+
protected buildHeaders(): Record<string, string>;
|
|
49
|
+
protected transformMessages(messages: ChatMessage[]): AnthropicMessage[];
|
|
50
|
+
private transformContent;
|
|
51
|
+
private transformTools;
|
|
52
|
+
private getSystemPrompt;
|
|
53
|
+
chat(options: ChatOptions): Promise<AIResponse>;
|
|
54
|
+
stream(options: ChatOptions): Promise<AIStream>;
|
|
55
|
+
embed(_options: EmbedOptions): Promise<EmbedResponse>;
|
|
56
|
+
private buildChatBody;
|
|
57
|
+
private makeRequest;
|
|
58
|
+
private handleError;
|
|
59
|
+
protected parseResponse(data: AnthropicCompletion, context: ProviderRequestContext): AIResponse;
|
|
60
|
+
protected parseStreamEvent(_chunk: string, _context: ProviderRequestContext): StreamEvent | null;
|
|
61
|
+
private parseAnthropicStream;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=anthropic.d.ts.map
|