@molecule/app-ide-react 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +89 -53
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/chat-model-mode-utilities.d.ts +29 -0
- package/dist/components/chat-model-mode-utilities.d.ts.map +1 -1
- package/dist/components/chat-model-mode-utilities.js +40 -0
- package/dist/components/chat-model-mode-utilities.js.map +1 -1
- package/dist/components/chat-models-utilities.d.ts +68 -8
- package/dist/components/chat-models-utilities.d.ts.map +1 -1
- package/dist/components/chat-models-utilities.js +116 -12
- package/dist/components/chat-models-utilities.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAa,MAAM,OAAO,CAAA;AAY3C,OAAO,KAAK,EAAa,WAAW,EAAmB,MAAM,uBAAuB,CAAA;AAsCpF,OAAO,KAAK,EAAE,cAAc,EAAoB,eAAe,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AA0BvD,OAAO,KAAK,EAAsB,SAAS,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -21,8 +21,8 @@ import { stripCommitCoauthorTrailer } from './chat-commit-utilities.js';
|
|
|
21
21
|
import { cachedPromptTokens, formatTokenTotal } from './chat-cost-utilities.js';
|
|
22
22
|
import { effortOptionsForModel, nativeEffortName, parseEffortCommand, resolveEffortArg, } from './chat-effort-utilities.js';
|
|
23
23
|
import { buildHelpText } from './chat-help-utilities.js';
|
|
24
|
-
import { freeTierLockReason, freeTierUsableMode, isModeModelLocked, modeSettingKey, parseModelModeCommand, resolveModeModel, } from './chat-model-mode-utilities.js';
|
|
25
|
-
import { modelUsageRate, sortModels } from './chat-models-utilities.js';
|
|
24
|
+
import { effectiveModeModelId, freeTierLockReason, freeTierUsableMode, isModeModelLocked, modeSettingKey, parseModelModeCommand, resolveModeModel, } from './chat-model-mode-utilities.js';
|
|
25
|
+
import { modelHasPeakPricing, modelPeakMultiplier, modelPeakWindowLabels, modelUsageRate, sortModels, } from './chat-models-utilities.js';
|
|
26
26
|
import { formatReportConfirmation, parseReportCommand } from './chat-report-utilities.js';
|
|
27
27
|
import { findScriptByName, formatRunOutput, parseRunCommand, parseScriptsCommand, runSucceeded, } from './chat-scripts-utilities.js';
|
|
28
28
|
import { buildSettingsList, summarizeSounds } from './chat-settings-utilities.js';
|
|
@@ -2484,6 +2484,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2484
2484
|
const DEFAULT_MODEL = FREE_TIER_MODEL;
|
|
2485
2485
|
const isFreeTier = !isPro;
|
|
2486
2486
|
const [currentModel, setCurrentModel] = useState('');
|
|
2487
|
+
// The PERSISTED legacy chatModel only — unlike currentModel, never seeded
|
|
2488
|
+
// with the catalog default. The distinction matters: a seeded default is not
|
|
2489
|
+
// a user choice, and treating it as one made every plan-mode surface label
|
|
2490
|
+
// the free-tier EXECUTOR as the active model while the server planned with
|
|
2491
|
+
// its own plan default (prod 2026-08-13: UI said "DeepSeek V4 Flash" for a
|
|
2492
|
+
// plan turn DeepSeek V4 Pro served).
|
|
2493
|
+
const [savedChatModel, setSavedChatModel] = useState('');
|
|
2487
2494
|
// Per-mode model overrides (SYN5). Empty string = unset; resolveModeModel then
|
|
2488
2495
|
// falls back to currentModel (the legacy single chatModel) for back-compat.
|
|
2489
2496
|
const [planModel, setPlanModel] = useState('');
|
|
@@ -2515,13 +2522,21 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2515
2522
|
setCurrentModel(DEFAULT_MODEL);
|
|
2516
2523
|
}
|
|
2517
2524
|
}, [currentModel, DEFAULT_MODEL]);
|
|
2525
|
+
// The model a conversation mode will ACTUALLY use, for every display surface
|
|
2526
|
+
// (picker pill, /model suffix, settings rows, fast-mode gating). Mirrors the
|
|
2527
|
+
// server's selection chain: per-mode setting → SAVED chatModel → the
|
|
2528
|
+
// server-sent per-mode default (owner account settings + tier already folded
|
|
2529
|
+
// in) → the catalog clamp.
|
|
2530
|
+
const effectiveModelForMode = (m) => effectiveModeModelId(m, { planModel, executeModel, chatModel: savedChatModel }, serverModelDefaults, isFreeTier, AVAILABLE_MODELS, FREE_TIER_MODEL);
|
|
2518
2531
|
useEffect(() => {
|
|
2519
2532
|
http
|
|
2520
2533
|
.get(`/projects/${projectId}`)
|
|
2521
2534
|
.then((res) => {
|
|
2522
2535
|
const s = res.data.settings;
|
|
2523
|
-
if (typeof s?.chatModel === 'string')
|
|
2536
|
+
if (typeof s?.chatModel === 'string') {
|
|
2524
2537
|
setCurrentModel(s.chatModel);
|
|
2538
|
+
setSavedChatModel(s.chatModel);
|
|
2539
|
+
}
|
|
2525
2540
|
if (typeof s?.planModel === 'string')
|
|
2526
2541
|
setPlanModel(s.planModel);
|
|
2527
2542
|
if (typeof s?.executeModel === 'string')
|
|
@@ -2654,6 +2669,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2654
2669
|
}));
|
|
2655
2670
|
if (fallback) {
|
|
2656
2671
|
setCurrentModel(fallback);
|
|
2672
|
+
setSavedChatModel(fallback);
|
|
2657
2673
|
http.patch(`/projects/${projectId}`, { settings: { chatModel: fallback } }).catch(() => {
|
|
2658
2674
|
/* persistence is best-effort; the in-memory switch is what matters */
|
|
2659
2675
|
});
|
|
@@ -2851,9 +2867,16 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2851
2867
|
// render as a normal user bubble: either it's suppressed entirely (hidden build
|
|
2852
2868
|
// kickoff) or it stays visible but flagged `automatic` so the chat shows it as sent
|
|
2853
2869
|
// by Synthase on the user's behalf (distinct avatar + accent), not typed by the user.
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2870
|
+
// Suppressed or not, a pending message is platform-composed, so it ALWAYS
|
|
2871
|
+
// carries `automatic` (+ `userInitiated` when a direct click requested it).
|
|
2872
|
+
// A suppressed send without `automatic` used to bypass the server's
|
|
2873
|
+
// user-stop gate — the hidden build kickoff resumed a plan turn ~37s
|
|
2874
|
+
// after an explicit Stop (prod 2026-08-13).
|
|
2875
|
+
const sendOpts = {
|
|
2876
|
+
...(pendingMessageSuppressUser ? { suppressUserMessage: true } : {}),
|
|
2877
|
+
automatic: true,
|
|
2878
|
+
...(pendingMessageUserInitiated ? { userInitiated: true } : {}),
|
|
2879
|
+
};
|
|
2857
2880
|
if (isLoading) {
|
|
2858
2881
|
// AI is busy — defer until streaming ends
|
|
2859
2882
|
deferredPendingRef.current = pendingMessage;
|
|
@@ -2885,11 +2908,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
2885
2908
|
deferredPendingRef.current = null;
|
|
2886
2909
|
deferredPendingSuppressRef.current = false;
|
|
2887
2910
|
deferredPendingUserInitiatedRef.current = false;
|
|
2888
|
-
// Same rule as the immediate path: a deferred pending message is system-composed
|
|
2889
|
-
//
|
|
2890
|
-
sendMessage(msg, undefined,
|
|
2891
|
-
? { suppressUserMessage: true }
|
|
2892
|
-
|
|
2911
|
+
// Same rule as the immediate path: a deferred pending message is system-composed —
|
|
2912
|
+
// always `automatic` (suppressed or visible), never a plain user bubble.
|
|
2913
|
+
sendMessage(msg, undefined, {
|
|
2914
|
+
...(suppress ? { suppressUserMessage: true } : {}),
|
|
2915
|
+
automatic: true,
|
|
2916
|
+
...(userInitiated ? { userInitiated: true } : {}),
|
|
2917
|
+
});
|
|
2893
2918
|
}
|
|
2894
2919
|
}, [isLoading, sendMessage]);
|
|
2895
2920
|
// ── Auto-delete queued autofix messages when user edits a relevant file ────
|
|
@@ -3439,6 +3464,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
3439
3464
|
else {
|
|
3440
3465
|
await http.patch(`/projects/${projectId}`, { settings: { chatModel: modelId } });
|
|
3441
3466
|
setCurrentModel(modelId);
|
|
3467
|
+
setSavedChatModel(modelId);
|
|
3442
3468
|
addSystemCard(t('ide.chat.modelSet', { name }, {
|
|
3443
3469
|
defaultValue: `Chat model set to ${name}`,
|
|
3444
3470
|
}));
|
|
@@ -4006,6 +4032,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4006
4032
|
try {
|
|
4007
4033
|
await http.patch(`/projects/${projectId}`, { settings: { chatModel: name } });
|
|
4008
4034
|
setCurrentModel(name);
|
|
4035
|
+
setSavedChatModel(name);
|
|
4009
4036
|
addSystemCard(t('ide.chat.modelSet', { name: resolved?.label ?? name }, {
|
|
4010
4037
|
defaultValue: `Chat model set to ${resolved?.label ?? name}`,
|
|
4011
4038
|
}));
|
|
@@ -4035,8 +4062,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4035
4062
|
// picker + slash-suffix resolveModeModel logic (P2-10: each mode's
|
|
4036
4063
|
// options come from ITS model's reasoning capabilities).
|
|
4037
4064
|
const modelForMode = (m) => {
|
|
4038
|
-
|
|
4039
|
-
return AVAILABLE_MODELS.find((model) => model.id === id);
|
|
4065
|
+
return AVAILABLE_MODELS.find((model) => model.id === effectiveModelForMode(m));
|
|
4040
4066
|
};
|
|
4041
4067
|
const effectiveEffort = (m) => effortByMode[m] ?? (effortLevel || undefined);
|
|
4042
4068
|
const modeStatusLine = (m) => {
|
|
@@ -4356,7 +4382,7 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4356
4382
|
// The composer toggle renders only when the model that will serve the CURRENT
|
|
4357
4383
|
// mode declares fast-tier pricing (`fastPricing` — e.g. Claude Opus 5). The
|
|
4358
4384
|
// server re-checks per iteration, so this is presentation-only gating.
|
|
4359
|
-
const activeModeModelId = (mode === 'plan' ?
|
|
4385
|
+
const activeModeModelId = effectiveModelForMode(mode === 'plan' ? 'plan' : 'execute');
|
|
4360
4386
|
const fastModeAvailable = Boolean(AVAILABLE_MODELS.find((m) => m.id === activeModeModelId)?.fastPricing);
|
|
4361
4387
|
// ── Model-picker mode dropdown ──────────────────────────────────────────────
|
|
4362
4388
|
// Options for the picker's mode selector; each shows the model currently
|
|
@@ -4364,32 +4390,33 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4364
4390
|
// re-scoping the open picker is informed. Cheap to recompute per render.
|
|
4365
4391
|
const pickerModeOptions = (() => {
|
|
4366
4392
|
const label = (id) => AVAILABLE_MODELS.find((m) => m.id === id)?.label || id;
|
|
4367
|
-
const followsDefault = t('ide.chat.settings.modelFollowsDefault', undefined, {
|
|
4368
|
-
defaultValue: 'Follows default model',
|
|
4369
|
-
});
|
|
4370
4393
|
// Name the model "default" resolves to when the server told us
|
|
4371
4394
|
// ("Default (DeepSeek V4 Flash)") — a bare "Fast default" is not decodable.
|
|
4372
4395
|
const auxDefault = (id) => id
|
|
4373
4396
|
? t('ide.chat.settings.modelDefaultNamed', { model: label(id) }, { defaultValue: 'Default ({{model}})' })
|
|
4374
4397
|
: t('ide.chat.settings.modelDefaultFast', undefined, { defaultValue: 'Fast default' });
|
|
4375
|
-
|
|
4398
|
+
// The named-default helper ("Default (<model>)") for a mode whose per-mode
|
|
4399
|
+
// value is unset — names what the mode ACTUALLY resolves to (saved default
|
|
4400
|
+
// model, else the server's per-mode default), never a vague phrase and
|
|
4401
|
+
// never the wrong mode's model.
|
|
4402
|
+
const modeDefault = (m) => t('ide.chat.settings.modelDefaultNamed', { model: label(effectiveModelForMode(m)) }, { defaultValue: 'Default ({{model}})' });
|
|
4376
4403
|
const rows = [
|
|
4377
4404
|
{
|
|
4378
4405
|
value: '',
|
|
4379
4406
|
mode: t('ide.chat.modelMode.default', undefined, { defaultValue: 'Default' }),
|
|
4380
|
-
model:
|
|
4381
|
-
? label(
|
|
4407
|
+
model: savedChatModel
|
|
4408
|
+
? label(savedChatModel)
|
|
4382
4409
|
: t('ide.chat.settings.modelUnset', undefined, { defaultValue: 'Not set' }),
|
|
4383
4410
|
},
|
|
4384
4411
|
{
|
|
4385
4412
|
value: 'plan',
|
|
4386
4413
|
mode: t('ide.chat.settings.modePlan', undefined, { defaultValue: 'Plan' }),
|
|
4387
|
-
model: planModel ? label(planModel) :
|
|
4414
|
+
model: planModel ? label(planModel) : modeDefault('plan'),
|
|
4388
4415
|
},
|
|
4389
4416
|
{
|
|
4390
4417
|
value: 'execute',
|
|
4391
4418
|
mode: t('ide.chat.settings.modeExecute', undefined, { defaultValue: 'Execute' }),
|
|
4392
|
-
model: executeModel ? label(executeModel) :
|
|
4419
|
+
model: executeModel ? label(executeModel) : modeDefault('execute'),
|
|
4393
4420
|
},
|
|
4394
4421
|
{
|
|
4395
4422
|
value: 'commit',
|
|
@@ -4408,12 +4435,16 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4408
4435
|
}));
|
|
4409
4436
|
})();
|
|
4410
4437
|
// The row that gets the "current" pill — the model the selected picker mode
|
|
4411
|
-
// actually uses (its per-mode value with the mode's fallback chain), not
|
|
4412
|
-
// unconditionally the legacy chatModel.
|
|
4438
|
+
// actually uses (its per-mode value with the mode's REAL fallback chain), not
|
|
4439
|
+
// unconditionally the legacy chatModel. The generic (no-mode) picker edits
|
|
4440
|
+
// the default slot, so its pill is the SAVED default only — a seeded catalog
|
|
4441
|
+
// default is not a choice and must not masquerade as one.
|
|
4413
4442
|
const pickerCurrentModelId = modelPicker
|
|
4414
|
-
? modelPicker.mode
|
|
4415
|
-
?
|
|
4416
|
-
:
|
|
4443
|
+
? modelPicker.mode === 'plan' || modelPicker.mode === 'execute'
|
|
4444
|
+
? effectiveModelForMode(modelPicker.mode)
|
|
4445
|
+
: modelPicker.mode
|
|
4446
|
+
? (resolveModeModel({ planModel, executeModel, commitModel, compactModel, chatModel: savedChatModel }, modelPicker.mode) ?? serverModelDefaults?.[modelPicker.mode])
|
|
4447
|
+
: savedChatModel || undefined
|
|
4417
4448
|
: undefined;
|
|
4418
4449
|
const filteredEntries = useMemo(() => {
|
|
4419
4450
|
if (!filePicker)
|
|
@@ -4710,11 +4741,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4710
4741
|
const soundsSummary = summarizeSounds(soundsConfig);
|
|
4711
4742
|
const notSet = t('ide.chat.settings.modelUnset', undefined, { defaultValue: 'Not set' });
|
|
4712
4743
|
const modelLabel = (id) => AVAILABLE_MODELS.find((m) => m.id === id)?.label || id;
|
|
4713
|
-
//
|
|
4714
|
-
//
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4744
|
+
// An unset per-mode model shows what the mode ACTUALLY resolves to
|
|
4745
|
+
// ("Default (<model>)"): the saved default model, else the server's
|
|
4746
|
+
// per-mode default — never the wrong mode's model (SYN11; the old
|
|
4747
|
+
// "Follows default model" phrasing implied plan follows the executor's
|
|
4748
|
+
// free-tier default while the server planned with its own plan default).
|
|
4749
|
+
const modeDefault = (m) => t('ide.chat.settings.modelDefaultNamed', { model: modelLabel(effectiveModelForMode(m)) }, { defaultValue: 'Default ({{model}})' });
|
|
4718
4750
|
// The auxiliary commit/compact jobs fall back to the server's own fast
|
|
4719
4751
|
// default when unset — NOT the default model — so their unset label differs.
|
|
4720
4752
|
// Name the resolved model when the server sent its defaults.
|
|
@@ -4722,9 +4754,9 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4722
4754
|
? t('ide.chat.settings.modelDefaultNamed', { model: modelLabel(id) }, { defaultValue: 'Default ({{model}})' })
|
|
4723
4755
|
: t('ide.chat.settings.modelDefaultFast', undefined, { defaultValue: 'Fast default' });
|
|
4724
4756
|
return buildSettingsList({
|
|
4725
|
-
model:
|
|
4726
|
-
planModel: planModel ? modelLabel(planModel) :
|
|
4727
|
-
executeModel: executeModel ? modelLabel(executeModel) :
|
|
4757
|
+
model: savedChatModel ? modelLabel(savedChatModel) : notSet,
|
|
4758
|
+
planModel: planModel ? modelLabel(planModel) : modeDefault('plan'),
|
|
4759
|
+
executeModel: executeModel ? modelLabel(executeModel) : modeDefault('execute'),
|
|
4728
4760
|
commitModel: commitModel ? modelLabel(commitModel) : auxDefault(serverModelDefaults?.commit),
|
|
4729
4761
|
compactModel: compactModel
|
|
4730
4762
|
? modelLabel(compactModel)
|
|
@@ -4736,15 +4768,12 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
4736
4768
|
// reasoning model shows "fixed"). Falls back to the legacy single level.
|
|
4737
4769
|
effort: t('ide.chat.settings.effortValue', {
|
|
4738
4770
|
plan: (() => {
|
|
4739
|
-
const model = AVAILABLE_MODELS.find((m) => m.id ===
|
|
4740
|
-
(resolveModeModel({ planModel, executeModel, chatModel: currentModel }, 'plan') ??
|
|
4741
|
-
currentModel));
|
|
4771
|
+
const model = AVAILABLE_MODELS.find((m) => m.id === effectiveModelForMode('plan'));
|
|
4742
4772
|
return (nativeEffortName(model, effortByMode.plan ?? effortLevel) ??
|
|
4743
4773
|
t('ide.chat.settings.effortFixed', undefined, { defaultValue: 'fixed' }));
|
|
4744
4774
|
})(),
|
|
4745
4775
|
execute: (() => {
|
|
4746
|
-
const model = AVAILABLE_MODELS.find((m) => m.id ===
|
|
4747
|
-
(resolveModeModel({ planModel, executeModel, chatModel: currentModel }, 'execute') ?? currentModel));
|
|
4776
|
+
const model = AVAILABLE_MODELS.find((m) => m.id === effectiveModelForMode('execute'));
|
|
4748
4777
|
return (nativeEffortName(model, effortByMode.execute ?? effortLevel) ??
|
|
4749
4778
|
t('ide.chat.settings.effortFixed', undefined, { defaultValue: 'fixed' }));
|
|
4750
4779
|
})(),
|
|
@@ -5076,18 +5105,13 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
5076
5105
|
let suffix = '';
|
|
5077
5106
|
if (cmd.id === 'model') {
|
|
5078
5107
|
// Reflect the model the ACTIVE conversation mode will
|
|
5079
|
-
// actually use
|
|
5080
|
-
//
|
|
5081
|
-
//
|
|
5082
|
-
// the
|
|
5083
|
-
//
|
|
5084
|
-
// parentheses never render
|
|
5085
|
-
|
|
5086
|
-
// chatModel -> the default / free-tier model id (|| so an
|
|
5087
|
-
// empty string also falls through).
|
|
5088
|
-
const effId = resolveModeModel({ planModel, executeModel, chatModel: currentModel }, mode) ||
|
|
5089
|
-
currentModel ||
|
|
5090
|
-
DEFAULT_MODEL ||
|
|
5108
|
+
// actually use — the full server chain (per-mode →
|
|
5109
|
+
// saved default → server per-mode default → clamp),
|
|
5110
|
+
// never a UI-seeded default (which mislabeled plan
|
|
5111
|
+
// mode with the free-tier executor). Falls through to
|
|
5112
|
+
// the free-tier id only before the catalog loads so
|
|
5113
|
+
// the parentheses never render empty (P3-16).
|
|
5114
|
+
const effId = effectiveModelForMode(mode === 'plan' ? 'plan' : 'execute') ||
|
|
5091
5115
|
FREE_TIER_MODEL;
|
|
5092
5116
|
const modelLabel = AVAILABLE_MODELS.find((m) => m.id === effId)?.label ?? effId;
|
|
5093
5117
|
suffix = modelLabel ? ` (${modelLabel})` : '';
|
|
@@ -5455,6 +5479,8 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
5455
5479
|
// region immediately re-rates the row (e.g. DeepSeek V4
|
|
5456
5480
|
// Pro: ×3 native-CN vs ×9 US-rehosted).
|
|
5457
5481
|
const usageRate = modelUsageRate(model, AVAILABLE_MODELS, modelRegion);
|
|
5482
|
+
// Whether the rate above is currently inflated by a peak window.
|
|
5483
|
+
const peakNow = modelPeakMultiplier(model, modelRegion);
|
|
5458
5484
|
const priceColor = usageRate <= 5
|
|
5459
5485
|
? isLight
|
|
5460
5486
|
? 'rgb(22,163,74)'
|
|
@@ -5577,7 +5603,17 @@ function ChatInner({ projectId, endpoint, initialMessage, onInitialMessageSent,
|
|
|
5577
5603
|
defaultValue: 'your key',
|
|
5578
5604
|
}) })) : (_jsx("span", { style: { color: priceColor }, title: t('ide.chat.models.usageRateHint', undefined, {
|
|
5579
5605
|
defaultValue: 'How fast this model uses your AI allowance, relative to the most economical model',
|
|
5580
|
-
}), children: t('ide.chat.models.usageRateValue', { rate: usageRate }, { defaultValue: '×{{rate}} usage' }) })), model
|
|
5606
|
+
}), children: t('ide.chat.models.usageRateValue', { rate: usageRate }, { defaultValue: '×{{rate}} usage' }) })), !isCustom && modelHasPeakPricing(model, modelRegion) && (_jsxs(_Fragment, { children: [' · ', _jsx("span", { style: {
|
|
5607
|
+
color: peakNow > 1 ? priceColor : undefined,
|
|
5608
|
+
opacity: peakNow > 1 ? 1 : 0.8,
|
|
5609
|
+
}, title: t('ide.chat.models.peakHint', {
|
|
5610
|
+
multiplier: model.peakPricing?.multiplier ?? 2,
|
|
5611
|
+
windows: modelPeakWindowLabels(model).join(', '),
|
|
5612
|
+
}, {
|
|
5613
|
+
defaultValue: 'This model costs ×{{multiplier}} between {{windows}}. It is the normal rate the rest of the day.',
|
|
5614
|
+
}), children: peakNow > 1
|
|
5615
|
+
? t('ide.chat.models.peakNow', { multiplier: model.peakPricing?.multiplier ?? 2 }, { defaultValue: 'peak ×{{multiplier}} now' })
|
|
5616
|
+
: t('ide.chat.models.peakLater', { multiplier: model.peakPricing?.multiplier ?? 2 }, { defaultValue: '×{{multiplier}} at peak hours' }) })] })), model.knowledgeCutoff ? _jsxs(_Fragment, { children: [" \u00B7 ", model.knowledgeCutoff] }) : null] }), (badges.length > 0 || showRegionPill) && (_jsxs("span", { style: {
|
|
5581
5617
|
display: 'flex',
|
|
5582
5618
|
gap: '4px',
|
|
5583
5619
|
flexWrap: 'wrap',
|