feat(projekt-matching): vLLM guided-json client with extraction and matching prompts
This commit is contained in:
58
projekt-matching/tests/test_llm.py
Normal file
58
projekt-matching/tests/test_llm.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from projektmatch import llm
|
||||
|
||||
|
||||
def fake_post(payloads):
|
||||
"""Return a mock for requests.post yielding chat completions."""
|
||||
responses = []
|
||||
for p in payloads:
|
||||
r = mock.Mock()
|
||||
r.status_code = 200
|
||||
r.json.return_value = {"choices": [{"message": {"content": p}}]}
|
||||
responses.append(r)
|
||||
return mock.Mock(side_effect=responses)
|
||||
|
||||
|
||||
def test_chat_json_strips_think_and_parses():
|
||||
content = "<think>lange Kette</think>{\"a\": 1}"
|
||||
with mock.patch("projektmatch.llm.requests.post", fake_post([content])) as p:
|
||||
out = llm.chat_json("http://v/v1", "m", [{"role": "user", "content": "x"}],
|
||||
{"type": "object"})
|
||||
assert out == {"a": 1}
|
||||
body = p.call_args.kwargs["json"]
|
||||
assert "max_tokens" not in body
|
||||
assert body["guided_json"] == {"type": "object"}
|
||||
|
||||
|
||||
def test_chat_json_fallback_to_response_format():
|
||||
bad = mock.Mock(status_code=400,
|
||||
text="Unknown parameter: 'guided_json'")
|
||||
good = mock.Mock(status_code=200)
|
||||
good.json.return_value = {"choices": [{"message": {"content": "{}"}}]}
|
||||
with mock.patch("projektmatch.llm.requests.post",
|
||||
mock.Mock(side_effect=[bad, good])) as p:
|
||||
assert llm.chat_json("http://v/v1", "m", [], {"type": "object"}) == {}
|
||||
assert "response_format" in p.call_args.kwargs["json"]
|
||||
|
||||
|
||||
def test_match_cv_retries_on_missing_nr_then_raises():
|
||||
reqs = [{"nr": 1, "text": "Python"}, {"nr": 2, "text": "K8s"}]
|
||||
partial = json.dumps({"ratings": [
|
||||
{"nr": 1, "rating": "yes", "reason": "ok"}]})
|
||||
with mock.patch("projektmatch.llm.requests.post",
|
||||
fake_post([partial, partial])):
|
||||
with pytest.raises(llm.LlmError, match="unvollständig"):
|
||||
llm.match_cv("http://v/v1", "m", "CV", reqs)
|
||||
|
||||
|
||||
def test_match_cv_ok():
|
||||
reqs = [{"nr": 1, "text": "Python"}]
|
||||
full = json.dumps({"ratings": [
|
||||
{"nr": 1, "rating": "unknown", "reason": "Teilevidenz"}]})
|
||||
with mock.patch("projektmatch.llm.requests.post", fake_post([full])):
|
||||
out = llm.match_cv("http://v/v1", "m", "CV", reqs)
|
||||
assert out[0]["rating"] == "unknown"
|
||||
Reference in New Issue
Block a user