From aa444d1e92866cf9e09e01350d06132df22046e2 Mon Sep 17 00:00:00 2001 From: james Date: Thu, 9 Oct 2025 20:49:09 +0100 Subject: [PATCH 01/10] Add text formatting options to OpenAI Text handler --- src/Providers/OpenAI/Handlers/Text.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index c9ea0737a..299340796 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -135,6 +135,7 @@ protected function sendRequest(Request $request): ClientResponse 'previous_response_id' => $request->providerOptions('previous_response_id'), 'truncation' => $request->providerOptions('truncation'), 'reasoning' => $request->providerOptions('reasoning'), + 'text' => $request->providerOptions('text'), ])) ); } From b1c22757f1b8085ea0bda02787fc9fa117b8466e Mon Sep 17 00:00:00 2001 From: james Date: Thu, 9 Oct 2025 21:17:50 +0100 Subject: [PATCH 02/10] Disable parallel tool calls in OpenAI Text handler --- src/Providers/OpenAI/Handlers/Text.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index 299340796..096a1f101 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -136,6 +136,7 @@ protected function sendRequest(Request $request): ClientResponse 'truncation' => $request->providerOptions('truncation'), 'reasoning' => $request->providerOptions('reasoning'), 'text' => $request->providerOptions('text'), + 'parallel_tool_calls' => false, ])) ); } From e0008fa5dd9b5f2cd98a214ef2f962c5351662f6 Mon Sep 17 00:00:00 2001 From: james Date: Fri, 10 Oct 2025 19:04:55 +0100 Subject: [PATCH 03/10] Fix reasoning mapping in ToolCallMap to use correct index --- src/Providers/OpenAI/Concerns/ProcessRateLimits.php | 2 +- src/Providers/OpenAI/Maps/ToolCallMap.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Providers/OpenAI/Concerns/ProcessRateLimits.php b/src/Providers/OpenAI/Concerns/ProcessRateLimits.php index cac845bb1..b7e93ab74 100644 --- a/src/Providers/OpenAI/Concerns/ProcessRateLimits.php +++ b/src/Providers/OpenAI/Concerns/ProcessRateLimits.php @@ -43,7 +43,7 @@ protected function processRateLimits(Response $response): array protected function parseResetTime(?string $resetTime): ?Carbon { - if ($resetTime === null || $resetTime === '' || $resetTime === '0') { + if (in_array($resetTime, [null, '', '0'], true)) { return null; } diff --git a/src/Providers/OpenAI/Maps/ToolCallMap.php b/src/Providers/OpenAI/Maps/ToolCallMap.php index 999a1e2af..55897bf28 100644 --- a/src/Providers/OpenAI/Maps/ToolCallMap.php +++ b/src/Providers/OpenAI/Maps/ToolCallMap.php @@ -19,13 +19,13 @@ public static function map(?array $toolCalls, ?array $reasonings = null): array return []; } - return array_map(fn (array $toolCall): ToolCall => new ToolCall( + return array_map(fn (array $toolCall, int $idx): ToolCall => new ToolCall( id: data_get($toolCall, 'id'), name: data_get($toolCall, 'name'), arguments: data_get($toolCall, 'arguments'), resultId: data_get($toolCall, 'call_id'), - reasoningId: data_get($reasonings, '0.id'), - reasoningSummary: data_get($reasonings, '0.summary'), + reasoningId: data_get($reasonings, $idx.'.id'), + reasoningSummary: data_get($reasonings, $idx.'.summary'), ), $toolCalls); } } From 214363a3dfb94f557d866a712bb3ed10ad85a126 Mon Sep 17 00:00:00 2001 From: james Date: Fri, 10 Oct 2025 19:11:42 +0100 Subject: [PATCH 04/10] Use laravel collect helper to map tool calls --- src/Providers/OpenAI/Maps/ToolCallMap.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Providers/OpenAI/Maps/ToolCallMap.php b/src/Providers/OpenAI/Maps/ToolCallMap.php index 55897bf28..87705ecfe 100644 --- a/src/Providers/OpenAI/Maps/ToolCallMap.php +++ b/src/Providers/OpenAI/Maps/ToolCallMap.php @@ -19,13 +19,16 @@ public static function map(?array $toolCalls, ?array $reasonings = null): array return []; } - return array_map(fn (array $toolCall, int $idx): ToolCall => new ToolCall( - id: data_get($toolCall, 'id'), - name: data_get($toolCall, 'name'), - arguments: data_get($toolCall, 'arguments'), - resultId: data_get($toolCall, 'call_id'), - reasoningId: data_get($reasonings, $idx.'.id'), - reasoningSummary: data_get($reasonings, $idx.'.summary'), - ), $toolCalls); + return collect($toolCalls)->map( + fn (array $toolCall, int $idx): ToolCall => new ToolCall( + id: data_get($toolCall, 'id'), + name: data_get($toolCall, 'name'), + arguments: data_get($toolCall, 'arguments'), + resultId: data_get($toolCall, 'call_id'), + reasoningId: data_get($reasonings, $idx.'.id'), + reasoningSummary: data_get($reasonings, $idx.'.summary'), + ), + ) + ->toArray(); } } From 2cb947df1282b491d79b7b839974f27ce6a9c07b Mon Sep 17 00:00:00 2001 From: james Date: Sat, 11 Oct 2025 14:19:43 +0100 Subject: [PATCH 05/10] Add debug logging for OpenAI tool calls in Text handler --- src/Providers/OpenAI/Handlers/Text.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index 096a1f101..94ae5bd09 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -58,12 +58,16 @@ public function handle(Request $request): Response $this->citations = $this->extractCitations($data); + $toolCalls = ToolCallMap::map( + array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'function_call'), + array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'reasoning'), + ); + + logger()->debug('OpenAI tool calls', ['toolCalls' => $toolCalls]); + $responseMessage = new AssistantMessage( content: data_get($data, 'output.{last}.content.0.text') ?? '', - toolCalls: ToolCallMap::map( - array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'function_call'), - array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'reasoning'), - ), + toolCalls: $toolCalls, additionalContent: Arr::whereNotNull([ 'citations' => $this->citations, ]), From 9193dfeed436c34cb663a2d47dbd131af06fd877 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 11 Oct 2025 15:54:30 +0100 Subject: [PATCH 06/10] (feature-allow-text-provider-option-in-text-request) Add support for configurable parallel tool calls and optional toolChoice finalization --- src/Text/Request.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Text/Request.php b/src/Text/Request.php index 7b70c2de1..921b8c40d 100644 --- a/src/Text/Request.php +++ b/src/Text/Request.php @@ -43,13 +43,14 @@ public function __construct( protected string|ToolChoice|null $toolChoice, array $providerOptions = [], protected array $providerTools = [], + protected bool $parallelToolCalls = true, ) { $this->providerOptions = $providerOptions; } - public function toolChoice(): string|ToolChoice|null + public function toolChoice(bool $finalRequest = false): string|ToolChoice|null { - return $this->toolChoice; + return $finalRequest ? ToolChoice::None : $this->toolChoice; } /** @@ -142,4 +143,9 @@ public function addMessage(Message $message): self return $this; } + + public function parallelToolCalls(): bool + { + return $this->parallelToolCalls; + } } From 28d8ab1262cc0cc194754caf21432cb09fca4297 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 11 Oct 2025 15:55:11 +0100 Subject: [PATCH 07/10] (feature-allow-text-provider-option-in-text-request) Refactor reasoning handling in ToolCallMap to resolve the latest reasoning item - Renamed `$reasonings` to `$reasoningItems` for clarity. - Added `resolveReasoningItem` method to determine the most recent reasoning item (ID and summary). - Updated `map` logic to utilize resolved reasoning instead of indexing directly. --- src/Providers/OpenAI/Maps/ToolCallMap.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Providers/OpenAI/Maps/ToolCallMap.php b/src/Providers/OpenAI/Maps/ToolCallMap.php index 87705ecfe..712353daf 100644 --- a/src/Providers/OpenAI/Maps/ToolCallMap.php +++ b/src/Providers/OpenAI/Maps/ToolCallMap.php @@ -10,25 +10,37 @@ class ToolCallMap { /** * @param array> $toolCalls - * @param null|array> $reasonings + * @param null|array> $reasoningItems * @return array */ - public static function map(?array $toolCalls, ?array $reasonings = null): array + public static function map(?array $toolCalls, ?array $reasoningItems = null): array { if ($toolCalls === null) { return []; } + [$reasoningId, $reasoningSummary] = self::resolveReasoningItem($reasoningItems); + return collect($toolCalls)->map( - fn (array $toolCall, int $idx): ToolCall => new ToolCall( + fn (array $toolCall): ToolCall => new ToolCall( id: data_get($toolCall, 'id'), name: data_get($toolCall, 'name'), arguments: data_get($toolCall, 'arguments'), resultId: data_get($toolCall, 'call_id'), - reasoningId: data_get($reasonings, $idx.'.id'), - reasoningSummary: data_get($reasonings, $idx.'.summary'), + reasoningId: $reasoningId, + reasoningSummary: $reasoningSummary, ), ) ->toArray(); } + + protected static function resolveReasoningItem(?array $reasoningItems): array + { + $reasoningItems = array_reverse($reasoningItems ?? []); + + return [ + data_get($reasoningItems, '0.id'), + data_get($reasoningItems, '0.summary'), + ]; + } } From 0502bb37d3188e63d012c449506c5cc1d8236c21 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 11 Oct 2025 15:55:43 +0100 Subject: [PATCH 08/10] (feature-allow-text-provider-option-in-text-request) Add finalRequestStep logic and support for dynamic parallel tool calls configuration - Introduced `finalRequestStep` method to calculate the final step in requests. - Updated `tool_choice` mapping to consider final request step logic. - Enabled `parallel_tool_calls` configuration to support dynamic values from the request. --- src/Providers/OpenAI/Handlers/Text.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index 94ae5bd09..3964001cb 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -122,6 +122,11 @@ protected function shouldContinue(Request $request): bool return $this->responseBuilder->steps->count() < $request->maxSteps(); } + protected function finalRequestStep(Request $request): bool + { + return $this->responseBuilder->steps->count() === $request->maxSteps() - 1; + } + protected function sendRequest(Request $request): ClientResponse { return $this->client->post( @@ -135,12 +140,16 @@ protected function sendRequest(Request $request): ClientResponse 'top_p' => $request->topP(), 'metadata' => $request->providerOptions('metadata'), 'tools' => $this->buildTools($request), - 'tool_choice' => ToolChoiceMap::map($request->toolChoice()), + 'tool_choice' => ToolChoiceMap::map( + $request->toolChoice( + $this->finalRequestStep($request), + ), + ), 'previous_response_id' => $request->providerOptions('previous_response_id'), 'truncation' => $request->providerOptions('truncation'), 'reasoning' => $request->providerOptions('reasoning'), 'text' => $request->providerOptions('text'), - 'parallel_tool_calls' => false, + 'parallel_tool_calls' => $request->parallelToolCalls(), ])) ); } From a0b8df34a09c371f1eafd00755461503f4c7da7d Mon Sep 17 00:00:00 2001 From: james Date: Sun, 12 Oct 2025 00:24:06 +0100 Subject: [PATCH 09/10] (feature-allow-text-provider-option-in-text-request) Inline tool call mapping in OpenAI Text handler - Simplified `ToolCallMap` usage by directly inlining the mapping logic during `AssistantMessage` creation. - Removed redundant debug logging for tool calls. --- src/Providers/OpenAI/Handlers/Text.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index 3964001cb..a2787f3e3 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -58,16 +58,12 @@ public function handle(Request $request): Response $this->citations = $this->extractCitations($data); - $toolCalls = ToolCallMap::map( - array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'function_call'), - array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'reasoning'), - ); - - logger()->debug('OpenAI tool calls', ['toolCalls' => $toolCalls]); - $responseMessage = new AssistantMessage( content: data_get($data, 'output.{last}.content.0.text') ?? '', - toolCalls: $toolCalls, + toolCalls: ToolCallMap::map( + array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'function_call'), + array_filter(data_get($data, 'output', []), fn (array $output): bool => $output['type'] === 'reasoning'), + ), additionalContent: Arr::whereNotNull([ 'citations' => $this->citations, ]), From f23fa842fd8e9b554a74e2b22919c0456d0949e9 Mon Sep 17 00:00:00 2001 From: james Date: Sun, 12 Oct 2025 15:45:22 +0100 Subject: [PATCH 10/10] (feature-allow-text-provider-option-in-text-request) Remove `parallelToolCalls` property and related method from `Text\Request` - Eliminated unused `parallelToolCalls` property and its associated getter method. - Simplified constructor by removing the redundant property declaration. --- src/Text/Request.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Text/Request.php b/src/Text/Request.php index 921b8c40d..b7af68471 100644 --- a/src/Text/Request.php +++ b/src/Text/Request.php @@ -43,7 +43,6 @@ public function __construct( protected string|ToolChoice|null $toolChoice, array $providerOptions = [], protected array $providerTools = [], - protected bool $parallelToolCalls = true, ) { $this->providerOptions = $providerOptions; } @@ -143,9 +142,4 @@ public function addMessage(Message $message): self return $this; } - - public function parallelToolCalls(): bool - { - return $this->parallelToolCalls; - } }