kaboo_workflows.config¶
Config schema, loaders, interpolation, and infrastructure resolution.
kaboo_workflows.config ¶
YAML configuration loading, validation, and resolution.
ResolvedConfig
dataclass
¶
ResolvedConfig(
*,
agents=dict(),
orchestrators=dict(),
entry,
mcp_lifecycle=MCPLifecycle(),
app_config=None,
)
Fully resolved config — lifecycle started, agents ready.
After calling :func:~kaboo_workflows.config.loaders.load, use
:meth:wire_event_queue to set up event streaming::
resolved = load("config.yaml")
event_queue = resolved.wire_event_queue()
wire_event_queue ¶
wire_event_queue(*, session_id=None, tool_labels=None)
Wire all agents and orchestrators for event streaming.
This is the recommended way to set up event streaming. It:
- Builds a :class:
~kaboo_workflows.types.SessionManifestfrom the resolved runtime objects. - Auto-resolves stream groups from the original AppConfig so every agent is attributed to a hierarchical dot-path group.
- Collects tool labels from MCP clients and agents in the original AppConfig (agent-level overrides MCP-level).
- Wires every agent (and orchestrator) with an
:class:
~kaboo_workflows.hooks.EventPublishervia :func:~kaboo_workflows.wire.make_event_queue. - Emits a SESSION_START event carrying the manifest as the first event on the queue.
.. warning::
This **mutates** the agents and orchestrators stored on this
instance by adding hooks and overwriting ``callback_handler``.
Call it only once per ``ResolvedConfig`` instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id
|
str | None
|
Optional session ID to embed in events. |
None
|
tool_labels
|
dict[str, str] | None
|
Optional runtime tool name → display label mapping. These take highest priority, overriding both agent-level and MCP-client-level labels from YAML config. |
None
|
Returns:
| Type | Description |
|---|---|
EventQueue
|
A ready-to-use :class: |
EventQueue
|
SESSION_START already on it. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the entry node cannot be resolved by object identity. |
Source code in src/kaboo_workflows/config/resolvers/config.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
ResolvedInfra
dataclass
¶
ResolvedInfra(
models=dict(),
clients=dict(),
mcp_lifecycle=MCPLifecycle(),
)
Infrastructure resolved from config — lifecycle NOT started.
This is the pure result of :func:resolve_infra. Lifecycle is cold,
agents are not yet created.
Session managers are NOT stored here — they are built per agent and per
orchestration at session time, from config.session_manager (the global
def) plus effective_session_id computed by load_session.
Use :func:~kaboo_workflows.config.loaders.load for a fully
activated system, or manually::
infra = resolve_infra(config)
infra.mcp_lifecycle.start()
agents = resolve_agents(agent_defs=config.agents, ...)
AgentDef ¶
Bases: BaseModel
Top-level agent definition.
All agents are defined flat under the agents: section.
Multi-agent orchestration is configured separately in the orchestrations: section.
tools accepts spec strings:
"module.path:function_name"— single function from module"module.path"— all@toolfunctions in module"./path/to/file.py:function_name"— single function from file"./path/to/file.py"— all@toolfunctions in file"./path/to/dir/"— all@toolfunctions in directory
hooks accepts import-path strings ("module.path:ClassName" or
"./file.py:ClassName") or inline :class:HookDef objects with
explicit type + optional params.
type
class-attribute
instance-attribute
¶
type = None
Custom agent factory import path.
Format: module.path:ClassName or ./file.py:ClassName.
When set, the factory is called instead of strands.Agent() directly.
The agent_kwargs dict is spread as **kwargs to this factory.
agent_kwargs
class-attribute
instance-attribute
¶
agent_kwargs = Field(default_factory=dict)
Additional keyword arguments passed to strands.Agent() or custom factory.
Valid Agent parameters: messages, callback_handler, record_direct_tool_call, trace_attributes, state, plugins, structured_output_prompt, structured_output_model, tool_executor, retry_strategy, concurrent_invocation_mode, load_tools_from_directory.
Warning: Use at your own risk — no schema-level validation is performed. Agent.init has 24 explicit parameters and no **kwargs; any invalid key will raise TypeError at construction time.
AppConfig ¶
Bases: BaseModel
Root YAML configuration.
Orchestrations are defined as a dict of named orchestration blocks that can reference each other for arbitrary nesting.
version
class-attribute
instance-attribute
¶
version = '1'
Schema version — omit to use the default "1".
history
class-attribute
instance-attribute
¶
history = False
Global default for per-agent history:.
Applies to any agent that does not set its own history:. Defaults to
False so sub-agents are stateless per run unless opted in. The entry
agent is unaffected — its transcript is always the CopilotKit chat.
attachments
class-attribute
instance-attribute
¶
attachments = Field(default_factory=AttachmentsDef)
Global reference/attachment policy (manifest + optional resolver tool).
runtime
class-attribute
instance-attribute
¶
runtime = Field(default_factory=RuntimeDef)
Runtime behavior toggles (per-invocation agent overrides, …).
telemetry
class-attribute
instance-attribute
¶
telemetry = Field(default_factory=TelemetryDef)
Opt-in OpenTelemetry tracing (see :class:TelemetryDef).
ConversationManagerDef ¶
Bases: BaseModel
Conversation manager configuration.
type must be a module.path:ClassName import path or a
./file.py:ClassName file-based import path. The resolver raises
ValueError if there is no colon separator. params are forwarded
as constructor kwargs.
Built-in strands classes:
strands.agent:SlidingWindowConversationManagerstrands.agent:SummarizingConversationManagerstrands.agent:NullConversationManager
DelegateConnectionDef ¶
Bases: BaseModel
A delegation connection: orchestrator calls agent as a tool.
DelegateOrchestrationDef ¶
Bases: BaseModel
Delegate mode: entry agent calls other agents as tools.
A new Agent is constructed from the entry_name agent's blueprint
(model, system_prompt, hooks, tools, etc.) with delegate tools added for
each connection. The original agent is never mutated.
Entry point is explicit via entry_name (consistent with swarm/graph).
agent_kwargs is merged over the entry agent's agent_kwargs —
orchestration values win on conflict, unset keys are inherited.
agent_kwargs
class-attribute
instance-attribute
¶
agent_kwargs = Field(default_factory=dict)
Merged over the entry agent's agent_kwargs (orchestration wins).
Common uses: system_prompt, callback_handler, conversation_manager.
reference_fields
classmethod
¶
reference_fields()
Return mapping of JSON paths to reference types for name sanitization.
Source code in src/kaboo_workflows/config/schema.py
477 478 479 480 481 482 483 | |
GraphEdgeDef ¶
Bases: BaseModel
An edge in a graph orchestration.
GraphOrchestrationDef ¶
Bases: BaseModel
Graph mode: DAG-based orchestration with conditional edges.
Agents execute in parallel batches based on dependency order. Uses strands Graph under the hood.
chat_output names the node whose streamed text becomes the assistant
reply when the graph is a first-class AG-UI entry. When omitted, the
adapter streams the sole terminal node (a node with no outgoing edges) if
there is exactly one, otherwise it emits the final node's text on
completion.
node_ids ¶
node_ids()
Return every node id referenced by this graph (entry + edge ends).
Source code in src/kaboo_workflows/config/schema.py
561 562 563 564 565 566 567 | |
terminal_nodes ¶
terminal_nodes()
Return nodes with no outgoing edge (candidate chat-output nodes).
Source code in src/kaboo_workflows/config/schema.py
569 570 571 572 | |
reference_fields
classmethod
¶
reference_fields()
Return mapping of JSON paths to reference types for name sanitization.
Source code in src/kaboo_workflows/config/schema.py
584 585 586 587 588 589 590 591 592 | |
HookDef ¶
Bases: BaseModel
Hook provider reference.
type must be a module.path:ClassName import path or a
./file.py:ClassName file-based import path. The resolver raises
ValueError if there is no colon separator. params are forwarded
as constructor kwargs.
MCPClientAuthDef ¶
Bases: BaseModel
Outbound auth for an MCP client (streamable-http / SSE only).
type selects a built-in strategy from
:func:~kaboo_workflows.auth.build_auth:
relay— forward the inbound caller token (see :class:~kaboo_workflows.auth.RelayTokenAuth).obo— AgentCore On-Behalf-Of exchange (:class:~kaboo_workflows.auth.OBOTokenAuth); requiresprovider. Provider-specific differences stay inparams:workload_nameto mint the workload access token from the inbound user token, andcustom_parametersfor anything the identity provider expects on the exchange (an Entra ID provider wantsrequested_token_use: on_behalf_of).m2m— client-credentials machine token (:class:~kaboo_workflows.auth.M2MClientCredentialsAuth).static— a fixed token (:class:~kaboo_workflows.auth.StaticTokenAuth).
params are forwarded as constructor kwargs to the chosen strategy.
header accepts a list as well as a single name, which is what a managed
AgentCore Gateway needs: it authenticates the caller on Authorization
and then replaces that header with its own outbound credential, so a token
the target must also see has to be sent twice.
Note: relay / obo derive from the per-request caller identity and
are reliable only when the MCP client is created per request (started inside
the request context). On a long-lived shared client (create_agui_app),
prefer static / m2m (machine identity) unless the deployment is
process-per-session (e.g. AgentCore Runtime).
MCPClientDef ¶
Bases: BaseModel
MCP client connection definition.
Exactly one of server, url, or command must be set.
params are forwarded to strands MCPClient (e.g., startup_timeout,
tool_filters, prefix). transport_options are forwarded to the
transport factory (e.g., headers, auth, timeout, http_client). auth
declaratively attaches an outbound auth strategy (see
:class:MCPClientAuthDef).
MCPServerDef ¶
Bases: BaseModel
MCP server definition.
ModelDef ¶
Bases: BaseModel
LLM model configuration.
SessionManagerDef ¶
Bases: BaseModel
Session manager configuration.
Built-in providers: "file", "s3", "agentcore".
For a custom class, set type to an import path
("module.path:ClassName"). The class must be a subclass of
strands.session.SessionManager. When type is set, provider
is ignored.
Session ID resolution order
- Runtime override (e.g., HTTP session header)
params.session_id- Random UUID (fresh session per CLI run)
SwarmOrchestrationDef ¶
Bases: BaseModel
Swarm mode: collaborative handoffs between peer agents.
Agents transfer control to each other via handoff_to_agent tool. Uses strands Swarm under the hood.
chat_output names the member agent whose streamed text becomes the
assistant reply when the swarm is a first-class AG-UI entry. When omitted,
the adapter emits the final active node's text once the run completes.
reference_fields
classmethod
¶
reference_fields()
Return mapping of JSON paths to reference types for name sanitization.
Source code in src/kaboo_workflows/config/schema.py
518 519 520 521 522 523 524 525 | |
interpolate ¶
interpolate(raw, *, variables=None, env=None)
Interpolate ${VAR} and ${VAR:-default} references in a YAML config dict.
Lookup order: variables dict -> env dict -> default value -> raise error.
$${ escapes to a literal ${, so config text (e.g. a system prompt
quoting template syntax from another language) can contain ${...}
without being treated as a variable reference. A $$ not followed by
{ is left untouched. The escape applies to config values, not to
entries inside the vars: block itself.
Uses a two-pass strategy to resolve cross-variable references inside the
vars: block before interpolating the rest of the config. Pass 1
resolves each var against env only; Pass 2 resolves each var sequentially
against the pass-1 results so chains like B: "${A}y" work correctly.
Any ${VAR} pattern still present after two passes indicates a circular
or undefined reference and raises ValueError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
dict[str, Any]
|
Raw parsed YAML dict (will not be mutated — returns a new dict). |
required |
variables
|
dict[str, Any] | None
|
User-defined variables (from the |
None
|
env
|
dict[str, str] | None
|
Environment variables (defaults to |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
New dict with all string values interpolated. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a variable is referenced but not found and has no default, or if a circular reference is detected in the vars block. |
Source code in src/kaboo_workflows/config/interpolation.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |
strip_anchors ¶
strip_anchors(raw)
Remove x-* keys (YAML anchor scratch pads) from the top level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
dict[str, Any]
|
Raw parsed YAML dict. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
New dict without top-level |
Source code in src/kaboo_workflows/config/interpolation.py
77 78 79 80 81 82 83 84 85 86 | |
load ¶
load(config)
Load config from file(s) or YAML string(s) and resolve to live objects.
This is the main entry point for zero-code usage.
Accepts a single source or a list of sources. Each source is either
a file path (str or Path) or a raw YAML string. File paths
are detected by checking if the path exists on disk; anything else
is parsed as inline YAML.
Pipeline:¶
- Parse each source (file read or inline YAML)
- Per-source: strip anchors, interpolate variables
- Sanitize collection keys (spaces/special chars -> underscores)
- Merge sources (if multiple), detect duplicate names
- Validate against schema (Pydantic)
- Resolve infrastructure (models, MCP — pure; no session manager)
- Start MCP servers (so clients can connect)
- Create agents (Agent.init auto-starts MCP clients)
- Wire orchestration / entry point
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ConfigInput | list[ConfigInput]
|
File path, raw YAML string, or list of either. |
required |
Returns:
| Type | Description |
|---|---|
ResolvedConfig
|
ResolvedConfig with agents, entry (callable), and mcp_lifecycle. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
Config file doesn't exist. |
ConfigurationError
|
Invalid YAML syntax, schema validation failure, or invalid references. |
REMARKS:¶
When multiple sources are provided, collection sections (agents,
models, mcp_servers, mcp_clients, orchestrations) are
merged. Duplicate names within the same section raise ValueError.
Singleton fields (entry, session_manager, log_level) use
last-wins semantics.
Side effect: this function starts MCP servers during resolution.
Agent.__init__ auto-starts MCP clients (via process_tools() ->
MCPClient.load_tools()), and those clients need running servers to
connect to. MCPLifecycle.start() is called before agent
creation to satisfy this dependency.
MCPLifecycle.start() is idempotent, so the caller's context
manager (async with resolved.mcp_lifecycle:) is a no-op on enter
but still required for graceful shutdown — __aexit__ stops
clients first, then servers.
Source code in src/kaboo_workflows/config/loaders/loaders.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
load_config ¶
load_config(config)
Parse and validate config from file(s) or YAML string(s).
Accepts a single source or a list. Each source is auto-detected:
Pathobjects are always treated as file paths.strvalues are treated as file paths if the file exists on disk; otherwise they are parsed as inline YAML content.
When multiple sources are provided, their collection sections
(agents, models, mcp_servers, mcp_clients,
orchestrations) are merged. Duplicate names within the same
section raise ValueError. Singleton fields (entry,
session_manager, log_level) use last-wins semantics.
Each source's vars: block is applied only to that source
(interpolation is per-source).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ConfigInput | list[ConfigInput]
|
File path, raw YAML string, or list of either. |
required |
Returns:
| Type | Description |
|---|---|
AppConfig
|
Validated AppConfig instance. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
A |
ConfigurationError
|
Invalid YAML, schema validation failure, or invalid references. |
Source code in src/kaboo_workflows/config/loaders/loaders.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | |
load_session ¶
load_session(config, infra, *, session_id=None)
Create agents and orchestration from already-started infrastructure.
This is the session-level counterpart to :func:load. Use it when
you want to share MCP servers across multiple sessions (e.g. one
session per HTTP request) while creating isolated agents per
session.
infra does NOT carry a session manager; instances are built per
agent/orchestration from config.session_manager plus an
effective_session_id computed here.
Typical server pattern::
app_config = load_config("config.yaml")
infra = resolve_infra(app_config)
infra.mcp_lifecycle.start()
# Per request:
resolved = load_session(app_config, infra, session_id="abc")
infra.mcp_lifecycle must already be started before calling this.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AppConfig
|
The validated AppConfig. |
required |
infra
|
ResolvedInfra
|
Resolved infrastructure with servers already started. |
required |
session_id
|
str | None
|
Optional runtime session ID. Combined with
|
None
|
Returns:
| Type | Description |
|---|---|
ResolvedConfig
|
ResolvedConfig with freshly created agents and entry point. |
Note
No SessionManager instance is built in this function; instances
are constructed at the leaves (build_agent_from_def,
OrchestrationBuilder._build_one).
Source code in src/kaboo_workflows/config/loaders/loaders.py
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
load_session_config ¶
load_session_config(
base_raw, overlay=None, *, allowed_mcp_hosts=None
)
Validate a per-run config layered over the service's base config.
This is how a run brings its own workflow — its own agents, delegates, graph
or swarm — without the service restarting or reading a file. The overlay
replaces the base's agents / orchestrations / entry and adds to its
models / mcp_clients; see
:func:~kaboo_workflows.config.loaders.helpers.merge_session_overlay.
Interpolation runs on the overlay as its own source, so a ${API_KEY} in a
database-authored config resolves from this process's environment. The secret
never has to live wherever the config was written.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_raw
|
dict
|
The service's config, parsed once by :func: |
required |
overlay
|
ConfigInput | None
|
The submitted config — normally a raw YAML string. |
None
|
allowed_mcp_hosts
|
list[str] | None
|
Hosts an overlay-declared MCP client URL may point at.
|
None
|
Returns:
| Type | Description |
|---|---|
AppConfig
|
Validated AppConfig for this run. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
Invalid YAML, schema failure, bad references, or an MCP client the overlay is not permitted to declare. |
Source code in src/kaboo_workflows/config/loaders/loaders.py
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | |
parse_config_sources ¶
parse_config_sources(config)
Parse and merge config source(s) into a processed raw dict.
The first half of :func:load_config, split out so a server can parse its
base config once at boot and reuse the result for every run — see
:func:load_session_config. Interpolation has already happened, so the
returned dict may contain resolved secrets; it is not for logging.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ConfigInput | list[ConfigInput]
|
File path, raw YAML string, or list of either. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
The merged raw config dict. |
Source code in src/kaboo_workflows/config/loaders/loaders.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
validate_raw_config ¶
validate_raw_config(raw)
Normalize, schema-validate and reference-check a raw config dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
dict
|
A processed raw config dict from :func: |
required |
Returns:
| Type | Description |
|---|---|
AppConfig
|
Validated AppConfig instance. |
Raises:
| Type | Description |
|---|---|
ConfigurationError
|
Schema validation failure, or invalid references. |
Source code in src/kaboo_workflows/config/loaders/loaders.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
resolve_infra ¶
resolve_infra(config)
Resolve infrastructure from an AppConfig (pure, no I/O).
Creates model objects, MCP server/client objects, and a lifecycle manager. Nothing is started.
Resolution order:
- Models (no dependencies)
- MCP servers (no dependencies)
- MCP clients (depend on servers)
- MCP lifecycle (assembles servers + clients, not started)
- Session manager validation only —
agentcoreprovider rejected globally; no instance is constructed (instances are built per-leaf at session time).
Agents and orchestration are resolved in :func:load after
mcp_lifecycle.start() because Agent.__init__ auto-starts
MCP clients which need servers to be running first. The lifecycle
start in load() is idempotent — the context manager is still
used for graceful shutdown.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AppConfig
|
Parsed AppConfig from YAML. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
ResolvedInfra
|
class: |
Source code in src/kaboo_workflows/config/resolvers/config.py
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | |
resolve_run_clients ¶
resolve_run_clients(config, infra)
Resolve this run's MCP clients as fresh objects it exclusively owns.
Client objects are cheap and hold no connection until an agent registers them as a tool provider, so a run can afford its own. Owning them is what makes the session's lifetime the connection's lifetime: the returned lifecycle is stopped when the run ends, and a session that cannot outlive its run cannot go stale between runs.
Servers are not re-resolved — those are processes, owned by the boot-time
lifecycle in infra — so a stdio client in config binds to the
already-running server of the same name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
AppConfig
|
The config for this run, which may declare clients the base config does not. |
required |
infra
|
ResolvedInfra
|
The process-wide infra, consulted for running servers. |
required |
Returns:
| Type | Description |
|---|---|
MCPLifecycle
|
A lifecycle holding only this run's clients, not yet started. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a |
Source code in src/kaboo_workflows/config/resolvers/config.py
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | |