Files
meeting-room/meeting_room/backends/custom/run.py
Hermes 99cb5a499b Initial commit: Meeting Room v0.1.0
Multi-agent discussion framework:
- Custom/AutoGen/CrewAI backends
- Multi-provider API (role→model binding)
- Tools system (files, web, commands)
- Markdown scenarios with YAML frontmatter
- Workspace init (meeting-room init)
- Session save (--save)
2026-05-03 08:07:35 +00:00

172 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Custom backend — round-robin agent discussion with tool support."""
import json
import os
import sys
import yaml
from meeting_room.api_client import chat, chat_stream, init_providers
from meeting_room.scenario_loader import load_scenario
from meeting_room.tools import set_workdir, get_tool_schemas, execute_tool
# ANSI colors
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"
MAX_TOOL_ITERATIONS = 5
def _format_conversation(conversation: list[dict]) -> str:
return "\n\n".join(f"[{msg['name']}]: {msg['content']}" for msg in conversation)
def _run_agent_turn(role_config, problem, conversation, is_first_message):
"""Run one agent turn with tool support. Returns final text response."""
system_msg = {"role": "system", "content": role_config["system_prompt"].strip()}
if is_first_message:
context_msg = {
"role": "user",
"content": (
f"Проблема для обсуждения:\n{problem}\n\n"
"Обсуди её с другими участниками. Выскажи свою позицию. "
"При необходимости используй инструменты."
),
}
else:
context_msg = {
"role": "user",
"content": (
f"Вот что уже обсудили:\n\n{_format_conversation(conversation)}\n\n"
"Продолжи обсуждение. Ответь на аргументы, предложи своё. "
"При необходимости используй инструменты."
),
}
messages = [system_msg, context_msg]
tool_schemas = get_tool_schemas(role_config.get("tools", "none"))
has_tools = len(tool_schemas) > 0
for _ in range(MAX_TOOL_ITERATIONS):
if has_tools:
result = chat(
provider=role_config["provider"],
model=role_config["model"],
messages=messages,
temperature=role_config.get("temperature", 0.7),
tools=tool_schemas,
)
else:
content = chat_stream(
provider=role_config["provider"],
model=role_config["model"],
messages=messages,
temperature=role_config.get("temperature", 0.7),
)
return content
if result["tool_calls"]:
for tc in result["tool_calls"]:
print(f"\n{TOOL_COLOR} -> {tc['name']}({tc['arguments']}){RESET}")
messages.append({
"role": "assistant",
"content": result["content"] or None,
"tool_calls": [
{
"id": tc["id"],
"type": "function",
"function": {"name": tc["name"], "arguments": tc["arguments"]},
}
for tc in result["tool_calls"]
],
})
for tc in result["tool_calls"]:
try:
args = json.loads(tc["arguments"]) if isinstance(tc["arguments"], str) else tc["arguments"]
except json.JSONDecodeError:
args = {}
tool_result = execute_tool(tc["name"], args)
preview = tool_result[:200].replace("\n", " ")
print(f"{TOOL_COLOR} <- {preview}{'...' if len(tool_result) > 200 else ''}{RESET}")
messages.append({
"role": "tool",
"tool_call_id": tc["id"],
"content": tool_result,
})
continue
if result["content"]:
print(result["content"])
return result["content"]
return result.get("content", "")
def run(scenario_path: str, config: dict):
init_providers(config)
scenario = load_scenario(scenario_path)
roles_config = config["roles"]
problem = scenario["problem"]
participants = scenario.get("participants", list(roles_config.keys()))
max_rounds = scenario.get("max_rounds", config["defaults"]["max_rounds"])
set_workdir(config["defaults"].get("workdir", "."))
conversation = []
print(f"\n{BOLD}{'='*60}")
print(f" MEETING ROOM — {scenario['name']}")
print(f"{'='*60}{RESET}\n")
print(f"{BOLD}Problem:{RESET}")
for line in problem.split("\n")[:5]:
print(f" {line}")
if problem.count("\n") > 5:
print(" ...")
print()
print(f"{BOLD}Participants:{RESET}")
for rid in participants:
r = roles_config[rid]
print(f" {r['name']}{r['provider']}/{r['model']} [tools: {r.get('tools', 'none')}]")
print(f"\n{BOLD}Rounds:{RESET} {max_rounds}")
print(f"{DIM}{''*60}{RESET}\n")
for round_num in range(1, max_rounds + 1):
print(f"{BOLD}[Round {round_num}/{max_rounds}]{RESET}\n")
for role_id in participants:
role = roles_config[role_id]
color = COLORS.get(role_id, "")
name = role["name"]
is_first = len(conversation) == 0
print(f"{color}{BOLD} {name}:{RESET}", end=" ")
try:
content = _run_agent_turn(role, problem, conversation, is_first)
except Exception as e:
content = f"[Error: {e}]"
print(content)
conversation.append({"role_id": role_id, "name": name, "content": content})
print()
print(f"{DIM}{''*60}{RESET}\n")
print(f"\n{BOLD}{'='*60}")
print(f" DISCUSSION COMPLETE — {len(conversation)} messages")
print(f"{'='*60}{RESET}\n")
return conversation