Skip to content

Orchestration backends

LangGraph and OpenAI Agents SDK — framework capabilities, provider compatibility, orchestrator CRUD, and config reference.

Intended audience: Stakeholders, Business analysts, Solution architects, Developers, Testers

Learning outcomes by role

Stakeholders

  • Compare LangGraph and OpenAI Agents SDK to choose the right engine for your AI product.

Business analysts

  • Document the framework × mode matrix and provider constraints as orchestrator configuration criteria.

Solution architects

  • Map provider credentials and network egress paths for each active backend.

Developers

  • Configure and create orchestrator instances using the correct framework, mode, and LLM config.

Testers

  • Build a compatibility matrix of framework × mode × provider for integration test coverage.

Choosing a backend means choosing the engine that will execute your agent graph — how tool calls get routed, how agents hand off to each other, and which model providers the framework can talk to. Production orchestrators use langgraph or openai_agents. The google_adk value appears in create-request validation patterns for forward compatibility, but OrchestratorFactory does not register google_adk backends — do not use it for new instances until support ships.

The framework_type and mode fields are frozen at orchestrator creation. Once an instance exists, switching engines means creating a new orchestrator and redirecting any central points that reference the old one.

When a chat request arrives, the platform resolves the orchestrator instance from the pool and calls its framework-specific astream method. That method uses the resolved LLM config (decrypted BYOK key, provider, base URL) to make model calls through the framework’s native SDK — LangChain for LangGraph, the OpenAI Agents SDK for openai_agents. Plugins inject tools into the graph at load time, before the first message arrives.

flowchart TD
    CREATE["POST /api/orgs/org_id/orchestrators"] --> VALIDATE["Schema validates framework_type and mode"]
    VALIDATE --> DB[(OrchestratorInstance row in PostgreSQL)]
    DB --> POOL["Pool loads instance into memory"]
    POOL --> ENGINE{framework_type?}
    ENGINE -->|langgraph| LG["LangGraph engine — LangChain BaseChatModel"]
    ENGINE -->|openai_agents| OA["OpenAI Agents SDK — OpenAIChatCompletionsModel or LitellmModel"]
    LG --> CHAT["Chat request resolved"]
    OA --> CHAT

Two backends are active today. Each has its own mode support and provider compatibility:

FrameworkSupported modes (factory + API validation)Provider support (FRAMEWORK_SUPPORTED_PROVIDERS)
langgraphsupervisor, coordinator, handoff, groundedAll providers listed in cadence/core/constants/framework.py
openai_agentssupervisor, coordinator, handoff (not grounded)openai, litellm, bifrost

langgraph uses the LangChain ecosystem and supports stateful graphs with all registered providers. openai_agents uses the OpenAI Agents SDK and is limited to providers that implement the OpenAI chat completions interface.

openai → langgraph ✓ openai_agents ✓
anthropic → langgraph ✓ openai_agents ✗
claude → langgraph ✓ openai_agents ✗
google → langgraph ✓ openai_agents ✗
gemini → langgraph ✓ openai_agents ✗
azure → langgraph ✓ openai_agents ✗
groq → langgraph ✓ openai_agents ✗
litellm → langgraph ✓ openai_agents ✓
tensorzero → langgraph ✓ openai_agents ✗
bifrost → langgraph ✓ openai_agents ✓

Source: FRAMEWORK_SUPPORTED_PROVIDERS in cadence/core/constants/framework.py. You can query the supported providers for any framework at runtime via GET /api/frameworks/{framework_type}/supported-providers. Creating an orchestrator with a mismatched provider and framework will fail at LLM config validation.

FieldTypeNotes
instance_idUUID7Primary key
org_idUUID7Owning org
namestring5–200 characters
framework_typestringlanggraph | openai_agentsimmutable after creation
modestringsupervisor | coordinator | handoff | groundedimmutable after creation
statusstringactive | suspended | inactive
tierstringhot | demand — target pool tier
whoamistring | nullIdentity context injected into answer nodes as a system prompt
configJSONMutable Tier 4 settings (see below)
plugin_settingsJSONActive plugin settings map (pid → {id, name, settings})
config_hashstring | nullSHA-256 of the effective config — used as a pool cache invalidation key
is_readybooleantrue if the instance is currently loaded in the pool (computed at query time)
is_deletedbooleanSoft delete
created_atISO-8601UTC
updated_atISO-8601UTC

The config field is a JSON object with these common keys:

KeyTypePurpose
default_llm_config_idUUID stringLLM configuration to use for model calls
mode_configobjectMode-specific settings (see Orchestration modes)
mode_config.max_agent_hopsintMaximum recursive agent invocations before the graph terminates
mode_config.max_tool_roundsintMaximum rounds of tool calls per agent turn
mode_config.enabled_parallel_tool_callsbooleanAllow concurrent tool invocations within a single turn
mode_config.node_execution_timeoutintPer-node timeout in seconds
mode_config.enabled_llm_validationbooleanRun a secondary LLM pass to validate agent outputs

For grounded mode, additional keys apply: scope_rules, enabled_validator, message_context_window, max_context_window, and per-node overrides (router_node, planner_node, synthesizer_node, error_handler_node). See Orchestration modes for the full grounded config reference.

