Add analysis feature

This commit is contained in:
Xin Wang
2026-08-07 10:59:47 +08:00
parent 485ad623d4
commit 56a32e81ca
23 changed files with 768 additions and 223 deletions

View File

@@ -9,6 +9,45 @@ from services.conversation_history import ConversationRecorder
class ConversationRecorderTest(unittest.IsolatedAsyncioTestCase):
async def test_completed_conversation_queues_enabled_analysis(self):
conversation = SimpleNamespace(
status="active",
ended_at=None,
message_count=0,
analysis_status="none",
analysis_data={},
analysis_error="",
)
class FakeSession:
async def __aenter__(self):
return self
async def __aexit__(self, *_args):
return None
async def get(self, _model, _session_id):
return conversation
async def commit(self):
return None
plan = {
"enabled": True,
"model_resource_id": "model_001",
"fields": [{"name": "customer_name", "type": "string"}],
}
recorder = ConversationRecorder("conv_test", plan)
recorder._sequence = 1
with patch(
"services.conversation_history.SessionLocal",
return_value=FakeSession(),
):
await recorder._finish(status="completed")
self.assertEqual(conversation.analysis_status, "pending")
self.assertEqual(conversation.analysis_data, {"plan": plan})
async def test_finish_waits_for_database_cleanup_when_cancelled(self):
recorder = ConversationRecorder("conv_test")
started = asyncio.Event()

View File

@@ -0,0 +1,81 @@
from __future__ import annotations
import unittest
from pydantic import ValidationError
from schemas import AnalysisConfig
from services.post_call.analyzer import (
_json_schema,
_parse_json_content,
_validated_result,
)
FIELDS = [
{
"id": "intent",
"name": "customer_intent",
"type": "enum",
"description": "客户意向",
"enum_values": ["high", "low"],
},
{
"id": "follow_up",
"name": "need_follow_up",
"type": "boolean",
"description": "是否需要跟进",
"enum_values": [],
},
]
class AnalysisConfigTest(unittest.TestCase):
def test_enabled_analysis_requires_model_and_fields(self):
with self.assertRaises(ValidationError):
AnalysisConfig(enabled=True)
def test_field_names_must_be_unique(self):
field = {
"id": "one",
"name": "customer_name",
"type": "string",
"description": "客户姓名",
}
with self.assertRaises(ValidationError):
AnalysisConfig(
enabled=True,
model_resource_id="model_001",
fields=[field, {**field, "id": "two"}],
)
class StructuredExtractionTest(unittest.TestCase):
def test_schema_marks_every_field_nullable_and_required(self):
schema = _json_schema(FIELDS)
self.assertEqual(
schema["required"], ["customer_intent", "need_follow_up"]
)
self.assertEqual(
schema["properties"]["customer_intent"]["enum"],
["high", "low", None],
)
def test_invalid_values_are_normalized_to_null(self):
result = _validated_result(
{"customer_intent": "medium", "need_follow_up": "yes"},
FIELDS,
)
self.assertEqual(
result,
{"customer_intent": None, "need_follow_up": None},
)
def test_json_parser_accepts_fenced_model_output(self):
self.assertEqual(
_parse_json_content('```json\n{"need_follow_up": true}\n```'),
{"need_follow_up": True},
)
if __name__ == "__main__":
unittest.main()