@jsonstudio/llms 0.4.4 → 0.4.6

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 (160) hide show
  1. package/dist/conversion/codec-registry.js +11 -1
  2. package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
  3. package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
  4. package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
  5. package/dist/conversion/codecs/responses-openai-codec.js +9 -2
  6. package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
  7. package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
  8. package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
  9. package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
  10. package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
  11. package/dist/conversion/hub/node-support.js +3 -1
  12. package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
  13. package/dist/conversion/hub/response/provider-response.js +1 -1
  14. package/dist/conversion/hub/response/response-mappers.js +1 -1
  15. package/dist/conversion/hub/response/response-runtime.js +109 -10
  16. package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
  17. package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
  18. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
  19. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
  20. package/dist/conversion/hub/standardized-bridge.js +3 -0
  21. package/dist/conversion/hub/tool-governance/rules.js +2 -2
  22. package/dist/conversion/index.d.ts +5 -0
  23. package/dist/conversion/index.js +5 -0
  24. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
  25. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
  26. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
  27. package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
  28. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
  29. package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
  30. package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
  31. package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
  32. package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
  33. package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
  34. package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
  35. package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
  36. package/dist/conversion/pipeline/index.d.ts +35 -0
  37. package/dist/conversion/pipeline/index.js +103 -0
  38. package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
  39. package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
  40. package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
  41. package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
  42. package/dist/conversion/pipeline/schema/index.d.ts +1 -0
  43. package/dist/conversion/pipeline/schema/index.js +1 -0
  44. package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
  45. package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
  46. package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
  47. package/dist/conversion/shared/anthropic-message-utils.js +587 -0
  48. package/dist/conversion/shared/bridge-actions.d.ts +39 -0
  49. package/dist/conversion/shared/bridge-actions.js +709 -0
  50. package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
  51. package/dist/conversion/shared/bridge-conversation-store.js +279 -0
  52. package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
  53. package/dist/conversion/shared/bridge-id-utils.js +42 -0
  54. package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
  55. package/dist/conversion/shared/bridge-instructions.js +113 -0
  56. package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
  57. package/dist/conversion/shared/bridge-message-types.js +1 -0
  58. package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
  59. package/dist/conversion/shared/bridge-message-utils.js +473 -0
  60. package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
  61. package/dist/conversion/shared/bridge-metadata.js +1 -0
  62. package/dist/conversion/shared/bridge-policies.d.ts +18 -0
  63. package/dist/conversion/shared/bridge-policies.js +276 -0
  64. package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
  65. package/dist/conversion/shared/bridge-request-adapter.js +430 -0
  66. package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
  67. package/dist/conversion/shared/chat-output-normalizer.js +56 -0
  68. package/dist/conversion/shared/chat-request-filters.js +24 -1
  69. package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
  70. package/dist/conversion/shared/gemini-tool-utils.js +130 -0
  71. package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
  72. package/dist/conversion/shared/metadata-passthrough.js +57 -0
  73. package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
  74. package/dist/conversion/shared/output-content-normalizer.js +119 -0
  75. package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
  76. package/dist/conversion/shared/reasoning-normalizer.js +368 -0
  77. package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
  78. package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
  79. package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
  80. package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
  81. package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
  82. package/dist/conversion/shared/reasoning-utils.js +42 -0
  83. package/dist/conversion/shared/responses-conversation-store.js +5 -11
  84. package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
  85. package/dist/conversion/shared/responses-message-utils.js +206 -0
  86. package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
  87. package/dist/conversion/shared/responses-output-builder.js +179 -0
  88. package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
  89. package/dist/conversion/shared/responses-output-utils.js +108 -0
  90. package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
  91. package/dist/conversion/shared/responses-request-adapter.js +9 -40
  92. package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
  93. package/dist/conversion/shared/responses-response-utils.js +209 -0
  94. package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
  95. package/dist/conversion/shared/responses-tool-utils.js +90 -0
  96. package/dist/conversion/shared/responses-types.d.ts +33 -0
  97. package/dist/conversion/shared/responses-types.js +1 -0
  98. package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
  99. package/dist/conversion/shared/tool-call-utils.js +56 -0
  100. package/dist/conversion/shared/tool-governor.js +5 -0
  101. package/dist/conversion/shared/tool-mapping.d.ts +19 -0
  102. package/dist/conversion/shared/tool-mapping.js +124 -0
  103. package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
  104. package/dist/conversion/shared/tool-normalizers.js +84 -0
  105. package/dist/router/virtual-router/bootstrap.js +18 -3
  106. package/dist/router/virtual-router/provider-registry.js +4 -2
  107. package/dist/router/virtual-router/types.d.ts +212 -0
  108. package/dist/sse/index.d.ts +38 -2
  109. package/dist/sse/index.js +27 -0
  110. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
  111. package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
  112. package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
  113. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
  114. package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
  115. package/dist/sse/json-to-sse/index.d.ts +7 -0
  116. package/dist/sse/json-to-sse/index.js +2 -0
  117. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
  118. package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
  119. package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
  120. package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
  121. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
  122. package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
  123. package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
  124. package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
  125. package/dist/sse/registry/sse-codec-registry.js +30 -1
  126. package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
  127. package/dist/sse/shared/reasoning-dispatcher.js +25 -0
  128. package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
  129. package/dist/sse/shared/responses-output-normalizer.js +45 -0
  130. package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
  131. package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
  132. package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
  133. package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
  134. package/dist/sse/shared/serializers/index.d.ts +41 -0
  135. package/dist/sse/shared/serializers/index.js +2 -0
  136. package/dist/sse/shared/writer.d.ts +127 -0
  137. package/dist/sse/shared/writer.js +37 -1
  138. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
  139. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
  140. package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
  141. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
  142. package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
  143. package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
  144. package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
  145. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
  146. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
  147. package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
  148. package/dist/sse/sse-to-json/index.d.ts +7 -0
  149. package/dist/sse/sse-to-json/index.js +2 -0
  150. package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
  151. package/dist/sse/types/anthropic-types.d.ts +170 -0
  152. package/dist/sse/types/anthropic-types.js +8 -5
  153. package/dist/sse/types/chat-types.d.ts +10 -0
  154. package/dist/sse/types/chat-types.js +2 -1
  155. package/dist/sse/types/core-interfaces.d.ts +1 -1
  156. package/dist/sse/types/gemini-types.d.ts +116 -0
  157. package/dist/sse/types/gemini-types.js +5 -0
  158. package/dist/sse/types/index.d.ts +5 -2
  159. package/dist/sse/types/index.js +2 -0
  160. package/package.json +1 -1
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Virtual Router 类型定义
3
+ */
4
+ import type { StandardizedRequest } from '../../conversion/hub/types/standardized.js';
5
+ export declare const DEFAULT_ROUTE = "default";
6
+ export declare const ROUTE_PRIORITY: string[];
7
+ export type RoutingPools = Record<string, string[]>;
8
+ export interface ProviderAuthConfig {
9
+ type: 'apiKey' | 'oauth';
10
+ secretRef?: string;
11
+ value?: string;
12
+ tokenFile?: string;
13
+ tokenUrl?: string;
14
+ deviceCodeUrl?: string;
15
+ clientId?: string;
16
+ clientSecret?: string;
17
+ scopes?: string[];
18
+ authorizationUrl?: string;
19
+ userInfoUrl?: string;
20
+ refreshUrl?: string;
21
+ oauthProviderId?: string;
22
+ rawType?: string;
23
+ }
24
+ export interface ProviderProfile {
25
+ providerKey: string;
26
+ providerType: string;
27
+ endpoint: string;
28
+ auth: ProviderAuthConfig;
29
+ outboundProfile: string;
30
+ compatibilityProfile?: string;
31
+ runtimeKey?: string;
32
+ modelId?: string;
33
+ processMode?: 'chat' | 'passthrough';
34
+ responsesConfig?: ResponsesProviderConfig;
35
+ }
36
+ export interface ProviderRuntimeProfile {
37
+ runtimeKey: string;
38
+ providerId: string;
39
+ keyAlias: string;
40
+ providerType: string;
41
+ endpoint: string;
42
+ headers?: Record<string, string>;
43
+ auth: ProviderAuthConfig;
44
+ outboundProfile: string;
45
+ compatibilityProfile?: string;
46
+ modelId?: string;
47
+ processMode?: 'chat' | 'passthrough';
48
+ responsesConfig?: ResponsesProviderConfig;
49
+ }
50
+ export interface VirtualRouterClassifierConfig {
51
+ longContextThresholdTokens?: number;
52
+ thinkingKeywords?: string[];
53
+ codingKeywords?: string[];
54
+ backgroundKeywords?: string[];
55
+ visionKeywords?: string[];
56
+ }
57
+ export interface LoadBalancingPolicy {
58
+ strategy: 'round-robin' | 'weighted' | 'sticky';
59
+ weights?: Record<string, number>;
60
+ }
61
+ export interface ProviderHealthConfig {
62
+ failureThreshold: number;
63
+ cooldownMs: number;
64
+ fatalCooldownMs?: number;
65
+ }
66
+ export interface VirtualRouterConfig {
67
+ routing: RoutingPools;
68
+ providers: Record<string, ProviderProfile>;
69
+ classifier: VirtualRouterClassifierConfig;
70
+ loadBalancing?: LoadBalancingPolicy;
71
+ health?: ProviderHealthConfig;
72
+ }
73
+ export type VirtualRouterProviderDefinition = Record<string, unknown>;
74
+ export interface VirtualRouterBootstrapInput extends Record<string, unknown> {
75
+ virtualrouter?: Record<string, unknown>;
76
+ providers?: Record<string, VirtualRouterProviderDefinition>;
77
+ routing?: Record<string, unknown>;
78
+ classifier?: VirtualRouterClassifierConfig;
79
+ loadBalancing?: LoadBalancingPolicy;
80
+ health?: ProviderHealthConfig;
81
+ }
82
+ export type ProviderRuntimeMap = Record<string, ProviderRuntimeProfile>;
83
+ export interface VirtualRouterBootstrapResult {
84
+ config: VirtualRouterConfig;
85
+ runtime: ProviderRuntimeMap;
86
+ targetRuntime: Record<string, ProviderRuntimeProfile>;
87
+ providers: Record<string, ProviderProfile>;
88
+ routing: RoutingPools;
89
+ }
90
+ export interface RouterMetadataInput {
91
+ requestId: string;
92
+ entryEndpoint: string;
93
+ processMode: 'chat' | 'passthrough';
94
+ stream: boolean;
95
+ direction: 'request' | 'response';
96
+ providerProtocol?: string;
97
+ stage?: 'inbound' | 'outbound' | 'response';
98
+ routeHint?: string;
99
+ }
100
+ export interface RoutingFeatures {
101
+ requestId: string;
102
+ model: string;
103
+ totalMessages: number;
104
+ userTextSample: string;
105
+ toolCount: number;
106
+ hasTools: boolean;
107
+ hasToolCallResponses: boolean;
108
+ hasVisionTool: boolean;
109
+ hasImageAttachment: boolean;
110
+ hasWebTool: boolean;
111
+ hasCodingTool: boolean;
112
+ hasThinkingKeyword: boolean;
113
+ estimatedTokens: number;
114
+ metadata: RouterMetadataInput;
115
+ }
116
+ export interface ClassificationResult {
117
+ routeName: string;
118
+ confidence: number;
119
+ reasoning: string;
120
+ fallback: boolean;
121
+ candidates?: string[];
122
+ }
123
+ export interface RoutingDecision {
124
+ routeName: string;
125
+ providerKey: string;
126
+ confidence: number;
127
+ reasoning: string;
128
+ fallback: boolean;
129
+ pool: string[];
130
+ }
131
+ export interface TargetMetadata {
132
+ providerKey: string;
133
+ providerType: string;
134
+ outboundProfile: string;
135
+ compatibilityProfile?: string;
136
+ runtimeKey?: string;
137
+ modelId: string;
138
+ processMode?: 'chat' | 'passthrough';
139
+ responsesConfig?: ResponsesProviderConfig;
140
+ }
141
+ export interface ResponsesProviderConfig {
142
+ toolCallIdStyle?: 'fc' | 'preserve';
143
+ }
144
+ export declare enum VirtualRouterErrorCode {
145
+ NO_STANDARDIZED_REQUEST = "NO_STANDARDIZED_REQUEST",
146
+ ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND",
147
+ PROVIDER_NOT_AVAILABLE = "PROVIDER_NOT_AVAILABLE",
148
+ CONFIG_ERROR = "CONFIG_ERROR"
149
+ }
150
+ export declare class VirtualRouterError extends Error {
151
+ readonly code: VirtualRouterErrorCode;
152
+ readonly details?: Record<string, unknown>;
153
+ constructor(message: string, code: VirtualRouterErrorCode, details?: Record<string, unknown>);
154
+ }
155
+ export interface RoutingDiagnostics {
156
+ routeName: string;
157
+ providerKey: string;
158
+ reasoning: string;
159
+ fallback: boolean;
160
+ pool: string[];
161
+ confidence: number;
162
+ }
163
+ export interface RoutingStatusSnapshot {
164
+ routes: Record<string, {
165
+ providers: string[];
166
+ hits: number;
167
+ lastUsedProvider?: string;
168
+ }>;
169
+ health: ProviderHealthState[];
170
+ }
171
+ export interface ProviderHealthState {
172
+ providerKey: string;
173
+ state: 'healthy' | 'tripped';
174
+ failureCount: number;
175
+ cooldownExpiresAt?: number;
176
+ reason?: string;
177
+ }
178
+ export interface ProviderFailureEvent {
179
+ providerKey: string;
180
+ routeName?: string;
181
+ reason?: string;
182
+ fatal?: boolean;
183
+ statusCode?: number;
184
+ errorCode?: string;
185
+ retryable?: boolean;
186
+ affectsHealth?: boolean;
187
+ cooldownOverrideMs?: number;
188
+ metadata?: Record<string, unknown>;
189
+ }
190
+ export interface ProviderErrorRuntimeMetadata {
191
+ requestId: string;
192
+ routeName?: string;
193
+ providerKey?: string;
194
+ providerId?: string;
195
+ providerType?: string;
196
+ providerProtocol?: string;
197
+ pipelineId?: string;
198
+ target?: TargetMetadata | Record<string, unknown>;
199
+ }
200
+ export interface ProviderErrorEvent {
201
+ code: string;
202
+ message: string;
203
+ stage: string;
204
+ status?: number;
205
+ recoverable?: boolean;
206
+ runtime: ProviderErrorRuntimeMetadata;
207
+ timestamp: number;
208
+ details?: Record<string, unknown>;
209
+ }
210
+ export interface FeatureBuilder {
211
+ build(request: StandardizedRequest, metadata: RouterMetadataInput): RoutingFeatures;
212
+ }
@@ -4,14 +4,19 @@
4
4
  */