MethodPathPermissionDescription
POST/api/orgs/{org_id}/orchestratorscadence:org:orchestrators:writeCreate a new orchestrator instance
GET/api/orgs/{org_id}/orchestratorscadence:org:orchestrators:readList org orchestrators
GET/api/orgs/{org_id}/orchestrators/{instance_id}cadence:org:orchestrators:readGet full orchestrator details
PATCH/api/orgs/{org_id}/orchestrators/{instance_id}cadence:org:orchestrators:writeUpdate name, tier, whoami, default_llm_config_id
PATCH/api/orgs/{org_id}/orchestrators/{instance_id}/configcadence:org:orchestrators:writeReplace the mutable config object
PATCH/api/orgs/{org_id}/orchestrators/{instance_id}/statuscadence:org:orchestrators:writeSet status to active or suspended
GET/api/orgs/{org_id}/orchestrators/{instance_id}/graphcadence:org:orchestrators:readReturn Mermaid graph definition (loaded instances only)
DELETE/api/orgs/{org_id}/orchestrators/{instance_id}cadence:org:orchestrators:writeDeactivate (org_admin) or soft-delete (sys_admin)
DELETE/api/orgs/{org_id}/orchestrators/{instance_id}/purgecadence:system:adminPermanently delete a soft-deleted orchestrator
GET/api/frameworks/{framework_type}/supported-providersAuthenticatedQuery provider and mode support for a framework
  1. Create an LLM configuration for the org with your provider credentials and note its id. See LLM configuration.

  2. Confirm that your chosen provider is compatible with the framework. Use GET /api/frameworks/{framework_type}/supported-providers to check at runtime, or consult the compatibility table above.

  3. Post to POST /api/orgs/{org_id}/orchestrators with all required fields. Both framework_type and mode are immutable — choose them carefully.

  4. Load the instance into the pool when ready to serve traffic. See Hot-reload and AI App pool.

cadence/api/orchestrator/schemas.py
class CreateOrchestratorRequest(BaseModel):
name: str = Field(..., min_length=5, max_length=200)
framework_type: str = Field(
..., pattern="^(langgraph|openai_agents|google_adk)$"
)
mode: str = Field(
..., pattern="^(supervisor|coordinator|handoff|grounded)$"
)
active_plugin_ids: List[str] = Field(..., min_length=1)
tier: str = Field(default="demand", pattern="^(hot|demand)$")
whoami: Optional[str] = None
config: Optional[Dict[str, Any]] = Field(default_factory=dict)
POST /api/orgs/{org_id}/orchestrators
{
"name": "Customer Support Agent",
"framework_type": "langgraph",
"mode": "supervisor",
"active_plugin_ids": ["com.example.support-tools"],
"tier": "hot",
"config": {
"default_llm_config_id": "01936a8f-...",
"mode_config": {
"max_agent_hops": 25,
"max_tool_rounds": 3,
"enabled_parallel_tool_calls": true,
"node_execution_timeout": 60,
"enabled_llm_validation": false
}
}
}

is_ready is not stored in the database. It is computed at query time by asking the pool whether the instance is currently loaded:

cadence/api/orchestrator/crud.py
def _check_is_ready(pool, instance_id: str, status: str) -> bool:
if status != "active":
return False
if pool is None:
return False
return pool.is_loaded(instance_id)

An orchestrator with is_ready = false will reject chat requests with a 503. Load it first via POST /api/orgs/{org_id}/orchestrators/{instance_id}/load.

Every time the orchestrator config is updated, the platform recomputes a config_hash — a SHA-256 digest of the effective resolved configuration JSON. The pool uses this hash to detect stale loaded instances: when the hash of a loaded instance no longer matches the database row, the pool triggers an automatic reload on the next request.

name, tier, whoami, and default_llm_config_id can be patched individually without reloading the instance. The full config object can be replaced atomically via PATCH .../config. Neither operation interrupts currently-running conversations — the new config takes effect on the next pool load.

cadence/api/orchestrator/schemas.py
class UpdateOrchestratorMetadataRequest(BaseModel):
name: Optional[str] = Field(None, min_length=10, max_length=200)
tier: Optional[str] = Field(None, pattern="^(hot|demand)$")
whoami: Optional[str] = None
default_llm_config_id: Optional[str] = None
SymptomCauseFix
422 on create — framework_typeTypo or unsupported valueUse langgraph or openai_agents
422 on create — modeMode value not in allowed setUse supervisor, coordinator, handoff, or grounded
422 active_plugin_idsEmpty listProvide at least one plugin ID
ConfigurationError at pool loadframework_type + mode combination not in factory registrygrounded requires langgraph; verify the combination
is_ready = false after createInstance not yet loaded into poolCall POST .../load; see Hot-reload
503 on chatInstance not loadedLoad the instance first
Need to switch framework or modeBoth fields are immutableCreate a new orchestrator; update central points to reference it
409 on deleteOrchestrator is referenced by a central pointRemove central point references before deleting
410 Gone on getOrchestrator has been deactivatedReactivate via PATCH .../status with {"status": "active"}