packagist prism-php/prism v0.101.0

6 hours ago

I'm back πŸ’œ

So this one is a catch-up release. 35 of your PRs, most of them fixes for things people have been hitting in production, plus a few features.

Big thanks to @wishborn, who merged and tested most of these in a fork while I was away, and cleaned up the PHPStan and merge fallout along the way. This release is built directly on that work.

There are a few breaking changes, so please read those before you upgrade.

Breaking Changes

  • Laravel 11 is no longer supported. Prism now requires Laravel ^12.61.1 or ^13.12.0. Every Laravel 11 release has open security advisories and Composer won't install them anymore, which is also why CI on main can't pass right now.
  • Unknown finish reasons no longer throw. Refusals, content filters, and anything a provider adds next month now come back as a normal response, on every provider. If you were catching a PrismException for these you'll want to check $response->finishReason now. (#996)
  • Anthropic refusal and pause_turn have their own finish reasons. They map to FinishReason::Refusal and FinishReason::Pause. A paused turn gets continued for you, up to your maxSteps. (#1003)
  • Mistral structured output uses json_schema now. It used to send json_object plus a system message describing your schema. Tools you pass to a Mistral structured request also get called now, they were ignored before. (#965)
  • Gemini structured output sends response_json_schema in place of response_schema. (#975)
  • DeepSeek promptTokens no longer includes cache hits. Those are in cacheReadInputTokens. (#1021)
  • Nullable enum schemas include null in the enum list. (#1024)
  • Text\Request and Structured\Request take a new topK constructor argument. You'll only notice if you build requests by hand. (#961)
  • executeToolCall() and resolveTool() in CallsTools are static. You'll only notice if you override them in a custom provider. (#1009)

Adaptive thinking for Anthropic

This is the one I'm super happy to finally have in. @GrahamCampbell added adaptive thinking and the effort option:

Prism::text()
    ->using('anthropic', 'claude-sonnet-4-6')
    ->withPrompt('Analyze the trade-offs between microservices and monolithic architectures')
    ->withProviderOptions([
        'thinking' => ['type' => 'adaptive'],
        'effort' => 'high',
    ])
    ->asText();

It works the same way with Prism::structured(). The Anthropic docs have the rest.

Known issues

Two things I know about and haven't fixed yet.

OpenRouter reports cache and reasoning tokens now, but cached tokens are still counted in promptTokens too. If you calculate cost from usage you'll double count them on OpenRouter. That fix is next.

OpenAI structured mode matches model families by prefix now, which fixes versioned and fine-tuned model names. It also means gpt-4o-2024-05-13 gets JSON schema mode, and that snapshot doesn't support it. It's a pretty old snapshot, so I'm not too worried about that one.

What's Changed

  • feat(zai): add streaming support by @delabon in #949
  • fix(anthropic): preserve server tool content blocks during multi-step tool loop by @lorisleiva in #952
  • feat(openrouter): extract reasoning tokens and reasoning_details from responses. by @pocketarc in #954
  • fix(openai): support fine-tuned models in structured output by @danielsimkus in #958
  • feat(gemini): add topK generation parameter support by @cyppe in #961
  • Fix xAI driver compatibility with OpenAI-compatible endpoints by @meirdick in #964
  • Add tool call support to Mistral structured handler by @CamilleScholtz in #965
  • fix(openai): interleave reasoning blocks with paired function calls in conversation history by @Cbrad24 in #971
  • fix(gemini): switch structured output to response_json_schema by @cainydev in #975
  • feat(gemini): support mixing provider tools with custom function tools by @cainydev in #976
  • fix(xai): cast empty tool properties to object in xAI ToolMap by @Cbrad24 in #977
  • feat(mistral): add reasoning_effort provider option by @Duetro in #980
  • fix: ToolCall::arguments() returns null for valid JSON null string by @obalaweb in #985
  • fix(groq): accept string values for boolean/number tool parameters by @obalaweb in #986
  • fix(groq): handle Llama models embedding arguments in function name by @obalaweb in #987
  • Fix typo in exception message in PrismException by @burigooth in #989
  • fix: OpenAI StructuredModeResolver should match versioned model names by @ipalaus in #991
  • fix(openrouter): support monitoring cache tokens / reasoning tokens on OpenRouter response by @kargnas in #992
  • feat(openrouter): support cacheTtl provider option for extended cache TTL by @kargnas in #993
  • feat(audio): Allow diarization with Mistral by @NoelDeMartin in #995
  • fix: handle unknown finish reasons gracefully by @cainydev in #996
  • fix(gemini): normalize array-type nullable notation in SchemaMap prop… by @yondifon in #997
  • Anthropic adaptive thinking by @GrahamCampbell in #998
  • fix(Gemini): Add Usage to StepFinishEvent by @Plytas in #1000
  • fix(Media): normalize mimeType for audio/wav by @barryvdh in #1001
  • feat(gemini): add service_tier support for Flex Inference by @smittie2000 in #1002
  • feat(anthropic): handle pause_turn and refusal stop reasons by @adamthehutt in #1003
  • fix(ToolMap): remove static duplicate parameters handling in mapping openrouter by @Aldiwildan77 in #1004
  • feat(gemini): extracting rate limit violations and retry-after information by @cutlope in #1008
  • Fix: concurrent tool execution fails with ProcessDriver due to $this capture and circular references by @vinitkadam03 in #1009
  • fix: cast empty tool properties to object for Mistral API compatibility by @afiktahirov in #1012
  • fix(ollama): exclude top-level keys from nested options in embeddings request by @sv-LayZ in #1013
  • fix(deepseek): preserve reasoning_content in thinking mode for tool calls by @tlotli in #1020
  • fix(deepseek): extract prompt_cache_hit_tokens and reasoning_tokens from usage by @Lintume in #1021
  • Fix: Nullable Enum Schema by @yiedpozi in #1024

Also in here, without an upstream PR of their own:

  • Tool arguments that show up as strings get coerced to the handler's declared int, float, bool, or backed enum type, by @wishborn
  • Gemini sends tools as a JSON array when you mix provider tools with function tools, by @saimaz
  • OpenRouter errors on a streamed request keep their real message
  • Z.ai streaming keeps content that arrives in the first chunk

What's next

Batch and Files (#951) and the new providers (Azure, Replicate, Vertex, Requesty) are not in this release. They're in review right now and each one needs some work before I'm comfortable tagging it. Human-in-the-loop tool approval (#932) is on the list too.

Full Changelog: v0.100.1...v0.101.0

Don't miss a new prism release

NewReleases is sending notifications on new releases.