@animalabs/membrane 0.1.3 → 0.1.5

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.
@@ -137,12 +137,12 @@ export class OpenRouterAdapter implements ProviderAdapter {
137
137
  options?: ProviderRequestOptions
138
138
  ): Promise<ProviderResponse> {
139
139
  const openRouterRequest = this.buildRequest(request);
140
-
140
+
141
141
  try {
142
142
  const response = await this.makeRequest(openRouterRequest, options);
143
- return this.parseResponse(response, request.model);
143
+ return this.parseResponse(response, request.model, openRouterRequest);
144
144
  } catch (error) {
145
- throw this.handleError(error);
145
+ throw this.handleError(error, openRouterRequest);
146
146
  }
147
147
  }
148
148
 
@@ -155,7 +155,7 @@ export class OpenRouterAdapter implements ProviderAdapter {
155
155
  openRouterRequest.stream = true;
156
156
  // Request usage data in stream for cache metrics
157
157
  openRouterRequest.stream_options = { include_usage: true };
158
-
158
+
159
159
  try {
160
160
  const response = await fetch(`${this.baseURL}/chat/completions`, {
161
161
  method: 'POST',
@@ -163,43 +163,43 @@ export class OpenRouterAdapter implements ProviderAdapter {
163
163
  body: JSON.stringify(openRouterRequest),
164
164
  signal: options?.signal,
165
165
  });
166
-
166
+
167
167
  if (!response.ok) {
168
168
  const errorText = await response.text();
169
169
  throw new Error(`OpenRouter error: ${response.status} ${errorText}`);
170
170
  }
171
-
171
+
172
172
  const reader = response.body?.getReader();
173
173
  if (!reader) {
174
174
  throw new Error('No response body');
175
175
  }
176
-
176
+
177
177
  const decoder = new TextDecoder();
178
178
  let accumulated = '';
179
179
  let finishReason = 'stop';
180
180
  let toolCalls: OpenRouterToolCall[] = [];
181
181
  let streamUsage: OpenRouterResponse['usage'] | undefined;
182
-
182
+
183
183
  while (true) {
184
184
  const { done, value } = await reader.read();
185
185
  if (done) break;
186
-
186
+
187
187
  const chunk = decoder.decode(value, { stream: true });
188
188
  const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
189
-
189
+
190
190
  for (const line of lines) {
191
191
  const data = line.slice(6);
192
192
  if (data === '[DONE]') continue;
193
-
193
+
194
194
  try {
195
195
  const parsed = JSON.parse(data);
196
196
  const delta = parsed.choices?.[0]?.delta;
197
-
197
+
198
198
  if (delta?.content) {
199
199
  accumulated += delta.content;
200
200
  callbacks.onChunk(delta.content);
201
201
  }
202
-
202
+
203
203
  // Handle streaming tool calls
204
204
  if (delta?.tool_calls) {
205
205
  for (const tc of delta.tool_calls) {
@@ -218,11 +218,11 @@ export class OpenRouterAdapter implements ProviderAdapter {
218
218
  }
219
219
  }
220
220
  }
221
-
221
+
222
222
  if (parsed.choices?.[0]?.finish_reason) {
223
223
  finishReason = parsed.choices[0].finish_reason;
224
224
  }
225
-
225
+
226
226
  // Capture usage data (comes in final chunk when stream_options.include_usage is set)
227
227
  if (parsed.usage) {
228
228
  streamUsage = parsed.usage;
@@ -232,21 +232,21 @@ export class OpenRouterAdapter implements ProviderAdapter {
232
232
  }
233
233
  }
234
234
  }
235
-
235
+
236
236
  // Build response with accumulated data
237
237
  const message: OpenRouterMessage = {
238
238
  role: 'assistant',
239
239
  content: accumulated || null,
240
240
  };
241
-
241
+
242
242
  if (toolCalls.length > 0) {
243
243
  message.tool_calls = toolCalls;
244
244
  }
245
-
246
- return this.parseStreamedResponse(message, finishReason, request.model, streamUsage);
247
-
245
+
246
+ return this.parseStreamedResponse(message, finishReason, request.model, streamUsage, openRouterRequest);
247
+
248
248
  } catch (error) {
249
- throw this.handleError(error);
249
+ throw this.handleError(error, openRouterRequest);
250
250
  }
251
251
  }
252
252
 
@@ -437,17 +437,17 @@ export class OpenRouterAdapter implements ProviderAdapter {
437
437
  return response.json() as Promise<OpenRouterResponse>;
438
438
  }
439
439
 
440
- private parseResponse(response: OpenRouterResponse, requestedModel: string): ProviderResponse {
440
+ private parseResponse(response: OpenRouterResponse, requestedModel: string, rawRequest: unknown): ProviderResponse {
441
441
  const choice = response.choices[0];
442
442
  const message = choice?.message;
443
-
443
+
444
444
  // Extract cache tokens - OpenRouter passes through both Anthropic and OpenAI caching
445
445
  // Anthropic: cache_creation_input_tokens, cache_read_input_tokens
446
446
  // OpenAI: prompt_tokens_details.cached_tokens
447
447
  const cacheCreationTokens = response.usage?.cache_creation_input_tokens;
448
- const cacheReadTokens = response.usage?.cache_read_input_tokens
448
+ const cacheReadTokens = response.usage?.cache_read_input_tokens
449
449
  ?? response.usage?.prompt_tokens_details?.cached_tokens;
450
-
450
+
451
451
  return {
452
452
  content: this.messageToContent(message),
453
453
  stopReason: this.mapFinishReason(choice?.finish_reason),
@@ -459,6 +459,7 @@ export class OpenRouterAdapter implements ProviderAdapter {
459
459
  cacheReadTokens: cacheReadTokens ?? undefined,
460
460
  },
461
461
  model: response.model ?? requestedModel,
462
+ rawRequest,
462
463
  raw: response,
463
464
  };
464
465
  }
@@ -467,13 +468,14 @@ export class OpenRouterAdapter implements ProviderAdapter {
467
468
  message: OpenRouterMessage,
468
469
  finishReason: string,
469
470
  requestedModel: string,
470
- streamUsage?: OpenRouterResponse['usage']
471
+ streamUsage?: OpenRouterResponse['usage'],
472
+ rawRequest?: unknown
471
473
  ): ProviderResponse {
472
474
  // Extract cache tokens if available from stream usage
473
475
  const cacheCreationTokens = streamUsage?.cache_creation_input_tokens;
474
- const cacheReadTokens = streamUsage?.cache_read_input_tokens
476
+ const cacheReadTokens = streamUsage?.cache_read_input_tokens
475
477
  ?? streamUsage?.prompt_tokens_details?.cached_tokens;
476
-
478
+
477
479
  return {
478
480
  content: this.messageToContent(message),
479
481
  stopReason: this.mapFinishReason(finishReason),
@@ -485,6 +487,7 @@ export class OpenRouterAdapter implements ProviderAdapter {
485
487
  cacheReadTokens: cacheReadTokens ?? undefined,
486
488
  },
487
489
  model: requestedModel,
490
+ rawRequest,
488
491
  raw: { message, finish_reason: finishReason, usage: streamUsage },
489
492
  };
490
493
  }
@@ -527,40 +530,41 @@ export class OpenRouterAdapter implements ProviderAdapter {
527
530
  }
528
531
  }
529
532
 
530
- private handleError(error: unknown): MembraneError {
533
+ private handleError(error: unknown, rawRequest?: unknown): MembraneError {
531
534
  if (error instanceof Error) {
532
535
  const message = error.message;
533
-
536
+
534
537
  if (message.includes('429') || message.includes('rate')) {
535
- return rateLimitError(message, undefined, error);
538
+ return rateLimitError(message, undefined, error, rawRequest);
536
539
  }
537
-
540
+
538
541
  if (message.includes('401') || message.includes('auth')) {
539
- return authError(message, error);
542
+ return authError(message, error, rawRequest);
540
543
  }
541
-
544
+
542
545
  if (message.includes('context') || message.includes('too long')) {
543
- return contextLengthError(message, error);
546
+ return contextLengthError(message, error, rawRequest);
544
547
  }
545
-
548
+
546
549
  if (message.includes('500') || message.includes('502') || message.includes('503')) {
547
- return serverError(message, undefined, error);
550
+ return serverError(message, undefined, error, rawRequest);
548
551
  }
549
-
552
+
550
553
  if (error.name === 'AbortError') {
551
- return abortError();
554
+ return abortError(undefined, rawRequest);
552
555
  }
553
-
556
+
554
557
  if (message.includes('network') || message.includes('fetch')) {
555
- return networkError(message, error);
558
+ return networkError(message, error, rawRequest);
556
559
  }
557
560
  }
558
-
561
+
559
562
  return new MembraneError({
560
563
  type: 'unknown',
561
564
  message: error instanceof Error ? error.message : String(error),
562
565
  retryable: false,
563
566
  rawError: error,
567
+ rawRequest,
564
568
  });
565
569
  }
566
570
  }
@@ -92,7 +92,7 @@ export class MembraneError extends Error {
92
92
  // Error Factory Functions
93
93
  // ============================================================================
94
94
 
95
- export function rateLimitError(message: string, retryAfterMs?: number, raw?: unknown): MembraneError {
95
+ export function rateLimitError(message: string, retryAfterMs?: number, raw?: unknown, rawRequest?: unknown): MembraneError {
96
96
  return new MembraneError({
97
97
  type: 'rate_limit',
98
98
  message,
@@ -100,91 +100,101 @@ export function rateLimitError(message: string, retryAfterMs?: number, raw?: unk
100
100
  retryAfterMs,
101
101
  httpStatus: 429,
102
102
  rawError: raw,
103
+ rawRequest,
103
104
  });
104
105
  }
105
106
 
106
- export function contextLengthError(message: string, raw?: unknown): MembraneError {
107
+ export function contextLengthError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
107
108
  return new MembraneError({
108
109
  type: 'context_length',
109
110
  message,
110
111
  retryable: false,
111
112
  httpStatus: 400,
112
113
  rawError: raw,
114
+ rawRequest,
113
115
  });
114
116
  }
115
117
 
116
- export function invalidRequestError(message: string, raw?: unknown): MembraneError {
118
+ export function invalidRequestError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
117
119
  return new MembraneError({
118
120
  type: 'invalid_request',
119
121
  message,
120
122
  retryable: false,
121
123
  httpStatus: 400,
122
124
  rawError: raw,
125
+ rawRequest,
123
126
  });
124
127
  }
125
128
 
126
- export function authError(message: string, raw?: unknown): MembraneError {
129
+ export function authError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
127
130
  return new MembraneError({
128
131
  type: 'auth',
129
132
  message,
130
133
  retryable: false,
131
134
  httpStatus: 401,
132
135
  rawError: raw,
136
+ rawRequest,
133
137
  });
134
138
  }
135
139
 
136
- export function serverError(message: string, httpStatus?: number, raw?: unknown): MembraneError {
140
+ export function serverError(message: string, httpStatus?: number, raw?: unknown, rawRequest?: unknown): MembraneError {
137
141
  return new MembraneError({
138
142
  type: 'server',
139
143
  message,
140
144
  retryable: true,
141
145
  httpStatus: httpStatus ?? 500,
142
146
  rawError: raw,
147
+ rawRequest,
143
148
  });
144
149
  }
145
150
 
146
- export function networkError(message: string, raw?: unknown): MembraneError {
151
+ export function networkError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
147
152
  return new MembraneError({
148
153
  type: 'network',
149
154
  message,
150
155
  retryable: true,
151
156
  rawError: raw,
157
+ rawRequest,
152
158
  });
153
159
  }
154
160
 
155
- export function timeoutError(message: string, raw?: unknown): MembraneError {
161
+ export function timeoutError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
156
162
  return new MembraneError({
157
163
  type: 'timeout',
158
164
  message,
159
165
  retryable: true,
160
166
  rawError: raw,
167
+ rawRequest,
161
168
  });
162
169
  }
163
170
 
164
- export function abortError(message: string = 'Request was aborted'): MembraneError {
171
+ export function abortError(message: string = 'Request was aborted', rawRequest?: unknown): MembraneError {
165
172
  return new MembraneError({
166
173
  type: 'abort',
167
174
  message,
168
175
  retryable: false,
169
176
  rawError: undefined,
177
+ rawRequest,
170
178
  });
171
179
  }
172
180
 
173
- export function safetyError(message: string, raw?: unknown): MembraneError {
181
+ export function safetyError(message: string, raw?: unknown, rawRequest?: unknown): MembraneError {
174
182
  return new MembraneError({
175
183
  type: 'safety',
176
184
  message,
177
185
  retryable: false,
178
186
  rawError: raw,
187
+ rawRequest,
179
188
  });
180
189
  }
181
190
 
182
- export function unsupportedError(message: string): MembraneError {
191
+ export function unsupportedError(message: string, rawRequest?: unknown): MembraneError {
183
192
  return new MembraneError({
184
193
  type: 'unsupported',
185
194
  message,
186
195
  retryable: false,
187
196
  rawError: undefined,
197
+ rawRequest,
188
198
  });
189
199
  }
190
200
 
@@ -238,7 +238,10 @@ export interface ProviderResponse {
238
238
 
239
239
  /** Model that actually ran */
240
240
  model: string;
241
-
241
+
242
+ /** Raw request that was actually sent to the API */
243
+ rawRequest: unknown;
244
+
242
245
  /** Raw response for debugging */
243
246
  raw: unknown;
244
247
  }