@amux.ai/llm-bridge 0.2.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.
@@ -0,0 +1,1328 @@
1
+ /**
2
+ * Message role types
3
+ */
4
+ type Role = 'system' | 'user' | 'assistant' | 'tool';
5
+ /**
6
+ * Text content part
7
+ */
8
+ interface TextContent {
9
+ type: 'text';
10
+ text: string;
11
+ }
12
+ /**
13
+ * Image source types
14
+ */
15
+ type ImageSource = {
16
+ type: 'url';
17
+ url: string;
18
+ } | {
19
+ type: 'base64';
20
+ mediaType: string;
21
+ data: string;
22
+ };
23
+ /**
24
+ * Image content part
25
+ */
26
+ interface ImageContent {
27
+ type: 'image';
28
+ source: ImageSource;
29
+ }
30
+ /**
31
+ * Content part union type
32
+ * Note: Tool calls use OpenAI-style toolCalls field on Message, not content parts
33
+ */
34
+ type ContentPart = TextContent | ImageContent;
35
+ /**
36
+ * Message content can be a string or an array of content parts
37
+ */
38
+ type MessageContent = string | ContentPart[];
39
+ /**
40
+ * Tool call (OpenAI-style)
41
+ * This is the unified format for tool calls in IR
42
+ */
43
+ interface ToolCall {
44
+ id: string;
45
+ type: 'function';
46
+ function: {
47
+ name: string;
48
+ arguments: string;
49
+ };
50
+ }
51
+ /**
52
+ * Message structure supporting multimodal content
53
+ */
54
+ interface Message {
55
+ role: Role;
56
+ content: MessageContent;
57
+ name?: string;
58
+ /**
59
+ * Tool call ID (for tool role messages - tool results)
60
+ * When role is 'tool', this identifies which tool call this is a response to
61
+ */
62
+ toolCallId?: string;
63
+ /**
64
+ * Tool calls made by the assistant (OpenAI-style)
65
+ * When role is 'assistant' and the model wants to call tools
66
+ */
67
+ toolCalls?: ToolCall[];
68
+ /**
69
+ * Reasoning/thinking content (DeepSeek, Qwen QwQ, Anthropic extended thinking)
70
+ * Contains the model's chain-of-thought reasoning process
71
+ */
72
+ reasoningContent?: string;
73
+ }
74
+
75
+ /**
76
+ * JSON Schema definition
77
+ */
78
+ interface JSONSchema {
79
+ type: string;
80
+ properties?: Record<string, unknown>;
81
+ required?: string[];
82
+ additionalProperties?: boolean;
83
+ description?: string;
84
+ [key: string]: unknown;
85
+ }
86
+ /**
87
+ * Function definition
88
+ */
89
+ interface FunctionDefinition {
90
+ name: string;
91
+ description?: string;
92
+ parameters?: JSONSchema;
93
+ strict?: boolean;
94
+ }
95
+ /**
96
+ * Tool definition
97
+ */
98
+ interface Tool {
99
+ type: 'function';
100
+ function: FunctionDefinition;
101
+ }
102
+ /**
103
+ * Tool choice options
104
+ */
105
+ type ToolChoice = 'auto' | 'none' | 'required' | {
106
+ type: 'function';
107
+ function: {
108
+ name: string;
109
+ };
110
+ };
111
+
112
+ /**
113
+ * Response format configuration
114
+ */
115
+ interface ResponseFormat {
116
+ /**
117
+ * Response type: 'text' | 'json_object' | 'json_schema'
118
+ */
119
+ type: 'text' | 'json_object' | 'json_schema';
120
+ /**
121
+ * JSON schema for structured output (when type is 'json_schema')
122
+ */
123
+ jsonSchema?: {
124
+ name: string;
125
+ description?: string;
126
+ schema: Record<string, unknown>;
127
+ strict?: boolean;
128
+ };
129
+ }
130
+ /**
131
+ * Thinking/reasoning configuration
132
+ * Supported by: DeepSeek, Qwen, Anthropic (extended thinking)
133
+ */
134
+ interface ThinkingConfig {
135
+ /**
136
+ * Enable thinking/reasoning mode
137
+ */
138
+ enabled: boolean;
139
+ /**
140
+ * Budget tokens for thinking (Anthropic)
141
+ */
142
+ budgetTokens?: number;
143
+ }
144
+ /**
145
+ * Generation configuration parameters
146
+ */
147
+ interface GenerationConfig {
148
+ /**
149
+ * Temperature (0-2, typically 0-1)
150
+ * Higher values make output more random
151
+ */
152
+ temperature?: number;
153
+ /**
154
+ * Top-p sampling (nucleus sampling)
155
+ * Alternative to temperature
156
+ */
157
+ topP?: number;
158
+ /**
159
+ * Top-k sampling
160
+ * Only consider top k tokens
161
+ */
162
+ topK?: number;
163
+ /**
164
+ * Maximum tokens to generate
165
+ */
166
+ maxTokens?: number;
167
+ /**
168
+ * Stop sequences
169
+ * Generation stops when any of these sequences is encountered
170
+ */
171
+ stopSequences?: string[];
172
+ /**
173
+ * Presence penalty (-2.0 to 2.0)
174
+ * Positive values penalize new tokens based on whether they appear in the text so far
175
+ */
176
+ presencePenalty?: number;
177
+ /**
178
+ * Frequency penalty (-2.0 to 2.0)
179
+ * Positive values penalize new tokens based on their frequency in the text so far
180
+ */
181
+ frequencyPenalty?: number;
182
+ /**
183
+ * Number of completions to generate
184
+ */
185
+ n?: number;
186
+ /**
187
+ * Seed for deterministic generation
188
+ */
189
+ seed?: number;
190
+ /**
191
+ * Response format configuration
192
+ */
193
+ responseFormat?: ResponseFormat;
194
+ /**
195
+ * Thinking/reasoning configuration
196
+ * Supported by: DeepSeek (deepseek-reasoner), Qwen (QwQ), Anthropic (extended thinking)
197
+ */
198
+ thinking?: ThinkingConfig;
199
+ /**
200
+ * Enable web search (Qwen specific)
201
+ */
202
+ enableSearch?: boolean;
203
+ /**
204
+ * Log probabilities configuration
205
+ */
206
+ logprobs?: boolean;
207
+ /**
208
+ * Number of top log probabilities to return
209
+ */
210
+ topLogprobs?: number;
211
+ }
212
+
213
+ /**
214
+ * Unified Intermediate Representation for LLM Requests
215
+ * This is the core data structure that all adapters convert to/from
216
+ */
217
+ interface LLMRequestIR {
218
+ /**
219
+ * Conversation messages
220
+ * Supports multi-turn dialogue with role-based messages
221
+ */
222
+ messages: Message[];
223
+ /**
224
+ * Model identifier (optional in IR, may be determined by router)
225
+ */
226
+ model?: string;
227
+ /**
228
+ * Tools/Functions available for the model to call
229
+ */
230
+ tools?: Tool[];
231
+ /**
232
+ * Tool choice strategy
233
+ */
234
+ toolChoice?: ToolChoice;
235
+ /**
236
+ * Streaming configuration
237
+ */
238
+ stream?: boolean;
239
+ /**
240
+ * Generation parameters
241
+ */
242
+ generation?: GenerationConfig;
243
+ /**
244
+ * System prompt (some providers use separate field)
245
+ */
246
+ system?: string;
247
+ /**
248
+ * Metadata for tracking and routing
249
+ */
250
+ metadata?: {
251
+ requestId?: string;
252
+ userId?: string;
253
+ sessionId?: string;
254
+ tags?: string[];
255
+ [key: string]: unknown;
256
+ };
257
+ /**
258
+ * Provider-specific extensions
259
+ * Allows passthrough of vendor-specific features
260
+ */
261
+ extensions?: {
262
+ [provider: string]: unknown;
263
+ };
264
+ /**
265
+ * Raw original request (for debugging/logging)
266
+ */
267
+ raw?: unknown;
268
+ }
269
+
270
+ /**
271
+ * Finish reason types
272
+ */
273
+ type FinishReason = 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'error';
274
+ /**
275
+ * Token usage statistics
276
+ */
277
+ interface Usage {
278
+ promptTokens: number;
279
+ completionTokens: number;
280
+ totalTokens: number;
281
+ /**
282
+ * Detailed token breakdown (provider-specific)
283
+ */
284
+ details?: {
285
+ /**
286
+ * Reasoning/thinking tokens (DeepSeek, Qwen)
287
+ */
288
+ reasoningTokens?: number;
289
+ /**
290
+ * Cached prompt tokens (DeepSeek, Anthropic)
291
+ */
292
+ cachedTokens?: number;
293
+ /**
294
+ * Cache creation tokens (Anthropic)
295
+ */
296
+ cacheCreationTokens?: number;
297
+ /**
298
+ * Cache read tokens (Anthropic)
299
+ */
300
+ cacheReadTokens?: number;
301
+ };
302
+ }
303
+ /**
304
+ * Response choice
305
+ */
306
+ interface Choice {
307
+ index: number;
308
+ message: Message;
309
+ finishReason?: FinishReason;
310
+ /**
311
+ * Log probabilities (if requested)
312
+ */
313
+ logprobs?: {
314
+ content?: Array<{
315
+ token: string;
316
+ logprob: number;
317
+ topLogprobs?: Array<{
318
+ token: string;
319
+ logprob: number;
320
+ }>;
321
+ }>;
322
+ };
323
+ }
324
+ /**
325
+ * Unified response structure
326
+ */
327
+ interface LLMResponseIR {
328
+ /**
329
+ * Response ID
330
+ */
331
+ id: string;
332
+ /**
333
+ * Model used
334
+ */
335
+ model: string;
336
+ /**
337
+ * Generated message(s)
338
+ */
339
+ choices: Choice[];
340
+ /**
341
+ * Token usage statistics
342
+ */
343
+ usage?: Usage;
344
+ /**
345
+ * Response creation timestamp
346
+ */
347
+ created?: number;
348
+ /**
349
+ * System fingerprint (for reproducibility)
350
+ */
351
+ systemFingerprint?: string;
352
+ /**
353
+ * Metadata
354
+ */
355
+ metadata?: {
356
+ requestId?: string;
357
+ [key: string]: unknown;
358
+ };
359
+ /**
360
+ * Provider-specific extensions
361
+ */
362
+ extensions?: {
363
+ [provider: string]: unknown;
364
+ };
365
+ /**
366
+ * Raw original response (for debugging/logging)
367
+ */
368
+ raw?: unknown;
369
+ }
370
+
371
+ /**
372
+ * Stream event types
373
+ */
374
+ type StreamEventType = 'start' | 'content' | 'reasoning' | 'tool_call' | 'end' | 'error';
375
+ /**
376
+ * Content delta
377
+ */
378
+ interface ContentDelta {
379
+ type: 'content';
380
+ delta: string;
381
+ index?: number;
382
+ }
383
+ /**
384
+ * Reasoning content delta (DeepSeek, Qwen, Anthropic)
385
+ */
386
+ interface ReasoningDelta {
387
+ type: 'reasoning';
388
+ delta: string;
389
+ index?: number;
390
+ }
391
+ /**
392
+ * Tool call delta
393
+ */
394
+ interface ToolCallDelta {
395
+ type: 'tool_call';
396
+ id?: string;
397
+ name?: string;
398
+ arguments?: string;
399
+ index?: number;
400
+ }
401
+ /**
402
+ * Stream event
403
+ */
404
+ interface LLMStreamEvent {
405
+ /**
406
+ * Event type
407
+ */
408
+ type: StreamEventType;
409
+ /**
410
+ * Event ID
411
+ */
412
+ id?: string;
413
+ /**
414
+ * Model used
415
+ */
416
+ model?: string;
417
+ /**
418
+ * Content delta
419
+ */
420
+ content?: ContentDelta;
421
+ /**
422
+ * Reasoning/thinking content delta
423
+ */
424
+ reasoning?: ReasoningDelta;
425
+ /**
426
+ * Tool call delta
427
+ */
428
+ toolCall?: ToolCallDelta;
429
+ /**
430
+ * Finish reason (for end event)
431
+ */
432
+ finishReason?: FinishReason;
433
+ /**
434
+ * Complete message (for end event)
435
+ */
436
+ message?: Message;
437
+ /**
438
+ * Usage statistics (for end event, if stream_options.include_usage is true)
439
+ */
440
+ usage?: Usage;
441
+ /**
442
+ * Error (for error event)
443
+ */
444
+ error?: {
445
+ message: string;
446
+ code?: string;
447
+ [key: string]: unknown;
448
+ };
449
+ /**
450
+ * Raw original event (for debugging)
451
+ */
452
+ raw?: unknown;
453
+ }
454
+ /**
455
+ * SSE (Server-Sent Events) event for streaming responses
456
+ * This is the formatted event that can be directly written to HTTP response
457
+ */
458
+ interface SSEEvent {
459
+ /**
460
+ * Event type (e.g., 'message_start', 'content_block_delta', 'data')
461
+ */
462
+ event: string;
463
+ /**
464
+ * Event data payload
465
+ */
466
+ data: unknown;
467
+ }
468
+
469
+ /**
470
+ * Error types
471
+ */
472
+ type ErrorType = 'network' | 'api' | 'validation' | 'rate_limit' | 'authentication' | 'permission' | 'not_found' | 'server' | 'unknown';
473
+ /**
474
+ * Unified error structure
475
+ */
476
+ interface LLMErrorIR {
477
+ /**
478
+ * Error type
479
+ */
480
+ type: ErrorType;
481
+ /**
482
+ * Error message
483
+ */
484
+ message: string;
485
+ /**
486
+ * Error code (provider-specific)
487
+ */
488
+ code?: string;
489
+ /**
490
+ * HTTP status code
491
+ */
492
+ status?: number;
493
+ /**
494
+ * Whether the error is retryable
495
+ */
496
+ retryable?: boolean;
497
+ /**
498
+ * Additional error details
499
+ */
500
+ details?: {
501
+ [key: string]: unknown;
502
+ };
503
+ /**
504
+ * Original error (for debugging)
505
+ */
506
+ raw?: unknown;
507
+ }
508
+
509
+ /**
510
+ * Provider endpoint configuration
511
+ */
512
+ interface ProviderEndpoint {
513
+ /**
514
+ * Base URL for the API
515
+ */
516
+ baseUrl: string;
517
+ /**
518
+ * Chat completions path (default: '/v1/chat/completions')
519
+ */
520
+ chatPath?: string;
521
+ /**
522
+ * Models list path (default: '/v1/models')
523
+ */
524
+ modelsPath?: string;
525
+ }
526
+ /**
527
+ * Adapter capabilities
528
+ */
529
+ interface AdapterCapabilities {
530
+ /**
531
+ * Supports streaming
532
+ */
533
+ streaming: boolean;
534
+ /**
535
+ * Supports tool/function calling
536
+ */
537
+ tools: boolean;
538
+ /**
539
+ * Supports vision (image input)
540
+ */
541
+ vision: boolean;
542
+ /**
543
+ * Supports multimodal content (images, audio, video, documents)
544
+ */
545
+ multimodal: boolean;
546
+ /**
547
+ * Supports system prompt
548
+ */
549
+ systemPrompt: boolean;
550
+ /**
551
+ * Supports tool choice
552
+ */
553
+ toolChoice: boolean;
554
+ /**
555
+ * Supports reasoning/thinking mode (DeepSeek, Qwen QwQ, Anthropic)
556
+ */
557
+ reasoning?: boolean;
558
+ /**
559
+ * Supports web search (Qwen)
560
+ */
561
+ webSearch?: boolean;
562
+ /**
563
+ * Supports JSON mode / structured output
564
+ */
565
+ jsonMode?: boolean;
566
+ /**
567
+ * Supports log probabilities
568
+ */
569
+ logprobs?: boolean;
570
+ /**
571
+ * Supports seed for reproducibility
572
+ */
573
+ seed?: boolean;
574
+ }
575
+ /**
576
+ * Validation result
577
+ */
578
+ interface ValidationResult {
579
+ valid: boolean;
580
+ errors?: string[];
581
+ warnings?: string[];
582
+ }
583
+ /**
584
+ * Adapter information
585
+ */
586
+ interface AdapterInfo {
587
+ name: string;
588
+ version: string;
589
+ capabilities: AdapterCapabilities;
590
+ /**
591
+ * Default endpoint configuration
592
+ */
593
+ endpoint?: ProviderEndpoint;
594
+ }
595
+
596
+ /**
597
+ * Stream handler function
598
+ */
599
+ type StreamHandler = (chunk: unknown) => LLMStreamEvent | LLMStreamEvent[] | null;
600
+ /**
601
+ * Error handler function
602
+ */
603
+ type ErrorHandler = (error: unknown) => LLMErrorIR;
604
+ /**
605
+ * Stream event builder interface
606
+ * Handles stateful conversion of IR stream events to provider-specific SSE events
607
+ */
608
+ interface StreamEventBuilder {
609
+ /**
610
+ * Process an IR stream event and return SSE events
611
+ * May return multiple events (e.g., message_start + content_block_start)
612
+ * May return empty array if no events should be emitted
613
+ */
614
+ process(event: LLMStreamEvent): SSEEvent[];
615
+ /**
616
+ * Get any final events that should be emitted when the stream ends
617
+ * Called after all events have been processed
618
+ */
619
+ finalize?(): SSEEvent[];
620
+ }
621
+ /**
622
+ * LLM Adapter interface
623
+ * Defines the contract for bidirectional conversion between provider formats and IR
624
+ */
625
+ interface LLMAdapter {
626
+ /**
627
+ * Adapter name (e.g., 'openai', 'anthropic')
628
+ */
629
+ readonly name: string;
630
+ /**
631
+ * Adapter version
632
+ */
633
+ readonly version: string;
634
+ /**
635
+ * Adapter capabilities
636
+ */
637
+ readonly capabilities: AdapterCapabilities;
638
+ /**
639
+ * Inbound conversion (Provider format → IR)
640
+ */
641
+ inbound: {
642
+ /**
643
+ * Parse provider request to IR
644
+ */
645
+ parseRequest(request: unknown): LLMRequestIR;
646
+ /**
647
+ * Parse provider response to IR
648
+ */
649
+ parseResponse?(response: unknown): LLMResponseIR;
650
+ /**
651
+ * Parse provider stream chunk to IR stream event
652
+ */
653
+ parseStream?(chunk: unknown): LLMStreamEvent | LLMStreamEvent[] | null;
654
+ /**
655
+ * Parse provider error to IR error
656
+ */
657
+ parseError?(error: unknown): LLMErrorIR;
658
+ };
659
+ /**
660
+ * Outbound conversion (IR → Provider format)
661
+ */
662
+ outbound: {
663
+ /**
664
+ * Build provider request from IR
665
+ */
666
+ buildRequest(ir: LLMRequestIR): unknown;
667
+ /**
668
+ * Build provider response from IR
669
+ */
670
+ buildResponse?(ir: LLMResponseIR): unknown;
671
+ /**
672
+ * Build provider stream event from IR stream event
673
+ */
674
+ buildStreamEvent?(ir: LLMStreamEvent): unknown;
675
+ /**
676
+ * Get stream handler for provider
677
+ */
678
+ buildStreamHandler?(): StreamHandler;
679
+ /**
680
+ * Get error handler for provider
681
+ */
682
+ buildErrorHandler?(): ErrorHandler;
683
+ /**
684
+ * Create a stream event builder for converting IR events to provider SSE format
685
+ * The builder maintains state for proper event sequencing (e.g., tracking content blocks)
686
+ */
687
+ createStreamBuilder?(): StreamEventBuilder;
688
+ };
689
+ /**
690
+ * Validate IR request for this adapter
691
+ */
692
+ validateRequest?(ir: LLMRequestIR): ValidationResult;
693
+ /**
694
+ * Check if adapter supports a specific capability
695
+ */
696
+ supportsCapability?(capability: keyof AdapterCapabilities): boolean;
697
+ /**
698
+ * Get adapter information
699
+ */
700
+ getInfo(): AdapterInfo;
701
+ }
702
+
703
+ /**
704
+ * Adapter registry for managing adapters
705
+ */
706
+ declare class AdapterRegistry {
707
+ private adapters;
708
+ /**
709
+ * Register an adapter
710
+ */
711
+ register(adapter: LLMAdapter): void;
712
+ /**
713
+ * Unregister an adapter
714
+ */
715
+ unregister(name: string): boolean;
716
+ /**
717
+ * Get an adapter by name
718
+ */
719
+ get(name: string): LLMAdapter | undefined;
720
+ /**
721
+ * Check if an adapter is registered
722
+ */
723
+ has(name: string): boolean;
724
+ /**
725
+ * List all registered adapters
726
+ */
727
+ list(): AdapterInfo[];
728
+ /**
729
+ * Clear all adapters
730
+ */
731
+ clear(): void;
732
+ }
733
+ /**
734
+ * Global adapter registry instance
735
+ */
736
+ declare const globalRegistry: AdapterRegistry;
737
+
738
+ /**
739
+ * Bridge configuration
740
+ */
741
+ interface BridgeConfig {
742
+ /**
743
+ * API key for the target provider
744
+ */
745
+ apiKey: string;
746
+ /**
747
+ * Base URL for the target provider API
748
+ * Overrides adapter's default baseUrl
749
+ */
750
+ baseURL?: string;
751
+ /**
752
+ * Custom chat endpoint path
753
+ * Overrides adapter's default chatPath
754
+ * Example: '/v1/chat/completions', '/responses'
755
+ */
756
+ chatPath?: string;
757
+ /**
758
+ * Custom models endpoint path
759
+ * Overrides adapter's default modelsPath
760
+ * Example: '/v1/models', '/models'
761
+ */
762
+ modelsPath?: string;
763
+ /**
764
+ * Request timeout in milliseconds
765
+ */
766
+ timeout?: number;
767
+ /**
768
+ * Maximum number of retries for failed requests
769
+ */
770
+ maxRetries?: number;
771
+ /**
772
+ * Custom headers
773
+ */
774
+ headers?: Record<string, string>;
775
+ /**
776
+ * Authentication header name (default: 'Authorization')
777
+ */
778
+ authHeaderName?: string;
779
+ /**
780
+ * Authentication header prefix (default: 'Bearer')
781
+ * Set to empty string if no prefix is needed
782
+ */
783
+ authHeaderPrefix?: string;
784
+ /**
785
+ * Additional provider-specific options
786
+ */
787
+ [key: string]: unknown;
788
+ }
789
+ /**
790
+ * Bridge lifecycle hooks
791
+ *
792
+ * Hooks allow external code to intercept and observe the Bridge's request/response flow
793
+ * at the IR (Intermediate Representation) layer, where all data is in a unified format.
794
+ *
795
+ * Use cases:
796
+ * - Logging and monitoring (track token usage, latency, errors)
797
+ * - Cost tracking (calculate costs based on token usage)
798
+ * - Debugging and tracing (inspect IR transformations)
799
+ * - Rate limiting and alerting (implement usage-based limits)
800
+ * - Audit logging (record all LLM API calls)
801
+ */
802
+ interface BridgeHooks {
803
+ /**
804
+ * Called after parsing the inbound request into IR, before building the outbound request
805
+ *
806
+ * @param ir - The unified request IR
807
+ * @returns void or Promise<void>
808
+ *
809
+ * @example
810
+ * ```typescript
811
+ * onRequest: async (ir) => {
812
+ * console.log(`Request to model: ${ir.model}`)
813
+ * console.log(`Message count: ${ir.messages.length}`)
814
+ * }
815
+ * ```
816
+ */
817
+ onRequest?: (ir: LLMRequestIR) => void | Promise<void>;
818
+ /**
819
+ * Called after parsing the provider response into IR, before building the final response
820
+ *
821
+ * This is the ideal place to extract metadata like token usage, as all providers'
822
+ * responses have been normalized to the same IR format.
823
+ *
824
+ * @param ir - The unified response IR
825
+ * @returns void or Promise<void>
826
+ *
827
+ * @example
828
+ * ```typescript
829
+ * onResponse: async (ir) => {
830
+ * if (ir.usage) {
831
+ * console.log(`Input tokens: ${ir.usage.promptTokens}`)
832
+ * console.log(`Output tokens: ${ir.usage.completionTokens}`)
833
+ * await recordTokenUsage(ir.usage)
834
+ * }
835
+ * }
836
+ * ```
837
+ */
838
+ onResponse?: (ir: LLMResponseIR) => void | Promise<void>;
839
+ /**
840
+ * Called for each streaming event after parsing into IR, before building the SSE event
841
+ *
842
+ * @param event - The unified stream event IR
843
+ * @returns void or Promise<void>
844
+ *
845
+ * @example
846
+ * ```typescript
847
+ * onStreamEvent: async (event) => {
848
+ * if (event.type === 'end' && event.usage) {
849
+ * await recordTokenUsage(event.usage)
850
+ * }
851
+ * }
852
+ * ```
853
+ */
854
+ onStreamEvent?: (event: LLMStreamEvent) => void | Promise<void>;
855
+ /**
856
+ * Called when an error occurs during the request/response flow
857
+ *
858
+ * @param error - The unified error IR
859
+ * @returns void or Promise<void>
860
+ *
861
+ * @example
862
+ * ```typescript
863
+ * onError: async (error) => {
864
+ * console.error(`Bridge error: ${error.message}`)
865
+ * await logError(error)
866
+ * }
867
+ * ```
868
+ */
869
+ onError?: (error: LLMErrorIR) => void | Promise<void>;
870
+ }
871
+ /**
872
+ * Bridge options
873
+ */
874
+ interface BridgeOptions {
875
+ /**
876
+ * Inbound adapter (parses incoming requests)
877
+ */
878
+ inbound: LLMAdapter;
879
+ /**
880
+ * Outbound adapter (builds outgoing requests)
881
+ */
882
+ outbound: LLMAdapter;
883
+ /**
884
+ * Configuration for the target provider
885
+ */
886
+ config: BridgeConfig;
887
+ /**
888
+ * Lifecycle hooks for intercepting IR-level events
889
+ *
890
+ * Hooks are called at key points in the request/response flow, allowing
891
+ * external code to observe and react to events at the IR layer where all
892
+ * data is in a unified format.
893
+ *
894
+ * @example
895
+ * ```typescript
896
+ * const bridge = createBridge({
897
+ * inbound: openaiAdapter,
898
+ * outbound: anthropicAdapter,
899
+ * config: { apiKey: '...' },
900
+ * hooks: {
901
+ * onResponse: async (ir) => {
902
+ * // Track token usage in unified format
903
+ * await recordTokens({
904
+ * input: ir.usage?.promptTokens ?? 0,
905
+ * output: ir.usage?.completionTokens ?? 0
906
+ * })
907
+ * }
908
+ * }
909
+ * })
910
+ * ```
911
+ */
912
+ hooks?: BridgeHooks;
913
+ /**
914
+ * Fixed target model (highest priority)
915
+ * If set, ignores the inbound model and always uses this model
916
+ */
917
+ targetModel?: string;
918
+ /**
919
+ * Model mapping function (second priority)
920
+ * Receives the inbound model name and returns the outbound model name
921
+ */
922
+ modelMapper?: (inboundModel: string) => string;
923
+ /**
924
+ * Model mapping table (third priority)
925
+ * Maps inbound model names to outbound model names
926
+ */
927
+ modelMapping?: {
928
+ [inboundModel: string]: string;
929
+ };
930
+ }
931
+ /**
932
+ * Compatibility report
933
+ */
934
+ interface CompatibilityReport {
935
+ compatible: boolean;
936
+ issues?: string[];
937
+ warnings?: string[];
938
+ }
939
+ /**
940
+ * Amux Bridge interface
941
+ */
942
+ interface LLMBridge {
943
+ /**
944
+ * Send a chat request
945
+ * Returns response in inbound adapter's format
946
+ */
947
+ chat(request: unknown): Promise<unknown>;
948
+ /**
949
+ * Send a chat request (raw IR response)
950
+ * Returns raw IR response for custom processing
951
+ * Use this when you need to access the IR directly
952
+ */
953
+ chatRaw(request: unknown): Promise<LLMResponseIR>;
954
+ /**
955
+ * Send a streaming chat request
956
+ * Returns SSE events in inbound adapter's format
957
+ * This is the recommended method for streaming - events can be directly written to HTTP response
958
+ */
959
+ chatStream(request: unknown): AsyncIterable<SSEEvent>;
960
+ /**
961
+ * Send a streaming chat request (raw IR events)
962
+ * Returns raw IR stream events for custom processing
963
+ * Use this when you need to customize the stream handling
964
+ */
965
+ chatStreamRaw(request: unknown): AsyncIterable<LLMStreamEvent>;
966
+ /**
967
+ * List available models from the provider
968
+ * Returns the raw model list response from the provider
969
+ */
970
+ listModels(): Promise<unknown>;
971
+ /**
972
+ * Check compatibility between inbound and outbound adapters
973
+ */
974
+ checkCompatibility(): CompatibilityReport;
975
+ /**
976
+ * Get adapter information
977
+ */
978
+ getAdapters(): {
979
+ inbound: {
980
+ name: string;
981
+ version: string;
982
+ };
983
+ outbound: {
984
+ name: string;
985
+ version: string;
986
+ };
987
+ };
988
+ }
989
+ /**
990
+ * HTTP request options
991
+ */
992
+ interface HTTPRequestOptions {
993
+ method: string;
994
+ url: string;
995
+ headers?: Record<string, string>;
996
+ body?: unknown;
997
+ timeout?: number;
998
+ signal?: AbortSignal;
999
+ }
1000
+ /**
1001
+ * HTTP response
1002
+ */
1003
+ interface HTTPResponse<T = unknown> {
1004
+ status: number;
1005
+ statusText: string;
1006
+ headers: Record<string, string>;
1007
+ data: T;
1008
+ }
1009
+
1010
+ /**
1011
+ * Bridge implementation
1012
+ */
1013
+ declare class Bridge implements LLMBridge {
1014
+ private inboundAdapter;
1015
+ private outboundAdapter;
1016
+ private config;
1017
+ private httpClient;
1018
+ private hooks?;
1019
+ private targetModel?;
1020
+ private modelMapper?;
1021
+ private modelMapping?;
1022
+ constructor(options: BridgeOptions);
1023
+ /**
1024
+ * Map model name from inbound to outbound
1025
+ * Priority: targetModel > modelMapper > modelMapping > original model
1026
+ */
1027
+ private mapModel;
1028
+ /**
1029
+ * Validate that the IR request features are supported by outbound adapter
1030
+ * @private
1031
+ */
1032
+ private validateCapabilities;
1033
+ /**
1034
+ * Send a chat request
1035
+ */
1036
+ chat(request: unknown): Promise<unknown>;
1037
+ /**
1038
+ * Send a chat request (raw IR response)
1039
+ * Returns raw IR response for custom processing
1040
+ */
1041
+ chatRaw(request: unknown): Promise<LLMResponseIR>;
1042
+ /**
1043
+ * Send a streaming chat request
1044
+ * Returns SSE events in inbound adapter's format
1045
+ */
1046
+ chatStream(request: unknown): AsyncIterable<SSEEvent>;
1047
+ /**
1048
+ * Send a streaming chat request (raw IR events)
1049
+ * Returns raw IR stream events for custom processing
1050
+ */
1051
+ chatStreamRaw(request: unknown): AsyncIterable<LLMStreamEvent>;
1052
+ /**
1053
+ * Process SSE lines and yield stream events
1054
+ * @private
1055
+ */
1056
+ private processSSELines;
1057
+ /**
1058
+ * List available models from the provider
1059
+ */
1060
+ listModels(): Promise<unknown>;
1061
+ /**
1062
+ * Check compatibility between adapters
1063
+ */
1064
+ checkCompatibility(): CompatibilityReport;
1065
+ /**
1066
+ * Get adapter information
1067
+ */
1068
+ getAdapters(): {
1069
+ inbound: {
1070
+ name: string;
1071
+ version: string;
1072
+ };
1073
+ outbound: {
1074
+ name: string;
1075
+ version: string;
1076
+ };
1077
+ };
1078
+ /**
1079
+ * Get default base URL from outbound adapter
1080
+ * Note: config.baseURL is checked before calling this method
1081
+ */
1082
+ private getDefaultBaseURL;
1083
+ /**
1084
+ * Get chat endpoint path
1085
+ * Supports dynamic model replacement for endpoints like /v1beta/models/{model}:generateContent
1086
+ * Priority: config.chatPath > adapter.endpoint.chatPath
1087
+ */
1088
+ private getEndpoint;
1089
+ /**
1090
+ * Get models endpoint path
1091
+ * Priority: config.modelsPath > adapter.endpoint.modelsPath
1092
+ */
1093
+ private getModelsPath;
1094
+ }
1095
+
1096
+ /**
1097
+ * Create a new Amux Bridge instance
1098
+ *
1099
+ * @example
1100
+ * ```typescript
1101
+ * import { createBridge } from '@amux.ai/llm-bridge'
1102
+ * import { openaiAdapter } from '@amux.ai/adapter-openai'
1103
+ * import { anthropicAdapter } from '@amux.ai/adapter-anthropic'
1104
+ *
1105
+ * const bridge = createBridge({
1106
+ * inbound: openaiAdapter,
1107
+ * outbound: anthropicAdapter,
1108
+ * config: {
1109
+ * apiKey: process.env.ANTHROPIC_API_KEY,
1110
+ * }
1111
+ * })
1112
+ *
1113
+ * const response = await bridge.chat({
1114
+ * model: 'gpt-4',
1115
+ * messages: [{ role: 'user', content: 'Hello!' }]
1116
+ * })
1117
+ * ```
1118
+ */
1119
+ declare function createBridge(options: BridgeOptions): LLMBridge;
1120
+
1121
+ /**
1122
+ * Simple HTTP client for making requests to LLM APIs
1123
+ */
1124
+ declare class HTTPClient {
1125
+ private defaultHeaders;
1126
+ private defaultTimeout;
1127
+ private maxRetries;
1128
+ private provider;
1129
+ private maxResponseSize;
1130
+ constructor(options?: {
1131
+ headers?: Record<string, string>;
1132
+ timeout?: number;
1133
+ maxRetries?: number;
1134
+ provider?: string;
1135
+ maxResponseSize?: number;
1136
+ });
1137
+ /**
1138
+ * Determine if a status code should be retried
1139
+ * @private
1140
+ */
1141
+ private shouldRetry;
1142
+ /**
1143
+ * Calculate backoff delay with jitter
1144
+ * @private
1145
+ */
1146
+ private getBackoffDelay;
1147
+ /**
1148
+ * Make an HTTP request with retry logic
1149
+ */
1150
+ request<T = unknown>(options: HTTPRequestOptions, retries?: number): Promise<HTTPResponse<T>>;
1151
+ /**
1152
+ * Make a streaming HTTP request
1153
+ */
1154
+ requestStream(options: HTTPRequestOptions): AsyncIterable<string>;
1155
+ }
1156
+
1157
+ /**
1158
+ * Base error class for all Amux errors
1159
+ */
1160
+ declare class LLMBridgeError extends Error {
1161
+ code: string;
1162
+ retryable: boolean;
1163
+ details?: unknown;
1164
+ constructor(message: string, code: string, retryable?: boolean, details?: unknown);
1165
+ }
1166
+ /**
1167
+ * Error from provider API calls
1168
+ */
1169
+ declare class APIError extends LLMBridgeError {
1170
+ status: number;
1171
+ provider: string;
1172
+ data?: unknown;
1173
+ response?: {
1174
+ headers?: Record<string, string>;
1175
+ };
1176
+ constructor(message: string, status: number, provider: string, data?: unknown, response?: {
1177
+ headers?: Record<string, string>;
1178
+ });
1179
+ }
1180
+ /**
1181
+ * Network-related errors (connection failures, timeouts, etc.)
1182
+ */
1183
+ declare class NetworkError extends LLMBridgeError {
1184
+ cause?: unknown;
1185
+ constructor(message: string, cause?: unknown);
1186
+ }
1187
+ /**
1188
+ * Request timeout errors
1189
+ */
1190
+ declare class TimeoutError extends LLMBridgeError {
1191
+ timeout: number;
1192
+ constructor(message: string, timeout: number);
1193
+ }
1194
+ /**
1195
+ * Validation errors (invalid request format, missing required fields, etc.)
1196
+ */
1197
+ declare class ValidationError extends LLMBridgeError {
1198
+ errors: string[];
1199
+ constructor(message: string, errors: string[]);
1200
+ }
1201
+ /**
1202
+ * Adapter-related errors (conversion failures, unsupported features, etc.)
1203
+ */
1204
+ declare class AdapterError extends LLMBridgeError {
1205
+ adapterName: string;
1206
+ constructor(message: string, adapterName: string, details?: unknown);
1207
+ }
1208
+ /**
1209
+ * Bridge orchestration errors
1210
+ */
1211
+ declare class BridgeError extends LLMBridgeError {
1212
+ constructor(message: string, details?: unknown);
1213
+ }
1214
+
1215
+ /**
1216
+ * Efficient SSE (Server-Sent Events) line parser
1217
+ * Handles incomplete chunks and extracts complete lines efficiently
1218
+ */
1219
+ declare class SSELineParser {
1220
+ private buffer;
1221
+ /**
1222
+ * Process a chunk of SSE data and extract complete lines
1223
+ * @param chunk - The data chunk to process
1224
+ * @returns Array of complete lines
1225
+ */
1226
+ processChunk(chunk: string): string[];
1227
+ /**
1228
+ * Extract all complete lines from the buffer
1229
+ * Incomplete lines remain in the buffer
1230
+ * @private
1231
+ */
1232
+ private extractLines;
1233
+ /**
1234
+ * Get any remaining data in the buffer and clear it
1235
+ * Call this when the stream ends to get the last incomplete line
1236
+ */
1237
+ flush(): string[];
1238
+ /**
1239
+ * Check if buffer has any remaining data
1240
+ */
1241
+ hasRemaining(): boolean;
1242
+ /**
1243
+ * Clear the buffer
1244
+ */
1245
+ clear(): void;
1246
+ }
1247
+
1248
+ /**
1249
+ * Map finish reason string to IR finish reason
1250
+ * @param reason - The finish reason string from the provider
1251
+ * @param customMappings - Optional custom mappings to extend/override standard mappings
1252
+ * @param defaultReason - Default reason if not found (default: 'stop')
1253
+ */
1254
+ declare function mapFinishReason(reason?: string, customMappings?: Record<string, FinishReason>, defaultReason?: FinishReason): FinishReason;
1255
+ /**
1256
+ * Map error type or code string to IR error type
1257
+ * @param type - The error type string from the provider
1258
+ * @param code - The error code string from the provider (optional)
1259
+ * @param customTypeMappings - Optional custom type mappings to extend/override standard mappings
1260
+ * @param customCodeMappings - Optional custom code mappings to extend/override standard mappings
1261
+ */
1262
+ declare function mapErrorType(type?: string, code?: string, customTypeMappings?: Record<string, ErrorType>, customCodeMappings?: Record<string, ErrorType>): ErrorType;
1263
+ /**
1264
+ * Parse OpenAI-compatible error response to IR
1265
+ * Works for OpenAI, DeepSeek, Moonshot, Qwen, Zhipu, and other compatible APIs
1266
+ *
1267
+ * @param error - The error object from the provider
1268
+ * @param customTypeMappings - Optional custom error type mappings
1269
+ * @param customCodeMappings - Optional custom error code mappings
1270
+ */
1271
+ declare function parseOpenAICompatibleError(error: unknown, customTypeMappings?: Record<string, ErrorType>, customCodeMappings?: Record<string, ErrorType>): LLMErrorIR;
1272
+
1273
+ /**
1274
+ * Convert message content to string
1275
+ * Used by response builders to convert IR content to provider format
1276
+ *
1277
+ * @param content - Message content (string or ContentPart array)
1278
+ * @returns String content or null if empty
1279
+ */
1280
+ declare function contentToString(content: MessageContent): string | null;
1281
+ /**
1282
+ * Check if content contains only text
1283
+ * @param content - Message content
1284
+ * @returns True if content is string or contains only text parts
1285
+ */
1286
+ declare function isTextOnlyContent(content: MessageContent): boolean;
1287
+ /**
1288
+ * Extract text from content
1289
+ * @param content - Message content
1290
+ * @returns Array of text strings
1291
+ */
1292
+ declare function extractTextFromContent(content: MessageContent): string[];
1293
+ /**
1294
+ * Check if content has images
1295
+ * @param content - Message content
1296
+ * @returns True if content contains image parts
1297
+ */
1298
+ declare function hasImageContent(content: MessageContent): boolean;
1299
+
1300
+ /**
1301
+ * Standard OpenAI-compatible usage object structure
1302
+ */
1303
+ interface StandardUsage {
1304
+ prompt_tokens: number;
1305
+ completion_tokens: number;
1306
+ total_tokens: number;
1307
+ completion_tokens_details?: {
1308
+ reasoning_tokens?: number;
1309
+ };
1310
+ prompt_cache_hit_tokens?: number;
1311
+ prompt_cache_miss_tokens?: number;
1312
+ }
1313
+ /**
1314
+ * Parse OpenAI-compatible usage to IR usage
1315
+ * @param usage - Usage object from provider response
1316
+ * @returns IR usage object or undefined
1317
+ */
1318
+ declare function parseOpenAIUsage(usage?: StandardUsage): Usage | undefined;
1319
+ /**
1320
+ * Build OpenAI-compatible usage from IR usage
1321
+ * @param usage - IR usage object
1322
+ * @param includeReasoningTokens - Whether to include reasoning tokens (default: true)
1323
+ * @param includeCacheTokens - Whether to include cache tokens (default: false)
1324
+ * @returns OpenAI-compatible usage object or undefined
1325
+ */
1326
+ declare function buildOpenAIUsage(usage?: Usage, includeReasoningTokens?: boolean, includeCacheTokens?: boolean): StandardUsage | undefined;
1327
+
1328
+ export { APIError, type AdapterCapabilities, AdapterError, type AdapterInfo, AdapterRegistry, Bridge, type BridgeConfig, BridgeError, type BridgeHooks, type BridgeOptions, type Choice, type CompatibilityReport, type ContentDelta, type ContentPart, type ErrorHandler, type ErrorType, type FinishReason, type FunctionDefinition, type GenerationConfig, HTTPClient, type HTTPRequestOptions, type HTTPResponse, type ImageContent, type ImageSource, type JSONSchema, type LLMAdapter, type LLMBridge, LLMBridgeError, type LLMErrorIR, type LLMRequestIR, type LLMResponseIR, type LLMStreamEvent, type Message, type MessageContent, NetworkError, type ProviderEndpoint, type ReasoningDelta, type ResponseFormat, type Role, type SSEEvent, SSELineParser, type StandardUsage, type StreamEventBuilder, type StreamEventType, type StreamHandler, type TextContent, type ThinkingConfig, TimeoutError, type Tool, type ToolCall, type ToolCallDelta, type ToolChoice, type Usage, ValidationError, type ValidationResult, buildOpenAIUsage, contentToString, createBridge, extractTextFromContent, globalRegistry, hasImageContent, isTextOnlyContent, mapErrorType, mapFinishReason, parseOpenAICompatibleError, parseOpenAIUsage };