RayMelius Claude Sonnet 4.6 commited on
Commit
6f4ff21
Β·
1 Parent(s): 5a7145d

Show all HF models individually in SWITCH LLM popup

Browse files

- providers list now has 4 separate HF entries (Zephyr, Qwen, Llama, Mistral)
- SwitchProviderRequest accepts optional model field
- switch_llm_provider() forwards model to create_llm_client()
- /llm/providers returns current_model so UI can checkmark the right row
- active checkmark matches on both provider id + model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

src/soci/api/routes.py CHANGED
@@ -263,24 +263,29 @@ async def get_conversations(include_history: bool = True, limit: int = 20):
263
 
264
  class SwitchProviderRequest(BaseModel):
265
  provider: str
 
266
 
267
 
268
  @router.get("/llm/providers")
269
  async def get_llm_providers():
270
  """Return available LLM providers (those with API keys set) and the current one."""
271
  import os
272
- from soci.api.server import get_llm_provider
273
  current = get_llm_provider()
 
274
  providers = []
275
  if os.environ.get("ANTHROPIC_API_KEY"):
276
- providers.append({"id": "claude", "label": "Claude (Anthropic)", "icon": "β—†"})
277
  if os.environ.get("GROQ_API_KEY"):
278
- providers.append({"id": "groq", "label": "Groq (Llama 8B)", "icon": "⚑"})
279
  if os.environ.get("GEMINI_API_KEY"):
280
- providers.append({"id": "gemini", "label": "Gemini 2.0 Flash", "icon": "✦"})
281
- providers.append( {"id": "hf", "label": "HF Zephyr 7B", "icon": "πŸ€—"})
282
- providers.append( {"id": "ollama", "label": "Ollama (local)", "icon": "πŸ¦™"})
283
- return {"current": current, "providers": providers}
 
 
 
284
 
285
 
286
  @router.get("/llm/test")
@@ -309,8 +314,8 @@ async def set_llm_provider(req: SwitchProviderRequest):
309
  if req.provider not in valid:
310
  raise HTTPException(status_code=400, detail=f"Unknown provider '{req.provider}'")
311
  try:
312
- await switch_llm_provider(req.provider)
313
- return {"ok": True, "provider": req.provider}
314
  except Exception as e:
315
  raise HTTPException(status_code=500, detail=str(e))
316
 
 
263
 
264
  class SwitchProviderRequest(BaseModel):
265
  provider: str
266
+ model: Optional[str] = None
267
 
268
 
269
  @router.get("/llm/providers")
270
  async def get_llm_providers():
271
  """Return available LLM providers (those with API keys set) and the current one."""
272
  import os
273
+ from soci.api.server import get_llm_provider, get_simulation
274
  current = get_llm_provider()
275
+ current_model = getattr(get_simulation().llm, "default_model", "")
276
  providers = []
277
  if os.environ.get("ANTHROPIC_API_KEY"):
278
+ providers.append({"id": "claude", "label": "Claude Haiku", "icon": "β—†", "model": ""})
279
  if os.environ.get("GROQ_API_KEY"):
280
+ providers.append({"id": "groq", "label": "Groq Llama 8B", "icon": "⚑", "model": ""})
281
  if os.environ.get("GEMINI_API_KEY"):
282
+ providers.append({"id": "gemini", "label": "Gemini 2.0 Flash", "icon": "✦", "model": ""})
283
+ providers.append({"id": "hf", "model": "HuggingFaceH4/zephyr-7b-beta", "label": "HF Zephyr 7B", "icon": "πŸ€—"})
284
+ providers.append({"id": "hf", "model": "Qwen/Qwen2.5-7B-Instruct", "label": "HF Qwen 2.5 7B", "icon": "πŸ€—"})
285
+ providers.append({"id": "hf", "model": "meta-llama/Llama-3.2-3B-Instruct", "label": "HF Llama 3.2 3B", "icon": "πŸ€—"})
286
+ providers.append({"id": "hf", "model": "mistralai/Mistral-7B-Instruct-v0.3", "label": "HF Mistral 7B", "icon": "πŸ€—"})
287
+ providers.append({"id": "ollama", "label": "Ollama (local)", "icon": "πŸ¦™", "model": ""})
288
+ return {"current": current, "current_model": current_model, "providers": providers}
289
 
290
 
291
  @router.get("/llm/test")
 
