Switch default model to qwen3 and add intent routing for reliable tool calling
Mistral didn't reliably use Ollama's structured tool calling API — it wrote tool calls as JSON text instead of invoking them. This adds an intent routing layer that classifies user intent via a fast non-streaming LLM call before streaming, executing detected tools directly and bypassing native tool calling. - Change default OLLAMA_MODEL from mistral to qwen3 - Add intent.py: classify_intent() with JSON parsing and fallback regex - Integrate intent routing into generation_task.py round 0 - Add all-day event support (iCalendar DATE values) to CalDAV service - Add recurring event support (RRULE) to CalDAV service and tool definition - Improve create_event tool description for descriptive titles - Enhance system prompt with structured tool usage guidance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+13
-7
@@ -12,7 +12,7 @@
|
||||
> Include file-level details in the commit body when the change is non-trivial.
|
||||
|
||||
## Last Updated
|
||||
2026-02-15 — Phase 8: CalDAV calendar integration, LLM-suggested tags, settings/model UI refinements
|
||||
2026-02-16 — Phase 9: Switch to qwen3, intent routing for reliable tool calling, all-day/recurring events
|
||||
|
||||
## Project Overview
|
||||
Fabled Assistant is a self-hosted note-taking and task-tracking application with
|
||||
@@ -263,10 +263,11 @@ fabledassistant/
|
||||
│ │ ├── llm.py # Ollama interaction: build_context with user_id, streaming (stream_chat + stream_chat_with_tools), ChatChunk dataclass, URL fetching
|
||||
│ │ ├── chat.py # Conversation CRUD with user_id isolation, add_message, save/summarize as note (LLM-titled, chat-tagged)
|
||||
│ │ ├── generation_buffer.py # In-memory SSE event buffer with cancel_event, reconnect support, auto-cleanup; supports chat (int keys) and assist (string keys)
|
||||
│ │ ├── generation_task.py # Background asyncio tasks: run_generation (chat, DB flush, titles, tool loop) + run_assist_generation (lightweight, no DB)
|
||||
│ │ ├── tools.py # LLM tool definitions (create_task, create_note, search_notes, CalDAV events) + execute_tool dispatcher
|
||||
│ │ ├── generation_task.py # Background asyncio tasks: run_generation (chat, DB flush, titles, intent routing + tool loop) + run_assist_generation (lightweight, no DB)
|
||||
│ │ ├── intent.py # Intent routing: classify_intent() makes fast non-streaming LLM call to detect tool intent before streaming
|
||||
│ │ ├── tools.py # LLM tool definitions (create_task, create_note, search_notes, CalDAV events with all-day/recurrence) + execute_tool dispatcher
|
||||
│ │ ├── tag_suggestions.py # LLM-powered tag suggestions: suggest_tags() builds prompt with existing tags, calls generate_completion, parses JSON response
|
||||
│ │ ├── caldav.py # CalDAV integration: create/list/search calendar events via caldav library (per-user config from settings)
|
||||
│ │ ├── caldav.py # CalDAV integration: create/list/search calendar events via caldav library, all-day + recurring event support (per-user config from settings)
|
||||
│ │ ├── settings.py # Settings CRUD with user_id isolation: get_setting, set_setting, set_settings_batch, get_all_settings
|
||||
│ │ ├── logging.py # App logging: log_audit, log_usage, log_error, get_logs, get_log_stats, delete_old_logs, start_log_retention_loop
|
||||
│ │ ├── email.py # SMTP email service: get_smtp_config, is_smtp_configured, send_email, send_test_email
|
||||
@@ -523,7 +524,7 @@ When adding a new migration, follow these conventions:
|
||||
- All task sections hidden when empty; marking done removes from all lists
|
||||
|
||||
### LLM Chat
|
||||
- Ollama integration via async HTTP (httpx), auto-pull default model on startup
|
||||
- Ollama integration via async HTTP (httpx), default model qwen3 (better tool support than mistral), auto-pull on startup
|
||||
- Background generation with `GenerationBuffer` (in-memory SSE fan-out, `Last-Event-ID` reconnect, 60s cleanup)
|
||||
- Stop generation with partial content preservation
|
||||
- Note-aware context building: current note + keyword search for related notes + URL fetching
|
||||
@@ -546,9 +547,14 @@ When adding a new migration, follow these conventions:
|
||||
components (linked titles for created items, search result lists). SSE emits `tool_call` events
|
||||
for real-time rendering during streaming. System prompt includes today's date for relative date
|
||||
resolution. Graceful degradation: models without tool support respond normally.
|
||||
- **Intent routing:** Before streaming, a fast non-streaming LLM call classifies user intent and
|
||||
extracts tool parameters (`services/intent.py`). If a tool call is detected, it executes directly
|
||||
— bypassing the model's native (sometimes unreliable) tool calling API. Falls through to normal
|
||||
streaming when no tool is detected or classification fails. Only runs on first round of tool loop.
|
||||
- **CalDAV calendar integration:** Per-user CalDAV settings (URL, username, password, calendar name).
|
||||
LLM tools: `create_event`, `list_events`, `search_events`. Runs synchronous caldav library calls
|
||||
in asyncio executor. Settings UI for CalDAV configuration.
|
||||
LLM tools: `create_event` (with `all_day` and `recurrence` support), `list_events`, `search_events`.
|
||||
All-day events use iCalendar DATE values; recurrence uses RRULE (e.g. `FREQ=YEARLY`).
|
||||
Runs synchronous caldav library calls in asyncio executor. Settings UI for CalDAV configuration.
|
||||
- **LLM-suggested tags:** Backend service (`tag_suggestions.py`) prompts LLM with existing user tags
|
||||
and note content, returns 3-5 relevant tag suggestions. Tags already in body are filtered out.
|
||||
Exposed via `POST /api/notes/suggest-tags` and `POST /api/notes/:id/append-tag`. Integrated in:
|
||||
|
||||
Reference in New Issue
Block a user