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:
@@ -175,7 +175,7 @@ def _preferred_model(models: list, preference) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _chat_options(temperature: float | None, num_gpu: int | None) -> dict:
|
||||
def _chat_options(temperature: float | None, num_gpu: int | None, num_ctx: int | None = None) -> dict:
|
||||
"""Assemble the Ollama `options` block from the knobs we expose.
|
||||
|
||||
Returns an empty dict when nothing is set so callers can omit `options`
|
||||
@@ -186,6 +186,8 @@ def _chat_options(temperature: float | None, num_gpu: int | None) -> dict:
|
||||
opts["temperature"] = temperature
|
||||
if num_gpu is not None:
|
||||
opts["num_gpu"] = num_gpu
|
||||
if num_ctx: # 0 / None -> let Ollama use the model default
|
||||
opts["num_ctx"] = num_ctx
|
||||
return opts
|
||||
|
||||
|
||||
@@ -503,10 +505,16 @@ class OllamaManager:
|
||||
temperature: float | None = None,
|
||||
num_gpu: int | None = None,
|
||||
think: bool = False,
|
||||
tools: list | None = None,
|
||||
num_ctx: int | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""Multi-turn chat via /api/chat (accepts a messages array with roles).
|
||||
|
||||
When `tools` is given (non-stream only), the request advertises them and
|
||||
the FULL message dict is returned (so the caller sees `tool_calls`);
|
||||
otherwise the response content string is returned as before.
|
||||
|
||||
`think` toggles Qwen3-style reasoning. Default off: the hidden <think>
|
||||
block is pure latency for chat/memory. Ollama ignores it for models that
|
||||
don't support thinking.
|
||||
@@ -516,12 +524,14 @@ class OllamaManager:
|
||||
if stream:
|
||||
return self._chat_stream(
|
||||
messages=messages, model=model, temperature=temperature,
|
||||
num_gpu=num_gpu, think=think, start=start,
|
||||
num_gpu=num_gpu, think=think, start=start, num_ctx=num_ctx,
|
||||
)
|
||||
else:
|
||||
body: dict = {"model": model, "messages": messages, "stream": False}
|
||||
body["think"] = think
|
||||
opts = _chat_options(temperature, num_gpu)
|
||||
if tools:
|
||||
body["tools"] = tools
|
||||
opts = _chat_options(temperature, num_gpu, num_ctx)
|
||||
if opts:
|
||||
body["options"] = opts
|
||||
self._apply_keep_alive(body)
|
||||
@@ -529,7 +539,9 @@ class OllamaManager:
|
||||
r = await client.post(f"{self._api_base}/api/chat", json=body)
|
||||
elapsed = time.perf_counter() - start
|
||||
r.raise_for_status()
|
||||
return r.json().get("message", {}).get("content", "")
|
||||
message = r.json().get("message", {})
|
||||
# Tool callers need the whole message (tool_calls); others want content.
|
||||
return message if tools else message.get("content", "")
|
||||
except Exception as e:
|
||||
elapsed = time.perf_counter() - start
|
||||
_log.exception("chat error after %.3fs: %s", elapsed, e)
|
||||
@@ -643,12 +655,12 @@ class OllamaManager:
|
||||
|
||||
async def _chat_stream(self, messages: list, model: str, start: float,
|
||||
temperature: float | None = None, num_gpu: int | None = None,
|
||||
think: bool = False):
|
||||
think: bool = False, num_ctx: int | None = None):
|
||||
"""Async generator streaming tokens, then a final __meta__ stats sentinel."""
|
||||
try:
|
||||
body: dict = {"model": model, "messages": messages, "stream": True}
|
||||
body["think"] = think # see chat(): reasoning off by default for speed
|
||||
opts = _chat_options(temperature, num_gpu)
|
||||
opts = _chat_options(temperature, num_gpu, num_ctx)
|
||||
if opts:
|
||||
body["options"] = opts
|
||||
self._apply_keep_alive(body)
|
||||
|
||||
Reference in New Issue
Block a user