feat(rag): add database routing configuration to templates

- Add 'databases' field to TemplateDefinition and TemplateMatchResult
- Support values: 'oxigraph' (SPARQL/KG), 'qdrant' (vector search)
- Add helper methods use_oxigraph() and use_qdrant()
- Default to both databases for backward compatibility
- Allows templates to skip vector search for factual/geographic queries
This commit is contained in:
kempersc 2026-01-09 11:54:17 +01:00
parent c88fd3af70
commit 4d5641b6c5

View file

@ -641,6 +641,12 @@ class TemplateDefinition(BaseModel):
response_modes: list[str] = Field(default_factory=lambda: ["prose"])
# Optional per-language UI templates for formatting simple answers
ui_template: Optional[dict[str, str]] = None
# Database routing configuration
# Available databases: "oxigraph" (SPARQL/KG), "qdrant" (vector search)
# When specified, only the listed databases are queried (skipping others)
# Default: query both databases (backward compatible)
# Use ["oxigraph"] for factual/geographic queries where vector search adds noise
databases: list[str] = Field(default_factory=lambda: ["oxigraph", "qdrant"])
class FollowUpPattern(BaseModel):
@ -716,6 +722,9 @@ class TemplateMatchResult(BaseModel):
# Response rendering configuration (passed through from template definition)
response_modes: list[str] = Field(default_factory=lambda: ["prose"])
ui_template: Optional[dict[str, str]] = None
# Database routing configuration (passed through from template definition)
# When ["oxigraph"] only, vector search is skipped for faster, deterministic results
databases: list[str] = Field(default_factory=lambda: ["oxigraph", "qdrant"])
def requires_llm(self) -> bool:
"""Check if this template requires LLM prose generation.
@ -723,6 +732,14 @@ class TemplateMatchResult(BaseModel):
Fast path rule: If "prose" is NOT in response_modes, LLM generation is skipped.
"""
return "prose" in self.response_modes
def use_oxigraph(self) -> bool:
"""Check if this template should query Oxigraph (SPARQL/KG)."""
return "oxigraph" in self.databases
def use_qdrant(self) -> bool:
"""Check if this template should query Qdrant (vector search)."""
return "qdrant" in self.databases
class ResolvedQuestion(BaseModel):
@ -2544,6 +2561,9 @@ class TemplateClassifier(dspy.Module):
# Response rendering configuration (template-driven)
response_modes=template_data.get("response_modes", ["prose"]),
ui_template=template_data.get("ui_template"),
# Database routing configuration (template-driven)
# Default to both databases for backward compatibility
databases=template_data.get("databases", ["oxigraph", "qdrant"]),
)
except Exception as e:
logger.warning(f"Failed to parse template {template_id}: {e}")
@ -3268,6 +3288,7 @@ class TemplateSPARQLPipeline(dspy.Module):
template_def = self.instantiator._get_template(template_id)
response_modes = template_def.response_modes if template_def else ["prose"]
ui_template = template_def.ui_template if template_def else None
databases = template_def.databases if template_def else ["oxigraph", "qdrant"]
return TemplateMatchResult(
matched=True,
@ -3277,7 +3298,8 @@ class TemplateSPARQLPipeline(dspy.Module):
sparql=sparql,
reasoning=match_result.reasoning,
response_modes=response_modes,
ui_template=ui_template
ui_template=ui_template,
databases=databases
)