"""Legacy backend — delegates to DiscussionEngine. This module is deprecated. Use meeting_room.engine.DiscussionEngine directly. """ import warnings warnings.warn( "backends.custom.run is deprecated. Use meeting_room.engine.DiscussionEngine instead.", DeprecationWarning, stacklevel=2, ) from meeting_room.api_client import APIClient from meeting_room.config import load_config from meeting_room.engine import DiscussionEngine from meeting_room.events import ( AGENT_ERROR, AGENT_MESSAGE, AGENT_TURN_START, DISCUSSION_END, DISCUSSION_START, ROUND_END, ROUND_START, TOOL_CALL, TOOL_RESULT, EventEmitter, ) from meeting_room.models import ProviderConfig from meeting_room.scenario_loader import load_scenario from meeting_room.tools import ToolRegistry # ── ANSI colors (kept for backward-compatible output) ── COLORS = { "moderator": "\033[96m", "skeptic": "\033[91m", "idea_generator": "\033[92m", "analyst": "\033[93m", } TOOL_COLOR = "\033[90m" RESET = "\033[0m" BOLD = "\033[1m" DIM = "\033[2m" def run(scenario_path: str, config: dict) -> list[dict]: """Run a discussion and return conversation in legacy dict format. This function is deprecated. Use DiscussionEngine directly. Parameters ---------- scenario_path : str Path to the scenario file (.md or .yaml). config : dict Raw config dict (as returned by :func:`load_config`). Returns ------- list[dict] Conversation in legacy format ``[{role_id, name, content}, ...]``. """ from meeting_room.config import load_discussion_config # Build typed config from the raw dict discussion_config = load_discussion_config() # Apply workdir from raw config if present if "defaults" in config and "workdir" in config["defaults"]: discussion_config.defaults.workdir = config["defaults"]["workdir"] # Build engine components providers = { name: ProviderConfig(**vals) for name, vals in config.get("providers", {}).items() } api_client = APIClient(providers=providers) tool_registry = ToolRegistry(workdir=discussion_config.defaults.workdir) event_bus = EventEmitter() # Subscribe event handlers for print output (same format as before) _subscribe_legacy_handlers(event_bus) # Load scenario scenario_data = load_scenario(scenario_path) # Create and run engine engine = DiscussionEngine( config=discussion_config, api_client=api_client, tool_registry=tool_registry, events=event_bus, ) messages = engine.run(scenario_data) # Convert Message objects to legacy dict format conversation = [ {"role_id": m.role_id, "name": m.name, "content": m.content} for m in messages ] return conversation def _subscribe_legacy_handlers(bus: EventEmitter) -> None: """Wire up event handlers that produce the same ANSI output as before.""" def on_discussion_start(event) -> None: data = event.data print(f"\n{BOLD}{'=' * 60}") print(f" MEETING ROOM — {data['name']}") print(f"{'=' * 60}{RESET}\n") problem = data.get("problem", "") if problem: print(f"{BOLD}Problem:{RESET}") for line in problem.split("\n")[:5]: print(f" {line}") if problem.count("\n") > 5: print(" ...") print() participants = data.get("participants", []) if participants: print(f"{BOLD}Participants:{RESET}") # Note: we don't have role details in the event, just names # The engine emits participant names, not full role info. for name in participants: print(f" {name}") print() print(f"{BOLD}Rounds:{RESET} {data['rounds']}") print(f"{DIM}{'─' * 60}{RESET}\n") def on_round_start(event) -> None: data = event.data print(f"{BOLD}[Round {data['round_num']}/{data['total_rounds']}]{RESET}\n") def on_agent_turn_start(event) -> None: data = event.data color = COLORS.get(data["role_id"], "") print(f"{color}{BOLD} {data['name']}:{RESET}", end=" ") def on_agent_message(event) -> None: # Content printed inline after name; just end the line print() def on_tool_call(event) -> None: data = event.data print(f"\n{TOOL_COLOR} -> {data['tool_name']}({data['arguments']}){RESET}") def on_tool_result(event) -> None: data = event.data preview = data["result_preview"] suffix = "..." if len(data.get("full_result", "")) > 200 else "" print(f"{TOOL_COLOR} <- {preview}{suffix}{RESET}") def on_agent_error(event) -> None: data = event.data print(f" [Error: {data['error']}]") def on_round_end(event) -> None: print(f"{DIM}{'─' * 60}{RESET}\n") def on_discussion_end(event) -> None: data = event.data print(f"\n{BOLD}{'=' * 60}") print(f" DISCUSSION COMPLETE — {data['total_messages']} messages") print(f"{'=' * 60}{RESET}\n") bus.on(DISCUSSION_START, on_discussion_start) bus.on(ROUND_START, on_round_start) bus.on(AGENT_TURN_START, on_agent_turn_start) bus.on(AGENT_MESSAGE, on_agent_message) bus.on(TOOL_CALL, on_tool_call) bus.on(TOOL_RESULT, on_tool_result) bus.on(AGENT_ERROR, on_agent_error) bus.on(ROUND_END, on_round_end) bus.on(DISCUSSION_END, on_discussion_end)