""" Base feature class for all negoT8 negotiation features. Every feature module must subclass BaseFeature and implement: - get_context() → pre-fetches tool data; returns string injected into negotiators - format_resolution() → returns a Telegram-ready Markdown string """ from abc import ABC, abstractmethod class BaseFeature(ABC): @abstractmethod async def get_context( self, preferences_a: dict, preferences_b: dict, user_a_id: int = None, user_b_id: int = None, ) -> str: """ Pre-fetch tool results (Tavily, Calculator, etc.) and return a formatted string to inject into the negotiator as domain context. Return "" if no external context is needed. user_a_id / user_b_id are optional — only SchedulingFeature uses them to query Google Calendar when the user hasn't specified times. """ ... @abstractmethod def format_resolution( self, resolution: dict, preferences_a: dict, preferences_b: dict ) -> str: """ Transform the raw resolution dict into a nice Telegram Markdown string. """ ... # ─── Feature dispatcher ─────────────────────────────────────────────────────── def get_feature(feature_type: str) -> BaseFeature: """Return the correct BaseFeature subclass for the given feature type.""" from features.scheduling import SchedulingFeature from features.expenses import ExpensesFeature from features.freelance import FreelanceFeature from features.roommate import RoommateFeature from features.trip import TripFeature from features.marketplace import MarketplaceFeature from features.collaborative import CollaborativeFeature from features.conflict import ConflictFeature from features.generic import GenericFeature mapping = { "scheduling": SchedulingFeature, "expenses": ExpensesFeature, "freelance": FreelanceFeature, "roommate": RoommateFeature, "trip": TripFeature, "marketplace": MarketplaceFeature, "collaborative": CollaborativeFeature, "conflict": ConflictFeature, "generic": GenericFeature, } cls = mapping.get(feature_type, GenericFeature) return cls()