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.
- package/README.md +7 -6
- package/UPGRADE.md +5 -6
- package/dist/server/coding-plan.js +94 -0
- package/dist/server/config-managed-fields.js +1 -0
- package/dist/server/conversions/compact.js +613 -0
- package/dist/server/conversions/detector.js +70 -0
- package/dist/server/conversions/index.js +285 -0
- package/dist/server/conversions/pairs/claude-completions/request.js +167 -0
- package/dist/server/conversions/pairs/claude-completions/response.js +56 -0
- package/dist/server/conversions/pairs/claude-completions/streaming.js +259 -0
- package/dist/server/conversions/pairs/claude-gemini/request.js +130 -0
- package/dist/server/conversions/pairs/claude-gemini/response.js +65 -0
- package/dist/server/conversions/pairs/claude-gemini/streaming.js +199 -0
- package/dist/server/conversions/pairs/claude-responses/request.js +190 -0
- package/dist/server/conversions/pairs/claude-responses/response.js +89 -0
- package/dist/server/conversions/pairs/claude-responses/streaming.js +266 -0
- package/dist/server/conversions/pairs/completions-claude/request.js +111 -0
- package/dist/server/conversions/pairs/completions-claude/response.js +67 -0
- package/dist/server/conversions/pairs/completions-claude/streaming.js +165 -0
- package/dist/server/conversions/pairs/completions-gemini/request.js +169 -0
- package/dist/server/conversions/pairs/completions-gemini/response.js +70 -0
- package/dist/server/conversions/pairs/completions-gemini/streaming.js +132 -0
- package/dist/server/conversions/pairs/completions-responses/request.js +149 -0
- package/dist/server/conversions/pairs/completions-responses/response.js +74 -0
- package/dist/server/conversions/pairs/completions-responses/streaming.js +189 -0
- package/dist/server/conversions/pairs/gemini-claude/request.js +118 -0
- package/dist/server/conversions/pairs/gemini-claude/response.js +45 -0
- package/dist/server/conversions/pairs/gemini-claude/streaming.js +146 -0
- package/dist/server/conversions/pairs/gemini-completions/request.js +151 -0
- package/dist/server/conversions/pairs/gemini-completions/response.js +54 -0
- package/dist/server/conversions/pairs/gemini-completions/streaming.js +108 -0
- package/dist/server/conversions/pairs/gemini-responses/request.js +18 -0
- package/dist/server/conversions/pairs/gemini-responses/response.js +18 -0
- package/dist/server/conversions/pairs/gemini-responses/streaming.js +43 -0
- package/dist/server/conversions/pairs/responses-claude/request.js +155 -0
- package/dist/server/conversions/pairs/responses-claude/response.js +70 -0
- package/dist/server/conversions/pairs/responses-claude/streaming.js +345 -0
- package/dist/server/conversions/pairs/responses-completions/request.js +207 -0
- package/dist/server/conversions/pairs/responses-completions/response.js +96 -0
- package/dist/server/conversions/pairs/responses-completions/streaming.js +344 -0
- package/dist/server/conversions/pairs/responses-gemini/request.js +18 -0
- package/dist/server/conversions/pairs/responses-gemini/response.js +18 -0
- package/dist/server/conversions/pairs/responses-gemini/streaming.js +43 -0
- package/dist/server/conversions/pairs/responses-responses/request.js +115 -0
- package/dist/server/conversions/pipeline.js +296 -0
- package/dist/server/conversions/stream-converter-adapter.js +49 -0
- package/dist/server/conversions/thinking/effort.js +61 -0
- package/dist/server/conversions/thinking/mapper.js +59 -0
- package/dist/server/conversions/thinking/providers.js +76 -0
- package/dist/server/conversions/types.js +5 -0
- package/dist/server/conversions/url-normalizer.js +58 -0
- package/dist/server/conversions/utils/format-mappers.js +57 -0
- package/dist/server/conversions/utils/id.js +33 -0
- package/dist/server/conversions/utils/stop-reasons.js +95 -0
- package/dist/server/conversions/utils/streaming-helpers.js +59 -0
- package/dist/server/conversions/utils/tool-schema.js +169 -0
- package/dist/server/conversions/utils/usage.js +82 -0
- package/dist/server/fs-database.js +465 -135
- package/dist/server/main.js +93 -33
- package/dist/server/original-config-reader.js +1 -1
- package/dist/server/proxy-server.js +1102 -804
- package/dist/server/transformers/chunk-collector.js +5 -1
- package/dist/server/transformers/streaming.js +6 -3235
- package/dist/server/type-migration.js +2 -3
- package/dist/server/utils.js +5 -0
- package/dist/ui/assets/{index-C7G0whng.css → index-BHR12ImE.css} +1 -1
- package/dist/ui/assets/index-DjdBW1yu.js +517 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/server/transformers/transformers.js +0 -1767
- package/dist/ui/assets/index-Nl6yJxrc.js +0 -514
- package/schema/claude.schema.md +0 -946
- package/schema/deepseek-chat.schema.md +0 -799
- package/schema/gemini.schema.md +0 -1408
- package/schema/openai-chat-completions.schema.md +0 -1088
- package/schema/openai-responses.schema.md +0 -226196
- 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
|
+
}
|