diff --git a/docs/cli.md b/docs/cli.md index 9144c65..d878ebd 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -133,31 +133,59 @@ Exit 0 → можно `up`. Exit 1 → причина в таблице / кра | Путь | Зачем | | --- | --- | | `/assistent` | Сводка: extension + overlay + roles + memory + live API + hints + playbook | +| `/assistent/diagnose` | То же + journal по умолчанию + session store meta | | `/assistent/extension` | DLL, Sqlite deps, Tab/Assets, git HEAD | | `/assistent/overlay` | personas на VM vs локальный `assistent-personas/` | | `/assistent/roles` | `ollama-roles.json` ↔ `/api/tags` (`default_chat`) | | `/assistent/memory` | `assistent.sqlite` + counts | | `/assistent/api` | `AssistentListPersonas/Models/Memory/Chats` через туннель (или SSH) | | `/assistent/api?chat_smoke=1` | + 1-token Ollama `/api/chat` (кратко грузит модель) | -| `/assistent/chat-eval` | **Opt-in** `GetNewSession` → `AssistentChat` (полный ответ ассистента) | | `/assistent/logs` | journalctl swarmui: build/load/Sqlite | | `/assistent?logs=1` | Сводка + journal | +| `/assistent/session` | **POST** — multi-turn debug chat (in-memory, TTL ~45m) | +| `/assistent/session/{id}/chat` | **POST** — ход + объект `trace` | +| `/assistent/session/{id}` | **GET** / **DELETE** — состояние / сброс | +| `/assistent/chat-eval` | One-shot: session + один chat + delete | Симптомы → куда смотреть: поле `playbook` в `/assistent`. -**`/assistent/chat-eval`** — не входит в `/snapshot`. Может загрузить chat-модель в VRAM и записать чат в Sqlite (если DLL на месте). Предпочтительно `POST` с JSON; для агентов удобен и `GET` с query. +**Session / chat-eval** не входят в `/snapshot`. Могут грузить VRAM, писать sqlite chat, тратить биллинг GPU. `compactContext` / Exact merge в Assistent HTTP API **нет** — sidecar реконструирует best-effort (`trace.gaps`). + +#### Agent playbook (curl) ```bash -# POST (предпочтительно) -curl -sS -X POST http://127.0.0.1:17821/assistent/chat-eval \ - -H "Content-Type: application/json" \ - -d "{\"message\":\"какой checkpoint и какие steps/cfg должны быть\",\"persona\":\"leonid\",\"timeout\":120}" +BASE=http://127.0.0.1:17821 -# GET (агенты / быстрый smoke) -curl -sS "http://127.0.0.1:17821/assistent/chat-eval?message=ping&timeout=90" +# Static+live health (no chat) +curl -sS "$BASE/assistent/diagnose" | jq '{ok,hints,playbook,session_store}' + +# Multi-turn conversational diagnostics +SID=$(curl -sS -X POST "$BASE/assistent/session" \ + -H 'Content-Type: application/json' \ + -d '{"persona":"neutral","pack":"ordinary","context":{"krea_profile":"turbo","checkpoint":"krea-turbo"}}' \ + | jq -r .debug_session_id) + +curl -sS -X POST "$BASE/assistent/session/$SID/chat" \ + -H 'Content-Type: application/json' \ + -d '{"message":"какой checkpoint и какие steps/cfg должны быть"}' \ + | jq '{ok,reply,trace:{timings_ms,patch,exact_merge,compact_context,system_layers,gaps}}' + +curl -sS -X POST "$BASE/assistent/session/$SID/chat" \ + -H 'Content-Type: application/json' \ + -d '{"message":"сделай generate с этими params"}' \ + | jq '{ok, trace:{patch,exact_merge}}' + +curl -sS "$BASE/assistent/session/$SID" | jq .session +curl -sS -X DELETE "$BASE/assistent/session/$SID" + +# One-shot convenience (session + one chat + delete) +curl -sS -X POST "$BASE/assistent/chat-eval" \ + -H 'Content-Type: application/json' \ + -d '{"message":"какие steps/cfg для turbo?","persona":"leonid","timeout":120}' \ + | jq '{ok,reply,patch,trace}' ``` -Ответ: `ok`, `ms` / `chat_ms`, `model` / `preferred`, `reply` / `reply_prose`, `patch` (prompt/steps/cfg/aspect/actions…), `errors`, `hints`. Туннель `:swarmui_local_port` (обычно 17801), иначе SSH на VM `:7801`. +Ответ chat: `ok`, `reply`, `trace` (`timings_ms`, `patch`, `exact_merge`, `compact_context`, `system_chars` / `system_layers`, `skills`, `hops`, `gaps`, `errors`/`warnings`/`hints`). Sqlite SaveChat fail — soft error, если текст ответа есть. После `up --no-tunnel` процесс завершается и sidecar гаснет — держи отдельно: @@ -165,7 +193,7 @@ curl -sS "http://127.0.0.1:17821/assistent/chat-eval?message=ping&timeout=90" gpu-rent debug ``` -Мутаций конфига/GPU нет (restart/hold/stop — как раньше через CLI). `chat_smoke` только пингует Ollama; `chat-eval` — полноценный AssistentChat (opt-in). +Мутаций конфига/GPU нет (restart/hold/stop — как раньше через CLI). `chat_smoke` только пингует Ollama; session/chat-eval — полноценный AssistentChat (opt-in). --- diff --git a/src/gpu_rent/debug_api.py b/src/gpu_rent/debug_api.py index 3a1ad81..c980fc4 100644 --- a/src/gpu_rent/debug_api.py +++ b/src/gpu_rent/debug_api.py @@ -1,8 +1,8 @@ """Localhost debug HTTP sidecar for gpu-rent. Bind only 127.0.0.1. Started from `up` / `tunnel` / `debug`. -Mostly read-only; `/assistent/chat-eval` is an opt-in AssistentChat probe -(may load VRAM / persist chat) and is not part of `/snapshot`. +Mostly read-only; Assistent session / chat-eval endpoints are opt-in +(may load VRAM / persist chat) and are not part of `/snapshot`. """ from __future__ import annotations @@ -63,16 +63,25 @@ _CHAT_EVAL_PARAMS = [ }, ] +_WARN_VRAM = ( + "WARNING: may load chat model into VRAM, may write assistent.sqlite chat, " + "and incurs GPU billing if the VM is up. Not part of /snapshot. " + "compactContext / Exact merge are reconstructed client-side " + "(Assistent HTTP API does not return them)." +) + _OPENAPI: dict[str, Any] = { "openapi": "3.0.3", "info": { "title": "gpu-rent Debug API", - "version": "1.1.0", + "version": "1.2.0", "description": ( "Localhost diagnostics for installer progress and " - "SwarmUI / Comfy / Ollama / Assistent. Mostly read-only; " - "POST/GET /assistent/chat-eval is opt-in AssistentChat " - "(may load VRAM / write chat) and is skipped from /snapshot." + "SwarmUI / Comfy / Ollama / Assistent. Mostly read-only. " + "Assistent multi-turn: POST /assistent/session + " + "/assistent/session/{id}/chat (trace object). " + "One-shot: POST/GET /assistent/chat-eval. " + + _WARN_VRAM ), }, "servers": [{"url": "http://127.0.0.1:17821"}], @@ -95,7 +104,9 @@ _OPENAPI: dict[str, Any] = { } }, "/snapshot": { - "get": {"summary": "Cheap aggregate (no full diag, no chat-eval)"} + "get": { + "summary": "Cheap aggregate (no full diag, no Assistent chat/session)" + } }, "/status": {"get": {"summary": "JSON mirror of gpu-rent status"}}, "/state": {"get": {"summary": "state.json without secrets"}}, @@ -142,6 +153,27 @@ _OPENAPI: dict[str, Any] = { ], } }, + "/assistent/diagnose": { + "get": { + "summary": ( + "Full static+live Assistent health (logs on by default). " + + _WARN_VRAM + ), + "parameters": [ + { + "name": "chat_smoke", + "in": "query", + "schema": {"type": "boolean"}, + }, + { + "name": "logs", + "in": "query", + "schema": {"type": "boolean", "default": True}, + "description": "Pass logs=0 to skip journal", + }, + ], + } + }, "/assistent/extension": { "get": {"summary": "DLL/Sqlite deps/Tab assets under Extensions/swarm-assistent"} }, @@ -171,18 +203,92 @@ _OPENAPI: dict[str, Any] = { "/assistent/logs": { "get": {"summary": "journalctl swarmui filtered for Assistent/Sqlite/build"} }, + "/assistent/session": { + "post": { + "summary": ( + "Start multi-turn Assistent debug session " + "(GetNewSession + optional GetConfig). " + _WARN_VRAM + ), + "requestBody": { + "required": False, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "persona": {"type": "string"}, + "pack": {"type": "string"}, + "model": {"type": "string"}, + "skills": { + "type": "array", + "items": {"type": "string"}, + }, + "context": { + "type": "object", + "description": ( + "Synthetic compactContext fields " + "(krea_profile, checkpoint, " + "recommended_params, …)" + ), + }, + "probe_config": { + "type": "boolean", + "default": True, + }, + }, + } + } + }, + }, + } + }, + "/assistent/session/{id}": { + "get": {"summary": "In-memory session summary + last trace"}, + "delete": {"summary": "Drop in-memory debug session"}, + }, + "/assistent/session/{id}/chat": { + "post": { + "summary": ( + "Send user message; returns reply + trace " + "(patch, Exact merge, compact_context sizes, system_layers). " + + _WARN_VRAM + ), + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["message"], + "properties": { + "message": {"type": "string"}, + "timeout": {"type": "number"}, + "persona": {"type": "string"}, + "pack": {"type": "string"}, + "model": {"type": "string"}, + "skills": { + "type": "array", + "items": {"type": "string"}, + }, + "context": {"type": "object"}, + }, + } + } + }, + }, + } + }, "/assistent/chat-eval": { "get": { "summary": ( - "Opt-in AssistentChat eval (GetNewSession→AssistentChat); " - "may load VRAM / write chat — not in /snapshot" + "One-shot AssistentChat (session+chat+delete). " + _WARN_VRAM ), "parameters": _CHAT_EVAL_PARAMS, }, "post": { "summary": ( "Same as GET; prefer JSON body " - "{message,persona,pack,model,timeout}" + "{message,persona,pack,model,timeout,context}" ), "requestBody": { "required": False, @@ -196,6 +302,7 @@ _OPENAPI: dict[str, Any] = { "pack": {"type": "string"}, "model": {"type": "string"}, "timeout": {"type": "number"}, + "context": {"type": "object"}, }, } } @@ -207,6 +314,21 @@ _OPENAPI: dict[str, Any] = { } +def _is_assistent_session_chat(path: str) -> bool: + parts = [p for p in path.split("/") if p] + return ( + len(parts) == 4 + and parts[0] == "assistent" + and parts[1] == "session" + and parts[3] == "chat" + ) + + +def _is_assistent_session_id(path: str) -> bool: + parts = [p for p in path.split("/") if p] + return len(parts) == 3 and parts[0] == "assistent" and parts[1] == "session" + + @dataclass class DebugHub: """Shared state for the sidecar process.""" @@ -397,7 +519,9 @@ def _make_handler(hub: DebugHub): f"
base {hub.base_url} · mostly read-only · 127.0.0.1
Agent: start at /openapi.json "
f"then /snapshot. "
- f"Opt-in chat: /assistent/chat-eval (not in snapshot).
/assistent/session (multi-turn) · "
+ f"/assistent/chat-eval (one-shot) — not in snapshot; "
+ f"may load VRAM."
f"