OpencodeAgent
What it is
- A lightweight wrapper around the
opencodeCLI server that:- starts/stops the server process,
- sends prompts via HTTP,
- optionally streams server events (SSE),
- exposes the agent as LangChain tools and FastAPI routes,
- optionally persists session/messages via
OpencodeSessionService.
Public API
Exceptions
OpencodeError: Base exception.OpencodeUnavailableError: Opencode server not reachable (e.g., connection refused).OpencodeRequestError(status_code: int, body: str): Non-2xx HTTP responses.OpencodeStartupError: Server failed to become ready / port not initialized / port conflict.OpencodeTimeoutError: HTTP request timed out.
Data / Schemas
-
OpencodeAgentConfiguration(dataclass)workdir: str: Working directory for the server process.port: int | None: Port to bind; ifNone, dynamically assigned.name: str: Agent/tool name.description: str: Agent/tool description.model: str | None: Optional"provider/model"identifier, sent as{providerID, modelID}.system_prompt: str: Optional system prompt primed once per session.opencode_bin: str: Binary name/path (default"opencode").startup_timeout: int: Seconds to wait for health (default15).request_timeout: float | None: If set, used ashttpx.Timeout(value); else connect/write/pool bounded andread=None.
-
OpencodeCompletionQuery(Pydantic)prompt: strthread_id: str = ""
-
OpencodeToolInput(Pydantic)message: strthread_id: str = ""
Class: OpencodeAgent
Core methods/operators:
start() -> None- Ensures auth bootstrap file (best-effort), selects/validates port, starts
opencode serve, waits for/global/health.
- Ensures auth bootstrap file (best-effort), selects/validates port, starts
stop() -> None- Terminates/kill the spawned server process (if started by this instance).
duplicate(queue=None, agent_shared_state=None)- Returns a new instance sharing configuration and (by default) shared state.
reset() -> None- Attempts to increment
state.thread_idif supported; otherwise clears internal thread→session mapping.
- Attempts to increment
invoke(prompt: str, thread_id: Optional[str]=None) -> str- Synchronous wrapper over
ainvoke(...).
- Synchronous wrapper over
ainvoke(message: str, thread_id: Optional[str]=None) -> str- Sends a message to an opencode session and returns extracted text from returned parts.
- Watches
/eventin the background to emit tool usage/response callbacks and auto-approve permissions.
astream(message: str, thread_id: Optional[str]=None) -> AsyncIterator[str]- Streams raw SSE
data:JSON lines (as strings) while prompt runs; auto-approves permissions and emits callbacks.
- Streams raw SSE
stream(message: str, thread_id: Optional[str]=None) -> AsyncIterator[str]- Alias of
astream.
- Alias of
as_tools() -> list[BaseTool]- Exposes a single LangChain
StructuredToolusingOpencodeToolInputschema.
- Exposes a single LangChain
as_api(router: APIRouter, route_name="", name="", description="", description_stream="", tags=None) -> None- Registers two FastAPI endpoints:
POST /{route_name}/completion→{"completion": "..."}POST /{route_name}/stream-completion→ SSE stream (EventSourceResponse) yielding{"data": chunk}and a finaltype=endmessage.
- Registers two FastAPI endpoints:
Callbacks:
on_tool_usage(callback)on_tool_response(callback)on_ai_message(callback)- Callbacks receive
langchain_core.messages.AIMessage(and foron_ai_message, alsoagent_name).
- Callbacks receive
Configuration/Dependencies
Runtime dependencies used in this module:
- External process:
opencodeCLI (opencode serve --port ...) - HTTP client:
httpx(sync/async) - API framework:
fastapi,sse_starlette(only foras_api) - LangChain:
langchain_core(only for callbacks/tools) - Optional persistence:
OpencodeSessionService(if provided)
Auth bootstrap (best-effort, only if missing):
- Loads
EngineConfigurationand, ifconfiguration.opencode.providersis present, writes auth JSON to:opencode.auth_file_pathor default~/.local/share/opencode/auth.json
- File is created only if it does not exist; mode set to
0600.
Network endpoints expected from opencode server:
GET /global/healthreturning JSON like{"healthy": true}GET /eventSSE stream withdata: <json>POST /sessionto create a sessionGET /session/{id}to check existing sessionPOST /session/{id}/messageto send message partsPOST /session/{id}/permissions/{permission_id}to approve permissions
Usage
Minimal invocation
from naas_abi_core.services.agent.OpencodeAgent import (
OpencodeAgent,
OpencodeAgentConfiguration,
)
conf = OpencodeAgentConfiguration(
workdir="/tmp/opencode-work",
port=None, # auto-pick a free port
name="opencode_agent",
description="Opencode-backed dev agent",
)
agent = OpencodeAgent(conf)
print(agent.invoke("Say hello in one sentence."))
agent.stop()
Async streaming (raw SSE data: payloads)
import asyncio
from naas_abi_core.services.agent.OpencodeAgent import OpencodeAgent, OpencodeAgentConfiguration
async def main():
agent = OpencodeAgent(OpencodeAgentConfiguration(
workdir="/tmp/opencode-work",
port=None,
name="opencode_agent",
description="Opencode-backed dev agent",
))
async for data in agent.astream("List three bullet points."):
print(data)
asyncio.run(main())
FastAPI routes
from fastapi import FastAPI, APIRouter
from naas_abi_core.services.agent.OpencodeAgent import OpencodeAgent, OpencodeAgentConfiguration
app = FastAPI()
router = APIRouter()
agent = OpencodeAgent(OpencodeAgentConfiguration(
workdir="/tmp/opencode-work",
port=None,
name="opencode_agent",
description="Opencode-backed dev agent",
))
agent.as_api(router, route_name="opencode")
app.include_router(router)
Caveats
start()will raiseOpencodeStartupErrorif:portcannot be initialized,- the port is in use and the process on it is not healthy,
- the spawned server does not become healthy within
startup_timeout, - the spawned server exits during startup.
- Default async HTTP timeout has
read=None(no read timeout). If you setrequest_timeout, it becomes a singlehttpx.Timeout(value)applied to all phases. - Streaming yields raw JSON strings from SSE
data:lines; consumers must parse and interpret them. - Permission auto-approval is attempted when event payloads contain strings starting with
per_(walks the entire event object).