From 52e77bb50c14f181225b07ccdeb89322cbb3dd92 Mon Sep 17 00:00:00 2001 From: vitya Date: Sun, 3 May 2026 23:42:39 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20v2=20plan=20=E2=80=94=20remove=20autoge?= =?UTF-8?q?n/crewai,=20add=20tasks=20and=20wiki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove autogen and crewai backends (focus on custom only) - Simplify CLI: remove --backend flag - Remove optional deps from pyproject.toml - Add .tasks/ with 7 tasks for Phase 1 refactor - Add .wiki/concepts/meeting-room-v2.md with architecture plan - Update .wiki/index.md and .tasks/STATUS.md Co-Authored-By: Claude Opus 4.7 --- .tasks/STATUS.md | 72 +++++++++++++++++------ .tasks/v2-p1-api-client.md | 25 ++++++++ .tasks/v2-p1-config-cli.md | 26 ++++++++ .tasks/v2-p1-deps.md | 25 ++++++++ .tasks/v2-p1-engine.md | 25 ++++++++ .tasks/v2-p1-events.md | 24 ++++++++ .tasks/v2-p1-models.md | 22 +++++++ .tasks/v2-p1-tools.md | 26 ++++++++ .wiki/concepts/meeting-room-v2.md | 57 ++++++++++++++++++ .wiki/index.md | 3 +- meeting_room/backends/autogen/__init__.py | 0 meeting_room/backends/autogen/run.py | 60 ------------------- meeting_room/backends/crewai/__init__.py | 0 meeting_room/backends/crewai/run.py | 53 ----------------- meeting_room/cli.py | 24 ++------ pyproject.toml | 4 -- 16 files changed, 289 insertions(+), 157 deletions(-) create mode 100644 .tasks/v2-p1-api-client.md create mode 100644 .tasks/v2-p1-config-cli.md create mode 100644 .tasks/v2-p1-deps.md create mode 100644 .tasks/v2-p1-engine.md create mode 100644 .tasks/v2-p1-events.md create mode 100644 .tasks/v2-p1-models.md create mode 100644 .tasks/v2-p1-tools.md create mode 100644 .wiki/concepts/meeting-room-v2.md delete mode 100644 meeting_room/backends/autogen/__init__.py delete mode 100644 meeting_room/backends/autogen/run.py delete mode 100644 meeting_room/backends/crewai/__init__.py delete mode 100644 meeting_room/backends/crewai/run.py diff --git a/.tasks/STATUS.md b/.tasks/STATUS.md index b23debf..259523d 100644 --- a/.tasks/STATUS.md +++ b/.tasks/STATUS.md @@ -1,25 +1,59 @@ # Task Board _Updated: 2026-05-03_ +## 🔴 v2-p1-models — Pydantic модели данных (ProviderConfig, RoleConfig, Session и др.) +**Status:** active +**Where I stopped:** Задачи созданы, начинаем с моделей +**Next action:** Создать meeting_room/models.py с Pydantic v2 моделями +**Branch:** main + +--- + +## 🟡 v2-p1-events — EventEmitter и типы событий +**Status:** ready +**Next action:** Создать meeting_room/events.py + +--- + +## 🟡 v2-p1-api-client — Рефактор APIClient (класс вместо глобалов) +**Status:** ready +**Next action:** Рефакторить meeting_room/api_client.py + +--- + +## 🟡 v2-p1-tools — Рефактор ToolRegistry (класс, Windows-совместимость) +**Status:** ready +**Next action:** Рефакторить meeting_room/tools.py + +--- + +## 🟡 v2-p1-engine — DiscussionEngine (emit вместо print) +**Status:** ready +**Next action:** Создать meeting_room/engine.py, извлечь из backends/custom/run.py + +--- + +## 🟡 v2-p1-config-cli — config.py + CLI рефактор + deprecation shim +**Status:** ready +**Next action:** Извлечь config.py из cli.py + +--- + +## ⚪ v2-p1-deps — Обновить зависимости (pydantic, pytest, [web]) +**Status:** ready +**Next action:** Обновить pyproject.toml и requirements.txt + +--- + \ No newline at end of file diff --git a/.tasks/v2-p1-api-client.md b/.tasks/v2-p1-api-client.md new file mode 100644 index 0000000..aa167df --- /dev/null +++ b/.tasks/v2-p1-api-client.md @@ -0,0 +1,25 @@ +# v2-p1-api-client + +## Goal +Рефактор api_client.py: класс APIClient вместо модульных глобалов, chat_stream() возвращает текст вместо печати, обратная совместимость через обёртки. + +## Key files +- `meeting_room/api_client.py` — изменить +- `tests/test_api_client.py` — создать + +## Decisions log +- 2026-05-03: Обратная совместимость через module-level функции-делегаты + +## Open questions +- [ ] + +## Completed steps +- [ ] Создать класс APIClient с __init__(providers: dict[str, ProviderConfig]) +- [ ] Перенести chat() и chat_stream() как instance methods +- [ ] chat_stream() возвращает str, не печатает +- [ ] Добавить module-level init_providers(), chat(), chat_stream() как обёртки над default instance +- [ ] Написать тесты с mocked httpx +- [ ] Запустить тесты + +## Notes +Критическое изменение: chat_stream() должен возвращать текст, а не печатать в stdout. Печать — ответственность вызывающего (CLI или WebSocket). \ No newline at end of file diff --git a/.tasks/v2-p1-config-cli.md b/.tasks/v2-p1-config-cli.md new file mode 100644 index 0000000..faa6233 --- /dev/null +++ b/.tasks/v2-p1-config-cli.md @@ -0,0 +1,26 @@ +# v2-p1-config-cli + +## Goal +Извлечь config.py из cli.py, реорганизовать CLI как подписчик событий, добавить deprecation shim в backends/custom/. + +## Key files +- `meeting_room/config.py` — создать (извлечь из cli.py) +- `meeting_room/cli.py` — изменить (тонкая обёртка, подписка на события) +- `meeting_room/backends/custom/run.py` — изменить (deprecation shim) + +## Decisions log +- 2026-05-03: CLI не меняет пользовательский интерфейс — те же флаги, тот же вывод + +## Open questions +- [ ] + +## Completed steps +- [ ] Создать config.py: load_config(), find_workspace_config(), load_discussion_config() +- [ ] Рефакторить cli.py: создать DiscussionEngine, подписаться на события с ANSI-цветами +- [ ] Сохранить init_workspace, save_session, list_scenarios, list_roles в cli.py +- [ ] backends/custom/run.py → deprecation warning + legacy wrapper +- [ ] Написать integration test (CLI с mock API, тот же формат вывода) +- [ ] Запустить все тесты + +## Notes +CLI должен работать идентично текущему. Это главное требование Фазы 1 — бесшовная совместимость. \ No newline at end of file diff --git a/.tasks/v2-p1-deps.md b/.tasks/v2-p1-deps.md new file mode 100644 index 0000000..d8c94c7 --- /dev/null +++ b/.tasks/v2-p1-deps.md @@ -0,0 +1,25 @@ +# v2-p1-deps + +## Goal +Обновить зависимости: добавить pydantic, pytest, web-опциональные. + +## Key files +- `pyproject.toml` — изменить +- `requirements.txt` — изменить + +## Decisions log +- 2026-05-03: pydantic в основных зависимостях, fastapi/uvicorn — опционально + +## Open questions +- [ ] + +## Completed steps +- [ ] Добавить pydantic>=2.0 в dependencies +- [ ] Добавить [web] optional deps: fastapi, uvicorn, websockets +- [ ] Добавить [dev] optional deps: pytest, pytest-mock +- [ ] Обновить requirements.txt +- [ ] Запустить pip install -e ".[dev]" и проверить импорт +- [ ] Закоммитить + +## Notes +FastAPI добавляется в Фазе 2, но опциональная группа [web] создаётся уже сейчас. \ No newline at end of file diff --git a/.tasks/v2-p1-engine.md b/.tasks/v2-p1-engine.md new file mode 100644 index 0000000..7dbd6bb --- /dev/null +++ b/.tasks/v2-p1-engine.md @@ -0,0 +1,25 @@ +# v2-p1-engine + +## Goal +Извлечь DiscussionEngine из backends/custom/run.py. Заменить print() на events.emit(). Добавить inject_message() для Boss/CSO. + +## Key files +- `meeting_room/engine.py` — создать +- `tests/test_engine.py` — создать + +## Decisions log +- 2026-05-03: Движок emit'ит события, потребитель подписывается + +## Open questions +- [ ] + +## Completed steps +- [ ] Создать DiscussionEngine с __init__(config, api_client, tool_registry, event_emitter) +- [ ] Перенести _run_agent_turn() из backends/custom/run.py +- [ ] Заменить все print() на self.events.emit() +- [ ] Добавить inject_message(role_id, content) для Boss/CSO +- [ ] Написать тесты с mock APIClient и ToolRegistry +- [ ] Запустить тесты + +## Notes +Движок полностью отделён от I/O. CLI подписывается на события и печатает, WebSocket подписывается и стримит. \ No newline at end of file diff --git a/.tasks/v2-p1-events.md b/.tasks/v2-p1-events.md new file mode 100644 index 0000000..1bb1806 --- /dev/null +++ b/.tasks/v2-p1-events.md @@ -0,0 +1,24 @@ +# v2-p1-events + +## Goal +Создать EventEmitter и типы событий для событийной архитектуры. Заменяет прямые print() вызовы в движке. + +## Key files +- `meeting_room/events.py` — создать +- `tests/test_events.py` — создать + +## Decisions log +- 2026-05-03: EventEmitter синхронный в Фазе 1, AsyncEventBridge в Фазе 2 + +## Open questions +- [ ] + +## Completed steps +- [ ] Создать events.py с константами событий (DISCUSSION_START, ROUND_START, AGENT_TURN_START, AGENT_MESSAGE, TOOL_CALL, TOOL_RESULT, AGENT_ERROR, ROUND_END, DISCUSSION_END, BOSS_TURN) +- [ ] Создать Event dataclass с type, data, timestamp +- [ ] Создать EventEmitter с методами on(), off(), emit() +- [ ] Написать тесты: подписка, отписка, эмиссия, множественные обработчики +- [ ] Запустить тесты + +## Notes +EventEmitter простой — dict of lists, без asyncio. Асинхронная версия появится в Фазе 2 (ws.py). \ No newline at end of file diff --git a/.tasks/v2-p1-models.md b/.tasks/v2-p1-models.md new file mode 100644 index 0000000..dfc8af2 --- /dev/null +++ b/.tasks/v2-p1-models.md @@ -0,0 +1,22 @@ +# v2-p1-models + +## Goal +Создать Pydantic v2 модели данных для meeting-room: ProviderConfig, RoleConfig, ScenarioConfig, DiscussionConfig, Message, Session. Заменить сырые dict'ы на типизированные модели. + +## Key files +- `meeting_room/models.py` — создать +- `tests/test_models.py` — создать + +## Decisions log +- 2026-05-03: выбрали Pydantic v2 для валидации и сериализации + +## Open questions +- [ ] Стоит ли использовать model_config для разрешения extra fields в config? + +## Completed steps +- [ ] Создать models.py с ProviderConfig, RoleConfig, ScenarioConfig, DiscussionConfig, Message, ToolCallRecord, Session +- [ ] Написать тесты: валидация, дефолты, невалидные данные +- [ ] Запустить тесты, убедиться что проходят + +## Notes +Модели должны быть совместимы с текущим форматом config.yaml — load_config() возвращает dict, который должен без проблем конвертироваться в DiscussionConfig через DiscussionConfig(**config). \ No newline at end of file diff --git a/.tasks/v2-p1-tools.md b/.tasks/v2-p1-tools.md new file mode 100644 index 0000000..a298b04 --- /dev/null +++ b/.tasks/v2-p1-tools.md @@ -0,0 +1,26 @@ +# v2-p1-tools + +## Goal +Рефактор tools.py: класс ToolRegistry вместо модульных глобалов, замена find/grep на чистый Python, описания инструментов на английском. + +## Key files +- `meeting_room/tools.py` — изменить +- `tests/test_tools.py` — создать + +## Decisions log +- 2026-05-03: Чистый Python вместо shell-команд для Windows-совместимости + +## Open questions +- [ ] + +## Completed steps +- [ ] Создать класс ToolRegistry с __init__(workdir: str) +- [ ] Перенести get_tool_schemas() и execute_tool() как instance methods +- [ ] Заменить tool_list_files: subprocess.run(["find",...]) → os.walk() + fnmatch +- [ ] Заменить tool_search_in_files: subprocess.run(["grep",...]) → re.search() по файлам +- [ ] Перевести описания инструментов и ошибки на английский +- [ ] Написать тесты (особенно list_files и search_in_files на Windows) +- [ ] Запустить тесты + +## Notes +Это критическое исправление — текущие find/grep не работают на Windows. \ No newline at end of file diff --git a/.wiki/concepts/meeting-room-v2.md b/.wiki/concepts/meeting-room-v2.md new file mode 100644 index 0000000..ca8a877 --- /dev/null +++ b/.wiki/concepts/meeting-room-v2.md @@ -0,0 +1,57 @@ +--- +title: "Meeting-Room v2: Интерактивный офис" +type: concept +domain: meeting-room +tags: [architecture, refactor, web-ui, artifacts] +updated: 2026-05-03 +--- + +# Meeting-Room v2: Интерактивный офис + +> Эволюция CLI-тула в интерактивный многоагентный офис с Web UI, ролями Boss/CSO и генерацией артефактов. + +## Проблема + +Текущий meeting-room — CLI-утилита с одним режимом: `meeting-room -s scenario.md`. Глобальное состояние, движок смешан с I/O, нет событийной системы, несовместимость с Windows. Не поддерживает интерактивность. + +## Решение — 4 фазы + +### Фаза 1: Рефактор ядра +- Pydantic модели данных (ProviderConfig, RoleConfig, Session) +- EventEmitter + типы событий (вместо print()) +- APIClient класс (вместо модульных глобалов) +- ToolRegistry класс (вместо модульных глобалов, чистый Python вместо find/grep) +- DiscussionEngine (извлечён из backends/custom/run.py, emit вместо print) +- Обратная совместимость CLI + +### Фаза 2: Web-сервер +- FastAPI + WebSocket + SSE +- REST: sessions, start, inject, roles, providers +- Минимальный HTML/CSS/JS UI (без фреймворка) +- `meeting-room serve` команда + +### Фаза 3: Интерактивность +- Boss-роль (пауза + inject через API) +- CSO-инъекция (Claude Code через REST) +- Setup Wizard (выбор участников, моделей, повестки) +- BrainstormEngine (1:1 с CSO) + +### Фаза 4: Артефакты +- LLM-экстрактор (задачи, знания, решения из дискуссии) +- TaskIntegrator → .tasks/ проекта (через projects-meta MCP) +- WikiIntegrator → .wiki/ проекта (через projects-meta MCP) +- ProjectCreator → Gitea API → clone → bootstrap +- SessionArchive (индекс, поиск, архивация) + +## Ключевые принципы + +- Событийная архитектура: движок emit'ит, потребитель подписывается +- Каждая фаза даёт работающий продукт +- CLI работает идентично на протяжении всех фаз +- Артефакты идут в проектные .tasks/ и .wiki/, не в глобальные +- Глобальные .tasks/ — только агрегация + +## See Also + +- [sources/organization-architecture-meeting](../../.wiki/sources/organization-architecture-meeting.md) — оргструктура +- [entities/cso](../../.wiki/entities/cso.md) — роль CSO \ No newline at end of file diff --git a/.wiki/index.md b/.wiki/index.md index 67c60d3..dcbda1c 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -12,7 +12,8 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever ## Concepts - +- [bootstrap-manifest.md](concepts/bootstrap-manifest.md) — skills used to initialize project structure +- [meeting-room-v2.md](concepts/meeting-room-v2.md) — v2 architecture: interactive office with Web UI ## Packages diff --git a/meeting_room/backends/autogen/__init__.py b/meeting_room/backends/autogen/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/meeting_room/backends/autogen/run.py b/meeting_room/backends/autogen/run.py deleted file mode 100644 index 0f60ab2..0000000 --- a/meeting_room/backends/autogen/run.py +++ /dev/null @@ -1,60 +0,0 @@ -"""AutoGen backend — uses microsoft/autogen for multi-agent conversation.""" - - -def check_installed(): - try: - import autogen # noqa - return True - except ImportError: - return False - - -def run(scenario_path: str, config: dict): - if not check_installed(): - print("AutoGen is not installed. Run:\n pip install meeting-room[autogen]") - return [] - - import yaml - import autogen - from meeting_room.api_client import init_providers - from meeting_room.scenario_loader import load_scenario - - scenario = load_scenario(scenario_path) - init_providers(config) - roles = config["roles"] - participants = scenario.get("participants", list(roles.keys())) - - llm_configs = {} - for prov_name, prov_cfg in config["providers"].items(): - llm_configs[prov_name] = { - "base_url": prov_cfg["base_url"], - "api_key": prov_cfg["api_key"], - } - - agents = [] - for role_id in participants: - role = roles[role_id] - prov = role["provider"] - llm_cfg = dict(llm_configs[prov]) - llm_cfg["model"] = role["model"] - - agent = autogen.ConversableAgent( - name=role["name"], - system_message=role["system_prompt"].strip(), - llm_config={"config_list": [llm_cfg], "temperature": role.get("temperature", 0.7)}, - human_input_mode="NEVER", - ) - agents.append(agent) - - groupchat = autogen.GroupChat( - agents=agents, - messages=[], - max_round=scenario.get("max_rounds", config["defaults"]["max_rounds"]), - ) - manager = autogen.GroupChatManager( - groupchat=groupchat, - llm_config={"config_list": [llm_configs[participants[0]]]}, - ) - - agents[0].initiate_chat(manager, message=scenario["problem"]) - return groupchat.messages diff --git a/meeting_room/backends/crewai/__init__.py b/meeting_room/backends/crewai/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/meeting_room/backends/crewai/run.py b/meeting_room/backends/crewai/run.py deleted file mode 100644 index 91f00d3..0000000 --- a/meeting_room/backends/crewai/run.py +++ /dev/null @@ -1,53 +0,0 @@ -"""CrewAI backend — uses crewai for task-oriented multi-agent discussion.""" - - -def check_installed(): - try: - import crewai # noqa - return True - except ImportError: - return False - - -def run(scenario_path: str, config: dict): - if not check_installed(): - print("CrewAI is not installed. Run:\n pip install meeting-room[crewai]") - return [] - - import yaml - from crewai import Agent, Task, Crew, Process - from meeting_room.scenario_loader import load_scenario - - scenario = load_scenario(scenario_path) - roles = config["roles"] - participants = scenario.get("participants", list(roles.keys())) - - agents = [] - for role_id in participants: - role = roles[role_id] - agent = Agent( - role=role["name"], - goal=role["system_prompt"].split(".")[0] + ".", - backstory=role["system_prompt"].strip(), - llm=f"{role['provider']}/{role['model']}", - verbose=True, - ) - agents.append(agent) - - discuss_task = Task( - description=f"Discuss this problem as a group:\n\n{scenario['problem']}", - agent=agents[0], - expected_output="Final solution with arguments from each participant", - ) - - crew = Crew( - agents=agents, - tasks=[discuss_task], - process=Process.sequential, - verbose=True, - ) - - result = crew.kickoff() - print(f"\n{'='*60}\nCREWAI RESULT:\n{'='*60}\n") - print(result) - return str(result) diff --git a/meeting_room/cli.py b/meeting_room/cli.py index 56b17e4..4b549f3 100644 --- a/meeting_room/cli.py +++ b/meeting_room/cli.py @@ -223,7 +223,7 @@ def find_workspace_config() -> str | None: return None -def run_discussion(backend: str, scenario: str, config: dict, save: bool = False): +def run_discussion(scenario: str, config: dict, save: bool = False): scenario_path = scenario if os.path.isabs(scenario) else os.path.join(os.getcwd(), scenario) if not os.path.exists(scenario_path): # Try bundled scenarios @@ -232,18 +232,8 @@ def run_discussion(backend: str, scenario: str, config: dict, save: bool = False print(f"Scenario not found: {scenario}") sys.exit(1) - if backend == "custom": - from meeting_room.backends.custom.run import run - conversation = run(scenario_path, config) - elif backend == "autogen": - from meeting_room.backends.autogen.run import run - conversation = run(scenario_path, config) - elif backend == "crewai": - from meeting_room.backends.crewai.run import run - conversation = run(scenario_path, config) - else: - print(f"Unknown backend: {backend}") - sys.exit(1) + from meeting_room.backends.custom.run import run + conversation = run(scenario_path, config) if save and conversation: # Determine sessions dir @@ -263,12 +253,6 @@ def main(): parser = argparse.ArgumentParser( description="Meeting Room — Multi-Agent Discussion Framework", ) - parser.add_argument( - "-b", "--backend", - choices=["custom", "autogen", "crewai"], - default="custom", - help="Discussion framework (default: custom)", - ) parser.add_argument( "-s", "--scenario", default="example-saas.md", @@ -336,7 +320,7 @@ def main(): if args.workdir: config["defaults"]["workdir"] = args.workdir - run_discussion(args.backend, args.scenario, config, save=args.save) + run_discussion(args.scenario, config, save=args.save) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index db7f2ad..c00118e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,6 @@ dependencies = [ "pyyaml>=6.0", ] -[project.optional-dependencies] -autogen = ["autogen-agentchat", "autogen-ext"] -crewai = ["crewai"] -all = ["autogen-agentchat", "autogen-ext", "crewai"] [project.scripts] meeting-room = "meeting_room.cli:main"