Symfony Configuration¶
The public config root for packages/symfony is reserved as:
The initial subtree map is:
connectionsembeddingsextractionhttpeventsagent_ctrlagentssessionstelemetryloggingtestingdelivery
This is intentionally defined before the runtime wiring lands so later tasks can extend one coherent config tree instead of introducing multiple unrelated roots.
Baseline Runtime Shapes¶
The package now translates the core runtime subtrees into typed config objects owned by:
Cognesy\Polyglot\Inference\Config\LLMConfigCognesy\Polyglot\Embeddings\Config\EmbeddingsConfigCognesy\Instructor\Config\StructuredOutputConfigCognesy\Http\Config\HttpClientConfig
Preferred Symfony-oriented shapes are:
instructor:
connections:
default: openai
items:
openai:
driver: openai
api_key: '%env(OPENAI_API_KEY)%'
model: gpt-4o-mini
embeddings:
default: openai
connections:
openai:
driver: openai
model: text-embedding-3-small
extraction:
output_mode: tools
max_retries: 2
http:
driver: symfony
timeout: 20
connect_timeout: 5
For migration safety, the translator also tolerates flatter connection maps such as instructor.connections.openai and legacy llm.presets.* lookups.
The current Symfony config tree is strict about subtree shape for the shipped core runtime surface:
connections,embeddings,extraction,http, andeventsmust be arrays- flat connection maps are still normalized into
itemsorconnectionsinternally - provider-specific extra connection keys such as
temperatureare preserved and forwarded into runtime options - malformed subtree shapes now fail during Symfony config processing instead of silently degrading to defaults
HTTP Transport Rules¶
The baseline transport rules are:
instructor.http.driver: symfonyuses the bundle-owned HTTP contract and prefers Symfony'shttp_clientservice when it is availableinstructor.http.driver: frameworkandinstructor.http.driver: http_clientare normalized to the same Symfony transport- other driver values such as
curlorguzzlekeep using the configured bundled HTTP driver explicitly
This keeps transport selection explicit while avoiding request-lifecycle assumptions in CLI, worker, and test contexts.
Event Bridge Rules¶
The bundle now owns the CanHandleEvents service and uses a package-owned dispatcher with an optional Symfony parent bridge.
instructor.events.dispatch_to_symfony: truekeeps framework listeners connected through Symfony's event dispatcherinstructor.events.dispatch_to_symfony: falsekeeps runtime events inside the package-owned bus only
This keeps runtime listeners and wiretaps package-local while still allowing Symfony applications to observe the same event stream when they want to.
See packages/symfony/docs/delivery.md for the distinction between internal wiretaps, the projected progress bus, Symfony listener delivery, Messenger flows, and CLI observation helpers.
Session Persistence Config¶
The package now defines an explicit instructor.sessions subtree:
instructor:
sessions:
store: memory # memory | file
file:
directory: '%kernel.cache_dir%/instructor/agent-sessions'
Rules:
store: memorykeeps the native-agent runtime process-local and ephemeralstore: fileenables the first supported persisted adapter for Symfonyfile.directorycontrols where JSON session payloads and lock files are stored- applications that need a custom backend should replace
Cognesy\Agents\Session\Contracts\CanStoreSessionsin the container rather than expectingpackages/agentsto own Symfony-specific storage policy
Compatibility aliases currently accepted by the config tree:
sessions.driver->sessions.storesessions.session_store->sessions.storesessions.directory->sessions.file.directory
See packages/symfony/docs/sessions.md for the storage conventions, resume flow, and conflict semantics.
Telemetry Config¶
The package now defines a typed instructor.telemetry subtree that matches the shared telemetry package model instead of leaving exporter composition to application glue:
instructor:
telemetry:
enabled: false
driver: null # null | otel | langfuse | logfire | composite
service_name: symfony
projectors:
instructor: true
polyglot: true
http: true
agent_ctrl: true
agents: true
http:
capture_streaming_chunks: false
drivers:
composite:
exporters: [otel, langfuse]
otel:
endpoint: '%env(default::INSTRUCTOR_TELEMETRY_OTEL_ENDPOINT)%'
headers: { }
langfuse:
host: '%env(default::INSTRUCTOR_TELEMETRY_LANGFUSE_HOST)%'
public_key: '%env(default::INSTRUCTOR_TELEMETRY_LANGFUSE_PUBLIC_KEY)%'
secret_key: '%env(default::INSTRUCTOR_TELEMETRY_LANGFUSE_SECRET_KEY)%'
logfire:
endpoint: '%env(default::INSTRUCTOR_TELEMETRY_LOGFIRE_ENDPOINT)%'
write_token: '%env(default::INSTRUCTOR_TELEMETRY_LOGFIRE_WRITE_TOKEN)%'
headers: { }
Rules:
enabled: falsekeeps telemetry fully optional in the baseline package pathdriverselects exactly one exporter strategy for the package-owned telemetry service graphdriver: compositefans out to the ordereddrivers.composite.exporterslistprojectorscontrols which runtime surfaces should attach to the shared telemetry bridge once telemetry wiring is enabledhttp.capture_streaming_chunksexists because chunk capture is useful for debugging but too noisy for a hard-coded default
This gives later service-wiring and lifecycle-hook tasks a stable public root without forcing Symfony adopters to invent their own exporter config conventions first.
See packages/symfony/docs/telemetry.md for the runtime bridge, projector ownership, and lifecycle behavior that now hangs off this subtree.
See packages/symfony/docs/operations.md for how the same telemetry surface behaves in web, API, Messenger, and CLI app shapes.
AgentCtrl Config Model¶
The package now defines the public instructor.agent_ctrl config subtree and uses it to drive both builder registration and runtime-context policy.
Preferred shape:
instructor:
agent_ctrl:
enabled: false
default_backend: claude_code
defaults:
timeout: 300
working_directory: '%kernel.project_dir%'
sandbox_driver: host
execution:
transport: sync
allow_cli: true
allow_http: false
allow_messenger: true
continuation:
mode: fresh
session_key: agent_ctrl_session_id
persist_session_id: true
allow_cross_context_resume: true
backends:
claude_code:
model: claude-sonnet-4-20250514
codex:
model: codex
opencode:
model: anthropic/claude-sonnet-4-20250514
pi:
enabled: false
gemini:
enabled: false
Notes:
defaultscarries the shared builder options that map cleanly ontoAgentCtrlConfig:model,timeout,working_directory, andsandbox_driverexecutioncaptures framework policy for sync versus Messenger-triggered execution and whether CLI, HTTP, or Messenger entrypoints are allowedcontinuationcaptures the default continuation mode, session-key naming, and whether explicitresume_sessionhandoff is allowed across runtime contextsbackendsholds per-agent overrides forclaude_code,codex,opencode,pi, andgemini
Runtime services driven by this config:
Cognesy\Instructor\Symfony\AgentCtrl\SymfonyAgentCtrlis the backend-aware builder entrypointCognesy\Instructor\Symfony\AgentCtrl\SymfonyAgentCtrlRuntimesbuilds context adapters forcli,http, andmessenger- named service IDs exist for
instructor.agent_ctrl.runtime.cli,instructor.agent_ctrl.runtime.http, andinstructor.agent_ctrl.runtime.messenger - CLI and HTTP runtimes only allow inline
execute()/executeStreaming()whenexecution.transportissync - the Messenger runtime can still execute inline inside a worker process even when the app-level transport is
messenger - runtime adapters expose
continuationPolicy(),continuation(),continueLast(),resumeSession(), andhandoff()so Symfony code can create same-contextcontinue_lastreferences locally and pass explicitresume_sessionhandoff references from web or CLI entrypoints into Messenger workers
Compatibility aliases currently accepted by the config tree:
default_agent->default_backenddirectory->working_directorysandbox->sandbox_driver- top-level backend keys such as
agent_ctrl.codex.*are normalized intoagent_ctrl.backends.codex.*
This keeps the public Symfony shape explicit while still tolerating the flatter Laravel-style config keys during migration and early package adoption.
Delivery Config¶
The package now defines a typed instructor.delivery subtree for progress projection, optional CLI observation, and Messenger-backed async work:
instructor:
delivery:
progress:
enabled: true
cli:
enabled: false
use_colors: true
show_timestamps: true
messenger:
enabled: true
bus_service: message_bus
observe_events:
- App\Runtime\Event\ProjectCompleted
- Cognesy\Agents\Session\Events\SessionSaved
Rules:
delivery.progress.enabled: truekeeps the projected progress bus attached to the package-owned runtime busdelivery.cli.enabled: trueauto-attaches the built-in console observer to the projected progress busdelivery.cli.use_colorsanddelivery.cli.show_timestampscontrol the package-owned CLI printerdelivery.messenger.enabled: trueenables the package-owned Messenger delivery seamdelivery.messenger.bus_servicepoints the observation bridge at the Symfony bus service that should receive runtime observation messagesdelivery.messenger.observe_eventsis an explicit allow-list of runtime event classes or interfaces that should be forwarded asRuntimeObservationMessageenvelopes
The package-owned delivery entrypoints are:
Cognesy\Instructor\Symfony\Delivery\Progress\Contracts\CanHandleProgressUpdatesCognesy\Instructor\Symfony\Delivery\Progress\RuntimeProgressUpdateCognesy\Instructor\Symfony\Delivery\Cli\SymfonyCliObservationFormatter-
Cognesy\Instructor\Symfony\Delivery\Cli\SymfonyCliObservationPrinter -
Cognesy\Instructor\Symfony\Delivery\Messenger\ExecuteAgentCtrlPromptMessage Cognesy\Instructor\Symfony\Delivery\Messenger\ExecuteNativeAgentPromptMessageCognesy\Instructor\Symfony\Delivery\Messenger\RuntimeObservationMessageCognesy\Instructor\Symfony\Delivery\Messenger\ExecuteAgentCtrlPromptMessageHandlerCognesy\Instructor\Symfony\Delivery\Messenger\ExecuteNativeAgentPromptMessageHandler
This keeps queue dispatch and progress projection explicit:
- applications dispatch package-owned execution messages instead of treating Symfony listeners as an async transport
- applications can consume
CanHandleProgressUpdateswhen they need transport-agnostic lifecycle or streaming updates for SSE, WebSockets, Mercure, or polling - CLI output stays opt-in and builds on the same projected progress bus instead of formatting the raw runtime bus directly
- the internal event bus remains the source of truth and can optionally forward selected runtime events into Messenger for queued observation work
- Messenger delivery remains separate from the projected progress bus so async work does not depend on console-formatting assumptions
See packages/symfony/docs/operations.md for the recommended production shape for web, API, Messenger, and CLI applications.
See packages/symfony/docs/migration.md for moving existing app-local Symfony delivery wiring onto this supported subtree.
Native Agent Extension Rules¶
The native-agent surface now supports two complementary extension modes.
Autoconfiguration:
- services implementing
Cognesy\Agents\Tool\Contracts\ToolInterfaceare autoconfigured into the native tool registry - services implementing
Cognesy\Agents\Builder\Contracts\CanProvideAgentCapabilityare autoconfigured into the capability registry - services whose class is
Cognesy\Agents\Template\Data\AgentDefinitionare autoconfigured into the definition registry - services whose class is
Cognesy\Instructor\Symfony\Agents\SchemaRegistrationare autoconfigured into the schema registry
Manual tags remain supported through Cognesy\Instructor\Symfony\Agents\AgentRegistryTags:
AgentRegistryTags::TOOLSAgentRegistryTags::CAPABILITIESAgentRegistryTags::DEFINITIONSAgentRegistryTags::SCHEMAS
Use autoconfiguration when the service type already makes the contribution obvious.
Use manual tags when you need an explicit override, especially for capability-name overrides via the alias tag attribute.
Example:
services:
App\Ai\Tools\SearchDocsTool:
autowire: true
autoconfigure: true
app.ai.capability.review:
class: App\Ai\Capability\ReviewCapability
autowire: true
tags:
- { name: instructor.agent.capability, alias: review }
Ownership Boundaries¶
packages/symfony owns:
- Symfony bundle registration
- config normalization and framework-facing defaults
- container wiring and service aliases
- migration of scattered Symfony glue into one supported framework package
Other packages keep their core responsibilities:
packages/eventskeeps reusable event-bus primitives, including the raw Symfony bridgepackages/loggingkeeps reusable logging primitives and factories- core runtime packages keep domain logic;
packages/symfonyonly provides framework integration
This boundary keeps the Symfony package batteries-included without duplicating core runtime logic.
Use packages/symfony/docs/migration.md when moving older event, logging, telemetry, or delivery glue under that boundary.