Server: Agents HTTP

Deploy agents as HTTP API servers using praisonai –serve or Agent.launch()

Deploy single or multi-agent systems as HTTP REST API servers.

Quick Start

  1. Install Dependencies
    bash
    pip install "praisonaiagents[os]"
  2. Set API Key
    bash
    export OPENAI_API_KEY="your-key"
  3. Initialize Agents
    bash
    praisonai --init "helpful assistant"
  4. Start Server (localhost)
    bash
    praisonai serve agents --port 8000

    Expected Output:

    📄 Loading workflow from: agents.yaml
    🚀 Starting PraisonAI API server...
       Host: 127.0.0.1
       Port: 8000
    🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/agents
    ✅ FastAPI server started at http://127.0.0.1:8000
    📚 API documentation available at http://127.0.0.1:8000/docs
  5. Start Server (public bind)

    A non-localhost host requires an API key:

    bash
    export PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)"
    praisonai serve agents --port 8000 --host 0.0.0.0
  6. Verify
    bash
    curl http://localhost:8000/health

Security

praisonai serve agents drives YAML-defined tools (including execute_command), so binding it to a non-localhost interface without authentication is refused.

bash
# Localhost — no key required
praisonai serve agents --port 8000

# Remote — key required (via env var)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# Or via flag
praisonai serve agents --port 8000 --host 0.0.0.0 --api-key "your-secret"

Clients then send the key in the Authorization: Bearer … header (mirrors jobs/server.py).

bash
curl -X POST http://SERVER_IP:8000/agents \
  -H "Authorization: Bearer your-secret" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is AI?"}'
Diagram

Yes

No

Set

Missing

praisonai serve agents

Host is
localhost?

Start — no key needed

–api-key or
PRAISONAI_SERVE_API_KEY?

Start — Bearer auth enforced

SystemExit — refuse to bind

Python – Single Agent

python
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
    llm="gpt-4o-mini"
)
agent.launch(path="/ask", port=8000, host="127.0.0.1")  # loopback default; set "0.0.0.0" to expose (requires a token)

Expected Output:

🚀 Agent 'Assistant' available at http://127.0.0.1:8000
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docs
🔌 Available endpoints: /ask

Cleaning up .launch() endpoints

Call Agent.close() when you’re done serving an agent — the launched HTTP endpoint is torn down, its route is removed from the FastAPI app, and the cached OpenAPI schema is invalidated so /docs and /openapi.json no longer advertise it. Only the agent’s own POST route on that path is dropped; other verbs bound to the same path (e.g. the built-in GET /health) are preserved.

python
from praisonaiagents import Agent

agent = Agent(name="Assistant", instructions="You are a helpful assistant.", llm="gpt-4o-mini")
agent.launch(path="/ask", port=8000)

# ... serve for a while ...

agent.close()   # /ask stops accepting requests; /health, /docs still work
Diagram
app.routerAgentFastAPI appapp.routerAgentFastAPI appserving…launch(path=”/ask”, port=8000)POST /ask registeredclose()drop POST /ask onlyinvalidate openapi_schema/health, /docs preserved

Python – Multi-Agent

python
from praisonaiagents import Agent, AgentTeam

researcher = Agent(name="Researcher", instructions="Research topics", llm="gpt-4o-mini")
writer = Agent(name="Writer", instructions="Write content", llm="gpt-4o-mini")

agents = AgentTeam(agents=[researcher, writer])
agents.launch(path="/content", port=8000, host="127.0.0.1")  # loopback default; set "0.0.0.0" to expose (requires a token)

Expected Output:

🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/content
📊 Available agents for this endpoint (2): Researcher, Writer
🔗 Per-agent endpoints: /content/researcher, /content/writer
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docs

agents.yaml

yaml
framework: praisonai
topic: research and write content
roles:
  researcher:
    role: Researcher
    goal: Research topics thoroughly
    backstory: Expert researcher
    tasks:
      research_task:
        description: Research the topic
        expected_output: Research findings
  writer:
    role: Writer
    goal: Write engaging content
    backstory: Expert writer
    tasks:
      write_task:
        description: Write based on research
        expected_output: Written content
