free-coding-models 0.5.83 → 0.5.85

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.
@@ -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-CSoKc1qF.js"></script>
51
+ <script type="module" crossorigin src="/assets/index-DWUely84.js"></script>
52
52
  <link rel="stylesheet" crossorigin href="/assets/index-C5-Ywvv3.css">
53
53
  </head>
54
54
  <body>
@@ -304,6 +304,32 @@ export default function RouterView({ onClose, onToast, favorites }) {
304
304
  }
305
305
  }
306
306
 
307
+ // 📖 t8 - family failover keeps the user inside the same model family when a
308
+ // request fails (e.g. DeepSeek on NIM -> DeepSeek on Together) instead of
309
+ // hopping to a different family. Per-set toggle, persisted via PUT /sets/:name.
310
+ const handleToggleFamilyFailover = async () => {
311
+ if (!activeSetName) return
312
+ const next = !(activeSet?.familyFailover !== false)
313
+ setSaveStatus(SAVE_STATUS_SAVING)
314
+ try {
315
+ const resp = await fetch(`/api/router/sets/${encodeURIComponent(activeSetName)}`, {
316
+ method: 'PUT',
317
+ headers: { 'Content-Type': 'application/json' },
318
+ body: JSON.stringify({ familyFailover: next }),
319
+ })
320
+ if (!resp.ok) {
321
+ const err = await resp.json().catch(() => ({}))
322
+ throw new Error(err.error || `HTTP ${resp.status}`)
323
+ }
324
+ setSaveStatus(SAVE_STATUS_SAVED)
325
+ onToast?.(`Family failover ${next ? 'enabled' : 'disabled'} for ${activeSetName}.`, 'info')
326
+ await fetchSets()
327
+ } catch (err) {
328
+ setSaveStatus(SAVE_STATUS_ERROR(err.message || String(err)))
329
+ onToast?.(`Failed to toggle family failover: ${err.message}`, 'error')
330
+ }
331
+ }
332
+
307
333
  const persistReorder = useCallback(async (nextModels) => {
308
334
  if (!activeSetName) return
309
335
  setSaveStatus(SAVE_STATUS_SAVING)
@@ -716,6 +742,15 @@ export default function RouterView({ onClose, onToast, favorites }) {
716
742
  ))}
717
743
  </select>
718
744
  )}
745
+ <button
746
+ className={`${styles.smallBtn} ${(activeSet?.familyFailover !== false) ? styles.probeBtnActive : ''}`}
747
+ onClick={handleToggleFamilyFailover}
748
+ disabled={saveStatus.kind === 'saving'}
749
+ title="When a model fails, first retry the same family on another provider (e.g. DeepSeek on NIM -> DeepSeek on Together) before falling back to set order"
750
+ >
751
+ <IconRoute size={11} />
752
+ Family failover: {activeSet?.familyFailover !== false ? 'on' : 'off'}
753
+ </button>
719
754
  </div>
720
755
  <div className={styles.setActions}>
721
756
  <SaveBadge status={saveStatus} />
@@ -1041,7 +1076,9 @@ export default function RouterView({ onClose, onToast, favorites }) {
1041
1076
  </span>
1042
1077
  <span className={styles.logDetail}>
1043
1078
  {[
1044
- entry.failover ? 'failover' : '',
1079
+ // 📖 t8 - a family hop stays in the same model family
1080
+ // (e.g. DeepSeek on another host) and is tagged as such.
1081
+ entry.failover ? (entry.failover_reason === 'family_failover' ? 'family' : 'failover') : '',
1045
1082
  entry.stream ? 'stream' : '',
1046
1083
  entry.error || '',
1047
1084
  ].filter(Boolean).join(', ')}