5
5
  import type { ChatCompletionResponse, ChatJsonToSseOptions, ResponsesJsonToSseOptions, ResponsesResponse, SseToChatJsonOptions, SseToResponsesJsonOptions } from './types/index.js';
6
6
  import type { AnthropicMessageResponse } from './types/anthropic-types.js';
7
+ import type { GeminiResponse, GeminiJsonToSseOptions, SseToGeminiJsonOptions } from './types/gemini-types.js';
7
8
  import { ChatSseToJsonConverter as ChatSseToJsonConverterCtor } from './sse-to-json/index.js';
8
9
  import { ResponsesSseToJsonConverter as ResponsesSseToJsonConverterCtor } from './sse-to-json/index.js';
9
10
  import { AnthropicJsonToSseConverter as AnthropicJsonToSseConverterCtor } from './json-to-sse/anthropic-json-to-sse-converter.js';
10
11
  import { AnthropicSseToJsonConverter as AnthropicSseToJsonConverterCtor } from './sse-to-json/anthropic-sse-to-json-converter.js';
12
+ import { GeminiJsonToSseConverter as GeminiJsonToSseConverterCtor } from './json-to-sse/gemini-json-to-sse-converter.js';
13
+ import { GeminiSseToJsonConverter as GeminiSseToJsonConverterCtor } from './sse-to-json/gemini-sse-to-json-converter.js';
11
14
  export { ChatJsonToSseConverter } from './json-to-sse/index.js';
12
15
  export { ChatSseToJsonConverter } from './sse-to-json/index.js';
13
16
  export { ResponsesJsonToSseConverter } from './json-to-sse/index.js';
14
17
  export { ResponsesSseToJsonConverter } from './sse-to-json/index.js';
18
+ export { GeminiJsonToSseConverter } from './json-to-sse/index.js';
19
+ export { GeminiSseToJsonConverter } from './sse-to-json/index.js';
15
20
  export * from './shared/utils.js';
16
21
  export * from './types/index.js';
17
22
  /**
@@ -45,8 +50,18 @@ export declare function createAnthropicConverters(): {
45
50
  roundTrip(input: AnthropicMessageResponse, options: {
46
51
  requestId: string;
47
52
  model: string;
53
+ reasoningMode?: import("./types/chat-types.js").ChatReasoningMode;
54
+ reasoningTextPrefix?: string;
48
55
  }): Promise<AnthropicMessageResponse>;
49
56
  };
57
+ /**
58
+ * 工厂函数:创建Gemini协议转换器
59
+ */
60
+ export declare function createGeminiConverters(): {
61
+ jsonToSse: GeminiJsonToSseConverterCtor;
62
+ sseToJson: GeminiSseToJsonConverterCtor;
63
+ roundTrip(input: GeminiResponse, options: GeminiJsonToSseOptions & SseToGeminiJsonOptions): Promise<GeminiResponse>;
64
+ };
50
65
  /**
51
66
  * 工厂函数:创建完整的双向转换器(支持Chat和Responses协议)
52
67
  */
