aicodeswitch 4.0.3 → 5.0.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 +7 -6
  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 +285 -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 +155 -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 +76 -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 +1102 -804
  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-DjdBW1yu.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-Nl6yJxrc.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,149 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Chat Completions → OpenAI Responses API request conversion.
4
+ *
5
+ * Handles the mapping from Chat Completions' role-based messages array
6
+ * to the Responses API's flat input items (message, function_call,
7
+ * function_call_output, reasoning).
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.completionsToResponses = completionsToResponses;
11
+ const id_js_1 = require("../../utils/id.js");
12
+ /**
13
+ * Convert a Chat Completions request body to an OpenAI Responses API request body.
14
+ */
15
+ function completionsToResponses(body) {
16
+ var _a, _b, _c;
17
+ const input = [];
18
+ let instructions;
19
+ // --- Extract system messages -> instructions ---
20
+ for (const msg of body.messages) {
21
+ if (msg.role === 'system' || msg.role === 'developer') {
22
+ const text = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
23
+ instructions = instructions ? instructions + '\n' + text : text;
24
+ }
25
+ }
26
+ // --- Convert messages -> input array ---
27
+ for (const msg of body.messages) {
28
+ if (msg.role === 'system' || msg.role === 'developer') {
29
+ continue; // already extracted as instructions
30
+ }
31
+ if (msg.role === 'user') {
32
+ input.push({
33
+ type: 'message',
34
+ role: 'user',
35
+ content: [{ type: 'input_text', text: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content) }],
36
+ });
37
+ }
38
+ else if (msg.role === 'assistant') {
39
+ // reasoning_content -> reasoning item
40
+ if (msg.reasoning_content) {
41
+ input.push({
42
+ type: 'reasoning',
43
+ id: (0, id_js_1.generateCallId)().replace('call_', 'rs_'),
44
+ summary: [{ type: 'summary_text', text: msg.reasoning_content }],
45
+ });
46
+ }
47
+ // tool_calls -> function_call items
48
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
49
+ for (const tc of msg.tool_calls) {
50
+ input.push({
51
+ type: 'function_call',
52
+ call_id: tc.id,
53
+ name: ((_a = tc.function) === null || _a === void 0 ? void 0 : _a.name) || '',
54
+ arguments: ((_b = tc.function) === null || _b === void 0 ? void 0 : _b.arguments) || '{}',
55
+ });
56
+ }
57
+ // If there is also text content, emit a message item first
58
+ if (msg.content) {
59
+ // Insert message before the function_calls we just added
60
+ const msgItem = {
61
+ type: 'message',
62
+ role: 'assistant',
63
+ content: [{ type: 'output_text', text: msg.content }],
64
+ };
65
+ // Find where tool_calls start and insert the message there
66
+ const firstToolIdx = input.length - msg.tool_calls.length;
67
+ input.splice(firstToolIdx, 0, msgItem);
68
+ }
69
+ }
70
+ else if (msg.content) {
71
+ // Plain assistant message
72
+ input.push({
73
+ type: 'message',
74
+ role: 'assistant',
75
+ content: [{ type: 'output_text', text: msg.content }],
76
+ });
77
+ }
78
+ }
79
+ else if (msg.role === 'tool') {
80
+ input.push({
81
+ type: 'function_call_output',
82
+ call_id: msg.tool_call_id,
83
+ content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
84
+ });
85
+ }
86
+ }
87
+ // --- Build result ---
88
+ const result = {
89
+ model: body.model,
90
+ input,
91
+ stream: (_c = body.stream) !== null && _c !== void 0 ? _c : false,
92
+ };
93
+ if (instructions)
94
+ result.instructions = instructions;
95
+ if (body.max_tokens !== undefined)
96
+ result.max_output_tokens = body.max_tokens;
97
+ if (body.max_completion_tokens !== undefined)
98
+ result.max_output_tokens = body.max_completion_tokens;
99
+ if (body.temperature !== undefined)
100
+ result.temperature = body.temperature;
101
+ if (body.top_p !== undefined)
102
+ result.top_p = body.top_p;
103
+ // --- Reasoning effort ---
104
+ if (body.reasoning_effort) {
105
+ result.reasoning = { effort: body.reasoning_effort };
106
+ }
107
+ // --- Tools ---
108
+ if (body.tools && body.tools.length > 0) {
109
+ result.tools = body.tools
110
+ .filter((t) => t.type === 'function' && t.function)
111
+ .map((t) => ({
112
+ type: 'function',
113
+ name: t.function.name,
114
+ description: t.function.description || '',
115
+ parameters: t.function.parameters || {},
116
+ }));
117
+ }
118
+ // --- Tool choice reverse mapping ---
119
+ if (body.tool_choice !== undefined) {
120
+ result.tool_choice = completionsToResponsesToolChoice(body.tool_choice);
121
+ }
122
+ return result;
123
+ }
124
+ /**
125
+ * Map Chat Completions tool_choice to Responses API tool_choice.
126
+ */
127
+ function completionsToResponsesToolChoice(toolChoice) {
128
+ if (typeof toolChoice === 'string') {
129
+ switch (toolChoice) {
130
+ case 'any':
131
+ case 'required': return 'required';
132
+ case 'auto': return 'auto';
133
+ case 'none': return 'none';
134
+ default: return 'auto';
135
+ }
136
+ }
137
+ if (typeof toolChoice === 'object') {
138
+ if (toolChoice.type === 'function' && toolChoice.function) {
139
+ return { type: 'function', name: toolChoice.function.name };
140
+ }
141
+ if (toolChoice.type === 'any')
142
+ return 'required';
143
+ if (toolChoice.type === 'auto')
144
+ return 'auto';
145
+ if (toolChoice.type === 'none')
146
+ return 'none';
147
+ }
148
+ return 'auto';
149
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Responses API → OpenAI Chat Completions response conversion.
4
+ *
5
+ * Converts a Responses API response into a Chat Completions response.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.responsesToCompletionsResponse = responsesToCompletionsResponse;
9
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
10
+ const id_js_1 = require("../../utils/id.js");
11
+ /**
12
+ * Convert a Responses API response to a Chat Completions response.
13
+ */
14
+ function responsesToCompletionsResponse(response) {
15
+ var _a;
16
+ const output = response.output || [];
17
+ let content = '';
18
+ let reasoningContent;
19
+ const toolCalls = [];
20
+ for (const item of output) {
21
+ if (item.type === 'message') {
22
+ // Join output_text texts
23
+ const texts = (item.content || [])
24
+ .filter((c) => c.type === 'output_text')
25
+ .map((c) => c.text || '');
26
+ content += texts.join('');
27
+ }
28
+ else if (item.type === 'reasoning') {
29
+ // Join summary texts
30
+ const summaries = (item.summary || [])
31
+ .map((s) => s.text || '')
32
+ .filter(Boolean);
33
+ if (summaries.length > 0) {
34
+ reasoningContent = (reasoningContent || '') + summaries.join('\n');
35
+ }
36
+ }
37
+ else if (item.type === 'function_call') {
38
+ toolCalls.push({
39
+ id: item.call_id,
40
+ type: 'function',
41
+ function: {
42
+ name: item.name || '',
43
+ arguments: item.arguments || '{}',
44
+ },
45
+ });
46
+ }
47
+ }
48
+ const message = {
49
+ role: 'assistant',
50
+ content: content || null,
51
+ };
52
+ if (reasoningContent) {
53
+ message.reasoning_content = reasoningContent;
54
+ }
55
+ if (toolCalls.length > 0) {
56
+ message.tool_calls = toolCalls;
57
+ }
58
+ const finishReason = (0, stop_reasons_js_1.responsesToCompletionsFinishReason)(response.status);
59
+ const usage = response.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
60
+ return {
61
+ id: ((_a = response.id) === null || _a === void 0 ? void 0 : _a.startsWith('chatcmpl-')) ? response.id : (0, id_js_1.generateCompletionsId)(),
62
+ object: 'chat.completion',
63
+ created: response.created_at || Math.floor(Date.now() / 1000),
64
+ model: response.model,
65
+ choices: [
66
+ {
67
+ index: 0,
68
+ message,
69
+ finish_reason: finishReason,
70
+ },
71
+ ],
72
+ usage,
73
+ };
74
+ }
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Responses API → OpenAI Chat Completions streaming conversion.
4
+ *
5
+ * Stateless converter that processes Responses API SSE events and produces
6
+ * Chat Completions SSE chunks.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ResponsesToCompletionsConverter = void 0;
10
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
11
+ const id_js_1 = require("../../utils/id.js");
12
+ const streaming_helpers_js_1 = require("../../utils/streaming-helpers.js");
13
+ /**
14
+ * Convert Responses API SSE events into Chat Completions SSE chunks.
15
+ */
16
+ class ResponsesToCompletionsConverter {
17
+ constructor() {
18
+ Object.defineProperty(this, "chatId", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: (0, id_js_1.generateCompletionsId)()
23
+ });
24
+ Object.defineProperty(this, "model", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: ''
29
+ });
30
+ }
31
+ convertEvent(event) {
32
+ var _a, _b;
33
+ if (!event.data)
34
+ return [];
35
+ if (event.data === '[DONE]' || ((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'done') {
36
+ return [{ data: '[DONE]', event: '' }];
37
+ }
38
+ try {
39
+ const eventName = event.event;
40
+ const parsed = (0, streaming_helpers_js_1.parseEventData)(event.data);
41
+ const chunks = [];
42
+ switch (eventName) {
43
+ case 'response.created': {
44
+ // First chunk with id, model
45
+ this.chatId = ((_b = parsed.id) === null || _b === void 0 ? void 0 : _b.startsWith('chatcmpl-')) ? parsed.id : (0, id_js_1.generateCompletionsId)();
46
+ this.model = parsed.model || '';
47
+ chunks.push({
48
+ event: '',
49
+ data: {
50
+ id: this.chatId,
51
+ object: 'chat.completion.chunk',
52
+ created: Math.floor(Date.now() / 1000),
53
+ model: this.model,
54
+ choices: [{
55
+ index: 0,
56
+ delta: { role: 'assistant', content: '' },
57
+ finish_reason: null,
58
+ }],
59
+ },
60
+ });
61
+ break;
62
+ }
63
+ case 'response.output_text.delta': {
64
+ // Text content delta
65
+ chunks.push({
66
+ event: '',
67
+ data: {
68
+ id: this.chatId,
69
+ object: 'chat.completion.chunk',
70
+ created: Math.floor(Date.now() / 1000),
71
+ model: this.model,
72
+ choices: [{
73
+ index: 0,
74
+ delta: { content: parsed.text || '' },
75
+ finish_reason: null,
76
+ }],
77
+ },
78
+ });
79
+ break;
80
+ }
81
+ case 'response.reasoning.delta':
82
+ case 'response.reasoning_summary_text.delta': {
83
+ // Reasoning content delta
84
+ const text = parsed.text || parsed.delta || '';
85
+ if (text) {
86
+ chunks.push({
87
+ event: '',
88
+ data: {
89
+ id: this.chatId,
90
+ object: 'chat.completion.chunk',
91
+ created: Math.floor(Date.now() / 1000),
92
+ model: this.model,
93
+ choices: [{
94
+ index: 0,
95
+ delta: { reasoning_content: text },
96
+ finish_reason: null,
97
+ }],
98
+ },
99
+ });
100
+ }
101
+ break;
102
+ }
103
+ case 'response.output_item.added': {
104
+ // function_call added
105
+ if (parsed.type === 'function_call') {
106
+ chunks.push({
107
+ event: '',
108
+ data: {
109
+ id: this.chatId,
110
+ object: 'chat.completion.chunk',
111
+ created: Math.floor(Date.now() / 1000),
112
+ model: this.model,
113
+ choices: [{
114
+ index: 0,
115
+ delta: {
116
+ tool_calls: [{
117
+ id: parsed.call_id || parsed.id,
118
+ type: 'function',
119
+ function: {
120
+ name: parsed.name || '',
121
+ arguments: '',
122
+ },
123
+ }],
124
+ },
125
+ finish_reason: null,
126
+ }],
127
+ },
128
+ });
129
+ }
130
+ break;
131
+ }
132
+ case 'response.function_call_arguments.delta': {
133
+ // Function call arguments fragment
134
+ const argsDelta = parsed.delta || '';
135
+ if (argsDelta) {
136
+ chunks.push({
137
+ event: '',
138
+ data: {
139
+ id: this.chatId,
140
+ object: 'chat.completion.chunk',
141
+ created: Math.floor(Date.now() / 1000),
142
+ model: this.model,
143
+ choices: [{
144
+ index: 0,
145
+ delta: {
146
+ tool_calls: [{
147
+ function: { arguments: argsDelta },
148
+ }],
149
+ },
150
+ finish_reason: null,
151
+ }],
152
+ },
153
+ });
154
+ }
155
+ break;
156
+ }
157
+ case 'response.completed': {
158
+ // Final chunk with finish_reason and usage
159
+ const finishReason = (0, stop_reasons_js_1.responsesToCompletionsFinishReason)(parsed.status);
160
+ const usage = parsed.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
161
+ chunks.push({
162
+ event: '',
163
+ data: {
164
+ id: this.chatId,
165
+ object: 'chat.completion.chunk',
166
+ created: Math.floor(Date.now() / 1000),
167
+ model: this.model || parsed.model || '',
168
+ choices: [{
169
+ index: 0,
170
+ delta: {},
171
+ finish_reason: finishReason,
172
+ }],
173
+ usage,
174
+ },
175
+ });
176
+ break;
177
+ }
178
+ // Events not mapped to Chat format: silently skip
179
+ default:
180
+ break;
181
+ }
182
+ return chunks;
183
+ }
184
+ catch (_c) {
185
+ return [event];
186
+ }
187
+ }
188
+ }
189
+ exports.ResponsesToCompletionsConverter = ResponsesToCompletionsConverter;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ /**
3
+ * Gemini generateContent → Claude Messages request conversion.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.geminiToClaude = geminiToClaude;
7
+ const tool_schema_js_1 = require("../../utils/tool-schema.js");
8
+ const id_js_1 = require("../../utils/id.js");
9
+ /**
10
+ * Convert a Gemini generateContent request body to a Claude Messages request body.
11
+ */
12
+ function geminiToClaude(body) {
13
+ var _a, _b;
14
+ const messages = [];
15
+ // --- System instruction → system ------------------------------------------
16
+ let system;
17
+ if (body.systemInstruction) {
18
+ const parts = body.systemInstruction.parts || [];
19
+ system = parts.map((p) => p.text || '').join('\n\n');
20
+ }
21
+ // --- Contents → messages --------------------------------------------------
22
+ if (body.contents) {
23
+ for (const entry of body.contents) {
24
+ const role = entry.role === 'model' ? 'assistant' : 'user';
25
+ const content = convertGeminiPartsToClaudeContent(entry.parts);
26
+ messages.push({ role, content });
27
+ }
28
+ }
29
+ // --- Build result ---------------------------------------------------------
30
+ const result = {
31
+ messages,
32
+ stream: false,
33
+ };
34
+ if (system)
35
+ result.system = system;
36
+ // --- Generation config reverse mapping ------------------------------------
37
+ const gc = body.generationConfig || {};
38
+ if (gc.maxOutputTokens)
39
+ result.max_tokens = gc.maxOutputTokens;
40
+ if (gc.temperature !== undefined)
41
+ result.temperature = gc.temperature;
42
+ if (gc.topP !== undefined)
43
+ result.top_p = gc.topP;
44
+ if (gc.stopSequences)
45
+ result.stop_sequences = gc.stopSequences;
46
+ // --- Tools reverse mapping ------------------------------------------------
47
+ const functionDeclarations = ((_a = body.tools) === null || _a === void 0 ? void 0 : _a.flatMap((t) => t.functionDeclarations || t)) || [];
48
+ if (functionDeclarations.length > 0) {
49
+ result.tools = (0, tool_schema_js_1.geminiToClaudeTools)(functionDeclarations);
50
+ }
51
+ // --- ToolConfig reverse mapping -------------------------------------------
52
+ if ((_b = body.toolConfig) === null || _b === void 0 ? void 0 : _b.functionCallingConfig) {
53
+ const fcc = body.toolConfig.functionCallingConfig;
54
+ if (fcc.mode === 'ANY') {
55
+ if (fcc.allowedFunctionNames && fcc.allowedFunctionNames.length === 1) {
56
+ result.tool_choice = { type: 'tool', name: fcc.allowedFunctionNames[0] };
57
+ }
58
+ else {
59
+ result.tool_choice = { type: 'any' };
60
+ }
61
+ }
62
+ else if (fcc.mode === 'AUTO') {
63
+ result.tool_choice = { type: 'auto' };
64
+ }
65
+ else if (fcc.mode === 'NONE') {
66
+ result.tool_choice = { type: 'none' };
67
+ }
68
+ }
69
+ return result;
70
+ }
71
+ /**
72
+ * Convert Gemini parts array into Claude content (string or content block array).
73
+ */
74
+ function convertGeminiPartsToClaudeContent(parts) {
75
+ var _a;
76
+ if (!parts || parts.length === 0)
77
+ return '';
78
+ const blocks = [];
79
+ for (const part of parts) {
80
+ if (part.text !== undefined && part.text !== null) {
81
+ blocks.push({ type: 'text', text: part.text });
82
+ }
83
+ else if (part.functionCall) {
84
+ blocks.push({
85
+ type: 'tool_use',
86
+ id: (0, id_js_1.generateToolUseId)(),
87
+ name: part.functionCall.name,
88
+ input: part.functionCall.args || {},
89
+ });
90
+ }
91
+ else if (part.functionResponse) {
92
+ const content = ((_a = part.functionResponse.response) === null || _a === void 0 ? void 0 : _a.content) ||
93
+ JSON.stringify(part.functionResponse.response);
94
+ blocks.push({
95
+ type: 'tool_result',
96
+ tool_use_id: part.functionResponse.name,
97
+ content,
98
+ });
99
+ }
100
+ else if (part.inlineData) {
101
+ blocks.push({
102
+ type: 'image',
103
+ source: {
104
+ type: 'base64',
105
+ media_type: part.inlineData.mimeType,
106
+ data: part.inlineData.data,
107
+ },
108
+ });
109
+ }
110
+ }
111
+ if (blocks.length === 0)
112
+ return '';
113
+ // If there is a single text block, return as plain string
114
+ if (blocks.length === 1 && blocks[0].type === 'text') {
115
+ return blocks[0].text;
116
+ }
117
+ return blocks;
118
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Messages → Gemini generateContent response conversion.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.claudeToGeminiResponse = claudeToGeminiResponse;
7
+ const stop_reasons_js_1 = require("../../utils/stop-reasons.js");
8
+ const usage_js_1 = require("../../utils/usage.js");
9
+ /**
10
+ * Convert a Claude Messages response into a Gemini generateContent response.
11
+ */
12
+ function claudeToGeminiResponse(response) {
13
+ const parts = [];
14
+ for (const block of response.content || []) {
15
+ if (block.type === 'text') {
16
+ parts.push({ text: block.text });
17
+ }
18
+ else if (block.type === 'tool_use') {
19
+ parts.push({
20
+ functionCall: {
21
+ name: block.name,
22
+ args: block.input,
23
+ },
24
+ });
25
+ }
26
+ else if (block.type === 'thinking') {
27
+ // Skip thinking blocks — Gemini has no equivalent
28
+ continue;
29
+ }
30
+ }
31
+ const finishReason = (0, stop_reasons_js_1.claudeToGeminiStopReason)(response.stop_reason);
32
+ const usageMetadata = (0, usage_js_1.claudeToGeminiUsage)(response.usage);
33
+ return {
34
+ candidates: [
35
+ {
36
+ content: {
37
+ parts,
38
+ role: 'model',
39
+ },
40
+ finishReason,
41
+ },
42
+ ],
43
+ usageMetadata,
44
+ };
45
+ }