forked from enderofwings/NexusOS
feat: playbook tools, RAG, vision, voice, chat controls, faster boot
- Tool-using playbooks: read-only tool registry (search_memory, search_history, search_documents, list_models, get_time), per-playbook allowlist, agentic loop, and a "running tool" status indicator. - Document ingest / RAG: documents table + chunker + embed/cosine retrieval reusing the existing stack; Documents page (upload/paste, viewer, delete); top-k chunks injected into the system prompt. - Vision chat: attach images, base64 into /api/chat. - Voice I/O: Web Speech dictation + read-aloud (browser-native, no backend). - Chat controls: stop, regenerate, edit-and-resend; num_ctx knob in Settings. - Per-playbook model override. - History polish: per-conversation + bulk ShareGPT export. - Faster ncp start: UI up first, Ollama warms in the background, with a per-phase timing readout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+61
-15
@@ -4,6 +4,7 @@ import { Playbook } from "./Playbook";
|
||||
import { Models } from "./Models";
|
||||
import { Settings } from "./Settings";
|
||||
import { Memory } from "./Memory";
|
||||
import { Documents } from "./Documents";
|
||||
import { Logs } from "./Logs";
|
||||
|
||||
import { API_BASE } from "./config";
|
||||
@@ -128,6 +129,15 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// Download ShareGPT JSONL — one conversation if convId given, else all.
|
||||
const exportConversations = (e, convId) => {
|
||||
if (e) e.stopPropagation();
|
||||
const url = convId
|
||||
? `${API_BASE}/conversations/export?conversation_id=${encodeURIComponent(convId)}`
|
||||
: `${API_BASE}/conversations/export`;
|
||||
window.open(url, "_blank");
|
||||
};
|
||||
|
||||
const deleteConversation = async (e, conversationId) => {
|
||||
e.stopPropagation();
|
||||
if (!window.confirm("Delete this conversation?")) return;
|
||||
@@ -149,6 +159,7 @@ function App() {
|
||||
{ key: "playbook", label: "📖 Playbooks" },
|
||||
{ key: "models", label: "🤖 Models", badge: isModelPulling },
|
||||
{ key: "memory", label: "🧠 Memory" },
|
||||
{ key: "documents", label: "📄 Documents" },
|
||||
{ key: "logs", label: "📜 Logs" },
|
||||
{ key: "settings", label: "⚙️ Settings" },
|
||||
];
|
||||
@@ -295,21 +306,39 @@ function App() {
|
||||
}}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "0.5rem", flexShrink: 0 }}>
|
||||
<span style={{ fontSize: "0.75rem", color: "#888", textTransform: "uppercase", letterSpacing: "0.05em" }}>Chats</span>
|
||||
<button
|
||||
onClick={startNewChat}
|
||||
title="New chat"
|
||||
style={{
|
||||
padding: "0.2rem 0.55rem",
|
||||
background: "#161616",
|
||||
color: "#bbb",
|
||||
border: "1px solid #2a2a2a",
|
||||
borderRadius: "6px",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.75rem",
|
||||
}}
|
||||
>
|
||||
+ New
|
||||
</button>
|
||||
<div style={{ display: "flex", gap: "0.35rem" }}>
|
||||
<button
|
||||
onClick={() => exportConversations(null, null)}
|
||||
title="Export all conversations (ShareGPT JSONL)"
|
||||
disabled={conversations.length === 0}
|
||||
style={{
|
||||
padding: "0.2rem 0.5rem",
|
||||
background: "#161616",
|
||||
color: conversations.length === 0 ? "#555" : "#bbb",
|
||||
border: "1px solid #2a2a2a",
|
||||
borderRadius: "6px",
|
||||
cursor: conversations.length === 0 ? "default" : "pointer",
|
||||
fontSize: "0.75rem",
|
||||
}}
|
||||
>
|
||||
⤓ Export
|
||||
</button>
|
||||
<button
|
||||
onClick={startNewChat}
|
||||
title="New chat"
|
||||
style={{
|
||||
padding: "0.2rem 0.55rem",
|
||||
background: "#161616",
|
||||
color: "#bbb",
|
||||
border: "1px solid #2a2a2a",
|
||||
borderRadius: "6px",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.75rem",
|
||||
}}
|
||||
>
|
||||
+ New
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
@@ -372,6 +401,22 @@ function App() {
|
||||
</div>
|
||||
{isHover && (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => exportConversations(e, conv.id)}
|
||||
title="Export this conversation"
|
||||
style={{
|
||||
padding: "0.15rem 0.4rem",
|
||||
background: "transparent",
|
||||
color: "#888",
|
||||
border: "1px solid #333",
|
||||
borderRadius: "4px",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.7rem",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
📤
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => renameConversation(e, conv)}
|
||||
title="Rename"
|
||||
@@ -434,6 +479,7 @@ function App() {
|
||||
)}
|
||||
{currentPage === "playbook" && <Playbook />}
|
||||
{currentPage === "memory" && <Memory />}
|
||||
{currentPage === "documents" && <Documents />}
|
||||
{currentPage === "logs" && <Logs />}
|
||||
{currentPage === "settings" && <Settings />}
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user