Appearance
Workflow
A workflow selects an entry subroutine and defines the Python environment and agent resources in which it executes. Several workflows may select the same subroutine with different dependencies, profiles, or sessions.
Entry contract
The generated workflow declaration exposes the selected subroutine's Input, Output, and Params. Those types originate in the subroutine's boundary module. The subroutine owns the graph and its behavior; the workflow supplies its execution environment.
An entry SubroutineCall identifies that graph and maps the subroutine's profile and session parameters to workflow resources. A workflow_call executes the workflow in its own process and Python environment. A subroutine_call executes the selected graph in the caller's process and environment.
Generated interface
The workflow's generated ◆ __init__.py imports the entry subroutine's Input, Output, Params, and StateScope and exposes:
| Declaration | Role |
|---|---|
GRAPH_ID | The workflow's canonical identifier. |
ENTRY | A SubroutineCall naming the entry graph and its profile and session argument mappings. |
PARAMS_TYPES | Maps (project_path, graph_id) pairs to parameter types for the entry subroutine and every subroutine reachable through in-process calls. |
RuntimeOptions | An empty, immutable generated dataclass. Provider settings come from project.json. |
configure(options, /) | Returns the generated CONFIGURATION. |
definition() | A cached factory combining the entry, input type, workflow sessions, and configuration in WorkflowDefinition[Input, Output, Params, StateScope]. |
Profile declarations and registry
Each workflow-owned profile exports an INVOKER from profiles/<profile>/__init__.py. The generated profiles/__init__.py collects these invokers in INVOKERS, a mapping from AgentProfileId to AgentInvoker. A workflow with no profiles exports an empty mapping, {}.
The generated bindings/__init__.py constructs CONFIGURATION = WorkflowConfiguration(profile_arguments=INVOKERS). ENTRY.profile_arguments maps entry-subroutine parameter IDs to profile IDs in this configuration. The concrete provider declarations are described on the Profile page.
Session declarations and registry
Each workflow-owned session exports a SESSION declaration from sessions/<session>/__init__.py, recording its identifier, name, and persistence policy. The generated sessions/__init__.py collects those declarations in the SESSIONS tuple, which the workflow factory passes to WorkflowDefinition.sessions. A workflow with no owned sessions exports ().
ENTRY.session_arguments maps entry-subroutine session parameters to these workflow-owned sessions. A session declared locally by the subroutine needs no entry mapping. When there are no resource parameters to bind, the corresponding entry argument mapping is empty. Session declarations and lifetimes are described on the Session page.
Authored files
Requirements
Every local workflow owns an authored ● requirements.txt. Write one PEP 508 requirement per line; blank lines and lines beginning with # are ignored.
Pip directives such as -r and --index-url are not supported, and the same distribution cannot be declared twice. The published verdog-runtime package is a project dependency and does not belong in this file.
Each workflow executes in a Python environment containing the runtime and its own requirements, including workflows imported from other projects. Update that environment's dependencies after changing requirements.txt.
The root ◆ pyproject.toml describes the shared source package and does not merge workflow requirements.
Runtime configuration
Runtime configuration has no authored module. The workflow's profile and session declarations, together with its entry-resource mappings, determine the generated configuration. Each entry mapping names a parameter on the selected subroutine and the workflow resource supplied for it. Locally owned subroutine resources remain part of the graph. Profile scope determines the available bindings; session persistence and lifetime determine conversation reuse.
Files
Each local workflow has generated declarations and an authored dependency file. Root workflows live directly under the project package:
General workflow structure
src/<package>/workflows/<workflow>/
├── ◆ __init__.py
├── profiles/
│ ├── ◆ __init__.py
│ └── <profile>/
│ └── ◆ __init__.py
├── sessions/
│ ├── ◆ __init__.py
│ └── <session>/
│ └── ◆ __init__.py
├── ◆ bindings/__init__.py
└── ● requirements.txt
A workflow declared inside a subroutine repeats the same layout under subroutines/<owner>/workflows/<id>/.
| File | Role |
|---|---|
◆ __init__.py | Declares the entry call, boundary types, reachable parameter types, and cached definition() factory. |
◆ profiles/<profile>/__init__.py | Constructs one provider invoker. |
◆ profiles/__init__.py | Maps workflow profile IDs to invokers in INVOKERS. |
◆ sessions/<session>/__init__.py | Declares one workflow-owned session and its persistence policy. |
◆ sessions/__init__.py | Collects workflow sessions in SESSIONS. |
◆ bindings/__init__.py | Constructs CONFIGURATION from the workflow's invoker registry. |
● requirements.txt | Declares the third-party Python packages installed only in this workflow's environment. |
An external workflow binding has no local workflow directory. Its alias and pinned target live in project.json and ◆ verdog.lock.
Countdown example
Source: ◆ workflows/main/__init__.py
python
from __future__ import annotations
from dataclasses import dataclass
from functools import cache
from typing import Final
from .bindings import CONFIGURATION
from .sessions import SESSIONS
from verdog_runtime.declarations import (
ParameterType,
SubroutineCall,
WorkflowConfiguration,
WorkflowDefinition,
)
from verdog_runtime.declarations.ids import GraphId, AgentProfileId
from demo.countdown.subroutines.main import (
GRAPH_ID as ENTRY_GRAPH_ID,
Input,
Output,
Params,
StateScope,
)
PARAMS_TYPES: Final[dict[tuple[str, GraphId], ParameterType]] = {
('.', ENTRY_GRAPH_ID): Params,
}
@dataclass(frozen=True, slots=True, kw_only=True)
class RuntimeOptions:
pass
def configure(options: RuntimeOptions, /) -> WorkflowConfiguration:
return CONFIGURATION
GRAPH_ID: Final[GraphId] = GraphId('demo.countdown.main')
ENTRY: Final[SubroutineCall] = SubroutineCall(
definition_id=ENTRY_GRAPH_ID,
definition_module='demo.countdown.subroutines.main',
project_path='.',
params_types=PARAMS_TYPES,
profile_arguments={
AgentProfileId('decrementer'): AgentProfileId('decrementer'),
},
session_arguments={},
)
@cache
def definition() -> WorkflowDefinition[Input, Output, Params, StateScope]:
return WorkflowDefinition(
id=GRAPH_ID,
input_type=Input,
entry=ENTRY,
sessions=SESSIONS,
configuration=CONFIGURATION,
)Countdown selects the main subroutine and exposes its Count input and output and empty Params type. The declarations come from the workflow configuration in project.json.
ENTRY maps the subroutine's decrementer parameter to the workflow profile of the same name. PARAMS_TYPES identifies the parameter type by project path and graph ID. The cached definition() combines the entry with the workflow's sessions and configuration. RuntimeOptions is empty, and the generated configure() returns the same CONFIGURATION.
Source: ◆ workflows/main/profiles/__init__.py
python
from typing import Final
from verdog_runtime.declarations import AgentInvoker
from verdog_runtime.declarations.ids import AgentProfileId
from demo.countdown.workflows.main.profiles.decrementer import (
INVOKER as DECREMENTER_INVOKER,
)
INVOKERS: Final[dict[AgentProfileId, AgentInvoker]] = {
AgentProfileId('decrementer'): DECREMENTER_INVOKER,
}The profile registry imports the invoker from the decrementer profile and associates it with the workflow profile ID. ENTRY.profile_arguments selects this ID for the child parameter.
Source: ◆ workflows/main/bindings/__init__.py
python
from typing import Final
from demo.countdown.workflows.main.profiles import INVOKERS
from verdog_runtime.declarations import WorkflowConfiguration
CONFIGURATION: Final[WorkflowConfiguration] = WorkflowConfiguration(
profile_arguments=INVOKERS,
)The bindings module passes the invoker registry to WorkflowConfiguration. This is the configuration imported by the workflow declaration above.
Source: ◆ workflows/main/sessions/__init__.py
python
from typing import Final
SESSIONS: Final = ()The workflow owns no sessions. The subroutine owns the persistent countdown session, so ENTRY.session_arguments is also empty.
Source: ● workflows/main/requirements.txt
text
jinja2>=3.1,<4This installs the template library in the workflow's environment. At execution, the Agent visit renders its prompt with Jinja2 and sends it through the mapped Codex invoker.