@@ -73,12 +88,19 @@ export declare function createBidirectionalConverters(): {
73
88
  roundTrip(input: AnthropicMessageResponse, options: {
74
89
  requestId: string;
75
90
  model: string;
91
+ reasoningMode?: import("./types/chat-types.js").ChatReasoningMode;
92
+ reasoningTextPrefix?: string;
76
93
  }): Promise<AnthropicMessageResponse>;
77
94
  };
95
+ gemini: {
96
+ jsonToSse: GeminiJsonToSseConverterCtor;
97
+ sseToJson: GeminiSseToJsonConverterCtor;
98
+ roundTrip(input: GeminiResponse, options: GeminiJsonToSseOptions & SseToGeminiJsonOptions): Promise<GeminiResponse>;
99
+ };
78
100
  /**
79
101
  * 自动检测协议类型并执行转换
80
102
  */
81
- autoConvert(input: any, options?: any): Promise<ChatCompletionResponse | ResponsesResponse | AnthropicMessageResponse>;
103
+ autoConvert(input: any, options?: any): Promise<ChatCompletionResponse | ResponsesResponse | AnthropicMessageResponse | GeminiResponse>;
82
104
  };
83
105
  /**
84
106
  * 默认转换器实例
@@ -122,12 +144,19 @@ export declare const bidirectionalConverters: {
122
144
  roundTrip(input: AnthropicMessageResponse, options: {
123
145
  requestId: string;
124
146
  model: string;
147
+ reasoningMode?: import("./types/chat-types.js").ChatReasoningMode;
148
+ reasoningTextPrefix?: string;
125
149
  }): Promise<AnthropicMessageResponse>;
126
150
  };
151
+ gemini: {
152
+ jsonToSse: GeminiJsonToSseConverterCtor;
153
+ sseToJson: GeminiSseToJsonConverterCtor;
154
+ roundTrip(input: GeminiResponse, options: GeminiJsonToSseOptions & SseToGeminiJsonOptions): Promise<GeminiResponse>;
155
+ };
127
156
  /**
128
157
  * 自动检测协议类型并执行转换
129
158
  */
