dsh: rename conversations, live model in header, swap to OmniRoute auto/* routes (opencode-go dropped - no creds/burn); direct deepseek routes dead
This commit is contained in:
44
dsh/app.py
44
dsh/app.py
@@ -36,7 +36,7 @@ BASE_DIR = Path(__file__).resolve().parent
|
||||
USER_NAME = os.getenv("DSH_USER", "user")
|
||||
DEFAULT_SLOT = os.getenv("DSH_SLOT", "Welcome to DeepSeek Harness")
|
||||
LLM_BASE = os.getenv("DSH_LLM_BASE", "http://192.168.20.13:20129/v1")
|
||||
LLM_MODEL = os.getenv("DSH_LLM_MODEL", "opencode-go/deepseek-v4-flash")
|
||||
LLM_MODEL = os.getenv("DSH_LLM_MODEL", "auto/best-chat")
|
||||
LLM_KEY = os.getenv("DSH_LLM_KEY", "")
|
||||
SYSTEM_PROMPT = os.getenv(
|
||||
"DSH_SYSTEM_PROMPT",
|
||||
@@ -574,17 +574,25 @@ async def generate_image(prompt: Annotated[str, Form()]) -> dict:
|
||||
|
||||
|
||||
|
||||
MODEL_CHOICES = ["opencode-go/deepseek-v4-flash",
|
||||
"deepseek/deepseek-v4-flash",
|
||||
"deepseek/deepseek-v4-pro",
|
||||
"z-ai/glm-5v-turbo"]
|
||||
MODEL_CHOICES = ["auto/best-chat",
|
||||
"auto/best-fast",
|
||||
"auto/cheap",
|
||||
"auto/best-reasoning",
|
||||
"auto/best-coding",
|
||||
"auto/best-vision",
|
||||
"openrouter/deepseek/deepseek-v4-flash",
|
||||
"openrouter/deepseek/deepseek-v4-pro"]
|
||||
MODEL_LABELS = {
|
||||
"opencode-go/deepseek-v4-flash": "Chat · opencode-go flash",
|
||||
"deepseek/deepseek-v4-flash": "Reasoning · deepseek flash",
|
||||
"deepseek/deepseek-v4-pro": "Coding · deepseek pro",
|
||||
"z-ai/glm-5v-turbo": "Multimodal · GLM vision",
|
||||
"auto/best-chat": "Chat · OmniRoute best",
|
||||
"auto/best-fast": "Fast · OmniRoute best-fast",
|
||||
"auto/cheap": "Budget · OmniRoute cheap",
|
||||
"auto/best-reasoning": "Reasoning · OmniRoute best",
|
||||
"auto/best-coding": "Coding · OmniRoute best",
|
||||
"auto/best-vision": "Multimodal · OmniRoute vision",
|
||||
"openrouter/deepseek/deepseek-v4-flash": "DeepSeek v4 flash (OpenRouter)",
|
||||
"openrouter/deepseek/deepseek-v4-pro": "DeepSeek v4 pro (OpenRouter)",
|
||||
}
|
||||
VISION_OVERRIDE_MODELS = {"z-ai/glm-5v-turbo"}
|
||||
VISION_OVERRIDE_MODELS = {"auto/best-vision"}
|
||||
|
||||
|
||||
@app.get("/api/models")
|
||||
@@ -619,6 +627,22 @@ async def sessions_get(sid: str) -> dict:
|
||||
"model": s.get("model") or LLM_MODEL}
|
||||
|
||||
|
||||
@app.post("/api/sessions/{sid}/rename")
|
||||
async def sessions_rename(sid: str, name: Annotated[str, Form()]) -> dict:
|
||||
"""Rename a conversation (shake off the auto-generated first-message title)."""
|
||||
name = (name or "").strip()[:80] or "Conversation"
|
||||
try:
|
||||
d = load_session(sid)
|
||||
d["name"] = name
|
||||
d["updated"] = datetime.now(timezone.utc).isoformat()
|
||||
_sdir()
|
||||
with open(_spath(sid), "w", encoding="utf-8") as fh:
|
||||
json.dump(d, fh, ensure_ascii=False)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return {"ok": False, "error": str(exc)}
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@app.post("/api/sessions/{sid}/delete")
|
||||
async def sessions_delete(sid: str) -> dict:
|
||||
try:
|
||||
|
||||
@@ -22,7 +22,7 @@ x-dsh-common: &common
|
||||
- /tmp
|
||||
environment: &env-base
|
||||
DSH_LLM_BASE: http://192.168.20.13:20129/v1
|
||||
DSH_LLM_MODEL: opencode-go/deepseek-v4-flash
|
||||
DSH_LLM_MODEL: auto/best-chat
|
||||
DSH_HISTORY_DIR: /data
|
||||
DSH_VISION_KEY: ${DSH_VISION_KEY:-}
|
||||
DSH_VISION_MODEL: openai/gpt-5
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#sessions li.sel { background: var(--primary, #0075de); color: #fff; }
|
||||
#sessions li .t { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
#sessions li .n { font-size: 11px; opacity: .7; }
|
||||
#sessions li .del { background: none; border: none; cursor: pointer; color: inherit; opacity: .5; font-size: 13px; }
|
||||
#sessions li .del, #sessions li .ren { background: none; border: none; cursor: pointer; color: inherit; opacity: .5; font-size: 13px; }
|
||||
.chat-col { display: flex; flex-direction: column; flex: 1; min-width: 0; }
|
||||
.log { flex: 1; overflow: auto; }
|
||||
.bubble { position: relative; white-space: pre-wrap; }
|
||||
@@ -103,7 +103,7 @@
|
||||
<button id="menuBtn" type="button" aria-label="Conversations" title="Conversations" style="display:none;align-items:center;justify-content:center;background:none;border:1px solid var(--hairline, #e6e6e6);border-radius:8px;padding:4px 9px;font-size:17px;cursor:pointer;line-height:1">☰</button>
|
||||
<button id="themeBtn" type="button" title="Toggle dark mode" style="background:none;border:1px solid var(--hairline, #e6e6e6);border-radius:8px;padding:4px 8px;font-size:16px;cursor:pointer;color:var(--ink, #111)">🌙</button>
|
||||
<div class="logo">DeepSeek Harness</div>
|
||||
<div class="who" style="margin-left:auto">{{ DSH_SLOT }} · <span class="model">{{ DSH_MODEL }}</span></div>
|
||||
<div class="who" style="margin-left:auto">{{ DSH_SLOT }} · <span class="model" id="whoModel">{{ DSH_MODEL }}</span></div>
|
||||
</header>
|
||||
|
||||
<div class="app">
|
||||
@@ -206,20 +206,39 @@
|
||||
const n = document.createElement('span'); n.className = 'n'; n.textContent = s.count;
|
||||
const d = document.createElement('button'); d.className = 'del'; d.textContent = '✕'; d.title = 'Delete';
|
||||
d.addEventListener('click', (e) => { e.stopPropagation(); deleteSession(s.id); });
|
||||
li.append(t, n, d);
|
||||
const r = document.createElement('button'); r.className = 'ren'; r.textContent = '✎'; r.title = 'Rename';
|
||||
r.addEventListener('click', (e) => { e.stopPropagation(); renameSession(s.id); });
|
||||
li.append(t, n, r, d);
|
||||
li.addEventListener('click', () => openSession(s.id));
|
||||
listEl.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function whoModel() {
|
||||
const m = modelSel.value || '';
|
||||
const label = [...modelSel.options].find(o => o.value === m);
|
||||
const el = document.getElementById('whoModel');
|
||||
if (el) el.textContent = label ? label.textContent : m;
|
||||
}
|
||||
async function renameSession(id) {
|
||||
const s = await (await fetch('/api/sessions/' + id)).json();
|
||||
const name = prompt('Rename conversation:', s.name || '');
|
||||
if (name === null) return;
|
||||
const fd = new URLSearchParams({ name });
|
||||
const d = await (await fetch('/api/sessions/' + id + '/rename', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: fd })).json();
|
||||
if (d.ok) loadSessions(); else alert('Rename failed: ' + (d.error || 'unknown'));
|
||||
}
|
||||
|
||||
async function fetchModels() {
|
||||
try {
|
||||
const d = await (await fetch('/api/models')).json();
|
||||
(d.current || []).forEach(m => {
|
||||
const o = document.createElement('option'); o.value = m; o.textContent = (d.labels && d.labels[m]) || m;
|
||||
if (m === d.default) o.selected = true;
|
||||
if (m === (d.default || (d.current && d.current[0]))) o.selected = true;
|
||||
modelSel.appendChild(o);
|
||||
});
|
||||
modelSel.addEventListener('change', () => { whoModel(); });
|
||||
whoModel();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user