Skip to content

ayush0x00/ModArch

Repository files navigation

OpenAgent

Central master coordinates query agents (send questions) and action agents (expose tools). The orchestrator (default gpt-4o-mini, overridable via OPENAGENT_CHAT_MODELS / POST /query) decides direct answers vs tool calls, optional auto_mode multi-step loops, inter-agent collaboration (peer-to-peer negotiation relayed through the master), prompt profiles (default, auto_multi, reasoning), and long-context summarization. Action agents can be in-memory (WebSocket), invocation (HTTP + Redis), or Kubernetes sandbox (one Pod per agent or pool runner). A React UI wires agents on a canvas, runs POST /query chat with attachments, exports/restores workspace YAML, and can generate agent code via the master LLM.

Architecture

flowchart TB
    subgraph ui [Web UI]
        Canvas[React Flow Canvas]
        Chat[Orchestrator Chat]
    end
    subgraph master [Master]
        WS[WebSocket /ws]
        QueryAPI[POST /query]
        RegisterAPI[POST /register]
        Callback[POST /tool_callback]
        Orch[Orchestrator]
        Reg[Registry]
    end
    subgraph redis [Redis]
        Cache[(Agent Cache)]
        Sessions[(Sessions)]
    end
    subgraph action_inmemory [Action In-Memory]
        WeatherAgent[weather-agent]
    end
    subgraph action_invocation [Action Invocation]
        InvokeServer[HTTP Server]
    end

    Canvas -->|link agents| RegisterAPI
    Chat -->|POST /query| QueryAPI
    QueryAPI -->|loopback WS| Orch
    Orch -->|agents_used| Chat
    WS --> Orch
    Orch --> Cache
    Orch --> Sessions
    Orch --> Reg
    Reg --> WeatherAgent
    RegisterAPI --> Cache
    InvokeServer -->|register once| RegisterAPI
    Orch -->|POST tool call| InvokeServer
    InvokeServer -->|POST result| Callback
    Callback --> WS
Loading
  • Query agent: Connects via WebSocket, sends queries, receives query_result. The UI provides a built-in query client on the canvas (always online).
  • In-memory action agent: Connects via WebSocket, registers tools, receives tool_call over WS. Not stored in Redis; gone when master or agent restarts.
  • Invocation action agent: Runs an HTTP server. Registers once via POST /register (or WS with invocation_url). Master stores invocation_base_url + per-tool endpoint in Redis. On tool call, master POSTs to {base}{endpoint}; agent POSTs result to master /tool_callback. Survives master restart; master health-checks GET {base}/health on startup and /refresh.

Callable tool handlers (symmetric): For WebSocket action agents, use OrchestratorActionAgent(agent_id, tools, tool_handler) with an async callable (optional progress_callback in the signature). For HTTP invocation, use InvocationActionAgent(agent_id, tools, invocation_base_url, tool_handler=...) — same pattern; the callable may accept progress (invocation style) or progress_callback. Or subclass OrchestratorWebSocketActionAgent / InvocationActionAgent and implement handle_tool on the class instead.

Setup

source ~/base/bin/activate   # or your venv
pip install -r requirements.txt

Copy .env.example to .env and set:

OPENAI_API_KEY=sk-...
REDIS_URL=redis://127.0.0.1:6379
MASTER_BASE_URL=http://127.0.0.1:8000

Optional for invocation agent: INVOCATION_BASE_URL, INVOCATION_PORT, MASTER_WS.

RAG stack (optional)

Same WebSocket pattern as other action agents (OrchestratorActionAgent): extract (chunks only) and index / search_knowledge are separate tools on separate agents (or the same bundle process, still separate tool calls). Implementation lives under openagent/rag_*.py. Extractor and indexer entrypoints live in system_agents/ (document_extractor_agent, document_indexer_agent). The indexer stores vectors in Redis at REDIS_URL in its own process (run a Redis sidecar or bundle Redis in the indexer container). Optional one-process bundle: python -m system_agents.rag_stack_ws.

