Upgrade
Upgrading to 2.7¶
StructuredOutputConfigBuilder removed¶
Cognesy\Instructor\Creation\StructuredOutputConfigBuilder is gone. It was a mutable second
representation of the same settings, kept in sync with StructuredOutputConfig by hand. Build
configs directly instead — every with*() method has the same name on StructuredOutputConfig,
which returns a new instance rather than mutating, so existing chains carry over by dropping the
trailing ->create():
// before
$config = (new StructuredOutputConfigBuilder())
->withOutputMode(OutputMode::Json)
->withMaxRetries(2)
->create();
// after
$config = (new StructuredOutputConfig())
->withOutputMode(OutputMode::Json)
->withMaxRetries(2);
// @doctest id="c3a6"
The builder's withConfig($defaults) seed has no replacement: start the chain from that config
($defaults->withMaxRetries(2)). StructuredOutputConfig gained withSchemaDescription() and
withStreamMaterializationInterval(), so every former builder method has a namesake with two
exceptions: withThrowOnTransformationFailure() and withDefaultToStdClass() have no fluent
equivalent on StructuredOutputConfig. Both were @deprecated 2.5 no-ops — transformation
failures always fail the attempt, and defaultToStdClass was superseded by per-request
intoStdClass() — and both are still accepted as named arguments to the constructor and to
with(), so config round-trips keep working:
One semantic difference to check: the builder's create() merged modePromptClasses into the
defaults, whatever route set them. On StructuredOutputConfig, withModePromptClass() merges a
single mode into the existing map — the common case, unchanged — but withModePromptClasses() and
the constructor argument replace the map wholesale. If you were passing a partial map expecting
the remaining modes to keep their defaults, merge explicitly:
$config = $config->withModePromptClasses([...$config->modePromptClasses(), ...$overrides]);
// @doctest id="420b"
In cognesy/instructor-agents, StructuredOutputPolicy::withConfigBuilder() is accordingly
renamed to withConfig() and now takes and returns a StructuredOutputConfig.
CanEmitStreamingUpdates renamed to CanDriveExecution¶
The internal execution-driver contract implemented by SyncExecutionDriver and
StreamingExecutionDriver has been renamed from Cognesy\Instructor\Contracts\CanEmitStreamingUpdates
to Cognesy\Instructor\Contracts\CanDriveExecution. The old name described the interface as a
streaming contract, but it is a pull-based execution driver used by both the sync and streaming
paths. Method signatures are unchanged; update any type hints referencing the old interface name.
Legacy RequestMaterializer removed¶
The legacy RequestMaterializer is gone, along with the only code path that read the inline
modePrompts, retryPrompt, and chatStructure settings. Those settings have been removed
from StructuredOutputConfig, together with the
accessors prompt(), modePrompts(), retryPrompt(), chatStructure() and the mutators
withRetryPrompt(), withModePrompt(), withModePrompts(), withChatStructure().
StructuredOutputConfig::fromArray() now ignores unknown keys instead of failing, so config
files, DSNs, and presets that still carry the removed keys keep loading — the values are
dropped. The Laravel instructor.extraction.retry_prompt key and the Symfony
retry_prompt / mode_prompts / chat_structure nodes were removed from their schemas.
Port these settings to prompt classes (modePromptClasses, retryPromptClass,
deserializationErrorPromptClass), or implement CanMaterializeRequest when prompt classes
are insufficient.
Upgrading to 2.5¶
Output targets are explicit¶
A plain JSON Schema now returns an associative array. It no longer creates an internal
Dynamic Structure on the way to the result.
$result = (new StructuredOutput)
->with(messages: $text, responseModel: $jsonSchema)
->get();
// array<string, mixed>
// @doctest id="5c77"
Use x-php-class in the schema or intoInstanceOf() to request class hydration. Use
intoStdClass() for stdClass. intoArray() remains useful when overriding a
class-backed schema.
Passing a Dynamic Structure explicitly preserves its identity:
$result = (new StructuredOutput)
->with(messages: $text, responseModel: $structure)
->get();
$data = $result->toArray();
// @doctest id="b226"
Remove workarounds that expected plain schemas to return Structure, including
unnecessary instanceof Structure checks and toArray() calls.
Validation and failures¶
Plain arrays now pass through schema validation before being returned. Synchronous and completed streaming responses share the same materialization path, and transformation failures fail the attempt instead of silently returning untransformed data.
An unknown root x-php-class now fails response-model preparation. Correct or remove
the metadata, or explicitly select another output target.
Prompt customization¶
StructuredPromptRequestMaterializer is the default. Customize bundled structured
output behavior with modePromptClasses, retryPromptClass, and
deserializationErrorPromptClass. See the 2.7 notes above for the removal of the legacy
inline prompt settings.
Deprecated compatibility APIs¶
- Replace
intoObject()withintoSelfDeserializing(). - Replace global
defaultToStdClassconfiguration with per-requestintoStdClass(). - Keep
PendingStructuredOutput::toJsonObject(),toJson(), andtoArray()unchanged; these methods still inspect the raw inference response.
Dynamic is now optional¶
cognesy/instructor-struct no longer requires the Dynamic package. Applications that
import Cognesy\Dynamic classes directly must declare their own dependency:
2.0-era API overview¶
The current docs use the runtime-first 2.x structured-output API.
What Changed¶
The public model is now:
StructuredOutputfor request constructionStructuredOutputRuntimefor runtime behaviorPendingStructuredOutputfor lazy executionStructuredOutputResponseas the primary final response objectStructuredOutputStreamfor streaming reads and final stream access
Response Ownership¶
Older docs and examples often treated the raw Polyglot response as the main response object.
That is no longer the intended API.
- use
response()when you want the final Instructor response - use
get()when you want only the parsed value - use
inferenceResponse()orfinalInferenceResponse()only when you need raw transport-level details
Streaming Contract¶
Streaming is now built around Instructor-owned stream state.
- Polyglot streams deltas
- Instructor accumulates those deltas in
StructuredOutputStreamState - final stream reads return
StructuredOutputResponse, not raw partial snapshot objects
If you relied on old partial snapshot behavior, update that code to consume:
stream()->responses()for partial and finalStructuredOutputResponseitemsstream()->partials()for parsed partial valuesstream()->sequence()for completed sequence items
Runtime Setup¶
Runtime configuration belongs on StructuredOutputRuntime, not on a global Instructor object.
create()returns a lazy handlestream()returns a dedicated stream objectStructuredOutput::fromConfig(...)andStructuredOutput::using(...)remain valid entry points- published config files are optional
Event Namespaces in 2.5¶
Structured-output events now live in namespaces that match their lifecycle stage. Update listener imports as follows:
| Previous namespace | Current namespace |
|---|---|
Events\PartialsGenerator\* |
Events\Streaming\* |
Events\Request\ResponseModel* |
Events\ResponseModel\ResponseModel* |
Events\Request\SequenceUpdated |
Events\Streaming\SequenceUpdated |
The old aliases were removed after the source, documentation, and public-usage audit found no consumers. Listener registration must use the current event class name.
The old validation-only recovery events and the result-specific
ResponseConvertedToObject and ResponseGenerationFailed events were also removed.
Use these result-neutral lifecycle events instead:
| Removed event | Current event |
|---|---|
NewValidationRecoveryAttempt |
Events\Attempt\ResponseRetryScheduled |
StructuredOutputRecoveryLimitReached |
Events\Attempt\ResponseRecoveryExhausted |
Events\Response\ResponseConvertedToObject |
Events\Response\ResponseMaterialized |
Events\Response\ResponseGenerationFailed |
Events\Response\ResponseMaterializationFailed |
Migration Rule¶
If you are updating older code, rewrite it around one of these shapes:
StructuredOutput->with(...)->get()StructuredOutput->with(...)->response()StructuredOutput->with(...)->stream()