feat: local LLM multi-model, session search, cron proactive types, notifications, docs overhaul
Local LLM:
- user_settings.py: per-user hosts/models config (local_llm.json)
- routers/local_llm.py + static/local_llm.html: dedicated settings page
- llm_client.py: local OpenAI-compatible backend via httpx
- config.py: LOCAL_API_URL/KEY/MODEL + per-backend timeouts
- Active model shown near backend toggle (amber hint text)
Memory distillation:
- memory_distiller.py: DISTILL_BACKEND_MID/LONG .env overrides
- scheduler.py + notification.py: notify NC Talk after mid/long distill
- notification.py: outbound channel abstraction (NC Talk, extensible)
Session search:
- routers/files.py: GET /sessions/search?q= with excerpts grouped by date
- static/index.html + app.js: search UI in file sidebar with highlight
- _esc() helper to prevent XSS in search results
Proactive cron:
- cron_runner.py: new job types — message (send directly) and brief (LLM + send)
- Both support optional per-job channel override
Channels:
- routers/nextcloud_talk.py: consolidated using notification._send_nct_message()
- routers/auth.py: local backend status in /auth/status
- routers/chat.py: /backend returns {primary, fallback, local_model} object
UI / UX:
- Copy button for user messages (matching assistant)
- Autocomplete disabled on sensitive form fields
- settings.html: local model section replaced with link to /settings/local
Docs overhaul:
- MASTER.md hub + ARCH__SYSTEM/BACKENDS/PERSONA/CHANNELS/FUTURE.md
- ARCH__Intelligence_Layer.md replaced with redirect table
- CORTEX.md trimmed to vision only; README updated
- OPEN_WEBUI_API.md added to docs/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -431,6 +431,8 @@
|
||||
padding: 0;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
/* Syntax highlighting — app theme controls the pre background; hljs adds token colors */
|
||||
.message.assistant pre code.hljs { background: transparent; padding: 0; }
|
||||
|
||||
.message.system {
|
||||
align-self: center;
|
||||
@@ -440,6 +442,80 @@
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
/* ── Tool call step cards (agent mode) ── */
|
||||
.tool-calls-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
margin: 4px 0 6px;
|
||||
align-self: stretch;
|
||||
}
|
||||
.tool-call {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.tool-call summary {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
padding: 0.35rem 0.65rem;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
color: var(--muted);
|
||||
}
|
||||
.tool-call summary::-webkit-details-marker { display: none; }
|
||||
.tool-call summary::before {
|
||||
content: '▶';
|
||||
font-size: 0.55rem;
|
||||
color: var(--muted);
|
||||
transition: transform 0.12s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tool-call[open] summary::before { transform: rotate(90deg); }
|
||||
.tool-call summary:hover { color: var(--text); background: rgba(255,255,255,0.03); }
|
||||
.tc-name {
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
.tc-snippet {
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 36ch;
|
||||
}
|
||||
.tc-body {
|
||||
padding: 0 0.65rem 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.tc-section { display: flex; flex-direction: column; gap: 2px; }
|
||||
.tc-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.tc-body pre {
|
||||
margin: 0;
|
||||
background: var(--pre-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
font-size: 0.78rem;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
align-self: flex-start;
|
||||
background: var(--error-bg);
|
||||
@@ -451,7 +527,7 @@
|
||||
.message.thinking { color: var(--muted); font-style: italic; }
|
||||
|
||||
/* Copy button */
|
||||
.message.assistant { position: relative; }
|
||||
.message.assistant, .message.user { position: relative; }
|
||||
|
||||
.copy-btn {
|
||||
display: inline-flex;
|
||||
@@ -471,7 +547,8 @@
|
||||
transition: opacity 0.15s, color 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.message.assistant:hover .copy-btn { opacity: 1; }
|
||||
.message.assistant:hover .copy-btn,
|
||||
.message.user:hover .copy-btn { opacity: 1; }
|
||||
.copy-btn:hover { color: var(--text); border-color: var(--muted); }
|
||||
.copy-btn.copied { color: var(--success); border-color: var(--success-dim); }
|
||||
|
||||
@@ -807,22 +884,12 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#file-modal-header select {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
color: var(--text);
|
||||
font-size: 0.85rem;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#file-modal-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
flex: 1;
|
||||
}
|
||||
.fm-spacer { flex: 1; }
|
||||
|
||||
.fm-btn {
|
||||
background: var(--bg);
|
||||
@@ -838,13 +905,153 @@
|
||||
.fm-btn.active { color: var(--accent); border-color: var(--accent); }
|
||||
.fm-btn.save { color: var(--accent); border-color: var(--inara-border); }
|
||||
.fm-btn.save:hover { background: var(--inara-bg); }
|
||||
#file-saved-msg {
|
||||
font-size: 0.75rem;
|
||||
color: #6abf6a;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
#file-modal-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── File sidebar ── */
|
||||
#file-sidebar-wrap {
|
||||
width: 190px;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg);
|
||||
}
|
||||
#file-sidebar {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* ── Session search (within sidebar) ── */
|
||||
#session-search-wrap {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 8px 8px 10px;
|
||||
}
|
||||
#session-search-label {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#session-search-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
#session-search-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
font-size: 0.78rem;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
#session-search-btn {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 0.78rem;
|
||||
padding: 3px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#session-search-btn:hover { color: var(--accent); border-color: var(--accent); }
|
||||
|
||||
/* ── Session search results panel ── */
|
||||
#session-search-results {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 12px 14px;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.sr-header { color: var(--muted); font-size: 0.72rem; margin-bottom: 10px; }
|
||||
.sr-date {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--accent);
|
||||
margin: 14px 0 4px;
|
||||
}
|
||||
.sr-date:first-of-type { margin-top: 0; }
|
||||
.sr-excerpt {
|
||||
background: var(--surface);
|
||||
border-left: 2px solid var(--border);
|
||||
border-radius: 0 4px 4px 0;
|
||||
padding: 6px 10px;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text);
|
||||
}
|
||||
.sr-excerpt mark {
|
||||
background: rgba(139,92,246,0.25);
|
||||
color: var(--accent);
|
||||
border-radius: 2px;
|
||||
padding: 0 1px;
|
||||
}
|
||||
.sr-empty, .sr-error { color: var(--muted); padding: 8px 0; }
|
||||
|
||||
.fg-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
padding: 7px 10px 5px;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.fg-header::before {
|
||||
content: '▾';
|
||||
font-size: 0.7rem;
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.fg-header.collapsed::before { transform: rotate(-90deg); }
|
||||
.fg-header.collapsed + .fg-items { display: none; }
|
||||
|
||||
.fg-items { display: flex; flex-direction: column; }
|
||||
|
||||
.file-item {
|
||||
padding: 6px 10px 6px 16px;
|
||||
cursor: pointer;
|
||||
border-left: 2px solid transparent;
|
||||
transition: background 0.1s, border-color 0.1s;
|
||||
}
|
||||
.file-item:hover { background: var(--surface); }
|
||||
.file-item.active {
|
||||
background: var(--inara-bg);
|
||||
border-left-color: var(--accent);
|
||||
}
|
||||
.file-item.missing { opacity: 0.45; }
|
||||
|
||||
.fi-name {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text);
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.file-item.active .fi-name { color: var(--accent); }
|
||||
|
||||
.fi-meta {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 2px;
|
||||
font-size: 0.68rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
#file-saved-msg.show { opacity: 1; }
|
||||
|
||||
#file-modal-body {
|
||||
flex: 1;
|
||||
@@ -935,9 +1142,14 @@
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
||||
}
|
||||
.ctx-btn:hover { color: var(--text); border-color: var(--muted); }
|
||||
.ctx-btn.active { color: var(--accent); border-color: var(--accent); }
|
||||
.ctx-btn.mem-on { color: var(--success); border-color: var(--success-dim); }
|
||||
.ctx-btn:hover { color: var(--text); border-color: var(--muted); }
|
||||
.ctx-btn.active { color: var(--accent); border-color: var(--accent); }
|
||||
.ctx-btn.mem-on { color: var(--success); border-color: var(--success-dim); }
|
||||
.ctx-btn.local-on { color: #f59e0b; border-color: #92400e; }
|
||||
#backend-model-hint {
|
||||
font-size: 0.68rem; color: #f59e0b; opacity: 0.8;
|
||||
margin-top: 4px; word-break: break-all; line-height: 1.3;
|
||||
}
|
||||
|
||||
#ctx-distill-status {
|
||||
margin-top: 6px;
|
||||
@@ -1173,6 +1385,48 @@
|
||||
|
||||
#auth-banner-close:hover { opacity: 1; }
|
||||
|
||||
/* ── Toasts ──────────────────────────────────────────────── */
|
||||
#toast-container {
|
||||
position: fixed;
|
||||
bottom: 1.25rem;
|
||||
right: 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 0.4rem;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
.toast {
|
||||
padding: 0.45rem 0.85rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background: #334155;
|
||||
border: 1px solid #475569;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.35);
|
||||
opacity: 0;
|
||||
transform: translateY(6px);
|
||||
transition: opacity 0.18s ease, transform 0.18s ease;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.toast.show { opacity: 1; transform: translateY(0); }
|
||||
.toast.success { background: #14532d; border-color: #16a34a; }
|
||||
.toast.error { background: #7f1d1d; border-color: #dc2626; }
|
||||
|
||||
/* Sessions backdrop — hidden by default, visible only as mobile drawer overlay */
|
||||
#sessions-backdrop {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 98;
|
||||
animation: backdrop-in 0.2s ease;
|
||||
}
|
||||
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
|
||||
|
||||
/* ── Mobile responsive ───────────────────────────────────── */
|
||||
@media (max-width: 520px) {
|
||||
header { padding: 8px 12px; gap: 8px; }
|
||||
@@ -1233,6 +1487,36 @@
|
||||
|
||||
/* Larger touch targets */
|
||||
#send, #stop { padding: 12px 14px; font-size: 1rem; }
|
||||
|
||||
/* File modal: sidebar collapses to a narrow strip */
|
||||
#file-modal-inner { width: 100vw; height: 100dvh; border-radius: 0; }
|
||||
#file-sidebar-wrap { width: 130px; }
|
||||
.fi-meta { display: none; }
|
||||
|
||||
/* Sessions backdrop active on mobile */
|
||||
#sessions-backdrop.open { display: block; }
|
||||
|
||||
/* Sessions panel → full-height drawer sliding in from the right */
|
||||
#sessions-panel {
|
||||
display: block !important; /* keep rendered so transition works */
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: min(300px, 85vw);
|
||||
max-height: none;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
border-left: 1px solid var(--border);
|
||||
transform: translateX(110%);
|
||||
transition: transform 0.25s ease;
|
||||
z-index: 99;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#sessions-panel.open { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* ── Touch devices — no hover capability ─────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user