from __future__ import annotations import uuid from .llm import LLMError, LLMService from .schemas import ( ModelRecommendationSet, PlanRecommendationRequest, PlanningMode, RecommendationItem, RecommendationResponse, ) class RecommendationService: def __init__(self, llm: LLMService) -> None: self.llm = llm async def create( self, request: PlanRecommendationRequest ) -> RecommendationResponse: generated = await self.llm.recommend(request) recommendations = self._canonicalize(request, generated) message = ( "已保留全部自选点位并给出建议顺序;最终路线与时长由客户端核算。" if request.mode == PlanningMode.CUSTOM else "已从当前 POI 白名单给出推荐;最终路线与时长由客户端核算。" ) return RecommendationResponse( request_id=str(uuid.uuid4()), mode=request.mode, assistant_message=message, recommendations=recommendations, generation_mode=self.llm.mode, ) @staticmethod def _canonicalize( request: PlanRecommendationRequest, generated: ModelRecommendationSet, ) -> list[RecommendationItem]: allowed_ids = {candidate.id for candidate in request.candidates} generated_by_id = {} for item in generated.recommendations: if item.poi_id not in allowed_ids: raise LLMError("模型返回了 POI 白名单外的 ID") if item.poi_id in generated_by_id: raise LLMError("模型返回了重复 POI ID") generated_by_id[item.poi_id] = item if request.mode == PlanningMode.CUSTOM: selected_ids = set(request.selected_poi_ids) ordered_ids = [ item.poi_id for item in sorted( generated.recommendations, key=lambda item: item.order or 9, ) if item.poi_id in selected_ids ] # Selection belongs to the user, not the model. Keep all selected IDs # even if a real model omits one, and discard unselected suggestions. ordered_ids.extend( poi_id for poi_id in request.selected_poi_ids if poi_id not in ordered_ids ) else: ordered_ids = [ item.poi_id for item in sorted( generated.recommendations, key=lambda item: item.order or 9, ) ][:8] if not ordered_ids: raise LLMError("模型未返回可用的 POI 推荐") return [ RecommendationItem( poi_id=poi_id, reason=( generated_by_id[poi_id].reason if poi_id in generated_by_id else "这是你选择的点位,已由系统保留。" ), order=index, ) for index, poi_id in enumerate(ordered_ids, start=1) ]