@providerprotocol/ai 0.0.11 → 0.0.12
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/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -10
- package/src/anthropic/index.ts +0 -3
- package/src/core/image.ts +0 -188
- package/src/core/llm.ts +0 -650
- package/src/core/provider.ts +0 -92
- package/src/google/index.ts +0 -3
- package/src/http/errors.ts +0 -112
- package/src/http/fetch.ts +0 -210
- package/src/http/index.ts +0 -31
- package/src/http/keys.ts +0 -136
- package/src/http/retry.ts +0 -205
- package/src/http/sse.ts +0 -136
- package/src/index.ts +0 -32
- package/src/ollama/index.ts +0 -3
- package/src/openai/index.ts +0 -39
- package/src/openrouter/index.ts +0 -11
- package/src/providers/anthropic/index.ts +0 -17
- package/src/providers/anthropic/llm.ts +0 -196
- package/src/providers/anthropic/transform.ts +0 -434
- package/src/providers/anthropic/types.ts +0 -213
- package/src/providers/google/index.ts +0 -17
- package/src/providers/google/llm.ts +0 -203
- package/src/providers/google/transform.ts +0 -447
- package/src/providers/google/types.ts +0 -214
- package/src/providers/ollama/index.ts +0 -43
- package/src/providers/ollama/llm.ts +0 -272
- package/src/providers/ollama/transform.ts +0 -434
- package/src/providers/ollama/types.ts +0 -260
- package/src/providers/openai/index.ts +0 -186
- package/src/providers/openai/llm.completions.ts +0 -201
- package/src/providers/openai/llm.responses.ts +0 -211
- package/src/providers/openai/transform.completions.ts +0 -561
- package/src/providers/openai/transform.responses.ts +0 -708
- package/src/providers/openai/types.ts +0 -1249
- package/src/providers/openrouter/index.ts +0 -177
- package/src/providers/openrouter/llm.completions.ts +0 -201
- package/src/providers/openrouter/llm.responses.ts +0 -211
- package/src/providers/openrouter/transform.completions.ts +0 -538
- package/src/providers/openrouter/transform.responses.ts +0 -742
- package/src/providers/openrouter/types.ts +0 -717
- package/src/providers/xai/index.ts +0 -223
- package/src/providers/xai/llm.completions.ts +0 -201
- package/src/providers/xai/llm.messages.ts +0 -195
- package/src/providers/xai/llm.responses.ts +0 -211
- package/src/providers/xai/transform.completions.ts +0 -565
- package/src/providers/xai/transform.messages.ts +0 -448
- package/src/providers/xai/transform.responses.ts +0 -678
- package/src/providers/xai/types.ts +0 -938
- package/src/types/content.ts +0 -133
- package/src/types/errors.ts +0 -85
- package/src/types/index.ts +0 -105
- package/src/types/llm.ts +0 -211
- package/src/types/messages.ts +0 -205
- package/src/types/provider.ts +0 -195
- package/src/types/schema.ts +0 -58
- package/src/types/stream.ts +0 -188
- package/src/types/thread.ts +0 -226
- package/src/types/tool.ts +0 -88
- package/src/types/turn.ts +0 -118
- package/src/utils/id.ts +0 -28
- package/src/xai/index.ts +0 -41
package/src/http/retry.ts
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import type { RetryStrategy } from '../types/provider.ts';
|
|
2
|
-
import type { UPPError } from '../types/errors.ts';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Exponential backoff retry strategy
|
|
6
|
-
*/
|
|
7
|
-
export class ExponentialBackoff implements RetryStrategy {
|
|
8
|
-
private maxAttempts: number;
|
|
9
|
-
private baseDelay: number;
|
|
10
|
-
private maxDelay: number;
|
|
11
|
-
private jitter: boolean;
|
|
12
|
-
|
|
13
|
-
constructor(options: {
|
|
14
|
-
maxAttempts?: number;
|
|
15
|
-
baseDelay?: number;
|
|
16
|
-
maxDelay?: number;
|
|
17
|
-
jitter?: boolean;
|
|
18
|
-
} = {}) {
|
|
19
|
-
this.maxAttempts = options.maxAttempts ?? 3;
|
|
20
|
-
this.baseDelay = options.baseDelay ?? 1000;
|
|
21
|
-
this.maxDelay = options.maxDelay ?? 30000;
|
|
22
|
-
this.jitter = options.jitter ?? true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
onRetry(error: UPPError, attempt: number): number | null {
|
|
26
|
-
if (attempt > this.maxAttempts) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Only retry on retryable errors
|
|
31
|
-
if (!this.isRetryable(error)) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Calculate delay with exponential backoff
|
|
36
|
-
let delay = this.baseDelay * Math.pow(2, attempt - 1);
|
|
37
|
-
delay = Math.min(delay, this.maxDelay);
|
|
38
|
-
|
|
39
|
-
// Add jitter
|
|
40
|
-
if (this.jitter) {
|
|
41
|
-
delay = delay * (0.5 + Math.random());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return Math.floor(delay);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private isRetryable(error: UPPError): boolean {
|
|
48
|
-
return (
|
|
49
|
-
error.code === 'RATE_LIMITED' ||
|
|
50
|
-
error.code === 'NETWORK_ERROR' ||
|
|
51
|
-
error.code === 'TIMEOUT' ||
|
|
52
|
-
error.code === 'PROVIDER_ERROR'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Linear backoff retry strategy
|
|
59
|
-
*/
|
|
60
|
-
export class LinearBackoff implements RetryStrategy {
|
|
61
|
-
private maxAttempts: number;
|
|
62
|
-
private delay: number;
|
|
63
|
-
|
|
64
|
-
constructor(options: {
|
|
65
|
-
maxAttempts?: number;
|
|
66
|
-
delay?: number;
|
|
67
|
-
} = {}) {
|
|
68
|
-
this.maxAttempts = options.maxAttempts ?? 3;
|
|
69
|
-
this.delay = options.delay ?? 1000;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
onRetry(error: UPPError, attempt: number): number | null {
|
|
73
|
-
if (attempt > this.maxAttempts) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Only retry on retryable errors
|
|
78
|
-
if (!this.isRetryable(error)) {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return this.delay * attempt;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private isRetryable(error: UPPError): boolean {
|
|
86
|
-
return (
|
|
87
|
-
error.code === 'RATE_LIMITED' ||
|
|
88
|
-
error.code === 'NETWORK_ERROR' ||
|
|
89
|
-
error.code === 'TIMEOUT' ||
|
|
90
|
-
error.code === 'PROVIDER_ERROR'
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* No retry strategy - fail immediately
|
|
97
|
-
*/
|
|
98
|
-
export class NoRetry implements RetryStrategy {
|
|
99
|
-
onRetry(): null {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Token bucket rate limiter with retry
|
|
106
|
-
*/
|
|
107
|
-
export class TokenBucket implements RetryStrategy {
|
|
108
|
-
private tokens: number;
|
|
109
|
-
private maxTokens: number;
|
|
110
|
-
private refillRate: number; // tokens per second
|
|
111
|
-
private lastRefill: number;
|
|
112
|
-
private maxAttempts: number;
|
|
113
|
-
|
|
114
|
-
constructor(options: {
|
|
115
|
-
maxTokens?: number;
|
|
116
|
-
refillRate?: number;
|
|
117
|
-
maxAttempts?: number;
|
|
118
|
-
} = {}) {
|
|
119
|
-
this.maxTokens = options.maxTokens ?? 10;
|
|
120
|
-
this.refillRate = options.refillRate ?? 1;
|
|
121
|
-
this.maxAttempts = options.maxAttempts ?? 3;
|
|
122
|
-
this.tokens = this.maxTokens;
|
|
123
|
-
this.lastRefill = Date.now();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
beforeRequest(): number {
|
|
127
|
-
this.refill();
|
|
128
|
-
|
|
129
|
-
if (this.tokens >= 1) {
|
|
130
|
-
this.tokens -= 1;
|
|
131
|
-
return 0;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Calculate time until next token
|
|
135
|
-
const msPerToken = 1000 / this.refillRate;
|
|
136
|
-
return Math.ceil(msPerToken);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
onRetry(error: UPPError, attempt: number): number | null {
|
|
140
|
-
if (attempt > this.maxAttempts) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (error.code !== 'RATE_LIMITED') {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Wait for token bucket to refill
|
|
149
|
-
const msPerToken = 1000 / this.refillRate;
|
|
150
|
-
return Math.ceil(msPerToken * 2); // Wait for 2 tokens
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
reset(): void {
|
|
154
|
-
this.tokens = this.maxTokens;
|
|
155
|
-
this.lastRefill = Date.now();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private refill(): void {
|
|
159
|
-
const now = Date.now();
|
|
160
|
-
const elapsed = (now - this.lastRefill) / 1000;
|
|
161
|
-
const newTokens = elapsed * this.refillRate;
|
|
162
|
-
|
|
163
|
-
this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
|
|
164
|
-
this.lastRefill = now;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Retry strategy that respects Retry-After headers
|
|
170
|
-
*/
|
|
171
|
-
export class RetryAfterStrategy implements RetryStrategy {
|
|
172
|
-
private maxAttempts: number;
|
|
173
|
-
private fallbackDelay: number;
|
|
174
|
-
private lastRetryAfter?: number;
|
|
175
|
-
|
|
176
|
-
constructor(options: {
|
|
177
|
-
maxAttempts?: number;
|
|
178
|
-
fallbackDelay?: number;
|
|
179
|
-
} = {}) {
|
|
180
|
-
this.maxAttempts = options.maxAttempts ?? 3;
|
|
181
|
-
this.fallbackDelay = options.fallbackDelay ?? 5000;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Set the Retry-After value from response headers
|
|
186
|
-
* Call this before onRetry when you have a Retry-After header
|
|
187
|
-
*/
|
|
188
|
-
setRetryAfter(seconds: number): void {
|
|
189
|
-
this.lastRetryAfter = seconds * 1000;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
onRetry(error: UPPError, attempt: number): number | null {
|
|
193
|
-
if (attempt > this.maxAttempts) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (error.code !== 'RATE_LIMITED') {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const delay = this.lastRetryAfter ?? this.fallbackDelay;
|
|
202
|
-
this.lastRetryAfter = undefined;
|
|
203
|
-
return delay;
|
|
204
|
-
}
|
|
205
|
-
}
|
package/src/http/sse.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-Sent Events (SSE) stream parser
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse a Server-Sent Events stream into JSON objects
|
|
7
|
-
* Handles standard SSE format with "data:" prefix
|
|
8
|
-
* Yields parsed JSON for each event
|
|
9
|
-
* Terminates on "[DONE]" message (OpenAI style)
|
|
10
|
-
*
|
|
11
|
-
* @param body - ReadableStream from fetch response
|
|
12
|
-
*/
|
|
13
|
-
export async function* parseSSEStream(
|
|
14
|
-
body: ReadableStream<Uint8Array>
|
|
15
|
-
): AsyncGenerator<unknown, void, unknown> {
|
|
16
|
-
const reader = body.getReader();
|
|
17
|
-
const decoder = new TextDecoder();
|
|
18
|
-
let buffer = '';
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
while (true) {
|
|
22
|
-
const { done, value } = await reader.read();
|
|
23
|
-
|
|
24
|
-
if (done) {
|
|
25
|
-
// Process any remaining data in buffer
|
|
26
|
-
if (buffer.trim()) {
|
|
27
|
-
const event = parseSSEEvent(buffer);
|
|
28
|
-
if (event !== null && event !== undefined) {
|
|
29
|
-
yield event;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
buffer += decoder.decode(value, { stream: true });
|
|
36
|
-
|
|
37
|
-
// Process complete events (separated by double newlines or \r\n\r\n)
|
|
38
|
-
const events = buffer.split(/\r?\n\r?\n/);
|
|
39
|
-
|
|
40
|
-
// Keep the last partial event in the buffer
|
|
41
|
-
buffer = events.pop() ?? '';
|
|
42
|
-
|
|
43
|
-
for (const eventText of events) {
|
|
44
|
-
if (!eventText.trim()) continue;
|
|
45
|
-
|
|
46
|
-
const event = parseSSEEvent(eventText);
|
|
47
|
-
if (event === 'DONE') {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (event !== null && event !== undefined) {
|
|
51
|
-
yield event;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} finally {
|
|
56
|
-
reader.releaseLock();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Parse a single SSE event
|
|
62
|
-
* Returns 'DONE' for [DONE] terminator
|
|
63
|
-
* Returns null for empty or unparseable events
|
|
64
|
-
* Returns parsed JSON otherwise
|
|
65
|
-
*/
|
|
66
|
-
function parseSSEEvent(eventText: string): unknown | 'DONE' | null {
|
|
67
|
-
const lines = eventText.split('\n');
|
|
68
|
-
let data = '';
|
|
69
|
-
let eventType = '';
|
|
70
|
-
|
|
71
|
-
for (const line of lines) {
|
|
72
|
-
const trimmedLine = line.trim();
|
|
73
|
-
if (trimmedLine.startsWith('event:')) {
|
|
74
|
-
eventType = trimmedLine.slice(6).trim();
|
|
75
|
-
} else if (trimmedLine.startsWith('data:')) {
|
|
76
|
-
// Append data (some providers send multi-line data)
|
|
77
|
-
const lineData = trimmedLine.slice(5).trim();
|
|
78
|
-
data += (data ? '\n' : '') + lineData;
|
|
79
|
-
} else if (trimmedLine.startsWith(':')) {
|
|
80
|
-
// Comment line, ignore (often used for keep-alive)
|
|
81
|
-
continue;
|
|
82
|
-
} else if (trimmedLine.startsWith('{') || trimmedLine.startsWith('[')) {
|
|
83
|
-
// Some providers (like Google) may send raw JSON without data: prefix
|
|
84
|
-
data += (data ? '\n' : '') + trimmedLine;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!data) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Check for OpenAI-style termination
|
|
93
|
-
if (data === '[DONE]') {
|
|
94
|
-
return 'DONE';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
const parsed = JSON.parse(data);
|
|
99
|
-
|
|
100
|
-
// If we have an event type, include it
|
|
101
|
-
if (eventType) {
|
|
102
|
-
return { _eventType: eventType, ...parsed };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return parsed;
|
|
106
|
-
} catch {
|
|
107
|
-
// Failed to parse JSON - could be a ping or malformed event
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Create a simple SSE reader that handles basic text streaming
|
|
114
|
-
* For providers that just stream text deltas
|
|
115
|
-
*/
|
|
116
|
-
export async function* parseSimpleTextStream(
|
|
117
|
-
body: ReadableStream<Uint8Array>
|
|
118
|
-
): AsyncGenerator<string, void, unknown> {
|
|
119
|
-
const reader = body.getReader();
|
|
120
|
-
const decoder = new TextDecoder();
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
while (true) {
|
|
124
|
-
const { done, value } = await reader.read();
|
|
125
|
-
|
|
126
|
-
if (done) break;
|
|
127
|
-
|
|
128
|
-
const text = decoder.decode(value, { stream: true });
|
|
129
|
-
if (text) {
|
|
130
|
-
yield text;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
} finally {
|
|
134
|
-
reader.releaseLock();
|
|
135
|
-
}
|
|
136
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Core entry points
|
|
2
|
-
export { llm } from './core/llm.ts';
|
|
3
|
-
export { createProvider } from './core/provider.ts';
|
|
4
|
-
export { Image } from './core/image.ts';
|
|
5
|
-
|
|
6
|
-
// Namespace object for alternative import style
|
|
7
|
-
import { llm } from './core/llm.ts';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* UPP namespace object
|
|
11
|
-
* Provides ai.llm(), ai.embedding(), ai.image() style access
|
|
12
|
-
*/
|
|
13
|
-
export const ai = {
|
|
14
|
-
llm,
|
|
15
|
-
// embedding, // Coming soon
|
|
16
|
-
// image, // Coming soon
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Re-export all types from types/index.ts
|
|
20
|
-
export * from './types/index.ts';
|
|
21
|
-
|
|
22
|
-
// Re-export HTTP utilities
|
|
23
|
-
export {
|
|
24
|
-
RoundRobinKeys,
|
|
25
|
-
WeightedKeys,
|
|
26
|
-
DynamicKey,
|
|
27
|
-
ExponentialBackoff,
|
|
28
|
-
LinearBackoff,
|
|
29
|
-
NoRetry,
|
|
30
|
-
TokenBucket,
|
|
31
|
-
RetryAfterStrategy,
|
|
32
|
-
} from './http/index.ts';
|
package/src/ollama/index.ts
DELETED
package/src/openai/index.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Re-export from providers/openai
|
|
2
|
-
export {
|
|
3
|
-
openai,
|
|
4
|
-
tools,
|
|
5
|
-
webSearchTool,
|
|
6
|
-
fileSearchTool,
|
|
7
|
-
codeInterpreterTool,
|
|
8
|
-
computerTool,
|
|
9
|
-
imageGenerationTool,
|
|
10
|
-
mcpTool,
|
|
11
|
-
} from '../providers/openai/index.ts';
|
|
12
|
-
export type {
|
|
13
|
-
OpenAICompletionsParams,
|
|
14
|
-
OpenAIResponsesParams,
|
|
15
|
-
OpenAIConfig,
|
|
16
|
-
OpenAIAPIMode,
|
|
17
|
-
OpenAIModelOptions,
|
|
18
|
-
OpenAIModelReference,
|
|
19
|
-
// Audio and web search types
|
|
20
|
-
OpenAIAudioConfig,
|
|
21
|
-
OpenAIWebSearchOptions,
|
|
22
|
-
OpenAIWebSearchUserLocation,
|
|
23
|
-
OpenAICompletionsWebSearchUserLocation,
|
|
24
|
-
// Built-in tool types
|
|
25
|
-
OpenAIBuiltInTool,
|
|
26
|
-
OpenAIWebSearchTool,
|
|
27
|
-
OpenAIFileSearchTool,
|
|
28
|
-
OpenAICodeInterpreterTool,
|
|
29
|
-
OpenAICodeInterpreterContainer,
|
|
30
|
-
OpenAIComputerTool,
|
|
31
|
-
OpenAIComputerEnvironment,
|
|
32
|
-
OpenAIImageGenerationTool,
|
|
33
|
-
OpenAIMcpTool,
|
|
34
|
-
OpenAIMcpServerConfig,
|
|
35
|
-
OpenAIResponsesToolUnion,
|
|
36
|
-
// Conversation and prompt types
|
|
37
|
-
OpenAIConversation,
|
|
38
|
-
OpenAIPromptTemplate,
|
|
39
|
-
} from '../providers/openai/index.ts';
|
package/src/openrouter/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Re-export from providers/openrouter
|
|
2
|
-
export { openrouter } from '../providers/openrouter/index.ts';
|
|
3
|
-
export type {
|
|
4
|
-
OpenRouterCompletionsParams,
|
|
5
|
-
OpenRouterResponsesParams,
|
|
6
|
-
OpenRouterConfig,
|
|
7
|
-
OpenRouterAPIMode,
|
|
8
|
-
OpenRouterModelOptions,
|
|
9
|
-
OpenRouterModelReference,
|
|
10
|
-
OpenRouterProviderPreferences,
|
|
11
|
-
} from '../providers/openrouter/index.ts';
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { createProvider } from '../../core/provider.ts';
|
|
2
|
-
import { createLLMHandler } from './llm.ts';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Anthropic provider
|
|
6
|
-
* Supports LLM modality with Claude models
|
|
7
|
-
*/
|
|
8
|
-
export const anthropic = createProvider({
|
|
9
|
-
name: 'anthropic',
|
|
10
|
-
version: '1.0.0',
|
|
11
|
-
modalities: {
|
|
12
|
-
llm: createLLMHandler(),
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
// Re-export types
|
|
17
|
-
export type { AnthropicLLMParams } from './types.ts';
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import type { LLMHandler, BoundLLMModel, LLMRequest, LLMResponse, LLMStreamResult, LLMCapabilities } from '../../types/llm.ts';
|
|
2
|
-
import type { StreamEvent } from '../../types/stream.ts';
|
|
3
|
-
import type { LLMProvider } from '../../types/provider.ts';
|
|
4
|
-
import { UPPError } from '../../types/errors.ts';
|
|
5
|
-
import { resolveApiKey } from '../../http/keys.ts';
|
|
6
|
-
import { doFetch, doStreamFetch } from '../../http/fetch.ts';
|
|
7
|
-
import { parseSSEStream } from '../../http/sse.ts';
|
|
8
|
-
import { normalizeHttpError } from '../../http/errors.ts';
|
|
9
|
-
import type { AnthropicLLMParams, AnthropicResponse, AnthropicStreamEvent } from './types.ts';
|
|
10
|
-
import {
|
|
11
|
-
transformRequest,
|
|
12
|
-
transformResponse,
|
|
13
|
-
transformStreamEvent,
|
|
14
|
-
createStreamState,
|
|
15
|
-
buildResponseFromState,
|
|
16
|
-
} from './transform.ts';
|
|
17
|
-
|
|
18
|
-
const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
|
|
19
|
-
const ANTHROPIC_VERSION = '2023-06-01';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Anthropic API capabilities
|
|
23
|
-
*/
|
|
24
|
-
const ANTHROPIC_CAPABILITIES: LLMCapabilities = {
|
|
25
|
-
streaming: true,
|
|
26
|
-
tools: true,
|
|
27
|
-
structuredOutput: true,
|
|
28
|
-
imageInput: true,
|
|
29
|
-
videoInput: false,
|
|
30
|
-
audioInput: false,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create Anthropic LLM handler
|
|
35
|
-
*/
|
|
36
|
-
export function createLLMHandler(): LLMHandler<AnthropicLLMParams> {
|
|
37
|
-
// Provider reference injected by createProvider() after construction
|
|
38
|
-
let providerRef: LLMProvider<AnthropicLLMParams> | null = null;
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
_setProvider(provider: LLMProvider<AnthropicLLMParams>) {
|
|
42
|
-
providerRef = provider;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
bind(modelId: string): BoundLLMModel<AnthropicLLMParams> {
|
|
46
|
-
// Use the injected provider reference (set by createProvider)
|
|
47
|
-
if (!providerRef) {
|
|
48
|
-
throw new UPPError(
|
|
49
|
-
'Provider reference not set. Handler must be used with createProvider().',
|
|
50
|
-
'INVALID_REQUEST',
|
|
51
|
-
'anthropic',
|
|
52
|
-
'llm'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const model: BoundLLMModel<AnthropicLLMParams> = {
|
|
57
|
-
modelId,
|
|
58
|
-
capabilities: ANTHROPIC_CAPABILITIES,
|
|
59
|
-
|
|
60
|
-
get provider(): LLMProvider<AnthropicLLMParams> {
|
|
61
|
-
return providerRef!;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
async complete(request: LLMRequest<AnthropicLLMParams>): Promise<LLMResponse> {
|
|
65
|
-
const apiKey = await resolveApiKey(
|
|
66
|
-
request.config,
|
|
67
|
-
'ANTHROPIC_API_KEY',
|
|
68
|
-
'anthropic',
|
|
69
|
-
'llm'
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const baseUrl = request.config.baseUrl ?? ANTHROPIC_API_URL;
|
|
73
|
-
const body = transformRequest(request, modelId);
|
|
74
|
-
|
|
75
|
-
const response = await doFetch(
|
|
76
|
-
baseUrl,
|
|
77
|
-
{
|
|
78
|
-
method: 'POST',
|
|
79
|
-
headers: {
|
|
80
|
-
'Content-Type': 'application/json',
|
|
81
|
-
'x-api-key': apiKey,
|
|
82
|
-
'anthropic-version': request.config.apiVersion ?? ANTHROPIC_VERSION,
|
|
83
|
-
},
|
|
84
|
-
body: JSON.stringify(body),
|
|
85
|
-
signal: request.signal,
|
|
86
|
-
},
|
|
87
|
-
request.config,
|
|
88
|
-
'anthropic',
|
|
89
|
-
'llm'
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const data = (await response.json()) as AnthropicResponse;
|
|
93
|
-
return transformResponse(data);
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
stream(request: LLMRequest<AnthropicLLMParams>): LLMStreamResult {
|
|
97
|
-
const state = createStreamState();
|
|
98
|
-
let responseResolve: (value: LLMResponse) => void;
|
|
99
|
-
let responseReject: (error: Error) => void;
|
|
100
|
-
|
|
101
|
-
const responsePromise = new Promise<LLMResponse>((resolve, reject) => {
|
|
102
|
-
responseResolve = resolve;
|
|
103
|
-
responseReject = reject;
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
async function* generateEvents(): AsyncGenerator<StreamEvent, void, unknown> {
|
|
107
|
-
try {
|
|
108
|
-
const apiKey = await resolveApiKey(
|
|
109
|
-
request.config,
|
|
110
|
-
'ANTHROPIC_API_KEY',
|
|
111
|
-
'anthropic',
|
|
112
|
-
'llm'
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const baseUrl = request.config.baseUrl ?? ANTHROPIC_API_URL;
|
|
116
|
-
const body = transformRequest(request, modelId);
|
|
117
|
-
body.stream = true;
|
|
118
|
-
|
|
119
|
-
const response = await doStreamFetch(
|
|
120
|
-
baseUrl,
|
|
121
|
-
{
|
|
122
|
-
method: 'POST',
|
|
123
|
-
headers: {
|
|
124
|
-
'Content-Type': 'application/json',
|
|
125
|
-
'x-api-key': apiKey,
|
|
126
|
-
'anthropic-version': request.config.apiVersion ?? ANTHROPIC_VERSION,
|
|
127
|
-
},
|
|
128
|
-
body: JSON.stringify(body),
|
|
129
|
-
signal: request.signal,
|
|
130
|
-
},
|
|
131
|
-
request.config,
|
|
132
|
-
'anthropic',
|
|
133
|
-
'llm'
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (!response.ok) {
|
|
137
|
-
const error = await normalizeHttpError(response, 'anthropic', 'llm');
|
|
138
|
-
responseReject(error);
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (!response.body) {
|
|
143
|
-
const error = new UPPError(
|
|
144
|
-
'No response body for streaming request',
|
|
145
|
-
'PROVIDER_ERROR',
|
|
146
|
-
'anthropic',
|
|
147
|
-
'llm'
|
|
148
|
-
);
|
|
149
|
-
responseReject(error);
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
for await (const data of parseSSEStream(response.body)) {
|
|
154
|
-
// Check for Anthropic error event
|
|
155
|
-
if (typeof data === 'object' && data !== null && 'type' in data) {
|
|
156
|
-
const event = data as AnthropicStreamEvent;
|
|
157
|
-
|
|
158
|
-
if (event.type === 'error') {
|
|
159
|
-
const error = new UPPError(
|
|
160
|
-
event.error.message,
|
|
161
|
-
'PROVIDER_ERROR',
|
|
162
|
-
'anthropic',
|
|
163
|
-
'llm'
|
|
164
|
-
);
|
|
165
|
-
responseReject(error);
|
|
166
|
-
throw error;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const uppEvent = transformStreamEvent(event, state);
|
|
170
|
-
if (uppEvent) {
|
|
171
|
-
yield uppEvent;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Build final response
|
|
177
|
-
responseResolve(buildResponseFromState(state));
|
|
178
|
-
} catch (error) {
|
|
179
|
-
responseReject(error as Error);
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
[Symbol.asyncIterator]() {
|
|
186
|
-
return generateEvents();
|
|
187
|
-
},
|
|
188
|
-
response: responsePromise,
|
|
189
|
-
};
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return model;
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|