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,190 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Messages → OpenAI Responses API request conversion.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.claudeToResponses = claudeToResponses;
7
+ const effort_js_1 = require("../../thinking/effort.js");
8
+ const tool_schema_js_1 = require("../../utils/tool-schema.js");
9
+ /**
10
+ * Convert a Claude Messages request body to an OpenAI Responses API request body.
11
+ */
12
+ function claudeToResponses(body) {
13
+ var _a;
14
+ const input = [];
15
+ // --- System prompt -> instructions ---
16
+ let instructions;
17
+ if (body.system) {
18
+ instructions = typeof body.system === 'string'
19
+ ? body.system
20
+ : Array.isArray(body.system)
21
+ ? body.system
22
+ .filter((s) => s.type === 'text' || typeof s.text === 'string')
23
+ .map((s) => s.text || '')
24
+ .join('\n')
25
+ : undefined;
26
+ // Strip any x-anthropic-billing-header prefixes
27
+ if (instructions) {
28
+ instructions = instructions.replace(/^x-anthropic-billing-header[^\n]*\n?/gi, '');
29
+ }
30
+ }
31
+ // --- Messages -> input array ---
32
+ if (body.messages) {
33
+ const buffer = new MessageBuffer();
34
+ for (const msg of body.messages) {
35
+ buffer.setCurrentRole(msg.role);
36
+ const content = msg.content;
37
+ if (typeof content === 'string') {
38
+ buffer.addText(content);
39
+ }
40
+ else if (Array.isArray(content)) {
41
+ for (const block of content) {
42
+ if (block.type === 'text') {
43
+ buffer.addText(block.text);
44
+ }
45
+ else if (block.type === 'image') {
46
+ buffer.addImage(block);
47
+ }
48
+ else if (block.type === 'tool_use') {
49
+ // Flush buffered content first, then emit standalone function_call
50
+ buffer.flush(input);
51
+ input.push({
52
+ type: 'function_call',
53
+ call_id: block.id,
54
+ name: block.name,
55
+ arguments: typeof block.input === 'string' ? block.input : JSON.stringify(block.input),
56
+ });
57
+ }
58
+ else if (block.type === 'tool_result') {
59
+ // Flush buffered content first, then emit standalone function_call_output
60
+ buffer.flush(input);
61
+ const outputContent = typeof block.content === 'string'
62
+ ? block.content
63
+ : JSON.stringify(block.content);
64
+ input.push({
65
+ type: 'function_call_output',
66
+ call_id: block.tool_use_id,
67
+ output: outputContent,
68
+ });
69
+ }
70
+ else if (block.type === 'thinking' || block.type === 'redacted_thinking') {
71
+ // Skip thinking blocks
72
+ continue;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ // Flush any remaining buffered content
78
+ buffer.flush(input);
79
+ }
80
+ // --- Build result ---
81
+ const result = {
82
+ model: body.model,
83
+ input,
84
+ stream: (_a = body.stream) !== null && _a !== void 0 ? _a : false,
85
+ };
86
+ if (instructions)
87
+ result.instructions = instructions;
88
+ if (body.max_tokens)
89
+ result.max_output_tokens = body.max_tokens;
90
+ if (body.temperature !== undefined)
91
+ result.temperature = body.temperature;
92
+ if (body.top_p !== undefined)
93
+ result.top_p = body.top_p;
94
+ // --- Thinking -> reasoning ---
95
+ if (body.thinking) {
96
+ const reasoning = (0, effort_js_1.claudeThinkingToResponsesReasoning)(body.thinking);
97
+ if (reasoning) {
98
+ result.reasoning = reasoning;
99
+ }
100
+ }
101
+ // --- Tools ---
102
+ if (body.tools && body.tools.length > 0) {
103
+ result.tools = (0, tool_schema_js_1.claudeToResponsesTools)(body.tools);
104
+ }
105
+ // --- Tool choice mapping ---
106
+ if (body.tool_choice) {
107
+ result.tool_choice = mapClaudeToolChoice(body.tool_choice);
108
+ }
109
+ // Note: stop_sequences are dropped (not supported in Responses API)
110
+ return result;
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Helpers
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Buffer for accumulating text/image content into a single message before
117
+ * flushing when encountering standalone items like function_call / function_call_output.
118
+ */
119
+ class MessageBuffer {
120
+ constructor() {
121
+ Object.defineProperty(this, "currentRole", {
122
+ enumerable: true,
123
+ configurable: true,
124
+ writable: true,
125
+ value: null
126
+ });
127
+ Object.defineProperty(this, "content", {
128
+ enumerable: true,
129
+ configurable: true,
130
+ writable: true,
131
+ value: []
132
+ });
133
+ }
134
+ setCurrentRole(role) {
135
+ // If role changed, flush what we have
136
+ if (this.currentRole !== null && this.currentRole !== role && this.content.length > 0) {
137
+ // Role changed — caller should flush first, but for safety we just track
138
+ }
139
+ this.currentRole = role;
140
+ }
141
+ addText(text) {
142
+ if (!this.currentRole)
143
+ return;
144
+ const textType = this.currentRole === 'assistant' ? 'output_text' : 'input_text';
145
+ this.content.push({ type: textType, text });
146
+ }
147
+ addImage(block) {
148
+ var _a, _b;
149
+ if (!this.currentRole)
150
+ return;
151
+ this.content.push({
152
+ type: 'input_image',
153
+ image_url: `data:${((_a = block.source) === null || _a === void 0 ? void 0 : _a.media_type) || block.media_type || 'image/png'};base64,${((_b = block.source) === null || _b === void 0 ? void 0 : _b.data) || block.data}`,
154
+ });
155
+ }
156
+ flush(target) {
157
+ if (this.content.length === 0 || !this.currentRole) {
158
+ this.content = [];
159
+ return;
160
+ }
161
+ const role = this.currentRole;
162
+ target.push({ role, content: this.content });
163
+ this.content = [];
164
+ }
165
+ }
166
+ /**
167
+ * Map Claude tool_choice to Responses API tool_choice.
168
+ */
169
+ function mapClaudeToolChoice(toolChoice) {
170
+ if (!toolChoice)
171
+ return undefined;
172
+ if (typeof toolChoice === 'string') {
173
+ switch (toolChoice) {
174
+ case 'any': return 'required';
175
+ case 'auto': return 'auto';
176
+ case 'none': return 'none';
177
+ default: return 'auto';
178
+ }
179
+ }
180
+ if (toolChoice.type === 'any')
181
+ return 'required';
182
+ if (toolChoice.type === 'auto')
183
+ return 'auto';
184
+ if (toolChoice.type === 'none')
185
+ return 'none';
186
+ if (toolChoice.type === 'tool') {
187
+ return { type: 'function', name: toolChoice.name };
188
+ }
189
+ return 'auto';
190
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Responses API → Claude Messages response conversion.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.responsesToClaudeResponse = responsesToClaudeResponse;
7
+ exports.claudeToResponsesUsage = claudeToResponsesUsage;
8
+ const id_js_1 = require("../../utils/id.js");
9
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
10
+ const usage_js_1 = require("../../utils/usage.js");
11
+ const mapper_js_1 = require("../../thinking/mapper.js");
12
+ /**
13
+ * Convert an OpenAI Responses API response to a Claude Messages response.
14
+ */
15
+ function responsesToClaudeResponse(response) {
16
+ var _a;
17
+ const output = response.output || [];
18
+ const content = [];
19
+ for (const item of output) {
20
+ if (item.type === 'message') {
21
+ // Message with text / refusal content
22
+ if (Array.isArray(item.content)) {
23
+ for (const part of item.content) {
24
+ if (part.type === 'output_text') {
25
+ content.push({ type: 'text', text: part.text || '' });
26
+ }
27
+ else if (part.type === 'refusal') {
28
+ content.push({ type: 'text', text: part.refusal || '' });
29
+ }
30
+ }
31
+ }
32
+ }
33
+ else if (item.type === 'function_call') {
34
+ let parsedInput;
35
+ try {
36
+ parsedInput = typeof item.arguments === 'string' ? JSON.parse(item.arguments) : item.arguments;
37
+ }
38
+ catch (_b) {
39
+ parsedInput = {};
40
+ }
41
+ content.push({
42
+ type: 'tool_use',
43
+ id: item.call_id || (0, id_js_1.generateCallId)(),
44
+ name: item.name,
45
+ input: parsedInput,
46
+ });
47
+ }
48
+ else if (item.type === 'reasoning') {
49
+ // Reasoning with summary -> thinking block
50
+ if (Array.isArray(item.summary) && item.summary.length > 0) {
51
+ const thinkingBlock = (0, mapper_js_1.reasoningToThinking)(item.summary);
52
+ if (thinkingBlock.thinking) {
53
+ content.push(thinkingBlock);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ const hasToolUse = content.some((b) => b.type === 'tool_use');
59
+ const stopReason = (0, stop_reasons_js_1.responsesToClaudeStopReason)(response.status, (_a = response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason, hasToolUse);
60
+ const usage = (0, usage_js_1.responsesToClaudeUsage)(response.usage);
61
+ return {
62
+ id: response.id || (0, id_js_1.generateMessageId)(),
63
+ type: 'message',
64
+ role: 'assistant',
65
+ content,
66
+ model: response.model || '',
67
+ stop_reason: stopReason,
68
+ stop_sequence: null,
69
+ usage,
70
+ };
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Helpers
74
+ // ---------------------------------------------------------------------------
75
+ /**
76
+ * Map Claude usage to Responses API usage.
77
+ */
78
+ function claudeToResponsesUsage(usage) {
79
+ var _a, _b;
80
+ if (!usage)
81
+ return { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
82
+ const input = (_a = usage.input_tokens) !== null && _a !== void 0 ? _a : 0;
83
+ const output = (_b = usage.output_tokens) !== null && _b !== void 0 ? _b : 0;
84
+ return {
85
+ input_tokens: input,
86
+ output_tokens: output,
87
+ total_tokens: input + output,
88
+ };
89
+ }
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Responses API SSE → Claude Messages SSE streaming conversion.
4
+ *
5
+ * Stateful converter that processes SSE events from an OpenAI Responses API
6
+ * upstream and emits Claude Messages SSE events.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ResponsesToClaudeConverter = void 0;
10
+ const id_js_1 = require("../../utils/id.js");
11
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
12
+ const usage_js_1 = require("../../utils/usage.js");
13
+ const streaming_helpers_js_1 = require("../../utils/streaming-helpers.js");
14
+ /**
15
+ * Stateful converter: Responses API SSE → Claude Messages SSE.
16
+ */
17
+ class ResponsesToClaudeConverter {
18
+ constructor() {
19
+ Object.defineProperty(this, "started", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: false
24
+ });
25
+ Object.defineProperty(this, "messageStopped", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: false
30
+ });
31
+ Object.defineProperty(this, "currentBlockType", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: null
36
+ });
37
+ Object.defineProperty(this, "currentBlockIndex", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: -1
42
+ });
43
+ Object.defineProperty(this, "nextBlockIndex", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: 0
48
+ });
49
+ Object.defineProperty(this, "messageId", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: (0, id_js_1.generateMessageId)()
54
+ });
55
+ Object.defineProperty(this, "model", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: ''
60
+ });
61
+ }
62
+ convertEvent(event) {
63
+ var _a;
64
+ if (!event.data)
65
+ return [];
66
+ const eventName = event.event;
67
+ const events = [];
68
+ try {
69
+ const data = (0, streaming_helpers_js_1.parseEventData)(event.data);
70
+ switch (eventName) {
71
+ case 'response.created': {
72
+ this.model = data.model || '';
73
+ this.started = false;
74
+ this.messageStopped = false;
75
+ // Will emit message_start when we get actual content
76
+ break;
77
+ }
78
+ case 'response.output_item.added': {
79
+ const item = data.item || data;
80
+ if (item.type === 'function_call') {
81
+ this.ensureStarted(events, data.model);
82
+ this.closeCurrentBlock(events);
83
+ // Open tool_use block
84
+ const idx = this.nextBlockIndex++;
85
+ events.push(this.makeSSE('content_block_start', {
86
+ type: 'content_block_start',
87
+ index: idx,
88
+ content_block: {
89
+ type: 'tool_use',
90
+ id: item.call_id || (0, id_js_1.generateCallId)(),
91
+ name: item.name || '',
92
+ input: {},
93
+ },
94
+ }));
95
+ this.currentBlockIndex = idx;
96
+ this.currentBlockType = null; // tool_use is not tracked as a "block type" for closing
97
+ }
98
+ break;
99
+ }
100
+ case 'response.content_part.added': {
101
+ const part = data.part || data;
102
+ if (part.type === 'output_text') {
103
+ this.ensureStarted(events, data.model);
104
+ if (this.currentBlockType !== 'text') {
105
+ this.closeCurrentBlock(events);
106
+ this.openTextBlock(events);
107
+ }
108
+ }
109
+ break;
110
+ }
111
+ case 'response.output_text.delta': {
112
+ this.ensureStarted(events, data.model);
113
+ if (this.currentBlockType !== 'text') {
114
+ this.closeCurrentBlock(events);
115
+ this.openTextBlock(events);
116
+ }
117
+ const delta = data.delta || '';
118
+ if (delta) {
119
+ events.push(this.makeSSE('content_block_delta', {
120
+ type: 'content_block_delta',
121
+ index: this.currentBlockIndex,
122
+ delta: { type: 'text_delta', text: delta },
123
+ }));
124
+ }
125
+ break;
126
+ }
127
+ case 'response.output_text.done': {
128
+ this.closeCurrentBlock(events);
129
+ break;
130
+ }
131
+ case 'response.reasoning.delta':
132
+ case 'response.reasoning_summary_text.delta': {
133
+ this.ensureStarted(events, data.model);
134
+ const text = data.delta || data.text || '';
135
+ if (!text)
136
+ break;
137
+ if (this.currentBlockType !== 'thinking') {
138
+ this.closeCurrentBlock(events);
139
+ this.openThinkingBlock(events);
140
+ }
141
+ events.push(this.makeSSE('content_block_delta', {
142
+ type: 'content_block_delta',
143
+ index: this.currentBlockIndex,
144
+ delta: { type: 'thinking_delta', thinking: text },
145
+ }));
146
+ break;
147
+ }
148
+ case 'response.reasoning.done': {
149
+ this.closeCurrentBlock(events);
150
+ break;
151
+ }
152
+ case 'response.function_call_arguments.delta': {
153
+ const argDelta = data.delta || '';
154
+ if (argDelta) {
155
+ events.push(this.makeSSE('content_block_delta', {
156
+ type: 'content_block_delta',
157
+ index: this.currentBlockIndex,
158
+ delta: { type: 'input_json_delta', partial_json: argDelta },
159
+ }));
160
+ }
161
+ break;
162
+ }
163
+ case 'response.function_call_arguments.done': {
164
+ // Close the tool_use block
165
+ if (this.currentBlockIndex >= 0 && this.currentBlockType === null) {
166
+ events.push(this.makeSSE('content_block_stop', {
167
+ type: 'content_block_stop',
168
+ index: this.currentBlockIndex,
169
+ }));
170
+ this.currentBlockIndex = -1;
171
+ this.currentBlockType = null;
172
+ }
173
+ break;
174
+ }
175
+ case 'response.completed': {
176
+ this.closeCurrentBlock(events);
177
+ if (this.started && !this.messageStopped) {
178
+ const responseData = data.response || data;
179
+ const stopReason = (0, stop_reasons_js_1.responsesToClaudeStopReason)(responseData.status, (_a = responseData.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason, true);
180
+ const usage = (0, usage_js_1.responsesToClaudeUsage)(responseData.usage);
181
+ events.push(this.makeSSE('message_delta', {
182
+ type: 'message_delta',
183
+ delta: { stop_reason: stopReason, stop_sequence: null },
184
+ usage,
185
+ }));
186
+ events.push(this.makeSSE('message_stop', {
187
+ type: 'message_stop',
188
+ }));
189
+ this.messageStopped = true;
190
+ }
191
+ break;
192
+ }
193
+ // Skip these events
194
+ case 'response.in_progress':
195
+ case 'response.output_item.done':
196
+ case 'response.content_part.done':
197
+ case 'response.reasoning_summary_part.added':
198
+ case 'response.reasoning_summary_part.done':
199
+ break;
200
+ default:
201
+ break;
202
+ }
203
+ }
204
+ catch (_b) {
205
+ // Ignore parse errors
206
+ }
207
+ return events;
208
+ }
209
+ ensureStarted(events, model) {
210
+ if (this.started)
211
+ return;
212
+ if (model)
213
+ this.model = model;
214
+ events.push(this.makeSSE('message_start', {
215
+ type: 'message_start',
216
+ message: {
217
+ id: this.messageId,
218
+ type: 'message',
219
+ role: 'assistant',
220
+ content: [],
221
+ model: this.model,
222
+ stop_reason: null,
223
+ stop_sequence: null,
224
+ usage: { input_tokens: 0, output_tokens: 0 },
225
+ },
226
+ }));
227
+ this.started = true;
228
+ }
229
+ openTextBlock(events) {
230
+ const idx = this.nextBlockIndex++;
231
+ events.push(this.makeSSE('content_block_start', {
232
+ type: 'content_block_start',
233
+ index: idx,
234
+ content_block: { type: 'text', text: '' },
235
+ }));
236
+ this.currentBlockIndex = idx;
237
+ this.currentBlockType = 'text';
238
+ }
239
+ openThinkingBlock(events) {
240
+ const idx = this.nextBlockIndex++;
241
+ events.push(this.makeSSE('content_block_start', {
242
+ type: 'content_block_start',
243
+ index: idx,
244
+ content_block: { type: 'thinking', thinking: '' },
245
+ }));
246
+ this.currentBlockIndex = idx;
247
+ this.currentBlockType = 'thinking';
248
+ }
249
+ closeCurrentBlock(events) {
250
+ if (this.currentBlockType !== null && this.currentBlockIndex >= 0) {
251
+ events.push(this.makeSSE('content_block_stop', {
252
+ type: 'content_block_stop',
253
+ index: this.currentBlockIndex,
254
+ }));
255
+ this.currentBlockType = null;
256
+ this.currentBlockIndex = -1;
257
+ }
258
+ }
259
+ makeSSE(claudeEventType, data) {
260
+ return {
261
+ event: claudeEventType,
262
+ data,
263
+ };
264
+ }
265
+ }
266
+ exports.ResponsesToClaudeConverter = ResponsesToClaudeConverter;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Chat Completions → Claude Messages request conversion.
4
+ *
5
+ * Converts a Chat Completions request body into a Claude Messages request body.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.completionsToClaude = completionsToClaude;
9
+ const tool_schema_js_1 = require("../../utils/tool-schema.js");
10
+ /**
11
+ * Convert an OpenAI Chat Completions request body to a Claude Messages request body.
12
+ */
13
+ function completionsToClaude(body) {
14
+ const messages = [];
15
+ let systemPrompt;
16
+ for (const msg of body.messages) {
17
+ if (msg.role === 'system') {
18
+ systemPrompt =
19
+ typeof msg.content === 'string' ? msg.content : msg.content;
20
+ continue;
21
+ }
22
+ const content = convertCompletionsMessageToClaude(msg);
23
+ if (content) {
24
+ const role = msg.role === 'tool'
25
+ ? 'user'
26
+ : msg.role === 'function'
27
+ ? 'assistant'
28
+ : msg.role;
29
+ messages.push({ role, content });
30
+ }
31
+ }
32
+ const result = {
33
+ model: body.model,
34
+ messages,
35
+ max_tokens: body.max_tokens || 8192,
36
+ stream: body.stream || false,
37
+ };
38
+ if (systemPrompt)
39
+ result.system = systemPrompt;
40
+ if (body.temperature !== undefined)
41
+ result.temperature = body.temperature;
42
+ if (body.top_p !== undefined)
43
+ result.top_p = body.top_p;
44
+ if (body.stop) {
45
+ result.stop_sequences = Array.isArray(body.stop) ? body.stop : [body.stop];
46
+ }
47
+ // Tool mapping (OpenAI parameters → Claude input_schema)
48
+ if (body.tools && body.tools.length > 0) {
49
+ result.tools = (0, tool_schema_js_1.completionsToClaudeTools)(body.tools);
50
+ }
51
+ return result;
52
+ }
53
+ /**
54
+ * Convert a single OpenAI message into Claude content (string or content block array).
55
+ */
56
+ function convertCompletionsMessageToClaude(msg) {
57
+ // Assistant with tool_calls
58
+ if (msg.role === 'assistant' && msg.tool_calls) {
59
+ const content = [];
60
+ // reasoning_content → thinking block (prepend)
61
+ if (msg.reasoning_content) {
62
+ content.push({
63
+ type: 'thinking',
64
+ thinking: msg.reasoning_content,
65
+ });
66
+ }
67
+ if (msg.content) {
68
+ content.push({ type: 'text', text: msg.content });
69
+ }
70
+ for (const tc of msg.tool_calls) {
71
+ content.push({
72
+ type: 'tool_use',
73
+ id: tc.id,
74
+ name: tc.function.name,
75
+ input: typeof tc.function.arguments === 'string'
76
+ ? JSON.parse(tc.function.arguments)
77
+ : tc.function.arguments,
78
+ });
79
+ }
80
+ return content;
81
+ }
82
+ // Tool role → tool_result block
83
+ if (msg.role === 'tool') {
84
+ return {
85
+ type: 'tool_result',
86
+ tool_use_id: msg.tool_call_id,
87
+ content: msg.content,
88
+ };
89
+ }
90
+ // Function role → tool_result block
91
+ if (msg.role === 'function') {
92
+ return {
93
+ type: 'tool_result',
94
+ tool_use_id: msg.name,
95
+ content: msg.content,
96
+ };
97
+ }
98
+ // Plain assistant with reasoning_content (no tool_calls)
99
+ if (msg.role === 'assistant' && msg.reasoning_content) {
100
+ const content = [];
101
+ content.push({
102
+ type: 'thinking',
103
+ thinking: msg.reasoning_content,
104
+ });
105
+ if (msg.content) {
106
+ content.push({ type: 'text', text: msg.content });
107
+ }
108
+ return content;
109
+ }
110
+ return msg.content;
111
+ }