forked from zhouruizhe/gmTouringMiniApp
106 lines
3.3 KiB
Python
106 lines
3.3 KiB
Python
import asyncio
|
|
|
|
import pytest
|
|
|
|
from app.llm import LLMError, LLMService
|
|
from app.planner import RecommendationService
|
|
from app.schemas import ModelRecommendationSet, PlanRecommendationRequest
|
|
|
|
|
|
def custom_request() -> PlanRecommendationRequest:
|
|
return PlanRecommendationRequest.model_validate(
|
|
{
|
|
"mode": "custom",
|
|
"preferences": {
|
|
"destination": "深圳市光明区",
|
|
"themes": ["朋友"],
|
|
"duration": "half_day",
|
|
"pace": "moderate",
|
|
"interests": ["自然风光"],
|
|
"adults": 2,
|
|
"children": 0,
|
|
"childAges": [],
|
|
"transport": "public_transport",
|
|
"budgetLevel": "standard",
|
|
"extraRequirements": "",
|
|
},
|
|
"durationMinutes": 120,
|
|
"selectedPoiIds": ["poi_a", "poi_b", "poi_c"],
|
|
"candidates": [
|
|
{
|
|
"id": poi_id,
|
|
"name": poi_id,
|
|
"categoryCode": "urban_park",
|
|
"tagCodes": ["walking"],
|
|
"summary": "test",
|
|
"recommendationIndex": 4,
|
|
}
|
|
for poi_id in ["poi_a", "poi_b", "poi_c", "poi_d"]
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
class PartialCustomLLM:
|
|
mode = "mock"
|
|
|
|
async def recommend(self, _request):
|
|
return ModelRecommendationSet.model_validate(
|
|
{
|
|
"recommendations": [
|
|
{"poiId": "poi_c", "reason": "先参观 C", "order": 1},
|
|
{"poiId": "poi_d", "reason": "模型多选了 D", "order": 2},
|
|
]
|
|
}
|
|
)
|
|
|
|
|
|
class UnknownPoiLLM:
|
|
mode = "mock"
|
|
|
|
async def recommend(self, _request):
|
|
return ModelRecommendationSet.model_validate(
|
|
{
|
|
"recommendations": [
|
|
{"poiId": "poi_unknown", "reason": "unknown", "order": 1}
|
|
]
|
|
}
|
|
)
|
|
|
|
|
|
def test_custom_selection_is_owned_by_user_not_model():
|
|
result = asyncio.run(RecommendationService(PartialCustomLLM()).create(custom_request()))
|
|
ids = [item.poi_id for item in result.recommendations]
|
|
assert ids == ["poi_c", "poi_a", "poi_b"]
|
|
assert set(ids) == {"poi_a", "poi_b", "poi_c"}
|
|
|
|
|
|
def test_model_output_with_unknown_id_is_rejected():
|
|
with pytest.raises(LLMError, match="白名单外"):
|
|
asyncio.run(RecommendationService(UnknownPoiLLM()).create(custom_request()))
|
|
|
|
|
|
def test_model_health_states_distinguish_mode_configuration_and_readiness(monkeypatch):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
monkeypatch.delenv("OPENAI_MODEL", raising=False)
|
|
monkeypatch.setenv("LLM_MODE", "mock")
|
|
mock = LLMService()
|
|
assert (mock.mode, mock.configured, mock.ready) == ("mock", False, True)
|
|
|
|
monkeypatch.setenv("LLM_MODE", "real")
|
|
unconfigured = LLMService()
|
|
assert (unconfigured.mode, unconfigured.configured, unconfigured.ready) == (
|
|
"real",
|
|
False,
|
|
False,
|
|
)
|
|
|
|
monkeypatch.setenv("OPENAI_API_KEY", "test-only-placeholder")
|
|
monkeypatch.setenv("OPENAI_MODEL", "test-only-model")
|
|
configured = LLMService()
|
|
assert (configured.mode, configured.configured, configured.ready) == (
|
|
"real",
|
|
True,
|
|
True,
|
|
)
|