Remove intent model entirely; quick-capture uses primary model
The separate intent model (OLLAMA_INTENT_MODEL / qwen2.5:7b) is removed from every part of the system. All classification now uses the primary model. Changes: - config.py: remove OLLAMA_INTENT_MODEL - intent.py: remove classify_intent() and all supporting infrastructure (_SYSTEM_PROMPT_TEMPLATE, _RESEARCH_PREFIX, _PRIOR_WORK_REFS); file now only contains the quick-capture classifier - quick_capture.py: classify_capture_intent() now called with Config.OLLAMA_MODEL - generation_task.py: remove intent_model_setting DB lookup and get_setting import; history summarization and research pipeline use the primary model directly - research.py: remove intent_model parameter from run_research_pipeline() and _generate_sub_queries(); both use the model param throughout - routes/settings.py: remove intent_model from model-key validation and response - app.py: remove intent model pre-warming at startup - SettingsView.vue: remove Intent Model selector and related refs/state Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,10 +10,8 @@ const authStore = useAuthStore();
|
||||
const toastStore = useToastStore();
|
||||
const assistantName = ref("");
|
||||
const defaultModel = ref("");
|
||||
const intentModel = ref("");
|
||||
const installedModels = ref<string[]>([]);
|
||||
const defaultChatModel = ref("");
|
||||
const defaultIntentModel = ref("");
|
||||
const newEmail = ref("");
|
||||
const emailPassword = ref("");
|
||||
const changingEmail = ref(false);
|
||||
@@ -80,10 +78,9 @@ onMounted(async () => {
|
||||
|
||||
// Load installed models and configured defaults
|
||||
try {
|
||||
const modelsData = await apiGet<{ models: string[]; default_chat_model: string; default_intent_model: string }>("/api/settings/models");
|
||||
const modelsData = await apiGet<{ models: string[]; default_chat_model: string }>("/api/settings/models");
|
||||
installedModels.value = modelsData.models;
|
||||
defaultChatModel.value = modelsData.default_chat_model;
|
||||
defaultIntentModel.value = modelsData.default_intent_model;
|
||||
} catch {
|
||||
// Ollama unreachable — dropdowns will be empty
|
||||
}
|
||||
@@ -91,7 +88,6 @@ onMounted(async () => {
|
||||
// Load notification preferences from user settings
|
||||
const allSettings = await apiGet<Record<string, string>>("/api/settings");
|
||||
defaultModel.value = allSettings.default_model ?? "";
|
||||
intentModel.value = allSettings.intent_model ?? "";
|
||||
if (allSettings.notify_task_reminders !== undefined) {
|
||||
notifyTaskReminders.value = allSettings.notify_task_reminders !== "false";
|
||||
}
|
||||
@@ -190,7 +186,6 @@ async function saveAssistant() {
|
||||
await store.updateSettings({
|
||||
assistant_name: assistantName.value.trim() || "Fable",
|
||||
default_model: defaultModel.value,
|
||||
intent_model: intentModel.value,
|
||||
});
|
||||
saved.value = true;
|
||||
setTimeout(() => (saved.value = false), 2000);
|
||||
@@ -414,14 +409,6 @@ function hostname(url: string): string {
|
||||
</select>
|
||||
<p class="field-hint">Model used for new conversations.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="intent-model">Intent Model</label>
|
||||
<select id="intent-model" v-model="intentModel" class="input">
|
||||
<option value="">Default ({{ defaultIntentModel || "qwen2.5:1.5b" }})</option>
|
||||
<option v-for="m in installedModels" :key="m" :value="m">{{ m }}</option>
|
||||
</select>
|
||||
<p class="field-hint">Smaller/faster model for intent routing before the main model.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn-save" @click="saveAssistant" :disabled="saving">
|
||||
|
||||
@@ -131,15 +131,9 @@ def create_app() -> Quart:
|
||||
except Exception:
|
||||
logger.warning("Failed to warm model '%s'", model, exc_info=True)
|
||||
|
||||
# Pull main model and (if configured) a separate intent model concurrently,
|
||||
# then warm them into VRAM so the first user request doesn't cold-load both
|
||||
# simultaneously (which causes Ollama 500 races).
|
||||
# Fire-and-forget so pulls/warming don't block startup.
|
||||
# Pull and warm the main model into VRAM at startup so the first request is fast.
|
||||
asyncio.create_task(_pull_model(Config.OLLAMA_MODEL, warm=True))
|
||||
models_to_warm = {Config.OLLAMA_MODEL}
|
||||
if Config.OLLAMA_INTENT_MODEL and Config.OLLAMA_INTENT_MODEL != Config.OLLAMA_MODEL:
|
||||
models_to_warm.add(Config.OLLAMA_INTENT_MODEL)
|
||||
for _model in models_to_warm:
|
||||
asyncio.create_task(_pull_model(_model, warm=True))
|
||||
# Also pull the embedding model (nomic-embed-text by default), but no need to warm it.
|
||||
if Config.EMBEDDING_MODEL not in models_to_warm:
|
||||
asyncio.create_task(_pull_model(Config.EMBEDDING_MODEL, warm=False))
|
||||
|
||||
@@ -24,9 +24,6 @@ class Config:
|
||||
)
|
||||
OLLAMA_URL: str = os.environ.get("OLLAMA_URL", "http://localhost:11434")
|
||||
OLLAMA_MODEL: str = os.environ.get("OLLAMA_MODEL", "qwen3:latest")
|
||||
# Optional dedicated model for intent classification.
|
||||
# Falls back to OLLAMA_MODEL if not set. Can also be overridden per-user via settings.
|
||||
OLLAMA_INTENT_MODEL: str = os.environ.get("OLLAMA_INTENT_MODEL", "qwen2.5:7b")
|
||||
# KV cache context window for generation. Lower = less VRAM, less throughput impact.
|
||||
OLLAMA_NUM_CTX: int = int(os.environ.get("OLLAMA_NUM_CTX", "16384"))
|
||||
SECRET_KEY: str = _read_secret("SECRET_KEY", "SECRET_KEY_FILE", "dev-secret-change-me")
|
||||
|
||||
@@ -78,7 +78,7 @@ async def quick_capture_route():
|
||||
if not text:
|
||||
return jsonify({"error": "text is required"}), 400
|
||||
|
||||
intent_model = Config.OLLAMA_INTENT_MODEL or Config.OLLAMA_MODEL
|
||||
model = Config.OLLAMA_MODEL
|
||||
|
||||
# Build tool list for this user, then restrict to capture-only operations.
|
||||
all_tools = await get_tools_for_user(uid)
|
||||
@@ -86,7 +86,7 @@ async def quick_capture_route():
|
||||
t for t in all_tools if t.get("function", {}).get("name") in _CAPTURE_TOOL_NAMES
|
||||
]
|
||||
|
||||
intent = await classify_capture_intent(text, capture_tools, intent_model)
|
||||
intent = await classify_capture_intent(text, capture_tools, model)
|
||||
|
||||
if intent.should_execute:
|
||||
# research_topic bypasses execute_tool — run the pipeline directly
|
||||
@@ -94,9 +94,8 @@ async def quick_capture_route():
|
||||
from fabledassistant.services.research import run_research_pipeline
|
||||
|
||||
topic = intent.arguments.get("topic", text)
|
||||
model = Config.OLLAMA_MODEL
|
||||
try:
|
||||
note = await run_research_pipeline(topic, uid, model, intent_model)
|
||||
note = await run_research_pipeline(topic, uid, model)
|
||||
logger.info(
|
||||
"Quick-capture uid=%d: research note id=%d '%s'",
|
||||
uid, note.id, note.title,
|
||||
@@ -114,7 +113,6 @@ async def quick_capture_route():
|
||||
# For notes, run a second LLM pass to generate a proper title and
|
||||
# well-formed body rather than using the raw capture text verbatim.
|
||||
if intent.tool_name == "create_note":
|
||||
model = Config.OLLAMA_MODEL
|
||||
title, body = await _process_note(text, model)
|
||||
intent.arguments["title"] = title
|
||||
intent.arguments["body"] = body
|
||||
@@ -141,7 +139,6 @@ async def quick_capture_route():
|
||||
|
||||
# Fallback: classify_capture_intent returned no-tool (e.g. LLM parse failure).
|
||||
# Still process the text through the note enrichment pass.
|
||||
model = Config.OLLAMA_MODEL
|
||||
fallback_title, fallback_body = await _process_note(text, model)
|
||||
|
||||
result = await execute_tool(
|
||||
|
||||
@@ -25,18 +25,17 @@ async def update_settings_route():
|
||||
if not isinstance(data, dict):
|
||||
return jsonify({"error": "Expected a JSON object"}), 400
|
||||
|
||||
if "default_model" in data or "intent_model" in data:
|
||||
if "default_model" in data:
|
||||
installed = await get_installed_models()
|
||||
for key in ("default_model", "intent_model"):
|
||||
if key in data and data[key]:
|
||||
model = str(data[key])
|
||||
if installed and model not in installed:
|
||||
return jsonify({"error": f"Model '{model}' is not installed"}), 400
|
||||
if data["default_model"]:
|
||||
model = str(data["default_model"])
|
||||
if installed and model not in installed:
|
||||
return jsonify({"error": f"Model '{model}' is not installed"}), 400
|
||||
|
||||
# Empty string for model keys means "reset to system default".
|
||||
# Empty string for default_model means "reset to system default".
|
||||
# Delete the DB row so get_setting() falls back to Config defaults
|
||||
# rather than returning "" and breaking model resolution everywhere.
|
||||
_MODEL_KEYS = frozenset({"default_model", "intent_model"})
|
||||
_MODEL_KEYS = frozenset({"default_model"})
|
||||
to_save = {}
|
||||
for k, v in data.items():
|
||||
str_v = str(v)
|
||||
@@ -59,7 +58,6 @@ async def get_models_route():
|
||||
return jsonify({
|
||||
"models": models,
|
||||
"default_chat_model": Config.OLLAMA_MODEL,
|
||||
"default_intent_model": Config.OLLAMA_INTENT_MODEL,
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ from fabledassistant.services.generation_buffer import (
|
||||
from fabledassistant.services.llm import ChatChunk, build_context, generate_completion, stream_chat, stream_chat_with_tools, summarize_history_for_context, wait_for_model_loaded
|
||||
from fabledassistant.services.chat import update_conversation_title
|
||||
from fabledassistant.services.logging import log_generation
|
||||
from fabledassistant.services.settings import get_setting
|
||||
from fabledassistant.services.tools import get_tools_for_user, execute_tool
|
||||
from fabledassistant.services.research import run_research_pipeline
|
||||
|
||||
@@ -156,16 +155,12 @@ async def run_generation(
|
||||
|
||||
buf.append_event("status", {"status": "Building context..."})
|
||||
|
||||
# Phase 1: Quick DB calls — resolve tools list and intent model in parallel.
|
||||
tools, intent_model_setting = await asyncio.gather(
|
||||
get_tools_for_user(user_id),
|
||||
get_setting(user_id, "intent_model", ""),
|
||||
)
|
||||
intent_model = intent_model_setting or Config.OLLAMA_INTENT_MODEL or model
|
||||
# Phase 1: Resolve the tools list for this user.
|
||||
tools = await get_tools_for_user(user_id)
|
||||
|
||||
logger.info(
|
||||
"Starting generation for conv %d: model=%s, intent_model=%s, tools=%d",
|
||||
conv_id, model, intent_model, len(tools),
|
||||
"Starting generation for conv %d: model=%s, tools=%d",
|
||||
conv_id, model, len(tools),
|
||||
)
|
||||
|
||||
# Phase 2: Summarize long conversation history if needed.
|
||||
@@ -173,7 +168,7 @@ async def run_generation(
|
||||
history_summary: str | None = None
|
||||
if len(history) > 20: # matches _HISTORY_SUMMARY_THRESHOLD in llm.py
|
||||
buf.append_event("status", {"status": "Summarizing conversation history..."})
|
||||
history_to_use, history_summary = await summarize_history_for_context(history, intent_model)
|
||||
history_to_use, history_summary = await summarize_history_for_context(history, model)
|
||||
|
||||
# Phase 3: Build context and wait for model in parallel.
|
||||
model_load_task = asyncio.create_task(wait_for_model_loaded(model, timeout=90.0))
|
||||
@@ -259,7 +254,7 @@ async def run_generation(
|
||||
if tool_name == "research_topic":
|
||||
topic = arguments.get("topic", "")
|
||||
try:
|
||||
note = await run_research_pipeline(topic, user_id, model, intent_model, buf)
|
||||
note = await run_research_pipeline(topic, user_id, model, buf)
|
||||
result = {
|
||||
"success": True,
|
||||
"type": "research_note",
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
"""Intent routing — classify user message before streaming.
|
||||
"""Quick-capture intent classifier.
|
||||
|
||||
Makes a fast non-streaming LLM call to detect tool intent and extract
|
||||
parameters. When a tool call is detected the caller can execute it
|
||||
directly, bypassing the model's native (and sometimes unreliable)
|
||||
structured tool-calling API.
|
||||
Classifies short capture text (note, task, event, research) for the
|
||||
/api/quick-capture endpoint using a dedicated prompt and the primary model.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -51,168 +49,6 @@ def _build_tool_summary(tools: list[dict]) -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
_SYSTEM_PROMPT_TEMPLATE = """\
|
||||
You are an intent classifier. Given a user message (and recent conversation \
|
||||
history for context), decide whether it requires calling one of the available \
|
||||
tools or is just general chat.
|
||||
|
||||
Today's date is {today}.
|
||||
|
||||
Available tools:
|
||||
{tool_summary}
|
||||
|
||||
Respond with ONLY a JSON object, no other text:
|
||||
- If a tool should be called: {{"tool": "tool_name", "arguments": {{...}}, "confidence": "high"|"medium"|"low", "ack": "One short sentence describing what you're about to do."}}
|
||||
- If it's general chat: {{"tool": null, "confidence": "high", "ack": null}}
|
||||
|
||||
Confidence levels:
|
||||
- "high": the intent is clear and all required arguments are unambiguous
|
||||
- "medium": probably requires the tool but some argument is uncertain or inferred
|
||||
- "low": uncertain whether this needs a tool at all, or the message is too ambiguous to act on
|
||||
|
||||
Rules:
|
||||
- Use recent conversation history to resolve references like "it", "that event", "the meeting", "move it", etc.
|
||||
- For dates like "tomorrow", "next Friday", "in 3 days", resolve them to YYYY-MM-DD format.
|
||||
- For datetime parameters, use ISO 8601 format (e.g. 2026-09-30T14:00:00).
|
||||
- Only include arguments the user actually specified or that can be clearly inferred.
|
||||
- Infer reasonable defaults: birthdays and holidays are all-day + yearly recurring; "weekly meeting" is weekly recurring.
|
||||
- Use descriptive titles: "My Birthday" not just "Birthday", "Team Standup" not just "Meeting".
|
||||
- "add to", "update", "edit", "expand", "flesh out", "modify", "append to", "continue writing" a note → use update_note with query=<note title from context> and mode="append" for additions or mode="replace" for full rewrites.
|
||||
- "mark as done", "complete", "finish", "mark in progress", "start" a task → use update_note with status field.
|
||||
- "set priority", "change priority", "make it high priority" → use update_note with priority field.
|
||||
- "set due date", "move due date", "due on Friday" for a task → use update_note with due_date field.
|
||||
- "what are my overdue tasks", "show overdue", "tasks due today", "high priority tasks", "in progress tasks", "what's due this week" → use list_tasks with appropriate status/priority/due_before/due_after filters. For overdue, set due_before to today's date.
|
||||
- If a note was created earlier in the conversation and the user provides more content for it, use update_note (not create_note).
|
||||
- Use create_note ONLY when the user explicitly wants a brand new note that doesn't already exist.
|
||||
- "update", "change", "move", "reschedule" an event → use update_event.
|
||||
- "delete", "cancel", "remove" an event → use delete_event.
|
||||
- "which calendars", "list calendars", "my calendars" → use list_calendars.
|
||||
- "remind me X minutes/hours before" an event → convert to reminder_minutes parameter on create_event.
|
||||
- When the user asks about events in a time period (e.g. "events in September", "what's on next week", "my schedule for March"), use list_events with date_from/date_to covering that period. Do NOT use search_events for time-based queries — search_events is only for keyword matching against event titles.
|
||||
- "delete", "remove", "trash", "get rid of" a note (not a task) → use delete_note with query=<note name/keyword>. NEVER use this for tasks.
|
||||
- "delete", "remove", "trash", "get rid of" a task → use delete_task with query=<task name/keyword>. NEVER use this for notes.
|
||||
- "read", "open", "show me", "what does X say", "display", "pull up" a specific note → use get_note with query=<note name>.
|
||||
- "list my notes", "show notes", "recent notes", "browse notes", "notes tagged X" → use list_notes (with optional q or tags).
|
||||
- "tag X with Y", "add tag Y to X", "untag Y from X", "remove tag Y from X" → use update_note with tags=[Y] and tag_mode="add" or "remove".
|
||||
- search_web: user explicitly wants a quick factual answer from the web — current events, version
|
||||
numbers, real-time facts ("what is the latest version of X", "who won the game last night").
|
||||
ONLY use when the information is clearly not in their notes and they need something fresh from
|
||||
the internet. Do NOT use for creative questions, brainstorming, game design, writing help, or
|
||||
when the user is building on content they already have. Do NOT use when the user references
|
||||
their own notes or prior research — use search_notes instead.
|
||||
- search_images: ONLY when the user explicitly asks to SEE, SHOW, DISPLAY, or VIEW an image or
|
||||
photo. Trigger phrases: "show me a picture/photo/image of", "what does X look like",
|
||||
"find a photo of", "display an image of". Do NOT use for general questions about appearance,
|
||||
descriptions, or when the user just wants information without visual content.
|
||||
- research_topic: user wants a comprehensive, multi-section research note created from web sources.
|
||||
Use whenever the user wants to deeply understand, learn about, or get a full written reference
|
||||
on any subject — regardless of how they phrase it. The topic can be anything: technical subjects,
|
||||
shopping decisions, comparisons, how things work, historical topics, etc.
|
||||
The "topic" argument should capture the full subject matter of the request.
|
||||
Prefer this over search_web when the user's request implies wanting thorough coverage rather than
|
||||
a quick answer.
|
||||
- For creative/ideation requests ("think of", "come up with", "ideas for", "help me design",
|
||||
"imagine", "brainstorm") use null (chat) — the main model answers these directly. Only route
|
||||
to a tool if the user also explicitly asks to search, look something up, or create/save something.
|
||||
- If the user references notes or research the assistant previously created, prefer search_notes
|
||||
(to retrieve the relevant note) or null (chat) so the main model can use its tools to find it.
|
||||
Never use search_web when existing notes likely contain the answer.
|
||||
- "ack": one short, natural sentence confirming the action (tool path only). Vary phrasing — do not always start with "Let me". Omit (null) for chat-only responses.
|
||||
- Do NOT wrap the JSON in markdown code fences."""
|
||||
|
||||
|
||||
# Fast-path: "Research: <topic>" sent by the Research button.
|
||||
_RESEARCH_PREFIX = re.compile(r"^[Rr]esearch:\s+(.+)", re.DOTALL)
|
||||
|
||||
# When the user refers to work the assistant previously did, they want the main
|
||||
# model to answer using existing context — not a web search. Skip intent and
|
||||
# fall through to the streaming path so the model can call search_notes itself.
|
||||
_PRIOR_WORK_REFS = re.compile(
|
||||
r"\b("
|
||||
r"research (you|that you|we) did"
|
||||
r"|research (you|that you|we) (made|created|compiled|wrote)"
|
||||
r"|note (you|that you|we) (made|created|wrote)"
|
||||
r"|notes (you|that you|we) (made|created|wrote)"
|
||||
r"|using (your|the) research"
|
||||
r"|based on (your|the) research"
|
||||
r"|from (your|the) (research|note|notes)"
|
||||
r")\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
async def classify_intent(
|
||||
user_message: str,
|
||||
tools: list[dict],
|
||||
model: str,
|
||||
history: list[dict] | None = None,
|
||||
) -> IntentResult:
|
||||
"""Classify user intent via a fast non-streaming LLM call.
|
||||
|
||||
history is a list of recent {role, content} messages (user/assistant only,
|
||||
no system messages) for resolving anaphoric references.
|
||||
|
||||
Returns an IntentResult. On any failure, returns IntentResult(tool_name=None)
|
||||
so the caller falls through to the normal streaming path.
|
||||
"""
|
||||
if not tools:
|
||||
return IntentResult()
|
||||
|
||||
# Fast-path: "Research: <topic>" is the canonical format sent by the Research
|
||||
# button in the UI. It always means research_topic — skip the LLM call entirely.
|
||||
valid_names = {t.get("function", {}).get("name") for t in tools}
|
||||
if "research_topic" in valid_names:
|
||||
m = _RESEARCH_PREFIX.match(user_message.strip())
|
||||
if m:
|
||||
topic = m.group(1).strip()
|
||||
logger.info("Intent fast-path: 'Research:' prefix → research_topic, topic='%s'", topic[:80])
|
||||
return IntentResult(
|
||||
tool_name="research_topic",
|
||||
arguments={"topic": topic},
|
||||
confidence="high",
|
||||
ack=f"I'll research that and compile a comprehensive note.",
|
||||
)
|
||||
|
||||
# Fast-path: user references prior assistant work ("research you did", "note you
|
||||
# made", etc.) — this is a request to use existing content, not search the web.
|
||||
# Return no-tool so the main model answers conversationally with search_notes
|
||||
# available if it needs to retrieve the note.
|
||||
if _PRIOR_WORK_REFS.search(user_message):
|
||||
logger.info(
|
||||
"Intent fast-path: prior-work reference detected → skipping tool dispatch"
|
||||
)
|
||||
return IntentResult()
|
||||
|
||||
tool_summary = _build_tool_summary(tools)
|
||||
today = date_type.today().isoformat()
|
||||
|
||||
messages: list[dict] = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": _SYSTEM_PROMPT_TEMPLATE.format(
|
||||
today=today, tool_summary=tool_summary
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
# Inject recent history turns so the model can resolve references
|
||||
if history:
|
||||
for turn in history:
|
||||
role = turn.get("role")
|
||||
content = turn.get("content", "")
|
||||
if role in ("user", "assistant") and content:
|
||||
messages.append({"role": role, "content": content})
|
||||
|
||||
messages.append({"role": "user", "content": user_message})
|
||||
|
||||
try:
|
||||
raw = await generate_completion(messages, model, max_tokens=350, num_ctx=4096)
|
||||
except Exception:
|
||||
logger.warning("Intent classification LLM call failed", exc_info=True)
|
||||
return IntentResult()
|
||||
|
||||
return _parse_intent(raw, tools)
|
||||
|
||||
|
||||
def _parse_intent(raw: str, tools: list[dict]) -> IntentResult:
|
||||
"""Parse the LLM's JSON response into an IntentResult."""
|
||||
|
||||
@@ -25,7 +25,6 @@ async def run_research_pipeline(
|
||||
topic: str,
|
||||
user_id: int,
|
||||
model: str,
|
||||
intent_model: str,
|
||||
buf=None,
|
||||
) -> Note:
|
||||
"""Full research pipeline: search → fetch → synthesize → create note.
|
||||
@@ -36,7 +35,7 @@ async def run_research_pipeline(
|
||||
# Step 1: Generate sub-queries
|
||||
if buf is not None:
|
||||
buf.append_event("status", {"status": "Generating search queries..."})
|
||||
queries = await _generate_sub_queries(topic, intent_model)
|
||||
queries = await _generate_sub_queries(topic, model)
|
||||
logger.info("Research: generated %d sub-queries for topic '%s'", len(queries), topic)
|
||||
|
||||
# Step 2: Search all queries in parallel (200 ms stagger to avoid hammering SearXNG)
|
||||
@@ -118,8 +117,8 @@ async def run_research_pipeline(
|
||||
return note
|
||||
|
||||
|
||||
async def _generate_sub_queries(topic: str, intent_model: str) -> list[str]:
|
||||
"""Ask the intent model for focused search queries for the topic."""
|
||||
async def _generate_sub_queries(topic: str, model: str) -> list[str]:
|
||||
"""Ask the model for focused search queries for the topic."""
|
||||
messages = [
|
||||
{
|
||||
"role": "system",
|
||||
@@ -135,7 +134,7 @@ async def _generate_sub_queries(topic: str, intent_model: str) -> list[str]:
|
||||
{"role": "user", "content": f"Topic: {topic}"},
|
||||
]
|
||||
try:
|
||||
raw = await generate_completion(messages, intent_model, max_tokens=200)
|
||||
raw = await generate_completion(messages, model, max_tokens=200)
|
||||
raw = raw.strip()
|
||||
raw = re.sub(r"^```(?:json)?\s*", "", raw)
|
||||
raw = re.sub(r"\s*```$", "", raw)
|
||||
|
||||
Reference in New Issue
Block a user