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
from
__future__
import
annotations
import
hashlib
import
hashlib
...
@@ -30,6 +32,9 @@ def _resolve_latest_path(chat_id: str) -> str | None:
...
@@ -30,6 +32,9 @@ def _resolve_latest_path(chat_id: str) -> str | None:
return
max
(
matches
,
key
=
os
.
path
.
getmtime
)
return
max
(
matches
,
key
=
os
.
path
.
getmtime
)
return
None
return
None
# ---
# timekeeping
# ---
def
_mez_now
()
->
str
:
def
_mez_now
()
->
str
:
return
datetime
.
now
(
ZoneInfo
(
"Europe/Berlin"
)).
strftime
(
"%Y-%m-%dT%H:%M:%SZ"
)
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:
...
@@ -43,6 +48,10 @@ def touch_sheet(sheet: dict[str, Any]) -> None:
_touch
(
sheet
)
_touch
(
sheet
)
# ---
# setup and formatting
# ---
def
get_chat_id
(
messages
:
list
[
dict
],
draft
:
str
|
None
=
None
)
->
str
:
def
get_chat_id
(
messages
:
list
[
dict
],
draft
:
str
|
None
=
None
)
->
str
:
if
draft
:
if
draft
:
return
draft
return
draft
...
@@ -130,6 +139,9 @@ def format_sheet_base(sheet: dict[str, Any]) -> str:
...
@@ -130,6 +139,9 @@ def format_sheet_base(sheet: dict[str, Any]) -> str:
parts
.
append
(
f
"INITIALIZED:
\n
{
get_initialized
(
sheet
)
}
"
)
parts
.
append
(
f
"INITIALIZED:
\n
{
get_initialized
(
sheet
)
}
"
)
return
"
\n\n
"
.
join
(
parts
)
return
"
\n\n
"
.
join
(
parts
)
# ---
# getter and setter
# ---
def
set_chat_id
(
sheet
:
dict
[
str
,
Any
],
value
:
str
)
->
None
:
def
set_chat_id
(
sheet
:
dict
[
str
,
Any
],
value
:
str
)
->
None
:
sheet
[
"chat_id"
]
=
value
sheet
[
"chat_id"
]
=
value
...
...
math-tutor/backend/app/deterministic_services/embedding_provider.py
View file @
2789b332
# Embedding
from
__future__
import
annotations
from
__future__
import
annotations
import
time
import
time
...
@@ -38,6 +40,8 @@ def get_embedder() -> tuple[BaseEmbeddings, bool]:
...
@@ -38,6 +40,8 @@ def get_embedder() -> tuple[BaseEmbeddings, bool]:
return
_CACHED_EMBEDDER
,
False
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
]:
def
warmup_embedder
()
->
Dict
[
str
,
Any
]:
started
=
time
.
perf_counter
()
started
=
time
.
perf_counter
()
embedder
,
cache_hit
=
get_embedder
()
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
from
__future__
import
annotations
import
math
import
math
...
@@ -10,7 +12,7 @@ from sentence_transformers import SentenceTransformer
...
@@ -10,7 +12,7 @@ from sentence_transformers import SentenceTransformer
# -----------------------------
# -----------------------------
#
K
onfiguration
s
model
le (optional, aber empfohlen)
#
c
onfiguration
model
# -----------------------------
# -----------------------------
class
EmbeddingType
(
str
,
Enum
):
class
EmbeddingType
(
str
,
Enum
):
...
@@ -44,7 +46,7 @@ class EmbeddingConfig(BaseModel):
...
@@ -44,7 +46,7 @@ class EmbeddingConfig(BaseModel):
...,
description
=
"Spezifische Konfiguration"
)
...,
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:
...
@@ -86,7 +88,7 @@ class BaseEmbeddings:
# -----------------------------
# -----------------------------
#
S
ub
k
lass
en
#
s
ub
c
lass
# -----------------------------
# -----------------------------
class
OpenAILikeEmbeddings
(
BaseEmbeddings
):
class
OpenAILikeEmbeddings
(
BaseEmbeddings
):
...
@@ -182,7 +184,7 @@ class SentenceTransformerEmbeddings(BaseEmbeddings):
...
@@ -182,7 +184,7 @@ class SentenceTransformerEmbeddings(BaseEmbeddings):
# -----------------------------
# -----------------------------
# Factory:
Erzeugt die richtige Embeddings-Instanz
# Factory:
creats embeddings based on config
# -----------------------------
# -----------------------------
class
EmbeddingFactory
:
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
asyncio
import
inspect
import
inspect
import
json
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
__future__
import
annotations
from
dataclasses
import
dataclass
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
__future__
import
annotations
from
typing
import
Dict
,
Iterable
,
Protocol
,
Set
,
Tuple
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
__future__
import
annotations
from
typing
import
List
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
from
__future__
import
annotations
import
json
import
json
...
@@ -6,6 +8,7 @@ from collections import deque
...
@@ -6,6 +8,7 @@ from collections import deque
from
datetime
import
datetime
from
datetime
import
datetime
from
threading
import
Lock
from
threading
import
Lock
from
typing
import
Any
from
typing
import
Any
from
zoneinfo
import
ZoneInfo
from
app.deterministic_services
import
context_store
from
app.deterministic_services
import
context_store
...
@@ -14,8 +17,8 @@ _LOG_DIR = os.path.join("logs", "chat_sessions")
...
@@ -14,8 +17,8 @@ _LOG_DIR = os.path.join("logs", "chat_sessions")
_LOG_PATH
=
os
.
path
.
join
(
_LOG_DIR
,
"archive.jsonl"
)
_LOG_PATH
=
os
.
path
.
join
(
_LOG_DIR
,
"archive.jsonl"
)
def
_
utc
_now
()
->
str
:
def
_
mez
_now
()
->
str
:
return
datetime
.
utc
now
().
strftime
(
"%Y-%m-%dT%H:%M:%SZ"
)
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
:
def
_extract_selected_task
(
sheet
:
dict
[
str
,
Any
])
->
dict
[
str
,
str
]
|
None
:
...
@@ -48,7 +51,7 @@ def archive_chat(
...
@@ -48,7 +51,7 @@ def archive_chat(
record
=
{
record
=
{
"chat_id"
:
chat_id
,
"chat_id"
:
chat_id
,
"saved_at"
:
_
utc
_now
(),
"saved_at"
:
_
mez
_now
(),
"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
.
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
__future__
import
annotations
from
functools
import
lru_cache
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"
...
@@ -21,6 +21,10 @@ TOPIC_MAP_PATH = TASKS_DIR / "_topic_to_index_map.yaml"
TASK_ASSET_URL_PREFIX
=
"/api/tasks/assets"
TASK_ASSET_URL_PREFIX
=
"/api/tasks/assets"
ParentRef
=
tuple
[
int
,
int
,
int
,
int
]
ParentRef
=
tuple
[
int
,
int
,
int
,
int
]
# ---
# normalizer and parser
# ---
def
_normalize_parent_ref_key
(
value
:
str
)
->
str
:
def
_normalize_parent_ref_key
(
value
:
str
)
->
str
:
return
_normalize_topic_key
(
value
)
return
_normalize_topic_key
(
value
)
...
@@ -113,6 +117,10 @@ def _format_parent_ref_label(ref: ParentRef) -> str:
...
@@ -113,6 +117,10 @@ def _format_parent_ref_label(ref: ParentRef) -> str:
return
f
"Subsection
{
chap
}
.
{
sec
}
.
{
sub
}
"
return
f
"Subsection
{
chap
}
.
{
sec
}
.
{
sub
}
"
return
f
"Subsubsection
{
chap
}
.
{
sec
}
.
{
sub
}
.
{
subsub
}
"
return
f
"Subsubsection
{
chap
}
.
{
sec
}
.
{
sub
}
.
{
subsub
}
"
# ---
# topic map loading
# ---
def
load_topic_map
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
dict
[
str
,
ParentRef
]:
def
load_topic_map
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
dict
[
str
,
ParentRef
]:
if
not
path
.
exists
():
if
not
path
.
exists
():
...
@@ -133,6 +141,9 @@ def load_topic_map(path: Path = TOPIC_MAP_PATH) -> dict[str, ParentRef]:
...
@@ -133,6 +141,9 @@ def load_topic_map(path: Path = TOPIC_MAP_PATH) -> dict[str, ParentRef]:
mapped
[
key
]
=
_normalize_parent_ref
(
parsed
)
mapped
[
key
]
=
_normalize_parent_ref
(
parsed
)
return
mapped
return
mapped
# ---
# topic summaries loading
# ---
def
_extract_topic_summary
(
text
:
str
)
->
str
:
def
_extract_topic_summary
(
text
:
str
)
->
str
:
body
=
text
.
replace
(
"
\r\n
"
,
"
\n
"
).
strip
()
body
=
text
.
replace
(
"
\r\n
"
,
"
\n
"
).
strip
()
...
@@ -288,6 +299,10 @@ def _format_topic_label(value: str) -> str:
...
@@ -288,6 +299,10 @@ def _format_topic_label(value: str) -> str:
return
cleaned
.
title
()
return
cleaned
.
title
()
# ---
# topic catalog
# ---
def
build_topic_catalog
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
list
[
dict
[
str
,
Any
]]:
def
build_topic_catalog
(
path
:
Path
=
TOPIC_MAP_PATH
)
->
list
[
dict
[
str
,
Any
]]:
topic_map
=
load_topic_map
(
path
)
topic_map
=
load_topic_map
(
path
)
topic_summaries
=
load_topic_summaries
()
topic_summaries
=
load_topic_summaries
()
...
@@ -305,6 +320,10 @@ def build_topic_catalog(path: Path = TOPIC_MAP_PATH) -> list[dict[str, Any]]:
...
@@ -305,6 +320,10 @@ def build_topic_catalog(path: Path = TOPIC_MAP_PATH) -> list[dict[str, Any]]:
return
response
return
response
# ---
# text helpers
# ---
def
_match_score
(
query_text
:
str
,
candidate_text
:
str
)
->
int
:
def
_match_score
(
query_text
:
str
,
candidate_text
:
str
)
->
int
:
query_tokens
=
_tokenize
(
query_text
)
query_tokens
=
_tokenize
(
query_text
)
if
not
query_tokens
:
if
not
query_tokens
:
...
@@ -322,6 +341,10 @@ def _slugify(value: str) -> str:
...
@@ -322,6 +341,10 @@ def _slugify(value: str) -> str:
return
collapsed
.
strip
(
"-"
)
return
collapsed
.
strip
(
"-"
)
# ---
# task image and block normalization
# ---
def
_normalize_block_images
(
images_raw
:
list
[
dict
[
str
,
str
]])
->
list
[
dict
[
str
,
str
]]:
def
_normalize_block_images
(
images_raw
:
list
[
dict
[
str
,
str
]])
->
list
[
dict
[
str
,
str
]]:
normalized_images
:
list
[
dict
[
str
,
str
]]
=
[]
normalized_images
:
list
[
dict
[
str
,
str
]]
=
[]
for
item
in
images_raw
:
for
item
in
images_raw
:
...
@@ -369,6 +392,10 @@ def _extract_text_and_images(blocks: Any) -> tuple[list[str], list[dict[str, str
...
@@ -369,6 +392,10 @@ def _extract_text_and_images(blocks: Any) -> tuple[list[str], list[dict[str, str
return
text_parts
,
images
return
text_parts
,
images
# ---
# task yaml normalization
# ---
def
_normalize_yaml_task_entry
(
task_entry
:
Any
,
position
:
int
)
->
dict
[
str
,
Any
]
|
None
:
def
_normalize_yaml_task_entry
(
task_entry
:
Any
,
position
:
int
)
->
dict
[
str
,
Any
]
|
None
:
if
not
isinstance
(
task_entry
,
dict
):
if
not
isinstance
(
task_entry
,
dict
):
return
None
return
None
...
@@ -437,6 +464,10 @@ def _normalize_yaml_task_file(content: Any, path: Path) -> dict[str, Any] | 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
]]:
def
load_task_files
(
tasks_dir
:
Path
=
TASKS_DIR
)
->
list
[
dict
[
str
,
Any
]]:
if
not
tasks_dir
.
exists
():
if
not
tasks_dir
.
exists
():
return
[]
return
[]
...
@@ -459,6 +490,10 @@ def load_cached_task_files() -> list[dict[str, Any]]:
...
@@ -459,6 +490,10 @@ def load_cached_task_files() -> list[dict[str, Any]]:
return
load_task_files
()
return
load_task_files
()
# ---
# task lookup
# ---
def
_find_task_file
(
task_files
:
list
[
dict
[
str
,
Any
]],
file_id
:
str
)
->
dict
[
str
,
Any
]
|
None
:
def
_find_task_file
(
task_files
:
list
[
dict
[
str
,
Any
]],
file_id
:
str
)
->
dict
[
str
,
Any
]
|
None
:
for
task_file
in
task_files
:
for
task_file
in
task_files
:
if
str
(
task_file
.
get
(
"_file_id"
,
""
))
==
file_id
:
if
str
(
task_file
.
get
(
"_file_id"
,
""
))
==
file_id
:
...
@@ -502,6 +537,10 @@ def find_task_details(
...
@@ -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
]]:
def
_normalize_task_images
(
task_entry
:
dict
[
str
,
Any
])
->
list
[
dict
[
str
,
str
]]:
images_raw
=
task_entry
.
get
(
"images"
,
[])
images_raw
=
task_entry
.
get
(
"images"
,
[])
if
not
isinstance
(
images_raw
,
list
):
if
not
isinstance
(
images_raw
,
list
):
...
@@ -674,6 +713,10 @@ def get_selected_task_parent_refs(sheet: dict[str, Any]) -> list[ParentRef]:
...
@@ -674,6 +713,10 @@ def get_selected_task_parent_refs(sheet: dict[str, Any]) -> list[ParentRef]:
return
sorted
(
refs
)
return
sorted
(
refs
)
# ---
# task selection for chat context
# ---
def
select_task_for_context
(
def
select_task_for_context
(
sheet
:
dict
[
str
,
Any
],
sheet
:
dict
[
str
,
Any
],
query_text
:
str
,
query_text
:
str
,
...
@@ -737,6 +780,10 @@ def select_task_for_context(
...
@@ -737,6 +780,10 @@ def select_task_for_context(
return
best_file
,
best_task
return
best_file
,
best_task
# ---
# public catalogs
# ---
def
build_task_catalog
(
task_files
:
list
[
dict
[
str
,
Any
]]
|
None
=
None
)
->
list
[
dict
[
str
,
Any
]]:
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
=
task_files
if
task_files
is
not
None
else
load_cached_task_files
()
catalog
=
sorted
(
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
__future__
import
annotations
from
contextvars
import
ContextVar
,
Token
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
json
import
os
import
os
from
datetime
import
datetime
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
from
__future__
import
annotations
import
hashlib
import
hashlib
...
@@ -17,7 +19,7 @@ import app.config
...
@@ -17,7 +19,7 @@ import app.config
embedding_dim
=
app
.
config
.
get_embedding_settings
().
target_dim
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]:
...
@@ -124,7 +126,7 @@ def load_docs(base_dir: Path) -> List[DocRecord]:
return
docs
return
docs
# --------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------------------
# Init
der D
atabse
# Init
of the d
atab
a
se
# --------------------------------------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------------------------------------
...
@@ -173,7 +175,7 @@ def init_db(pg_url: str) -> None:
...
@@ -173,7 +175,7 @@ def init_db(pg_url: str) -> None:
conn
.
commit
()
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]:
...
@@ -274,7 +276,7 @@ def embed_query(embedder: EmbeddingLike, text: str) -> List[float]:
return
embedder
.
embed_query
(
text
)
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(
...
@@ -856,7 +858,7 @@ def retrieve(
return
sorted
(
sources
,
key
=
lambda
source
:
source
.
score
,
reverse
=
True
)
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
...
@@ -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]:
...
@@ -1140,7 +1142,7 @@ def _retrivla_to_sources(retrievd: Dict[str, List[Retrieved]]) -> List[Source]:
return
sources
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