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