Commit 8c452e91 authored by Kantz's avatar Kantz
Browse files

funktionsweise mit neuem Retrivalmodus wieder hiergestellt.

parent 3d666534
......@@ -6,7 +6,7 @@ def generate_hint(
LLM_solution: str,
math_solution: str | None = None,
history: str | None = None,
retrival: str | None = None,
sources: str | None = None,
) -> str:
system_prompt = (
"Du bist ein didaktischer Tutor. "
......@@ -28,8 +28,8 @@ def generate_hint(
prompt += "Mathematische Loesung:\n" + math_solution + "\n"
if history:
prompt += "\nHistorie:\n" + history + "\n"
if retrival:
prompt = "\nKontext:\n" + retrival + "\n" + prompt
if sources:
prompt = "\nKontext:\n" + sources + "\n" + prompt
prompt += "\nGebe einen hilfreichen Tipp zur Lösung der Aufgabe. Halte dich kurz und prägnant."
result = llm_client.chat(
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}],
......
......@@ -6,7 +6,8 @@ from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from app.deterministic_services import context_store
from app.deterministic_services.vector_store import Retrieved
from app.deterministic_services import Source
router = APIRouter()
......@@ -21,7 +22,7 @@ class ContextRequest(BaseModel):
draft: Optional[str] = None
@router.post("/api/context/retrieval")
def get_retrieval_context(request: ContextRequest) -> dict:
def get_retrieval_context(request: ContextRequest) -> List[dict]:
if not request or not request.messages:
raise HTTPException(status_code=400, detail="messages required")
......@@ -31,29 +32,7 @@ def get_retrieval_context(request: ContextRequest) -> dict:
if not sheet:
raise HTTPException(status_code=404, detail="context sheet not found")
result = context_store.get_retrieval_context(sheet)
if not result or not result.get("query"):
return {
"chat_id": chat_id,
"query": "",
"children": [],
"children_direct": [],
"children_expanded": [],
"subsections": [],
"sections": [],
"neighbors": [],
}
def pack(items: list[Retrieved]) -> list[dict]:
return [item.to_dict() for item in items]
return {
"chat_id": chat_id,
"query": result["query"],
"children": pack(result["children"]),
"children_direct": pack(result["children_direct"]),
"children_expanded": pack(result["children_expanded"]),
"subsections": pack(result["subsections"]),
"sections": pack(result["sections"]),
"neighbors": pack(result["neighbors"]),
}
sources = context_store.get_retrieval(sheet)
if not sources:
return []
return [source.model_dump() for source in sources]
......@@ -41,7 +41,7 @@ class QueryRequest(BaseModel):
expand_links: bool = True
section_index: Optional[int] = None
subsection_index: Optional[int] = None
type_filter: Optional[List[str]] = None
source_type_filter: Optional[List[str]] = None
neighbor_expand: int = 0
......@@ -83,23 +83,11 @@ def query(request: QueryRequest) -> dict:
k=request.k,
section_index=request.section_index,
subsection_index=request.subsection_index,
type_filter=request.type_filter,
source_type_filter=request.source_type_filter,
expand_links=request.expand_links,
neighbor_expand=request.neighbor_expand,
)
def pack(items: list[vector_store.Retrieved]) -> list[dict]:
return [item.to_dict() for item in items]
return {
"query": result["query"],
"children": pack(result["children"]),
"children_direct": pack(result["children_direct"]),
"children_expanded": pack(result["children_expanded"]),
"subsections": pack(result["subsections"]),
"sections": pack(result["sections"]),
"neighbors": pack(result["neighbors"]),
}
return result
@router.get("/api/retrieval/sections")
......
......@@ -78,7 +78,7 @@ def add_retrieval_context(
query: str,
sources: list[Source],
) -> None:
retrieval_entry = {"query": query, "sources": [source.to_string() for source in sources]}
retrieval_entry = {"query": query, "sources": [source.model_dump() for source in sources]}
sheet["retrieval_contexts"].append(retrieval_entry)
sheet["updated_at"] = _utc_now()
......@@ -91,7 +91,7 @@ def update_retrieval_context(
retrievals = sheet.get("retrieval_contexts", [])
if retrievals:
latest_retrieval = retrievals[-1]
latest_retrieval["sources"] = [source.to_string() for source in sources]
latest_retrieval["sources"] = [source.model_dump() for source in sources]
latest_retrieval["query"] = query
sheet["updated_at"] = _utc_now()
......@@ -167,16 +167,15 @@ def format_sheet(sheet: dict[str, Any]) -> str:
blocks = []
for item in retrievals:
query = item.get('query', '')
context = item.get('context', '')
sources = item.get('sources', [])
# Erstelle eine formatierte Liste der Quellen mit ihren Scores
source_blocks = []
for source in sources:
source_blocks.append(f" - [{source.get('score', 0):.3f}] {source.get('label', '')} {source.get('ref', '')}")
source_blocks.append(Source.model_validate(source).to_string())
source_info = "\n".join(source_blocks) if source_blocks else ""
blocks.append(f"QUERY: {query}\n{context}\nSOURCES:\n{source_info}")
blocks.append(f"QUERY: {query}\nSOURCES:\n{source_info}")
parts.append("RETRIEVAL_CONTEXT:\n" + "\n\n".join(blocks))
else:
parts.append("RETRIEVAL_CONTEXT:\n(leer)")
......@@ -200,12 +199,13 @@ def format_sheet(sheet: dict[str, Any]) -> str:
return "\n\n".join(parts)
def get_retrival(sheet: dict[str, Any]) -> str:
def get_retrieval(sheet: dict[str, Any]) -> list[Source]:
retrievals = sheet.get("retrieval_contexts", [])
if not retrievals:
return ""
latest = retrievals[-1]
return latest.get("context", "")
return []
latest_retrieval = retrievals[-1]
sources= latest_retrieval.get("sources", [])
return [Source.model_validate(source) for source in sources]
def save_sheet(sheet: dict[str, Any]) -> None:
os.makedirs(_LOG_DIR, exist_ok=True)
......
......@@ -88,12 +88,13 @@ def run_chat(messages: list[dict], draft: str | None = None) -> dict:
_bootstrap_context(sheet, full_query, tool_log)
# Hinweis und Ausgaben generierung
hint_args = {
"task": context_store.get_task(sheet),
"LLM_solution": context_store.last_LLM_solution(sheet),
"math_solution": context_store.first_math_solution(sheet),
"history": context_store.format_history(messages),
"retrival": context_store.get_retrival(sheet),
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(sheet)]),
}
reply = hint_LLM.generate_hint(**hint_args)
_append_tool_log(tool_log, "generate_hint", hint_args, reply)
......
......@@ -258,7 +258,7 @@ class Source(BaseModel):
def to_dict(self) -> Dict[str, Any]:
return {
"source_id": self.source_id,
"source_id": self.source_id.to_dict(),
"retrieved_as": self.retrieved_as,
"source_type": self.source_type,
"score": self.score,
......@@ -281,6 +281,15 @@ class SourceID(BaseModel):
title: str
doc_type: str
def to_dict(self) -> Dict[str, Any]:
return {
"chapter_title": self.chapter_title,
"section_title": self.section_title,
"subsection_title": self.subsection_title,
"title": self.title,
"doc_type": self.doc_type,
}
def to_string(self) -> str:
string_rep = self.title
if self.subsection_title:
......
......@@ -36,7 +36,7 @@ def main() -> None:
"LLM_solution": context_store.last_LLM_solution(sheet),
"math_solution": context_store.first_math_solution(sheet),
"history": context_store.get_history(sheet),
"retrival": context_store.get_retrival(sheet),
"retrival": context_store.get_retrieval(sheet),
}
reply = hint_LLM.generate_hint(**hint_args)
......
......@@ -23,11 +23,59 @@ type ArchivedChatDetail = {
history: ChatMessage[];
};
type ContextSource = {
source_id: {
chapter_title?: string | null;
section_title?: string | null;
subsection_title?: string | null;
title: string;
doc_type: string;
};
retrieved_as?: string | null;
source_type?: string | null;
score?: number;
markdown?: string | null;
};
const createSessionId = () =>
`session_${Date.now().toString(36)}_${Math.random()
.toString(36)
.slice(2, 8)}`;
const sourceIdToUid = (source: ContextSource, index: number) => {
const parts = [
source.source_id.chapter_title,
source.source_id.section_title,
source.source_id.subsection_title,
source.source_id.title,
source.source_id.doc_type,
].filter(Boolean);
return parts.length ? parts.join("|") : `source_${index}`;
};
const sourceIdToPath = (source: ContextSource) => {
const parts = [
source.source_id.chapter_title,
source.source_id.section_title,
source.source_id.subsection_title,
].filter(Boolean);
return parts.length ? parts.join(" / ") : null;
};
const toRetrievedDoc = (source: ContextSource, index: number): RetrievedDoc => ({
uid: sourceIdToUid(source, index),
doc_type: source.source_id.doc_type,
score: source.score,
metadata: {
section_title: source.source_id.section_title ?? null,
subsection_title: source.source_id.subsection_title ?? null,
title: source.source_id.title ?? null,
type: source.source_type ?? null,
path: sourceIdToPath(source),
},
markdown: source.markdown || "",
});
export default function App() {
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
const [draft, setDraft] = useState("");
......@@ -64,29 +112,10 @@ export default function App() {
setMessages((prev) => [...prev, userMessage]);
setDraft("");
try {
setRetrievalLoading(true);
setRetrievalError(null);
const retrievalRequest = fetch(`${backendUrl}/api/retrieval/query`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: trimmed,
k: 4,
expand_links: true,
}),
}).then(async (res) => {
if (!res.ok) {
throw new Error(`Retrieval failed: ${res.status}`);
}
const payload = await res.json();
setDirectChildren(payload.children_direct || []);
setIndirectChildren(payload.children_expanded || []);
setSubsections(payload.subsections || []);
setSections(payload.sections || []);
});
setRetrievalLoading(true);
setRetrievalError(null);
try {
const response = await fetch(`${backendUrl}/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
......@@ -114,8 +143,6 @@ export default function App() {
},
]);
}
await retrievalRequest;
} catch (error) {
setMessages((prev) => [
...prev,
......@@ -125,6 +152,63 @@ export default function App() {
text: "Chat request failed. Check the API logs.",
},
]);
setRetrievalError("Retrieval failed. Check the API logs.");
setRetrievalLoading(false);
void error;
return;
}
try {
const contextResponse = await fetch(`${backendUrl}/api/context/retrieval`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
messages: [...messages, userMessage].map((message) => ({
role: message.role,
text: message.text,
})),
draft: chatSessionId,
}),
});
if (!contextResponse.ok) {
throw new Error(`Retrieval failed: ${contextResponse.status}`);
}
const sources: ContextSource[] = await contextResponse.json();
const nextDirect: RetrievedDoc[] = [];
const nextIndirect: RetrievedDoc[] = [];
const nextSubsections: RetrievedDoc[] = [];
const nextSections: RetrievedDoc[] = [];
sources.forEach((source, index) => {
const doc = toRetrievedDoc(source, index);
switch (source.retrieved_as) {
case "children_direct":
nextDirect.push(doc);
break;
case "children_expanded":
nextIndirect.push(doc);
break;
case "subsections":
nextSubsections.push(doc);
break;
case "sections":
nextSections.push(doc);
break;
case "neighbors":
nextIndirect.push(doc);
break;
default:
nextIndirect.push(doc);
}
});
setDirectChildren(nextDirect);
setIndirectChildren(nextIndirect);
setSubsections(nextSubsections);
setSections(nextSections);
} catch (error) {
setRetrievalError("Retrieval failed. Check the API logs.");
void error;
} finally {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment