coding-tool-x 3.8.1 → 3.8.3
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/CHANGELOG.md +2 -0
- package/dist/web/assets/{Analytics-8dRv1aYW.js → Analytics-COEUXmd4.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BQRXSICO.js → ConfigTemplates-DLLkS53H.js} +1 -1
- package/dist/web/assets/{Home-BtzWhKIa.js → Home-DCEyLzwX.js} +1 -1
- package/dist/web/assets/{PluginManager-BTa-Ygf6.js → PluginManager-Oe1ZHoMO.js} +1 -1
- package/dist/web/assets/{ProjectList-F_cB8ldA.js → ProjectList-CWjuAFof.js} +1 -1
- package/dist/web/assets/{SessionList-LOrbPQvu.js → SessionList-v3ehGg9U.js} +1 -1
- package/dist/web/assets/{SkillManager-BzI_t-yV.js → SkillManager-DUF_qP5S.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Db9RGiz7.js → WorkspaceManager-CjAWBosO.js} +1 -1
- package/dist/web/assets/index-D5hyHPj7.js +2 -0
- package/dist/web/index.html +1 -1
- package/docs/superpowers/plans/2026-08-10-codex-conversion-compatibility.md +251 -0
- package/docs/superpowers/plans/2026-08-11-windows-command-window-hiding.md +90 -0
- package/docs/superpowers/plans/2026-08-13-omp-restart-recovery.md +48 -0
- package/docs/superpowers/specs/2026-08-10-codex-conversion-compatibility-design.md +61 -0
- package/docs/superpowers/specs/2026-08-11-windows-command-window-hiding-design.md +44 -0
- package/docs/superpowers/specs/2026-08-13-omp-restart-recovery-design.md +22 -0
- package/package.json +1 -1
- package/src/commands/daemon.js +3 -12
- package/src/commands/update.js +1 -1
- package/src/index.js +8 -1
- package/src/server/omp-proxy-server.js +9 -6
- package/src/server/opencode-proxy-server.js +55 -1037
- package/src/server/services/claude-wire.js +694 -0
- package/src/server/services/codex-wire.js +219 -0
- package/src/server/services/gemini-wire.js +759 -0
- package/src/server/services/model-detector.js +49 -131
- package/src/server/services/omp-channels.js +23 -4
- package/src/server/services/omp-gateway.js +29 -1
- package/src/server/services/omp-settings-manager.js +27 -3
- package/src/server/services/opencode-gateway-adapters.js +22 -1147
- package/src/server/services/opencode-normalization.js +546 -0
- package/src/server/services/session-history-index.js +1 -1
- package/src/server/services/session-history-worker.js +2 -1
- package/src/server/services/speed-test.js +85 -303
- package/dist/web/assets/index-DIWJmyL6.js +0 -2
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
const https = require('https');
|
|
8
8
|
const http = require('http');
|
|
9
|
-
const crypto = require('crypto');
|
|
10
9
|
const { URL } = require('url');
|
|
11
10
|
const { probeModelAvailability } = require('./model-detector');
|
|
12
11
|
const { getEffectiveApiKey: getClaudeEffectiveApiKey } = require('./channels');
|
|
@@ -14,7 +13,13 @@ const { getEffectiveApiKey: getCodexEffectiveApiKey } = require('./codex-channel
|
|
|
14
13
|
const { getEffectiveApiKey: getGeminiEffectiveApiKey } = require('./gemini-channels');
|
|
15
14
|
const { getEffectiveApiKey: getOpenCodeEffectiveApiKey } = require('./opencode-channels');
|
|
16
15
|
const { getEffectiveApiKey: getOmpEffectiveApiKey } = require('./omp-channels');
|
|
17
|
-
const {
|
|
16
|
+
const { createCodexRequest } = require('./codex-wire');
|
|
17
|
+
const { createClaudeRequest, buildClaudeTargetUrl } = require('./claude-wire');
|
|
18
|
+
const {
|
|
19
|
+
createGeminiRequest,
|
|
20
|
+
buildGeminiTargetUrl,
|
|
21
|
+
shouldUseGeminiCliFormat
|
|
22
|
+
} = require('./gemini-wire');
|
|
18
23
|
|
|
19
24
|
// 测试结果缓存
|
|
20
25
|
const testResultsCache = new Map();
|
|
@@ -23,19 +28,7 @@ const testResultsCache = new Map();
|
|
|
23
28
|
const DEFAULT_TIMEOUT = 15000;
|
|
24
29
|
const MIN_TIMEOUT = 5000;
|
|
25
30
|
const MAX_TIMEOUT = 60000;
|
|
26
|
-
const CLAUDE_CODE_USER_AGENT = 'claude-cli/2.1.59 (external, cli)';
|
|
27
|
-
const CLAUDE_MESSAGES_BETA_FLAGS = Object.freeze([
|
|
28
|
-
'claude-code-20250219',
|
|
29
|
-
'interleaved-thinking-2025-05-14',
|
|
30
|
-
'prompt-caching-scope-2026-01-05',
|
|
31
|
-
'effort-2025-11-24'
|
|
32
|
-
]);
|
|
33
|
-
const CLAUDE_ADVANCED_TOOL_USE_BETA = 'advanced-tool-use-2025-11-20';
|
|
34
31
|
const ROUTE_OR_METHOD_MISMATCH_STATUS = new Set([404, 405, 501]);
|
|
35
|
-
const CLAUDE_USER_ID_ACCOUNT_RE = /^user_([0-9a-f]{64})_account__session_[a-z0-9._-]+$/i;
|
|
36
|
-
const CLAUDE_USER_ID_FULL_RE = /^user_[0-9a-f]{64}_account__session_[a-z0-9._-]+$/i;
|
|
37
|
-
let cachedClaudeAccountId = '';
|
|
38
|
-
let cachedClaudeUserId = '';
|
|
39
32
|
|
|
40
33
|
/**
|
|
41
34
|
* 规范化超时时间
|
|
@@ -117,200 +110,23 @@ function resolveEffectiveApiKey(channel, channelType) {
|
|
|
117
110
|
}
|
|
118
111
|
}
|
|
119
112
|
|
|
120
|
-
function
|
|
121
|
-
|
|
122
|
-
case 'darwin':
|
|
123
|
-
return 'MacOS';
|
|
124
|
-
case 'win32':
|
|
125
|
-
return 'Windows';
|
|
126
|
-
case 'linux':
|
|
127
|
-
return 'Linux';
|
|
128
|
-
default:
|
|
129
|
-
return `other::${process.platform}`;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function mapStainlessArch() {
|
|
134
|
-
switch (process.arch) {
|
|
135
|
-
case 'x64':
|
|
136
|
-
return 'x64';
|
|
137
|
-
case 'arm64':
|
|
138
|
-
return 'arm64';
|
|
139
|
-
case 'ia32':
|
|
140
|
-
return 'x86';
|
|
141
|
-
default:
|
|
142
|
-
return `other::${process.arch}`;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function buildClaudeBetaHeader(options = {}) {
|
|
147
|
-
const hasTools = !!options.hasTools;
|
|
148
|
-
const betaFlags = [...CLAUDE_MESSAGES_BETA_FLAGS];
|
|
149
|
-
if (hasTools) {
|
|
150
|
-
betaFlags.push(CLAUDE_ADVANCED_TOOL_USE_BETA);
|
|
151
|
-
}
|
|
152
|
-
return betaFlags.join(',');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function buildClaudeRequestHeaders(apiKey, options = {}) {
|
|
156
|
-
return {
|
|
157
|
-
'x-api-key': apiKey || '',
|
|
158
|
-
authorization: `Bearer ${apiKey || ''}`,
|
|
159
|
-
'anthropic-version': '2023-06-01',
|
|
160
|
-
'anthropic-beta': buildClaudeBetaHeader(options),
|
|
161
|
-
'anthropic-dangerous-direct-browser-access': 'true',
|
|
162
|
-
'x-app': 'cli',
|
|
163
|
-
'x-stainless-retry-count': '0',
|
|
164
|
-
'x-stainless-timeout': '600',
|
|
165
|
-
'x-stainless-runtime-version': process.version,
|
|
166
|
-
'x-stainless-package-version': '0.74.0',
|
|
167
|
-
'x-stainless-lang': 'js',
|
|
168
|
-
'x-stainless-runtime': 'node',
|
|
169
|
-
'x-stainless-arch': mapStainlessArch(),
|
|
170
|
-
'x-stainless-os': mapStainlessOs(),
|
|
171
|
-
accept: 'application/json',
|
|
172
|
-
'accept-encoding': 'gzip, deflate',
|
|
173
|
-
'accept-language': '*',
|
|
174
|
-
'sec-fetch-mode': 'cors',
|
|
175
|
-
connection: 'keep-alive',
|
|
176
|
-
'content-type': 'application/json',
|
|
177
|
-
'user-agent': CLAUDE_CODE_USER_AGENT
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function resolveClaudeAccountIdFromUserId(userId = '') {
|
|
182
|
-
const value = normalizeNonEmptyString(userId);
|
|
183
|
-
if (!value) return '';
|
|
184
|
-
const matched = value.match(CLAUDE_USER_ID_ACCOUNT_RE);
|
|
185
|
-
return matched ? matched[1].toLowerCase() : '';
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function resolveClaudeAccountIdFromLogs() {
|
|
189
|
-
try {
|
|
190
|
-
const template = loadClaudeRequestTemplate();
|
|
191
|
-
const userId = template?.userId || '';
|
|
192
|
-
const accountId = resolveClaudeAccountIdFromUserId(userId);
|
|
193
|
-
return (accountId && accountId !== '0'.repeat(64)) ? accountId : '';
|
|
194
|
-
} catch {
|
|
195
|
-
return '';
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function resolveClaudeUserIdFromLogs() {
|
|
113
|
+
function buildRequestPathFromTargetUrl(targetUrl) {
|
|
114
|
+
if (!targetUrl) return '';
|
|
200
115
|
try {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
if (!userId || !CLAUDE_USER_ID_FULL_RE.test(userId)) return '';
|
|
204
|
-
const accountId = resolveClaudeAccountIdFromUserId(userId);
|
|
205
|
-
if (!accountId || accountId === '0'.repeat(64)) return '';
|
|
206
|
-
return userId;
|
|
116
|
+
const parsed = new URL(targetUrl);
|
|
117
|
+
return parsed.pathname + parsed.search;
|
|
207
118
|
} catch {
|
|
208
119
|
return '';
|
|
209
120
|
}
|
|
210
121
|
}
|
|
211
122
|
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (cachedClaudeUserId) {
|
|
218
|
-
return cachedClaudeUserId;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const envUserId = normalizeNonEmptyString(
|
|
222
|
-
process.env.OPENCODE_CLAUDE_USER_ID || process.env.CLAUDE_CODE_USER_ID
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (envUserId && CLAUDE_USER_ID_FULL_RE.test(envUserId)) {
|
|
226
|
-
cachedClaudeUserId = envUserId;
|
|
227
|
-
return cachedClaudeUserId;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const fromLogs = resolveClaudeUserIdFromLogs();
|
|
231
|
-
if (fromLogs) {
|
|
232
|
-
cachedClaudeUserId = fromLogs;
|
|
233
|
-
return cachedClaudeUserId;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return '';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function resolveClaudeAccountId() {
|
|
240
|
-
if (cachedClaudeAccountId) {
|
|
241
|
-
return cachedClaudeAccountId;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const envAccountId = String(
|
|
245
|
-
process.env.OPENCODE_CLAUDE_ACCOUNT_ID || process.env.CLAUDE_CODE_ACCOUNT_ID || ''
|
|
246
|
-
).trim().toLowerCase();
|
|
247
|
-
|
|
248
|
-
if (/^[0-9a-f]{64}$/.test(envAccountId)) {
|
|
249
|
-
cachedClaudeAccountId = envAccountId;
|
|
250
|
-
return cachedClaudeAccountId;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const fromLogs = resolveClaudeAccountIdFromLogs();
|
|
254
|
-
if (fromLogs) {
|
|
255
|
-
cachedClaudeAccountId = fromLogs;
|
|
256
|
-
return cachedClaudeAccountId;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
cachedClaudeAccountId = '0'.repeat(64);
|
|
260
|
-
return cachedClaudeAccountId;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function buildClaudeCodeUserId() {
|
|
264
|
-
const preferredUserId = resolveClaudePreferredUserId();
|
|
265
|
-
if (preferredUserId) {
|
|
266
|
-
return preferredUserId;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const accountId = resolveClaudeAccountId();
|
|
270
|
-
const sessionId = typeof crypto.randomUUID === 'function'
|
|
271
|
-
? crypto.randomUUID()
|
|
272
|
-
: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
273
|
-
return `user_${accountId}_account__session_${sessionId}`;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
function buildGeminiNativeGeneratePath(parsedUrl, model) {
|
|
278
|
-
let pathname = parsedUrl.pathname.replace(/\/+$/, '');
|
|
279
|
-
const modelsIndex = pathname.indexOf('/models');
|
|
280
|
-
if (modelsIndex >= 0) {
|
|
281
|
-
pathname = pathname.slice(0, modelsIndex);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
let apiBasePath;
|
|
285
|
-
if (!pathname || pathname === '/') {
|
|
286
|
-
apiBasePath = '/v1beta';
|
|
287
|
-
} else if (pathname.endsWith('/v1beta') || pathname.endsWith('/v1')) {
|
|
288
|
-
apiBasePath = pathname;
|
|
289
|
-
} else {
|
|
290
|
-
apiBasePath = `${pathname}/v1beta`;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return `${apiBasePath}/models/${encodeURIComponent(model)}:generateContent`;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function buildGeminiCliGeneratePath(parsedUrl) {
|
|
297
|
-
let pathname = parsedUrl.pathname.replace(/\/+$/, '');
|
|
298
|
-
if (!pathname || pathname === '/') {
|
|
299
|
-
return '/v1internal:generateContent';
|
|
123
|
+
function resolveGeminiWireMode(channel, baseUrl) {
|
|
124
|
+
const providerApi = String(channel?.providerApi || channel?.wireApi || '').trim().toLowerCase();
|
|
125
|
+
if (providerApi === 'google-gemini-cli') return { useCli: true, allowFallback: false };
|
|
126
|
+
if (providerApi === 'google-generative-ai' || providerApi === 'google-vertex') {
|
|
127
|
+
return { useCli: false, allowFallback: false };
|
|
300
128
|
}
|
|
301
|
-
|
|
302
|
-
return pathname.replace(/:streamGenerateContent$/, ':generateContent');
|
|
303
|
-
}
|
|
304
|
-
if (pathname.endsWith(':generateContent')) {
|
|
305
|
-
return pathname;
|
|
306
|
-
}
|
|
307
|
-
if (pathname.endsWith('/v1internal')) {
|
|
308
|
-
return `${pathname}:generateContent`;
|
|
309
|
-
}
|
|
310
|
-
if (pathname.endsWith('/v1')) {
|
|
311
|
-
return '/v1internal:generateContent';
|
|
312
|
-
}
|
|
313
|
-
return `${pathname}/v1internal:generateContent`;
|
|
129
|
+
return { useCli: shouldUseGeminiCliFormat(baseUrl), allowFallback: true };
|
|
314
130
|
}
|
|
315
131
|
|
|
316
132
|
function buildCodexResponsesPath(parsedUrl) {
|
|
@@ -327,24 +143,6 @@ function buildCodexResponsesPath(parsedUrl) {
|
|
|
327
143
|
return `${pathname}/responses`;
|
|
328
144
|
}
|
|
329
145
|
|
|
330
|
-
function shouldUseGeminiCliFormat(parsedUrl) {
|
|
331
|
-
const host = String(parsedUrl.hostname || '').toLowerCase();
|
|
332
|
-
const pathname = parsedUrl.pathname.replace(/\/+$/, '');
|
|
333
|
-
|
|
334
|
-
if (pathname.includes('/v1internal') || pathname.endsWith(':generateContent') || pathname.endsWith(':streamGenerateContent')) {
|
|
335
|
-
return true;
|
|
336
|
-
}
|
|
337
|
-
if (pathname.includes('/v1beta') || pathname.includes('/models/')) {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
if (host.includes('cloudcode-pa.googleapis.com')) {
|
|
341
|
-
return true;
|
|
342
|
-
}
|
|
343
|
-
if (!pathname || pathname === '/') {
|
|
344
|
-
return !host.includes('generativelanguage.googleapis.com') && !host.includes('aiplatform.googleapis.com');
|
|
345
|
-
}
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
146
|
|
|
349
147
|
function extractJsonPayloads(responseData) {
|
|
350
148
|
const payloads = [];
|
|
@@ -660,127 +458,111 @@ async function testAPIFunctionality(baseUrl, apiKey, timeout, channelType = 'cla
|
|
|
660
458
|
}
|
|
661
459
|
|
|
662
460
|
if (channelType === 'claude') {
|
|
663
|
-
// Anthropic Messages API - 模拟 Claude Code 请求格式
|
|
664
|
-
let aompPath = parsedUrl.pathname.replace(/\/$/, '');
|
|
665
|
-
if (!aompPath.endsWith('/messages')) {
|
|
666
|
-
aompPath = aompPath + (aompPath.endsWith('/v1') ? '/messages' : '/v1/messages');
|
|
667
|
-
}
|
|
668
|
-
aompPath += '?beta=true';
|
|
669
|
-
|
|
670
461
|
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'claude-sonnet-4-20250514';
|
|
671
|
-
const sessionId = typeof crypto.randomUUID === 'function'
|
|
672
|
-
? crypto.randomUUID()
|
|
673
|
-
: `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
674
|
-
const userId = buildClaudeCodeUserId() || `user_${'0'.repeat(64)}_account__session_${sessionId}`;
|
|
675
|
-
const template = resolveClaudeRequestTemplate();
|
|
676
|
-
const systemBlocks = template.system;
|
|
677
|
-
const toolsToUse = template.tools;
|
|
678
462
|
const requestPayload = {
|
|
679
463
|
model: testModel,
|
|
680
464
|
max_tokens: 10,
|
|
681
465
|
temperature: 1,
|
|
682
|
-
|
|
683
|
-
messages: [{ role: 'user', content: [{ type: 'text', text: 'Hi' }] }],
|
|
684
|
-
system: systemBlocks,
|
|
685
|
-
metadata: { user_id: userId },
|
|
686
|
-
tools: toolsToUse
|
|
466
|
+
messages: [{ role: 'user', content: [{ type: 'text', text: 'Hi' }] }]
|
|
687
467
|
};
|
|
468
|
+
const primaryConverted = createClaudeRequest('/v1/messages', requestPayload, {
|
|
469
|
+
apiKey,
|
|
470
|
+
baseUrl,
|
|
471
|
+
fallbackModel: testModel,
|
|
472
|
+
stream: true
|
|
473
|
+
});
|
|
474
|
+
const fallbackConverted = createClaudeRequest('/v1/messages', requestPayload, {
|
|
475
|
+
apiKey,
|
|
476
|
+
baseUrl,
|
|
477
|
+
fallbackModel: testModel,
|
|
478
|
+
stream: false
|
|
479
|
+
});
|
|
480
|
+
const aompPath = buildRequestPathFromTargetUrl(buildClaudeTargetUrl(baseUrl));
|
|
688
481
|
primaryRequestConfig = {
|
|
689
482
|
aompPath,
|
|
690
|
-
requestBody: JSON.stringify(
|
|
691
|
-
headers:
|
|
483
|
+
requestBody: JSON.stringify(primaryConverted.body),
|
|
484
|
+
headers: primaryConverted.headers,
|
|
692
485
|
isStreamingResponse: true
|
|
693
486
|
};
|
|
694
|
-
// Fallback: non-streaming for gateways that don't support SSE
|
|
695
487
|
fallbackRequestConfig = {
|
|
696
488
|
aompPath,
|
|
697
|
-
requestBody: JSON.stringify(
|
|
698
|
-
headers:
|
|
489
|
+
requestBody: JSON.stringify(fallbackConverted.body),
|
|
490
|
+
headers: fallbackConverted.headers,
|
|
699
491
|
isStreamingResponse: false
|
|
700
492
|
};
|
|
701
493
|
} else if (channelType === 'codex') {
|
|
702
494
|
const aompPath = buildCodexResponsesPath(parsedUrl);
|
|
703
|
-
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'gpt-5
|
|
495
|
+
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'gpt-5.5';
|
|
704
496
|
const codexSessionId = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
705
|
-
|
|
706
|
-
const baseBody = {
|
|
497
|
+
const converted = createCodexRequest({
|
|
707
498
|
model: testModel,
|
|
708
499
|
instructions: 'You are Codex.',
|
|
709
500
|
input: [{ type: 'message', role: 'user', content: [{ type: 'input_text', text: 'ping' }] }],
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
501
|
+
prompt_cache_key: codexSessionId,
|
|
502
|
+
stream: false,
|
|
503
|
+
store: false
|
|
504
|
+
}, {
|
|
505
|
+
apiKey,
|
|
506
|
+
sessionId: codexSessionId
|
|
507
|
+
});
|
|
713
508
|
|
|
714
509
|
primaryRequestConfig = {
|
|
715
510
|
aompPath,
|
|
716
|
-
requestBody: JSON.stringify(
|
|
717
|
-
headers:
|
|
718
|
-
'Authorization': `Bearer ${apiKey || ''}`,
|
|
719
|
-
'Accept': 'application/json',
|
|
720
|
-
'Connection': 'Keep-Alive',
|
|
721
|
-
'Version': '0.101.0',
|
|
722
|
-
'Session_id': codexSessionId,
|
|
723
|
-
'Originator': 'codex_cli_rs',
|
|
724
|
-
'Content-Type': 'application/json',
|
|
725
|
-
'User-Agent': 'codex_cli_rs/0.101.0 (Mac OS 26.0.1; arm64) Apple_Terminal/464',
|
|
726
|
-
'openai-beta': 'responses=experimental'
|
|
727
|
-
},
|
|
728
|
-
isStreamingResponse: false
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
fallbackRequestConfig = {
|
|
732
|
-
aompPath,
|
|
733
|
-
requestBody: JSON.stringify({ ...baseBody, stream: true }),
|
|
734
|
-
headers: {
|
|
735
|
-
...primaryRequestConfig.headers,
|
|
736
|
-
'Accept': 'text/event-stream'
|
|
737
|
-
},
|
|
511
|
+
requestBody: JSON.stringify(converted.body),
|
|
512
|
+
headers: converted.headers,
|
|
738
513
|
isStreamingResponse: true
|
|
739
514
|
};
|
|
515
|
+
fallbackRequestConfig = primaryRequestConfig;
|
|
740
516
|
} else if (channelType === 'gemini') {
|
|
741
517
|
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'gemini-2.5-pro';
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
518
|
+
const minimalPayload = {
|
|
519
|
+
model: testModel,
|
|
520
|
+
max_output_tokens: 1,
|
|
521
|
+
temperature: 0,
|
|
522
|
+
input: [{ type: 'message', role: 'user', content: [{ type: 'input_text', text: 'ping' }] }]
|
|
523
|
+
};
|
|
524
|
+
const wireMode = resolveGeminiWireMode(channel, baseUrl);
|
|
525
|
+
const createGeminiRequestConfig = (useCli) => {
|
|
526
|
+
const converted = createGeminiRequest('/v1/responses', minimalPayload, {
|
|
527
|
+
apiKey,
|
|
528
|
+
fallbackModel: testModel,
|
|
529
|
+
stream: false,
|
|
530
|
+
useCli
|
|
531
|
+
});
|
|
532
|
+
return {
|
|
533
|
+
aompPath: buildRequestPathFromTargetUrl(
|
|
534
|
+
buildGeminiTargetUrl(baseUrl, converted.model, apiKey, { stream: false, useCli })
|
|
535
|
+
),
|
|
536
|
+
requestBody: JSON.stringify(converted.body),
|
|
537
|
+
headers: converted.headers,
|
|
538
|
+
isStreamingResponse: false
|
|
539
|
+
};
|
|
764
540
|
};
|
|
765
541
|
|
|
766
|
-
|
|
767
|
-
|
|
542
|
+
primaryRequestConfig = createGeminiRequestConfig(wireMode.useCli);
|
|
543
|
+
fallbackRequestConfig = wireMode.allowFallback
|
|
544
|
+
? createGeminiRequestConfig(!wireMode.useCli)
|
|
545
|
+
: null;
|
|
546
|
+
} else if (channelType === 'openai_compatible' || channelType === 'opencode') {
|
|
547
|
+
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'gpt-4o-mini';
|
|
548
|
+
let aompPath = parsedUrl.pathname.replace(/\/$/, '');
|
|
549
|
+
if (!aompPath.endsWith('/chat/completions')) {
|
|
550
|
+
aompPath = aompPath + (aompPath.endsWith('/v1') ? '/chat/completions' : '/v1/chat/completions');
|
|
551
|
+
}
|
|
552
|
+
primaryRequestConfig = {
|
|
553
|
+
aompPath,
|
|
768
554
|
requestBody: JSON.stringify({
|
|
769
|
-
|
|
770
|
-
|
|
555
|
+
model: testModel,
|
|
556
|
+
max_tokens: 1,
|
|
557
|
+
messages: [{ role: 'user', content: 'Hi' }]
|
|
771
558
|
}),
|
|
772
559
|
headers: {
|
|
773
560
|
'Authorization': `Bearer ${apiKey || ''}`,
|
|
774
|
-
'x-goog-api-key': apiKey || '',
|
|
775
|
-
'Accept': 'application/json',
|
|
776
561
|
'Content-Type': 'application/json',
|
|
777
|
-
'User-Agent': '
|
|
562
|
+
'User-Agent': 'Coding-Tool-SpeedTest/1.0'
|
|
778
563
|
},
|
|
779
564
|
isStreamingResponse: false
|
|
780
565
|
};
|
|
781
|
-
|
|
782
|
-
primaryRequestConfig = useCliFormat ? cliRequestConfig : nativeRequestConfig;
|
|
783
|
-
fallbackRequestConfig = useCliFormat ? nativeRequestConfig : cliRequestConfig;
|
|
784
566
|
} else {
|
|
785
567
|
testModel = modelProbe?.preferredTestModel || normalizeNonEmptyString(model) || 'gpt-4o-mini';
|
|
786
568
|
let aompPath = parsedUrl.pathname.replace(/\/$/, '');
|