mindnet/app/models/dto.py

181 lines
6.2 KiB
Python
Raw Permalink Normal View History

2025-10-07 11:03:16 +02:00
"""
2025-12-15 15:40:39 +01:00
FILE: app/models/dto.py
DESCRIPTION: Pydantic-Modelle (DTOs) für Request/Response Bodies. Definiert das API-Schema.
VERSION: 0.7.1 (WP-25: Stream-Tracing Support)
2025-12-15 15:40:39 +01:00
STATUS: Active
DEPENDENCIES: pydantic, typing, uuid
2025-10-07 11:03:16 +02:00
"""
from __future__ import annotations
2025-12-07 16:32:02 +01:00
from pydantic import BaseModel, Field
from typing import List, Literal, Optional, Dict, Any
2025-12-07 19:29:48 +01:00
import uuid
2025-10-07 11:03:16 +02:00
# WP-25: Erweiterte Kanten-Typen gemäß neuer decision_engine.yaml
EdgeKind = Literal[
"references", "references_at", "backlink", "next", "prev",
"belongs_to", "depends_on", "related_to", "similar_to",
"caused_by", "derived_from", "based_on", "solves", "blocks",
"uses", "guides", "enforced_by", "implemented_in", "part_of",
"experienced_in", "impacts", "risk_of"
]
2025-10-07 11:03:16 +02:00
2025-12-07 19:29:48 +01:00
# --- Basis-DTOs ---
2025-10-07 11:03:16 +02:00
class NodeDTO(BaseModel):
"""Darstellung eines Knotens (Note oder Chunk) im API-Graph."""
id: str
type: Literal["note", "chunk"]
title: Optional[str] = None
note_id: Optional[str] = None
tags: Optional[List[str]] = None
in_degree: Optional[int] = None
out_degree: Optional[int] = None
score: Optional[float] = None
section_title: Optional[str] = None
section_path: Optional[str] = None
path: Optional[str] = None
class EdgeDTO(BaseModel):
"""Darstellung einer Kante im API-Graph."""
id: str
2025-12-08 10:32:57 +01:00
kind: str
2025-10-07 11:03:16 +02:00
source: str
target: str
weight: float
direction: Literal["out", "in", "undirected"] = "out"
# WP-24c v4.5.3: Erweiterte Provenance-Werte für Chunk-Aware Edges
# Unterstützt alle tatsächlich verwendeten Provenance-Typen im System
provenance: Optional[Literal[
"explicit", "rule", "smart", "structure",
"explicit:callout", "explicit:wikilink", "explicit:note_zone", "explicit:note_scope",
"inline:rel", "callout:edge", "semantic_ai", "structure:belongs_to", "structure:order",
"derived:backlink", "edge_defaults", "global_pool"
]] = "explicit"
2025-12-18 14:30:24 +01:00
confidence: float = 1.0
target_section: Optional[str] = None
2025-10-07 11:03:16 +02:00
2025-12-07 19:29:48 +01:00
# --- Request Models ---
2025-10-07 11:03:16 +02:00
class QueryRequest(BaseModel):
"""
Request für /query. Unterstützt Multi-Stream Isolation via filters.
WP-24c v4.1.0: Erweitert um Section-Filtering und Scope-Awareness.
2025-10-07 11:03:16 +02:00
"""
mode: Literal["semantic", "edge", "hybrid"] = "hybrid"
query: Optional[str] = None
query_vector: Optional[List[float]] = None
top_k: int = 10
2025-12-07 16:32:02 +01:00
expand: Dict = {"depth": 1, "edge_types": ["references", "belongs_to", "prev", "next", "depends_on", "related_to"]}
2025-10-07 11:03:16 +02:00
filters: Optional[Dict] = None
ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True}
2025-12-07 16:32:02 +01:00
explain: bool = False
2025-12-18 16:53:29 +01:00
# WP-22/25: Dynamische Gewichtung der Graphen-Highways
boost_edges: Optional[Dict[str, float]] = None
# WP-24c v4.1.0: Section-Filtering für präzise Section-Links
target_section: Optional[str] = None
2025-12-07 16:32:02 +01:00
2025-12-07 19:29:48 +01:00
class FeedbackRequest(BaseModel):
"""User-Feedback zu einem spezifischen Treffer oder der Gesamtantwort."""
2025-12-07 19:29:48 +01:00
query_id: str = Field(..., description="ID der ursprünglichen Suche")
2025-12-09 22:27:56 +01:00
node_id: str = Field(..., description="ID des bewerteten Treffers oder 'generated_answer'")
2025-12-18 16:53:29 +01:00
score: int = Field(..., ge=1, le=5, description="1 (Irrelevant) bis 5 (Perfekt)")
2025-12-07 19:29:48 +01:00
comment: Optional[str] = None
2025-12-08 10:32:57 +01:00
class ChatRequest(BaseModel):
"""Request für /chat (WP-25 Einstieg)."""
2025-12-08 10:32:57 +01:00
message: str = Field(..., description="Die Nachricht des Users")
2025-12-18 16:53:29 +01:00
conversation_id: Optional[str] = Field(None, description="ID für Chat-Verlauf")
2025-12-08 10:32:57 +01:00
top_k: int = 5
explain: bool = False
# --- Explanation Models ---
2025-12-07 16:32:02 +01:00
class ScoreBreakdown(BaseModel):
2025-12-18 14:30:24 +01:00
"""Aufschlüsselung der Score-Komponenten nach der WP-22 Formel."""
2025-12-08 10:32:57 +01:00
semantic_contribution: float
edge_contribution: float
centrality_contribution: float
2025-12-07 16:32:02 +01:00
raw_semantic: float
raw_edge_bonus: float
raw_centrality: float
2025-12-08 10:32:57 +01:00
node_weight: float
2025-12-18 16:44:47 +01:00
status_multiplier: float = 1.0
graph_boost_factor: float = 1.0
2025-12-07 16:32:02 +01:00
class Reason(BaseModel):
2025-12-07 19:29:48 +01:00
"""Ein semantischer Grund für das Ranking."""
# WP-25: 'status' hinzugefügt für Synchronität mit retriever.py
kind: Literal["semantic", "edge", "type", "centrality", "lifecycle", "status"]
2025-12-07 16:32:02 +01:00
message: str
score_impact: Optional[float] = None
details: Optional[Dict[str, Any]] = None
class Explanation(BaseModel):
2025-12-07 19:29:48 +01:00
"""Container für alle Erklärungsdaten eines Treffers."""
2025-12-07 16:32:02 +01:00
breakdown: ScoreBreakdown
reasons: List[Reason]
related_edges: Optional[List[EdgeDTO]] = None
2025-12-18 16:44:47 +01:00
applied_intent: Optional[str] = None
applied_boosts: Optional[Dict[str, float]] = None
2025-12-07 16:32:02 +01:00
2025-12-07 19:29:48 +01:00
# --- Response Models ---
2025-10-07 11:03:16 +02:00
class QueryHit(BaseModel):
"""
Einzelnes Trefferobjekt.
WP-25: stream_origin hinzugefügt für Tracing und Feedback-Optimierung.
WP-24c v4.1.0: source_chunk_id für RAG-Kontext hinzugefügt.
"""
2025-10-07 11:03:16 +02:00
node_id: str
2025-12-18 16:53:29 +01:00
note_id: str
2025-10-07 11:03:16 +02:00
semantic_score: float
edge_bonus: float
centrality_bonus: float
total_score: float
paths: Optional[List[List[Dict]]] = None
source: Optional[Dict] = None
2025-12-18 16:44:47 +01:00
payload: Optional[Dict] = None
2025-12-07 16:32:02 +01:00
explanation: Optional[Explanation] = None
stream_origin: Optional[str] = Field(None, description="Name des Ursprungs-Streams")
source_chunk_id: Optional[str] = Field(None, description="Chunk-ID der Quelle (für RAG-Kontext)")
2025-10-07 11:03:16 +02:00
class QueryResponse(BaseModel):
"""Antwortstruktur für /query (wird von DecisionEngine Streams genutzt)."""
2025-12-07 19:29:48 +01:00
query_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
2025-10-07 11:03:16 +02:00
results: List[QueryHit]
used_mode: str
latency_ms: int
class GraphResponse(BaseModel):
2025-12-07 19:29:48 +01:00
"""Antwortstruktur für /graph/{note_id}."""
2025-10-07 11:03:16 +02:00
center_note_id: str
nodes: List[NodeDTO]
edges: List[EdgeDTO]
2025-12-08 10:32:57 +01:00
stats: Dict[str, int]
class ChatResponse(BaseModel):
"""
Antwortstruktur für /chat.
WP-25: 'intent' spiegelt nun die gewählte Strategie wider.
2025-12-08 10:32:57 +01:00
"""
2025-12-18 16:53:29 +01:00
query_id: str = Field(..., description="Traceability ID")
2025-12-08 10:32:57 +01:00
answer: str = Field(..., description="Generierte Antwort vom LLM")
sources: List[QueryHit] = Field(..., description="Die genutzten Quellen (alle Streams)")
2025-12-08 18:48:24 +01:00
latency_ms: int
intent: Optional[str] = Field("FACT", description="Die gewählte WP-25 Strategie")
intent_source: Optional[str] = Field("LLM_Router", description="Quelle der Intent-Erkennung")