@animalabs/membrane 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/context/index.d.ts +10 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +9 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/process.d.ts +22 -0
- package/dist/context/process.d.ts.map +1 -0
- package/dist/context/process.js +369 -0
- package/dist/context/process.js.map +1 -0
- package/dist/context/types.d.ts +118 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +60 -0
- package/dist/context/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/membrane.d.ts +96 -0
- package/dist/membrane.d.ts.map +1 -0
- package/dist/membrane.js +893 -0
- package/dist/membrane.js.map +1 -0
- package/dist/providers/anthropic.d.ts +36 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +265 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +74 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +412 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/openai.d.ts +69 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +455 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +76 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +492 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/transforms/chat.d.ts +52 -0
- package/dist/transforms/chat.d.ts.map +1 -0
- package/dist/transforms/chat.js +136 -0
- package/dist/transforms/chat.js.map +1 -0
- package/dist/transforms/index.d.ts +6 -0
- package/dist/transforms/index.d.ts.map +1 -0
- package/dist/transforms/index.js +6 -0
- package/dist/transforms/index.js.map +1 -0
- package/dist/transforms/prefill.d.ts +89 -0
- package/dist/transforms/prefill.d.ts.map +1 -0
- package/dist/transforms/prefill.js +401 -0
- package/dist/transforms/prefill.js.map +1 -0
- package/dist/types/config.d.ts +103 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +21 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/content.d.ts +81 -0
- package/dist/types/content.d.ts.map +1 -0
- package/dist/types/content.js +40 -0
- package/dist/types/content.js.map +1 -0
- package/dist/types/errors.d.ts +42 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +208 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +9 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/message.d.ts +46 -0
- package/dist/types/message.d.ts.map +1 -0
- package/dist/types/message.js +38 -0
- package/dist/types/message.js.map +1 -0
- package/dist/types/provider.d.ts +155 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +5 -0
- package/dist/types/provider.js.map +1 -0
- package/dist/types/request.d.ts +78 -0
- package/dist/types/request.d.ts.map +1 -0
- package/dist/types/request.js +5 -0
- package/dist/types/request.js.map +1 -0
- package/dist/types/response.d.ts +131 -0
- package/dist/types/response.d.ts.map +1 -0
- package/dist/types/response.js +7 -0
- package/dist/types/response.js.map +1 -0
- package/dist/types/streaming.d.ts +164 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/types/streaming.js +5 -0
- package/dist/types/streaming.js.map +1 -0
- package/dist/types/tools.d.ts +71 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +5 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/stream-parser.d.ts +53 -0
- package/dist/utils/stream-parser.d.ts.map +1 -0
- package/dist/utils/stream-parser.js +359 -0
- package/dist/utils/stream-parser.js.map +1 -0
- package/dist/utils/tool-parser.d.ts +130 -0
- package/dist/utils/tool-parser.d.ts.map +1 -0
- package/dist/utils/tool-parser.js +571 -0
- package/dist/utils/tool-parser.js.map +1 -0
- package/package.json +37 -0
- package/src/context/index.ts +24 -0
- package/src/context/process.ts +520 -0
- package/src/context/types.ts +231 -0
- package/src/index.ts +23 -0
- package/src/membrane.ts +1174 -0
- package/src/providers/anthropic.ts +340 -0
- package/src/providers/index.ts +31 -0
- package/src/providers/openai-compatible.ts +570 -0
- package/src/providers/openai.ts +625 -0
- package/src/providers/openrouter.ts +662 -0
- package/src/transforms/chat.ts +212 -0
- package/src/transforms/index.ts +22 -0
- package/src/transforms/prefill.ts +585 -0
- package/src/types/config.ts +172 -0
- package/src/types/content.ts +181 -0
- package/src/types/errors.ts +277 -0
- package/src/types/index.ts +154 -0
- package/src/types/message.ts +89 -0
- package/src/types/provider.ts +249 -0
- package/src/types/request.ts +131 -0
- package/src/types/response.ts +223 -0
- package/src/types/streaming.ts +231 -0
- package/src/types/tools.ts +92 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/stream-parser.ts +440 -0
- package/src/utils/tool-parser.ts +715 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic provider adapter
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
6
|
+
import type {
|
|
7
|
+
ProviderAdapter,
|
|
8
|
+
ProviderRequest,
|
|
9
|
+
ProviderRequestOptions,
|
|
10
|
+
ProviderResponse,
|
|
11
|
+
StreamCallbacks,
|
|
12
|
+
ContentBlock,
|
|
13
|
+
} from '../types/index.js';
|
|
14
|
+
import {
|
|
15
|
+
MembraneError,
|
|
16
|
+
rateLimitError,
|
|
17
|
+
contextLengthError,
|
|
18
|
+
authError,
|
|
19
|
+
serverError,
|
|
20
|
+
abortError,
|
|
21
|
+
} from '../types/index.js';
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Adapter Configuration
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
export interface AnthropicAdapterConfig {
|
|
28
|
+
/** API key (defaults to ANTHROPIC_API_KEY env var) */
|
|
29
|
+
apiKey?: string;
|
|
30
|
+
|
|
31
|
+
/** Base URL override */
|
|
32
|
+
baseURL?: string;
|
|
33
|
+
|
|
34
|
+
/** Default max tokens */
|
|
35
|
+
defaultMaxTokens?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Anthropic Adapter
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
export class AnthropicAdapter implements ProviderAdapter {
|
|
43
|
+
readonly name = 'anthropic';
|
|
44
|
+
private client: Anthropic;
|
|
45
|
+
private defaultMaxTokens: number;
|
|
46
|
+
|
|
47
|
+
constructor(config: AnthropicAdapterConfig = {}) {
|
|
48
|
+
this.client = new Anthropic({
|
|
49
|
+
apiKey: config.apiKey,
|
|
50
|
+
baseURL: config.baseURL,
|
|
51
|
+
});
|
|
52
|
+
this.defaultMaxTokens = config.defaultMaxTokens ?? 4096;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
supportsModel(modelId: string): boolean {
|
|
56
|
+
return modelId.startsWith('claude-');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async complete(
|
|
60
|
+
request: ProviderRequest,
|
|
61
|
+
options?: ProviderRequestOptions
|
|
62
|
+
): Promise<ProviderResponse> {
|
|
63
|
+
const anthropicRequest = this.buildRequest(request);
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const response = await this.client.messages.create({
|
|
67
|
+
...anthropicRequest,
|
|
68
|
+
stream: false,
|
|
69
|
+
}, {
|
|
70
|
+
signal: options?.signal,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return this.parseResponse(response);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw this.handleError(error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async stream(
|
|
80
|
+
request: ProviderRequest,
|
|
81
|
+
callbacks: StreamCallbacks,
|
|
82
|
+
options?: ProviderRequestOptions
|
|
83
|
+
): Promise<ProviderResponse> {
|
|
84
|
+
const anthropicRequest = this.buildRequest(request);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const stream = await this.client.messages.stream(anthropicRequest, {
|
|
88
|
+
signal: options?.signal,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
let accumulated = '';
|
|
92
|
+
const contentBlocks: unknown[] = [];
|
|
93
|
+
let currentBlockIndex = -1;
|
|
94
|
+
|
|
95
|
+
for await (const event of stream) {
|
|
96
|
+
if (event.type === 'content_block_start') {
|
|
97
|
+
currentBlockIndex = event.index;
|
|
98
|
+
contentBlocks[currentBlockIndex] = event.content_block;
|
|
99
|
+
callbacks.onContentBlock?.(currentBlockIndex, event.content_block);
|
|
100
|
+
} else if (event.type === 'content_block_delta') {
|
|
101
|
+
if (event.delta.type === 'text_delta') {
|
|
102
|
+
const chunk = event.delta.text;
|
|
103
|
+
accumulated += chunk;
|
|
104
|
+
callbacks.onChunk(chunk);
|
|
105
|
+
} else if (event.delta.type === 'thinking_delta') {
|
|
106
|
+
// Handle thinking delta
|
|
107
|
+
callbacks.onChunk(event.delta.thinking);
|
|
108
|
+
}
|
|
109
|
+
} else if (event.type === 'content_block_stop') {
|
|
110
|
+
callbacks.onContentBlock?.(currentBlockIndex, contentBlocks[currentBlockIndex]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const finalMessage = await stream.finalMessage();
|
|
115
|
+
return this.parseResponse(finalMessage);
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw this.handleError(error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private buildRequest(request: ProviderRequest): Anthropic.MessageCreateParams {
|
|
123
|
+
const params: Anthropic.MessageCreateParams = {
|
|
124
|
+
model: request.model,
|
|
125
|
+
max_tokens: request.maxTokens || this.defaultMaxTokens,
|
|
126
|
+
messages: request.messages as Anthropic.MessageParam[],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Handle system prompt - can be string or content blocks with cache_control
|
|
130
|
+
if (request.system) {
|
|
131
|
+
if (typeof request.system === 'string') {
|
|
132
|
+
params.system = request.system;
|
|
133
|
+
} else if (Array.isArray(request.system)) {
|
|
134
|
+
// System is an array of content blocks (with potential cache_control)
|
|
135
|
+
params.system = request.system as Anthropic.TextBlockParam[];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (request.temperature !== undefined) {
|
|
140
|
+
params.temperature = request.temperature;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (request.stopSequences && request.stopSequences.length > 0) {
|
|
144
|
+
params.stop_sequences = request.stopSequences;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (request.tools && request.tools.length > 0) {
|
|
148
|
+
params.tools = request.tools as Anthropic.Tool[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Apply extra params
|
|
152
|
+
if (request.extra) {
|
|
153
|
+
Object.assign(params, request.extra);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return params;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private parseResponse(response: Anthropic.Message): ProviderResponse {
|
|
160
|
+
return {
|
|
161
|
+
content: response.content,
|
|
162
|
+
stopReason: response.stop_reason ?? 'end_turn',
|
|
163
|
+
stopSequence: response.stop_sequence ?? undefined,
|
|
164
|
+
usage: {
|
|
165
|
+
inputTokens: response.usage.input_tokens,
|
|
166
|
+
outputTokens: response.usage.output_tokens,
|
|
167
|
+
cacheCreationTokens: (response.usage as any).cache_creation_input_tokens,
|
|
168
|
+
cacheReadTokens: (response.usage as any).cache_read_input_tokens,
|
|
169
|
+
},
|
|
170
|
+
model: response.model,
|
|
171
|
+
raw: response,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private handleError(error: unknown): MembraneError {
|
|
176
|
+
if (error instanceof Anthropic.APIError) {
|
|
177
|
+
const status = error.status;
|
|
178
|
+
const message = error.message;
|
|
179
|
+
|
|
180
|
+
if (status === 429) {
|
|
181
|
+
// Try to parse retry-after
|
|
182
|
+
const retryAfter = this.parseRetryAfter(error);
|
|
183
|
+
return rateLimitError(message, retryAfter, error);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (status === 401) {
|
|
187
|
+
return authError(message, error);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (message.includes('context') || message.includes('too long')) {
|
|
191
|
+
return contextLengthError(message, error);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (status >= 500) {
|
|
195
|
+
return serverError(message, status, error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
200
|
+
return abortError();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return new MembraneError({
|
|
204
|
+
type: 'unknown',
|
|
205
|
+
message: error instanceof Error ? error.message : String(error),
|
|
206
|
+
retryable: false,
|
|
207
|
+
rawError: error,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private parseRetryAfter(error: { message: string }): number | undefined {
|
|
212
|
+
// Try to extract retry-after from headers or message
|
|
213
|
+
const message = error.message;
|
|
214
|
+
const match = message.match(/retry after (\d+)/i);
|
|
215
|
+
if (match && match[1]) {
|
|
216
|
+
return parseInt(match[1], 10) * 1000;
|
|
217
|
+
}
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// Content Conversion Utilities
|
|
224
|
+
// ============================================================================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Convert normalized content blocks to Anthropic format
|
|
228
|
+
* Preserves cache_control for prompt caching
|
|
229
|
+
*/
|
|
230
|
+
export function toAnthropicContent(blocks: ContentBlock[]): Anthropic.ContentBlockParam[] {
|
|
231
|
+
const result: Anthropic.ContentBlockParam[] = [];
|
|
232
|
+
|
|
233
|
+
for (const block of blocks) {
|
|
234
|
+
switch (block.type) {
|
|
235
|
+
case 'text': {
|
|
236
|
+
const textBlock: any = { type: 'text', text: block.text };
|
|
237
|
+
// Preserve cache_control if present
|
|
238
|
+
if (block.cache_control) {
|
|
239
|
+
textBlock.cache_control = block.cache_control;
|
|
240
|
+
}
|
|
241
|
+
result.push(textBlock);
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
case 'image':
|
|
246
|
+
if (block.source.type === 'base64') {
|
|
247
|
+
result.push({
|
|
248
|
+
type: 'image',
|
|
249
|
+
source: {
|
|
250
|
+
type: 'base64',
|
|
251
|
+
media_type: block.source.mediaType as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp',
|
|
252
|
+
data: block.source.data,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case 'document':
|
|
259
|
+
result.push({
|
|
260
|
+
type: 'document',
|
|
261
|
+
source: {
|
|
262
|
+
type: 'base64',
|
|
263
|
+
media_type: block.source.mediaType as 'application/pdf',
|
|
264
|
+
data: block.source.data,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case 'tool_use':
|
|
270
|
+
result.push({
|
|
271
|
+
type: 'tool_use',
|
|
272
|
+
id: block.id,
|
|
273
|
+
name: block.name,
|
|
274
|
+
input: block.input,
|
|
275
|
+
});
|
|
276
|
+
break;
|
|
277
|
+
|
|
278
|
+
case 'tool_result':
|
|
279
|
+
result.push({
|
|
280
|
+
type: 'tool_result',
|
|
281
|
+
tool_use_id: block.toolUseId,
|
|
282
|
+
content: typeof block.content === 'string'
|
|
283
|
+
? block.content
|
|
284
|
+
: JSON.stringify(block.content),
|
|
285
|
+
is_error: block.isError,
|
|
286
|
+
});
|
|
287
|
+
break;
|
|
288
|
+
|
|
289
|
+
case 'thinking':
|
|
290
|
+
result.push({
|
|
291
|
+
type: 'thinking',
|
|
292
|
+
thinking: block.thinking,
|
|
293
|
+
} as any);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Convert Anthropic response content to normalized format
|
|
303
|
+
*/
|
|
304
|
+
export function fromAnthropicContent(blocks: Anthropic.ContentBlock[]): ContentBlock[] {
|
|
305
|
+
const result: ContentBlock[] = [];
|
|
306
|
+
|
|
307
|
+
for (const block of blocks) {
|
|
308
|
+
switch (block.type) {
|
|
309
|
+
case 'text':
|
|
310
|
+
result.push({ type: 'text', text: block.text });
|
|
311
|
+
break;
|
|
312
|
+
|
|
313
|
+
case 'tool_use':
|
|
314
|
+
result.push({
|
|
315
|
+
type: 'tool_use',
|
|
316
|
+
id: block.id,
|
|
317
|
+
name: block.name,
|
|
318
|
+
input: block.input as Record<string, unknown>,
|
|
319
|
+
});
|
|
320
|
+
break;
|
|
321
|
+
|
|
322
|
+
case 'thinking':
|
|
323
|
+
result.push({
|
|
324
|
+
type: 'thinking',
|
|
325
|
+
thinking: (block as any).thinking,
|
|
326
|
+
signature: (block as any).signature,
|
|
327
|
+
});
|
|
328
|
+
break;
|
|
329
|
+
|
|
330
|
+
default:
|
|
331
|
+
// Handle redacted_thinking or unknown types
|
|
332
|
+
if ((block as any).type === 'redacted_thinking') {
|
|
333
|
+
result.push({ type: 'redacted_thinking' });
|
|
334
|
+
}
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
AnthropicAdapter,
|
|
7
|
+
toAnthropicContent,
|
|
8
|
+
fromAnthropicContent,
|
|
9
|
+
type AnthropicAdapterConfig,
|
|
10
|
+
} from './anthropic.js';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
OpenRouterAdapter,
|
|
14
|
+
toOpenRouterMessages,
|
|
15
|
+
fromOpenRouterMessage,
|
|
16
|
+
type OpenRouterAdapterConfig,
|
|
17
|
+
} from './openrouter.js';
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
OpenAIAdapter,
|
|
21
|
+
toOpenAIContent,
|
|
22
|
+
fromOpenAIContent,
|
|
23
|
+
type OpenAIAdapterConfig,
|
|
24
|
+
} from './openai.js';
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
OpenAICompatibleAdapter,
|
|
28
|
+
toOpenAIMessages,
|
|
29
|
+
fromOpenAIMessage,
|
|
30
|
+
type OpenAICompatibleAdapterConfig,
|
|
31
|
+
} from './openai-compatible.js';
|