feat: add conversational RollCalc assistant
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import base64
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import app as rollcalc_app
|
||||
from conversation_service import ConversationService, InMemoryConversationStore
|
||||
from ollama_nlu import NLUResponse, OllamaUnavailableError, validate_nlu_payload
|
||||
|
||||
|
||||
class StaticNLUClient:
|
||||
def __init__(self, payload):
|
||||
self.payload = payload
|
||||
|
||||
def interpret(
|
||||
self,
|
||||
message,
|
||||
*,
|
||||
has_state=False,
|
||||
expected_fields=(),
|
||||
current_state=None,
|
||||
):
|
||||
if isinstance(self.payload, Exception):
|
||||
raise self.payload
|
||||
return NLUResponse(
|
||||
interpretation=validate_nlu_payload(self.payload),
|
||||
raw_model_json="raw-json",
|
||||
latency_ms=1.0,
|
||||
model="test-model",
|
||||
)
|
||||
|
||||
|
||||
class ConversationApiTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.TemporaryDirectory()
|
||||
self.original_log_file = rollcalc_app.LOG_FILE
|
||||
self.original_service = rollcalc_app.CONVERSATION_SERVICE
|
||||
rollcalc_app.LOG_FILE = os.path.join(self.temp_dir.name, "access.json")
|
||||
rollcalc_app.app.config.update(TESTING=True)
|
||||
self.client = rollcalc_app.app.test_client()
|
||||
credentials = base64.b64encode(b"mtazl:rollcalc").decode("ascii")
|
||||
self.headers = {"Authorization": f"Basic {credentials}"}
|
||||
|
||||
def tearDown(self):
|
||||
rollcalc_app.LOG_FILE = self.original_log_file
|
||||
rollcalc_app.CONVERSATION_SERVICE = self.original_service
|
||||
self.temp_dir.cleanup()
|
||||
|
||||
def configure(self, payload):
|
||||
rollcalc_app.CONVERSATION_SERVICE = ConversationService(
|
||||
StaticNLUClient(payload),
|
||||
store=InMemoryConversationStore(),
|
||||
build_info=rollcalc_app.BUILD_INFO,
|
||||
)
|
||||
|
||||
def create_conversation(self):
|
||||
response = self.client.post("/api/conversations", headers=self.headers)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
return response.get_json()["conversation_id"]
|
||||
|
||||
def test_conversation_success_exposes_authoritative_pdf(self):
|
||||
self.configure({
|
||||
"intent": "new_calculation",
|
||||
"article_number": "180205",
|
||||
"article_name_hint": "Bentofix NSP 4900",
|
||||
"roll_length_m": 65.0,
|
||||
"width_m": None,
|
||||
"core_type": "150mm PVC",
|
||||
"core_diameter_mm": None,
|
||||
"include_roll_weight": False,
|
||||
})
|
||||
conversation_id = self.create_conversation()
|
||||
response = self.client.post(
|
||||
f"/api/conversations/{conversation_id}/messages",
|
||||
json={"message": "Berechnen"},
|
||||
headers=self.headers,
|
||||
)
|
||||
payload = response.get_json()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(payload["status"], "success")
|
||||
self.assertEqual(
|
||||
payload["result"]["provenance"]["calculator"],
|
||||
"roll_calculation.calculate_roll",
|
||||
)
|
||||
|
||||
pdf = self.client.get(payload["pdf"]["url"], headers=self.headers)
|
||||
self.assertEqual(pdf.status_code, 200)
|
||||
self.assertEqual(pdf.mimetype, "application/pdf")
|
||||
self.assertEqual(pdf.data.count(b"/Type /Page "), 1)
|
||||
calculation = payload["result"]["calculation"]
|
||||
self.assertIn(
|
||||
f"{calculation['average_diameter_mm']:.1f} mm".encode("ascii"),
|
||||
pdf.data,
|
||||
)
|
||||
|
||||
def test_ollama_unavailable_returns_controlled_http_error(self):
|
||||
self.configure(OllamaUnavailableError("offline"))
|
||||
conversation_id = self.create_conversation()
|
||||
|
||||
response = self.client.post(
|
||||
f"/api/conversations/{conversation_id}/messages",
|
||||
json={"message": "Berechnen"},
|
||||
headers=self.headers,
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 503)
|
||||
self.assertEqual(response.get_json()["status"], "nlu_error")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user