@lvce-editor/chat-view 1.14.0 → 1.15.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.
@@ -1036,6 +1036,7 @@ const create$2 = rpcId => {
1036
1036
  };
1037
1037
 
1038
1038
  const {
1039
+ invoke: invoke$1,
1039
1040
  set: set$2
1040
1041
  } = create$2(ExtensionHostWorker);
1041
1042
 
@@ -1048,6 +1049,9 @@ const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
1048
1049
  const command = 'HandleMessagePort.handleMessagePort2';
1049
1050
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
1050
1051
  };
1052
+ const activateByEvent$1 = (event, assetDir, platform) => {
1053
+ return invoke('ExtensionHostManagement.activateByEvent', event, assetDir, platform);
1054
+ };
1051
1055
  const getPreference = async key => {
1052
1056
  return await invoke('Preferences.get', key);
1053
1057
  };
@@ -1224,6 +1228,9 @@ const composePlaceholder = () => {
1224
1228
  const openRouterApiKeyPlaceholder = () => {
1225
1229
  return i18nString('Enter OpenRouter API key');
1226
1230
  };
1231
+ const openApiApiKeyPlaceholder = () => {
1232
+ return i18nString('Enter OpenAI API key');
1233
+ };
1227
1234
  const sendMessage = () => {
1228
1235
  return i18nString('Send message');
1229
1236
  };
@@ -1233,6 +1240,9 @@ const save = () => {
1233
1240
  const getOpenRouterApiKey = () => {
1234
1241
  return i18nString('Get API Key');
1235
1242
  };
1243
+ const getOpenApiApiKey = () => {
1244
+ return i18nString('Get API Key');
1245
+ };
1236
1246
  const deleteChatSession$1 = () => {
1237
1247
  return i18nString('Delete chat session');
1238
1248
  };
@@ -1325,8 +1335,13 @@ const createDefaultState = () => {
1325
1335
  inputSource: 'script',
1326
1336
  lastSubmittedSessionId: '',
1327
1337
  listItemHeight: 40,
1338
+ mockApiCommandId: '',
1328
1339
  models: getDefaultModels(),
1329
1340
  nextMessageId: 1,
1341
+ openApiApiBaseUrl: 'https://api.openai.com/v1',
1342
+ openApiApiKey: '',
1343
+ openApiApiKeyInput: '',
1344
+ openApiApiKeysSettingsUrl: 'https://platform.openai.com/api-keys',
1330
1345
  openRouterApiBaseUrl: 'https://openrouter.ai/api/v1',
1331
1346
  openRouterApiKey: '',
1332
1347
  openRouterApiKeyInput: '',
@@ -1344,6 +1359,7 @@ const createDefaultState = () => {
1344
1359
  tokensUsed: 0,
1345
1360
  uid: 0,
1346
1361
  usageOverviewEnabled: false,
1362
+ useMockApi: false,
1347
1363
  viewMode: 'list',
1348
1364
  warningCount: 0,
1349
1365
  width: 0,
@@ -2084,14 +2100,44 @@ const deleteSession = async (state, id) => {
2084
2100
  };
2085
2101
  };
2086
2102
 
2103
+ const handleClickOpenApiApiKeySettings = async state => {
2104
+ await openExternal(state.openApiApiKeysSettingsUrl);
2105
+ return state;
2106
+ };
2107
+
2087
2108
  const handleClickOpenRouterApiKeySettings = async state => {
2088
2109
  await openExternal(state.openRouterApiKeysSettingsUrl);
2089
2110
  return state;
2090
2111
  };
2091
2112
 
2113
+ const openApiApiKeyRequiredMessage = 'OpenAI API key is not configured. Enter your OpenAI API key below and click Save.';
2114
+ const openApiRequestFailedMessage = 'OpenAI request failed.';
2092
2115
  const openRouterApiKeyRequiredMessage = 'OpenRouter API key is not configured. Enter your OpenRouter API key below and click Save.';
2093
2116
  const openRouterRequestFailedMessage = 'OpenRouter request failed. Possible reasons:';
2117
+ const openRouterTooManyRequestsMessage = 'OpenRouter rate limit reached (429). Please try again soon. Helpful tips:';
2094
2118
  const openRouterRequestFailureReasons = ['ContentSecurityPolicyViolation: Check DevTools for details.', 'OpenRouter server offline: Check DevTools for details.', 'Check your internet connection.'];
2119
+ const openRouterTooManyRequestsReasons = ['Wait a short time and retry your request.', 'Reduce request frequency to avoid rate limits.', 'Use a different model if this one is saturated.'];
2120
+
2121
+ const activateByEvent = (event, assetDir, platform) => {
2122
+ // @ts-ignore
2123
+ return activateByEvent$1(event, assetDir, platform);
2124
+ };
2125
+
2126
+ const executeProvider = async ({
2127
+ assetDir,
2128
+ event,
2129
+ method,
2130
+ noProviderFoundMessage,
2131
+ params,
2132
+ platform
2133
+ }) => {
2134
+ await activateByEvent(event, assetDir, platform);
2135
+ // @ts-ignore
2136
+ const result = invoke$1(method, ...params);
2137
+ return result;
2138
+ };
2139
+
2140
+ const CommandExecute = 'ExtensionHostCommand.executeCommand';
2095
2141
 
2096
2142
  const delay = async ms => {
2097
2143
  await new Promise(resolve => setTimeout(resolve, ms));
@@ -2102,10 +2148,8 @@ const getMockAiResponse = async userMessage => {
2102
2148
  return `Mock AI response: I received "${userMessage}".`;
2103
2149
  };
2104
2150
 
2105
- const defaultOpenRouterApiBaseUrl = 'https://openrouter.ai/api/v1';
2106
- const getOpenRouterApiEndpoint = openRouterApiBaseUrl => {
2107
- const trimmedBaseUrl = (openRouterApiBaseUrl || defaultOpenRouterApiBaseUrl).replace(/\/+$/, '');
2108
- return `${trimmedBaseUrl}/chat/completions`;
2151
+ const getOpenApiApiEndpoint = openApiApiBaseUrl => {
2152
+ return `${openApiApiBaseUrl}/chat/completions`;
2109
2153
  };
2110
2154
 
2111
2155
  const getTextContent = content => {
@@ -2129,15 +2173,179 @@ const getTextContent = content => {
2129
2173
  return textParts.join('\n');
2130
2174
  };
2131
2175
 
2132
- const getOpenRouterAssistantText = async (userText, modelId, openRouterApiKey, openRouterApiBaseUrl) => {
2176
+ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApiApiBaseUrl) => {
2177
+ let response;
2178
+ try {
2179
+ response = await fetch(getOpenApiApiEndpoint(openApiApiBaseUrl), {
2180
+ body: JSON.stringify({
2181
+ messages: messages.map(message => ({
2182
+ content: message.text,
2183
+ role: message.role
2184
+ })),
2185
+ model: modelId
2186
+ }),
2187
+ headers: {
2188
+ Authorization: `Bearer ${openApiApiKey}`,
2189
+ 'Content-Type': 'application/json'
2190
+ },
2191
+ method: 'POST'
2192
+ });
2193
+ } catch {
2194
+ return {
2195
+ details: 'request-failed',
2196
+ type: 'error'
2197
+ };
2198
+ }
2199
+ if (!response.ok) {
2200
+ return {
2201
+ details: 'http-error',
2202
+ statusCode: response.status,
2203
+ type: 'error'
2204
+ };
2205
+ }
2206
+ let parsed;
2207
+ try {
2208
+ parsed = await response.json();
2209
+ } catch {
2210
+ return {
2211
+ details: 'request-failed',
2212
+ type: 'error'
2213
+ };
2214
+ }
2215
+ if (!parsed || typeof parsed !== 'object') {
2216
+ return {
2217
+ text: '',
2218
+ type: 'success'
2219
+ };
2220
+ }
2221
+ const choices = Reflect.get(parsed, 'choices');
2222
+ if (!Array.isArray(choices)) {
2223
+ return {
2224
+ text: '',
2225
+ type: 'success'
2226
+ };
2227
+ }
2228
+ const firstChoice = choices[0];
2229
+ if (!firstChoice || typeof firstChoice !== 'object') {
2230
+ return {
2231
+ text: '',
2232
+ type: 'success'
2233
+ };
2234
+ }
2235
+ const message = Reflect.get(firstChoice, 'message');
2236
+ if (!message || typeof message !== 'object') {
2237
+ return {
2238
+ text: '',
2239
+ type: 'success'
2240
+ };
2241
+ }
2242
+ const content = Reflect.get(message, 'content');
2243
+ return {
2244
+ text: getTextContent(content),
2245
+ type: 'success'
2246
+ };
2247
+ };
2248
+
2249
+ const getOpenApiModelId = selectedModelId => {
2250
+ const openApiPrefix = 'openapi/';
2251
+ const openAiPrefix = 'openai/';
2252
+ const normalizedModelId = selectedModelId.toLowerCase();
2253
+ if (normalizedModelId.startsWith(openApiPrefix)) {
2254
+ return selectedModelId.slice(openApiPrefix.length);
2255
+ }
2256
+ if (normalizedModelId.startsWith(openAiPrefix)) {
2257
+ return selectedModelId.slice(openAiPrefix.length);
2258
+ }
2259
+ return selectedModelId;
2260
+ };
2261
+
2262
+ const getOpenRouterApiEndpoint = openRouterApiBaseUrl => {
2263
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
2264
+ return `${trimmedBaseUrl}/chat/completions`;
2265
+ };
2266
+
2267
+ const getOpenRouterKeyEndpoint = openRouterApiBaseUrl => {
2268
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
2269
+ return `${trimmedBaseUrl}/auth/key`;
2270
+ };
2271
+
2272
+ const getOpenRouterRaw429Message = async response => {
2273
+ let parsed;
2274
+ try {
2275
+ parsed = await response.json();
2276
+ } catch {
2277
+ return undefined;
2278
+ }
2279
+ if (!parsed || typeof parsed !== 'object') {
2280
+ return undefined;
2281
+ }
2282
+ const error = Reflect.get(parsed, 'error');
2283
+ if (!error || typeof error !== 'object') {
2284
+ return undefined;
2285
+ }
2286
+ const metadata = Reflect.get(error, 'metadata');
2287
+ if (!metadata || typeof metadata !== 'object') {
2288
+ return undefined;
2289
+ }
2290
+ const raw = Reflect.get(metadata, 'raw');
2291
+ if (typeof raw !== 'string' || !raw) {
2292
+ return undefined;
2293
+ }
2294
+ return raw;
2295
+ };
2296
+ const getOpenRouterLimitInfo = async (openRouterApiKey, openRouterApiBaseUrl) => {
2297
+ let response;
2298
+ try {
2299
+ response = await fetch(getOpenRouterKeyEndpoint(openRouterApiBaseUrl), {
2300
+ headers: {
2301
+ Authorization: `Bearer ${openRouterApiKey}`
2302
+ },
2303
+ method: 'GET'
2304
+ });
2305
+ } catch {
2306
+ return undefined;
2307
+ }
2308
+ if (!response.ok) {
2309
+ return undefined;
2310
+ }
2311
+ let parsed;
2312
+ try {
2313
+ parsed = await response.json();
2314
+ } catch {
2315
+ return undefined;
2316
+ }
2317
+ if (!parsed || typeof parsed !== 'object') {
2318
+ return undefined;
2319
+ }
2320
+ const data = Reflect.get(parsed, 'data');
2321
+ if (!data || typeof data !== 'object') {
2322
+ return undefined;
2323
+ }
2324
+ const limitRemaining = Reflect.get(data, 'limit_remaining');
2325
+ const limitReset = Reflect.get(data, 'limit_reset');
2326
+ const usage = Reflect.get(data, 'usage');
2327
+ const usageDaily = Reflect.get(data, 'usage_daily');
2328
+ const normalizedLimitInfo = {
2329
+ limitRemaining: typeof limitRemaining === 'number' || limitRemaining === null ? limitRemaining : undefined,
2330
+ limitReset: typeof limitReset === 'string' || limitReset === null ? limitReset : undefined,
2331
+ usage: typeof usage === 'number' ? usage : undefined,
2332
+ usageDaily: typeof usageDaily === 'number' ? usageDaily : undefined
2333
+ };
2334
+ const hasLimitInfo = normalizedLimitInfo.limitRemaining !== undefined || normalizedLimitInfo.limitReset !== undefined || normalizedLimitInfo.usage !== undefined || normalizedLimitInfo.usageDaily !== undefined;
2335
+ if (!hasLimitInfo) {
2336
+ return undefined;
2337
+ }
2338
+ return normalizedLimitInfo;
2339
+ };
2340
+ const getOpenRouterAssistantText = async (messages, modelId, openRouterApiKey, openRouterApiBaseUrl) => {
2133
2341
  let response;
2134
2342
  try {
2135
2343
  response = await fetch(getOpenRouterApiEndpoint(openRouterApiBaseUrl), {
2136
2344
  body: JSON.stringify({
2137
- messages: [{
2138
- content: userText,
2139
- role: 'user'
2140
- }],
2345
+ messages: messages.map(message => ({
2346
+ content: message.text,
2347
+ role: message.role
2348
+ })),
2141
2349
  model: modelId
2142
2350
  }),
2143
2351
  headers: {
@@ -2147,29 +2355,74 @@ const getOpenRouterAssistantText = async (userText, modelId, openRouterApiKey, o
2147
2355
  method: 'POST'
2148
2356
  });
2149
2357
  } catch {
2150
- throw new Error(openRouterRequestFailedMessage);
2358
+ return {
2359
+ details: 'request-failed',
2360
+ type: 'error'
2361
+ };
2151
2362
  }
2152
2363
  if (!response.ok) {
2153
- throw new Error(`Failed to get OpenRouter response: ${response.status}`);
2364
+ if (response.status === 429) {
2365
+ const retryAfter = response.headers?.get?.('retry-after') ?? null;
2366
+ const rawMessage = await getOpenRouterRaw429Message(response);
2367
+ const limitInfo = await getOpenRouterLimitInfo(openRouterApiKey, openRouterApiBaseUrl);
2368
+ return {
2369
+ details: 'too-many-requests',
2370
+ limitInfo: limitInfo || retryAfter ? {
2371
+ ...limitInfo,
2372
+ retryAfter
2373
+ } : undefined,
2374
+ rawMessage,
2375
+ statusCode: 429,
2376
+ type: 'error'
2377
+ };
2378
+ }
2379
+ return {
2380
+ details: 'http-error',
2381
+ statusCode: response.status,
2382
+ type: 'error'
2383
+ };
2384
+ }
2385
+ let parsed;
2386
+ try {
2387
+ parsed = await response.json();
2388
+ } catch {
2389
+ return {
2390
+ details: 'request-failed',
2391
+ type: 'error'
2392
+ };
2154
2393
  }
2155
- const parsed = await response.json();
2156
2394
  if (!parsed || typeof parsed !== 'object') {
2157
- return '';
2395
+ return {
2396
+ text: '',
2397
+ type: 'success'
2398
+ };
2158
2399
  }
2159
2400
  const choices = Reflect.get(parsed, 'choices');
2160
2401
  if (!Array.isArray(choices)) {
2161
- return '';
2402
+ return {
2403
+ text: '',
2404
+ type: 'success'
2405
+ };
2162
2406
  }
2163
2407
  const firstChoice = choices[0];
2164
2408
  if (!firstChoice || typeof firstChoice !== 'object') {
2165
- return '';
2409
+ return {
2410
+ text: '',
2411
+ type: 'success'
2412
+ };
2166
2413
  }
2167
2414
  const message = Reflect.get(firstChoice, 'message');
2168
2415
  if (!message || typeof message !== 'object') {
2169
- return '';
2416
+ return {
2417
+ text: '',
2418
+ type: 'success'
2419
+ };
2170
2420
  }
2171
2421
  const content = Reflect.get(message, 'content');
2172
- return getTextContent(content);
2422
+ return {
2423
+ text: getTextContent(content),
2424
+ type: 'success'
2425
+ };
2173
2426
  };
2174
2427
 
2175
2428
  /* eslint-disable @cspell/spellchecker */
@@ -2181,6 +2434,16 @@ const getOpenRouterModelId = selectedModelId => {
2181
2434
  return selectedModelId;
2182
2435
  };
2183
2436
 
2437
+ const isOpenApiModel = (selectedModelId, models) => {
2438
+ const selectedModel = models.find(model => model.id === selectedModelId);
2439
+ const normalizedProvider = selectedModel?.provider?.toLowerCase();
2440
+ if (normalizedProvider === 'openapi' || normalizedProvider === 'openai' || normalizedProvider === 'open-ai') {
2441
+ return true;
2442
+ }
2443
+ const normalizedModelId = selectedModelId.toLowerCase();
2444
+ return normalizedModelId.startsWith('openapi/') || normalizedModelId.startsWith('openai/');
2445
+ };
2446
+
2184
2447
  /* eslint-disable @cspell/spellchecker */
2185
2448
 
2186
2449
  const isOpenRouterModel = (selectedModelId, models) => {
@@ -2192,25 +2455,200 @@ const isOpenRouterModel = (selectedModelId, models) => {
2192
2455
  return selectedModelId.toLowerCase().startsWith('openrouter/');
2193
2456
  };
2194
2457
 
2195
- const getAiResponse = async (userText, nextMessageId, selectedModelId, models, openRouterApiKey, openRouterApiBaseUrl) => {
2458
+ const getOpenRouterTooManyRequestsMessage = errorResult => {
2459
+ const details = [];
2460
+ if (errorResult.rawMessage) {
2461
+ details.push(errorResult.rawMessage);
2462
+ }
2463
+ const {
2464
+ limitInfo
2465
+ } = errorResult;
2466
+ if (limitInfo) {
2467
+ if (limitInfo.retryAfter) {
2468
+ details.push(`Retry after: ${limitInfo.retryAfter}.`);
2469
+ }
2470
+ if (limitInfo.limitReset) {
2471
+ details.push(`Limit resets: ${limitInfo.limitReset}.`);
2472
+ }
2473
+ if (limitInfo.limitRemaining === null) {
2474
+ details.push('Credits remaining: unlimited.');
2475
+ } else if (typeof limitInfo.limitRemaining === 'number') {
2476
+ details.push(`Credits remaining: ${limitInfo.limitRemaining}.`);
2477
+ }
2478
+ if (typeof limitInfo.usageDaily === 'number') {
2479
+ details.push(`Credits used today (UTC): ${limitInfo.usageDaily}.`);
2480
+ }
2481
+ if (typeof limitInfo.usage === 'number') {
2482
+ details.push(`Credits used (all time): ${limitInfo.usage}.`);
2483
+ }
2484
+ }
2485
+ if (details.length === 0) {
2486
+ return openRouterTooManyRequestsMessage;
2487
+ }
2488
+ return `${openRouterTooManyRequestsMessage} ${details.join(' ')}`;
2489
+ };
2490
+ const getOpenRouterErrorMessage = errorResult => {
2491
+ switch (errorResult.details) {
2492
+ case 'http-error':
2493
+ case 'request-failed':
2494
+ return openRouterRequestFailedMessage;
2495
+ case 'too-many-requests':
2496
+ return getOpenRouterTooManyRequestsMessage(errorResult);
2497
+ }
2498
+ };
2499
+ const getOpenApiErrorMessage = errorResult => {
2500
+ switch (errorResult.details) {
2501
+ case 'http-error':
2502
+ case 'request-failed':
2503
+ return openApiRequestFailedMessage;
2504
+ }
2505
+ };
2506
+ const normalizeLimitInfo = value => {
2507
+ if (!value || typeof value !== 'object') {
2508
+ return undefined;
2509
+ }
2510
+ const limitRemaining = Reflect.get(value, 'limitRemaining');
2511
+ const limitReset = Reflect.get(value, 'limitReset');
2512
+ const retryAfter = Reflect.get(value, 'retryAfter');
2513
+ const usage = Reflect.get(value, 'usage');
2514
+ const usageDaily = Reflect.get(value, 'usageDaily');
2515
+ const normalized = {
2516
+ limitRemaining: typeof limitRemaining === 'number' || limitRemaining === null ? limitRemaining : undefined,
2517
+ limitReset: typeof limitReset === 'string' || limitReset === null ? limitReset : undefined,
2518
+ retryAfter: typeof retryAfter === 'string' || retryAfter === null ? retryAfter : undefined,
2519
+ usage: typeof usage === 'number' ? usage : undefined,
2520
+ usageDaily: typeof usageDaily === 'number' ? usageDaily : undefined
2521
+ };
2522
+ const hasDetails = normalized.limitRemaining !== undefined || normalized.limitReset !== undefined || normalized.retryAfter !== undefined || normalized.usage !== undefined || normalized.usageDaily !== undefined;
2523
+ return hasDetails ? normalized : undefined;
2524
+ };
2525
+ const normalizeMockResult = value => {
2526
+ if (typeof value === 'string') {
2527
+ return {
2528
+ text: value,
2529
+ type: 'success'
2530
+ };
2531
+ }
2532
+ if (!value || typeof value !== 'object') {
2533
+ return {
2534
+ details: 'request-failed',
2535
+ type: 'error'
2536
+ };
2537
+ }
2538
+ const type = Reflect.get(value, 'type');
2539
+ if (type === 'success') {
2540
+ const text = Reflect.get(value, 'text');
2541
+ if (typeof text === 'string') {
2542
+ return {
2543
+ text,
2544
+ type: 'success'
2545
+ };
2546
+ }
2547
+ return {
2548
+ details: 'request-failed',
2549
+ type: 'error'
2550
+ };
2551
+ }
2552
+ if (type === 'error') {
2553
+ const details = Reflect.get(value, 'details');
2554
+ if (details === 'request-failed' || details === 'too-many-requests' || details === 'http-error') {
2555
+ const rawMessage = Reflect.get(value, 'rawMessage');
2556
+ const statusCode = Reflect.get(value, 'statusCode');
2557
+ return {
2558
+ details,
2559
+ limitInfo: normalizeLimitInfo(Reflect.get(value, 'limitInfo')),
2560
+ rawMessage: typeof rawMessage === 'string' ? rawMessage : undefined,
2561
+ statusCode: typeof statusCode === 'number' ? statusCode : undefined,
2562
+ type: 'error'
2563
+ };
2564
+ }
2565
+ }
2566
+ const text = Reflect.get(value, 'text');
2567
+ if (typeof text === 'string') {
2568
+ return {
2569
+ text,
2570
+ type: 'success'
2571
+ };
2572
+ }
2573
+ return {
2574
+ details: 'request-failed',
2575
+ type: 'error'
2576
+ };
2577
+ };
2578
+ const getMockOpenRouterAssistantText = async (messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform) => {
2579
+ if (!mockApiCommandId) {
2580
+ return {
2581
+ details: 'request-failed',
2582
+ type: 'error'
2583
+ };
2584
+ }
2585
+ try {
2586
+ const result = await executeProvider({
2587
+ assetDir,
2588
+ event: `onCommand:${mockApiCommandId}`,
2589
+ method: CommandExecute,
2590
+ noProviderFoundMessage: 'No mock api command found',
2591
+ params: [mockApiCommandId, {
2592
+ messages,
2593
+ modelId,
2594
+ openRouterApiBaseUrl,
2595
+ openRouterApiKey
2596
+ }],
2597
+ platform
2598
+ });
2599
+ return normalizeMockResult(result);
2600
+ } catch {
2601
+ return {
2602
+ details: 'request-failed',
2603
+ type: 'error'
2604
+ };
2605
+ }
2606
+ };
2607
+ const getAiResponse = async (userText, messages, nextMessageId, selectedModelId, models, openApiApiKey, openApiApiBaseUrl, openRouterApiKey, openRouterApiBaseUrl, useMockApi, mockApiCommandId, assetDir, platform) => {
2196
2608
  let text = '';
2609
+ const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
2197
2610
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
2198
- if (usesOpenRouterModel) {
2199
- if (openRouterApiKey) {
2200
- try {
2201
- text = await getOpenRouterAssistantText(userText, getOpenRouterModelId(selectedModelId), openRouterApiKey, openRouterApiBaseUrl);
2202
- } catch (error) {
2203
- if (error instanceof Error && error.message) {
2204
- text = error.message;
2205
- } else {
2206
- text = openRouterRequestFailedMessage;
2207
- }
2611
+ if (usesOpenApiModel) {
2612
+ if (openApiApiKey) {
2613
+ const result = await getOpenApiAssistantText(messages, getOpenApiModelId(selectedModelId), openApiApiKey, openApiApiBaseUrl);
2614
+ if (result.type === 'success') {
2615
+ const {
2616
+ text: assistantText
2617
+ } = result;
2618
+ text = assistantText;
2619
+ } else {
2620
+ text = getOpenApiErrorMessage(result);
2621
+ }
2622
+ } else {
2623
+ text = openApiApiKeyRequiredMessage;
2624
+ }
2625
+ } else if (usesOpenRouterModel) {
2626
+ const modelId = getOpenRouterModelId(selectedModelId);
2627
+ if (useMockApi) {
2628
+ const result = await getMockOpenRouterAssistantText(messages, modelId, openRouterApiBaseUrl, openRouterApiKey, mockApiCommandId, assetDir, platform);
2629
+ if (result.type === 'success') {
2630
+ const {
2631
+ text: assistantText
2632
+ } = result;
2633
+ text = assistantText;
2634
+ } else {
2635
+ text = getOpenRouterErrorMessage(result);
2636
+ }
2637
+ } else if (openRouterApiKey) {
2638
+ const result = await getOpenRouterAssistantText(messages, modelId, openRouterApiKey, openRouterApiBaseUrl);
2639
+ if (result.type === 'success') {
2640
+ const {
2641
+ text: assistantText
2642
+ } = result;
2643
+ text = assistantText;
2644
+ } else {
2645
+ text = getOpenRouterErrorMessage(result);
2208
2646
  }
2209
2647
  } else {
2210
2648
  text = openRouterApiKeyRequiredMessage;
2211
2649
  }
2212
2650
  }
2213
- if (!text && !usesOpenRouterModel) {
2651
+ if (!text && !usesOpenApiModel && !usesOpenRouterModel) {
2214
2652
  text = await getMockAiResponse(userText);
2215
2653
  }
2216
2654
  const assistantTime = new Date().toLocaleTimeString([], {
@@ -2232,6 +2670,61 @@ const update = async settings => {
2232
2670
  await invoke('Preferences.update', settings);
2233
2671
  };
2234
2672
 
2673
+ const setOpenApiApiKey = async (state, openApiApiKey, persist = true) => {
2674
+ if (persist) {
2675
+ await update({
2676
+ 'secrets.openApiKey': openApiApiKey
2677
+ });
2678
+ }
2679
+ return {
2680
+ ...state,
2681
+ openApiApiKey,
2682
+ openApiApiKeyInput: openApiApiKey
2683
+ };
2684
+ };
2685
+
2686
+ const handleClickSaveOpenApiApiKey = async state => {
2687
+ const {
2688
+ openApiApiKeyInput
2689
+ } = state;
2690
+ const openApiApiKey = openApiApiKeyInput.trim();
2691
+ if (!openApiApiKey) {
2692
+ return state;
2693
+ }
2694
+ const updatedState = await setOpenApiApiKey(state, openApiApiKey);
2695
+ const session = updatedState.sessions.find(item => item.id === updatedState.selectedSessionId);
2696
+ if (!session) {
2697
+ return updatedState;
2698
+ }
2699
+ const lastMessage = session.messages.at(-1);
2700
+ const shouldRetryOpenApi = lastMessage?.role === 'assistant' && lastMessage.text === openApiApiKeyRequiredMessage;
2701
+ if (!shouldRetryOpenApi) {
2702
+ return updatedState;
2703
+ }
2704
+ const previousUserMessage = session.messages.toReversed().find(item => item.role === 'user');
2705
+ if (!previousUserMessage) {
2706
+ return updatedState;
2707
+ }
2708
+ const retryMessages = session.messages.slice(0, -1);
2709
+ const assistantMessage = await getAiResponse(previousUserMessage.text, retryMessages, updatedState.nextMessageId, updatedState.selectedModelId, updatedState.models, updatedState.openApiApiKey, updatedState.openApiApiBaseUrl, updatedState.openRouterApiKey, updatedState.openRouterApiBaseUrl, updatedState.useMockApi, updatedState.mockApiCommandId, updatedState.assetDir, updatedState.platform);
2710
+ const updatedSession = {
2711
+ ...session,
2712
+ messages: [...session.messages.slice(0, -1), assistantMessage]
2713
+ };
2714
+ await saveChatSession(updatedSession);
2715
+ const updatedSessions = updatedState.sessions.map(item => {
2716
+ if (item.id !== updatedState.selectedSessionId) {
2717
+ return item;
2718
+ }
2719
+ return updatedSession;
2720
+ });
2721
+ return {
2722
+ ...updatedState,
2723
+ nextMessageId: updatedState.nextMessageId + 1,
2724
+ sessions: updatedSessions
2725
+ };
2726
+ };
2727
+
2235
2728
  const setOpenRouterApiKey = async (state, openRouterApiKey, persist = true) => {
2236
2729
  if (persist) {
2237
2730
  await update({
@@ -2267,7 +2760,10 @@ const handleClickSaveOpenRouterApiKey = async state => {
2267
2760
  if (!previousUserMessage) {
2268
2761
  return updatedState;
2269
2762
  }
2270
- const assistantMessage = await getAiResponse(previousUserMessage.text, updatedState.nextMessageId, updatedState.selectedModelId, updatedState.models, openRouterApiKey, updatedState.openRouterApiBaseUrl);
2763
+ const retryMessages = session.messages.slice(0, -1);
2764
+
2765
+ // @ts-ignore
2766
+ const assistantMessage = await getAiResponse(previousUserMessage.text, retryMessages, updatedState.nextMessageId, updatedState.selectedModelId, updatedState.models, updatedState.openApiApiKey, updatedState.openApiApiBaseUrl, openRouterApiKey, updatedState.openRouterApiBaseUrl, updatedState.useMockApi, updatedState.mockApiCommandId, updatedState.assetDir, updatedState.platform);
2271
2767
  const updatedSession = {
2272
2768
  ...session,
2273
2769
  messages: [...session.messages.slice(0, -1), assistantMessage]
@@ -2296,14 +2792,20 @@ const focusInput = state => {
2296
2792
 
2297
2793
  const handleSubmit = async state => {
2298
2794
  const {
2795
+ assetDir,
2299
2796
  composerValue,
2797
+ mockApiCommandId,
2300
2798
  models,
2301
2799
  nextMessageId,
2800
+ openApiApiBaseUrl,
2801
+ openApiApiKey,
2302
2802
  openRouterApiBaseUrl,
2303
2803
  openRouterApiKey,
2804
+ platform,
2304
2805
  selectedModelId,
2305
2806
  selectedSessionId,
2306
2807
  sessions,
2808
+ useMockApi,
2307
2809
  viewMode
2308
2810
  } = state;
2309
2811
  const userText = composerValue.trim();
@@ -2377,7 +2879,9 @@ const handleSubmit = async state => {
2377
2879
  set(state.uid, state, optimisticState);
2378
2880
  // @ts-ignore
2379
2881
  await invoke('Chat.rerender');
2380
- const assistantMessage = await getAiResponse(userText, optimisticState.nextMessageId, selectedModelId, models, openRouterApiKey, openRouterApiBaseUrl);
2882
+ const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
2883
+ const messages = selectedOptimisticSession?.messages ?? [];
2884
+ const assistantMessage = await getAiResponse(userText, messages, optimisticState.nextMessageId, selectedModelId, models, openApiApiKey, openApiApiBaseUrl, openRouterApiKey, openRouterApiBaseUrl, useMockApi, mockApiCommandId, assetDir, platform);
2381
2885
  const updatedSessions = optimisticState.sessions.map(session => {
2382
2886
  if (session.id !== optimisticState.selectedSessionId) {
2383
2887
  return session;
@@ -2438,6 +2942,10 @@ const getRenameIdFromInputName = name => {
2438
2942
  return name.slice(RenamePrefix.length);
2439
2943
  };
2440
2944
 
2945
+ const OpenApiApiKeyInput = 'open-api-api-key';
2946
+ const SaveOpenApiApiKey = 'save-openapi-api-key';
2947
+ const OpenOpenApiApiKeySettings = 'open-openapi-api-key-settings';
2948
+
2441
2949
  /* eslint-disable @cspell/spellchecker */
2442
2950
  const OpenRouterApiKeyInput = 'open-router-api-key';
2443
2951
  const SaveOpenRouterApiKey = 'save-openrouter-api-key';
@@ -2524,9 +3032,15 @@ const handleClick = async (state, name, id = '') => {
2524
3032
  if (name === SaveOpenRouterApiKey) {
2525
3033
  return handleClickSaveOpenRouterApiKey(state);
2526
3034
  }
3035
+ if (name === SaveOpenApiApiKey) {
3036
+ return handleClickSaveOpenApiApiKey(state);
3037
+ }
2527
3038
  if (name === OpenOpenRouterApiKeySettings) {
2528
3039
  return handleClickOpenRouterApiKeySettings(state);
2529
3040
  }
3041
+ if (name === OpenOpenApiApiKeySettings) {
3042
+ return handleClickOpenApiApiKeySettings(state);
3043
+ }
2530
3044
  return state;
2531
3045
  };
2532
3046
 
@@ -2557,6 +3071,12 @@ const handleClickSettings = async () => {
2557
3071
  };
2558
3072
 
2559
3073
  const handleInput = async (state, name, value, inputSource = 'user') => {
3074
+ if (name === OpenApiApiKeyInput) {
3075
+ return {
3076
+ ...state,
3077
+ openApiApiKeyInput: value
3078
+ };
3079
+ }
2560
3080
  if (name === OpenRouterApiKeyInput) {
2561
3081
  return {
2562
3082
  ...state,
@@ -2776,6 +3296,23 @@ const loadSelectedSessionMessages = async (sessions, selectedSessionId) => {
2776
3296
  const loadContent = async (state, savedState) => {
2777
3297
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
2778
3298
  const savedViewMode = getSavedViewMode(savedState);
3299
+ let openApiApiKey = '';
3300
+ try {
3301
+ const savedOpenApiKey = await get('secrets.openApiKey');
3302
+ if (typeof savedOpenApiKey === 'string' && savedOpenApiKey) {
3303
+ openApiApiKey = savedOpenApiKey;
3304
+ } else {
3305
+ const legacySavedOpenApiApiKey = await get('secrets.openApiApiKey');
3306
+ if (typeof legacySavedOpenApiApiKey === 'string' && legacySavedOpenApiApiKey) {
3307
+ openApiApiKey = legacySavedOpenApiApiKey;
3308
+ } else {
3309
+ const legacySavedOpenAiApiKey = await get('secrets.openAiApiKey');
3310
+ openApiApiKey = typeof legacySavedOpenAiApiKey === 'string' ? legacySavedOpenAiApiKey : '';
3311
+ }
3312
+ }
3313
+ } catch {
3314
+ openApiApiKey = '';
3315
+ }
2779
3316
  let openRouterApiKey = '';
2780
3317
  try {
2781
3318
  const savedOpenRouterApiKey = await get('secrets.openRouterApiKey');
@@ -2808,6 +3345,8 @@ const loadContent = async (state, savedState) => {
2808
3345
  return {
2809
3346
  ...state,
2810
3347
  initial: false,
3348
+ openApiApiKey,
3349
+ openApiApiKeyInput: openApiApiKey,
2811
3350
  openRouterApiKey,
2812
3351
  openRouterApiKeyInput: openRouterApiKey,
2813
3352
  selectedModelId,
@@ -2906,6 +3445,8 @@ const ChatListItemLabel = 'ChatListItemLabel';
2906
3445
  const Markdown = 'Markdown';
2907
3446
  const Message = 'Message';
2908
3447
  const ChatMessageContent = 'ChatMessageContent';
3448
+ const ChatOrderedList = 'ChatOrderedList';
3449
+ const ChatOrderedListItem = 'ChatOrderedListItem';
2909
3450
  const MessageUser = 'MessageUser';
2910
3451
  const MessageAssistant = 'MessageAssistant';
2911
3452
  const MultilineInputBox = 'MultilineInputBox';
@@ -2935,8 +3476,12 @@ const getModelLabel = model => {
2935
3476
  if (model.provider === 'openRouter') {
2936
3477
  return `${model.name} (OpenRouter)`;
2937
3478
  }
3479
+ if (model.provider === 'openApi' || model.provider === 'openAI' || model.provider === 'openai') {
3480
+ return `${model.name} (OpenAI)`;
3481
+ }
2938
3482
  return model.name;
2939
3483
  };
3484
+
2940
3485
  const getModelOptionDOm = (model, selectedModelId) => {
2941
3486
  return [{
2942
3487
  childCount: 1,
@@ -3111,18 +3656,25 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
3111
3656
  }, text(selectedSessionTitle), ...getChatHeaderActionsDom()];
3112
3657
  };
3113
3658
 
3114
- const getMissingOpenRouterApiKeyDom = openRouterApiKeyInput => {
3659
+ const getMissingApiKeyDom = ({
3660
+ getApiKeyText,
3661
+ inputName,
3662
+ inputValue,
3663
+ openSettingsButtonName,
3664
+ placeholder,
3665
+ saveButtonName
3666
+ }) => {
3115
3667
  return [{
3116
3668
  childCount: 2,
3117
3669
  type: Div
3118
3670
  }, {
3119
3671
  childCount: 0,
3120
3672
  className: InputBox,
3121
- name: OpenRouterApiKeyInput,
3673
+ name: inputName,
3122
3674
  onInput: HandleInput,
3123
- placeholder: openRouterApiKeyPlaceholder(),
3675
+ placeholder,
3124
3676
  type: Input,
3125
- value: openRouterApiKeyInput
3677
+ value: inputValue
3126
3678
  }, {
3127
3679
  childCount: 2,
3128
3680
  className: Actions,
@@ -3130,35 +3682,73 @@ const getMissingOpenRouterApiKeyDom = openRouterApiKeyInput => {
3130
3682
  }, {
3131
3683
  childCount: 1,
3132
3684
  className: mergeClassNames(Button, ButtonPrimary),
3133
- name: SaveOpenRouterApiKey,
3685
+ name: saveButtonName,
3134
3686
  onClick: HandleClick,
3135
3687
  type: Button$1
3136
3688
  }, text(save()), {
3137
3689
  childCount: 1,
3138
3690
  className: mergeClassNames(Button, ButtonSecondary),
3139
- name: OpenOpenRouterApiKeySettings,
3691
+ name: openSettingsButtonName,
3140
3692
  onClick: HandleClick,
3141
3693
  type: Button$1
3142
- }, text(getOpenRouterApiKey())];
3694
+ }, text(getApiKeyText)];
3695
+ };
3696
+
3697
+ const getMissingOpenApiApiKeyDom = openApiApiKeyInput => {
3698
+ return getMissingApiKeyDom({
3699
+ getApiKeyText: getOpenApiApiKey(),
3700
+ inputName: OpenApiApiKeyInput,
3701
+ inputValue: openApiApiKeyInput,
3702
+ openSettingsButtonName: OpenOpenApiApiKeySettings,
3703
+ placeholder: openApiApiKeyPlaceholder(),
3704
+ saveButtonName: SaveOpenApiApiKey
3705
+ });
3706
+ };
3707
+
3708
+ const getMissingOpenRouterApiKeyDom = openRouterApiKeyInput => {
3709
+ return getMissingApiKeyDom({
3710
+ getApiKeyText: getOpenRouterApiKey(),
3711
+ inputName: OpenRouterApiKeyInput,
3712
+ inputValue: openRouterApiKeyInput,
3713
+ openSettingsButtonName: OpenOpenRouterApiKeySettings,
3714
+ placeholder: openRouterApiKeyPlaceholder(),
3715
+ saveButtonName: SaveOpenRouterApiKey
3716
+ });
3143
3717
  };
3144
3718
 
3145
3719
  const getOpenRouterRequestFailedDom = () => {
3146
3720
  return [{
3147
3721
  childCount: openRouterRequestFailureReasons.length,
3148
- className: Markdown,
3722
+ className: ChatOrderedList,
3149
3723
  type: Ol
3150
3724
  }, ...openRouterRequestFailureReasons.flatMap(reason => {
3151
3725
  return [{
3152
3726
  childCount: 1,
3727
+ className: ChatOrderedListItem,
3153
3728
  type: Li
3154
3729
  }, text(reason)];
3155
3730
  })];
3156
3731
  };
3157
- const getChatMessageDom = (message, openRouterApiKeyInput) => {
3732
+ const getOpenRouterTooManyRequestsDom = () => {
3733
+ return [{
3734
+ childCount: openRouterTooManyRequestsReasons.length,
3735
+ className: ChatOrderedList,
3736
+ type: Ol
3737
+ }, ...openRouterTooManyRequestsReasons.flatMap(reason => {
3738
+ return [{
3739
+ childCount: 1,
3740
+ className: ChatOrderedListItem,
3741
+ type: Li
3742
+ }, text(reason)];
3743
+ })];
3744
+ };
3745
+ const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput = '') => {
3158
3746
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
3747
+ const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
3159
3748
  const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
3160
3749
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
3161
- const extraChildCount = isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage ? 2 : 1;
3750
+ const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
3751
+ const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? 2 : 1;
3162
3752
  return [{
3163
3753
  childCount: 1,
3164
3754
  className: mergeClassNames(Message, roleClassName),
@@ -3171,7 +3761,7 @@ const getChatMessageDom = (message, openRouterApiKeyInput) => {
3171
3761
  childCount: 1,
3172
3762
  className: Markdown,
3173
3763
  type: P
3174
- }, text(message.text), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : [])];
3764
+ }, text(message.text), ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
3175
3765
  };
3176
3766
 
3177
3767
  const getEmptyMessagesDom = () => {
@@ -3181,7 +3771,8 @@ const getEmptyMessagesDom = () => {
3181
3771
  type: Div
3182
3772
  }, text(startConversation())];
3183
3773
  };
3184
- const getMessagesDom = (messages, openRouterApiKeyInput) => {
3774
+
3775
+ const getMessagesDom = (messages, openRouterApiKeyInput, openApiApiKeyInput = '') => {
3185
3776
  if (messages.length === 0) {
3186
3777
  return getEmptyMessagesDom();
3187
3778
  }
@@ -3189,10 +3780,10 @@ const getMessagesDom = (messages, openRouterApiKeyInput) => {
3189
3780
  childCount: messages.length,
3190
3781
  className: 'ChatMessages',
3191
3782
  type: Div
3192
- }, ...messages.flatMap(message => getChatMessageDom(message, openRouterApiKeyInput))];
3783
+ }, ...messages.flatMap(message => getChatMessageDom(message, openRouterApiKeyInput, openApiApiKeyInput))];
3193
3784
  };
3194
3785
 
3195
- const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
3786
+ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
3196
3787
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
3197
3788
  const selectedSessionTitle = selectedSession?.title || chatTitle();
3198
3789
  const messages = selectedSession ? selectedSession.messages : [];
@@ -3200,7 +3791,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue,
3200
3791
  childCount: 3,
3201
3792
  className: mergeClassNames(Viewlet, Chat),
3202
3793
  type: Div
3203
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages, openRouterApiKeyInput), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
3794
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages, openRouterApiKeyInput, openApiApiKeyInput), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
3204
3795
  };
3205
3796
 
3206
3797
  const getChatHeaderListModeDom = () => {
@@ -3284,10 +3875,10 @@ const getChatModeUnsupportedVirtualDom = () => {
3284
3875
  }, text(unknownViewMode())];
3285
3876
  };
3286
3877
 
3287
- const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
3878
+ const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput = '') => {
3288
3879
  switch (viewMode) {
3289
3880
  case 'detail':
3290
- return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
3881
+ return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
3291
3882
  case 'list':
3292
3883
  return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
3293
3884
  default:
@@ -3300,6 +3891,7 @@ const renderItems = (oldState, newState) => {
3300
3891
  composerValue,
3301
3892
  initial,
3302
3893
  models,
3894
+ openApiApiKeyInput,
3303
3895
  openRouterApiKeyInput,
3304
3896
  selectedModelId,
3305
3897
  selectedSessionId,
@@ -3313,7 +3905,7 @@ const renderItems = (oldState, newState) => {
3313
3905
  if (initial) {
3314
3906
  return [SetDom2, uid, []];
3315
3907
  }
3316
- const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
3908
+ const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput);
3317
3909
  return [SetDom2, uid, dom];
3318
3910
  };
3319
3911
 
@@ -3488,6 +4080,21 @@ const setChatList = state => {
3488
4080
  };
3489
4081
  };
3490
4082
 
4083
+ const defaultMockApiCommandId = 'ChatE2e.mockApi';
4084
+ const useMockApi = (state, value, mockApiCommandId = defaultMockApiCommandId) => {
4085
+ if (!value) {
4086
+ return {
4087
+ ...state,
4088
+ useMockApi: false
4089
+ };
4090
+ }
4091
+ return {
4092
+ ...state,
4093
+ mockApiCommandId,
4094
+ useMockApi: true
4095
+ };
4096
+ };
4097
+
3491
4098
  const commandMap = {
3492
4099
  'Chat.clearInput': wrapCommand(clearInput),
3493
4100
  'Chat.create': create,
@@ -3521,7 +4128,8 @@ const commandMap = {
3521
4128
  'Chat.saveState': wrapGetter(saveState),
3522
4129
  'Chat.setChatList': wrapCommand(setChatList),
3523
4130
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
3524
- 'Chat.terminate': terminate
4131
+ 'Chat.terminate': terminate,
4132
+ 'Chat.useMockApi': wrapCommand(useMockApi)
3525
4133
  };
3526
4134
 
3527
4135
  const listen = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",