Commit ca498dfa authored by Kantz's avatar Kantz
Browse files

reworking the context architecture

parent 6cf9eb8c
...@@ -7,7 +7,11 @@ from typing import List, Optional ...@@ -7,7 +7,11 @@ from typing import List, Optional
import app.config as config import app.config as config
from app.deterministic_services import session_store from app.deterministic_services import session_store
from app.deterministic_services import context_store, retrieval_store, task_catalog from app.deterministic_services import retrieval_store, task_catalog
from app.deterministic_services.context_stores import (
context_store_base,
context_store_task,
)
from app.deterministic_services import socratic_oranisator from app.deterministic_services import socratic_oranisator
from app.deterministic_services.orchestrators import ( from app.deterministic_services.orchestrators import (
orchestrator_qa, orchestrator_qa,
...@@ -129,10 +133,10 @@ def chat(request: ChatRequest) -> ChatResponse: ...@@ -129,10 +133,10 @@ def chat(request: ChatRequest) -> ChatResponse:
@router.post("/api/chat/bootstrap-socratic", response_model=SocraticBootstrapResponse) @router.post("/api/chat/bootstrap-socratic", response_model=SocraticBootstrapResponse)
def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapResponse: def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapResponse:
chat_id = context_store.get_chat_id([], draft=request.draft) chat_id = context_store_base.get_chat_id([], draft=request.draft)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
sheet = context_store.context_store_task.init_sheet(chat_id, []) sheet = context_store_task.init_sheet(chat_id, [])
try: try:
topic_entry = socratic_oranisator.get_topic_entry(request.topic_key) topic_entry = socratic_oranisator.get_topic_entry(request.topic_key)
...@@ -145,9 +149,9 @@ def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapRe ...@@ -145,9 +149,9 @@ def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapRe
pg_url=config.get_postgres_url(), pg_url=config.get_postgres_url(),
parent_refs=refs, parent_refs=refs,
) )
context_store.set_sources(sheet, sources) context_store_base.set_sources(sheet, sources)
context_store.set_initialized(sheet, True) context_store_base.set_initialized(sheet, True)
context_store.save_sheet(sheet) context_store_base.save_sheet(sheet)
try: try:
reply = socratic_oranisator.get_initial_message(request.topic_key) reply = socratic_oranisator.get_initial_message(request.topic_key)
...@@ -155,8 +159,8 @@ def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapRe ...@@ -155,8 +159,8 @@ def bootstrap_socratic(request: SocraticBootstrapRequest) -> SocraticBootstrapRe
logger.exception("Socratic bootstrap prompt lookup failed") logger.exception("Socratic bootstrap prompt lookup failed")
raise HTTPException(status_code=500, detail=str(exc)) from exc raise HTTPException(status_code=500, detail=str(exc)) from exc
context_store.append_history_message(sheet, "assistant", reply) context_store_base.append_history_message(sheet, "assistant", reply)
context_store.save_sheet(sheet) context_store_base.save_sheet(sheet)
return SocraticBootstrapResponse( return SocraticBootstrapResponse(
reply=reply, reply=reply,
......
...@@ -7,7 +7,10 @@ from typing import List, Optional ...@@ -7,7 +7,10 @@ from typing import List, Optional
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from app.deterministic_services import context_store from app.deterministic_services.context_stores import (
context_store_base,
context_store_task,
)
router = APIRouter() router = APIRouter()
...@@ -33,12 +36,12 @@ def get_retrieval_context(request: ContextRequest) -> List[dict]: ...@@ -33,12 +36,12 @@ def get_retrieval_context(request: ContextRequest) -> List[dict]:
raise HTTPException(status_code=400, detail="messages required") raise HTTPException(status_code=400, detail="messages required")
messages = [{"role": m.role, "content": m.text} for m in request.messages] messages = [{"role": m.role, "content": m.text} for m in request.messages]
chat_id = context_store.get_chat_id(messages, draft=request.draft) chat_id = context_store_base.get_chat_id(messages, draft=request.draft)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
raise HTTPException(status_code=404, detail="context sheet not found") raise HTTPException(status_code=404, detail="context sheet not found")
sources = context_store.get_retrieval(sheet) sources = context_store_base.get_retrieval(sheet)
if not sources: if not sources:
return [] return []
return [source.model_dump() for source in sources] return [source.model_dump() for source in sources]
...@@ -48,10 +51,10 @@ def get_retrieval_context(request: ContextRequest) -> List[dict]: ...@@ -48,10 +51,10 @@ def get_retrieval_context(request: ContextRequest) -> List[dict]:
@router.post("/api/context/solution") @router.post("/api/context/solution")
def get_context_solution(request: DraftRequest) -> dict[str, str]: def get_context_solution(request: DraftRequest) -> dict[str, str]:
sheet = context_store.load_sheet(request.draft) sheet = context_store_base.load_sheet(request.draft)
if not sheet: if not sheet:
raise HTTPException(status_code=404, detail="context sheet not found") raise HTTPException(status_code=404, detail="context sheet not found")
return { return {
"solution": context_store.context_store_task.get_solution(sheet), "solution": context_store_task.get_solution(sheet),
} }
...@@ -10,7 +10,11 @@ from fastapi import APIRouter, HTTPException ...@@ -10,7 +10,11 @@ from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from app.deterministic_services import context_store, task_catalog, socratic_oranisator from app.deterministic_services import task_catalog, socratic_oranisator
from app.deterministic_services.context_stores import (
context_store_base,
context_store_task,
)
router = APIRouter() router = APIRouter()
...@@ -128,10 +132,10 @@ def list_socratic_topics() -> SocraticResponse: ...@@ -128,10 +132,10 @@ def list_socratic_topics() -> SocraticResponse:
@router.post("/api/tasks/select", response_model=SelectTaskResponse) @router.post("/api/tasks/select", response_model=SelectTaskResponse)
def select_task(request: SelectTaskRequest) -> SelectTaskResponse: def select_task(request: SelectTaskRequest) -> SelectTaskResponse:
chat_id = context_store.get_chat_id([], draft=request.draft) chat_id = context_store_base.get_chat_id([], draft=request.draft)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
sheet = context_store.context_store_task.init_sheet(chat_id, []) sheet = context_store_task.init_sheet(chat_id, [])
updated = task_catalog.select_task_by_ids( updated = task_catalog.select_task_by_ids(
sheet, sheet,
...@@ -141,7 +145,7 @@ def select_task(request: SelectTaskRequest) -> SelectTaskResponse: ...@@ -141,7 +145,7 @@ def select_task(request: SelectTaskRequest) -> SelectTaskResponse:
if not updated: if not updated:
raise HTTPException(status_code=404, detail="task not found") raise HTTPException(status_code=404, detail="task not found")
context_store.save_sheet(sheet) context_store_base.save_sheet(sheet)
file_id, task_id = task_catalog.get_selected_task_ids(sheet) file_id, task_id = task_catalog.get_selected_task_ids(sheet)
return SelectTaskResponse( return SelectTaskResponse(
status="ok", status="ok",
...@@ -152,17 +156,17 @@ def select_task(request: SelectTaskRequest) -> SelectTaskResponse: ...@@ -152,17 +156,17 @@ def select_task(request: SelectTaskRequest) -> SelectTaskResponse:
@router.post("/api/tasks/select-topic", response_model=SelectTopicResponse) @router.post("/api/tasks/select-topic", response_model=SelectTopicResponse)
def select_topic(request: SelectTopicRequest) -> SelectTopicResponse: def select_topic(request: SelectTopicRequest) -> SelectTopicResponse:
chat_id = context_store.get_chat_id([], draft=request.draft) chat_id = context_store_base.get_chat_id([], draft=request.draft)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
sheet = context_store.context_store_task.init_sheet(chat_id, []) sheet = context_store_task.init_sheet(chat_id, [])
try: try:
topic_key = _set_selected_socratic_topic(sheet, request.topic_key) topic_key = _set_selected_socratic_topic(sheet, request.topic_key)
except ValueError as exc: except ValueError as exc:
raise HTTPException(status_code=404, detail="topic not found") from exc raise HTTPException(status_code=404, detail="topic not found") from exc
context_store.save_sheet(sheet) context_store_base.save_sheet(sheet)
return SelectTopicResponse( return SelectTopicResponse(
status="ok", status="ok",
topic_key=topic_key, topic_key=topic_key,
......
from __future__ import annotations
# Shared/base API
from app.deterministic_services.context_stores.context_store_base import (
add_decision,
append_history_message,
format_history,
get_chat_id,
get_chat_id_value,
get_created_at,
get_decisions,
get_history,
get_history_turns,
get_initialized,
get_retrieval,
get_updated_at,
load_sheet,
save_sheet,
set_chat_id,
set_created_at,
set_decisions,
set_history,
set_initialized,
set_sources,
set_updated_at,
update_history,
update_retrieval_context,
)
# Old/open variant is the default for backwards compatibility.
from app.deterministic_services.context_stores.context_store_open import (
add_LLM_solution,
format_sheet,
get_llm_solutions,
get_task,
init_sheet,
last_LLM_solution,
set_llm_solutions,
)
# Expose variant modules so callers can opt in explicitly.
from app.deterministic_services.context_stores import context_store_task, context_store_open
__all__ = [
# shared/base
"get_chat_id",
"load_sheet",
"save_sheet",
"format_history",
"update_history",
"append_history_message",
"get_history",
"get_history_turns",
"update_retrieval_context",
"get_retrieval",
"add_decision",
"set_chat_id",
"get_chat_id_value",
"set_created_at",
"get_created_at",
"set_updated_at",
"get_updated_at",
"set_history",
"set_decisions",
"get_decisions",
"set_initialized",
"get_initialized",
"set_sources",
# default/open variant
"init_sheet",
"format_sheet",
"set_llm_solutions",
"get_llm_solutions",
"add_LLM_solution",
"last_LLM_solution",
"get_task",
# explicit variants
"context_store_open",
"context_store_task",
]
...@@ -6,13 +6,16 @@ from typing import Any, Callable, List, TypeVar ...@@ -6,13 +6,16 @@ from typing import Any, Callable, List, TypeVar
import app.config as config import app.config as config
from app.deterministic_services import ( from app.deterministic_services import (
context_store,
embedding_provider, embedding_provider,
referenz_decoder, referenz_decoder,
retrieval_store, retrieval_store,
tool_log_context, tool_log_context,
tool_logging, tool_logging,
) )
from app.deterministic_services.context_stores import (
context_store_base,
context_store_open,
)
from app.deterministic_services.context_stores.context_store_base import berlin_now_iso from app.deterministic_services.context_stores.context_store_base import berlin_now_iso
from app.deterministic_services.retrieval_store import Source from app.deterministic_services.retrieval_store import Source
...@@ -167,7 +170,7 @@ def bootstrap_retrieval(sheet: dict, query_text: str, tool_log: list[dict]) -> N ...@@ -167,7 +170,7 @@ def bootstrap_retrieval(sheet: dict, query_text: str, tool_log: list[dict]) -> N
finished_at=finished_at, finished_at=finished_at,
duration_ms=duration_ms, duration_ms=duration_ms,
) )
context_store.update_retrieval_context(sheet, sources) context_store_base.update_retrieval_context(sheet, sources)
# -- # --
# Chat Status # Chat Status
...@@ -186,14 +189,14 @@ def init_chat_state( ...@@ -186,14 +189,14 @@ def init_chat_state(
raise ValueError("last user message required") raise ValueError("last user message required")
last_user = user_messages[-1] last_user = user_messages[-1]
chat_id = context_store.get_chat_id(messages, draft=draft) chat_id = context_store_base.get_chat_id(messages, draft=draft)
new_chat = is_new_chat(messages) new_chat = is_new_chat(messages)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
create_sheet = init_sheet_fn or context_store.init_sheet create_sheet = init_sheet_fn or context_store_open.init_sheet
sheet = create_sheet(chat_id, messages) sheet = create_sheet(chat_id, messages)
context_store.update_history(sheet, messages) context_store_base.update_history(sheet, messages)
return ChatState( return ChatState(
messages=messages, messages=messages,
draft=draft, draft=draft,
...@@ -214,12 +217,12 @@ def finalize_response(state: ChatState, reply: str | None) -> dict: ...@@ -214,12 +217,12 @@ def finalize_response(state: ChatState, reply: str | None) -> dict:
reply = "Dazu steht nichts im Material" reply = "Dazu steht nichts im Material"
else: else:
decoded, _ = referenz_decoder.decode_references( decoded, _ = referenz_decoder.decode_references(
reply, context_store.get_retrieval(state.sheet) reply, context_store_base.get_retrieval(state.sheet)
) )
reply = decoded reply = decoded
context_store.append_history_message(state.sheet, "assistant", reply) context_store_base.append_history_message(state.sheet, "assistant", reply)
context_store.save_sheet(state.sheet) context_store_base.save_sheet(state.sheet)
tool_logging.write_tool_log( tool_logging.write_tool_log(
state.tool_log, state.tool_log,
created_at=state.sheet.get("created_at"), created_at=state.sheet.get("created_at"),
......
from __future__ import annotations from __future__ import annotations
from app.LLM_services import qa_LLM from app.LLM_services import qa_LLM
from app.deterministic_services import context_store from app.deterministic_services.context_stores import (
context_store_base,
context_store_open,
)
from app.deterministic_services.orchestrators import orchestrator_base as base from app.deterministic_services.orchestrators import orchestrator_base as base
# beim ersten Aufruf # beim ersten Aufruf
...@@ -18,9 +21,11 @@ def _on_turn_logic(state: base.ChatState) -> None: ...@@ -18,9 +21,11 @@ def _on_turn_logic(state: base.ChatState) -> None:
def _on_build_reply(state: base.ChatState) -> str | None: def _on_build_reply(state: base.ChatState) -> str | None:
args = { args = {
"question": context_store.get_task(state.sheet), "question": context_store_open.get_task(state.sheet),
"history": context_store.format_history(state.messages), "history": context_store_base.format_history(state.messages),
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(state.sheet)]), "sources": "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(state.sheet)]
),
} }
return base.log_timed_call( return base.log_timed_call(
state.tool_log, state.tool_log,
......
...@@ -2,7 +2,11 @@ from __future__ import annotations ...@@ -2,7 +2,11 @@ from __future__ import annotations
from app.LLM_services import socratic_LLM from app.LLM_services import socratic_LLM
import app.config as config import app.config as config
from app.deterministic_services import context_store, retrieval_store, task_catalog from app.deterministic_services import retrieval_store, task_catalog
from app.deterministic_services.context_stores import (
context_store_base,
context_store_task,
)
from app.deterministic_services.orchestrators import orchestrator_base as base from app.deterministic_services.orchestrators import orchestrator_base as base
...@@ -30,7 +34,7 @@ def _retrieve_context_for_topic(state: base.ChatState, query_text: str) -> int: ...@@ -30,7 +34,7 @@ def _retrieve_context_for_topic(state: base.ChatState, query_text: str) -> int:
pg_url=config.get_postgres_url(), pg_url=config.get_postgres_url(),
parent_refs=refs, parent_refs=refs,
) )
context_store.update_retrieval_context(state.sheet, sources) context_store_base.update_retrieval_context(state.sheet, sources)
return { return {
"parent_refs": refs, "parent_refs": refs,
"source_count": len(sources), "source_count": len(sources),
...@@ -58,13 +62,15 @@ def _on_turn_logic(state: base.ChatState) -> None: ...@@ -58,13 +62,15 @@ def _on_turn_logic(state: base.ChatState) -> None:
def _on_build_reply(state: base.ChatState) -> str | None: def _on_build_reply(state: base.ChatState) -> str | None:
history_turns = context_store.get_history_turns(state.sheet) history_turns = context_store_base.get_history_turns(state.sheet)
parent_refs = task_catalog.get_selected_topic_parent_refs(state.sheet) parent_refs = task_catalog.get_selected_topic_parent_refs(state.sheet)
args = { args = {
"query": state.last_user, "query": state.last_user,
"parent_refs": parent_refs, "parent_refs": parent_refs,
"history": history_turns, "history": history_turns,
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(state.sheet)]), "sources": "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(state.sheet)]
),
} }
return base.log_timed_call( return base.log_timed_call(
state.tool_log, state.tool_log,
...@@ -97,5 +103,5 @@ def run_chat( ...@@ -97,5 +103,5 @@ def run_chat(
on_bootstrap=on_bootstrap, on_bootstrap=on_bootstrap,
on_turn_logic=on_turn_logic, on_turn_logic=on_turn_logic,
on_build_reply=_on_build_reply, on_build_reply=_on_build_reply,
init_sheet_fn=context_store.context_store_task.init_sheet, init_sheet_fn=context_store_task.init_sheet,
) )
...@@ -2,16 +2,16 @@ from __future__ import annotations ...@@ -2,16 +2,16 @@ from __future__ import annotations
from app.LLM_services import task_hint_LLM from app.LLM_services import task_hint_LLM
import app.config as config import app.config as config
from app.deterministic_services import ( from app.deterministic_services import retrieval_store, task_catalog
context_store, from app.deterministic_services.context_stores import (
retrieval_store, context_store_base,
task_catalog, context_store_task,
) )
from app.deterministic_services.orchestrators import orchestrator_base as base from app.deterministic_services.orchestrators import orchestrator_base as base
def _ensure_context_task_fields(state: base.ChatState, query_text: str) -> tuple[str, str] | None: def _ensure_context_task_fields(state: base.ChatState, query_text: str) -> tuple[str, str] | None:
store_new = context_store.context_store_task store_new = context_store_task
has_task = bool(store_new.get_task(state.sheet)) has_task = bool(store_new.get_task(state.sheet))
has_hints = bool(store_new.get_hints(state.sheet)) has_hints = bool(store_new.get_hints(state.sheet))
has_solution = bool(store_new.get_solution(state.sheet)) has_solution = bool(store_new.get_solution(state.sheet))
...@@ -26,12 +26,14 @@ def _ensure_context_task_fields(state: base.ChatState, query_text: str) -> tuple ...@@ -26,12 +26,14 @@ def _ensure_context_task_fields(state: base.ChatState, query_text: str) -> tuple
if was_selected: if was_selected:
return selected[0], selected[1] return selected[0], selected[1]
sources_text = "\n".join([source.to_string() for source in context_store.get_retrieval(state.sheet)]) sources_text = "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(state.sheet)]
)
selection = task_catalog.select_task_for_context( selection = task_catalog.select_task_for_context(
state.sheet, state.sheet,
query_text=query_text, query_text=query_text,
sources_text=sources_text, sources_text=sources_text,
history=context_store.get_history_turns(state.sheet), history=context_store_base.get_history_turns(state.sheet),
) )
if not selection: if not selection:
return None return None
...@@ -64,7 +66,7 @@ def _retrieve_context_for_task(state: base.ChatState, query_text: str) -> int: ...@@ -64,7 +66,7 @@ def _retrieve_context_for_task(state: base.ChatState, query_text: str) -> int:
pg_url=config.get_postgres_url(), pg_url=config.get_postgres_url(),
parent_refs=refs, parent_refs=refs,
) )
context_store.update_retrieval_context(state.sheet, sources) context_store_base.update_retrieval_context(state.sheet, sources)
return { return {
"parent_refs": refs, "parent_refs": refs,
"source_count": len(sources), "source_count": len(sources),
...@@ -87,7 +89,7 @@ def _on_bootstrap(state: base.ChatState, query_text: str) -> None: ...@@ -87,7 +89,7 @@ def _on_bootstrap(state: base.ChatState, query_text: str) -> None:
source_count = _retrieve_context_for_task(state, query_text) source_count = _retrieve_context_for_task(state, query_text)
if source_count > 0: if source_count > 0:
_ensure_context_task_fields(state, query_text) _ensure_context_task_fields(state, query_text)
task_text = context_store.context_store_task.get_task(state.sheet).strip() task_text = context_store_task.get_task(state.sheet).strip()
retrieval_query = query_text retrieval_query = query_text
if task_text: if task_text:
retrieval_query = f"Aufgabe:\n{task_text}\n\n{query_text}" retrieval_query = f"Aufgabe:\n{task_text}\n\n{query_text}"
...@@ -101,15 +103,17 @@ def _on_turn_logic(state: base.ChatState) -> None: ...@@ -101,15 +103,17 @@ def _on_turn_logic(state: base.ChatState) -> None:
# Antwort generieren # Antwort generieren
def _on_build_reply(state: base.ChatState) -> str | None: def _on_build_reply(state: base.ChatState) -> str | None:
store_new = context_store.context_store_task store_new = context_store_task
history_turns = context_store.get_history_turns(state.sheet) history_turns = context_store_base.get_history_turns(state.sheet)
args = { args = {
"query": state.last_user if not state.new_chat else None, "query": state.last_user if not state.new_chat else None,
"task": store_new.get_task(state.sheet), "task": store_new.get_task(state.sheet),
"hints": store_new.get_hints(state.sheet), "hints": store_new.get_hints(state.sheet),
"solution": store_new.get_solution(state.sheet), "solution": store_new.get_solution(state.sheet),
"history": history_turns, "history": history_turns,
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(state.sheet)]), "sources": "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(state.sheet)]
),
} }
return base.log_timed_call( return base.log_timed_call(
state.tool_log, state.tool_log,
...@@ -151,5 +155,5 @@ def run_chat( ...@@ -151,5 +155,5 @@ def run_chat(
on_bootstrap=on_bootstrap, on_bootstrap=on_bootstrap,
on_turn_logic=on_turn_logic, on_turn_logic=on_turn_logic,
on_build_reply=_on_build_reply, on_build_reply=_on_build_reply,
init_sheet_fn=context_store.context_store_task.init_sheet, init_sheet_fn=context_store_task.init_sheet,
) )
from __future__ import annotations from __future__ import annotations
from app.LLM_services import open_hint_LLM, solver_LLM from app.LLM_services import open_hint_LLM, solver_LLM
from app.deterministic_services import context_store from app.deterministic_services.context_stores import (
context_store_base,
context_store_open,
)
from app.deterministic_services.orchestrators import orchestrator_base as base from app.deterministic_services.orchestrators import orchestrator_base as base
# beim ersten Aufruf # beim ersten Aufruf
...@@ -13,25 +16,27 @@ def _on_bootstrap(state: base.ChatState, query_text: str) -> None: ...@@ -13,25 +16,27 @@ def _on_bootstrap(state: base.ChatState, query_text: str) -> None:
def _on_turn_logic(state: base.ChatState) -> None: def _on_turn_logic(state: base.ChatState) -> None:
if state.new_chat: if state.new_chat:
sheet_text = context_store.format_sheet(state.sheet) sheet_text = context_store_open.format_sheet(state.sheet)
llm_solution = base.log_timed_call( llm_solution = base.log_timed_call(
state.tool_log, state.tool_log,
"LLM_Solution", "LLM_Solution",
{"question": state.last_user, "sheet": sheet_text}, {"question": state.last_user, "sheet": sheet_text},
lambda: solver_LLM.solve_question(state.last_user, sheet_text), lambda: solver_LLM.solve_question(state.last_user, sheet_text),
) )
context_store.add_LLM_solution(state.sheet, llm_solution) context_store_open.add_LLM_solution(state.sheet, llm_solution)
# Antwort generieren # Antwort generieren
def _on_build_reply(state: base.ChatState) -> str | None: def _on_build_reply(state: base.ChatState) -> str | None:
history_turns = context_store.get_history_turns(state.sheet) history_turns = context_store_base.get_history_turns(state.sheet)
args = { args = {
"query": state.last_user if not state.new_chat else None, "query": state.last_user if not state.new_chat else None,
"task": context_store.get_task(state.sheet), "task": context_store_open.get_task(state.sheet),
"LLM_solution": context_store.last_LLM_solution(state.sheet), "LLM_solution": context_store_open.last_LLM_solution(state.sheet),
"history": history_turns, "history": history_turns,
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(state.sheet)]), "sources": "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(state.sheet)]
),
} }
return base.log_timed_call( return base.log_timed_call(
state.tool_log, state.tool_log,
......
...@@ -8,7 +8,10 @@ from collections import deque ...@@ -8,7 +8,10 @@ from collections import deque
from threading import Lock from threading import Lock
from typing import Any from typing import Any
from app.deterministic_services import context_store from app.deterministic_services.context_stores import (
context_store_base,
context_store_open,
)
from app.deterministic_services.context_stores.context_store_base import berlin_now_iso from app.deterministic_services.context_stores.context_store_base import berlin_now_iso
_LOCK = Lock() _LOCK = Lock()
...@@ -36,20 +39,20 @@ def archive_chat( ...@@ -36,20 +39,20 @@ def archive_chat(
draft: str | None = None, draft: str | None = None,
orchestrator: str | None = None, orchestrator: str | None = None,
) -> str: ) -> str:
chat_id = context_store.get_chat_id(messages, draft=draft) chat_id = context_store_base.get_chat_id(messages, draft=draft)
sheet = context_store.load_sheet(chat_id) sheet = context_store_base.load_sheet(chat_id)
if not sheet: if not sheet:
sheet = context_store.init_sheet(chat_id, messages) sheet = context_store_open.init_sheet(chat_id, messages)
context_store.update_history(sheet, messages) context_store_base.update_history(sheet, messages)
context_store.save_sheet(sheet) context_store_base.save_sheet(sheet)
record = { record = {
"chat_id": chat_id, "chat_id": chat_id,
"saved_at": berlin_now_iso(), "saved_at": berlin_now_iso(),
"orchestrator": str(orchestrator or "").strip().lower() or None, "orchestrator": str(orchestrator or "").strip().lower() or None,
"history": sheet.get("history", []), "history": sheet.get("history", []),
"context_sheet": context_store.format_sheet(sheet), "context_sheet": context_store_open.format_sheet(sheet),
"retrieval_contexts": sheet.get("retrieval_contexts", []), "retrieval_contexts": sheet.get("retrieval_contexts", []),
"sources": sheet.get("sources", []), "sources": sheet.get("sources", []),
"selected_task": _extract_selected_task(sheet), "selected_task": _extract_selected_task(sheet),
......
...@@ -11,7 +11,7 @@ from urllib.parse import quote ...@@ -11,7 +11,7 @@ from urllib.parse import quote
import yaml import yaml
from app import config from app import config
from app.deterministic_services import context_store from app.deterministic_services.context_stores import context_store_task
from app.deterministic_services.vector_store import parse_markdown_with_frontmatter from app.deterministic_services.vector_store import parse_markdown_with_frontmatter
TASKS_DIR = config.get_task_folder() TASKS_DIR = config.get_task_folder()
...@@ -571,7 +571,7 @@ def set_selected_task( ...@@ -571,7 +571,7 @@ def set_selected_task(
task_entry: dict[str, Any], task_entry: dict[str, Any],
) -> None: ) -> None:
task_text, hints, solution, _ = _build_task_payload(task_file, task_entry) task_text, hints, solution, _ = _build_task_payload(task_file, task_entry)
store_new = context_store.context_store_task store_new = context_store_task
store_new.set_task(sheet, task_text) store_new.set_task(sheet, task_text)
store_new.set_hints(sheet, hints) store_new.set_hints(sheet, hints)
store_new.set_solution(sheet, solution) store_new.set_solution(sheet, solution)
......
...@@ -37,7 +37,7 @@ class FinalizeResponseHistoryTest(unittest.TestCase): ...@@ -37,7 +37,7 @@ class FinalizeResponseHistoryTest(unittest.TestCase):
"app.deterministic_services.orchestrators.orchestrator_base.referenz_decoder.decode_references", "app.deterministic_services.orchestrators.orchestrator_base.referenz_decoder.decode_references",
return_value=("Dekodierte Antwort", []), return_value=("Dekodierte Antwort", []),
), patch( ), patch(
"app.deterministic_services.orchestrators.orchestrator_base.context_store.save_sheet" "app.deterministic_services.orchestrators.orchestrator_base.context_store_base.save_sheet"
) as save_sheet, patch( ) as save_sheet, patch(
"app.deterministic_services.orchestrators.orchestrator_base.tool_logging.write_tool_log" "app.deterministic_services.orchestrators.orchestrator_base.tool_logging.write_tool_log"
): ):
...@@ -57,7 +57,7 @@ class FinalizeResponseHistoryTest(unittest.TestCase): ...@@ -57,7 +57,7 @@ class FinalizeResponseHistoryTest(unittest.TestCase):
state = _build_state() state = _build_state()
with patch( with patch(
"app.deterministic_services.orchestrators.orchestrator_base.context_store.save_sheet" "app.deterministic_services.orchestrators.orchestrator_base.context_store_base.save_sheet"
) as save_sheet, patch( ) as save_sheet, patch(
"app.deterministic_services.orchestrators.orchestrator_base.tool_logging.write_tool_log" "app.deterministic_services.orchestrators.orchestrator_base.tool_logging.write_tool_log"
): ):
......
...@@ -7,7 +7,10 @@ os.environ.setdefault("EMBEDDING_PROVIDER", "sentence-transformer") ...@@ -7,7 +7,10 @@ os.environ.setdefault("EMBEDDING_PROVIDER", "sentence-transformer")
os.environ.setdefault("EMBEDDING_TYPE", "sentence-transformer") os.environ.setdefault("EMBEDDING_TYPE", "sentence-transformer")
from app.LLM_services import open_hint_LLM as hint_LLM from app.LLM_services import open_hint_LLM as hint_LLM
from app.deterministic_services import context_store from app.deterministic_services.context_stores import (
context_store_base,
context_store_open,
)
def _load_sheet_from_path(path: str) -> dict[str, Any]: def _load_sheet_from_path(path: str) -> dict[str, Any]:
...@@ -19,7 +22,7 @@ def _resolve_sheet(args: argparse.Namespace) -> dict[str, Any]: ...@@ -19,7 +22,7 @@ def _resolve_sheet(args: argparse.Namespace) -> dict[str, Any]:
if args.sheet: if args.sheet:
return _load_sheet_from_path(args.sheet) return _load_sheet_from_path(args.sheet)
if args.chat_id: if args.chat_id:
sheet = context_store.load_sheet(args.chat_id) sheet = context_store_base.load_sheet(args.chat_id)
if sheet is None: if sheet is None:
raise FileNotFoundError( raise FileNotFoundError(
f"Kein Context-Sheet gefunden fuer chat_id={args.chat_id}") f"Kein Context-Sheet gefunden fuer chat_id={args.chat_id}")
...@@ -38,13 +41,15 @@ def main() -> None: ...@@ -38,13 +41,15 @@ def main() -> None:
sheet = _resolve_sheet(args) sheet = _resolve_sheet(args)
history_turns = context_store.get_history_turns(sheet) history_turns = context_store_base.get_history_turns(sheet)
args = { args = {
"query": history_turns[-1]["content"] if history_turns else "", "query": history_turns[-1]["content"] if history_turns else "",
"task": context_store.get_task(sheet), "task": context_store_open.get_task(sheet),
"LLM_solution": context_store.last_LLM_solution(sheet), "LLM_solution": context_store_open.last_LLM_solution(sheet),
"history": history_turns, "history": history_turns,
"sources": "\n".join([source.to_string() for source in context_store.get_retrieval(sheet)]), "sources": "\n".join(
[source.to_string() for source in context_store_base.get_retrieval(sheet)]
),
} }
reply = hint_LLM.generate_hint(**args) reply = hint_LLM.generate_hint(**args)
......
...@@ -517,17 +517,22 @@ class TaskApiSocraticTest(unittest.TestCase): ...@@ -517,17 +517,22 @@ class TaskApiSocraticTest(unittest.TestCase):
def test_select_topic_endpoint_returns_selected_key(self) -> None: def test_select_topic_endpoint_returns_selected_key(self) -> None:
sheet: dict[str, object] = {} sheet: dict[str, object] = {}
with patch("app.api.tasks.context_store.get_chat_id", return_value="chat-1"), patch( with patch("app.api.tasks.context_store_base.get_chat_id", return_value="chat-1"), patch(
"app.api.tasks.context_store.load_sheet", "app.api.tasks.context_store_base.load_sheet",
return_value=sheet, return_value=sheet,
), patch( ), patch(
"app.api.tasks.context_store.context_store_task.init_sheet", "app.api.tasks.context_store_task.init_sheet",
return_value=sheet, return_value=sheet,
), patch( ), patch(
"app.api.tasks.task_catalog.select_topic_by_key", "app.api.tasks.socratic_oranisator.get_topic_entry",
return_value=True, return_value={
) as select_mock, patch( "topic_key": "quadratische gleichungen",
"app.api.tasks.context_store.save_sheet" "label": "Quadratische Gleichungen",
"level": "subsubsection",
"refs": [[1, 3, 3, 1]],
},
), patch(
"app.api.tasks.context_store_base.save_sheet"
), patch( ), patch(
"app.api.tasks.task_catalog.get_selected_topic_ids", "app.api.tasks.task_catalog.get_selected_topic_ids",
return_value=(None, "quadratische gleichungen"), return_value=(None, "quadratische gleichungen"),
...@@ -542,7 +547,8 @@ class TaskApiSocraticTest(unittest.TestCase): ...@@ -542,7 +547,8 @@ class TaskApiSocraticTest(unittest.TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["topic_key"], "quadratische gleichungen") self.assertEqual(response.json()["topic_key"], "quadratische gleichungen")
select_mock.assert_called_once_with(sheet, topic_key="quadratische_gleichungen") self.assertEqual(sheet["selected_topic_key"], "quadratische gleichungen")
self.assertEqual(sheet["selected_topic_parent_refs"], [[1, 3, 3, 1]])
class ChatBootstrapSocraticTest(unittest.TestCase): class ChatBootstrapSocraticTest(unittest.TestCase):
...@@ -554,21 +560,29 @@ class ChatBootstrapSocraticTest(unittest.TestCase): ...@@ -554,21 +560,29 @@ class ChatBootstrapSocraticTest(unittest.TestCase):
def test_bootstrap_socratic_loads_sources_and_writes_only_assistant_history(self) -> None: def test_bootstrap_socratic_loads_sources_and_writes_only_assistant_history(self) -> None:
sheet: dict[str, object] = {} sheet: dict[str, object] = {}
with patch("app.api.chat.context_store.get_chat_id", return_value="chat-1"), patch( with patch("app.api.chat.context_store_base.get_chat_id", return_value="chat-1"), patch(
"app.api.chat.context_store.load_sheet", "app.api.chat.context_store_base.load_sheet",
return_value=sheet, return_value=sheet,
), patch( ), patch(
"app.api.chat.context_store.context_store_task.init_sheet", "app.api.chat.context_store_task.init_sheet",
return_value=sheet, return_value=sheet,
), patch( ), patch(
"app.api.chat.context_store.save_sheet" "app.api.chat.context_store_base.save_sheet"
), patch( ), patch(
"app.api.chat.context_store.set_sources" "app.api.chat.context_store_base.set_sources"
), patch( ), patch(
"app.api.chat.context_store.set_initialized" "app.api.chat.context_store_base.set_initialized"
), patch( ), patch(
"app.api.chat.task_catalog.load_topic_map", "app.api.chat.socratic_oranisator.get_topic_entry",
return_value={"quadratische gleichungen": (1, 3, 3, 1)}, return_value={
"topic_key": "quadratische gleichungen",
"label": "Quadratische Gleichungen",
"level": "subsubsection",
"refs": [[1, 3, 3, 1]],
},
), patch(
"app.api.chat.config.get_postgres_url",
return_value="postgresql://localhost/test",
), patch( ), patch(
"app.api.chat.retrieval_store.retrieve_for_parent_refs", "app.api.chat.retrieval_store.retrieve_for_parent_refs",
return_value=[], return_value=[],
......
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