fix(rag): add fallback imports for semantic_router and temporal_intent
Support both relative and absolute imports for running as module or script.
This commit is contained in:
parent
bd06e4f864
commit
c0d31b3905
1 changed files with 114 additions and 11 deletions
|
|
@ -33,18 +33,32 @@ from dspy import Example, Prediction, History
|
|||
from dspy.streaming import StatusMessage, StreamListener, StatusMessageProvider
|
||||
|
||||
# Semantic routing (Signal-Decision pattern) for fast LLM-free query classification
|
||||
from .semantic_router import (
|
||||
QuerySignals,
|
||||
RouteConfig,
|
||||
get_signal_extractor,
|
||||
get_decision_router,
|
||||
)
|
||||
try:
|
||||
from semantic_router import (
|
||||
QuerySignals,
|
||||
RouteConfig,
|
||||
get_signal_extractor,
|
||||
get_decision_router,
|
||||
)
|
||||
except ImportError:
|
||||
from .semantic_router import (
|
||||
QuerySignals,
|
||||
RouteConfig,
|
||||
get_signal_extractor,
|
||||
get_decision_router,
|
||||
)
|
||||
|
||||
# Temporal intent extraction for detailed temporal constraint detection
|
||||
from .temporal_intent import (
|
||||
TemporalConstraint,
|
||||
get_temporal_extractor,
|
||||
)
|
||||
try:
|
||||
from temporal_intent import (
|
||||
TemporalConstraint,
|
||||
get_temporal_extractor,
|
||||
)
|
||||
except ImportError:
|
||||
from .temporal_intent import (
|
||||
TemporalConstraint,
|
||||
get_temporal_extractor,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -4578,6 +4592,7 @@ class HeritageRAGPipeline(dspy.Module):
|
|||
# TEMPLATE-FIRST APPROACH: Try template-based generation before LLM
|
||||
template_used = False
|
||||
template_id = None
|
||||
template_result = None # Keep full template_result for requires_llm() check
|
||||
|
||||
if "sparql" in routing.sources:
|
||||
# 3a. TRY TEMPLATE-BASED SPARQL FIRST (deterministic, validated)
|
||||
|
|
@ -4933,6 +4948,82 @@ class HeritageRAGPipeline(dspy.Module):
|
|||
"query_type": detected_query_type,
|
||||
}
|
||||
|
||||
# =================================================================
|
||||
# FACTUAL QUERY FAST-PATH: Skip LLM for table/map/count queries
|
||||
# =================================================================
|
||||
# If template matched and doesn't require LLM prose, skip expensive LLM generation
|
||||
# This provides instant responses for factual queries (lists, counts, maps)
|
||||
skip_llm_generation = False
|
||||
factual_answer_text = None
|
||||
|
||||
if template_result and hasattr(template_result, 'requires_llm') and not template_result.requires_llm():
|
||||
skip_llm_generation = True
|
||||
response_modes = getattr(template_result, 'response_modes', [])
|
||||
logger.info(f"[Streaming FAST-PATH] Skipping LLM generation, response_modes={response_modes}")
|
||||
|
||||
# Generate answer from ui_template (same logic as main.py non-streaming endpoint)
|
||||
if hasattr(template_result, 'ui_template') and template_result.ui_template:
|
||||
lang = language if language in template_result.ui_template else "nl"
|
||||
ui_tmpl = template_result.ui_template.get(lang, template_result.ui_template.get("nl", ""))
|
||||
|
||||
# Build context for template rendering
|
||||
template_context = {
|
||||
"result_count": len(retrieved_results),
|
||||
"count": retrieved_results[0].get("count", len(retrieved_results)) if retrieved_results else 0,
|
||||
**(template_result.slots or {})
|
||||
}
|
||||
|
||||
# Add human-readable labels for institution types and locations
|
||||
try:
|
||||
from schema_labels import get_label_resolver
|
||||
label_resolver = get_label_resolver()
|
||||
INSTITUTION_TYPE_LABELS_NL = label_resolver.get_all_institution_type_labels("nl")
|
||||
SUBREGION_LABELS = label_resolver.get_all_subregion_labels("nl")
|
||||
except ImportError:
|
||||
logger.warning("[Streaming FAST-PATH] schema_labels not available, using fallback")
|
||||
INSTITUTION_TYPE_LABELS_NL = {
|
||||
"M": "musea", "L": "bibliotheken", "A": "archieven", "G": "galerijen",
|
||||
"O": "overheidsinstellingen", "R": "onderzoekscentra", "C": "bedrijfsarchieven",
|
||||
"U": "instellingen", "B": "botanische tuinen en dierentuinen",
|
||||
"E": "onderwijsinstellingen", "S": "heemkundige kringen", "F": "monumenten",
|
||||
"I": "immaterieel erfgoedgroepen", "X": "gecombineerde instellingen",
|
||||
"P": "privéverzamelingen", "H": "religieuze erfgoedsites",
|
||||
"D": "digitale platforms", "N": "erfgoedorganisaties", "T": "culinair erfgoed"
|
||||
}
|
||||
SUBREGION_LABELS = {
|
||||
"NL-DR": "Drenthe", "NL-FR": "Friesland", "NL-GE": "Gelderland",
|
||||
"NL-GR": "Groningen", "NL-LI": "Limburg", "NL-NB": "Noord-Brabant",
|
||||
"NL-NH": "Noord-Holland", "NL-OV": "Overijssel", "NL-UT": "Utrecht",
|
||||
"NL-ZE": "Zeeland", "NL-ZH": "Zuid-Holland", "NL-FL": "Flevoland"
|
||||
}
|
||||
|
||||
# Add institution_type_nl label
|
||||
if "institution_type" in (template_result.slots or {}):
|
||||
type_code = template_result.slots["institution_type"]
|
||||
template_context["institution_type_nl"] = INSTITUTION_TYPE_LABELS_NL.get(type_code, type_code)
|
||||
|
||||
# Add human-readable location label
|
||||
if "location" in (template_result.slots or {}):
|
||||
loc_code = template_result.slots["location"]
|
||||
if loc_code in SUBREGION_LABELS:
|
||||
template_context["location"] = SUBREGION_LABELS[loc_code]
|
||||
|
||||
# Render template with simple replacement (avoids Jinja2 dependency)
|
||||
factual_answer_text = ui_tmpl
|
||||
for key, value in template_context.items():
|
||||
factual_answer_text = factual_answer_text.replace("{{ " + key + " }}", str(value))
|
||||
factual_answer_text = factual_answer_text.replace("{{" + key + "}}", str(value))
|
||||
|
||||
elif "count" in response_modes:
|
||||
# Count query
|
||||
count_value = retrieved_results[0].get("count", len(retrieved_results)) if retrieved_results else 0
|
||||
factual_answer_text = f"Aantal: {count_value}"
|
||||
else:
|
||||
# List/table query - simple result count
|
||||
factual_answer_text = f"Gevonden: {len(retrieved_results)} resultaten."
|
||||
|
||||
logger.info(f"[Streaming FAST-PATH] Generated factual answer: {factual_answer_text}")
|
||||
|
||||
# =================================================================
|
||||
# ANSWER GENERATION PHASE - Stream tokens using dspy.streamify
|
||||
# =================================================================
|
||||
|
|
@ -4952,7 +5043,18 @@ class HeritageRAGPipeline(dspy.Module):
|
|||
current_field = None # Track which DSPy output field we're in
|
||||
STREAMABLE_FIELDS = {'answer'} # Only stream these fields to frontend
|
||||
|
||||
while not streaming_succeeded and retry_count <= max_stream_retries:
|
||||
# FAST-PATH: If factual query, yield answer immediately and skip LLM
|
||||
if skip_llm_generation and factual_answer_text:
|
||||
answer_text = factual_answer_text
|
||||
confidence = 1.0 # Factual queries from SPARQL have high confidence
|
||||
citations = ["SPARQL kennisgraaf"]
|
||||
follow_up = []
|
||||
streaming_succeeded = True
|
||||
yield {"type": "token", "content": answer_text}
|
||||
logger.info(f"[Streaming FAST-PATH] Yielded factual answer, skipping LLM generation")
|
||||
|
||||
# STANDARD PATH: Use LLM for prose generation
|
||||
while not skip_llm_generation and not streaming_succeeded and retry_count <= max_stream_retries:
|
||||
try:
|
||||
# Create streamified version of the answer generator
|
||||
streamified_answer_gen = dspy.streamify(self.answer_gen)
|
||||
|
|
@ -5109,6 +5211,7 @@ class HeritageRAGPipeline(dspy.Module):
|
|||
# Template-based SPARQL fields
|
||||
template_used=template_used, # Whether template was used instead of LLM generation
|
||||
template_id=template_id, # Which template was used (None if LLM fallback)
|
||||
factual_result=skip_llm_generation, # True if LLM was skipped (factual query fast-path)
|
||||
)
|
||||
|
||||
# Cache the response (fire and forget)
|
||||
|
|
|
|||
Loading…
Reference in a new issue