Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
math_tutor_dev
public_math_tutor
Commits
2789b332
Commit
2789b332
authored
Jun 18, 2026
by
Kantz
Browse files
adding comments
parent
8c1db631
Changes
13
Hide whitespace changes
Inline
Side-by-side
math-tutor/backend/app/deterministic_services/context_stores/context_store_base.py
View file @
2789b332
# Context store base implementation.
from
__future__
import
annotations
import
hashlib
...
...
@@ -30,6 +32,9 @@ def _resolve_latest_path(chat_id: str) -> str | None:
return
max
(
matches
,
key
=
os
.
path
.
getmtime
)
return
None
# ---
# timekeeping
# ---
def
_mez_now
()
->
str
:
return
datetime
.
now
(
ZoneInfo
(
"Europe/Berlin"
)).
strftime
(
"%Y-%m-%dT%H:%M:%SZ"
)
...
...
@@ -43,6 +48,10 @@ def touch_sheet(sheet: dict[str, Any]) -> None:
_touch
(
sheet
)
# ---
# setup and formatting
# ---
def
get_chat_id
(
messages
:
list
[
dict
],
draft
:
str
|
None
=
None
)
->
str
:
if
draft
:
return
draft
...
...
@@ -130,6 +139,9 @@ def format_sheet_base(sheet: dict[str, Any]) -> str:
parts
.
append
(
f
"INITIALIZED:
\n
{
get_initialized
(
sheet
)
}
"
)
return
"
\n\n
"
.
join
(
parts
)
# ---
# getter and setter
# ---
def
set_chat_id
(
sheet
:
dict
[
str
,
Any
],
value
:
str
)
->
None
:
sheet
[
"chat_id"
]
=
value
...
...
math-tutor/backend/app/deterministic_services/embedding_provider.py
View file @
2789b332
# Embedding
from
__future__
import
annotations
import
time
...
...
@@ -38,6 +40,8 @@ def get_embedder() -> tuple[BaseEmbeddings, bool]:
return
_CACHED_EMBEDDER
,
False
# Warm up the embedder by initializing it and running a dummy query. This can help reduce latency for the first real query.
def
warmup_embedder
()
->
Dict
[
str
,
Any
]:
started
=
time
.
perf_counter
()
embedder
,
cache_hit
=
get_embedder
()
...
...
math-tutor/backend/app/deterministic_services/embeddings.py
View file @
2789b332
# Embeddings-Implementation with Factory-Pattern
from
__future__
import
annotations
import
math
...
...
@@ -10,7 +12,7 @@ from sentence_transformers import SentenceTransformer
# -----------------------------
#
K
onfiguration
s
model
le (optional, aber empfohlen)
#
c
onfiguration
model
# -----------------------------
class
EmbeddingType
(
str
,
Enum
):
...
...
@@ -44,7 +46,7 @@ class EmbeddingConfig(BaseModel):
...,
description
=
"Spezifische Konfiguration"
)
# -----------------------------
#
B
as
isk
lass
e
f
ü
r
E
mbeddings
#
b
as
e c
lass f
o
r
e
mbeddings
# -----------------------------
...
...
@@ -86,7 +88,7 @@ class BaseEmbeddings:
# -----------------------------
#
S
ub
k
lass
en
#
s
ub
c
lass
# -----------------------------
class
OpenAILikeEmbeddings
(
BaseEmbeddings
):
...
...
@@ -182,7 +184,7 @@ class SentenceTransformerEmbeddings(BaseEmbeddings):
# -----------------------------
# Factory:
Erzeugt die richtige Embeddings-Instanz
# Factory:
creats embeddings based on config
# -----------------------------
class
EmbeddingFactory
:
...
...
math-tutor/backend/app/deterministic_services/llm_client.py
View file @
2789b332
# LLM client with support for multiple providers and tool use, including quota tracking and tool call logging.
import
asyncio
import
inspect
import
json
...
...
math-tutor/backend/app/deterministic_services/llm_quota.py
View file @
2789b332
# LLM-Quota-Management: tracks the dayly usage of LLM calls and tokens, enforces limits, and provides usage data for monitoring and billing purposes.
# Limits are set in .env
from
__future__
import
annotations
from
dataclasses
import
dataclass
...
...
math-tutor/backend/app/deterministic_services/referenz_decoder.py
View file @
2789b332
# decodes referneces from the _task_index.json to match sources, tasks and socratic prompts.
# There must be a easyer way to do this.
from
__future__
import
annotations
from
typing
import
Dict
,
Iterable
,
Protocol
,
Set
,
Tuple
...
...
math-tutor/backend/app/deterministic_services/retrieval_store.py
View file @
2789b332
# Mask for Vector Store Retrieval.
# dont know why this is here?
from
__future__
import
annotations
from
typing
import
List
...
...
math-tutor/backend/app/deterministic_services/session_store.py
View file @
2789b332
# Session Store for archiving and retrieving chat sessions.
from
__future__
import
annotations
import
json
...
...
@@ -6,6 +8,7 @@ from collections import deque
from
datetime
import
datetime
from
threading
import
Lock
from
typing
import
Any
from
zoneinfo
import
ZoneInfo
from
app.deterministic_services
import
context_store
...
...
@@ -14,8 +17,8 @@ _LOG_DIR = os.path.join("logs", "chat_sessions")
_LOG_PATH
=
os
.
path
.
join
(
_LOG_DIR
,
"archive.jsonl"
)
def
_
utc
_now
()
->
str
:
return
datetime
.
utc
now
().
strftime
(
"%Y-%m-%dT%H:%M:%SZ"
)
def
_
mez
_now
()
->
str
:
return
datetime
.
now
(
ZoneInfo
(
"Europe/Berlin"
)
).
strftime
(
"%Y-%m-%dT%H:%M:%SZ"
)
def
_extract_selected_task
(
sheet
:
dict
[
str
,
Any
])
->
dict
[
str
,
str
]
|
None
:
...
...
@@ -48,7 +51,7 @@ def archive_chat(
record
=
{
"chat_id"
:
chat_id
,
"saved_at"
:
_
utc
_now
(),
"saved_at"
:
_
mez
_now
(),
"orchestrator"
:
str
(
orchestrator
or
""
).
strip
().
lower
()
or
None
,
"history"
:
sheet
.
get
(
"history"
,
[]),
"context_sheet"
:
context_store
.
format_sheet
(
sheet
),
...
...
math-tutor/backend/app/deterministic_services/socratic_oranisator.py
View file @
2789b332
# Socratic Oranisator for managing initial prompts and topic catalog.
from
__future__
import
annotations
from
functools
import
lru_cache
...
...
math-tutor/backend/app/deterministic_services/task_catalog.py
View file @
2789b332
...
...
@@ -21,6 +21,10 @@ TOPIC_MAP_PATH = TASKS_DIR / "_topic_to_index_map.yaml"
TASK_ASSET_URL_PREFIX
=
"/api/tasks/assets"
ParentRef
=
tuple
[
int
,
int
,
int
,
int
]
# ---
# normalizer and parser
# ---
def
_normalize_parent_ref_key
(
value
:
str
)
->
str
:
return
_normalize_topic_key
(
value
)
...
...
@@ -113,6 +117,10 @@ def _format_parent_ref_label(ref: ParentRef) -> str:
return
f
"Subsection
{
chap
}
.
{
sec
}
.
{
sub
}
"
return
f
"Subsubsection
{
chap
}
.
{
sec
}
.
{
sub
}
.
{
subsub
}
"
# ---
# topic map loading
# ---
def
load_topic_map
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
dict
[
str
,
ParentRef
]:
if
not
path
.
exists
():
...
...
@@ -133,6 +141,9 @@ def load_topic_map(path: Path = TOPIC_MAP_PATH) -> dict[str, ParentRef]:
mapped
[
key
]
=
_normalize_parent_ref
(
parsed
)
return
mapped
# ---
# topic summaries loading
# ---
def
_extract_topic_summary
(
text
:
str
)
->
str
:
body
=
text
.
replace
(
"
\r\n
"
,
"
\n
"
).
strip
()
...
...
@@ -288,6 +299,10 @@ def _format_topic_label(value: str) -> str:
return
cleaned
.
title
()
# ---
# topic catalog
# ---
def
build_topic_catalog
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
list
[
dict
[
str
,
Any
]]:
topic_map
=
load_topic_map
(
path
)
topic_summaries
=
load_topic_summaries
()
...
...
@@ -305,6 +320,10 @@ def build_topic_catalog(path: Path = TOPIC_MAP_PATH) -> list[dict[str, Any]]:
return
response
# ---
# text helpers
# ---
def
_match_score
(
query_text
:
str
,
candidate_text
:
str
)
->
int
:
query_tokens
=
_tokenize
(
query_text
)
if
not
query_tokens
:
...
...
@@ -322,6 +341,10 @@ def _slugify(value: str) -> str:
return
collapsed
.
strip
(
"-"
)
# ---
# task image and block normalization
# ---
def
_normalize_block_images
(
images_raw
:
list
[
dict
[
str
,
str
]])
->
list
[
dict
[
str
,
str
]]:
normalized_images
:
list
[
dict
[
str
,
str
]]
=
[]
for
item
in
images_raw
:
...
...
@@ -369,6 +392,10 @@ def _extract_text_and_images(blocks: Any) -> tuple[list[str], list[dict[str, str
return
text_parts
,
images
# ---
# task yaml normalization
# ---
def
_normalize_yaml_task_entry
(
task_entry
:
Any
,
position
:
int
)
->
dict
[
str
,
Any
]
|
None
:
if
not
isinstance
(
task_entry
,
dict
):
return
None
...
...
@@ -437,6 +464,10 @@ def _normalize_yaml_task_file(content: Any, path: Path) -> dict[str, Any] | None
}
# ---
# task file loading
# ---
def
load_task_files
(
tasks_dir
:
Path
=
TASKS_DIR
)
->
list
[
dict
[
str
,
Any
]]:
if
not
tasks_dir
.
exists
():
return
[]
...
...
@@ -459,6 +490,10 @@ def load_cached_task_files() -> list[dict[str, Any]]:
return
load_task_files
()
# ---
# task lookup
# ---
def
_find_task_file
(
task_files
:
list
[
dict
[
str
,
Any
]],
file_id
:
str
)
->
dict
[
str
,
Any
]
|
None
:
for
task_file
in
task_files
:
if
str
(
task_file
.
get
(
"_file_id"
,
""
))
==
file_id
:
...
...
@@ -502,6 +537,10 @@ def find_task_details(
}
# ---
# selected task and topic state
# ---
def
_normalize_task_images
(
task_entry
:
dict
[
str
,
Any
])
->
list
[
dict
[
str
,
str
]]:
images_raw
=
task_entry
.
get
(
"images"
,
[])
if
not
isinstance
(
images_raw
,
list
):
...
...
@@ -674,6 +713,10 @@ def get_selected_task_parent_refs(sheet: dict[str, Any]) -> list[ParentRef]:
return
sorted
(
refs
)
# ---
# task selection for chat context
# ---
def
select_task_for_context
(
sheet
:
dict
[
str
,
Any
],
query_text
:
str
,
...
...
@@ -737,6 +780,10 @@ def select_task_for_context(
return
best_file
,
best_task
# ---
# public catalogs
# ---
def
build_task_catalog
(
task_files
:
list
[
dict
[
str
,
Any
]]
|
None
=
None
)
->
list
[
dict
[
str
,
Any
]]:
catalog
=
task_files
if
task_files
is
not
None
else
load_cached_task_files
()
catalog
=
sorted
(
...
...
math-tutor/backend/app/deterministic_services/tool_log_context.py
View file @
2789b332
# logger for the tools while the programm is running.
from
__future__
import
annotations
from
contextvars
import
ContextVar
,
Token
...
...
math-tutor/backend/app/deterministic_services/tool_logging.py
View file @
2789b332
# Logger for tool calls, saving them to JSON files in a structured way.
import
json
import
os
from
datetime
import
datetime
...
...
math-tutor/backend/app/deterministic_services/vector_store.py
View file @
2789b332
# Vector Store implementation using PostgreSQL with pgvector extension.
from
__future__
import
annotations
import
hashlib
...
...
@@ -17,7 +19,7 @@ import app.config
embedding_dim
=
app
.
config
.
get_embedding_settings
().
target_dim
# --------------------------------------------------------------------------------------------------------------------
#
Einlesen der
Do
k
ument
e
#
Reading of
Do
c
ument
s
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -124,7 +126,7 @@ def load_docs(base_dir: Path) -> List[DocRecord]:
return
docs
# --------------------------------------------------------------------------------------------------------------------
# Init
der D
atabse
# Init
of the d
atab
a
se
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -173,7 +175,7 @@ def init_db(pg_url: str) -> None:
conn
.
commit
()
# --------------------------------------------------------------------------------------------------------------------
#
Einfügen der Dok
ument
e u
nd
E
mbeddings
#
Insert and update of doc
ument
s a
nd
e
mbeddings
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -274,7 +276,7 @@ def embed_query(embedder: EmbeddingLike, text: str) -> List[float]:
return
embedder
.
embed_query
(
text
)
# --------------------------------------------------------------------------------------------------------------------
# Retrival
der
Do
k
ument
e
# Retri
e
val
of
Do
c
ument
s
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -856,7 +858,7 @@ def retrieve(
return
sorted
(
sources
,
key
=
lambda
source
:
source
.
score
,
reverse
=
True
)
# --------------------------------------------------------------------------------------------------------------------
# Retrival
m
it
P
arent
Referenzen
# Retri
e
val
w
it
h p
arent
refs
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -1058,7 +1060,7 @@ def merge_sources(primary: List[Source], additional: List[Source]) -> List[Sourc
# --------------------------------------------------------------------------------------------------------------------
#
R
etrival
in Sources umwandeln
#
transform r
etri
e
val
results to sources
# --------------------------------------------------------------------------------------------------------------------
...
...
@@ -1140,7 +1142,7 @@ def _retrivla_to_sources(retrievd: Dict[str, List[Retrieved]]) -> List[Source]:
return
sources
# --------------------------------------------------------------------------------------------------------------------
# List
en
f
ü
r
F
ilter
u
ng
# List
s
f
o
r
f
ilter
i
ng
# --------------------------------------------------------------------------------------------------------------------
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment