@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.
Files changed (131) hide show
  1. package/dist/context/index.d.ts +10 -0
  2. package/dist/context/index.d.ts.map +1 -0
  3. package/dist/context/index.js +9 -0
  4. package/dist/context/index.js.map +1 -0
  5. package/dist/context/process.d.ts +22 -0
  6. package/dist/context/process.d.ts.map +1 -0
  7. package/dist/context/process.js +369 -0
  8. package/dist/context/process.js.map +1 -0
  9. package/dist/context/types.d.ts +118 -0
  10. package/dist/context/types.d.ts.map +1 -0
  11. package/dist/context/types.js +60 -0
  12. package/dist/context/types.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +18 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/membrane.d.ts +96 -0
  18. package/dist/membrane.d.ts.map +1 -0
  19. package/dist/membrane.js +893 -0
  20. package/dist/membrane.js.map +1 -0
  21. package/dist/providers/anthropic.d.ts +36 -0
  22. package/dist/providers/anthropic.d.ts.map +1 -0
  23. package/dist/providers/anthropic.js +265 -0
  24. package/dist/providers/anthropic.js.map +1 -0
  25. package/dist/providers/index.d.ts +8 -0
  26. package/dist/providers/index.d.ts.map +1 -0
  27. package/dist/providers/index.js +8 -0
  28. package/dist/providers/index.js.map +1 -0
  29. package/dist/providers/openai-compatible.d.ts +74 -0
  30. package/dist/providers/openai-compatible.d.ts.map +1 -0
  31. package/dist/providers/openai-compatible.js +412 -0
  32. package/dist/providers/openai-compatible.js.map +1 -0
  33. package/dist/providers/openai.d.ts +69 -0
  34. package/dist/providers/openai.d.ts.map +1 -0
  35. package/dist/providers/openai.js +455 -0
  36. package/dist/providers/openai.js.map +1 -0
  37. package/dist/providers/openrouter.d.ts +76 -0
  38. package/dist/providers/openrouter.d.ts.map +1 -0
  39. package/dist/providers/openrouter.js +492 -0
  40. package/dist/providers/openrouter.js.map +1 -0
  41. package/dist/transforms/chat.d.ts +52 -0
  42. package/dist/transforms/chat.d.ts.map +1 -0
  43. package/dist/transforms/chat.js +136 -0
  44. package/dist/transforms/chat.js.map +1 -0
  45. package/dist/transforms/index.d.ts +6 -0
  46. package/dist/transforms/index.d.ts.map +1 -0
  47. package/dist/transforms/index.js +6 -0
  48. package/dist/transforms/index.js.map +1 -0
  49. package/dist/transforms/prefill.d.ts +89 -0
  50. package/dist/transforms/prefill.d.ts.map +1 -0
  51. package/dist/transforms/prefill.js +401 -0
  52. package/dist/transforms/prefill.js.map +1 -0
  53. package/dist/types/config.d.ts +103 -0
  54. package/dist/types/config.d.ts.map +1 -0
  55. package/dist/types/config.js +21 -0
  56. package/dist/types/config.js.map +1 -0
  57. package/dist/types/content.d.ts +81 -0
  58. package/dist/types/content.d.ts.map +1 -0
  59. package/dist/types/content.js +40 -0
  60. package/dist/types/content.js.map +1 -0
  61. package/dist/types/errors.d.ts +42 -0
  62. package/dist/types/errors.d.ts.map +1 -0
  63. package/dist/types/errors.js +208 -0
  64. package/dist/types/errors.js.map +1 -0
  65. package/dist/types/index.d.ts +18 -0
  66. package/dist/types/index.d.ts.map +1 -0
  67. package/dist/types/index.js +9 -0
  68. package/dist/types/index.js.map +1 -0
  69. package/dist/types/message.d.ts +46 -0
  70. package/dist/types/message.d.ts.map +1 -0
  71. package/dist/types/message.js +38 -0
  72. package/dist/types/message.js.map +1 -0
  73. package/dist/types/provider.d.ts +155 -0
  74. package/dist/types/provider.d.ts.map +1 -0
  75. package/dist/types/provider.js +5 -0
  76. package/dist/types/provider.js.map +1 -0
  77. package/dist/types/request.d.ts +78 -0
  78. package/dist/types/request.d.ts.map +1 -0
  79. package/dist/types/request.js +5 -0
  80. package/dist/types/request.js.map +1 -0
  81. package/dist/types/response.d.ts +131 -0
  82. package/dist/types/response.d.ts.map +1 -0
  83. package/dist/types/response.js +7 -0
  84. package/dist/types/response.js.map +1 -0
  85. package/dist/types/streaming.d.ts +164 -0
  86. package/dist/types/streaming.d.ts.map +1 -0
  87. package/dist/types/streaming.js +5 -0
  88. package/dist/types/streaming.js.map +1 -0
  89. package/dist/types/tools.d.ts +71 -0
  90. package/dist/types/tools.d.ts.map +1 -0
  91. package/dist/types/tools.js +5 -0
  92. package/dist/types/tools.js.map +1 -0
  93. package/dist/utils/index.d.ts +5 -0
  94. package/dist/utils/index.d.ts.map +1 -0
  95. package/dist/utils/index.js +5 -0
  96. package/dist/utils/index.js.map +1 -0
  97. package/dist/utils/stream-parser.d.ts +53 -0
  98. package/dist/utils/stream-parser.d.ts.map +1 -0
  99. package/dist/utils/stream-parser.js +359 -0
  100. package/dist/utils/stream-parser.js.map +1 -0
  101. package/dist/utils/tool-parser.d.ts +130 -0
  102. package/dist/utils/tool-parser.d.ts.map +1 -0
  103. package/dist/utils/tool-parser.js +571 -0
  104. package/dist/utils/tool-parser.js.map +1 -0
  105. package/package.json +37 -0
  106. package/src/context/index.ts +24 -0
  107. package/src/context/process.ts +520 -0
  108. package/src/context/types.ts +231 -0
  109. package/src/index.ts +23 -0
  110. package/src/membrane.ts +1174 -0
  111. package/src/providers/anthropic.ts +340 -0
  112. package/src/providers/index.ts +31 -0
  113. package/src/providers/openai-compatible.ts +570 -0
  114. package/src/providers/openai.ts +625 -0
  115. package/src/providers/openrouter.ts +662 -0
  116. package/src/transforms/chat.ts +212 -0
  117. package/src/transforms/index.ts +22 -0
  118. package/src/transforms/prefill.ts +585 -0
  119. package/src/types/config.ts +172 -0
  120. package/src/types/content.ts +181 -0
  121. package/src/types/errors.ts +277 -0
  122. package/src/types/index.ts +154 -0
  123. package/src/types/message.ts +89 -0
  124. package/src/types/provider.ts +249 -0
  125. package/src/types/request.ts +131 -0
  126. package/src/types/response.ts +223 -0
  127. package/src/types/streaming.ts +231 -0
  128. package/src/types/tools.ts +92 -0
  129. package/src/utils/index.ts +15 -0
  130. package/src/utils/stream-parser.ts +440 -0
  131. package/src/utils/tool-parser.ts +715 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Type exports for membrane
3
+ */
4
+
5
+ // Content blocks
6
+ export type {
7
+ CacheControl,
8
+ MediaSource,
9
+ Base64Source,
10
+ UrlSource,
11
+ ContentBlock,
12
+ TextContent,
13
+ ImageContent,
14
+ DocumentContent,
15
+ AudioContent,
16
+ VideoContent,
17
+ GeneratedImageContent,
18
+ ToolUseContent,
19
+ ToolResultContent,
20
+ ThinkingContent,
21
+ RedactedThinkingContent,
22
+ } from './content.js';
23
+
24
+ export {
25
+ isTextContent,
26
+ isImageContent,
27
+ isDocumentContent,
28
+ isAudioContent,
29
+ isVideoContent,
30
+ isGeneratedImageContent,
31
+ isToolUseContent,
32
+ isToolResultContent,
33
+ isThinkingContent,
34
+ isRedactedThinkingContent,
35
+ isMediaContent,
36
+ } from './content.js';
37
+
38
+ // Messages
39
+ export type {
40
+ MessageMetadata,
41
+ NormalizedMessage,
42
+ } from './message.js';
43
+
44
+ export {
45
+ textMessage,
46
+ extractText,
47
+ hasMedia,
48
+ hasToolUse,
49
+ } from './message.js';
50
+
51
+ // Tools
52
+ export type {
53
+ ToolParameter,
54
+ ToolDefinition,
55
+ ToolCall,
56
+ ToolResult,
57
+ ToolResultContentBlock,
58
+ ToolContext,
59
+ ParsedToolCalls,
60
+ } from './tools.js';
61
+
62
+ // Request
63
+ export type {
64
+ GenerationConfig,
65
+ StopSequenceStrategy,
66
+ StopSequenceConfig,
67
+ RequestOptions,
68
+ NormalizedRequest,
69
+ ToolMode,
70
+ } from './request.js';
71
+
72
+ // Response
73
+ export type {
74
+ StopReason,
75
+ BasicUsage,
76
+ DetailedUsage,
77
+ CostBreakdown,
78
+ StopInfo,
79
+ ModelInfo,
80
+ TimingInfo,
81
+ CacheInfo,
82
+ ResponseDetails,
83
+ RawAccess,
84
+ NormalizedResponse,
85
+ AbortedResponse,
86
+ } from './response.js';
87
+
88
+ export { isAbortedResponse } from './response.js';
89
+
90
+ // Provider
91
+ export type {
92
+ ProviderQuirks,
93
+ MediaCapabilities,
94
+ ProviderCapabilities,
95
+ ModelPricing,
96
+ ModelDefinition,
97
+ ModelRegistry,
98
+ ModelFilter,
99
+ ProviderAdapter,
100
+ ProviderRequest,
101
+ ProviderRequestOptions,
102
+ ProviderResponse,
103
+ StreamCallbacks,
104
+ } from './provider.js';
105
+
106
+ // Streaming
107
+ export type {
108
+ StreamState,
109
+ StreamOptions,
110
+ CompleteOptions,
111
+ OnChunkCallback,
112
+ OnContentBlockCallback,
113
+ OnToolCallsCallback,
114
+ OnPreToolContentCallback,
115
+ OnUsageCallback,
116
+ OnBlockCallback,
117
+ BlockEvent,
118
+ BlockDelta,
119
+ } from './streaming.js';
120
+
121
+ // Errors
122
+ export type {
123
+ MembraneErrorType,
124
+ ErrorInfo,
125
+ } from './errors.js';
126
+
127
+ export {
128
+ MembraneError,
129
+ rateLimitError,
130
+ contextLengthError,
131
+ invalidRequestError,
132
+ authError,
133
+ serverError,
134
+ networkError,
135
+ timeoutError,
136
+ abortError,
137
+ safetyError,
138
+ unsupportedError,
139
+ classifyError,
140
+ } from './errors.js';
141
+
142
+ // Config
143
+ export type {
144
+ RetryConfig,
145
+ MediaConfig,
146
+ MembraneHooks,
147
+ MembraneLogger,
148
+ MembraneConfig,
149
+ } from './config.js';
150
+
151
+ export {
152
+ DEFAULT_RETRY_CONFIG,
153
+ DEFAULT_MEDIA_CONFIG,
154
+ } from './config.js';
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Normalized message types
3
+ */
4
+
5
+ import type { ContentBlock, CacheControl } from './content.js';
6
+
7
+ // Re-export CacheControl for consumers
8
+ export type { CacheControl } from './content.js';
9
+
10
+ // ============================================================================
11
+ // Message Metadata
12
+ // ============================================================================
13
+
14
+ export interface MessageMetadata {
15
+ /** Original timestamp */
16
+ timestamp?: Date;
17
+
18
+ /** Source ID from originating system (Discord message ID, UI message ID, etc.) */
19
+ sourceId?: string;
20
+
21
+ /** Cache control for this message */
22
+ cacheControl?: CacheControl;
23
+
24
+ /** Stream routing (for VEIL/connectome compatibility) */
25
+ streamId?: string;
26
+
27
+ /** Additional metadata (pass-through) */
28
+ [key: string]: unknown;
29
+ }
30
+
31
+ // ============================================================================
32
+ // Normalized Message
33
+ // ============================================================================
34
+
35
+ /**
36
+ * A message from a single participant.
37
+ * This is the core abstraction - no artificial "user" vs "assistant" roles.
38
+ */
39
+ export interface NormalizedMessage {
40
+ /** Participant name: "Alice", "Bob", "Claude", etc. */
41
+ participant: string;
42
+
43
+ /** Content blocks */
44
+ content: ContentBlock[];
45
+
46
+ /** Message metadata */
47
+ metadata?: MessageMetadata;
48
+ }
49
+
50
+ // ============================================================================
51
+ // Helpers
52
+ // ============================================================================
53
+
54
+ /**
55
+ * Create a simple text message
56
+ */
57
+ export function textMessage(participant: string, text: string, metadata?: MessageMetadata): NormalizedMessage {
58
+ return {
59
+ participant,
60
+ content: [{ type: 'text', text }],
61
+ metadata,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Extract all text content from a message
67
+ */
68
+ export function extractText(message: NormalizedMessage): string {
69
+ return message.content
70
+ .filter((block): block is { type: 'text'; text: string } => block.type === 'text')
71
+ .map((block) => block.text)
72
+ .join('\n');
73
+ }
74
+
75
+ /**
76
+ * Check if message has any media content
77
+ */
78
+ export function hasMedia(message: NormalizedMessage): boolean {
79
+ return message.content.some((block) =>
80
+ ['image', 'document', 'audio', 'video'].includes(block.type)
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Check if message has tool use
86
+ */
87
+ export function hasToolUse(message: NormalizedMessage): boolean {
88
+ return message.content.some((block) => block.type === 'tool_use');
89
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Provider capability and registry types
3
+ */
4
+
5
+ // ============================================================================
6
+ // Provider Quirks
7
+ // ============================================================================
8
+
9
+ export interface ProviderQuirks {
10
+ /** Anthropic: must trim trailing whitespace from assistant messages */
11
+ trimAssistantTrailingWhitespace?: boolean;
12
+
13
+ /** Most providers: require alternating user/assistant roles */
14
+ requiresAlternatingRoles?: boolean;
15
+
16
+ /** Prefill mode: images must be in user turns */
17
+ imagesMustBeInUserTurn?: boolean;
18
+
19
+ /** Whether stop sequence is consumed (not in output) or present */
20
+ stopSequenceConsumed?: boolean;
21
+
22
+ /** Parameters to strip from request (provider rejects them) */
23
+ rejectParams?: string[];
24
+
25
+ /** Provider-specific notes */
26
+ notes?: string;
27
+ }
28
+
29
+ // ============================================================================
30
+ // Media Capabilities
31
+ // ============================================================================
32
+
33
+ export interface MediaCapabilities {
34
+ // Input support
35
+ imageInput: boolean;
36
+ pdfInput: boolean;
37
+ audioInput: boolean;
38
+ videoInput: boolean;
39
+
40
+ // Output support
41
+ imageGeneration: boolean;
42
+
43
+ // Limits
44
+ maxImageSizeBytes?: number;
45
+ maxImageDimensions?: { width: number; height: number };
46
+ maxPdfPages?: number;
47
+ maxAudioDurationSec?: number;
48
+ maxVideoDurationSec?: number;
49
+
50
+ // Supported formats
51
+ imageFormats?: string[]; // ['image/jpeg', 'image/png', ...]
52
+ audioFormats?: string[]; // ['audio/mpeg', 'audio/wav', ...]
53
+ videoFormats?: string[]; // ['video/mp4', 'video/webm', ...]
54
+ }
55
+
56
+ // ============================================================================
57
+ // Provider Capabilities
58
+ // ============================================================================
59
+
60
+ export interface ProviderCapabilities {
61
+ // Mode support
62
+ supportsPrefill: boolean;
63
+ supportsChat: boolean;
64
+ supportsCaching: boolean;
65
+ supportsThinking: boolean;
66
+ supportsStreaming: boolean;
67
+
68
+ // Media
69
+ media: MediaCapabilities;
70
+
71
+ // Limits
72
+ maxContextTokens: number;
73
+ maxOutputTokens: number;
74
+ maxStopSequences: number;
75
+ maxCacheBreakpoints?: number;
76
+
77
+ // Quirks
78
+ quirks: ProviderQuirks;
79
+ }
80
+
81
+ // ============================================================================
82
+ // Model Pricing
83
+ // ============================================================================
84
+
85
+ export interface ModelPricing {
86
+ /** Cost per million input tokens */
87
+ inputPerMillion: number;
88
+
89
+ /** Cost per million output tokens */
90
+ outputPerMillion: number;
91
+
92
+ /** Cost per million cache write tokens */
93
+ cacheWritePerMillion?: number;
94
+
95
+ /** Cost per million cache read tokens */
96
+ cacheReadPerMillion?: number;
97
+
98
+ /** Currency code */
99
+ currency: string;
100
+ }
101
+
102
+ // ============================================================================
103
+ // Model Information
104
+ // ============================================================================
105
+
106
+ export interface ModelDefinition {
107
+ /** Unique model identifier */
108
+ id: string;
109
+
110
+ /** Provider (anthropic, openrouter, google, etc.) */
111
+ provider: string;
112
+
113
+ /** Display name for UI */
114
+ displayName: string;
115
+
116
+ /** Capabilities */
117
+ capabilities: ProviderCapabilities;
118
+
119
+ /** Pricing (optional) */
120
+ pricing?: ModelPricing;
121
+
122
+ /** Aliases that resolve to this model */
123
+ aliases?: string[];
124
+
125
+ /** Whether model is deprecated */
126
+ deprecated?: boolean;
127
+
128
+ /** Successor model if deprecated */
129
+ successorId?: string;
130
+ }
131
+
132
+ // ============================================================================
133
+ // Model Registry Interface
134
+ // ============================================================================
135
+
136
+ export interface ModelRegistry {
137
+ /** Get capabilities for a model */
138
+ getCapabilities(modelId: string): ProviderCapabilities | undefined;
139
+
140
+ /** Get pricing for a model */
141
+ getPricing(modelId: string): ModelPricing | undefined;
142
+
143
+ /** Get quirks for a model */
144
+ getQuirks(modelId: string): ProviderQuirks | undefined;
145
+
146
+ /** Get full model definition */
147
+ getModel(modelId: string): ModelDefinition | undefined;
148
+
149
+ /** Resolve alias to canonical model ID */
150
+ resolveModel(idOrAlias: string): string;
151
+
152
+ /** List all models (optionally filtered) */
153
+ listModels(filter?: ModelFilter): ModelDefinition[];
154
+ }
155
+
156
+ export interface ModelFilter {
157
+ provider?: string;
158
+ supportsPrefill?: boolean;
159
+ supportsThinking?: boolean;
160
+ supportsImageGeneration?: boolean;
161
+ includeDeprecated?: boolean;
162
+ }
163
+
164
+ // ============================================================================
165
+ // Provider Adapter Interface
166
+ // ============================================================================
167
+
168
+ export interface ProviderAdapter {
169
+ /** Provider name */
170
+ readonly name: string;
171
+
172
+ /** Check if this adapter handles a model */
173
+ supportsModel(modelId: string): boolean;
174
+
175
+ /** Make a completion request (non-streaming) */
176
+ complete(
177
+ request: ProviderRequest,
178
+ options?: ProviderRequestOptions
179
+ ): Promise<ProviderResponse>;
180
+
181
+ /** Make a streaming request */
182
+ stream(
183
+ request: ProviderRequest,
184
+ callbacks: StreamCallbacks,
185
+ options?: ProviderRequestOptions
186
+ ): Promise<ProviderResponse>;
187
+ }
188
+
189
+ // Internal types used by adapters
190
+ export interface ProviderRequest {
191
+ /** Raw messages in provider format */
192
+ messages: unknown[];
193
+
194
+ /** System prompt - can be string or content blocks with cache_control */
195
+ system?: string | unknown[];
196
+
197
+ /** Model ID */
198
+ model: string;
199
+
200
+ /** Max tokens */
201
+ maxTokens: number;
202
+
203
+ /** Temperature */
204
+ temperature?: number;
205
+
206
+ /** Stop sequences */
207
+ stopSequences?: string[];
208
+
209
+ /** Tools in provider format */
210
+ tools?: unknown[];
211
+
212
+ /** Additional provider-specific params */
213
+ extra?: Record<string, unknown>;
214
+ }
215
+
216
+ export interface ProviderRequestOptions {
217
+ signal?: AbortSignal;
218
+ timeoutMs?: number;
219
+ }
220
+
221
+ export interface ProviderResponse {
222
+ /** Raw response content */
223
+ content: unknown;
224
+
225
+ /** Stop reason in provider format */
226
+ stopReason: string;
227
+
228
+ /** Which stop sequence triggered */
229
+ stopSequence?: string;
230
+
231
+ /** Usage in provider format */
232
+ usage: {
233
+ inputTokens: number;
234
+ outputTokens: number;
235
+ cacheCreationTokens?: number;
236
+ cacheReadTokens?: number;
237
+ };
238
+
239
+ /** Model that actually ran */
240
+ model: string;
241
+
242
+ /** Raw response for debugging */
243
+ raw: unknown;
244
+ }
245
+
246
+ export interface StreamCallbacks {
247
+ onChunk: (chunk: string) => void;
248
+ onContentBlock?: (index: number, block: unknown) => void;
249
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Request types for membrane
3
+ */
4
+
5
+ import type { NormalizedMessage } from './message.js';
6
+ import type { ToolDefinition } from './tools.js';
7
+
8
+ // ============================================================================
9
+ // Generation Config
10
+ // ============================================================================
11
+
12
+ export interface GenerationConfig {
13
+ /** Model identifier */
14
+ model: string;
15
+
16
+ /** Maximum tokens to generate */
17
+ maxTokens: number;
18
+
19
+ /** Temperature (0-2) */
20
+ temperature?: number;
21
+
22
+ /** Top P nucleus sampling */
23
+ topP?: number;
24
+
25
+ /** Top K sampling (provider-specific) */
26
+ topK?: number;
27
+
28
+ /** Presence penalty (provider-specific) */
29
+ presencePenalty?: number;
30
+
31
+ /** Frequency penalty (provider-specific) */
32
+ frequencyPenalty?: number;
33
+
34
+ /** Enable thinking/reasoning mode */
35
+ thinking?: {
36
+ enabled: boolean;
37
+ budgetTokens?: number;
38
+ };
39
+
40
+ /** Image generation config (Gemini) */
41
+ imageGeneration?: {
42
+ enabled: boolean;
43
+ modalities: ('TEXT' | 'IMAGE')[];
44
+ aspectRatio?: '1:1' | '16:9' | '9:16' | '4:3' | '3:4';
45
+ imageSize?: 'SMALL' | 'MEDIUM' | 'LARGE';
46
+ };
47
+ }
48
+
49
+ // ============================================================================
50
+ // Stop Sequence Config
51
+ // ============================================================================
52
+
53
+ export type StopSequenceStrategy =
54
+ | 'none' // Trust API stop sequences only
55
+ | 'post-facto' // Disable API sequences, check in code
56
+ | 'resume-on-unclosed'; // Resume if stopped inside XML block
57
+
58
+ export interface StopSequenceConfig {
59
+ /** Stop sequences to use */
60
+ sequences: string[];
61
+
62
+ /** Strategy for handling false positives */
63
+ strategy?: StopSequenceStrategy;
64
+
65
+ /** Max resumptions for 'resume-on-unclosed' strategy */
66
+ maxResumptions?: number;
67
+
68
+ /** Additional sequences only checked post-facto (not sent to API) */
69
+ postFactoOnly?: string[];
70
+ }
71
+
72
+ // ============================================================================
73
+ // Request Options
74
+ // ============================================================================
75
+
76
+ export interface RequestOptions {
77
+ /** Abort signal for cancellation */
78
+ signal?: AbortSignal;
79
+
80
+ /** Request timeout in milliseconds */
81
+ timeoutMs?: number;
82
+
83
+ /** Request ID for correlation */
84
+ requestId?: string;
85
+
86
+ /** Tags for filtering/attribution */
87
+ tags?: Record<string, string>;
88
+ }
89
+
90
+ // ============================================================================
91
+ // Tool Mode
92
+ // ============================================================================
93
+
94
+ export type ToolMode =
95
+ | 'xml' // XML injection in prefill mode (chatperx style)
96
+ | 'native' // Native API tool support (Anthropic tool_use, OpenAI tool_calls)
97
+ | 'auto'; // Automatically choose based on provider/mode
98
+
99
+ // ============================================================================
100
+ // Normalized Request
101
+ // ============================================================================
102
+
103
+ export interface NormalizedRequest {
104
+ /** Conversation messages */
105
+ messages: NormalizedMessage[];
106
+
107
+ /** System prompt */
108
+ system?: string;
109
+
110
+ /** Generation configuration */
111
+ config: GenerationConfig;
112
+
113
+ /** Tool definitions */
114
+ tools?: ToolDefinition[];
115
+
116
+ /** Tool execution mode (default: 'auto') */
117
+ toolMode?: ToolMode;
118
+
119
+ /** Stop sequence configuration */
120
+ stopSequences?: StopSequenceConfig | string[];
121
+
122
+ /**
123
+ * Maximum participants to include in auto-generated stop sequences (prefill mode).
124
+ * Set to 0 to disable participant-based stop sequences (allows frags/quotes).
125
+ * If not specified, uses membrane config default (10).
126
+ */
127
+ maxParticipantsForStop?: number;
128
+
129
+ /** Provider-specific parameters (pass-through) */
130
+ providerParams?: Record<string, unknown>;
131
+ }