bash
export PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)"
praisonai serve agents --port 8000 --host 0.0.0.0

CLI Commands

bash
# Start agents server (localhost default — no key needed)
praisonai serve agents --port 8000

# With custom host for remote access (key required — see Security)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# With agents file
praisonai serve agents --file agents.yaml --port 8000

# Give the server more time on slow machines
export PRAISONAI_SERVER_READY_TIMEOUT=15
praisonai serve agents --port 8000
OptionDefaultDescription
--port8000Server port
--host127.0.0.1Server host (use 0.0.0.0 for remote)
--fileagents.yamlAgents YAML file
--reloadfalseEnable hot reload
--api-key–API key. Required when --host is not 127.0.0.1 / localhost / ::1. Can also be set via PRAISONAI_SERVE_API_KEY.

launch() Parameters

ParameterTypeDefaultDescription
pathstr/API endpoint path
portint8000Server port
hoststr127.0.0.1Server host. Loopback by default; set "0.0.0.0" to expose, which requires PRAISONAI_LAUNCH_AUTH_TOKEN.
debugboolFalseDebug mode
protocolstrhttphttp or mcp

Endpoints

EndpointMethodDescription
/agentsPOSTSend query to all agents — runs through the cached generator (ToolResolver, tool_timeout, approval, guardrails, retry)
/agents/{agent_name}POSTCall a specific agent through the same pipeline as /agents. Returns 404 for an unknown name
/{path}POSTSend query to agent(s)
/{path}/listGETList available agents
/{path}/{agent_id}POSTCall specific agent
/healthGETHealth check
/docsGETSwagger UI

Example Request/Response

Request:

bash
curl -X POST http://localhost:8000/ask \
  -H "Content-Type: application/json" \
  -d '{"query": "What is AI?"}'

Response:

json
{
  "response": "Artificial intelligence (AI) refers to..."
}

Remote Access

Use host="0.0.0.0" to allow remote connections. For praisonai serve agents, a non-localhost bind requires an API key (see Security):

bash
# CLI — key required for non-localhost
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# Python launch() — set a token before exposing publicly
export PRAISONAI_LAUNCH_AUTH_TOKEN="$(openssl rand -base64 32)"
agent.launch(path="/ask", port=8000, host="0.0.0.0")  # binding non-loopback without a token auto-generates one

Connect from remote (include the Bearer key when serving agents):

bash
curl -X POST http://SERVER_IP:8000/ask \
  -H "Authorization: Bearer your-secret" \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello"}'

How It Works

The agents server builds one shared generator per app at startup, then builds a lightweight per-request generator for each call that borrows the cached generator’s warm pieces.

Diagram
sequenceDiagram
    participant Client
    participant App as FastAPI (lifespan)
    participant Cached as Cached Generator
    participant PerReq as Per-request Generator

    App->>Cached: build once at startup<br/>(warm adapter, config_list,<br/>tool_resolver, tool-timeout pool)
    Client->>App: POST /agents or /agents/{name}
    App->>PerReq: build per-request<br/>(own cli_config, borrows cached pieces)
    PerReq->>PerReq: run through ToolResolver, tool_timeout,<br/>approval, guardrails, retry
    PerReq-->>Client: response
    App->>Cached: close() at shutdown<br/>(owns tool-timeout pool)

    classDef app fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef cached fill:#10B981,stroke:#7C90A0,color:#fff
    classDef perreq fill:#F59E0B,stroke:#7C90A0,color:#fff

    class App app
    class Cached cached
    class PerReq perreq
  • Generator cached per app. A FastAPI lifespan builds a single AgentsGenerator at startup and close()s it at shutdown — no per-request YAML re-parse, framework re-resolution, or fresh 32-worker tool-timeout pool on every call.
  • Truly concurrent. Each request builds a lightweight per-request AgentsGenerator that carries its own cli_config and borrows the cached generator’s warm, immutable pieces (adapter, config_list, tool_resolver, tool-timeout thread pool). Concurrent requests never share mutable state and are not serialised — throughput now scales with the event loop.
  • Cached pool ownership. The cached generator still owns and shuts down the tool-timeout thread pool at app shutdown; per-request generators treat it as borrowed and delegate leak accounting / recycling back to the owner (_get_tool_timeout_executor, _note_leaked_worker, _timeout_owner_key).
  • Graceful fallback. If the cached generator can’t be built, the server falls back to the per-request praisonai.arun path.
  • Route convergence. Both /agents and /agents/{agent_name} run through the same YAML lowering (ToolResolver, tool_timeout, approval, guardrails, retry policy). POST /agents/{agent_name} returns 404 for an unknown name.

Per-tenant validation strictness and timeout pool

Scope YAML validation strictness and the sync-tool timeout pool per request instead of per process. Tenant A migrating strict configs no longer forces strictness on tenant B’s legacy YAMLs.

Pass strict_validation / tool_timeout_workers through the per-request generator’s cli_config — the same DI seam the fast-path already uses to borrow the cached generator’s warm pieces. No os.environ mutation, no cross-tenant bleed.

python
# Per-request generator borrows warm pieces from the cached generator
# (adapter, config_list, tool_resolver, tool-timeout pool ownership)
# and carries its own cli_config — no os.environ mutation, no cross-tenant bleed.
per_request = AgentsGenerator(
    agent_file=tenant_agent_file,
    framework=framework,
    config_list=cached.config_list,
    cli_config={
        **base_cli_config,
        "strict_validation": tenant.strict,          # True/False per tenant
        "tool_timeout_workers": tenant.pool_size,    # e.g. 128 for a heavy tenant
    },
    tool_timeout_executor=cached._get_tool_timeout_executor(),  # borrowed
    tool_resolver=cached.tool_resolver,
    adapter=cached.framework_adapter,
)

Each toggle resolves the same way: explicit constructor arg first, then the matching cli_config key, then the env var, then the built-in default.

Diagram

Per-run toggle resolution

highest

🎯 Constructor arg

Pick first non-None

🧩 cli_config key

🌐 Env var

⚙️ Built-in default

✅ Value for this run

strict_validation:
  explicit arg  >  cli_config["strict_validation"]  >  PRAISONAI_VALIDATE_STRICT env  >  False (default)

tool_timeout_workers:
  explicit arg  >  cli_config["tool_timeout_workers"]  >  PRAISONAI_TOOL_TIMEOUT_WORKERS env  >  32 (default)

Environment Variables

VariableDefaultDescription
PRAISONAI_SERVE_API_KEY–API key required to bind praisonai serve agents on a non-localhost host. The server exits with SystemExit if this is unset and no --api-key is passed when --host is not localhost. Clients authenticate with Authorization: Bearer <key>.
PRAISONAI_SERVER_READY_TIMEOUT5.0Seconds to wait for the FastAPI server to become ready after .launch() / praisonai serve agents. A warning is logged if exceeded; startup continues.

Troubleshooting

IssueFix
--api-key ... is required when binding to a non-localhost host at startupYou bound to a non-localhost host without a key. Set PRAISONAI_SERVE_API_KEY=... (or --api-key ...), or bind to 127.0.0.1 / localhost for a private-only server.
Port in uselsof -i :8000 then kill process
No agents.yamlpraisonai --init "topic"
Missing API keyexport OPENAI_API_KEY="your-key"
Server exits immediately on --host 0.0.0.0Non-localhost bind needs auth: export PRAISONAI_SERVE_API_KEY="your-secret" (or pass --api-key)
Missing depspip install "praisonaiagents[os]"
Connection refusedUse host="0.0.0.0" for remote
Firewall blockingOpen port in firewall
Agent server slow to start / “did not become ready” warningSet PRAISONAI_SERVER_READY_TIMEOUT=10 (seconds) before launching, or check server logs for the underlying startup error.

Related