Docker / Kubernetes (recommended)
Build the runtime image, then either:

  • One container, two registrations: python -m system_agents.rag_stack_ws
  • One agent per Pod: python -m system_agents.document_extractor_agent, python -m system_agents.document_indexer_agent

Set REDIS_URL on the master (for session blobs / attachments). For K8s sandbox new_pod deploys of document-indexer-agent, the master adds a Redis sidecar in the same Pod and sets REDIS_URL=redis://127.0.0.1:6379 on the indexer (override with OPENAGENT_K8S_INDEXER_REDIS_SIDECAR=0 and OPENAGENT_K8S_INDEXER_REDIS_URL if you use a shared cluster Redis instead).

Set MASTER_WS (and OPENAI_API_KEY) in each Pod so agents reach the master.

Local (no image)
From repo root with venv + deps: PYTHONPATH=. python -m system_agents.rag_stack_ws (or each system_agents.* module separately)

In the UI, link RAG · Extractor and RAG · Indexer to the orchestrator. Attach files in chat uses POST /query attachments (stored in Redis on the master; the orchestrator prompt gets filenames + refs).

Useful env: OPENAGENT_EMBEDDING_MODEL, RAG_MAX_ATTACHMENT_BYTES, RAG_TOOL_CALL_TIMEOUT.

