Three merges landed in laravel/ai. One closes a data-loss bug with OpenAI reasoning models, one adds a missing generation parameter, and one reworks how tool names are resolved across every first-party gateway. Each change is self-contained, but together they round out the surface area developers hit first when building agents.
@Cbrad24 merged PR #301, fixing a bug introduced back in PR #203 where reasoning_id and reasoning_summary were silently dropped when replaying a conversation from the database.
OpenAI's reasoning models require those fields to be present on replayed messages. Without them, the model loses its chain of reasoning across turns, which breaks any multi-turn agent that persists and restores history. The fix ensures both fields are carried through the restore path the same way all other message data is.
This resolves issue #271. If your agents use o1, o3, or any other reasoning model and store conversation history, this patch is relevant to you.
@Duetro merged PR #306, adding top_p nucleus sampling as a first-class option alongside the existing temperature parameter.
The implementation follows the same reflection-based pattern already used for Temperature and MaxTokens. A new #[TopP] attribute can be applied directly to agent classes, and TextGenerationOptions grows a topP property. The value is forwarded to Prism via usingTopP(), so the provider-level plumbing is handled there.
1use Laravel\AI\Attributes\TopP;2 3#[TopP(0.9)]4class SupportAgent extends Agent5{6 // ...7}
Or set it explicitly on the options object when you need runtime control:
1$options = new TextGenerationOptions();2$options->topP = 0.9;
top_p and temperature are both sampling strategies that narrow the token distribution, but they operate differently. Using top_p alone is common when you want diversity to be probability-bounded rather than temperature-scaled.
@seankndy merged PR #420, adding a ResolvesToolName trait that lets any tool class expose a public name() method. Every first-party gateway now calls that method when one exists, falling back to class_basename when it does not. The Prism gateway already used this pattern internally; the native gateways now share the same logic.
The practical problem this solves: adapter-style tools, where a single class dispatches to multiple distinct targets per instance, all resolved to the same class_basename. Providers receiving duplicate names cannot route calls correctly, and lookups inside InvokesTools::findTool would collide.
1readonly class McpToolAdapter implements Tool 2{ 3 public function __construct( 4 private McpClient $client, 5 private string $toolName, 6 private string $serverSlug, 7 // ... 8 ) {} 9 10 public function name(): string11 {12 return "{$this->serverSlug}__{$this->toolName}";13 }14 15 public function description(): Stringable|string16 {17 return $this->toolDescription;18 }19 20 // handle(), inputSchema(), etc.21}
With that name() method in place, two McpToolAdapter instances wrapping different MCP servers will be registered under distinct names and routed correctly by the provider. Covered gateways: Anthropic, AzureOpenAI, DeepSeek, Gemini, Groq, Mistral, Ollama, OpenAI, OpenRouter, and xAI.
Existing tools that do not define name() keep the current class_basename behavior unchanged.
If you enjoyed this article, please consider supporting our work for as low as $5 / month.
Sponsor
Written by
Writing and maintaining @LaravelMagazine. Host of "The Laravel Magazine Podcast". Pronouns: vi/vim.