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
7a683fea
Commit
7a683fea
authored
Jun 02, 2026
by
Kantz
Browse files
weitere entfernung von subsection elementen
parent
134a9c5c
Changes
13
Hide whitespace changes
Inline
Side-by-side
math-tutor/backend/.env-example
View file @
7a683fea
...
...
@@ -8,7 +8,6 @@ DAILY_LLM_TOKEN_LIMIT="500000"
FRONTEND_URL="http://frontend:3000"
ORCHESTRATOR="task" # "tutor", "task" or "qa"
RETRIEVAL_IMPL="child" # "child" or "subsection"
TASK_FOLDER="tasks"
LLM_PROVIDER="gwdg" # "openai", "gwdg", "mistral", or "ollama"
...
...
math-tutor/backend/app/LLM_services/socratic_LLM.py
View file @
7a683fea
...
...
@@ -6,7 +6,6 @@ from app.deterministic_services import llm_client
ParentRef
=
tuple
[
int
,
int
,
int
,
int
]
SubsectionRef
=
tuple
[
int
,
int
,
int
]
HINT_SYSTEM_PROMPT
=
r
"""
...
...
@@ -45,14 +44,11 @@ def _format_parent_refs(parent_refs: Iterable[ParentRef] | None) -> str:
def
generate_dialog
(
query
:
str
|
None
,
subsection_refs
:
list
[
SubsectionRef
]
|
None
=
None
,
parent_refs
:
list
[
ParentRef
]
|
None
=
None
,
history
:
list
[
dict
]
|
None
=
None
,
sources
:
str
|
None
=
None
,
)
->
str
:
effective_parent_refs
=
parent_refs
if
effective_parent_refs
is
None
and
subsection_refs
is
not
None
:
effective_parent_refs
=
[(
chap
,
sec
,
sub
,
0
)
for
chap
,
sec
,
sub
in
subsection_refs
]
context_parts
=
[
f
"Parent-Referenzen:
\n
{
_format_parent_refs
(
effective_parent_refs
)
}
"
,
...
...
math-tutor/backend/app/config.py
View file @
7a683fea
...
...
@@ -27,12 +27,6 @@ def get_orchestrator() -> str:
return
os
.
getenv
(
"ORCHESTRATOR"
,
"qa"
).
lower
()
def
get_retrieval_impl
()
->
str
:
value
=
os
.
getenv
(
"RETRIEVAL_IMPL"
,
"child"
).
strip
().
lower
()
if
value
in
{
"child"
,
"subsection"
}:
return
value
return
"child"
def
get_task_folder
()
->
Path
:
value
=
os
.
getenv
(
"TASK_FOLDER"
,
"tasks"
).
strip
()
...
...
math-tutor/backend/app/deterministic_services/retrieval_store.py
View file @
7a683fea
...
...
@@ -2,13 +2,10 @@ from __future__ import annotations
from
typing
import
List
import
app.config
as
config
from
app.deterministic_services
import
vector_store
,
vector_store_subsection
from
app.deterministic_services
import
vector_store
from
app.deterministic_services.vector_store
import
EmbeddingLike
,
Source
def
_use_subsection_retrieval
()
->
bool
:
return
config
.
get_retrieval_impl
()
==
"subsection"
def
retrieve
(
...
...
@@ -24,20 +21,6 @@ def retrieve(
expand_links
:
bool
=
True
,
neighbor_expand
:
int
=
0
,
)
->
List
[
Source
]:
if
_use_subsection_retrieval
():
return
vector_store_subsection
.
retrieve
(
pg_url
=
pg_url
,
embedder
=
embedder
,
query
=
query
,
k
=
k
,
chapter_index
=
chapter_index
,
section_index
=
section_index
,
subsection_index
=
subsection_index
,
subsubsection_index
=
subsubsection_index
,
source_type_filter
=
source_type_filter
,
expand_links
=
expand_links
,
neighbor_expand
=
neighbor_expand
,
)
return
vector_store
.
retrieve
(
pg_url
=
pg_url
,
...
...
@@ -58,7 +41,6 @@ def retrieve_with_subsections(
pg_url
:
str
,
embedder
:
EmbeddingLike
,
query
:
str
,
subsection_refs
:
list
[
vector_store
.
SubsectionRef
]
|
None
=
None
,
k
:
int
=
4
,
chapter_index
:
int
|
None
=
None
,
section_index
:
int
|
None
=
None
,
...
...
@@ -68,27 +50,11 @@ def retrieve_with_subsections(
expand_links
:
bool
=
True
,
neighbor_expand
:
int
=
0
,
)
->
List
[
Source
]:
if
_use_subsection_retrieval
():
return
vector_store_subsection
.
retrieve_with_subsections
(
pg_url
=
pg_url
,
embedder
=
embedder
,
query
=
query
,
subsection_refs
=
subsection_refs
,
k
=
k
,
chapter_index
=
chapter_index
,
section_index
=
section_index
,
subsection_index
=
subsection_index
,
subsubsection_index
=
subsubsection_index
,
source_type_filter
=
source_type_filter
,
expand_links
=
expand_links
,
neighbor_expand
=
neighbor_expand
,
)
return
vector_store
.
retrieve_with_subsections
(
pg_url
=
pg_url
,
embedder
=
embedder
,
query
=
query
,
subsection_refs
=
subsection_refs
,
k
=
k
,
chapter_index
=
chapter_index
,
section_index
=
section_index
,
...
...
@@ -100,17 +66,6 @@ def retrieve_with_subsections(
)
def
retrieve_for_subsections
(
pg_url
:
str
,
subsection_refs
:
list
[
vector_store
.
SubsectionRef
]
|
None
=
None
,
)
->
List
[
Source
]:
# Deprecated: subsection-ref retrieval is kept only for legacy task/socratic flows.
return
vector_store
.
load_children_for_subsections
(
pg_url
=
pg_url
,
subsection_refs
=
subsection_refs
,
)
def
retrieve_for_parent_refs
(
pg_url
:
str
,
parent_refs
:
list
[
vector_store
.
ParentRef
]
|
None
=
None
,
...
...
math-tutor/backend/app/deterministic_services/socratic_oranisator.py
View file @
7a683fea
...
...
@@ -75,7 +75,7 @@ def get_topic_entry(topic_key: str, path: Path = PROMPTS_PATH) -> dict[str, Any]
if
not
refs
:
raise
ValueError
(
f
"missing refs for topic '
{
normalized_key
}
'"
)
label_source
=
str
(
item
.
get
(
"label"
)
or
item
.
get
(
"topic"
)
or
item
.
get
(
"subsection"
)
or
normalized_key
)
label_source
=
str
(
item
.
get
(
"label"
)
or
item
.
get
(
"topic"
)
or
normalized_key
)
return
{
"topic_key"
:
normalized_key
,
"label"
:
task_catalog
.
_format_topic_label
(
label_source
)
or
task_catalog
.
_format_topic_label
(
normalized_key
),
...
...
math-tutor/backend/app/deterministic_services/vector_store.py
View file @
7a683fea
...
...
@@ -856,12 +856,10 @@ def retrieve(
return
sorted
(
sources
,
key
=
lambda
source
:
source
.
score
,
reverse
=
True
)
# --------------------------------------------------------------------------------------------------------------------
# Retrival mit
Subsection
Referenzen
# Retrival mit
Parent
Referenzen
# --------------------------------------------------------------------------------------------------------------------
# Deprecated: subsection refs only represent the legacy subsection-centric task/socratic flow.
SubsectionRef
=
Tuple
[
int
,
int
,
int
]
ParentRef
=
Tuple
[
int
,
int
,
int
,
int
]
...
...
@@ -892,58 +890,6 @@ def _parent_ref_scope(ref: ParentRef) -> DominantScope:
return
DominantScope
(
chapter_index
=
chap
,
section_index
=
sec
,
subsection_index
=
sub
,
subsubsection_index
=
subsub
,
level
=
"subsubsection"
)
def
_normalize_subsection_refs
(
subsection_refs
:
Optional
[
List
[
SubsectionRef
]],
)
->
List
[
SubsectionRef
]:
# Deprecated compatibility helper for subsection-only retrieval.
if
not
subsection_refs
:
return
[]
normalized
=
{
(
int
(
chap
),
int
(
sec
),
int
(
sub
))
for
chap
,
sec
,
sub
in
subsection_refs
}
return
sorted
(
normalized
)
def
load_children_for_subsections
(
pg_url
:
str
,
subsection_refs
:
Optional
[
List
[
SubsectionRef
]],
)
->
List
[
Source
]:
# Deprecated compatibility path for subsection-only task/socratic retrieval.
refs
=
_normalize_subsection_refs
(
subsection_refs
)
if
not
refs
:
return
[]
chap_arr
=
[
chap
for
chap
,
_
,
_
in
refs
]
sec_arr
=
[
sec
for
_
,
sec
,
_
in
refs
]
sub_arr
=
[
sub
for
_
,
_
,
sub
in
refs
]
sql
=
"""
SELECT
d.uid, d.doc_type,
d.chapter_index, d.section_index, d.subsection_index, d.subsubsection_index, d.child_index,
d.chapter_title, d.section_title, d.subsection_title, d.subsubsection_title, d.title, d.source_type,
d.path, d.markdown,
1.0 AS score
FROM docs d
JOIN unnest(%(chap_arr)s::int[], %(sec_arr)s::int[], %(sub_arr)s::int[]) AS u(chap, sec, sub)
ON d.chapter_index = u.chap AND d.section_index = u.sec AND d.subsection_index = u.sub
WHERE d.doc_type = 'child'
ORDER BY d.chapter_index, d.section_index, d.subsection_index, d.child_index
"""
with
psycopg
.
connect
(
pg_url
,
row_factory
=
dict_row
)
as
conn
:
with
conn
.
cursor
()
as
cur
:
cur
.
execute
(
sql
,
{
"chap_arr"
:
chap_arr
,
"sec_arr"
:
sec_arr
,
"sub_arr"
:
sub_arr
,
},
)
rows
=
cur
.
fetchall
()
children
=
[
_row_to_retrieved
(
row
)
for
row
in
rows
]
return
_retrivla_to_sources
({
"task_childs"
:
children
})
def
load_sources_for_parent_refs
(
pg_url
:
str
,
...
...
@@ -1111,45 +1057,6 @@ def merge_sources(primary: List[Source], additional: List[Source]) -> List[Sourc
return
merged
def
retrieve_with_subsections
(
pg_url
:
str
,
embedder
:
EmbeddingLike
,
query
:
str
,
subsection_refs
:
Optional
[
List
[
SubsectionRef
]]
=
None
,
k
:
int
=
4
,
chapter_index
:
Optional
[
int
]
=
None
,
section_index
:
Optional
[
int
]
=
None
,
subsection_index
:
Optional
[
int
]
=
None
,
subsubsection_index
:
Optional
[
int
]
=
None
,
source_type_filter
:
Optional
[
List
[
str
]]
=
None
,
expand_links
:
bool
=
True
,
neighbor_expand
:
int
=
0
,
)
->
List
[
Source
]:
# Deprecated compatibility path for subsection-only retrieval composition.
# Oversampling improves recall with ivfflat when additional filters exclude
# close hits. We trim back to k after retrieval.
vector_k
=
max
(
k
*
4
,
k
+
16
)
vector_sources
=
retrieve
(
pg_url
=
pg_url
,
embedder
=
embedder
,
query
=
query
,
k
=
vector_k
,
chapter_index
=
chapter_index
,
section_index
=
section_index
,
subsection_index
=
subsection_index
,
subsubsection_index
=
subsubsection_index
,
source_type_filter
=
source_type_filter
,
expand_links
=
expand_links
,
neighbor_expand
=
neighbor_expand
,
)
vector_sources
=
vector_sources
[:
k
]
subsection_children
=
load_children_for_subsections
(
pg_url
=
pg_url
,
subsection_refs
=
subsection_refs
,
)
return
merge_sources
(
vector_sources
,
subsection_children
)
# --------------------------------------------------------------------------------------------------------------------
# Retrival in Sources umwandeln
# --------------------------------------------------------------------------------------------------------------------
...
...
math-tutor/backend/app/deterministic_services/vector_store_subsection.py
deleted
100644 → 0
View file @
134a9c5c
from
__future__
import
annotations
from
typing
import
Any
,
Dict
,
List
,
Optional
import
psycopg
from
pgvector
import
Vector
from
pgvector.psycopg
import
register_vector
from
psycopg.rows
import
dict_row
from
app.deterministic_services.vector_store
import
(
EmbeddingLike
,
Source
,
SubsectionRef
,
_retrivla_to_sources
,
_row_to_retrieved
,
embed_query
,
load_children_for_subsections
,
merge_sources
,
)
# Deprecated: this module keeps the legacy subsection-centric retrieval path for compatibility.
def
retrieve
(
pg_url
:
str
,
embedder
:
EmbeddingLike
,
query
:
str
,
k
:
int
=
4
,
chapter_index
:
Optional
[
int
]
=
None
,
section_index
:
Optional
[
int
]
=
None
,
subsection_index
:
Optional
[
int
]
=
None
,
subsubsection_index
:
Optional
[
int
]
=
None
,
source_type_filter
:
Optional
[
List
[
str
]]
=
None
,
expand_links
:
bool
=
False
,
neighbor_expand
:
int
=
0
,
)
->
List
[
Source
]:
# Deprecated compatibility path. Parameters kept for drop-in compatibility with child-level retrieve.
_
=
expand_links
_
=
neighbor_expand
_
=
subsubsection_index
qvec
=
Vector
(
embed_query
(
embedder
,
query
))
where
=
[
"doc_type = ANY(%(sub_doc_types)s)"
]
params
:
Dict
[
str
,
Any
]
=
{
"qvec"
:
qvec
,
"k"
:
k
,
"sub_doc_types"
:
[
"subsection"
,
"chapter"
],
}
if
chapter_index
is
not
None
:
where
.
append
(
"chapter_index = %(chapter_index)s"
)
params
[
"chapter_index"
]
=
chapter_index
if
section_index
is
not
None
:
where
.
append
(
"section_index = %(section_index)s"
)
params
[
"section_index"
]
=
section_index
if
subsection_index
is
not
None
:
where
.
append
(
"subsection_index = %(subsection_index)s"
)
params
[
"subsection_index"
]
=
subsection_index
if
source_type_filter
:
where
.
append
(
"source_type = ANY(%(source_type_filter)s)"
)
params
[
"source_type_filter"
]
=
source_type_filter
where_sql
=
" AND "
.
join
(
where
)
sql
=
f
"""
SELECT
uid, doc_type,
chapter_index, section_index, subsection_index, subsubsection_index, child_index,
chapter_title, section_title, subsection_title, subsubsection_title, title, source_type,
path, markdown,
1 - (embedding <=> %(qvec)s) AS score
FROM docs
WHERE
{
where_sql
}
ORDER BY embedding <=> %(qvec)s
LIMIT %(k)s;
"""
with
psycopg
.
connect
(
pg_url
,
row_factory
=
dict_row
)
as
conn
:
register_vector
(
conn
)
with
conn
.
cursor
()
as
cur
:
cur
.
execute
(
sql
,
params
)
rows
=
cur
.
fetchall
()
subsections
=
[
_row_to_retrieved
(
row
,
source_type
=
"subsection"
)
for
row
in
rows
]
return
_retrivla_to_sources
({
"subsections_direct"
:
subsections
})
def
retrieve_with_subsections
(
pg_url
:
str
,
embedder
:
EmbeddingLike
,
query
:
str
,
subsection_refs
:
Optional
[
List
[
SubsectionRef
]]
=
None
,
k
:
int
=
4
,
chapter_index
:
Optional
[
int
]
=
None
,
section_index
:
Optional
[
int
]
=
None
,
subsection_index
:
Optional
[
int
]
=
None
,
subsubsection_index
:
Optional
[
int
]
=
None
,
source_type_filter
:
Optional
[
List
[
str
]]
=
None
,
expand_links
:
bool
=
False
,
neighbor_expand
:
int
=
0
,
)
->
List
[
Source
]:
# Deprecated compatibility path for subsection-only retrieval composition.
vector_k
=
max
(
k
*
4
,
k
+
16
)
vector_sources
=
retrieve
(
pg_url
=
pg_url
,
embedder
=
embedder
,
query
=
query
,
k
=
vector_k
,
chapter_index
=
chapter_index
,
section_index
=
section_index
,
subsection_index
=
subsection_index
,
subsubsection_index
=
subsubsection_index
,
source_type_filter
=
source_type_filter
,
expand_links
=
expand_links
,
neighbor_expand
=
neighbor_expand
,
)
vector_sources
=
vector_sources
[:
k
]
subsection_children
=
load_children_for_subsections
(
pg_url
=
pg_url
,
subsection_refs
=
subsection_refs
,
)
return
merge_sources
(
vector_sources
,
subsection_children
)
math-tutor/backend/scripts/convert_tasks_json_to_yaml.py
View file @
7a683fea
...
...
@@ -40,15 +40,15 @@ def load_json_task(path: Path) -> dict[str, Any]:
return
payload
def
_build_topic_entries
(
subsection
s
:
Any
)
->
list
[
dict
[
str
,
str
]]:
if
not
isinstance
(
subsection
s
,
list
):
def
_build_topic_entries
(
topic
s
:
Any
)
->
list
[
dict
[
str
,
str
]]:
if
not
isinstance
(
topic
s
,
list
):
return
[]
topics
:
list
[
dict
[
str
,
str
]]
=
[]
for
item
in
subsection
s
:
topic
_entrie
s
:
list
[
dict
[
str
,
str
]]
=
[]
for
item
in
topic
s
:
topic_id
=
str
(
item
).
strip
()
if
topic_id
:
topics
.
append
({
"topic-id"
:
topic_id
})
return
topics
topic
_entrie
s
.
append
({
"topic-id"
:
topic_id
})
return
topic
_entrie
s
def
_build_text_block
(
text
:
str
)
->
dict
[
str
,
str
]:
...
...
@@ -105,7 +105,7 @@ def convert_task_payload(payload: dict[str, Any], source_file_id: str, image_pat
"titel"
:
title
,
"slug"
:
slug
,
"description"
:
intro
,
"exercise_topic"
:
_build_topic_entries
(
payload
.
get
(
"
subsection
s"
,
[])),
"exercise_topic"
:
_build_topic_entries
(
payload
.
get
(
"
topic
s"
,
[])),
"tasks"
:
converted_tasks
,
}
...
...
math-tutor/frontend/src/components/Task/SocraticPanel.tsx
View file @
7a683fea
...
...
@@ -4,20 +4,20 @@ import { t } from "../../i18n";
import
{
escapeAsterisksInsideMath
}
from
"
../../utils/mathMarkdown
"
;
type
SocraticPanelProps
=
{
selected
Subsection
Label
?:
string
;
selected
Subsection
Key
?:
string
;
selected
Subsection
Summary
?:
string
;
selected
Topic
Label
?:
string
;
selected
Topic
Key
?:
string
;
selected
Topic
Summary
?:
string
;
onChangeSelection
?:
()
=>
void
;
};
export
default
function
SocraticPanel
({
selected
Subsection
Label
,
selected
Subsection
Key
,
selected
Subsection
Summary
,
selected
Topic
Label
,
selected
Topic
Key
,
selected
Topic
Summary
,
onChangeSelection
,
}:
SocraticPanelProps
)
{
const
contentRef
=
useRef
<
HTMLDivElement
|
null
>
(
null
);
const
summaryMarkdown
=
selected
Subsection
Summary
?.
trim
()
||
""
;
const
summaryMarkdown
=
selected
Topic
Summary
?.
trim
()
||
""
;
const
renderedSummary
=
useMemo
(
()
=>
escapeAsterisksInsideMath
(
summaryMarkdown
),
[
summaryMarkdown
]
...
...
@@ -32,7 +32,7 @@ export default function SocraticPanel({
return
;
}
mathjax
.
typesetPromise
([
contentRef
.
current
]).
catch
(()
=>
undefined
);
},
[
selected
Subsection
Summary
]);
},
[
selected
Topic
Summary
]);
return
(
<
section
className
=
"task-panel"
>
...
...
@@ -40,18 +40,18 @@ export default function SocraticPanel({
<
div
className
=
"task-panel-title"
>
{
t
(
"
orchestratorModeSocraticLabel
"
)
}
</
div
>
{
onChangeSelection
?
(
<
button
type
=
"button"
className
=
"btn task-panel-change-btn"
onClick
=
{
onChangeSelection
}
>
{
t
(
"
change
Subsection
Area
"
)
}
{
t
(
"
change
Topic
Area
"
)
}
</
button
>
)
:
null
}
</
div
>
<
div
className
=
"task-panel-meta"
>
<
div
>
{
selected
Subsection
Label
||
""
}
</
div
>
<
div
>
{
selected
Subsection
Key
?
`
${
t
(
"
subsection
Key
"
)}
:
${
selected
Subsection
Key
}
`
:
""
}
</
div
>
<
div
>
{
selected
Topic
Label
||
""
}
</
div
>
<
div
>
{
selected
Topic
Key
?
`
${
t
(
"
topic
Key
"
)}
:
${
selected
Topic
Key
}
`
:
""
}
</
div
>
</
div
>
<
div
className
=
"task-panel-content socratic-summary-content"
ref
=
{
contentRef
}
>
{
renderedSummary
?
<
ReactMarkdown
>
{
renderedSummary
}
</
ReactMarkdown
>
:
selected
Subsection
Label
||
t
(
"
no
Subsection
Selected
"
)
}
{
renderedSummary
?
<
ReactMarkdown
>
{
renderedSummary
}
</
ReactMarkdown
>
:
selected
Topic
Label
||
t
(
"
no
Topic
Selected
"
)
}
</
div
>
</
section
>
);
...
...
math-tutor/frontend/src/i18n.ts
View file @
7a683fea
...
...
@@ -27,7 +27,7 @@
orchestratorModeTutorDescription
:
"
Help with your own questions
"
,
orchestratorModeTaskDescription
:
"
Help with textbook exercises
"
,
orchestratorModeSocraticDescription
:
"
Socratic guidance based on fixed
subsection
context for textbook exercises
"
,
"
Socratic guidance based on fixed
topic
context for textbook exercises
"
,
directChildren
:
"
Direct children
"
,
taskChildren
:
"
Task sources
"
,
indirectChildren
:
"
Indirect children
"
,
...
...
@@ -49,8 +49,8 @@
hideThinking
:
"
Hide thinking
"
,
noTasksAvailable
:
"
No tasks available
"
,
noTaskSelected
:
"
No task selected.
"
,
no
Subsection
sAvailable
:
"
No
subsection
s available
"
,
no
Subsection
Selected
:
"
No
subsection
selected.
"
,
no
Topic
sAvailable
:
"
No
Topic
s available
"
,
no
Topic
Selected
:
"
No
topic
selected.
"
,
savedChats
:
"
Saved Chats
"
,
saving
:
"
Saving...
"
,
noSavedChatsYet
:
"
No saved chats yet.
"
,
...
...
@@ -74,16 +74,16 @@
"
Saving the canvas failed. Please check backend logs.
"
,
taskSelectionTitle
:
"
Select a Task
"
,
taskSelectionSubtitle
:
"
Choose a task and start a tutor session
"
,
socraticSelectionTitle
:
"
Select a
Subsection
"
,
socraticSelectionSubtitle
:
"
Choose a
subsection
and start a socratic session
"
,
subsection
:
"
Subsection
"
,
socraticSelectionTitle
:
"
Select a
Topic
"
,
socraticSelectionSubtitle
:
"
Choose a
topic
and start a socratic session
"
,
topic
:
"
Topic
"
,
taskFile
:
"
Task Set
"
,
taskId
:
"
Task ID
"
,
subsectionKey
:
"
Subsection
Key
"
,
topicKey
:
"
Topic
Key
"
,
solveWithTutor
:
"
Solve with Tutor
"
,
startSocratic
:
"
Start Socratic
"
,
changeTaskArea
:
"
Change Task Area
"
,
change
Subsection
Area
:
"
Change
Subsection
Area
"
,
change
Topic
Area
:
"
Change
Topic
Area
"
,
previousTask
:
"
Previous Task
"
,
nextTask
:
"
Next Task
"
,
backendChecking
:
"
Checking backend availability...
"
,
...
...
@@ -94,9 +94,9 @@
lastCheckFailed
:
"
Last check: {detail}
"
,
deepLinkInvalidTask
:
"
Invalid task link. Please choose a task manually.
"
,
deepLinkInitFailed
:
"
Task link initialization failed. Please choose a task manually.
"
,
deepLinkInvalid
Subsection
:
"
Invalid
subsection
link. Please choose a
subsection
manually.
"
,
deepLinkInitFailed
Subsection
:
"
Subsection
link initialization failed. Please choose a
subsection
manually.
"
,
deepLinkInvalid
Topic
:
"
Invalid
topic
link. Please choose a
topic
manually.
"
,
deepLinkInitFailed
Topic
:
"
Topic
link initialization failed. Please choose a
topic
manually.
"
,
},
de
:
{
chats
:
"
Chats
"
,
...
...
@@ -127,7 +127,7 @@
orchestratorModeTutorDescription
:
"
Hilfe bei selbst gestellten Fragen
"
,
orchestratorModeTaskDescription
:
"
Hilfe bei Aufgaben aus dem Lehrwerk
"
,
orchestratorModeSocraticDescription
:
"
Sokratische Anleitung auf Basis fester
Subsection
-Kontexte für Aufgaben aus dem Lehrwerk
"
,
"
Sokratische Anleitung auf Basis fester
Topic
-Kontexte für Aufgaben aus dem Lehrwerk
"
,
directChildren
:
"
Direkte Quellen
"
,
taskChildren
:
"
Aufgaben-Quellen
"
,
indirectChildren
:
"
Indirekte Quellen
"
,
...
...
@@ -149,8 +149,8 @@
hideThinking
:
"
Thinking ausblenden
"
,
noTasksAvailable
:
"
Keine Aufgaben verfügbar
"
,
noTaskSelected
:
"
Keine Aufgabe ausgewählt.
"
,
no
Subsection
sAvailable
:
"
Keine Unterabschnitte verfügbar
"
,
no
Subsection
Selected
:
"
Kein Unterabschnitt ausgewählt.
"
,
no
Topic
sAvailable
:
"
Keine Unterabschnitte verfügbar
"
,
no
Topic
Selected
:
"
Kein Unterabschnitt ausgewählt.
"
,
savedChats
:
"
Gespeicherte Chats
"
,
saving
:
"
Speichere...
"
,
noSavedChatsYet
:
"
Noch keine gespeicherten Chats.
"
,
...
...
@@ -180,16 +180,16 @@
"
Da ist wohl das Speichern des Canvas fehlgeschlagen. Gib gerne deinem Dozenten bescheid. In vielen Fällen hilft es die Seite neu zu laden.
"
,
taskSelectionTitle
:
"
Aufgabe auswählen
"
,
taskSelectionSubtitle
:
"
Wähle eine Aufgabe und starte den Tutor-Chat
"
,
socraticSelectionTitle
:
"
Unterabschnitt
auswählen
"
,
socraticSelectionSubtitle
:
"
Wähle ein
en Unterabschnitt
und starte den sokratischen Chat
"
,
subsection
:
"
Unterabschnitt
"
,
socraticSelectionTitle
:
"
Thema
auswählen
"
,
socraticSelectionSubtitle
:
"
Wähle ein
Thema
und starte den sokratischen Chat
"
,
topic
:
"
Thema
"
,
taskFile
:
"
Aufgabenset
"
,
taskId
:
"
Aufgaben-ID
"
,
subsectionKey
:
"
Unterabschnitt
-Schlüssel
"
,
topicKey
:
"
Thema
-Schlüssel
"
,
solveWithTutor
:
"
Mit Tutor lösen
"
,
startSocratic
:
"
Sokratischen Dialog führen
"
,
changeTaskArea
:
"
Aufgabengebiet ändern
"
,
change
SubsectionArea
:
"
Unterabschnitt
ändern
"
,
change
TopicArea
:
"
Thema
ändern
"
,
previousTask
:
"
Vorherige Aufgabe
"
,
nextTask
:
"
Nächste Aufgabe
"
,
backendChecking
:
"
Backend-Verbindung wird geprüft...
"
,
...
...
@@ -202,10 +202,10 @@
"
Ungültiger Aufgaben-Link. Bitte wähle die Aufgabe manuell aus.
"
,
deepLinkInitFailed
:
"
Der Aufgaben-Link konnte nicht initialisiert werden. Bitte wähle die Aufgabe manuell aus.
"
,
deepLinkInvalid
Subsection
:
"
Ungültiger
Unterabschn
it
t
-Link. Bitte wähle d
en Unterabschnitt
manuell aus.
"
,
deepLinkInitFailed
Subsection
:
"
Der
Unterabschn
it
t
-Link konnte nicht initialisiert werden. Bitte wähle d
en Unterabschnitt
manuell aus.
"
,
deepLinkInvalid
Topic
:
"
Ungültiger
Themense
it
e
-Link. Bitte wähle d
as Thema
manuell aus.
"
,
deepLinkInitFailed
Topic
:
"
Der
Themense
it
e
-Link konnte nicht initialisiert werden. Bitte wähle d
as Thema
manuell aus.
"
,
},
}
as
const
;
...
...
math-tutor/frontend/src/pages/ChatPage.tsx
View file @
7a683fea
...
...
@@ -14,7 +14,7 @@ import { selectTask, selectTopic } from "../api/taskApi";
import
{
t
}
from
"
../i18n
"
;
import
{
createSessionId
,
useTutorSession
}
from
"
../state/tutorSession
"
;
import
{
getSelectionRouteForOrchestrator
,
isSocraticOrchestrator
}
from
"
../utils/orchestratorRoutes
"
;
import
{
normalize
Subsection
Key
as
normalizeTopicKey
}
from
"
../utils/
subsection
Key
"
;
import
{
normalize
Topic
Key
as
normalizeTopicKey
}
from
"
../utils/
topic
Key
"
;
import
sumintLogo
from
"
../../SuMINT-Logo.png
"
;
const
initialMessages
:
ChatMessage
[]
=
[];
...
...
@@ -240,7 +240,7 @@ export default function ChatPage() {
const
fileId
=
String
(
searchParams
.
get
(
"
file_id
"
)
||
""
).
trim
();
const
rawTaskId
=
String
(
searchParams
.
get
(
"
task_id
"
)
||
""
).
trim
();
const
rawTopicKey
=
String
(
searchParams
.
get
(
"
topic_key
"
)
||
searchParams
.
get
(
"
subsection_key
"
)
||
""
searchParams
.
get
(
"
topic_key
"
)
||
""
).
trim
();
const
taskId
=
/^
\d{1,2}
$/
.
test
(
rawTaskId
)
&&
rawTaskId
.
length
<
2
...
...
@@ -291,7 +291,7 @@ export default function ChatPage() {
(
deepLinkTarget
.
isSocratic
&&
!
deepLinkTarget
.
hasRequiredTopicParams
)
)
{
setDeepLinkError
(
deepLinkTarget
.
isSocratic
?
t
(
"
deepLinkInvalid
Subsection
"
)
:
t
(
"
deepLinkInvalidTask
"
)
deepLinkTarget
.
isSocratic
?
t
(
"
deepLinkInvalid
Topic
"
)
:
t
(
"
deepLinkInvalidTask
"
)
);
setTaskRef
(
null
);
setTopicRef
(
null
);
...
...
@@ -355,7 +355,7 @@ export default function ChatPage() {
}
catch
(
error
)
{
if
(
!
cancelled
)
{
setDeepLinkError
(
deepLinkTarget
.
isSocratic
?
t
(
"
deepLinkInitFailed
Subsection
"
)
:
t
(
"
deepLinkInitFailed
"
)
deepLinkTarget
.
isSocratic
?
t
(
"
deepLinkInitFailed
Topic
"
)
:
t
(
"
deepLinkInitFailed
"
)
);
setTaskRef
(
null
);
setTopicRef
(
null
);
...
...
@@ -1272,9 +1272,9 @@ export default function ChatPage() {
<
aside
className
=
{
`retrieval-column
${
isTaskModeEnabled
?
"
retrieval-column-task-mode
"
:
""
}
`
}
>
{
selectedOrchestrator
===
"
socratic
"
&&
selectedTopic
?
(
<
SocraticPanel
selected
Subsection
Label
=
{
selectedTopic
.
label
}
selected
Subsection
Key
=
{
selectedTopic
.
topicKey
}
selected
Subsection
Summary
=
{
selectedTopic
.
summary
}
selected
Topic
Label
=
{
selectedTopic
.
label
}
selected
Topic
Key
=
{
selectedTopic
.
topicKey
}
selected
Topic
Summary
=
{
selectedTopic
.
summary
}
onChangeSelection
=
{
handleChangeTaskArea
}
/>
)
:
null
}
...
...
math-tutor/frontend/src/pages/SocraticSelectionPage.tsx
View file @
7a683fea
...
...
@@ -27,9 +27,9 @@ export default function SocraticSelectionPage() {
unlockTask
,
isTasksInitialized
,
}
=
useTutorSession
();
const
subsection
DisplayRef
=
useRef
<
HTMLDivElement
|
null
>
(
null
);
const
topic
DisplayRef
=
useRef
<
HTMLDivElement
|
null
>
(
null
);
const
subsection
MenuOptions
=
useMemo
(
const
topic
MenuOptions
=
useMemo
(
()
=>
topics
.
map
((
option
)
=>
({
value
:
option
.
topic_key
,
...
...
@@ -50,28 +50,28 @@ export default function SocraticSelectionPage() {
},
[
isTasksInitialized
,
navigate
,
selectedOrchestrator
,
unlockTask
]);
useEffect
(()
=>
{
if
(
!
selectedTopic
?.
label
||
!
subsection
DisplayRef
.
current
)
{
if
(
!
selectedTopic
?.
label
||
!
topic
DisplayRef
.
current
)
{
return
;
}
const
mathjax
=
window
.
MathJax
;
if
(
!
mathjax
?.
typesetPromise
)
{
return
;
}
mathjax
.
typesetPromise
([
subsection
DisplayRef
.
current
]).
catch
(()
=>
undefined
);
mathjax
.
typesetPromise
([
topic
DisplayRef
.
current
]).
catch
(()
=>
undefined
);
},
[
selectedTopic
?.
label
]);
useEffect
(()
=>
{
if
(
!
subsection
MenuOptions
.
length
)
{
if
(
!
topic
MenuOptions
.
length
)
{
return
;
}
if
(
selectedTopicRef
&&
subsection
MenuOptions
.
some
((
option
)
=>
option
.
value
===
selectedTopicRef
.
topicKey
)
topic
MenuOptions
.
some
((
option
)
=>
option
.
value
===
selectedTopicRef
.
topicKey
)
)
{
return
;
}
setTopicKey
(
subsection
MenuOptions
[
0
].
value
);
},
[
selectedTopicRef
,
setTopicKey
,
subsection
MenuOptions
]);
setTopicKey
(
topic
MenuOptions
[
0
].
value
);
},
[
selectedTopicRef
,
setTopicKey
,
topic
MenuOptions
]);
const
handleStartSocratic
=
async
()
=>
{
if
(
!
selectedTopicRef
)
{
...
...
@@ -131,24 +131,24 @@ export default function SocraticSelectionPage() {
</
div
>
<
div
className
=
"task-select-controls"
>
<
label
className
=
"task-select-label"
htmlFor
=
"socratic-
subsection
-select"
>
{
t
(
"
subsection
"
)
}
<
label
className
=
"task-select-label"
htmlFor
=
"socratic-
topic
-select"
>
{
t
(
"
topic
"
)
}
</
label
>
<
select
id
=
"socratic-
subsection
-select"
id
=
"socratic-
topic
-select"
className
=
"task-select"
value
=
{
selectedTopicRef
?.
topicKey
||
""
}
onChange
=
{
(
event
)
=>
setTopicKey
(
event
.
target
.
value
)
}
disabled
=
{
!
subsection
MenuOptions
.
length
}
disabled
=
{
!
topic
MenuOptions
.
length
}
>
{
subsection
MenuOptions
.
length
?
(
subsection
MenuOptions
.
map
((
option
)
=>
(
{
topic
MenuOptions
.
length
?
(
topic
MenuOptions
.
map
((
option
)
=>
(
<
option
key
=
{
option
.
value
}
value
=
{
option
.
value
}
>
{
option
.
label
}
</
option
>
))
)
:
(
<
option
value
=
""
>
{
t
(
"
no
Subsection
sAvailable
"
)
}
</
option
>
<
option
value
=
""
>
{
t
(
"
no
Topic
sAvailable
"
)
}
</
option
>
)
}
</
select
>
</
div
>
...
...
math-tutor/frontend/src/utils/
subsection
Key.ts
→
math-tutor/frontend/src/utils/
topic
Key.ts
View file @
7a683fea
const
collapseWhitespace
=
(
value
:
string
):
string
=>
value
.
replace
(
/
\s
+/g
,
"
"
).
trim
();
export
const
normalize
Subsection
Key
=
(
value
:
string
):
string
=>
export
const
normalize
Topic
Key
=
(
value
:
string
):
string
=>
collapseWhitespace
(
String
(
value
).
replace
(
/
[
-_
]
+/g
,
"
"
).
toLowerCase
());
export
const
subsection
KeyToSlug
=
(
value
:
string
):
string
=>
normalize
Subsection
Key
(
value
).
replace
(
/
\s
+/g
,
"
-
"
);
export
const
topic
KeyToSlug
=
(
value
:
string
):
string
=>
normalize
Topic
Key
(
value
).
replace
(
/
\s
+/g
,
"
-
"
);
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