314
  if req.provider not in valid:
315
  raise HTTPException(status_code=400, detail=f"Unknown provider '{req.provider}'")
316
  try:
317
+ await switch_llm_provider(req.provider, model=req.model or None)
318
+ return {"ok": True, "provider": req.provider, "model": req.model}
319
  except Exception as e:
320
  raise HTTPException(status_code=500, detail=str(e))
321
 
src/soci/api/server.py CHANGED
@@ -57,14 +57,14 @@ def get_llm_provider() -> str:
57
  return _llm_provider
58
 
59
 
60
- async def switch_llm_provider(provider: str) -> None:
61
  """Hot-swap the LLM client on the running simulation."""
62
  global _llm_provider, _simulation
63
  assert _simulation is not None, "Simulation not initialized"
64
- new_llm = create_llm_client(provider=provider)
65
  _simulation.llm = new_llm
66
  _llm_provider = provider
67
- logger.info(f"LLM provider switched to: {provider} ({new_llm.__class__.__name__})")
68
 
69
 
70
  async def simulation_loop(sim: Simulation, db: Database, tick_delay: float = 2.0) -> None:
 
57
  return _llm_provider
58
 
59
 
60
+ async def switch_llm_provider(provider: str, model: Optional[str] = None) -> None:
61
  """Hot-swap the LLM client on the running simulation."""
62
  global _llm_provider, _simulation
63
  assert _simulation is not None, "Simulation not initialized"
64
+ new_llm = create_llm_client(provider=provider, model=model)
65
  _simulation.llm = new_llm
66
  _llm_provider = provider
67
+ logger.info(f"LLM provider switched to: {provider}/{model or 'default'} ({new_llm.__class__.__name__})")
68
 
69
 
70
  async def simulation_loop(sim: Simulation, db: Database, tick_delay: float = 2.0) -> None:
web/index.html CHANGED
@@ -3373,19 +3373,22 @@ document.getElementById('llm-model').addEventListener('click', async (e) => {
3373
  existing.forEach(el => el.remove());
3374
 
3375
  for (const p of data.providers) {
 
3376
  const row = document.createElement('div');
3377
- row.className = 'llm-opt' + (p.id === data.current ? ' active' : '');
3378
- row.innerHTML = `<span class="llm-check">${p.id === data.current ? 'βœ”' : ''}</span>
3379
  <span style="font-size:15px">${p.icon}</span>
3380
  <span>${p.label}</span>`;
3381
  row.addEventListener('click', async () => {
3382
  popup.style.display = 'none'; _llmPopupOpen = false;
3383
- if (p.id === data.current) return;
3384
  try {
 
 
3385
  const r = await fetch(`${API_BASE}/llm/provider`, {
3386
  method: 'POST',
3387
  headers: {'Content-Type': 'application/json'},
3388
- body: JSON.stringify({provider: p.id}),
3389
  });
3390
  if (!r.ok) { const err = await r.json(); showToast(`LLM switch failed: ${err.detail}`, 'event'); return; }
3391
  showToast(`Switched to ${p.label}`, 'conv');
 
3373
  existing.forEach(el => el.remove());
3374
 
3375
  for (const p of data.providers) {
3376
+ const isActive = p.id === data.current && (p.model === data.current_model || (!p.model && p.id !== 'hf'));
3377
  const row = document.createElement('div');
3378
+ row.className = 'llm-opt' + (isActive ? ' active' : '');
3379
+ row.innerHTML = `<span class="llm-check">${isActive ? 'βœ”' : ''}</span>
3380
  <span style="font-size:15px">${p.icon}</span>
3381
  <span>${p.label}</span>`;
3382
  row.addEventListener('click', async () => {
3383
  popup.style.display = 'none'; _llmPopupOpen = false;
3384
+ if (isActive) return;
3385
  try {
3386
+ const body = {provider: p.id};
3387
+ if (p.model) body.model = p.model;
3388
  const r = await fetch(`${API_BASE}/llm/provider`, {
3389
  method: 'POST',
3390
  headers: {'Content-Type': 'application/json'},
3391
+ body: JSON.stringify(body),
3392
  });
3393
  if (!r.ok) { const err = await r.json(); showToast(`LLM switch failed: ${err.detail}`, 'event'); return; }
3394
  showToast(`Switched to ${p.label}`, 'conv');