llm-switcher 1.6.0 → 1.6.2
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 +26 -0
- package/README.md +42 -34
- package/README.vi.md +38 -33
- package/docs/session-2026-10-07-compaction.md +224 -0
- package/formats.mjs +85 -25
- package/idlecache.mjs +159 -0
- package/idlecall.mjs +46 -54
- package/idlecompact.mjs +171 -238
- package/package.json +1 -1
- package/proxy.mjs +111 -135
- package/shim.mjs +29 -0
- package/state.mjs +48 -2
- package/switch.mjs +12 -5
- package/tests/certs.test.mjs +7 -1
- package/tests/codex-daemon.test.mjs +57 -3
- package/tests/compact-cli.test.mjs +34 -0
- package/tests/dashboard.e2e.test.mjs +7 -1
- package/tests/formats.test.mjs +44 -1
- package/tests/idlecompact.e2e.test.mjs +203 -0
- package/tests/idlecompact.test.mjs +233 -279
- package/ui.html +449 -81
- package/claudesession.mjs +0 -196
- package/codexsession.mjs +0 -141
package/ui.html
CHANGED
|
@@ -905,6 +905,10 @@
|
|
|
905
905
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 12h16M12 4v16"/><circle cx="12" cy="12" r="8"/></svg>
|
|
906
906
|
Routes & Profiles<span class="ct" id="ct-routes"></span>
|
|
907
907
|
</a>
|
|
908
|
+
<a class="nav-link" href="#/compact" data-nav="compact">
|
|
909
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 14h6m0 0v6m0-6L3 21"/><path d="M20 10h-6m0 0V4m0 6l7-7"/><circle cx="12" cy="12" r="2"/></svg>
|
|
910
|
+
Idle Compaction<span class="ct" id="ct-compact"></span>
|
|
911
|
+
</a>
|
|
908
912
|
<a class="nav-link" href="#/models" data-nav="models">
|
|
909
913
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="6" rx="1.5"/><rect x="3" y="14" width="18" height="6" rx="1.5"/></svg>
|
|
910
914
|
Model Catalog<span class="ct" id="ct-models"></span>
|
|
@@ -952,57 +956,6 @@
|
|
|
952
956
|
<!-- VIEW 1: Routes & Profiles -->
|
|
953
957
|
<div id="view-routes" class="view-panel" style="display:none;">
|
|
954
958
|
<div class="sec">
|
|
955
|
-
<!-- Idle compaction -->
|
|
956
|
-
<div class="card">
|
|
957
|
-
<div class="cardh">
|
|
958
|
-
<h2 style="font-size:1.02rem; font-weight:600;">Idle compaction</h2>
|
|
959
|
-
<span class="badge outline" id="badge-idle-compact">OFF</span>
|
|
960
|
-
</div>
|
|
961
|
-
<p style="margin:.2rem 0 .7rem; color:var(--muted); font-size:.86rem;">
|
|
962
|
-
A Claude Code conversation that comes back after a pause has lost the provider's prompt
|
|
963
|
-
cache, so its history is paid for at full price. Shorten it then, and write the
|
|
964
|
-
compaction into the session file so the next resume starts short.
|
|
965
|
-
</p>
|
|
966
|
-
<div class="row">
|
|
967
|
-
<label class="cb" style="display:flex; align-items:center; gap:.5rem;">
|
|
968
|
-
<input type="checkbox" id="idle-compact-enabled" onchange="saveIdleCompact()">
|
|
969
|
-
<span>Shorten a conversation that was idle too long</span>
|
|
970
|
-
</label>
|
|
971
|
-
</div>
|
|
972
|
-
<div class="row">
|
|
973
|
-
<div class="who">
|
|
974
|
-
<div class="l">Pause that counts as losing the cache</div>
|
|
975
|
-
<div class="meta">minutes · Anthropic 5m default, OpenAI 30m</div>
|
|
976
|
-
</div>
|
|
977
|
-
<div style="min-width:9rem;">
|
|
978
|
-
<input type="number" id="idle-compact-minutes" min="1" max="1440" class="inp"
|
|
979
|
-
style="width:100%;" onchange="saveIdleCompact()">
|
|
980
|
-
</div>
|
|
981
|
-
</div>
|
|
982
|
-
<div class="row">
|
|
983
|
-
<div class="who">
|
|
984
|
-
<div class="l">Context it must be larger than</div>
|
|
985
|
-
<div class="meta">KB · below this nothing is touched</div>
|
|
986
|
-
</div>
|
|
987
|
-
<div style="min-width:9rem;">
|
|
988
|
-
<input type="number" id="idle-compact-kb" min="8" max="4096" class="inp"
|
|
989
|
-
style="width:100%;" onchange="saveIdleCompact()">
|
|
990
|
-
</div>
|
|
991
|
-
</div>
|
|
992
|
-
<div class="row">
|
|
993
|
-
<div class="who">
|
|
994
|
-
<div class="l">Model that writes the summary</div>
|
|
995
|
-
<div class="meta">a cheaper model writes a cheaper and worse summary</div>
|
|
996
|
-
</div>
|
|
997
|
-
<div style="min-width:16rem; flex:1;">
|
|
998
|
-
<input type="text" id="idle-compact-model" class="inp" style="width:100%;"
|
|
999
|
-
placeholder="empty = the model of the conversation itself"
|
|
1000
|
-
onchange="saveIdleCompact()">
|
|
1001
|
-
</div>
|
|
1002
|
-
</div>
|
|
1003
|
-
<span id="idle-compact-status" class="meta" aria-live="polite"></span>
|
|
1004
|
-
</div>
|
|
1005
|
-
|
|
1006
959
|
<!-- Active Coding CLI Targets -->
|
|
1007
960
|
<div class="card">
|
|
1008
961
|
<div class="cardh">
|
|
@@ -1067,6 +1020,186 @@
|
|
|
1067
1020
|
</div>
|
|
1068
1021
|
</div>
|
|
1069
1022
|
|
|
1023
|
+
<!-- VIEW: Idle Prefix Cache & Compaction -->
|
|
1024
|
+
<div id="view-compact" class="view-panel" style="display:none;">
|
|
1025
|
+
<div class="sec">
|
|
1026
|
+
<!-- Overview Stats -->
|
|
1027
|
+
<div class="stats">
|
|
1028
|
+
<div class="stat">
|
|
1029
|
+
<div class="k">Compaction Engine</div>
|
|
1030
|
+
<div class="v" id="stat-compact-status" style="color:var(--muted); font-size:1.4rem;">Disabled</div>
|
|
1031
|
+
<div class="status" id="stat-compact-indicator" style="margin-top:0.3rem;">Prefix caching inactive</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
|
|
1034
|
+
<div class="stat">
|
|
1035
|
+
<div class="k">Inactivity Pause</div>
|
|
1036
|
+
<div class="v" id="stat-compact-pause" style="color:var(--accent); font-size:1.4rem;">15 min</div>
|
|
1037
|
+
<div class="hint" style="margin-top:0.3rem;">Trigger pause threshold</div>
|
|
1038
|
+
</div>
|
|
1039
|
+
|
|
1040
|
+
<div class="stat">
|
|
1041
|
+
<div class="k">Min Size Threshold</div>
|
|
1042
|
+
<div class="v" id="stat-compact-size" style="color:var(--mint); font-size:1.4rem;">64 KB</div>
|
|
1043
|
+
<div class="hint" style="margin-top:0.3rem;">Smaller payloads bypassed</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
|
|
1046
|
+
<div class="stat">
|
|
1047
|
+
<div class="k">Cache Lookback</div>
|
|
1048
|
+
<div class="v" id="stat-compact-lookback" style="color:var(--code); font-size:1.4rem;">72 hrs</div>
|
|
1049
|
+
<div class="hint" style="margin-top:0.3rem;">Storage retention window</div>
|
|
1050
|
+
</div>
|
|
1051
|
+
</div>
|
|
1052
|
+
|
|
1053
|
+
<!-- Hero Master Card -->
|
|
1054
|
+
<div class="card pad">
|
|
1055
|
+
<div class="sech">
|
|
1056
|
+
<div>
|
|
1057
|
+
<div style="display:flex; align-items:center; gap:0.6rem;">
|
|
1058
|
+
<h2 style="font-size:1.05rem; font-weight:600;">Idle Prefix Cache & Compaction</h2>
|
|
1059
|
+
<span class="badge outline" id="badge-idle-compact">OFF</span>
|
|
1060
|
+
</div>
|
|
1061
|
+
<p class="hint" style="margin-top:0.35rem; max-width:54rem; line-height:1.45;">
|
|
1062
|
+
Shortens long conversation histories when an agent session remains inactive past a configured pause.
|
|
1063
|
+
The edge gateway maintains a canonical shortened prefix for resumed turns, cutting context tokens while keeping client transcripts 100% untouched.
|
|
1064
|
+
</p>
|
|
1065
|
+
</div>
|
|
1066
|
+
<div style="display:flex; align-items:center; gap:0.8rem;">
|
|
1067
|
+
<label class="switch" title="Toggle Idle Compaction">
|
|
1068
|
+
<input type="checkbox" id="idle-compact-enabled" onchange="saveIdleCompact()">
|
|
1069
|
+
<span></span>
|
|
1070
|
+
</label>
|
|
1071
|
+
</div>
|
|
1072
|
+
</div>
|
|
1073
|
+
<div id="idle-compact-status" style="margin-top:0.75rem; font-size:0.82rem; min-height:1.2rem;" aria-live="polite"></div>
|
|
1074
|
+
</div>
|
|
1075
|
+
|
|
1076
|
+
<!-- Configuration Section (Hidden when inactive) -->
|
|
1077
|
+
<div id="idle-compact-config-section" style="display:none; flex-direction:column; gap:1.25rem;">
|
|
1078
|
+
<!-- Card 1: Core Policy & Trigger Thresholds -->
|
|
1079
|
+
<div class="card">
|
|
1080
|
+
<div class="cardh">
|
|
1081
|
+
<h3 style="font-size:0.95rem; font-weight:600;">Compaction Policy & Triggers</h3>
|
|
1082
|
+
<span class="badge ok">Gateway Invariant</span>
|
|
1083
|
+
</div>
|
|
1084
|
+
|
|
1085
|
+
<div class="row">
|
|
1086
|
+
<div class="who">
|
|
1087
|
+
<div class="l">Inactivity Pause Threshold</div>
|
|
1088
|
+
<div class="meta">Elapsed idle minutes after which the conversation prefix is compacted</div>
|
|
1089
|
+
</div>
|
|
1090
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1091
|
+
<input type="number" id="idle-compact-minutes" min="1" max="1440" step="any" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Inactivity pause threshold in minutes (1 – 1440 min)" placeholder="15">
|
|
1092
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.2rem; user-select:none;">min</span>
|
|
1093
|
+
</div>
|
|
1094
|
+
</div>
|
|
1095
|
+
|
|
1096
|
+
<div class="row">
|
|
1097
|
+
<div class="who">
|
|
1098
|
+
<div class="l">Minimum Context Payload</div>
|
|
1099
|
+
<div class="meta">Context threshold; conversations smaller than this are kept untouched</div>
|
|
1100
|
+
</div>
|
|
1101
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1102
|
+
<input type="number" id="idle-compact-kb" min="8" max="4096" step="any" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Minimum context payload size in kilobytes (8 – 4096 KB)" placeholder="64">
|
|
1103
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.2rem; user-select:none;">KB</span>
|
|
1104
|
+
</div>
|
|
1105
|
+
</div>
|
|
1106
|
+
|
|
1107
|
+
<div class="row">
|
|
1108
|
+
<div class="who">
|
|
1109
|
+
<div class="l">Summarization Model</div>
|
|
1110
|
+
<div class="meta">Upstream model ID to generate turn summaries (empty = deterministic truncation)</div>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div style="min-width:16rem; flex:1; max-width:26rem; margin-left:auto;">
|
|
1113
|
+
<input type="text" id="idle-compact-model" placeholder="empty = no model call (deterministic)" maxlength="512" onchange="saveIdleCompact()" style="font-family:var(--font-mono); font-size:0.8rem;" title="Upstream model used to generate turn summary text">
|
|
1114
|
+
</div>
|
|
1115
|
+
</div>
|
|
1116
|
+
|
|
1117
|
+
<div class="row">
|
|
1118
|
+
<div class="who">
|
|
1119
|
+
<div class="l">Shorten Codex CLI Sessions</div>
|
|
1120
|
+
<div class="meta">Off by default; Codex retains native turn history in local SQLite storage</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
<label class="switch" title="Toggle Codex compaction" style="margin-left:auto;">
|
|
1123
|
+
<input type="checkbox" id="idle-compact-codex" onchange="saveIdleCompact()">
|
|
1124
|
+
<span></span>
|
|
1125
|
+
</label>
|
|
1126
|
+
</div>
|
|
1127
|
+
</div>
|
|
1128
|
+
|
|
1129
|
+
<!-- Card 2: Context Retention & Budget Controls -->
|
|
1130
|
+
<div class="card">
|
|
1131
|
+
<div class="cardh">
|
|
1132
|
+
<h3 style="font-size:0.95rem; font-weight:600;">Context Retention & Cache Budgets</h3>
|
|
1133
|
+
<span class="badge accent">Turn Budgets</span>
|
|
1134
|
+
</div>
|
|
1135
|
+
|
|
1136
|
+
<div class="row">
|
|
1137
|
+
<div class="who">
|
|
1138
|
+
<div class="l">Verbatim Recent Turns</div>
|
|
1139
|
+
<div class="meta">Number of most recent turns always preserved byte-for-byte</div>
|
|
1140
|
+
</div>
|
|
1141
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1142
|
+
<input type="number" id="idle-compact-keep" min="1" max="100" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Number of most recent turns to retain verbatim (1 – 100 turns)" placeholder="6">
|
|
1143
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.4rem; user-select:none;">turns</span>
|
|
1144
|
+
</div>
|
|
1145
|
+
</div>
|
|
1146
|
+
|
|
1147
|
+
<div class="row">
|
|
1148
|
+
<div class="who">
|
|
1149
|
+
<div class="l">Character Budget Per Earlier Turn</div>
|
|
1150
|
+
<div class="meta">Maximum character budget preserved per earlier conversation turn</div>
|
|
1151
|
+
</div>
|
|
1152
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1153
|
+
<input type="number" id="idle-compact-user-chars" min="128" max="24000" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Character budget limit per earlier text turn (128 – 24000 chars)" placeholder="3000">
|
|
1154
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.4rem; user-select:none;">chars</span>
|
|
1155
|
+
</div>
|
|
1156
|
+
</div>
|
|
1157
|
+
|
|
1158
|
+
<div class="row">
|
|
1159
|
+
<div class="who">
|
|
1160
|
+
<div class="l">Maximum Summary Length</div>
|
|
1161
|
+
<div class="meta">Upper character limit allocated for generated turn summary blocks</div>
|
|
1162
|
+
</div>
|
|
1163
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1164
|
+
<input type="number" id="idle-compact-summary-chars" min="256" max="128000" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Upper character limit allocated for generated summary (256 – 128000 chars)" placeholder="24000">
|
|
1165
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.4rem; user-select:none;">chars</span>
|
|
1166
|
+
</div>
|
|
1167
|
+
</div>
|
|
1168
|
+
|
|
1169
|
+
<div class="row">
|
|
1170
|
+
<div class="who">
|
|
1171
|
+
<div class="l">Cache Prefix Retention Window</div>
|
|
1172
|
+
<div class="meta">Duration in hours before idle cached prefixes are pruned from state disk</div>
|
|
1173
|
+
</div>
|
|
1174
|
+
<div style="display:flex; align-items:center; gap:0.45rem; margin-left:auto;">
|
|
1175
|
+
<input type="number" id="idle-compact-lookback" min="1" max="168" step="any" onchange="saveIdleCompact()" style="width:7.5rem; text-align:right;" title="Duration in hours before idle cached prefixes are pruned (1 – 168 hours)" placeholder="72">
|
|
1176
|
+
<span style="font-size:0.8rem; color:var(--muted); min-width:2.4rem; user-select:none;">hours</span>
|
|
1177
|
+
</div>
|
|
1178
|
+
</div>
|
|
1179
|
+
</div>
|
|
1180
|
+
|
|
1181
|
+
<!-- Card 3: Architecture & Safety Invariants -->
|
|
1182
|
+
<div class="card pad">
|
|
1183
|
+
<h3 style="font-size:0.95rem; font-weight:600; margin-bottom:0.75rem;">Edge Gateway Invariants & Storage Safety</h3>
|
|
1184
|
+
<div class="sec">
|
|
1185
|
+
<div class="copyline">
|
|
1186
|
+
<code>Zero Client Mutation: Transcripts on client disk (Claude Code / Codex) are never modified.</code>
|
|
1187
|
+
</div>
|
|
1188
|
+
<div class="copyline">
|
|
1189
|
+
<code>Atomic CAS Cache: Compare-and-swap generation counters prevent stale compaction races.</code>
|
|
1190
|
+
</div>
|
|
1191
|
+
<div class="copyline">
|
|
1192
|
+
<code>Private State Storage: Cached prefixes saved under ~/.llm-switcher/state/idle-compact/ (0600 permissions).</code>
|
|
1193
|
+
</div>
|
|
1194
|
+
<div class="copyline">
|
|
1195
|
+
<code>Deterministic Safety: If no summary model is configured, gateway uses pure heuristic truncation without external calls.</code>
|
|
1196
|
+
</div>
|
|
1197
|
+
</div>
|
|
1198
|
+
</div>
|
|
1199
|
+
</div>
|
|
1200
|
+
</div>
|
|
1201
|
+
</div>
|
|
1202
|
+
|
|
1070
1203
|
<!-- VIEW 2: Discovered Models Catalog -->
|
|
1071
1204
|
<div id="view-models" class="view-panel" style="display:none;">
|
|
1072
1205
|
<div class="sec">
|
|
@@ -1242,13 +1375,6 @@
|
|
|
1242
1375
|
</div>
|
|
1243
1376
|
|
|
1244
1377
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:0.9rem;">
|
|
1245
|
-
<label class="f">Thinking Mode
|
|
1246
|
-
<select id="p-thinking-mode">
|
|
1247
|
-
<option value="auto">AUTO (restores reasoning, strips tags safely)</option>
|
|
1248
|
-
<option value="native">NATIVE (strict reasoning_effort)</option>
|
|
1249
|
-
<option value="off">OFF (never send thinking)</option>
|
|
1250
|
-
</select>
|
|
1251
|
-
</label>
|
|
1252
1378
|
<label class="f">Routing Mode
|
|
1253
1379
|
<select id="p-mode">
|
|
1254
1380
|
<option value="convert">CONVERT (Bi-directional translation)</option>
|
|
@@ -1262,11 +1388,46 @@
|
|
|
1262
1388
|
<!-- TAB 3: Models -->
|
|
1263
1389
|
<div id="tab-models" class="tab-content" style="display:none; gap:0.9rem; flex-direction:column;">
|
|
1264
1390
|
<div style="display:flex; justify-content:space-between; align-items:center; gap:0.75rem;">
|
|
1265
|
-
<
|
|
1391
|
+
<div>
|
|
1392
|
+
<h3 id="model-slot-heading" style="font-size:0.92rem; font-weight:600; margin:0;">Model Slots Mapping</h3>
|
|
1393
|
+
<span class="hint" style="font-size:0.75rem;">Configure target model and individual thinking effort for each slot</span>
|
|
1394
|
+
</div>
|
|
1266
1395
|
<div id="models-status" class="models-status" aria-live="polite"></div>
|
|
1267
1396
|
</div>
|
|
1268
1397
|
|
|
1269
1398
|
<div id="slot-fields" style="display:grid; gap:0.75rem;"></div>
|
|
1399
|
+
|
|
1400
|
+
<!-- Global Reasoning Policy -->
|
|
1401
|
+
<div style="border-top:1px solid var(--line); padding-top:0.85rem; margin-top:0.35rem; display:grid; gap:0.85rem;">
|
|
1402
|
+
<div style="display:flex; justify-content:space-between; align-items:center;">
|
|
1403
|
+
<h4 style="font-size:0.88rem; font-weight:600; margin:0;">Reasoning Mode & Policy</h4>
|
|
1404
|
+
<span class="hint">Controls reasoning behavior across all model slots</span>
|
|
1405
|
+
</div>
|
|
1406
|
+
|
|
1407
|
+
<div style="display:grid; grid-template-columns:1fr 1fr; gap:0.9rem;">
|
|
1408
|
+
<label class="f">Thinking Mode
|
|
1409
|
+
<select id="p-thinking-mode">
|
|
1410
|
+
<option value="auto">AUTO (restores reasoning, strips tags safely)</option>
|
|
1411
|
+
<option value="native">NATIVE (strict reasoning_effort)</option>
|
|
1412
|
+
<option value="off">OFF (never send thinking to any model)</option>
|
|
1413
|
+
</select>
|
|
1414
|
+
</label>
|
|
1415
|
+
<label class="f" id="f-thinking-effort" style="display:none;">Global Fallback Effort
|
|
1416
|
+
<select id="p-thinking-effort">
|
|
1417
|
+
<option value="auto">Auto (inherit from client tool)</option>
|
|
1418
|
+
<option value="low">Low (fast, light reasoning)</option>
|
|
1419
|
+
<option value="medium">Medium (balanced reasoning)</option>
|
|
1420
|
+
<option value="high">High (deep reasoning)</option>
|
|
1421
|
+
<option value="xhigh">Extra High (complex problem solving)</option>
|
|
1422
|
+
<option value="max">Max (maximum reasoning depth)</option>
|
|
1423
|
+
</select>
|
|
1424
|
+
</label>
|
|
1425
|
+
</div>
|
|
1426
|
+
|
|
1427
|
+
<div id="bifrost-hint" style="display:none; padding:0.6rem 0.85rem; border-radius:6px; background:rgba(59,130,246,0.08); border:1px solid rgba(59,130,246,0.25); font-size:0.82rem; color:#60a5fa; line-height:1.4;">
|
|
1428
|
+
⚡ <strong>Bifrost Passthrough:</strong> Byte-for-byte routing to intact without format conversion. Thinking effort is controlled natively by your client tool.
|
|
1429
|
+
</div>
|
|
1430
|
+
</div>
|
|
1270
1431
|
</div>
|
|
1271
1432
|
|
|
1272
1433
|
<!-- Test Connection Box -->
|
|
@@ -1474,7 +1635,7 @@
|
|
|
1474
1635
|
function handleRoute() {
|
|
1475
1636
|
const hash = window.location.hash || '#/routes';
|
|
1476
1637
|
const route = hash.replace(/^#\/?/, '').split('/')[0] || 'routes';
|
|
1477
|
-
const validRoutes = ['routes', 'models', 'logs', 'doctor'];
|
|
1638
|
+
const validRoutes = ['routes', 'compact', 'models', 'logs', 'doctor'];
|
|
1478
1639
|
const activeRoute = validRoutes.includes(route) ? route : 'routes';
|
|
1479
1640
|
|
|
1480
1641
|
// Update active nav link
|
|
@@ -1495,6 +1656,7 @@
|
|
|
1495
1656
|
updateHeader(activeRoute);
|
|
1496
1657
|
|
|
1497
1658
|
// Lazy load view content
|
|
1659
|
+
if (activeRoute === 'compact') loadIdleCompact();
|
|
1498
1660
|
if (activeRoute === 'models') renderCatalogView();
|
|
1499
1661
|
if (activeRoute === 'logs') loadInspectorLogs();
|
|
1500
1662
|
}
|
|
@@ -1512,7 +1674,6 @@
|
|
|
1512
1674
|
const activeTargets = Object.entries(activeMap).filter(([_, v]) => Boolean(v));
|
|
1513
1675
|
|
|
1514
1676
|
if (route === 'routes') {
|
|
1515
|
-
loadIdleCompact();
|
|
1516
1677
|
titleEl.textContent = 'Routes & Profiles';
|
|
1517
1678
|
if (activeTargets.length > 0) {
|
|
1518
1679
|
const parts = [];
|
|
@@ -1528,6 +1689,14 @@
|
|
|
1528
1689
|
<button class="btn btn-outline" id="btn-toggle-routes" onclick="toggleProxy()">${activeTargets.length > 0 ? 'Use official endpoints' : 'Activate compatible routes'}</button>
|
|
1529
1690
|
<button class="btn btn-outline" onclick="loadStatus()">Refresh</button>
|
|
1530
1691
|
`;
|
|
1692
|
+
} else if (route === 'compact') {
|
|
1693
|
+
loadIdleCompact();
|
|
1694
|
+
titleEl.textContent = 'Idle Compaction';
|
|
1695
|
+
subEl.textContent = 'Edge gateway prefix compaction and cache pruning for long sessions.';
|
|
1696
|
+
actionsEl.innerHTML = `
|
|
1697
|
+
<button class="btn btn-outline" onclick="loadIdleCompact()">Refresh</button>
|
|
1698
|
+
<button class="btn btn-primary" onclick="saveIdleCompact()">Save Configuration</button>
|
|
1699
|
+
`;
|
|
1531
1700
|
} else if (route === 'models') {
|
|
1532
1701
|
titleEl.textContent = 'Dynamic Model Catalog';
|
|
1533
1702
|
subEl.textContent = 'Auto-polled models from upstream providers to guarantee zero 404/drift errors.';
|
|
@@ -1675,6 +1844,11 @@
|
|
|
1675
1844
|
return defaultModels[slotId] || '';
|
|
1676
1845
|
}
|
|
1677
1846
|
|
|
1847
|
+
function effortForSlot(modelEfforts, slotId) {
|
|
1848
|
+
if (!modelEfforts) return 'auto';
|
|
1849
|
+
return modelEfforts[slotId] || 'auto';
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1678
1852
|
function renderModelSlots(slotFmt, p) {
|
|
1679
1853
|
const container = document.getElementById('slot-fields');
|
|
1680
1854
|
const heading = document.getElementById('model-slot-heading');
|
|
@@ -1687,11 +1861,27 @@
|
|
|
1687
1861
|
|
|
1688
1862
|
for (const s of cfg.slots) {
|
|
1689
1863
|
const val = currentProfileBuffer ? valueForSlot(currentProfileBuffer.defaultModels, s.id) : valueForSlot(p?.defaultModels, s.id);
|
|
1864
|
+
const effVal = currentProfileBuffer ? effortForSlot(currentProfileBuffer.modelEfforts, s.id) : effortForSlot(p?.modelEfforts, s.id);
|
|
1690
1865
|
|
|
1691
1866
|
const slotRow = document.createElement('div');
|
|
1692
|
-
slotRow.
|
|
1867
|
+
slotRow.className = 'slot-row-card';
|
|
1868
|
+
slotRow.style.cssText = 'display:flex; flex-direction:column; gap:0.35rem; padding:0.6rem 0.75rem; border-radius:6px; background:var(--card-bg, rgba(255,255,255,0.02)); border:1px solid var(--line);';
|
|
1693
1869
|
slotRow.innerHTML = `
|
|
1694
|
-
<
|
|
1870
|
+
<div style="display:flex; justify-content:space-between; align-items:center; gap:0.5rem;">
|
|
1871
|
+
<label for="p-${s.id}" style="font-size:0.82rem; font-weight:600; margin:0;">${escapeHtml(s.name)}</label>
|
|
1872
|
+
<div style="display:flex; align-items:center; gap:0.35rem;">
|
|
1873
|
+
<label for="p-effort-${s.id}" style="font-size:0.75rem; color:var(--muted); margin:0;">Target Effort:</label>
|
|
1874
|
+
<select id="p-effort-${s.id}" class="slot-effort inp" onchange="formDirty = true" style="height:26px; padding:0 0.4rem; font-size:0.78rem; border-radius:4px; min-width:6.8rem;">
|
|
1875
|
+
<option value="auto">Auto (Default)</option>
|
|
1876
|
+
<option value="none">None (0)</option>
|
|
1877
|
+
<option value="low">Low</option>
|
|
1878
|
+
<option value="medium">Medium</option>
|
|
1879
|
+
<option value="high">High</option>
|
|
1880
|
+
<option value="xhigh">Extra High</option>
|
|
1881
|
+
<option value="max">Max</option>
|
|
1882
|
+
</select>
|
|
1883
|
+
</div>
|
|
1884
|
+
</div>
|
|
1695
1885
|
<div class="combo">
|
|
1696
1886
|
<input type="text" id="p-${s.id}" oninput="updateWindowNotes()" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="p-${s.id}-list" autocomplete="off" spellcheck="false" placeholder="${escapeHtml(s.placeholder)}" value="${escapeHtml(val)}">
|
|
1697
1887
|
<button type="button" class="combo-toggle" tabindex="-1" aria-label="Show models for ${escapeHtml(s.name)}">
|
|
@@ -1699,10 +1889,15 @@
|
|
|
1699
1889
|
</button>
|
|
1700
1890
|
<ul class="combo-list" id="p-${s.id}-list" role="listbox" aria-label="${escapeHtml(s.name)} models" hidden></ul>
|
|
1701
1891
|
</div>
|
|
1702
|
-
<div
|
|
1892
|
+
<div style="display:flex; justify-content:space-between; align-items:center; gap:0.5rem; min-height:1rem;">
|
|
1893
|
+
<div class="slot-note" id="p-${s.id}-note" role="status" aria-live="polite"></div>
|
|
1894
|
+
<div class="slot-bifrost-hint" id="p-bifrost-${s.id}" style="display:none; font-size:0.74rem; color:#60a5fa;">⚡ Bifrost Passthrough (native effort)</div>
|
|
1895
|
+
</div>
|
|
1703
1896
|
`;
|
|
1704
1897
|
container.appendChild(slotRow);
|
|
1705
1898
|
wireCombo(slotRow.querySelector('.combo'));
|
|
1899
|
+
const effSelect = slotRow.querySelector('#p-effort-' + s.id);
|
|
1900
|
+
if (effSelect) effSelect.value = effVal || 'auto';
|
|
1706
1901
|
}
|
|
1707
1902
|
updateWindowNotes();
|
|
1708
1903
|
}
|
|
@@ -1716,6 +1911,7 @@
|
|
|
1716
1911
|
const cfg = getActiveSlotConfig(renderedSlotFormat);
|
|
1717
1912
|
if (!currentProfileBuffer) currentProfileBuffer = {};
|
|
1718
1913
|
const defaultModels = { ...(currentProfileBuffer.defaultModels || {}) };
|
|
1914
|
+
const modelEfforts = { ...(currentProfileBuffer.modelEfforts || {}) };
|
|
1719
1915
|
for (const s of cfg.slots) {
|
|
1720
1916
|
const inp = document.getElementById('p-' + s.id);
|
|
1721
1917
|
if (inp) {
|
|
@@ -1723,13 +1919,19 @@
|
|
|
1723
1919
|
if (val) defaultModels[s.id] = val;
|
|
1724
1920
|
else delete defaultModels[s.id];
|
|
1725
1921
|
}
|
|
1922
|
+
const effSelect = document.getElementById('p-effort-' + s.id);
|
|
1923
|
+
if (effSelect) {
|
|
1924
|
+
const effVal = effSelect.value;
|
|
1925
|
+
if (effVal && effVal !== 'auto') modelEfforts[s.id] = effVal;
|
|
1926
|
+
else delete modelEfforts[s.id];
|
|
1927
|
+
}
|
|
1726
1928
|
}
|
|
1727
1929
|
currentProfileBuffer.defaultModels = defaultModels;
|
|
1930
|
+
currentProfileBuffer.modelEfforts = modelEfforts;
|
|
1728
1931
|
// An old config can still carry the retired 1M flags. They are dropped on the next save
|
|
1729
1932
|
// instead of being written back: the window is the model's own now.
|
|
1730
1933
|
delete currentProfileBuffer.model1M;
|
|
1731
|
-
return { defaultModels };
|
|
1732
|
-
|
|
1934
|
+
return { defaultModels, modelEfforts };
|
|
1733
1935
|
}
|
|
1734
1936
|
|
|
1735
1937
|
// 128000 reads as 128K and 262144 as 256K: a round decimal count keeps base 1000.
|
|
@@ -1756,6 +1958,64 @@
|
|
|
1756
1958
|
const model = document.getElementById('p-' + s.id)?.value.trim() || '';
|
|
1757
1959
|
note.textContent = model ? windowLine(model) : '';
|
|
1758
1960
|
}
|
|
1961
|
+
syncEffortOptions();
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
function syncEffortOptions() {
|
|
1965
|
+
const mode = document.getElementById('p-thinking-mode')?.value;
|
|
1966
|
+
const url = (document.getElementById('p-url')?.value || '').toLowerCase();
|
|
1967
|
+
const tool = document.getElementById('p-tool')?.value || 'auto';
|
|
1968
|
+
const isIntact = url.includes('intact') || url.includes(':20130') || url.includes(':8080') ||
|
|
1969
|
+
url.includes(':20142') || url.includes(':20150') || url.includes(':20152');
|
|
1970
|
+
const isThinkingOff = mode === 'off';
|
|
1971
|
+
|
|
1972
|
+
const cfg = getActiveSlotConfig(renderedSlotFormat);
|
|
1973
|
+
let anyBifrost = false;
|
|
1974
|
+
|
|
1975
|
+
for (const s of cfg.slots) {
|
|
1976
|
+
const inp = document.getElementById('p-' + s.id);
|
|
1977
|
+
const effSelect = document.getElementById('p-effort-' + s.id);
|
|
1978
|
+
const bifrostHint = document.getElementById('p-bifrost-' + s.id);
|
|
1979
|
+
if (!effSelect) continue;
|
|
1980
|
+
|
|
1981
|
+
const modelVal = (inp?.value || '').trim().toLowerCase();
|
|
1982
|
+
let slotIsBifrost = false;
|
|
1983
|
+
if (isIntact && modelVal) {
|
|
1984
|
+
if (tool === 'claude') slotIsBifrost = modelVal.startsWith('claude/') || modelVal.startsWith('anthropic/');
|
|
1985
|
+
else if (tool === 'codex') slotIsBifrost = modelVal.startsWith('codex/');
|
|
1986
|
+
else if (tool === 'agy') slotIsBifrost = modelVal.startsWith('antigravity/');
|
|
1987
|
+
else if (tool === 'auto') slotIsBifrost = modelVal.startsWith('claude/') || modelVal.startsWith('anthropic/') || modelVal.startsWith('codex/') || modelVal.startsWith('antigravity/');
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
if (slotIsBifrost) {
|
|
1991
|
+
anyBifrost = true;
|
|
1992
|
+
if (bifrostHint) bifrostHint.style.display = 'block';
|
|
1993
|
+
effSelect.disabled = true;
|
|
1994
|
+
effSelect.style.opacity = '0.6';
|
|
1995
|
+
effSelect.title = 'Bifrost passthrough active: effort controlled natively by client tool';
|
|
1996
|
+
} else {
|
|
1997
|
+
if (bifrostHint) bifrostHint.style.display = 'none';
|
|
1998
|
+
if (isThinkingOff) {
|
|
1999
|
+
effSelect.disabled = true;
|
|
2000
|
+
effSelect.style.opacity = '0.5';
|
|
2001
|
+
effSelect.title = 'Thinking mode is OFF globally';
|
|
2002
|
+
} else {
|
|
2003
|
+
effSelect.disabled = false;
|
|
2004
|
+
effSelect.style.opacity = '1';
|
|
2005
|
+
effSelect.title = 'Target thinking effort for this model';
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
const effortEl = document.getElementById('p-thinking-effort');
|
|
2011
|
+
const globalBifrostHint = document.getElementById('bifrost-hint');
|
|
2012
|
+
if (effortEl) {
|
|
2013
|
+
effortEl.disabled = isThinkingOff;
|
|
2014
|
+
effortEl.style.opacity = isThinkingOff ? '0.5' : '1';
|
|
2015
|
+
}
|
|
2016
|
+
if (globalBifrostHint) {
|
|
2017
|
+
globalBifrostHint.style.display = anyBifrost ? 'block' : 'none';
|
|
2018
|
+
}
|
|
1759
2019
|
}
|
|
1760
2020
|
|
|
1761
2021
|
// Prefetch Counts for Nav Badges
|
|
@@ -1783,6 +2043,8 @@
|
|
|
1783
2043
|
if (ctLogs && lastLogsCount > 0) ctLogs.textContent = lastLogsCount;
|
|
1784
2044
|
}
|
|
1785
2045
|
} catch {}
|
|
2046
|
+
|
|
2047
|
+
loadIdleCompact();
|
|
1786
2048
|
}
|
|
1787
2049
|
|
|
1788
2050
|
// Load Status
|
|
@@ -1790,13 +2052,64 @@
|
|
|
1790
2052
|
// dashboard and the terminal cannot show two different settings.
|
|
1791
2053
|
function showIdleCompact(c) {
|
|
1792
2054
|
c = c || {};
|
|
1793
|
-
|
|
1794
|
-
document.getElementById('idle-compact-
|
|
1795
|
-
|
|
1796
|
-
document.getElementById('idle-compact-
|
|
2055
|
+
const enabled = !!c.enabled;
|
|
2056
|
+
const elEnabled = document.getElementById('idle-compact-enabled');
|
|
2057
|
+
if (elEnabled) elEnabled.checked = enabled;
|
|
2058
|
+
const configSec = document.getElementById('idle-compact-config-section');
|
|
2059
|
+
if (configSec) configSec.style.display = enabled ? 'flex' : 'none';
|
|
2060
|
+
const elMinutes = document.getElementById('idle-compact-minutes');
|
|
2061
|
+
if (elMinutes) elMinutes.value = c.idleMinutes ?? 15;
|
|
2062
|
+
const elKb = document.getElementById('idle-compact-kb');
|
|
2063
|
+
if (elKb) elKb.value = (c.minBytes ?? 65536) / 1024;
|
|
2064
|
+
const elModel = document.getElementById('idle-compact-model');
|
|
2065
|
+
if (elModel) elModel.value = c.model || '';
|
|
2066
|
+
const elCodex = document.getElementById('idle-compact-codex');
|
|
2067
|
+
if (elCodex) elCodex.checked = !!c.codex;
|
|
2068
|
+
const elKeep = document.getElementById('idle-compact-keep');
|
|
2069
|
+
if (elKeep) elKeep.value = c.keepRecent ?? 6;
|
|
2070
|
+
const elUserChars = document.getElementById('idle-compact-user-chars');
|
|
2071
|
+
if (elUserChars) elUserChars.value = c.userChars ?? 3000;
|
|
2072
|
+
const elSummaryChars = document.getElementById('idle-compact-summary-chars');
|
|
2073
|
+
if (elSummaryChars) elSummaryChars.value = c.summaryMaxChars ?? 24000;
|
|
2074
|
+
const elLookback = document.getElementById('idle-compact-lookback');
|
|
2075
|
+
if (elLookback) elLookback.value = c.sessionLookbackHours ?? 72;
|
|
2076
|
+
|
|
2077
|
+
// Badge on hero card
|
|
1797
2078
|
const badge = document.getElementById('badge-idle-compact');
|
|
1798
|
-
badge
|
|
1799
|
-
|
|
2079
|
+
if (badge) {
|
|
2080
|
+
badge.textContent = enabled ? 'ACTIVE' : 'OFF';
|
|
2081
|
+
badge.className = 'badge ' + (enabled ? 'ok' : 'outline');
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// Nav link count
|
|
2085
|
+
const ctCompact = document.getElementById('ct-compact');
|
|
2086
|
+
if (ctCompact) {
|
|
2087
|
+
ctCompact.textContent = enabled ? 'ON' : '';
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// Stats cards
|
|
2091
|
+
const statStatus = document.getElementById('stat-compact-status');
|
|
2092
|
+
if (statStatus) {
|
|
2093
|
+
statStatus.textContent = enabled ? 'Active' : 'Disabled';
|
|
2094
|
+
statStatus.style.color = enabled ? 'var(--ok)' : 'var(--muted)';
|
|
2095
|
+
}
|
|
2096
|
+
const statInd = document.getElementById('stat-compact-indicator');
|
|
2097
|
+
if (statInd) {
|
|
2098
|
+
statInd.textContent = enabled ? 'Prefix caching active' : 'Prefix caching inactive';
|
|
2099
|
+
statInd.className = 'status ' + (enabled ? 'on' : '');
|
|
2100
|
+
}
|
|
2101
|
+
const statPause = document.getElementById('stat-compact-pause');
|
|
2102
|
+
if (statPause) {
|
|
2103
|
+
statPause.textContent = (c.idleMinutes ?? 15) + ' min';
|
|
2104
|
+
}
|
|
2105
|
+
const statSize = document.getElementById('stat-compact-size');
|
|
2106
|
+
if (statSize) {
|
|
2107
|
+
statSize.textContent = ((c.minBytes ?? 65536) / 1024) + ' KB';
|
|
2108
|
+
}
|
|
2109
|
+
const statLookback = document.getElementById('stat-compact-lookback');
|
|
2110
|
+
if (statLookback) {
|
|
2111
|
+
statLookback.textContent = (c.sessionLookbackHours ?? 72) + ' hrs';
|
|
2112
|
+
}
|
|
1800
2113
|
}
|
|
1801
2114
|
|
|
1802
2115
|
async function loadIdleCompact() {
|
|
@@ -1807,14 +2120,35 @@
|
|
|
1807
2120
|
}
|
|
1808
2121
|
|
|
1809
2122
|
async function saveIdleCompact() {
|
|
2123
|
+
const isEnabled = document.getElementById('idle-compact-enabled').checked;
|
|
2124
|
+
const configSec = document.getElementById('idle-compact-config-section');
|
|
2125
|
+
if (configSec) configSec.style.display = isEnabled ? 'flex' : 'none';
|
|
1810
2126
|
const status = document.getElementById('idle-compact-status');
|
|
2127
|
+
for (const input of document.querySelectorAll('[id^="idle-compact-"][type="number"]')) {
|
|
2128
|
+
if (!input.value || !input.checkValidity()) {
|
|
2129
|
+
input.reportValidity();
|
|
2130
|
+
if (status) {
|
|
2131
|
+
status.textContent = 'Not saved: enter a value within the displayed bounds.';
|
|
2132
|
+
status.style.color = 'var(--err)';
|
|
2133
|
+
}
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
1811
2137
|
const patch = {
|
|
2138
|
+
codex: document.getElementById('idle-compact-codex').checked,
|
|
2139
|
+
keepRecent: Number(document.getElementById('idle-compact-keep').value),
|
|
2140
|
+
userChars: Number(document.getElementById('idle-compact-user-chars').value),
|
|
2141
|
+
summaryMaxChars: Number(document.getElementById('idle-compact-summary-chars').value),
|
|
2142
|
+
sessionLookbackHours: Number(document.getElementById('idle-compact-lookback').value),
|
|
1812
2143
|
enabled: document.getElementById('idle-compact-enabled').checked,
|
|
1813
|
-
idleMinutes: Number(document.getElementById('idle-compact-minutes').value)
|
|
1814
|
-
minBytes: (Number(document.getElementById('idle-compact-kb').value)
|
|
2144
|
+
idleMinutes: Number(document.getElementById('idle-compact-minutes').value),
|
|
2145
|
+
minBytes: Math.round(Number(document.getElementById('idle-compact-kb').value) * 1024),
|
|
1815
2146
|
model: document.getElementById('idle-compact-model').value.trim()
|
|
1816
2147
|
};
|
|
1817
|
-
status
|
|
2148
|
+
if (status) {
|
|
2149
|
+
status.textContent = 'Saving...';
|
|
2150
|
+
status.style.color = 'var(--muted)';
|
|
2151
|
+
}
|
|
1818
2152
|
try {
|
|
1819
2153
|
const r = await api('/api/idle-compact', {
|
|
1820
2154
|
method: 'POST',
|
|
@@ -1823,9 +2157,18 @@
|
|
|
1823
2157
|
});
|
|
1824
2158
|
if (!r.ok) throw new Error((await r.json()).error || r.status);
|
|
1825
2159
|
showIdleCompact((await r.json()).idleCompact);
|
|
1826
|
-
status
|
|
2160
|
+
if (status) {
|
|
2161
|
+
status.textContent = 'Saved. Applies to subsequent conversations that resume after a pause.';
|
|
2162
|
+
status.style.color = 'var(--ok)';
|
|
2163
|
+
setTimeout(() => { if (status) status.textContent = ''; }, 4500);
|
|
2164
|
+
}
|
|
2165
|
+
showToast('Idle compaction configuration saved');
|
|
1827
2166
|
} catch (e) {
|
|
1828
|
-
status
|
|
2167
|
+
if (status) {
|
|
2168
|
+
status.textContent = 'Not saved: ' + e.message;
|
|
2169
|
+
status.style.color = 'var(--err)';
|
|
2170
|
+
}
|
|
2171
|
+
showToast('Failed to save: ' + e.message, true);
|
|
1829
2172
|
}
|
|
1830
2173
|
}
|
|
1831
2174
|
|
|
@@ -2036,6 +2379,7 @@
|
|
|
2036
2379
|
formLoadedKey = key;
|
|
2037
2380
|
formDirty = false;
|
|
2038
2381
|
currentProfileBuffer = JSON.parse(JSON.stringify(p));
|
|
2382
|
+
if (!currentProfileBuffer.modelEfforts) currentProfileBuffer.modelEfforts = {};
|
|
2039
2383
|
|
|
2040
2384
|
document.getElementById('form-title').textContent = 'Edit Profile: ' + p.name;
|
|
2041
2385
|
document.getElementById('p-key').value = key;
|
|
@@ -2048,6 +2392,11 @@
|
|
|
2048
2392
|
document.getElementById('p-outformat').value = p.outFormat || '';
|
|
2049
2393
|
document.getElementById('p-mode').value = p.mode || 'convert';
|
|
2050
2394
|
document.getElementById('p-thinking-mode').value = p.thinkingMode || 'auto';
|
|
2395
|
+
if (document.getElementById('p-thinking-effort')) {
|
|
2396
|
+
document.getElementById('p-thinking-effort').value = p.thinkingEffort || 'auto';
|
|
2397
|
+
}
|
|
2398
|
+
renderModelSlots(toolVal, p);
|
|
2399
|
+
syncEffortOptions();
|
|
2051
2400
|
document.getElementById('btn-delete').style.display = 'inline-flex';
|
|
2052
2401
|
|
|
2053
2402
|
const activeMap = state.activeProfiles || (state.config?.activeProfiles || {});
|
|
@@ -2056,8 +2405,6 @@
|
|
|
2056
2405
|
if (activeTag) {
|
|
2057
2406
|
activeTag.textContent = activeTargets.length ? '● Active: ' + activeTargets.join(', ') : '';
|
|
2058
2407
|
}
|
|
2059
|
-
|
|
2060
|
-
renderModelSlots(toolVal, p);
|
|
2061
2408
|
}
|
|
2062
2409
|
|
|
2063
2410
|
function initNewProfile() {
|
|
@@ -2072,7 +2419,8 @@
|
|
|
2072
2419
|
outFormat: 'openai-chat',
|
|
2073
2420
|
mode: 'convert',
|
|
2074
2421
|
thinkingMode: 'auto',
|
|
2075
|
-
defaultModels: {}
|
|
2422
|
+
defaultModels: {},
|
|
2423
|
+
modelEfforts: {}
|
|
2076
2424
|
};
|
|
2077
2425
|
|
|
2078
2426
|
document.getElementById('form-title').textContent = 'Add New Provider Profile';
|
|
@@ -2085,10 +2433,14 @@
|
|
|
2085
2433
|
document.getElementById('p-outformat').value = 'openai-chat';
|
|
2086
2434
|
document.getElementById('p-mode').value = 'convert';
|
|
2087
2435
|
document.getElementById('p-thinking-mode').value = 'auto';
|
|
2436
|
+
if (document.getElementById('p-thinking-effort')) {
|
|
2437
|
+
document.getElementById('p-thinking-effort').value = 'auto';
|
|
2438
|
+
}
|
|
2439
|
+
renderModelSlots('auto', currentProfileBuffer);
|
|
2440
|
+
syncEffortOptions();
|
|
2088
2441
|
document.getElementById('btn-delete').style.display = 'none';
|
|
2089
2442
|
document.getElementById('active-tag').textContent = '';
|
|
2090
2443
|
|
|
2091
|
-
renderModelSlots('auto', currentProfileBuffer);
|
|
2092
2444
|
switchTab('tab-general');
|
|
2093
2445
|
}
|
|
2094
2446
|
|
|
@@ -2163,6 +2515,8 @@
|
|
|
2163
2515
|
document.getElementById('p-outformat').value = t.outFormat;
|
|
2164
2516
|
document.getElementById('p-mode').value = t.mode;
|
|
2165
2517
|
document.getElementById('p-thinking-mode').value = t.thinkingMode;
|
|
2518
|
+
document.getElementById('p-thinking-effort').value = t.thinkingEffort || 'auto';
|
|
2519
|
+
syncEffortOptions();
|
|
2166
2520
|
currentProfileBuffer = { ...currentProfileBuffer, ...t };
|
|
2167
2521
|
formDirty = true;
|
|
2168
2522
|
renderModelSlots(t.tool || 'auto', t);
|
|
@@ -2218,6 +2572,13 @@
|
|
|
2218
2572
|
// already had the key.
|
|
2219
2573
|
const thinkingMode = document.getElementById('p-thinking-mode').value;
|
|
2220
2574
|
if (thinkingMode !== 'auto' || existing.thinkingMode) profilePayload.thinkingMode = thinkingMode;
|
|
2575
|
+
const thinkingEffort = document.getElementById('p-thinking-effort')?.value;
|
|
2576
|
+
if (thinkingEffort && (thinkingEffort !== 'auto' || existing.thinkingEffort)) {
|
|
2577
|
+
profilePayload.thinkingEffort = thinkingEffort;
|
|
2578
|
+
}
|
|
2579
|
+
if (currentProfileBuffer?.modelEfforts && Object.keys(currentProfileBuffer.modelEfforts).length > 0) {
|
|
2580
|
+
profilePayload.modelEfforts = { ...currentProfileBuffer.modelEfforts };
|
|
2581
|
+
}
|
|
2221
2582
|
|
|
2222
2583
|
if (keyVal === MASKED_KEY) {
|
|
2223
2584
|
profilePayload.apiKey = existing.apiKey || '';
|
|
@@ -2873,6 +3234,13 @@
|
|
|
2873
3234
|
// Setup log auto-refresh
|
|
2874
3235
|
toggleLogAutoRefresh();
|
|
2875
3236
|
|
|
3237
|
+
// Setup profile modal effort & bifrost change triggers
|
|
3238
|
+
document.getElementById('p-thinking-mode')?.addEventListener('change', syncEffortOptions);
|
|
3239
|
+
document.getElementById('p-mode')?.addEventListener('change', syncEffortOptions);
|
|
3240
|
+
document.getElementById('p-outformat')?.addEventListener('change', syncEffortOptions);
|
|
3241
|
+
document.getElementById('p-url')?.addEventListener('input', syncEffortOptions);
|
|
3242
|
+
document.getElementById('p-tool')?.addEventListener('change', syncEffortOptions);
|
|
3243
|
+
|
|
2876
3244
|
// Trigger initial route
|
|
2877
3245
|
handleRoute();
|
|
2878
3246
|
|