Skip to content

Upgrade

Custom Inference Drivers in v2.7

Only driver authors are affected. Preset names, Inference, PendingInference, InferenceStream, InferenceResponse, Embeddings, PendingEmbeddings, and LLMConfig are unchanged, and so is every interface under Cognesy\Polyglot\Inference\Contracts.

All 26 provider driver shells under Cognesy\Polyglot\Inference\Drivers\ were removed: A21Driver, CerebrasDriver, DeepseekDriver, FireworksDriver, GlmDriver, GroqDriver, InceptionDriver, MetaDriver, MinimaxiDriver, MistralDriver, OpenAIDriver, OpenAICompatibleDriver, OpenRouterDriver, PerplexityDriver, QwenDriver, SambaNovaDriver, XAiDriver, AnthropicDriver, AzureDriver, BedrockOpenAIDriver, CohereV2Driver, GeminiDriver, GeminiOAIDriver, HuggingFaceDriver, OpenAIResponsesDriver, and OpenResponsesDriver. Their only content was composing collaborators, so all bundled inference registrations — including native-protocol and bespoke-endpoint providers — are now InferenceDriverSpec rows in BundledInferenceDrivers, all served by one SpecifiedInferenceDriver. The embeddings driver of the same short name, Embeddings\Drivers\OpenAI\OpenAIDriver, is untouched.

Replacing a subclass of a bundled driver

Extend SpecifiedInferenceDriver and name your subclass in the spec's driverClass. It still receives the five collaborators assembled for it:

final class MyDriver extends SpecifiedInferenceDriver { /* override one method */ }

$registry = BundledInferenceDrivers::registry()->withDriver(
    'my-provider',
    new InferenceDriverSpec(
        bodyFormat: MyBodyFormat::class,
        driverClass: MyDriver::class,
    ),
);
// @doctest id="83de"

If you only changed the wire format, no subclass is needed — pass your own bodyFormat, requestAdapter, responseAdapter, usageFormat, or messageFormat to the spec. Each defaults to the OpenAI implementation.

Replacing a capabilities() override

Per-model capability logic becomes data on the spec rather than a method override — either a fixed DriverCapabilities, or a Closure(string $model): DriverCapabilities when the answer depends on the model. Omit it for "everything supported".

new InferenceDriverSpec(
    bodyFormat: MyBodyFormat::class,
    capabilities: new DriverCapabilities(responseFormatWithTools: false),
);
// @doctest id="8509"

InferenceDriverRegistry::withDriver() still accepts a class-string or a callable, so drivers registered that way need no change.

Moved classes

Cognesy\Polyglot\Inference\Contracts\MessageMapper is now Cognesy\Polyglot\Inference\Drivers\MessageMapper — it is a driver helper, not a contract. Update the import; the class is otherwise unchanged. This one has no alias.

Five classes that neither subsystem owns moved under Cognesy\Polyglot\Support\:

Old FQCN New FQCN
Inference\Core\SensitiveDataRedactor Support\Redaction\SensitiveDataRedactor
Inference\Config\RetryBackoff Support\Retry\RetryBackoff
Inference\Config\RetryJitter Support\Retry\RetryJitter
Inference\Config\RetryPolicyInvariants Support\Retry\RetryPolicyInvariants
Polyglot\Pricing\Cost Support\Pricing\Cost

All five old names keep working. The package registers a lazy class_alias() autoloader, so an old FQCN resolves to the same class — instanceof holds in both directions and RetryJitter::Full === Support\Retry\RetryJitter::Full. Migrate at your convenience; the aliases are removed at the next major.

Removed dead classes

Inference\Enums\InferenceContentType, Inference\Collections\InferenceResponseList, and Embeddings\Traits\HasFinders were deleted. None had a usage anywhere in the repository.

Custom Embeddings Drivers in v2.7

CanHandleVectorization now returns the domain response directly. PHP cannot provide a compatibility shim for this interface return-type change, so custom implementations must be updated together with the v2.7 package upgrade.

- public function handle(EmbeddingsRequest $request): HttpResponse;
- public function fromData(array $data): ?EmbeddingsResponse;
+ public function handle(EmbeddingsRequest $request): EmbeddingsResponse;
// @doctest id="f790"

Move HTTP response decoding and adaptation into handle(). The separate fromData() method is no longer part of the driver contract. Drivers that extend BaseEmbedDriver inherit the new behavior unless they override handle().

Migrating from v1 to v2

Polyglot 2.0 is centered around explicit request fields.

The main migration points are:

  • remove old output mode usage
  • set responseFormat for native JSON or JSON schema
  • set tools and toolChoice for tool calling
  • use stream()->deltas() for streaming

Response Model

Polyglot is now explicitly the raw inference layer.

  • InferenceResponse is the final raw provider response
  • streaming yields PartialInferenceDelta
  • structured value ownership belongs to higher-level packages such as Instructor

If older code assumed that Polyglot streaming yielded accumulated partial response snapshots, update that code to work from deltas instead.

Before

<?php

$data = $inference
    ->with(
        messages: 'Return JSON.',
        mode: $oldMode,
    )
    ->asJsonData();
// @doctest id="656c"

After

<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Data\ResponseFormat;
use Cognesy\Polyglot\Inference\Inference;

$data = Inference::using('openai')
    ->withMessages(Messages::fromString('Return JSON.'))
    ->withResponseFormat(new ResponseFormat(type: 'json_object'))
    ->asJsonData();
// @doctest id="70bb"

Markdown-JSON fallback is no longer a Polyglot concern. Use Instructor when you need higher-level structured output strategies.

Streaming Migration

Update old streaming code like this:

  • replace partial-response iteration with stream()->deltas()
  • assemble final raw output with final()
  • move partial structured parsing to Instructor or your own delta accumulator