feat(config,compose): dispatcher agent, api frontend, merge prompt and morning seed
This commit is contained in:
@@ -82,5 +82,6 @@ PORT_MESSAGES=62990
|
|||||||
PORT_MCP=62991
|
PORT_MCP=62991
|
||||||
PORT_ADMIN=62992
|
PORT_ADMIN=62992
|
||||||
PORT_MARKDOWN=62993
|
PORT_MARKDOWN=62993
|
||||||
|
PORT_API=62994
|
||||||
# Публичный URL за reverse proxy (см. ./caddy). Пусто для localhost/dev
|
# Публичный URL за reverse proxy (см. ./caddy). Пусто для localhost/dev
|
||||||
PUBLIC_BASE_URL=
|
PUBLIC_BASE_URL=
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import date
|
from datetime import date, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from beaver_gateway.agents.base import BaseAgent, ExposedMcp
|
from beaver_gateway.agents.base import BaseAgent, ExposedMcp
|
||||||
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeOptions
|
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeOptions
|
||||||
from beaver_gateway.agents.raycast import RaycastAgent, RemoteTool, UserPreferences
|
from beaver_gateway.agents.raycast import RaycastAgent, RemoteTool, UserPreferences
|
||||||
|
from beaver_gateway.core.conversations import ConversationTexts, SeedContext
|
||||||
from beaver_gateway.core.prompt import assemble
|
from beaver_gateway.core.prompt import assemble
|
||||||
from beaver_gateway.core.registry import Gateway
|
from beaver_gateway.core.registry import Gateway
|
||||||
from beaver_gateway.core.turn_record import TurnRecord, slugify
|
from beaver_gateway.core.turn_record import TurnRecord, slugify
|
||||||
from beaver_gateway.frontends.admin import AdminFrontend
|
from beaver_gateway.frontends.admin import AdminFrontend
|
||||||
from beaver_gateway.frontends.anthropic import AnthropicMessagesFrontend
|
from beaver_gateway.frontends.anthropic import AnthropicMessagesFrontend
|
||||||
|
from beaver_gateway.frontends.api import ApiFrontend
|
||||||
from beaver_gateway.frontends.base import Frontend
|
from beaver_gateway.frontends.base import Frontend
|
||||||
from beaver_gateway.frontends.markdown import MarkdownFrontend
|
from beaver_gateway.frontends.markdown import MarkdownFrontend
|
||||||
from beaver_gateway.frontends.mcp_server import McpServerFrontend
|
from beaver_gateway.frontends.mcp_server import McpServerFrontend
|
||||||
@@ -60,8 +62,31 @@ DEEP_PROMPT = (
|
|||||||
)
|
)
|
||||||
QUICK_PROMPT = assemble((*VOICE, PROFILE, overlay("quick", "быстрый")))
|
QUICK_PROMPT = assemble((*VOICE, PROFILE, overlay("quick", "быстрый")))
|
||||||
|
|
||||||
|
# Диспетчер: тот же голос, окружение по kind (§3.12), оверлей по роли.
|
||||||
|
# Форк ветки (слив) думает как дистиллятор в окружении джоба.
|
||||||
|
_DISPATCHER_BASE = (*VOICE, PROFILE, CORRECTIONS, VAULT_MAP)
|
||||||
|
DISPATCHER_PROMPTS = {
|
||||||
|
"master": (
|
||||||
|
*_DISPATCHER_BASE,
|
||||||
|
environment("мастер"),
|
||||||
|
overlay("dispatcher", "диспетчер"),
|
||||||
|
),
|
||||||
|
"branch": (*_DISPATCHER_BASE, environment("ветка"), overlay("branch", "ветка")),
|
||||||
|
"fork": (
|
||||||
|
*_DISPATCHER_BASE,
|
||||||
|
environment("джоб"),
|
||||||
|
overlay("distiller", "дистиллятор"),
|
||||||
|
),
|
||||||
|
"job": (
|
||||||
|
*_DISPATCHER_BASE,
|
||||||
|
environment("джоб"),
|
||||||
|
overlay("distiller", "дистиллятор"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
# §4.3: наборы скиллов = папки = плагины. Глубокие грузят общие + vault.
|
# §4.3: наборы скиллов = папки = плагины. Глубокие грузят общие + vault.
|
||||||
DEEP_SKILLS = (SKILLS / "общие", SKILLS / "vault")
|
DEEP_SKILLS = (SKILLS / "общие", SKILLS / "vault")
|
||||||
|
DISPATCHER_SKILLS = (SKILLS / "общие", SKILLS / "диспетчер", SKILLS / "vault")
|
||||||
|
|
||||||
|
|
||||||
def chat_log_path(record: TurnRecord, vault: Path) -> Path:
|
def chat_log_path(record: TurnRecord, vault: Path) -> Path:
|
||||||
@@ -128,6 +153,24 @@ UserPrefsRu = lambda: UserPreferences( # noqa: E731
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dispatcher(name: str, model: str, effort: str | None = None) -> ClaudeAgent:
|
||||||
|
return ClaudeAgent(
|
||||||
|
name=name,
|
||||||
|
model=model,
|
||||||
|
cwd=VAULT,
|
||||||
|
prompt_sources=DISPATCHER_PROMPTS["master"],
|
||||||
|
prompt_sources_by_kind=DISPATCHER_PROMPTS,
|
||||||
|
skill_sets=DISPATCHER_SKILLS,
|
||||||
|
gateway_tools=("read_conversation", "spawn", "say", "schedule"),
|
||||||
|
options=ClaudeOptions(
|
||||||
|
effort=effort,
|
||||||
|
# §3.7: у диспетчера AskUserQuestion остаётся, планов и ноутбуков нет.
|
||||||
|
disallowed_tools=("ExitPlanMode", "EnterPlanMode", "NotebookEdit"),
|
||||||
|
),
|
||||||
|
expose_mcps=CLAUDE_MCPS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def deep(name: str, model: str, effort: str | None = None) -> ClaudeAgent:
|
def deep(name: str, model: str, effort: str | None = None) -> ClaudeAgent:
|
||||||
return ClaudeAgent(
|
return ClaudeAgent(
|
||||||
name=name,
|
name=name,
|
||||||
@@ -168,6 +211,8 @@ def raycast(name: str, model: str, reasoning_effort: str | None = None) -> Rayca
|
|||||||
|
|
||||||
|
|
||||||
agents: list[BaseAgent] = [
|
agents: list[BaseAgent] = [
|
||||||
|
# §12.D4: диспетчер на medium; high - по руке или в ветке.
|
||||||
|
dispatcher("beaver-dispatcher", "claude-opus-5", effort="medium"),
|
||||||
deep("beaver-opus-high", "claude-opus-5", effort="high"),
|
deep("beaver-opus-high", "claude-opus-5", effort="high"),
|
||||||
raycast("beaver-gemini-pro-high", "google-gemini-3.1-pro", reasoning_effort="high"),
|
raycast("beaver-gemini-pro-high", "google-gemini-3.1-pro", reasoning_effort="high"),
|
||||||
deep("beaver-fable-high", "claude-fable-5", effort="high"),
|
deep("beaver-fable-high", "claude-fable-5", effort="high"),
|
||||||
@@ -196,6 +241,7 @@ frontends: list[Frontend] = [
|
|||||||
),
|
),
|
||||||
McpServerFrontend(host="0.0.0.0", port=62991, public_base_url=_public("/mcp")),
|
McpServerFrontend(host="0.0.0.0", port=62991, public_base_url=_public("/mcp")),
|
||||||
AdminFrontend(host="0.0.0.0", port=62992, public_base_url=_public("/admin")),
|
AdminFrontend(host="0.0.0.0", port=62992, public_base_url=_public("/admin")),
|
||||||
|
ApiFrontend(host="0.0.0.0", port=62994, public_base_url=_public("/api")),
|
||||||
MarkdownFrontend(
|
MarkdownFrontend(
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=62993,
|
port=62993,
|
||||||
@@ -208,4 +254,22 @@ frontends: list[Frontend] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
gateway = Gateway(agents=agents, mcps=mcps, frontends=frontends)
|
def _read(path: Path) -> str | None:
|
||||||
|
return path.read_text(encoding="utf-8").strip() if path.exists() else None
|
||||||
|
|
||||||
|
|
||||||
|
def seed_body(ctx: SeedContext) -> str | None:
|
||||||
|
"""§8.2: утренний сид - вчерашний хендаут из `дни/`; остальные сиды - gateway."""
|
||||||
|
if ctx.seed != "morning":
|
||||||
|
return None
|
||||||
|
yesterday = date.today() - timedelta(days=1)
|
||||||
|
handout = _read(BEAVER / "дни" / f"{yesterday:%Y-%m-%d}.md")
|
||||||
|
return handout or "Хендаут за вчера не приехал."
|
||||||
|
|
||||||
|
|
||||||
|
texts = ConversationTexts(
|
||||||
|
merge_prompt=_read(GRANULES / "слив.md") or ConversationTexts().merge_prompt,
|
||||||
|
seed=seed_body,
|
||||||
|
)
|
||||||
|
|
||||||
|
gateway = Gateway(agents=agents, mcps=mcps, frontends=frontends, texts=texts)
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ services:
|
|||||||
- "${PORT_MCP:-62991}:62991" # mcp
|
- "${PORT_MCP:-62991}:62991" # mcp
|
||||||
- "${PORT_ADMIN:-62992}:62992" # admin
|
- "${PORT_ADMIN:-62992}:62992" # admin
|
||||||
- "${PORT_MARKDOWN:-62993}:62993" # obsidian companion
|
- "${PORT_MARKDOWN:-62993}:62993" # obsidian companion
|
||||||
|
- "${PORT_API:-62994}:62994" # conversations api + sse
|
||||||
volumes:
|
volumes:
|
||||||
- ./config.py:/config/config.py:ro
|
- ./config.py:/config/config.py:ro
|
||||||
- ./config.json:/config/config.json:ro
|
- ./config.json:/config/config.json:ro
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ dependencies = [
|
|||||||
{ name = "greenlet" },
|
{ name = "greenlet" },
|
||||||
{ name = "itsdangerous" },
|
{ name = "itsdangerous" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2" },
|
||||||
|
{ name = "psutil" },
|
||||||
{ name = "psycopg", extra = ["binary"], marker = "extra == 'extra-12-beaver-agent-local'" },
|
{ name = "psycopg", extra = ["binary"], marker = "extra == 'extra-12-beaver-agent-local'" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "pydantic-settings" },
|
{ name = "pydantic-settings" },
|
||||||
@@ -361,6 +362,7 @@ requires-dist = [
|
|||||||
{ name = "greenlet", specifier = ">=3.5.0" },
|
{ name = "greenlet", specifier = ">=3.5.0" },
|
||||||
{ name = "itsdangerous", specifier = ">=2.2.0" },
|
{ name = "itsdangerous", specifier = ">=2.2.0" },
|
||||||
{ name = "jinja2", specifier = ">=3.1.6" },
|
{ name = "jinja2", specifier = ">=3.1.6" },
|
||||||
|
{ name = "psutil", specifier = ">=7.2.2" },
|
||||||
{ name = "psycopg", extras = ["binary"], specifier = ">=3.3.4" },
|
{ name = "psycopg", extras = ["binary"], specifier = ">=3.3.4" },
|
||||||
{ name = "pydantic", specifier = ">=2.13.4" },
|
{ name = "pydantic", specifier = ">=2.13.4" },
|
||||||
{ name = "pydantic-settings", specifier = ">=2.14.1" },
|
{ name = "pydantic-settings", specifier = ">=2.14.1" },
|
||||||
@@ -1438,6 +1440,34 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" },
|
{ url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psutil"
|
||||||
|
version = "7.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg"
|
name = "psycopg"
|
||||||
version = "3.3.4"
|
version = "3.3.4"
|
||||||
|
|||||||
Reference in New Issue
Block a user