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,43 @@
1
+ "use strict";
2
+ /**
3
+ * Composite streaming converter: Gemini SSE → Responses API SSE.
4
+ *
5
+ * Chains: GeminiToCompletionsConverter → CompletionsToResponsesConverter
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.GeminiToResponsesConverter = void 0;
9
+ const streaming_js_1 = require("../completions-gemini/streaming.js");
10
+ const streaming_js_2 = require("../responses-completions/streaming.js");
11
+ const streaming_helpers_js_1 = require("../../utils/streaming-helpers.js");
12
+ /**
13
+ * Composite converter: Gemini SSE → Completions SSE → Responses API SSE
14
+ */
15
+ class GeminiToResponsesConverter {
16
+ constructor() {
17
+ Object.defineProperty(this, "first", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: void 0
22
+ });
23
+ Object.defineProperty(this, "second", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ this.first = new streaming_js_1.GeminiToCompletionsConverter();
30
+ this.second = new streaming_js_2.CompletionsToResponsesConverter();
31
+ }
32
+ convertEvent(event) {
33
+ const intermediate = this.first.convertEvent(event);
34
+ return intermediate.flatMap((e) => this.second.convertEvent(e));
35
+ }
36
+ flush() {
37
+ const intermediate = (0, streaming_helpers_js_1.flushConverter)(this.first);
38
+ const secondFromIntermediate = intermediate.flatMap((e) => this.second.convertEvent(e));
39
+ const secondFlush = (0, streaming_helpers_js_1.flushConverter)(this.second);
40
+ return [...secondFromIntermediate, ...secondFlush];
41
+ }
42
+ }
43
+ exports.GeminiToResponsesConverter = GeminiToResponsesConverter;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * Responses → Responses 同格式降级兼容转换。
4
+ *
5
+ * 当 Codex 等客户端向非 OpenAI 的第三方 Responses API 提供商(如火山方舟/豆包)发送请求时,
6
+ * 需要清理 OpenAI 私有扩展、转换消息格式,以确保兼容性。
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.downgradeResponsesRequest = downgradeResponsesRequest;
10
+ /**
11
+ * Responses API 非标准 tool 类型集合。
12
+ * OpenAI 私有扩展(custom、tool_search、web_search 等),
13
+ * 其他 Responses API 提供商不支持这些类型,直接转发会导致 400 错误。
14
+ */
15
+ const NON_STANDARD_TOOL_TYPES = new Set([
16
+ 'custom', 'tool_search', 'web_search', 'file_search', 'code_interpreter',
17
+ ]);
18
+ /**
19
+ * Responses API 降级兼容时需移除的顶层字段集合。
20
+ * 这些字段非所有 Responses API 提供商都支持,开启降级兼容时会被移除以避免 400 错误。
21
+ * - reasoning: { effort } — OpenAI 推理努力程度控制(火山方舟不支持)
22
+ * - text: { verbosity } — OpenAI 响应 verbosity 控制(火山方舟用 text.format 做结构化输出)
23
+ * - prompt_cache_key — OpenAI 提示缓存键
24
+ * - client_metadata — OpenAI 客户端元数据
25
+ * - include — OpenAI 响应包含项(如 reasoning.encrypted_content)
26
+ * - parallel_tool_calls — OpenAI 并行工具调用控制(火山方舟未明确支持)
27
+ */
28
+ const DOWNGRADE_STRIP_FIELDS = new Set([
29
+ 'reasoning', 'text', 'prompt_cache_key', 'client_metadata',
30
+ 'include', 'parallel_tool_calls',
31
+ ]);
32
+ /**
33
+ * 将 input message 的 content 规范化为 ContentItem 数组。
34
+ * 火山方舟等第三方 Responses API 提供商要求 content 为 []*responses.ContentItem 数组格式,
35
+ * 不接受纯字符串。因此需要确保 content 始终为数组:
36
+ * - 字符串 → [{type: "input_text"/"output_text", text: "..."}](根据 role 选择类型)
37
+ * - 数组 → 保持不变
38
+ */
39
+ function normalizeInputContent(content, role) {
40
+ // 已经是数组,保持不变
41
+ if (Array.isArray(content))
42
+ return content;
43
+ // 字符串:根据 role 转为对应的 ContentItem 数组
44
+ if (typeof content === 'string') {
45
+ const type = role === 'assistant' ? 'output_text' : 'input_text';
46
+ return [{ type, text: content }];
47
+ }
48
+ return content;
49
+ }
50
+ /**
51
+ * 对 Responses API 请求体执行降级兼容转换。
52
+ *
53
+ * 处理项:
54
+ * 1. 过滤非标准 tool 类型(custom/tool_search/web_search 等),仅保留 function
55
+ * 2. 移除非标准顶层字段(reasoning/text/include/parallel_tool_calls 等)
56
+ * 3. 转换 input 消息格式:
57
+ * - role: "developer" → role: "system"
58
+ * - content 字符串 → content: [{type:"input_text"/"output_text", text:"..."}]
59
+ * - 补全 status: "completed"
60
+ */
61
+ function downgradeResponsesRequest(body) {
62
+ if (!body || typeof body !== 'object')
63
+ return body;
64
+ let sanitized = body;
65
+ const ensureCopy = () => {
66
+ if (sanitized === body)
67
+ sanitized = Object.assign({}, sanitized);
68
+ return sanitized;
69
+ };
70
+ // 1. 过滤掉非标准 tool 类型,仅保留 function 类型
71
+ if (Array.isArray(body.tools)) {
72
+ const filteredTools = body.tools.filter((t) => !NON_STANDARD_TOOL_TYPES.has(t.type));
73
+ if (filteredTools.length !== body.tools.length) {
74
+ sanitized = Object.assign(Object.assign({}, sanitized), { tools: filteredTools });
75
+ }
76
+ }
77
+ // 2. 移除非标准顶层字段
78
+ for (const field of DOWNGRADE_STRIP_FIELDS) {
79
+ if (field in sanitized) {
80
+ ensureCopy();
81
+ delete sanitized[field];
82
+ }
83
+ }
84
+ // 3. 转换 input 消息格式
85
+ if (Array.isArray(sanitized.input)) {
86
+ let patched = false;
87
+ const patchedInput = sanitized.input.map((item) => {
88
+ if (!item || typeof item !== 'object')
89
+ return item;
90
+ let modified = Object.assign({}, item);
91
+ // 3a. developer → system
92
+ if (modified.role === 'developer') {
93
+ modified.role = 'system';
94
+ patched = true;
95
+ }
96
+ // 3b. 规范化 content 为 ContentItem 数组
97
+ const normalized = normalizeInputContent(modified.content, modified.role);
98
+ if (normalized !== modified.content) {
99
+ modified.content = normalized;
100
+ patched = true;
101
+ }
102
+ // 3c. 补全 status 字段
103
+ if (modified.type === 'message' && !('status' in modified)) {
104
+ modified.status = 'completed';
105
+ patched = true;
106
+ }
107
+ return modified;
108
+ });
109
+ if (patched) {
110
+ ensureCopy();
111
+ sanitized.input = patchedInput;
112
+ }
113
+ }
114
+ return sanitized;
115
+ }
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ /**
3
+ * SSE streaming pipeline for the conversion system.
4
+ *
5
+ * Wraps the conversion StreamConverter in an async generator pipeline
6
+ * that handles SSE parsing, conversion, and serialization.
7
+ */
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
18
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
19
+ var m = o[Symbol.asyncIterator], i;
20
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
21
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
22
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
23
+ };
24
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
25
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
26
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
27
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
28
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
29
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
30
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
31
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
32
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
33
+ function fulfill(value) { resume("next", value); }
34
+ function reject(value) { resume("throw", value); }
35
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.SSEEventParser = void 0;
39
+ exports.serializeSSE = serializeSSE;
40
+ exports.createStreamPipeline = createStreamPipeline;
41
+ const stream_1 = require("stream");
42
+ const index_js_1 = require("./index.js");
43
+ /**
44
+ * Serialize an SSEEvent to wire format.
45
+ */
46
+ function serializeSSE(event) {
47
+ let result = '';
48
+ if (event.event)
49
+ result += `event: ${event.event}\n`;
50
+ if (event.id)
51
+ result += `id: ${event.id}\n`;
52
+ const dataStr = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
53
+ result += `data: ${dataStr}\n\n`;
54
+ return result;
55
+ }
56
+ /**
57
+ * Lightweight SSE event parser.
58
+ */
59
+ class SSEEventParser {
60
+ constructor() {
61
+ Object.defineProperty(this, "buffer", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: ''
66
+ });
67
+ Object.defineProperty(this, "currentEvent", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: {}
72
+ });
73
+ }
74
+ pushChunk(chunk) {
75
+ this.buffer += chunk.toString();
76
+ const lines = this.buffer.split('\n');
77
+ this.buffer = lines.pop() || '';
78
+ return this.consumeLines(lines);
79
+ }
80
+ flush() {
81
+ if (!this.buffer) {
82
+ return this.finishCurrentEvent();
83
+ }
84
+ const lines = this.buffer.split('\n');
85
+ this.buffer = '';
86
+ const events = this.consumeLines(lines);
87
+ return events.concat(this.finishCurrentEvent());
88
+ }
89
+ consumeLines(lines) {
90
+ const events = [];
91
+ for (const rawLine of lines) {
92
+ const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine;
93
+ if (line === '') {
94
+ events.push(...this.finishCurrentEvent());
95
+ }
96
+ else if (line.startsWith('event: ')) {
97
+ this.currentEvent.event = line.substring(7);
98
+ }
99
+ else if (line.startsWith('id: ')) {
100
+ this.currentEvent.id = line.substring(4);
101
+ }
102
+ else if (line.startsWith('data: ')) {
103
+ this.currentEvent.data = (this.currentEvent.data || '') + line.substring(6);
104
+ }
105
+ else if (line.startsWith('data:')) {
106
+ this.currentEvent.data = (this.currentEvent.data || '') + line.substring(5);
107
+ }
108
+ }
109
+ return events;
110
+ }
111
+ finishCurrentEvent() {
112
+ if (this.currentEvent.data === undefined) {
113
+ this.currentEvent = {};
114
+ return [];
115
+ }
116
+ const event = this.currentEvent;
117
+ this.currentEvent = {};
118
+ return [event];
119
+ }
120
+ }
121
+ exports.SSEEventParser = SSEEventParser;
122
+ /**
123
+ * Node.js Transform stream that parses SSE bytes into SSEEvent objects.
124
+ */
125
+ class SSEParserTransform extends stream_1.Transform {
126
+ constructor() {
127
+ super({ objectMode: true });
128
+ Object.defineProperty(this, "parser", {
129
+ enumerable: true,
130
+ configurable: true,
131
+ writable: true,
132
+ value: new SSEEventParser()
133
+ });
134
+ }
135
+ _transform(chunk, _encoding, callback) {
136
+ const events = this.parser.pushChunk(chunk);
137
+ for (const event of events) {
138
+ this.push(event);
139
+ }
140
+ callback();
141
+ }
142
+ _flush(callback) {
143
+ const events = this.parser.flush();
144
+ for (const event of events) {
145
+ this.push(event);
146
+ }
147
+ callback();
148
+ }
149
+ }
150
+ /**
151
+ * Convert a Web ReadableStream to a Node.js Readable.
152
+ */
153
+ function toNodeReadable(stream) {
154
+ if (stream instanceof ReadableStream) {
155
+ const reader = stream.getReader();
156
+ return new stream_1.Readable({
157
+ read() {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ try {
160
+ const { done, value } = yield reader.read();
161
+ if (done)
162
+ this.push(null);
163
+ else
164
+ this.push(value);
165
+ }
166
+ catch (e) {
167
+ this.destroy(e);
168
+ }
169
+ });
170
+ },
171
+ });
172
+ }
173
+ return stream;
174
+ }
175
+ /**
176
+ * Check if the format pair should use raw passthrough (no conversion needed).
177
+ */
178
+ function shouldPassthroughRaw(fromFormat, toFormat) {
179
+ return fromFormat === toFormat;
180
+ }
181
+ /**
182
+ * Creates a streaming pipeline that converts SSE events from one format to another.
183
+ *
184
+ * @param upstreamBody - The upstream response body stream
185
+ * @param fromFormat - The upstream format (source)
186
+ * @param toFormat - The client format (target)
187
+ * @param onEvent - Optional callback for monitoring parsed events (e.g., usage tracking)
188
+ * @returns AsyncGenerator yielding SSE-formatted string chunks
189
+ */
190
+ function createStreamPipeline(upstreamBody, fromFormat, toFormat, onEvent) {
191
+ return __asyncGenerator(this, arguments, function* createStreamPipeline_1() {
192
+ var _a, e_1, _b, _c;
193
+ const nodeStream = toNodeReadable(upstreamBody);
194
+ // Raw passthrough: same format, no conversion needed
195
+ if (shouldPassthroughRaw(fromFormat, toFormat)) {
196
+ const parser = onEvent ? new SSEEventParser() : null;
197
+ try {
198
+ for (var _d = true, _e = __asyncValues(nodeStream), _f; _f = yield __await(_e.next()), _a = _f.done, !_a; _d = true) {
199
+ _c = _f.value;
200
+ _d = false;
201
+ const chunk = _c;
202
+ if (parser) {
203
+ const parsedEvents = parser.pushChunk(typeof chunk === 'string' ? chunk : chunk.toString());
204
+ for (const event of parsedEvents) {
205
+ try {
206
+ const parsed = event.data ? JSON.parse(event.data) : null;
207
+ onEvent(parsed);
208
+ }
209
+ catch ( /* ignore */_g) { /* ignore */ }
210
+ }
211
+ }
212
+ yield yield __await(chunk);
213
+ }
214
+ }
215
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
216
+ finally {
217
+ try {
218
+ if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
219
+ }
220
+ finally { if (e_1) throw e_1.error; }
221
+ }
222
+ if (parser) {
223
+ for (const event of parser.flush()) {
224
+ try {
225
+ const parsed = event.data ? JSON.parse(event.data) : null;
226
+ onEvent(parsed);
227
+ }
228
+ catch ( /* ignore */_h) { /* ignore */ }
229
+ }
230
+ }
231
+ return yield __await(void 0);
232
+ }
233
+ // Conversion path: parse SSE → convert → serialize
234
+ const parser = new SSEParserTransform();
235
+ const eventStream = nodeStream.pipe(parser);
236
+ const eventQueue = [];
237
+ let resolveEvent = null;
238
+ let done = false;
239
+ eventStream.on('data', (event) => {
240
+ if (resolveEvent) {
241
+ resolveEvent(event);
242
+ resolveEvent = null;
243
+ }
244
+ else {
245
+ eventQueue.push(event);
246
+ }
247
+ });
248
+ eventStream.on('end', () => {
249
+ done = true;
250
+ if (resolveEvent) {
251
+ resolveEvent(null);
252
+ resolveEvent = null;
253
+ }
254
+ });
255
+ eventStream.on('error', () => {
256
+ done = true;
257
+ if (resolveEvent) {
258
+ resolveEvent(null);
259
+ resolveEvent = null;
260
+ }
261
+ });
262
+ const getNextEvent = () => {
263
+ if (eventQueue.length > 0)
264
+ return Promise.resolve(eventQueue.shift());
265
+ if (done)
266
+ return Promise.resolve(null);
267
+ return new Promise((resolve) => {
268
+ resolveEvent = resolve;
269
+ });
270
+ };
271
+ const converter = (0, index_js_1.createStreamConverter)({ fromFormat, toFormat });
272
+ while (true) {
273
+ const event = yield __await(getNextEvent());
274
+ if (!event)
275
+ break;
276
+ if (onEvent) {
277
+ try {
278
+ const parsed = event.data ? JSON.parse(event.data) : null;
279
+ onEvent(parsed);
280
+ }
281
+ catch ( /* ignore */_j) { /* ignore */ }
282
+ }
283
+ const convertedEvents = converter.convertEvent(event);
284
+ for (const converted of convertedEvents) {
285
+ yield yield __await(serializeSSE(converted));
286
+ }
287
+ }
288
+ // Flush any remaining state from the converter
289
+ if (converter.flush) {
290
+ const finalEvents = converter.flush();
291
+ for (const converted of finalEvents) {
292
+ yield yield __await(serializeSSE(converted));
293
+ }
294
+ }
295
+ });
296
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * StreamConverterAdapter — 将 conversions StreamConverter 桥接为 Node.js Transform 流。
4
+ *
5
+ * 新系统的 StreamConverter 是纯对象接口(convertEvent/flush),
6
+ * 而代理管道使用 Node.js Transform 流通过 stream.pipeline() 串联。
7
+ * 此 adapter 在两者之间做透明桥接。
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.StreamConverterAdapter = void 0;
11
+ const stream_1 = require("stream");
12
+ class StreamConverterAdapter extends stream_1.Transform {
13
+ constructor(converter) {
14
+ super({ objectMode: true });
15
+ Object.defineProperty(this, "converter", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: converter
20
+ });
21
+ }
22
+ _transform(event, _encoding, callback) {
23
+ try {
24
+ const convertedEvents = this.converter.convertEvent(event);
25
+ for (const converted of convertedEvents) {
26
+ this.push(converted);
27
+ }
28
+ callback();
29
+ }
30
+ catch (err) {
31
+ callback(err);
32
+ }
33
+ }
34
+ _flush(callback) {
35
+ try {
36
+ if (this.converter.flush) {
37
+ const finalEvents = this.converter.flush();
38
+ for (const converted of finalEvents) {
39
+ this.push(converted);
40
+ }
41
+ }
42
+ callback();
43
+ }
44
+ catch (err) {
45
+ callback(err);
46
+ }
47
+ }
48
+ }
49
+ exports.StreamConverterAdapter = StreamConverterAdapter;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * Thinking parameter mapping across API formats.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.claudeThinkingToReasoningEffort = claudeThinkingToReasoningEffort;
7
+ exports.claudeThinkingToResponsesReasoning = claudeThinkingToResponsesReasoning;
8
+ exports.reasoningEffortToClaudeThinking = reasoningEffortToClaudeThinking;
9
+ exports.isOSeriesModel = isOSeriesModel;
10
+ /** Maps Claude thinking config to reasoning_effort string */
11
+ function claudeThinkingToReasoningEffort(thinking) {
12
+ var _a;
13
+ if (!thinking || thinking.type === 'disabled')
14
+ return null;
15
+ // Priority: output_config.effort
16
+ if ((_a = thinking.output_config) === null || _a === void 0 ? void 0 : _a.effort) {
17
+ return thinking.output_config.effort;
18
+ }
19
+ // Fallback: derive from type + budget_tokens
20
+ if (thinking.type === 'adaptive')
21
+ return 'xhigh';
22
+ if (thinking.budget_tokens !== undefined) {
23
+ if (thinking.budget_tokens < 4000)
24
+ return 'low';
25
+ if (thinking.budget_tokens < 16000)
26
+ return 'medium';
27
+ return 'high';
28
+ }
29
+ return 'medium';
30
+ }
31
+ /** Maps Claude thinking config to Responses API reasoning parameter */
32
+ function claudeThinkingToResponsesReasoning(thinking) {
33
+ const effort = claudeThinkingToReasoningEffort(thinking);
34
+ if (!effort)
35
+ return null;
36
+ return { effort };
37
+ }
38
+ /** Reverse: reasoning_effort string → Claude thinking config */
39
+ function reasoningEffortToClaudeThinking(effort) {
40
+ if (!effort)
41
+ return undefined;
42
+ switch (effort) {
43
+ case 'low':
44
+ return { type: 'enabled', budget_tokens: 2048 };
45
+ case 'medium':
46
+ return { type: 'enabled', budget_tokens: 8192 };
47
+ case 'high':
48
+ return { type: 'enabled', budget_tokens: 32000 };
49
+ case 'xhigh':
50
+ return { type: 'adaptive' };
51
+ default:
52
+ return { type: 'enabled', budget_tokens: 8192 };
53
+ }
54
+ }
55
+ /** Check if a model is an OpenAI o-series reasoning model */
56
+ function isOSeriesModel(model) {
57
+ if (!model)
58
+ return false;
59
+ const lower = model.toLowerCase();
60
+ return /\bo[1-9]\b/.test(lower) || /\bo4\b/.test(lower) || /\bgpt-?5\b/.test(lower);
61
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /**
3
+ * Thinking content mapping across API formats.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.thinkingToReasoningContent = thinkingToReasoningContent;
7
+ exports.reasoningContentToThinking = reasoningContentToThinking;
8
+ exports.reasoningToThinking = reasoningToThinking;
9
+ exports.thinkingToReasoningSummary = thinkingToReasoningSummary;
10
+ exports.fixThinkingHistory = fixThinkingHistory;
11
+ exports.redactedThinkingPlaceholder = redactedThinkingPlaceholder;
12
+ /** Claude thinking text → reasoning_content string */
13
+ function thinkingToReasoningContent(thinking) {
14
+ return thinking;
15
+ }
16
+ /** reasoning_content string → Claude thinking block */
17
+ function reasoningContentToThinking(content) {
18
+ return { type: 'thinking', thinking: content };
19
+ }
20
+ /** Responses API reasoning summary → Claude thinking block */
21
+ function reasoningToThinking(summary) {
22
+ const text = summary
23
+ .filter((s) => s.type === 'summary_text')
24
+ .map((s) => s.text || '')
25
+ .join('');
26
+ return { type: 'thinking', thinking: text || '' };
27
+ }
28
+ /** Claude thinking text → Responses API reasoning summary array */
29
+ function thinkingToReasoningSummary(thinking) {
30
+ return [{ type: 'summary_text', text: thinking }];
31
+ }
32
+ /** Fix history messages: ensure thinking/reasoning_content is present alongside tool use */
33
+ function fixThinkingHistory(messages, format) {
34
+ return messages.map(msg => {
35
+ var _a, _b, _c, _d, _e;
36
+ if (msg.role !== 'assistant')
37
+ return msg;
38
+ const hasToolUse = (format === 'claude' && ((_b = (_a = msg.content) === null || _a === void 0 ? void 0 : _a.some) === null || _b === void 0 ? void 0 : _b.call(_a, (b) => b.type === 'tool_use'))) ||
39
+ (format === 'completions' && (((_c = msg.tool_calls) === null || _c === void 0 ? void 0 : _c.length) > 0));
40
+ if (!hasToolUse)
41
+ return msg;
42
+ if (format === 'claude') {
43
+ const hasThinking = (_e = (_d = msg.content) === null || _d === void 0 ? void 0 : _d.some) === null || _e === void 0 ? void 0 : _e.call(_d, (b) => b.type === 'thinking');
44
+ if (!hasThinking) {
45
+ return Object.assign(Object.assign({}, msg), { content: [{ type: 'thinking', thinking: 'tool call' }, ...(msg.content || [])] });
46
+ }
47
+ }
48
+ else {
49
+ if (!msg.reasoning_content) {
50
+ return Object.assign(Object.assign({}, msg), { reasoning_content: 'tool call' });
51
+ }
52
+ }
53
+ return msg;
54
+ });
55
+ }
56
+ /** Placeholder for redacted thinking blocks */
57
+ function redactedThinkingPlaceholder() {
58
+ return '[redacted thinking]';
59
+ }