130
- autoConvert(input: any, options?: any): Promise<ChatCompletionResponse | ResponsesResponse | AnthropicMessageResponse>;
159
+ autoConvert(input: any, options?: any): Promise<ChatCompletionResponse | ResponsesResponse | AnthropicMessageResponse | GeminiResponse>;
131
160
  };
132
161
  export declare const anthropicConverters: {
133
162
  jsonToSse: AnthropicJsonToSseConverterCtor;
@@ -135,6 +164,13 @@ export declare const anthropicConverters: {
135
164
  roundTrip(input: AnthropicMessageResponse, options: {
136
165
  requestId: string;
137
166
  model: string;
167
+ reasoningMode?: import("./types/chat-types.js").ChatReasoningMode;
168
+ reasoningTextPrefix?: string;
138
169
  }): Promise<AnthropicMessageResponse>;
139
170
  };
171
+ export declare const geminiConverters: {
172
+ jsonToSse: GeminiJsonToSseConverterCtor;
173
+ sseToJson: GeminiSseToJsonConverterCtor;
174
+ roundTrip(input: GeminiResponse, options: GeminiJsonToSseOptions & SseToGeminiJsonOptions): Promise<GeminiResponse>;
175
+ };
140
176
  export { defaultSseCodecRegistry, SseCodecRegistry, type SseCodec, type SseProtocol, type JsonToSseContext, type SseToJsonContext, type NormalizeSseContext } from './registry/sse-codec-registry.js';
package/dist/sse/index.js CHANGED
@@ -8,12 +8,17 @@ import { ResponsesJsonToSseConverter as ResponsesJsonToSseConverterCtor } from '
8
8
  import { ResponsesSseToJsonConverter as ResponsesSseToJsonConverterCtor } from './sse-to-json/index.js';
9
9
  import { AnthropicJsonToSseConverter as AnthropicJsonToSseConverterCtor } from './json-to-sse/anthropic-json-to-sse-converter.js';
10
10
  import { AnthropicSseToJsonConverter as AnthropicSseToJsonConverterCtor } from './sse-to-json/anthropic-sse-to-json-converter.js';
11
+ import { GeminiJsonToSseConverter as GeminiJsonToSseConverterCtor } from './json-to-sse/gemini-json-to-sse-converter.js';
12
+ import { GeminiSseToJsonConverter as GeminiSseToJsonConverterCtor } from './sse-to-json/gemini-sse-to-json-converter.js';
11
13
  // Chat协议转换器
12
14
  export { ChatJsonToSseConverter } from './json-to-sse/index.js';
13
15
  export { ChatSseToJsonConverter } from './sse-to-json/index.js';
14
16
  // Responses协议转换器
15
17
  export { ResponsesJsonToSseConverter } from './json-to-sse/index.js';
16
18
  export { ResponsesSseToJsonConverter } from './sse-to-json/index.js';
19
+ // Gemini协议转换器
20
+ export { GeminiJsonToSseConverter } from './json-to-sse/index.js';
21
+ export { GeminiSseToJsonConverter } from './sse-to-json/index.js';
17
22
  // 共享工具导出
18
23
  export * from './shared/utils.js';
19
24
  // 序列化器暂时禁用以专注核心SSE转换功能
@@ -74,6 +79,22 @@ export function createAnthropicConverters() {
74
79
  }
75
80
  };
