Add MCP control server, live API-generation view, and repetition penalty

- scripts/jwash_mcp.py: MCP server (FastMCP/stdio), an HTTP client of the running
  J-Wash server so an external LLM can drive an already-loaded model. Tools:
  generate, find_token, list_layers, scale_token/replace_token (pure-weights,
  layers required), set_intensity, list_edits/reset_edits.
- api/app.py + ui: /api/generate now records the last exchange (surfaced in
  /api/status) and broadcasts on /ws when done; the UI shows a "generated via
  API/MCP" panel at the top of the chat and an Options "API monitor" toggle that
  swaps the 2s status poll for an event-driven refresh.
- sampling: repetition penalty (default 1.0, applied in model_manager._sample),
  exposed as a "rep" field in the chat controls; intentionally not exposed
  through the MCP.
- ui: remove the redundant "md" chat toggle (already available in Options).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Extraltodeus
2026-07-14 07:29:29 +02:00
co-authored by Claude Opus 4.8
parent 407ce9eef0
commit 4bc7c8005e
5 changed files with 381 additions and 9 deletions
+21
View File
@@ -88,6 +88,9 @@ fit_manager.on_progress = _broadcast_fit
# concurrent HF downloads: one state per repo_id
_downloads = {}
_downloads_lock = threading.Lock()
# last synchronous /api/generate exchange (CLI/MCP): surfaced in /api/status so
# the UI can show what an external client generates, without persisting it
_last_generation = None
class LoadRequest(BaseModel):
@@ -203,6 +206,7 @@ def api_status():
"interventions": interventions.summary(),
"interventions_scale": interventions.global_scale,
"interventions_mode": interventions.mode,
"last_generation": _last_generation,
}
@@ -740,6 +744,23 @@ async def api_generate_sync(req: GenerateSyncRequest):
raise HTTPException(500, str(exc))
if done.get("error"):
raise HTTPException(500, done["error"])
global _last_generation
_last_generation = {
"n": (_last_generation or {}).get("n", 0) + 1,
"prompt": next(
(m.get("content", "") for m in reversed(req.messages) if m.get("role") == "user"),
"",
),
"text": done.get("text", ""),
"stats": done.get("stats"),
}
# nudge any watching UI to refresh once this API generation is done (used by
# the "API monitor" mode, which drops the 2s status poll for event-driven refresh)
for ws in list(_ws_locks):
try:
await _ws_send(ws, json.dumps({"type": "api_generation"}))
except Exception:
pass
return {"text": done.get("text", ""), "stats": done.get("stats")}