free-coding-models 0.5.30 → 0.5.32
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/bin/free-coding-models.js +22 -2
- package/changelog/v0.5.31.md +6 -0
- package/changelog/v0.5.32.md +4 -0
- package/package.json +1 -1
- package/src/core/router-daemon.js +8 -1
- package/web/dist/assets/index-C_tCF0A5.js +40 -0
- package/web/dist/index.html +1 -1
- package/web/server.js +39 -0
- package/web/src/App.jsx +1 -0
- package/web/src/components/router/RouterView.jsx +54 -1
- package/web/dist/assets/index-I6N91rH7.js +0 -40
package/web/dist/index.html
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
49
49
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
50
50
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
51
|
-
<script type="module" crossorigin src="/assets/index-
|
|
51
|
+
<script type="module" crossorigin src="/assets/index-C_tCF0A5.js"></script>
|
|
52
52
|
<link rel="stylesheet" crossorigin href="/assets/index-CsFt3qt5.css">
|
|
53
53
|
</head>
|
|
54
54
|
<body>
|
package/web/server.js
CHANGED
|
@@ -751,6 +751,45 @@ async function handleRequest(req, res) {
|
|
|
751
751
|
return
|
|
752
752
|
}
|
|
753
753
|
|
|
754
|
+
// 📖 /api/router/sets/:name — PUT to edit/rename/replace models in the set.
|
|
755
|
+
const setPutMatch = url.pathname.match(/^\/api\/router\/sets\/([^/]+)$/)
|
|
756
|
+
if (setPutMatch && req.method === 'PUT') {
|
|
757
|
+
const setName = decodeURIComponent(setPutMatch[1])
|
|
758
|
+
const body = await readJsonBody(req)
|
|
759
|
+
const proxy = await proxyToDaemon(`/sets/${encodeURIComponent(setName)}`, {
|
|
760
|
+
method: 'PUT',
|
|
761
|
+
headers: { 'Content-Type': 'application/json' },
|
|
762
|
+
body: JSON.stringify(body),
|
|
763
|
+
})
|
|
764
|
+
if (proxy?.ok) { sendJson(res, 200, proxy.data); return }
|
|
765
|
+
// 📖 Fallback: if daemon is offline, edit config directly so the UI still works.
|
|
766
|
+
if (!proxy || !proxy.ok) {
|
|
767
|
+
if (!config.router) config.router = {}
|
|
768
|
+
if (!config.router.sets) config.router.sets = {}
|
|
769
|
+
if (config.router.sets[setName]) {
|
|
770
|
+
const nextName = typeof body.name === 'string' && body.name.trim() ? body.name.trim() : setName
|
|
771
|
+
const nextSets = { ...config.router.sets }
|
|
772
|
+
delete nextSets[setName]
|
|
773
|
+
nextSets[nextName] = {
|
|
774
|
+
...config.router.sets[setName],
|
|
775
|
+
...body,
|
|
776
|
+
name: nextName,
|
|
777
|
+
models: Array.isArray(body.models) ? body.models : config.router.sets[setName].models,
|
|
778
|
+
}
|
|
779
|
+
config.router.sets = nextSets
|
|
780
|
+
if (config.router.activeSet === setName) {
|
|
781
|
+
config.router.activeSet = nextName
|
|
782
|
+
}
|
|
783
|
+
saveConfig(config)
|
|
784
|
+
broadcastUpdate({ immediate: true })
|
|
785
|
+
sendJson(res, 200, { set: config.router.sets[nextName], router: config.router })
|
|
786
|
+
return
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
sendJson(res, proxy?.status || 502, proxy?.data || { error: 'Daemon not reachable' })
|
|
790
|
+
return
|
|
791
|
+
}
|
|
792
|
+
|
|
754
793
|
// 📖 /api/router/sets/:name/sync — re-run the probe-based sync pipeline
|
|
755
794
|
// 📖 against the named set. Used by the Web Router Dashboard's "Sync
|
|
756
795
|
// 📖 best models" button so the user can rebuild a set with models
|
package/web/src/App.jsx
CHANGED
|
@@ -55,7 +55,7 @@ const SAVE_STATUS_SAVING = { kind: 'saving' }
|
|
|
55
55
|
const SAVE_STATUS_SAVED = { kind: 'saved' }
|
|
56
56
|
const SAVE_STATUS_ERROR = (message) => ({ kind: 'error', message })
|
|
57
57
|
|
|
58
|
-
export default function RouterView({ onClose, onToast }) {
|
|
58
|
+
export default function RouterView({ onClose, onToast, favorites }) {
|
|
59
59
|
const [status, setStatus] = useState(null)
|
|
60
60
|
const [stats, setStats] = useState(null)
|
|
61
61
|
const [quickSetup, setQuickSetup] = useState(null)
|
|
@@ -252,6 +252,48 @@ export default function RouterView({ onClose, onToast }) {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
// 📖 Replace active set models with favorites
|
|
256
|
+
const handleUseFavorites = async () => {
|
|
257
|
+
if (!activeSetName) return
|
|
258
|
+
const favList = favorites?.favorites || []
|
|
259
|
+
if (favList.length === 0) {
|
|
260
|
+
onToast?.('You do not have any favorite models yet. Star some models first!', 'info')
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const nextModels = favList.map((key, idx) => {
|
|
265
|
+
const parts = key.split('/')
|
|
266
|
+
const provider = parts[0]
|
|
267
|
+
const model = parts.slice(1).join('/')
|
|
268
|
+
return { provider, model, priority: idx + 1 }
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
setSaveStatus(SAVE_STATUS_SAVING)
|
|
272
|
+
try {
|
|
273
|
+
const resp = await fetch(`/api/router/sets/${encodeURIComponent(activeSetName)}`, {
|
|
274
|
+
method: 'PUT',
|
|
275
|
+
headers: { 'Content-Type': 'application/json' },
|
|
276
|
+
body: JSON.stringify({ models: nextModels }),
|
|
277
|
+
})
|
|
278
|
+
if (!resp.ok) {
|
|
279
|
+
const err = await resp.json().catch(() => ({}))
|
|
280
|
+
throw new Error(err.error || `HTTP ${resp.status}`)
|
|
281
|
+
}
|
|
282
|
+
const data = await resp.json()
|
|
283
|
+
if (data?.sets?.[activeSetName]) {
|
|
284
|
+
setSetsData((prev) => ({ ...prev, sets: data.sets }))
|
|
285
|
+
} else {
|
|
286
|
+
await fetchSets()
|
|
287
|
+
}
|
|
288
|
+
setSaveStatus(SAVE_STATUS_SAVED)
|
|
289
|
+
onToast?.(`Replaced active set with ${favList.length} favorite model${favList.length === 1 ? '' : 's'}.`, 'success')
|
|
290
|
+
await fetchStatus()
|
|
291
|
+
} catch (err) {
|
|
292
|
+
setSaveStatus(SAVE_STATUS_ERROR(err.message || String(err)))
|
|
293
|
+
onToast?.(`Failed to replace set: ${err.message}`, 'error')
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
255
297
|
const persistReorder = useCallback(async (nextModels) => {
|
|
256
298
|
if (!activeSetName) return
|
|
257
299
|
setSaveStatus(SAVE_STATUS_SAVING)
|
|
@@ -675,6 +717,17 @@ export default function RouterView({ onClose, onToast }) {
|
|
|
675
717
|
<IconWand size={11} />
|
|
676
718
|
Sync best
|
|
677
719
|
</button>
|
|
720
|
+
{favorites && (
|
|
721
|
+
<button
|
|
722
|
+
className={styles.smallBtn}
|
|
723
|
+
onClick={handleUseFavorites}
|
|
724
|
+
disabled={saveStatus.kind === 'saving'}
|
|
725
|
+
title="Replace current router models with your favorite models"
|
|
726
|
+
>
|
|
727
|
+
<IconList size={11} />
|
|
728
|
+
Use favorites
|
|
729
|
+
</button>
|
|
730
|
+
)}
|
|
678
731
|
{/* 📖 Probe all — run AI Latency benchmarks on every model in the
|
|
679
732
|
set. Results stream into the rows below (AI Lat column). */}
|
|
680
733
|
<button
|