aicodeswitch 4.0.4 → 5.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 (77) hide show
  1. package/README.md +6 -5
  2. package/UPGRADE.md +5 -6
  3. package/dist/server/coding-plan.js +94 -0
  4. package/dist/server/config-managed-fields.js +1 -0
  5. package/dist/server/conversions/compact.js +613 -0
  6. package/dist/server/conversions/detector.js +70 -0
  7. package/dist/server/conversions/index.js +290 -0
  8. package/dist/server/conversions/pairs/claude-completions/request.js +167 -0
  9. package/dist/server/conversions/pairs/claude-completions/response.js +56 -0
  10. package/dist/server/conversions/pairs/claude-completions/streaming.js +259 -0
  11. package/dist/server/conversions/pairs/claude-gemini/request.js +130 -0
  12. package/dist/server/conversions/pairs/claude-gemini/response.js +65 -0
  13. package/dist/server/conversions/pairs/claude-gemini/streaming.js +199 -0
  14. package/dist/server/conversions/pairs/claude-responses/request.js +190 -0
  15. package/dist/server/conversions/pairs/claude-responses/response.js +89 -0
  16. package/dist/server/conversions/pairs/claude-responses/streaming.js +266 -0
  17. package/dist/server/conversions/pairs/completions-claude/request.js +111 -0
  18. package/dist/server/conversions/pairs/completions-claude/response.js +67 -0
  19. package/dist/server/conversions/pairs/completions-claude/streaming.js +165 -0
  20. package/dist/server/conversions/pairs/completions-gemini/request.js +169 -0
  21. package/dist/server/conversions/pairs/completions-gemini/response.js +70 -0
  22. package/dist/server/conversions/pairs/completions-gemini/streaming.js +132 -0
  23. package/dist/server/conversions/pairs/completions-responses/request.js +149 -0
  24. package/dist/server/conversions/pairs/completions-responses/response.js +74 -0
  25. package/dist/server/conversions/pairs/completions-responses/streaming.js +189 -0
  26. package/dist/server/conversions/pairs/gemini-claude/request.js +118 -0
  27. package/dist/server/conversions/pairs/gemini-claude/response.js +45 -0
  28. package/dist/server/conversions/pairs/gemini-claude/streaming.js +146 -0
  29. package/dist/server/conversions/pairs/gemini-completions/request.js +151 -0
  30. package/dist/server/conversions/pairs/gemini-completions/response.js +54 -0
  31. package/dist/server/conversions/pairs/gemini-completions/streaming.js +108 -0
  32. package/dist/server/conversions/pairs/gemini-responses/request.js +18 -0
  33. package/dist/server/conversions/pairs/gemini-responses/response.js +18 -0
  34. package/dist/server/conversions/pairs/gemini-responses/streaming.js +43 -0
  35. package/dist/server/conversions/pairs/responses-claude/request.js +180 -0
  36. package/dist/server/conversions/pairs/responses-claude/response.js +70 -0
  37. package/dist/server/conversions/pairs/responses-claude/streaming.js +345 -0
  38. package/dist/server/conversions/pairs/responses-completions/request.js +207 -0
  39. package/dist/server/conversions/pairs/responses-completions/response.js +96 -0
  40. package/dist/server/conversions/pairs/responses-completions/streaming.js +344 -0
  41. package/dist/server/conversions/pairs/responses-gemini/request.js +18 -0
  42. package/dist/server/conversions/pairs/responses-gemini/response.js +18 -0
  43. package/dist/server/conversions/pairs/responses-gemini/streaming.js +43 -0
  44. package/dist/server/conversions/pairs/responses-responses/request.js +115 -0
  45. package/dist/server/conversions/pipeline.js +296 -0
  46. package/dist/server/conversions/stream-converter-adapter.js +49 -0
  47. package/dist/server/conversions/thinking/effort.js +61 -0
  48. package/dist/server/conversions/thinking/mapper.js +59 -0
  49. package/dist/server/conversions/thinking/providers.js +80 -0
  50. package/dist/server/conversions/types.js +5 -0
  51. package/dist/server/conversions/url-normalizer.js +58 -0
  52. package/dist/server/conversions/utils/format-mappers.js +57 -0
  53. package/dist/server/conversions/utils/id.js +33 -0
  54. package/dist/server/conversions/utils/stop-reasons.js +95 -0
  55. package/dist/server/conversions/utils/streaming-helpers.js +59 -0
  56. package/dist/server/conversions/utils/tool-schema.js +169 -0
  57. package/dist/server/conversions/utils/usage.js +82 -0
  58. package/dist/server/fs-database.js +465 -135
  59. package/dist/server/main.js +93 -33
  60. package/dist/server/original-config-reader.js +1 -1
  61. package/dist/server/proxy-server.js +887 -633
  62. package/dist/server/transformers/chunk-collector.js +5 -1
  63. package/dist/server/transformers/streaming.js +6 -3235
  64. package/dist/server/type-migration.js +2 -3
  65. package/dist/server/utils.js +5 -0
  66. package/dist/ui/assets/{index-C7G0whng.css → index-BHR12ImE.css} +1 -1
  67. package/dist/ui/assets/index-Rwiqttz-.js +517 -0
  68. package/dist/ui/index.html +2 -2
  69. package/package.json +1 -1
  70. package/dist/server/transformers/transformers.js +0 -1767
  71. package/dist/ui/assets/index-Dl-B9pXM.js +0 -514
  72. package/schema/claude.schema.md +0 -946
  73. package/schema/deepseek-chat.schema.md +0 -799
  74. package/schema/gemini.schema.md +0 -1408
  75. package/schema/openai-chat-completions.schema.md +0 -1088
  76. package/schema/openai-responses.schema.md +0 -226196
  77. package/schema/stream.md +0 -2592
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ /**
3
+ * AICodingBus Unified Format Conversion System
4
+ *
5
+ * Provides conversion between 4 API formats:
6
+ * 1. Claude Messages API
7
+ * 2. OpenAI Responses API
8
+ * 3. OpenAI Chat Completions API
9
+ * 4. Gemini GenerateContent API
10
+ *
11
+ * 12 unidirectional pairs cover all client→upstream combinations.
12
+ * Naming: pairs/{client}-{upstream}/ — left=client, right=upstream.
13
+ *
14
+ * Provider-specific post-processing (thinking params, reasoning history fix)
15
+ * is driven by the ReasoningConfig passed through TransformRequestOptions.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.processCompactResponse = exports.prepareCompactRequest = exports.buildCompactedResponse = exports.extractSummaryFromResponse = exports.buildCompactUpstreamRequest = exports.buildCompactionPrompt = exports.COMPACTION_SYSTEM_PROMPT = exports.isCodexCompactRequest = exports.isLastClaudeMessageCompact = exports.isClaudeCompactRequest = exports.extractMessageContent = exports.extractConversationText = exports.getReasoningConfig = exports.sourceTypeToFormat = exports.detectRequestFormat = void 0;
19
+ exports.transformRequest = transformRequest;
20
+ exports.transformResponse = transformResponse;
21
+ exports.createStreamConverter = createStreamConverter;
22
+ exports.buildTargetBody = buildTargetBody;
23
+ var detector_js_1 = require("./detector.js");
24
+ Object.defineProperty(exports, "detectRequestFormat", { enumerable: true, get: function () { return detector_js_1.detectRequestFormat; } });
25
+ Object.defineProperty(exports, "sourceTypeToFormat", { enumerable: true, get: function () { return detector_js_1.sourceTypeToFormat; } });
26
+ var providers_js_1 = require("./thinking/providers.js");
27
+ Object.defineProperty(exports, "getReasoningConfig", { enumerable: true, get: function () { return providers_js_1.getReasoningConfig; } });
28
+ const providers_js_2 = require("./thinking/providers.js");
29
+ // --- Compact API ---
30
+ var compact_js_1 = require("./compact.js");
31
+ Object.defineProperty(exports, "extractConversationText", { enumerable: true, get: function () { return compact_js_1.extractConversationText; } });
32
+ Object.defineProperty(exports, "extractMessageContent", { enumerable: true, get: function () { return compact_js_1.extractMessageContent; } });
33
+ Object.defineProperty(exports, "isClaudeCompactRequest", { enumerable: true, get: function () { return compact_js_1.isClaudeCompactRequest; } });
34
+ Object.defineProperty(exports, "isLastClaudeMessageCompact", { enumerable: true, get: function () { return compact_js_1.isLastClaudeMessageCompact; } });
35
+ Object.defineProperty(exports, "isCodexCompactRequest", { enumerable: true, get: function () { return compact_js_1.isCodexCompactRequest; } });
36
+ Object.defineProperty(exports, "COMPACTION_SYSTEM_PROMPT", { enumerable: true, get: function () { return compact_js_1.COMPACTION_SYSTEM_PROMPT; } });
37
+ Object.defineProperty(exports, "buildCompactionPrompt", { enumerable: true, get: function () { return compact_js_1.buildCompactionPrompt; } });
38
+ Object.defineProperty(exports, "buildCompactUpstreamRequest", { enumerable: true, get: function () { return compact_js_1.buildCompactUpstreamRequest; } });
39
+ Object.defineProperty(exports, "extractSummaryFromResponse", { enumerable: true, get: function () { return compact_js_1.extractSummaryFromResponse; } });
40
+ Object.defineProperty(exports, "buildCompactedResponse", { enumerable: true, get: function () { return compact_js_1.buildCompactedResponse; } });
41
+ // Unified high-level API
42
+ Object.defineProperty(exports, "prepareCompactRequest", { enumerable: true, get: function () { return compact_js_1.prepareCompactRequest; } });
43
+ Object.defineProperty(exports, "processCompactResponse", { enumerable: true, get: function () { return compact_js_1.processCompactResponse; } });
44
+ // --- claude client → * upstream ---
45
+ const request_js_1 = require("./pairs/claude-completions/request.js");
46
+ const response_js_1 = require("./pairs/claude-completions/response.js");
47
+ const streaming_js_1 = require("./pairs/claude-completions/streaming.js");
48
+ const request_js_2 = require("./pairs/claude-responses/request.js");
49
+ const response_js_2 = require("./pairs/claude-responses/response.js");
50
+ const streaming_js_2 = require("./pairs/claude-responses/streaming.js");
51
+ const request_js_3 = require("./pairs/claude-gemini/request.js");
52
+ const response_js_3 = require("./pairs/claude-gemini/response.js");
53
+ const streaming_js_3 = require("./pairs/claude-gemini/streaming.js");
54
+ // --- completions client → * upstream ---
55
+ const request_js_4 = require("./pairs/completions-claude/request.js");
56
+ const response_js_4 = require("./pairs/completions-claude/response.js");
57
+ const streaming_js_4 = require("./pairs/completions-claude/streaming.js");
58
+ const request_js_5 = require("./pairs/completions-responses/request.js");
59
+ const response_js_5 = require("./pairs/completions-responses/response.js");
60
+ const streaming_js_5 = require("./pairs/completions-responses/streaming.js");
61
+ const request_js_6 = require("./pairs/completions-gemini/request.js");
62
+ const response_js_6 = require("./pairs/completions-gemini/response.js");
63
+ const streaming_js_6 = require("./pairs/completions-gemini/streaming.js");
64
+ // --- responses client → * upstream ---
65
+ const request_js_7 = require("./pairs/responses-claude/request.js");
66
+ const response_js_7 = require("./pairs/responses-claude/response.js");
67
+ const streaming_js_7 = require("./pairs/responses-claude/streaming.js");
68
+ const request_js_8 = require("./pairs/responses-completions/request.js");
69
+ const response_js_8 = require("./pairs/responses-completions/response.js");
70
+ const streaming_js_8 = require("./pairs/responses-completions/streaming.js");
71
+ const request_js_9 = require("./pairs/responses-gemini/request.js");
72
+ const response_js_9 = require("./pairs/responses-gemini/response.js");
73
+ const streaming_js_9 = require("./pairs/responses-gemini/streaming.js");
74
+ // --- responses → responses (同格式降级兼容) ---
75
+ const request_js_10 = require("./pairs/responses-responses/request.js");
76
+ // --- gemini client → * upstream ---
77
+ const request_js_11 = require("./pairs/gemini-claude/request.js");
78
+ const response_js_10 = require("./pairs/gemini-claude/response.js");
79
+ const streaming_js_10 = require("./pairs/gemini-claude/streaming.js");
80
+ const request_js_12 = require("./pairs/gemini-completions/request.js");
81
+ const response_js_11 = require("./pairs/gemini-completions/response.js");
82
+ const streaming_js_11 = require("./pairs/gemini-completions/streaming.js");
83
+ const request_js_13 = require("./pairs/gemini-responses/request.js");
84
+ const response_js_12 = require("./pairs/gemini-responses/response.js");
85
+ const streaming_js_12 = require("./pairs/gemini-responses/streaming.js");
86
+ // --- Provider-driven post-processing ---
87
+ const mapper_js_1 = require("./thinking/mapper.js");
88
+ const effort_js_1 = require("./thinking/effort.js");
89
+ // ============================================================
90
+ // Public API: Request Transformation
91
+ // ============================================================
92
+ /**
93
+ * Transform a request body from one format to another.
94
+ */
95
+ function transformRequest(options) {
96
+ const { fromFormat, toFormat, body, sanitizeBody, providerConfig } = options;
97
+ const targetBody = buildTargetBody({ fromFormat, toFormat, body, sanitizeBody, providerConfig });
98
+ return { body: targetBody, headers: {} };
99
+ }
100
+ // ============================================================
101
+ // Public API: Response Transformation
102
+ // ============================================================
103
+ /**
104
+ * Transform a response body from upstream format back to client format.
105
+ */
106
+ function transformResponse(options) {
107
+ const { fromFormat, toFormat, response } = options;
108
+ // Passthrough: same format
109
+ if (fromFormat === toFormat) {
110
+ return response;
111
+ }
112
+ const key = `${fromFormat}->${toFormat}`;
113
+ switch (key) {
114
+ // --- upstream claude → client * ---
115
+ case 'claude->completions':
116
+ return (0, response_js_4.claudeResponseToCompletions)(response);
117
+ case 'claude->responses':
118
+ return (0, response_js_7.claudeToResponsesResponse)(response);
119
+ case 'claude->gemini':
120
+ return (0, response_js_10.claudeToGeminiResponse)(response);
121
+ // --- upstream responses → client * ---
122
+ case 'responses->claude':
123
+ return (0, response_js_2.responsesToClaudeResponse)(response);
124
+ case 'responses->completions':
125
+ return (0, response_js_5.responsesToCompletionsResponse)(response);
126
+ case 'responses->gemini':
127
+ return (0, response_js_12.responsesToGeminiResponse)(response);
128
+ // --- upstream completions → client * ---
129
+ case 'completions->claude':
130
+ return (0, response_js_1.completionsResponseToClaude)(response);
131
+ case 'completions->responses':
132
+ return (0, response_js_8.completionsToResponsesResponse)(response);
133
+ case 'completions->gemini':
134
+ return (0, response_js_11.completionsToGeminiResponse)(response);
135
+ // --- upstream gemini → client * ---
136
+ case 'gemini->claude':
137
+ return (0, response_js_3.geminiToClaudeResponse)(response);
138
+ case 'gemini->completions':
139
+ return (0, response_js_6.geminiToCompletionsResponse)(response);
140
+ case 'gemini->responses':
141
+ return (0, response_js_9.geminiToResponsesResponse)(response);
142
+ default:
143
+ return response;
144
+ }
145
+ }
146
+ // ============================================================
147
+ // Public API: Stream Converter Factory
148
+ // ============================================================
149
+ /**
150
+ * Create a streaming converter for the given format pair.
151
+ */
152
+ function createStreamConverter(options) {
153
+ const { fromFormat, toFormat } = options;
154
+ // Passthrough: same format
155
+ if (fromFormat === toFormat) {
156
+ return new PassthroughConverter();
157
+ }
158
+ const key = `${fromFormat}->${toFormat}`;
159
+ switch (key) {
160
+ // --- upstream → claude client ---
161
+ case 'completions->claude':
162
+ return new streaming_js_1.CompletionsToClaudeConverter();
163
+ case 'gemini->claude':
164
+ return new streaming_js_3.GeminiToClaudeConverter();
165
+ case 'responses->claude':
166
+ return new streaming_js_2.ResponsesToClaudeConverter();
167
+ // --- upstream → responses client ---
168
+ case 'completions->responses':
169
+ return new streaming_js_8.CompletionsToResponsesConverter();
170
+ case 'gemini->responses':
171
+ return new streaming_js_9.GeminiToResponsesConverter();
172
+ case 'claude->responses':
173
+ return new streaming_js_7.ClaudeToResponsesConverter();
174
+ // --- upstream → completions client ---
175
+ case 'claude->completions':
176
+ return new streaming_js_4.ClaudeToCompletionsConverter();
177
+ case 'gemini->completions':
178
+ return new streaming_js_6.GeminiToCompletionsConverter();
179
+ case 'responses->completions':
180
+ return new streaming_js_5.ResponsesToCompletionsConverter();
181
+ // --- upstream → gemini client ---
182
+ case 'claude->gemini':
183
+ return new streaming_js_10.ClaudeToGeminiConverter();
184
+ case 'completions->gemini':
185
+ return new streaming_js_11.CompletionsToGeminiConverter();
186
+ case 'responses->gemini':
187
+ return new streaming_js_12.ResponsesToGeminiConverter();
188
+ default:
189
+ return new PassthroughConverter();
190
+ }
191
+ }
192
+ // ============================================================
193
+ // Helpers
194
+ // ============================================================
195
+ /**
196
+ * Transform a request body from one format to another,
197
+ * with provider-driven post-processing for completions targets.
198
+ */
199
+ function buildTargetBody(options) {
200
+ const { fromFormat, toFormat, body, sanitizeBody, providerConfig } = options;
201
+ // Dispatch to the correct conversion pair
202
+ const key = `${fromFormat}->${toFormat}`;
203
+ let result;
204
+ switch (key) {
205
+ // --- claude → * ---
206
+ case 'claude->completions':
207
+ result = (0, request_js_1.claudeToCompletions)(body);
208
+ break;
209
+ case 'claude->responses':
210
+ result = (0, request_js_2.claudeToResponses)(body);
211
+ break;
212
+ case 'claude->gemini':
213
+ result = (0, request_js_3.claudeToGemini)(body);
214
+ break;
215
+ // --- responses → * ---
216
+ case 'responses->completions':
217
+ result = (0, request_js_8.responsesToCompletions)(body);
218
+ break;
219
+ case 'responses->claude':
220
+ result = (0, request_js_7.responsesToClaude)(body);
221
+ break;
222
+ case 'responses->gemini':
223
+ result = (0, request_js_9.responsesToGeminiRequest)(body);
224
+ break;
225
+ case 'responses->responses': {
226
+ if (sanitizeBody) {
227
+ // Responses 格式降级兼容:委托给 responses-responses pair 处理
228
+ result = (0, request_js_10.downgradeResponsesRequest)(body);
229
+ }
230
+ else {
231
+ result = body;
232
+ }
233
+ break;
234
+ }
235
+ // --- completions → * ---
236
+ case 'completions->claude':
237
+ result = (0, request_js_4.completionsToClaude)(body);
238
+ break;
239
+ case 'completions->responses':
240
+ result = (0, request_js_5.completionsToResponses)(body);
241
+ break;
242
+ case 'completions->gemini':
243
+ result = (0, request_js_6.completionsToGemini)(body);
244
+ break;
245
+ // --- gemini → * ---
246
+ case 'gemini->claude':
247
+ result = (0, request_js_11.geminiToClaude)(body);
248
+ break;
249
+ case 'gemini->completions':
250
+ result = (0, request_js_12.geminiToCompletions)(body);
251
+ break;
252
+ case 'gemini->responses':
253
+ result = (0, request_js_13.geminiToResponsesRequest)(body);
254
+ break;
255
+ default:
256
+ result = body;
257
+ }
258
+ // --- Provider-driven post-processing for completions targets ---
259
+ if (toFormat === 'completions' && providerConfig) {
260
+ const isReasoningContentCompletion = providerConfig.outputFormat === 'reasoning_content';
261
+ if (isReasoningContentCompletion) {
262
+ // 修复历史:确保 assistant + tool_calls 消息有 reasoning_content
263
+ if (result.messages) {
264
+ result.messages = (0, mapper_js_1.fixThinkingHistory)(result.messages, 'completions');
265
+ }
266
+ // 剥离 stream_options(reasoning_content 提供商通常不支持)
267
+ delete result.stream_options;
268
+ }
269
+ // 注入 thinking 参数(如 thinking: { type: 'enabled' })和 effort 参数
270
+ if (providerConfig.supportsThinking || providerConfig.supportsEffort) {
271
+ const effort = body.thinking ? (0, effort_js_1.claudeThinkingToReasoningEffort)(body.thinking) : null;
272
+ result = (0, providers_js_2.applyReasoningConfig)(result, providerConfig, effort);
273
+ }
274
+ }
275
+ // --- Safety net for Claude upstream: ensure thinking blocks alongside tool_use ---
276
+ // When thinking mode is enabled, Claude requires thinking blocks in assistant messages with tool_use
277
+ if (toFormat === 'claude' && result.thinking && result.messages) {
278
+ result.messages = (0, mapper_js_1.fixThinkingHistory)(result.messages, 'claude');
279
+ }
280
+ return result;
281
+ }
282
+ /** Identity converter that passes events through unchanged */
283
+ class PassthroughConverter {
284
+ convertEvent(event) {
285
+ return [event];
286
+ }
287
+ flush() {
288
+ return [];
289
+ }
290
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Messages → OpenAI Chat Completions request conversion.
4
+ *
5
+ * Enhanced with reasoning_content / thinking block support.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.claudeToCompletions = claudeToCompletions;
9
+ const effort_js_1 = require("../../thinking/effort.js");
10
+ const tool_schema_js_1 = require("../../utils/tool-schema.js");
11
+ /**
12
+ * Convert a Claude Messages request body to an OpenAI Chat Completions request body.
13
+ *
14
+ * Always preserves reasoning_content: thinking / redacted_thinking blocks are surfaced
15
+ * as reasoning_content on assistant messages.
16
+ */
17
+ function claudeToCompletions(body) {
18
+ const messages = [];
19
+ // --- System prompt -------------------------------------------------------
20
+ if (body.system) {
21
+ const systemContent = typeof body.system === 'string'
22
+ ? body.system
23
+ : Array.isArray(body.system)
24
+ ? body.system
25
+ .filter((s) => s.type === 'text' || typeof s.text === 'string')
26
+ .map((s) => s.text || '')
27
+ .join('\n')
28
+ : '';
29
+ if (systemContent) {
30
+ messages.push({ role: 'system', content: systemContent });
31
+ }
32
+ }
33
+ // --- Messages ------------------------------------------------------------
34
+ if (body.messages) {
35
+ for (const msg of body.messages) {
36
+ const converted = convertClaudeMessageToCompletions(msg);
37
+ const toolResults = converted._toolResults;
38
+ delete converted._toolResults;
39
+ // When the message contains only tool_result blocks (no text),
40
+ // converted.content is null – skip pushing the empty wrapper to avoid
41
+ // sending an invalid { role: "user", content: null } message.
42
+ const hasOnlyToolResults = toolResults && toolResults.length > 0 && converted.content === null;
43
+ if (!hasOnlyToolResults) {
44
+ messages.push(converted);
45
+ }
46
+ // Emit tool_result blocks as separate 'tool' role messages (OpenAI format)
47
+ if (toolResults) {
48
+ for (const tr of toolResults) {
49
+ messages.push(tr);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ // --- Build result --------------------------------------------------------
55
+ const result = {
56
+ model: body.model,
57
+ messages,
58
+ stream: body.stream || false,
59
+ };
60
+ // Parameter mapping
61
+ if (body.max_tokens)
62
+ result.max_tokens = body.max_tokens;
63
+ if (body.temperature !== undefined)
64
+ result.temperature = body.temperature;
65
+ if (body.top_p !== undefined)
66
+ result.top_p = body.top_p;
67
+ if (body.stop_sequences)
68
+ result.stop = body.stop_sequences;
69
+ // Tool mapping (Claude input_schema → OpenAI parameters)
70
+ if (body.tools && body.tools.length > 0) {
71
+ result.tools = (0, tool_schema_js_1.claudeToCompletionsTools)(body.tools);
72
+ }
73
+ // --- O-series reasoning model handling -----------------------------------
74
+ if (body.thinking && body.model && (0, effort_js_1.isOSeriesModel)(body.model)) {
75
+ // O-series models use max_completion_tokens instead of max_tokens
76
+ if (result.max_tokens) {
77
+ result.max_completion_tokens = result.max_tokens;
78
+ delete result.max_tokens;
79
+ }
80
+ // Inject reasoning_effort derived from Claude thinking config
81
+ const effort = (0, effort_js_1.claudeThinkingToReasoningEffort)(body.thinking);
82
+ if (effort) {
83
+ result.reasoning_effort = effort;
84
+ }
85
+ }
86
+ return result;
87
+ }
88
+ /**
89
+ * Convert a single Claude message into an OpenAI message object.
90
+ * Returns `{ role, content, tool_calls?, reasoning_content?, _toolResults? }`.
91
+ */
92
+ function convertClaudeMessageToCompletions(msg) {
93
+ const content = msg.content;
94
+ // String content – simple passthrough
95
+ if (typeof content === 'string') {
96
+ return { role: msg.role, content };
97
+ }
98
+ if (!Array.isArray(content)) {
99
+ return { role: msg.role, content: String(content) };
100
+ }
101
+ const parts = [];
102
+ const toolCalls = [];
103
+ const toolResults = [];
104
+ const reasoningParts = [];
105
+ for (const block of content) {
106
+ if (block.type === 'text') {
107
+ parts.push(block.text);
108
+ }
109
+ else if (block.type === 'tool_use') {
110
+ toolCalls.push({
111
+ id: block.id,
112
+ type: 'function',
113
+ function: {
114
+ name: block.name,
115
+ arguments: typeof block.input === 'string'
116
+ ? block.input
117
+ : JSON.stringify(block.input),
118
+ },
119
+ });
120
+ }
121
+ else if (block.type === 'tool_result') {
122
+ const resultContent = typeof block.content === 'string'
123
+ ? block.content
124
+ : JSON.stringify(block.content);
125
+ toolResults.push({
126
+ role: 'tool',
127
+ tool_call_id: block.tool_use_id,
128
+ content: resultContent,
129
+ });
130
+ }
131
+ else if (block.type === 'thinking') {
132
+ reasoningParts.push(block.thinking || '');
133
+ }
134
+ else if (block.type === 'redacted_thinking') {
135
+ reasoningParts.push('[redacted thinking]');
136
+ }
137
+ }
138
+ // Assistant message with tool calls
139
+ if (toolCalls.length > 0) {
140
+ const result = {
141
+ role: 'assistant',
142
+ content: parts.join('\n') || null,
143
+ tool_calls: toolCalls,
144
+ };
145
+ if (msg.role === 'assistant' && reasoningParts.length > 0) {
146
+ result.reasoning_content = reasoningParts.join('\n');
147
+ }
148
+ return result;
149
+ }
150
+ // User message with tool results – emit as separate tool messages
151
+ if (toolResults.length > 0) {
152
+ return {
153
+ role: msg.role,
154
+ content: parts.join('\n') || null,
155
+ _toolResults: toolResults,
156
+ };
157
+ }
158
+ // Plain message (may include reasoning_content for assistant)
159
+ const result = {
160
+ role: msg.role,
161
+ content: parts.join('\n') || null,
162
+ };
163
+ if (msg.role === 'assistant' && reasoningParts.length > 0) {
164
+ result.reasoning_content = reasoningParts.join('\n');
165
+ }
166
+ return result;
167
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Chat Completions → Claude Messages response conversion.
4
+ *
5
+ * Enhanced with reasoning_content / thinking block support.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.completionsResponseToClaude = completionsResponseToClaude;
9
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
10
+ const usage_js_1 = require("../../utils/usage.js");
11
+ /**
12
+ * Convert an OpenAI Chat Completions response into a Claude Messages response.
13
+ */
14
+ function completionsResponseToClaude(response) {
15
+ var _a, _b, _c, _d;
16
+ const choice = (_a = response.choices) === null || _a === void 0 ? void 0 : _a[0];
17
+ if (!choice)
18
+ return response;
19
+ const content = [];
20
+ // reasoning_content → thinking block (prepend before other content)
21
+ if ((_b = choice.message) === null || _b === void 0 ? void 0 : _b.reasoning_content) {
22
+ content.push({
23
+ type: 'thinking',
24
+ thinking: choice.message.reasoning_content,
25
+ });
26
+ }
27
+ // Text content
28
+ if ((_c = choice.message) === null || _c === void 0 ? void 0 : _c.content) {
29
+ content.push({ type: 'text', text: choice.message.content });
30
+ }
31
+ // Tool calls
32
+ if ((_d = choice.message) === null || _d === void 0 ? void 0 : _d.tool_calls) {
33
+ for (const tc of choice.message.tool_calls) {
34
+ content.push({
35
+ type: 'tool_use',
36
+ id: tc.id,
37
+ name: tc.function.name,
38
+ input: typeof tc.function.arguments === 'string'
39
+ ? JSON.parse(tc.function.arguments)
40
+ : tc.function.arguments,
41
+ });
42
+ }
43
+ }
44
+ const stopReason = (0, stop_reasons_js_1.completionsToClaudeStopReason)(choice.finish_reason);
45
+ const usage = (0, usage_js_1.completionsToClaudeUsage)(response.usage);
46
+ return {
47
+ id: response.id,
48
+ type: 'message',
49
+ role: 'assistant',
50
+ content,
51
+ model: response.model,
52
+ stop_reason: stopReason,
53
+ stop_sequence: null,
54
+ usage,
55
+ };
56
+ }