Update project and configurations

This commit is contained in:
Zou-Seay
2026-06-11 16:28:00 +08:00
parent 12d3922091
commit a29a91867d
237 changed files with 164880 additions and 90 deletions

View File

@@ -0,0 +1,173 @@
from __future__ import annotations
import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
import yaml
from app.schemas.configuration import (
ActionsConfig,
ContextRewriteConfig,
DialogActsConfig,
DialogRulesConfig,
DomainConfig,
FormsConfig,
ResponsesConfig,
WorkflowTemplatesConfig,
)
from app.services.dialog_act import DialogActEngine
from app.services.dialog_rules import DialogRuleEngine
from app.services.intent_registry import IntentRegistry
from app.services.rewrite_engine import ContextRewriteEngine
@dataclass
class RuntimeConfigBundle:
intent_registry: IntentRegistry
response_templates: dict[str, str]
intent_hints: dict[str, str]
dialog_rules: DialogRuleEngine
dialog_act_engine: DialogActEngine
workflow_templates: WorkflowTemplatesConfig
rewrite_engine: ContextRewriteEngine = field(default_factory=ContextRewriteEngine)
class ConfigLoader:
def __init__(
self,
domain_path: str,
action_path: str,
response_path: str,
form_path: str | None = None,
rule_path: str | None = None,
dialog_act_path: str | None = None,
workflow_path: str | None = None,
legacy_intent_path: str | None = None,
context_rewrite_path: str | None = None,
) -> None:
self._domain_path = Path(domain_path)
self._action_path = Path(action_path)
self._response_path = Path(response_path)
self._form_path = Path(form_path) if form_path else None
self._rule_path = Path(rule_path) if rule_path else None
self._dialog_act_path = Path(dialog_act_path) if dialog_act_path else None
self._workflow_path = Path(workflow_path) if workflow_path else None
self._legacy_intent_path = Path(legacy_intent_path) if legacy_intent_path else None
self._context_rewrite_path = Path(context_rewrite_path) if context_rewrite_path else None
def load(self) -> RuntimeConfigBundle:
if self._domain_path.exists() and self._action_path.exists():
return self._load_from_config_files()
if self._legacy_intent_path is not None and self._legacy_intent_path.exists():
return RuntimeConfigBundle(
intent_registry=IntentRegistry.from_json(str(self._legacy_intent_path)),
response_templates=self._load_response_templates(),
intent_hints={},
dialog_rules=self._load_dialog_rules(),
dialog_act_engine=self._load_dialog_act_engine(),
workflow_templates=self._load_workflow_templates(),
rewrite_engine=self._load_rewrite_engine(),
)
raise FileNotFoundError(
"no runtime config found, expected config/*.yml or legacy intent json"
)
def _load_from_config_files(self) -> RuntimeConfigBundle:
domain = DomainConfig.model_validate(self._read_structured_file(self._domain_path))
actions = ActionsConfig.model_validate(self._read_structured_file(self._action_path))
forms = self._load_forms()
action_map = {item.action_id: item for item in actions.actions}
form_map = {item.intent_id: item for item in forms.forms}
intents = []
for item in domain.intents:
form = form_map.get(item.intent_id)
if form is not None:
item = item.model_copy(
update={
"required_slots": form.required_slots,
"ask_templates": form.ask_templates,
}
)
intents.append(item.to_intent_definition(action_map))
intent_hints = {
item.intent_id: item.label.strip()
for item in domain.intents
if item.label and item.label.strip()
}
return RuntimeConfigBundle(
intent_registry=IntentRegistry(intents),
response_templates=self._load_response_templates(),
intent_hints=intent_hints,
dialog_rules=self._load_dialog_rules(),
dialog_act_engine=self._load_dialog_act_engine(),
workflow_templates=self._load_workflow_templates(),
rewrite_engine=self._load_rewrite_engine(),
)
def _load_response_templates(self) -> dict[str, str]:
if not self._response_path.exists():
return {}
raw = self._read_structured_file(self._response_path)
parsed = ResponsesConfig.model_validate(raw)
return parsed.templates
def _load_forms(self) -> FormsConfig:
if self._form_path is None or not self._form_path.exists():
return FormsConfig()
raw = self._read_structured_file(self._form_path)
return FormsConfig.model_validate(raw)
def _load_dialog_rules(self) -> DialogRuleEngine:
if self._rule_path is None or not self._rule_path.exists():
return DialogRuleEngine()
raw = self._read_structured_file(self._rule_path)
parsed = DialogRulesConfig.model_validate(raw)
return DialogRuleEngine(
stop_phrases=tuple(parsed.stop.phrases) or DialogRuleEngine.stop_phrases,
positive_confirmation_tokens=tuple(parsed.confirmation.positive_tokens)
or DialogRuleEngine.positive_confirmation_tokens,
negative_confirmation_tokens=tuple(parsed.confirmation.negative_tokens)
or DialogRuleEngine.negative_confirmation_tokens,
confirmation_required_intents=tuple(parsed.confirmation.required_intents)
or DialogRuleEngine.confirmation_required_intents,
confirmation_required_risk_levels=tuple(parsed.confirmation.required_risk_levels)
or DialogRuleEngine.confirmation_required_risk_levels,
metadata={"source": str(self._rule_path)},
)
def _load_dialog_act_engine(self) -> DialogActEngine:
if self._dialog_act_path is None or not self._dialog_act_path.exists():
return DialogActEngine()
raw = self._read_structured_file(self._dialog_act_path)
parsed = DialogActsConfig.model_validate(raw)
return DialogActEngine(
patterns={
item.act_id: tuple(item.phrases)
for item in parsed.acts
},
numeric_patterns={
item.act_id: tuple(item.numeric_patterns)
for item in parsed.acts
if item.numeric_patterns
},
)
def _load_rewrite_engine(self) -> ContextRewriteEngine:
if self._context_rewrite_path is None or not self._context_rewrite_path.exists():
return ContextRewriteEngine()
raw = self._read_structured_file(self._context_rewrite_path)
config = ContextRewriteConfig.model_validate(raw)
return ContextRewriteEngine(config=config)
def _load_workflow_templates(self) -> WorkflowTemplatesConfig:
if self._workflow_path is None or not self._workflow_path.exists():
return WorkflowTemplatesConfig()
raw = self._read_structured_file(self._workflow_path)
return WorkflowTemplatesConfig.model_validate(raw)
def _read_structured_file(self, path: Path) -> dict[str, Any]:
if path.suffix.lower() == ".json":
return json.loads(path.read_text(encoding="utf-8"))
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}