76
81
  }
82
+ /**
83
+ * 工厂函数:创建Gemini协议转换器
84
+ */
85
+ export function createGeminiConverters() {
86
+ const jsonToSse = new GeminiJsonToSseConverterCtor();
87
+ const sseToJson = new GeminiSseToJsonConverterCtor();
88
+ return {
89
+ jsonToSse,
90
+ sseToJson,
91
+ async roundTrip(input, options) {
92
+ const sseStream = await jsonToSse.convertResponseToJsonToSse(input, options);
93
+ const result = await sseToJson.convertSseToJson(sseStream, options);
94
+ return result;
95
+ }
96
+ };
97
+ }
77
98
  /**
78
99
  * 工厂函数:创建完整的双向转换器(支持Chat和Responses协议)
79
100
  */
@@ -81,10 +102,12 @@ export function createBidirectionalConverters() {
81
102
  const chat = createChatConverters();
82
103
  const responses = createResponsesConverters();
83
104
  const anthropic = createAnthropicConverters();
105
+ const gemini = createGeminiConverters();
84
106
  return {
85
107
  chat,
86
108
  responses,
87
109
  anthropic,
110
+ gemini,
88
111
  /**
89
112
  * 自动检测协议类型并执行转换
90
113
  */
@@ -99,6 +122,9 @@ export function createBidirectionalConverters() {
99
122
  else if (input.type === 'message') {
100
123
  return await anthropic.roundTrip(input, options);
101
124
  }
125
+ else if (Array.isArray(input?.candidates)) {
126
+ return await gemini.roundTrip(input, options);
127
+ }
102
128
  else {
103
129
  throw new Error(`无法识别的协议类型: ${input.object}`);
104
130
  }
@@ -112,4 +138,5 @@ export const chatConverters = createChatConverters();
112
138
  export const responsesConverters = createResponsesConverters();
113
139
  export const bidirectionalConverters = createBidirectionalConverters();
114
140
  export const anthropicConverters = createAnthropicConverters();
141
+ export const geminiConverters = createGeminiConverters();
115
142
  export { defaultSseCodecRegistry, SseCodecRegistry } from './registry/sse-codec-registry.js';
@@ -0,0 +1,14 @@
1
+ import { PassThrough } from 'node:stream';
2
+ import { AnthropicMessageResponse, AnthropicJsonToSseOptions, DEFAULT_ANTHROPIC_CONVERSION_CONFIG } from '../types/index.js';
3
+ export declare class AnthropicJsonToSseConverter {
4
+ private config;
5
+ private contexts;
6
+ constructor(config?: Partial<typeof DEFAULT_ANTHROPIC_CONVERSION_CONFIG>);
7
+ convertResponseToJsonToSse(response: AnthropicMessageResponse, options: AnthropicJsonToSseOptions): Promise<PassThrough>;
8
+ private createContext;
9
+ private processResponse;
10
+ private updateStats;
11
+ private validateResponse;
12
+ private handleStreamError;
13
+ private wrapError;
14
+ }
@@ -1,79 +1,112 @@
1
1
  import { PassThrough } from 'node:stream';
2
- /**
3
- * Minimal Anthropic JSON → SSE converter (v3)
4
- * - Emits a compact but spec-aligned event sequence for text content.
5
- * - Tool use blocks: emits content_block_start (tool_use), input_json_delta (whole), content_block_stop.
6
- *
7
- * NOTE: This converter purposely avoids any tool governance. It only maps shapes/fields.
8
- */
2
+ import { DEFAULT_ANTHROPIC_CONVERSION_CONFIG } from '../types/index.js';
3
+ import { ErrorUtils } from '../shared/utils.js';
4
+ import { createAnthropicSequencer } from './sequencers/anthropic-sequencer.js';
5
+ import { createAnthropicStreamWriter } from '../shared/writer.js';
9
6
  export class AnthropicJsonToSseConverter {
10
- async convertResponseToJsonToSse(payload, options) {
11
- const stream = new PassThrough();
12
- const write = (event, data) => {
13
- stream.write(`event: ${event}\n`);
14
- stream.write(`data: ${JSON.stringify(data)}\n\n`);
15
- };
16
- const messageId = payload.id || `msg_${Date.now()}`;
17
- const model = payload.model || options.model || 'unknown';
18
- // 1) message_start
19
- write('message_start', {
20
- type: 'message_start',
21
- message: { id: messageId, type: 'message', role: 'assistant', model }
7
+ config = DEFAULT_ANTHROPIC_CONVERSION_CONFIG;
8
+ contexts = new Map();
9
+ constructor(config) {
10
+ if (config) {
11
+ this.config = { ...this.config, ...config };
12
+ }
13
+ }
14
+ async convertResponseToJsonToSse(response, options) {
15
+ const context = this.createContext(response, options);
16
+ this.contexts.set(options.requestId, context);
17
+ const stream = new PassThrough({ objectMode: true });
18
+ const writer = createAnthropicStreamWriter(stream, {
19
+ onEvent: (event) => this.updateStats(context, event),
20
+ onError: (error) => this.handleStreamError(context, error, stream)
21
+ });
22
+ this.processResponse(response, context, writer, stream).catch((error) => {
23
+ this.handleStreamError(context, error, stream);
24
+ });
25
+ return Object.assign(stream, {
26
+ protocol: 'anthropic-messages',
27
+ direction: 'json_to_sse',
28
+ requestId: options.requestId,
29
+ getStats: () => context.eventStats,
30
+ complete: () => writer.complete(),
31
+ abort: (error) => writer.abort(error)
22
32
  });
23
- // 2) Iterate content blocks
24
- let index = 0;
25
- for (const block of payload.content || []) {
26
- if (!block || typeof block !== 'object')
27
- continue;
28
- const type = block.type;
29
- if (type === 'text') {
30
- write('content_block_start', {
31
- type: 'content_block_start',
32
- index,
33
- content_block: { type: 'text' }
34
- });
35
- const text = String(block.text || '');
36
- if (text) {
37
- write('content_block_delta', {
38
- type: 'content_block_delta',
39
- index,
40
- delta: { type: 'text_delta', text }
41
- });
42
- }
43
- write('content_block_stop', { type: 'content_block_stop', index });
44
- index += 1;
45
- }
46
- else if (type === 'tool_use') {
47
- const id = String(block.id || `call_${Math.random().toString(36).slice(2, 10)}`);
48
- const name = String(block.name || 'unknown_tool');
49
- const input = block.input ?? {};
50
- write('content_block_start', {
51
- type: 'content_block_start',
52
- index,
53
- content_block: { type: 'tool_use', id, name }
54
- });
55
- try {
56
- const partial_json = JSON.stringify(input);
57
- write('content_block_delta', {
58
- type: 'content_block_delta',
59
- index,
60
- delta: { type: 'input_json_delta', partial_json }
61
- });
62
- }
63
- catch {
64
- // best-effort: skip delta on stringify error
65
- }
66
- write('content_block_stop', { type: 'content_block_stop', index });
67
- index += 1;
68
- }
33
+ }
34
+ createContext(response, options) {
35
+ const stats = {
36
+ totalEvents: 0,
37
+ contentBlocks: 0,
38
+ toolUseBlocks: 0,
39
+ thinkingBlocks: 0,
40
+ textBlocks: 0,
41
+ errors: 0,
42
+ startTime: Date.now()
43
+ };
44
+ return {
45
+ requestId: options.requestId,
46
+ model: options.model,
47
+ response,
48
+ options,
49
+ startTime: Date.now(),
50
+ eventStats: stats
51
+ };
52
+ }
53
+ async processResponse(response, context, writer, stream) {
54
+ try {
55
+ this.validateResponse(response);
56
+ const sequencer = createAnthropicSequencer({
57
+ chunkSize: context.options.chunkSize ?? this.config.defaultChunkSize,
58
+ chunkDelayMs: context.options.chunkDelayMs ?? this.config.defaultDelayMs,
59
+ enableDelay: Boolean(context.options.chunkDelayMs),
60
+ reasoningMode: context.options.reasoningMode ?? this.config.reasoningMode,
61
+ reasoningTextPrefix: context.options.reasoningTextPrefix ?? this.config.reasoningTextPrefix
62
+ });
63
+ const events = sequencer.sequenceResponse(response, context.requestId);
64
+ await writer.writeAnthropicEvents(events);
65
+ writer.complete();
66
+ }
67
+ catch (error) {
68
+ writer.abort(error);
69
+ throw this.wrapError('ANTHROPIC_JSON_TO_SSE_FAILED', error, context.requestId);
69
70
  }
70
- // 3) message_delta (stop_reason hint)
71
- const stop_reason = payload.stop_reason || 'end_turn';
72
- write('message_delta', { type: 'message_delta', delta: { stop_reason } });
73
- // 4) message_stop
74
- write('message_stop', { type: 'message_stop' });
75
- // DONE
76
- stream.end();
77
- return stream;
71
+ finally {
72
+ this.contexts.delete(context.requestId);
73
+ stream.end();
74
+ }
75
+ }
76
+ updateStats(context, event) {
77
+ context.eventStats.totalEvents += 1;
78
+ if (!event || typeof event !== 'object')
79
+ return;
80
+ const type = event.type;
81
+ if (type === 'content_block_start') {
82
+ context.eventStats.contentBlocks += 1;
83
+ const blockType = event.data?.content_block?.type;
84
+ if (blockType === 'tool_use')
85
+ context.eventStats.toolUseBlocks += 1;
86
+ if (blockType === 'thinking')
87
+ context.eventStats.thinkingBlocks += 1;
88
+ if (blockType === 'text')
89
+ context.eventStats.textBlocks += 1;
90
+ }
91
+ }
92
+ validateResponse(response) {
93
+ if (!response || typeof response !== 'object') {
94
+ throw new Error('Invalid Anthropic response payload');
95
+ }
96
+ if (!Array.isArray(response.content)) {
97
+ throw new Error('Anthropic response content must be an array');
98
+ }
99
+ }
100
+ handleStreamError(context, error, stream) {
101
+ context.eventStats.errors += 1;
102
+ try {
103
+ stream.destroy(error);
104
+ }
105
+ catch {
106
+ /* noop */
107
+ }
108
+ }
109
+ wrapError(code, error, requestId) {
110
+ return ErrorUtils.createError(error.message, code, { requestId });
78
111
  }
79
112
  }
@@ -85,7 +85,9 @@ export class ChatJsonToSseConverterRefactored {
85
85
  const sequencer = createChatSequencer({
86
86
  chunkSize: context.options.maxTokensPerChunk || this.config.maxTokensPerChunk,
87
87
  chunkDelayMs: context.options.chunkDelayMs || this.config.defaultChunkDelayMs,
88
- enableDelay: !!context.options.chunkDelayMs
88
+ enableDelay: !!context.options.chunkDelayMs,
89
+ reasoningMode: context.options.reasoningMode || this.config.reasoningMode,
90
+ reasoningTextPrefix: context.options.reasoningTextPrefix ?? this.config.reasoningTextPrefix
89
91
  });
90
92
  // 4. 生成事件序列并写入流
91
93
  const eventStream = sequencer.sequenceRequest(request, context.model, context.requestId);
@@ -113,7 +115,9 @@ export class ChatJsonToSseConverterRefactored {
113
115
  const sequencer = createChatSequencer({
114
116
  chunkSize: context.options.maxTokensPerChunk || this.config.maxTokensPerChunk,
115
117
  chunkDelayMs: context.options.chunkDelayMs || this.config.defaultChunkDelayMs,
116
- enableDelay: !!context.options.chunkDelayMs
118
+ enableDelay: !!context.options.chunkDelayMs,
119
+ reasoningMode: context.options.reasoningMode || this.config.reasoningMode,
120
+ reasoningTextPrefix: context.options.reasoningTextPrefix ?? this.config.reasoningTextPrefix
117
121
  });
118
122
  // 4. 生成事件序列并写入流
119
123
  const eventStream = sequencer.sequenceResponse(response, context.model, context.requestId);
@@ -0,0 +1,14 @@
1
+ import { PassThrough } from 'node:stream';
2
+ import { GeminiResponse, GeminiJsonToSseOptions, DEFAULT_GEMINI_CONVERSION_CONFIG } from '../types/index.js';
3
+ export declare class GeminiJsonToSseConverter {
4
+ private config;
5
+ private contexts;
6
+ constructor(config?: Partial<typeof DEFAULT_GEMINI_CONVERSION_CONFIG>);
7
+ convertResponseToJsonToSse(response: GeminiResponse, options: GeminiJsonToSseOptions): Promise<PassThrough>;
8
+ private createContext;
9
+ private processResponse;
10
+ private validateResponse;
11
+ private updateStats;
12
+ private handleStreamError;
13
+ private wrapError;
14
+ }