The UI polls GET /agents/discover to list system_agents/*.py then agents/*.py on the master (merged into the sidebar with the default RAG rows). Linking a WebSocket agent on the canvas triggers deploySingleAgentToSandbox when OPENAGENT_K8S_SANDBOX=1 (same path as sidebar Restore); without K8s, run the module locally after linking.

Kubernetes sandbox (optional)

When OPENAGENT_K8S_SANDBOX=1 (default), the master can deploy agent code into the cluster via POST /internal/sandbox-agent/start (new_pod or pool placement). The UI uses this for canvas “deploy to sandbox” flows; GET /agents merges Redis sandbox metadata and live Pod status (and may attach log_tail for unhealthy workloads).

Minikube quick path: from repo root, ./start.sh builds openagent/agent-runtime:latest and openagent/agent-pool-runner:latest inside the Minikube Docker daemon and applies deploy/k8s/*.yaml. Then run the master on the host with a reachable bind address, e.g. OPENAGENT_K8S_SANDBOX=1 uvicorn master.app:app --host 0.0.0.0 --port 8000, and set OPENAGENT_K8S_MASTER_WS (or pass master_ws from the UI) to a WebSocket URL that resolves from inside Pods (see GET /internal/sandbox/status hint).

Web UI (openagent-ui/)

A React dashboard for managing the orchestrator workspace.

  • Upload YAML to load master config and agent definitions, or Connect to master to discover agents already registered.
  • Canvas (React Flow): the orchestrator node sits at the center. A built-in Query Client node is always present on the left (always online). The sidebar lists RAG · Extractor and RAG · Indexer after you connect to the master (even when GET /agents is empty); drag them onto the canvas and link to the orchestrator, then start the WS processes (PYTHONPATH=. python -m system_agents.document_extractor_agent, same for document_indexer_agent, or python -m system_agents.rag_stack_ws for both in one process). Drag other agents from YAML or from discovery; linking registers HTTP agents with the master. With K8s sandbox on, linking a WS agent can deploy it remotely instead of only local python -m ….
  • Agent builder: natural-language generate + save flows call POST /generate-agent and POST /save-agent on the master (toggle with OPENAGENT_AGENT_GENERATOR).
  • Orchestrator chat: an integrated POST /query panel at the bottom. Attach files sends attachments (base64) to the master; blobs are stored in Redis and only refs + filenames appear in the orchestrator prompt. Queries are scoped to canvas-linked action agents. Auto mode, plan approval, execute plan, model, and prompt profile map to the JSON fields documented under Master HTTP endpointsPOST /query. Follow-up messages share a session_id until you click New session (clears transcript and server-side session).
  • Agent highlighting: when the orchestrator answers a query, the canvas highlights which agent(s) were used with a purple glow (4-second fade). If the orchestrator answers from its own context, the orchestrator node itself glows.
  • Export YAML: snapshot master URLs, agent rows, tools, handlers, and canvas layout into a portable YAML file. The built-in query client is stripped from exports (auto-injected on load).
  • Restore on master: re-register HTTP agents from YAML in Redis (POST /internal/workspace/restore).

YAML is parsed by openagent/yaml_runner.py (same schema the UI uploads). Agents can run inside the master via default-on OPENAGENT_YAML_RUNNER (UI upload or POST /internal/yaml-agents/start with {"yaml": "<text>"}). See openagent-ui/README.md.

Configuration (config.py)

All settings are read from the environment (and from .env via dotenv). Defaults are below.

Env variable Default Description
Master
REDIS_URL redis://127.0.0.1:6379 Redis URL (use 127.0.0.1 not localhost in containers—avoids IPv6 / errno 99).
REDIS_AGENT_TTL_SECONDS 86400 (24h) TTL for cached (invocation) agent keys. Refreshed when the agent is used.
REDIS_SESSION_TTL_SECONDS 86400 (24h) TTL for session keys; conversation history expires after this.
MASTER_BASE_URL http://127.0.0.1:8000 Base URL of the master (HTTP). Used for tool callback URL and health.
OPENAI_API_KEY Required. OpenAI API key for orchestrator and summarizer.
Orchestrator / models
OPENAGENT_CHAT_MODELS (see config.py) Single global list: comma-separated OpenAI model ids for the orchestrator dropdown, POST /query orchestrator_model, and agent generator. First entry is the default when ORCHESTRATOR_MODEL is unset.
ORCHESTRATOR_MODEL first of OPENAGENT_CHAT_MODELS Default orchestrator model when the client omits orchestrator_model. If set to an id not in the global list, that id is prepended to the list.
AGENT_GENERATOR_MODEL same default as orchestrator Default for POST /generate-agent when model is omitted. Must be in the global list (or prepended like ORCHESTRATOR_MODEL).
ORCHESTRATOR_MODEL_ALLOWLIST Legacy only (used only when OPENAGENT_CHAT_MODELS is unset): comma-separated extras merged with ORCHESTRATOR_MODEL. Prefer OPENAGENT_CHAT_MODELS.
ORCHESTRATOR_CONTEXT_MAX_TOKENS 12000 Max tokens for orchestrator context (summaries + tail + tools + query). When exceeded, older turns are summarized.
ORCHESTRATOR_RECENT_TURNS 8 Number of recent raw turns to keep in full before summarizing the rest.
SUMMARIZER_MODEL (none) Model for summarizing long context. If unset, uses ORCHESTRATOR_MODEL.
Timeouts (seconds)
HEALTH_CHECK_TIMEOUT 2.0 Timeout for master health checks (e.g. invocation agent /health).
TOOL_CALL_TIMEOUT 30.0 Max time to wait for a tool result (WS or HTTP callback) before failing the query.
TOOL_INVOKE_HTTP_TIMEOUT 2.0 Timeout for master POST to invocation agent's tool endpoint.
Agents (client → master)
MASTER_WS ws://127.0.0.1:8000/ws WebSocket URL used by query/action agents to connect to the master.
WS_CONNECT_TIMEOUT 10.0 Timeout for WebSocket connect.
WS_REGISTRATION_TIMEOUT 10.0 Timeout for registration response after register message.
WS_KEEPALIVE_INTERVAL 30 Seconds between agent→master ping frames (0 disables). Keeps WS alive through proxies.
QUERY_RESPONSE_TIMEOUT 180.0 Timeout for query agent to receive query_result after sending query.
HTTP_REGISTER_TIMEOUT 10.0 Timeout for invocation agent HTTP POST to /register.
Orchestrator auto-mode
ORCHESTRATOR_AUTO_MAX_STEPS 20 Max tool rounds per user message when auto_mode is on (POST /query).
ORCHESTRATOR_AUTO_SPAWN_WAIT_SECONDS 120 How long to wait for a sandboxed agent to register on WS after spawn.
Feature toggles
OPENAGENT_YAML_RUNNER 1 In-master YAML agent runner (POST /internal/yaml-agents/start, UI upload). 0 disables.
OPENAGENT_WORKSPACE_RESTORE 1 POST /internal/workspace/restore. 0 disables.
OPENAGENT_HTTP_QUERY 1 POST /query. 0 returns 403.
OPENAGENT_AGENT_GENERATOR 1 POST /generate-agent / models. 0 disables.
RAG / attachments
OPENAGENT_EMBEDDING_MODEL text-embedding-3-small Embeddings for indexer.
OPENAGENT_RAG_EXTRACT_MODEL gpt-4o-mini LLM for extract_document / PDF ingest.
RAG_MAX_ATTACHMENT_BYTES 2097152 Per-file cap for POST /query attachments.
RAG_MAX_ATTACHMENTS 5 Max files per query.
RAG_TOOL_HTTP_TIMEOUT 120.0 HTTP timeout for long RAG tool calls from master perspective.
RAG_TOOL_CALL_TIMEOUT 180.0 Max wait for RAG tool completion.
RAG_EXTRACT_CACHE_TTL_SECONDS 604800 Redis TTL for extract cache (7d).
Kubernetes sandbox
OPENAGENT_K8S_SANDBOX 1 Enable pool/new_pod deploy APIs and sandbox field on GET /agents. 0 for local-only.
OPENAGENT_K8S_NAMESPACE agents Namespace for agent workloads.
OPENAGENT_K8S_POOL_LABEL_SELECTOR openagent.io/role=agent-pool Label selector for pool runner Pods.
OPENAGENT_K8S_MASTER_WS ws://host.minikube.internal:8000/ws WS URL injected into Pods when UI does not override (must be reachable from cluster).
OPENAGENT_K8S_AGENT_IMAGE openagent/agent-runtime:latest Image for new_pod agents.
OPENAGENT_K8S_AGENT_IMAGE_PULL_POLICY Never Pull policy (Minikube local builds).
OPENAGENT_K8S_POOL_RUNNER_IMAGE openagent/agent-pool-runner:latest Pool runner image.
OPENAGENT_K8S_POOL_RUNNER_PORT 8080 Pool runner HTTP port inside the Pod.
OPENAGENT_K8S_LOG_TAIL_LINES 120 Log lines attached to unhealthy GET /agents sandbox rows.
OPENAGENT_K8S_INDEXER_REDIS_SIDECAR 1 Redis sidecar for document-indexer-agent in new_pod mode.
OPENAGENT_K8S_INDEXER_REDIS_URL (REDIS_URL) Used when sidecar is off.
OPENAGENT_K8S_SANDBOX_RUNTIME_TTL 604800 Redis TTL for sandbox placement metadata.
Invocation agent (demo)
INVOCATION_PORT 9000 Default port for demo invocation agent.
INVOCATION_HOST 127.0.0.1 Host for invocation base URL.
INVOCATION_BASE_URL http://{INVOCATION_HOST}:{INVOCATION_PORT} Full base URL when not set explicitly.

Run

Terminal 1 – Redis (required for invocation agents and for master to list known agents)

redis-server

Terminal 2 – Master (WebSocket on 8000, HTTP APIs)

uvicorn master.app:app --reload --reload-dir master --reload-dir protocol --reload-dir openagent --reload-exclude "agents/**" --host 0.0.0.0 --port 8000

Only master/, protocol/, and openagent/ are watched; --reload-exclude is a safety net if you ever use --reload without --reload-dir, so saving generated files under agents/ does not restart the master and drop WebSocket connections (WS agents must reconnect).

If you run uvicorn … --reload without --reload-dir, always add --reload-exclude "agents/**" or every agent save will reload the server.

Terminal 3 – Action agent (in-memory)

PYTHONPATH=. python sample_agents/action_weather.py

Stays connected via WebSocket; tools are available only while this process and the master are running.

Terminal 4 – Action agent (invocation) (optional)

PYTHONPATH=. python sample_agents/demo_invocation_agent.py

Starts an HTTP server and registers with the master once. No WebSocket. Master invokes via HTTP; agent survives master restart (re-register or rely on Redis).

Terminal 5 – Query agent

PYTHONPATH=. python sample_agents/query_demo.py

Sends example queries (including a long-task that exercises progress); the orchestrator calls the weather or echo/get_time/long_task tools. Watch the master terminal for progress (WS) or progress (HTTP) lines.

Master HTTP endpoints

Endpoint Description
GET /health Liveness
GET /agents Tracker (in-memory WS) + Redis invocation agents + optional K8s “stub” rows; includes sandbox when OPENAGENT_K8S_SANDBOX=1
GET /agents/discover Files for the UI sidebar: system_agents/*.py then agents/*.py (deduped by agent_id)
GET /refresh Re-fetch Redis and health-check invocation agents; prints status to the master terminal
POST /register HTTP action agent: invocation_base_url + tools (+ optional per-tool endpoint) → Redis
POST /unregister Remove cached HTTP agent by agent_id
POST /query Orchestrator via internal WS loopback. Body (JSON): query (required); optional session_id, allowed_agent_ids, attachments ([{filename, mime_type, content_base64}]), orchestrator_model, orchestrator_prompt_profile (default | auto_multi | reasoning), auto_mode, auto_require_plan_approval, auto_plan_execute. Disabled → 403 when OPENAGENT_HTTP_QUERY=0.
GET /query/orchestrator-models default_model, allowed_models, prompt_profiles for the UI
GET /query/session-rag-context?session_id= Attachment refs + session-side RAG profile from Redis (vectors still via indexer tools)
POST /tool_callback Invocation agents POST tool results (call_id, success, result / error)
POST /tool_progress Optional progress updates for long HTTP tools
GET /tool_progress/{call_id} Poll latest progress
POST /generate-agent LLM codegen from description (name, description, agent_type, transport, optional model, …). OPENAGENT_AGENT_GENERATOR=0 → 403.
GET /generate-agent/models Model list + default for the generator UI
POST /save-agent Persist generated Python under agents/<module>.py
GET /agent-source?handler= Read source for a handler module path under the repo
POST /internal/yaml-agents/start {"yaml": "<text>"} — run YAML agents in the master asyncio loop (OPENAGENT_YAML_RUNNER=0 → 403)
POST /internal/workspace/restore {"yaml": "<text>", "register_http": true} — re-register HTTP agents from exported UI YAML
GET /internal/sandbox/status Whether K8s sandbox is enabled + namespace + default master WS hint
GET /internal/k8s/pools List pool runner Pods (403 if sandbox off)
GET /internal/k8s/workloads Dedicated agent Pods + pool Pods overview
POST /internal/sandbox-agent/start Deploy agent code: placement new_pod or pool (+ pool_pod_name), optional master_ws
POST /internal/sandbox-agent/stop Tear down sandbox workload for agent_id
GET /internal/sandbox-agent/logs?agent_id=&tail_lines= Fetch container logs for a deployed sandbox agent

WebSocket at /ws: connect, send register, then query (query agents) or receive tool_call (action agents).

Invocation agent contract

  • Health: Expose GET {invocation_base_url}/health → 200 and e.g. {"status":"ok"}.
  • Tool call: Master POSTs to {invocation_base_url}{tool.endpoint} with body: call_id, tool_name, arguments, callback_url.
  • Result: Agent POSTs to callback_url with call_id, success, and either result or error.

Each tool can have its own endpoint (e.g. /run, /get_time).

Project layout

Path Description
protocol/ Wire protocol (Pydantic: register, query, tool_call, tool_result, ToolSchema with optional endpoint)
openagent/ Client helpers: AgentClient, connect_master, run_action_agent, register_invocation_agent, ToolSchema
master/ FastAPI app, WebSocket, Redis cache, orchestrator, registry, tracker, collaboration manager, http_query_gateway
sample_agents/ Runnable examples: action_weather.py, demo_invocation_agent.py, query_demo.py
agents/ User- or UI-generated modules (POST /save-agent); empty in a fresh clone
openagent-ui/ React dashboard: canvas (React Flow), sidebar, orchestrator chat, YAML import/export

Message contract (WebSocket)

  • Agent → Master: register, query, tool_result, tool_progress, peer_message, collaboration_end, ping
  • Master → Agent: registered, query_result, tool_call, collaboration_start, peer_message, pong, error

query_result includes an optional agents_used field (list of {agent_id, tool_name}) so clients can see which agents handled the query. Empty list means the orchestrator answered directly.

peer_message is bidirectional: agents send it to the master (with to_agent), the master stamps from_agent and relays to the target. collaboration_start carries the session task, peer list, and role. collaboration_end carries a summary string.

See protocol/messages.py for full fields. Register may include invocation_url (legacy) for action agents; then the master stores them in Redis and invokes via HTTP.

Inter-agent collaboration

When a user query requires multiple agents to negotiate, iterate, or converge on a result — not just sequential tool calls — the orchestrator can start a collaboration session. Agents communicate peer-to-peer through the master (which relays and records every exchange), then the orchestrator synthesizes the outcome for the user.

How it works

sequenceDiagram
    participant User
    participant Orchestrator
    participant Master
    participant AgentA
    participant AgentB

    User->>Master: Query
    Master->>Orchestrator: decide(query, snapshot)
    Orchestrator->>Master: start_collaboration(agents, task)
    Master->>Master: Create collab session in Redis
    Master->>AgentA: CollaborationStart(session_id, peers, task, role=initiator)
    Master->>AgentB: CollaborationStart(session_id, peers, task, role=responder)
    AgentA->>Master: PeerMessage(to=B, content)
    Master->>AgentB: PeerMessage(from=A, content)
    AgentB->>Master: PeerMessage(to=A, content)
    Master->>AgentA: PeerMessage(from=B, content)
    AgentA->>Master: CollaborationEnd(summary)
    Master->>AgentB: CollaborationEnd(summary)
    Master->>Orchestrator: transcript
    Orchestrator->>Master: synthesize answer
    Master->>User: QueryResult
Loading

Discovery and handshake: Agents run in isolated containers (or local processes) and only know the master. They have no awareness of each other until the orchestrator decides collaboration is needed. The master handles discovery by sending each agent a CollaborationStart message containing the peer list — each peer's agent_id, metadata, and tools. Agents don't need network routes to each other; the master relays every PeerMessage between them. This works identically for local, pool, and dedicated-pod agents.

Message flow: The initiator (first agent in the list) sends first. Each PeerMessage carries a free-form content dict (proposals, counters, acceptances — whatever the agents' logic requires). The master appends every message to the collaboration session's entries[] in Redis before relaying. Any agent can signal completion by sending CollaborationEnd with a summary; the master notifies all other peers and resolves the session.

Safety guards: Sessions auto-terminate after COLLABORATION_TIMEOUT_SECONDS (default 120s) or COLLABORATION_MAX_MESSAGES (default 50) to prevent infinite loops.

Collaboration session store

Collaboration sessions are stored in Redis under a separate schema from orchestrator sessions:

Aspect Orchestrator sessions (session:) Collaboration sessions (collab:)
Key session:{id} collab:{id}
Shape flat list[turn] {metadata + entries[]} object
Entry types answer_directly, call_tool, summary handshake, message, end
Participants orchestrator ↔ one agent N agents peer-to-peer
Linking standalone parent_session_id back-ref

A reverse index at collab:by_parent:{parent_session_id} (Redis SET) lets you find all collaborations spawned from a given orchestrator session. When a collaboration completes, a summary turn (decision: "collaboration") is appended to the parent orchestrator session so follow-up queries see the result in context.

Inspect a collaboration:

# Full session document (metadata + all entries)
redis-cli GET 'collab:<collab_session_id>' | python3 -m json.tool

# Find collaborations for an orchestrator session
redis-cli SMEMBERS 'collab:by_parent:<session_id>'

Writing a collaboration-capable agent

Pass a collaboration_handler to OrchestratorActionAgent. This auto-sets supports_collaboration: true in the agent's metadata (the orchestrator only considers agents with this flag for start_collaboration).

from openagent import OrchestratorActionAgent, ToolSchema
from openagent.client import CollaborationContext

async def handle_tool(tool_name, arguments, *, progress_callback=None):
    ...

async def handle_collaboration(ctx: CollaborationContext) -> str:
    if ctx.role == "initiator":
        await ctx.send_message(ctx.peers[0]["agent_id"], {"proposal": 70.0})
        reply = await ctx.receive_message(timeout=60)
        # reply is a PeerMessage or CollaborationEnd or None (timeout)
        return f"Agreed on {reply.content}"
    else:
        msg = await ctx.receive_message(timeout=60)
        await ctx.send_message(msg.from_agent, {"accept": True})
        return "Accepted"

agent = OrchestratorActionAgent(
    "my-agent", TOOLS, handle_tool,
    collaboration_handler=handle_collaboration,
)

CollaborationContext provides:

  • ctx.session_id, ctx.task, ctx.role ("initiator" / "responder"), ctx.peers, ctx.context
  • await ctx.send_message(to_agent, content_dict) — send a PeerMessage
  • await ctx.receive_message(timeout) — wait for next PeerMessage or CollaborationEnd; returns None on timeout
  • await ctx.end(summary) — signal completion

Auto-generated collaboration agents

When the auto-mode planner determines that generated agents need to collaborate, it sets requires_collaboration: true on those entries. The code generator then uses a collaboration-aware template that includes a handle_collaboration function with real negotiation logic.

Configuration

Env variable Default Description
OPENAGENT_COLLABORATION 1 Enable inter-agent collaboration. 0 disables.
OPENAGENT_COLLAB_TIMEOUT 120 Max seconds for a collaboration session before auto-termination.
OPENAGENT_COLLAB_MAX_MESSAGES 50 Max peer messages per session (prevents infinite loops).
OPENAGENT_COLLAB_SESSION_TTL 86400 Redis TTL for collaboration session keys.

Session management

Conversations are session-scoped. The master creates or reuses a session per query so multi-turn context is preserved.

  • Storage: Redis key session:{session_id}; value is a JSON array of turns. Sessions expire after REDIS_SESSION_TTL_SECONDS (default 24h).
  • Session ID: First query in a conversation omits session_id; the master creates one (UUID) and returns it in query_result.session_id. Subsequent queries send that session_id so the master loads the same turns.
  • Turns: Each turn is a dict: query, query_id, decision ("answer_directly", "call_tool", or "collaboration"), and for tool calls tool_agent_id, tool_name, tool_args, result/error. Collaboration turns have decision: "collaboration", collab_session_id, agents, task, summary, and message_count. Summary turns (see below) have decision: "summary", summary, and covers_through_index.
  • Create/load/append: master/session_store.pycreate_session, load_session, append_turn. The orchestrator loads turns before each decision and appends a new turn after each answer or tool result.

A sample session file (raw turns plus summary turns as stored in Redis) is at experiments/results/sample_session_file.json. It shows interleaved raw turns (weather queries, tool calls, direct answers) and summary turns with covers_through_index.


Summary logic (long context)

When building orchestrator context, we keep token usage bounded by summarizing older turns and persisting summaries in the session.

  • Context for each query = all previous summary turns (in order) + all raw turns after the last summary. So older conversation is compressed into summary blocks; only the “tail” after the last summary is sent in full.
  • When over limit (ORCHESTRATOR_CONTEXT_MAX_TOKENS): we take the raw tail, keep the last ORCHESTRATOR_RECENT_TURNS turns in full, and summarize the rest with the same orchestrator model. We append a summary turn to the session: {"decision": "summary", "summary": "<text>", "covers_through_index": N}. covers_through_index is the index (in the full session list) of the last turn that summary covers; “raw turns after last summary” are all turns with index > last summary’s covers_through_index.
  • Config: ORCHESTRATOR_CONTEXT_MAX_TOKENS (default 12000), ORCHESTRATOR_RECENT_TURNS (default 8). Lower them to trigger summarization earlier (e.g. 1200 and 2 for testing).
  • Token counting: master/session_context.py uses tiktoken (cl100k_base). Summarization and message building live in build_orchestrator_messages_async; the master appends the returned summary turn to the session when present.

Clearing Redis

To reset cached (invocation) agents:

redis-cli FLUSHDB

Then restart the master; invocation agents must re-register.

About

Architecture for Agents to communicate worldwide.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors