diff --git a/src/Providers/Anthropic/Handlers/Structured.php b/src/Providers/Anthropic/Handlers/Structured.php index 244f1747b..35644835b 100644 --- a/src/Providers/Anthropic/Handlers/Structured.php +++ b/src/Providers/Anthropic/Handlers/Structured.php @@ -11,7 +11,6 @@ use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Contracts\PrismRequest; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsCitations; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsProviderToolCalls; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsText; @@ -71,8 +70,7 @@ public function handle(): Response return match ($tempResponse->finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($toolCalls, $tempResponse), - FinishReason::Stop, FinishReason::Length => $this->handleStop($tempResponse), - default => throw new PrismException('Anthropic: unknown finish reason'), + default => $this->handleStop($tempResponse), }; } diff --git a/src/Providers/Anthropic/Handlers/Text.php b/src/Providers/Anthropic/Handlers/Text.php index ee6b2e37a..49fd25394 100644 --- a/src/Providers/Anthropic/Handlers/Text.php +++ b/src/Providers/Anthropic/Handlers/Text.php @@ -11,7 +11,6 @@ use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Contracts\PrismRequest; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsCitations; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsProviderToolCalls; use Prism\Prism\Providers\Anthropic\Concerns\ExtractsText; @@ -55,8 +54,7 @@ public function handle(): Response return match ($this->tempResponse->finishReason) { FinishReason::ToolCalls => $this->handleToolCalls(), - FinishReason::Stop, FinishReason::Length => $this->handleStop(), - default => throw new PrismException('Anthropic: unknown finish reason'), + default => $this->handleStop(), }; } diff --git a/src/Providers/DeepSeek/Handlers/Text.php b/src/Providers/DeepSeek/Handlers/Text.php index c8a15d253..468ce825c 100644 --- a/src/Providers/DeepSeek/Handlers/Text.php +++ b/src/Providers/DeepSeek/Handlers/Text.php @@ -9,7 +9,6 @@ use Illuminate\Support\Arr; use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\DeepSeek\Concerns\MapsFinishReason; use Prism\Prism\Providers\DeepSeek\Concerns\ValidatesResponses; use Prism\Prism\Providers\DeepSeek\Maps\MessageMap; @@ -47,8 +46,7 @@ public function handle(Request $request): TextResponse return match ($this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop => $this->handleStop($data, $request), - default => throw new PrismException('DeepSeek: unknown finish reason'), + default => $this->handleStop($data, $request), }; } diff --git a/src/Providers/Gemini/Handlers/Structured.php b/src/Providers/Gemini/Handlers/Structured.php index 6d4870f59..61303219c 100644 --- a/src/Providers/Gemini/Handlers/Structured.php +++ b/src/Providers/Gemini/Handlers/Structured.php @@ -67,8 +67,7 @@ public function handle(Request $request): StructuredResponse return match ($finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request, $finishReason), - default => throw new PrismException('Gemini: unhandled finish reason'), + default => $this->handleStop($data, $request, $finishReason), }; } diff --git a/src/Providers/Gemini/Handlers/Text.php b/src/Providers/Gemini/Handlers/Text.php index fce051b67..d26b0266d 100644 --- a/src/Providers/Gemini/Handlers/Text.php +++ b/src/Providers/Gemini/Handlers/Text.php @@ -9,7 +9,6 @@ use Illuminate\Support\Arr; use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Gemini\Concerns\ValidatesResponse; use Prism\Prism\Providers\Gemini\Maps\CitationMapper; use Prism\Prism\Providers\Gemini\Maps\FinishReasonMap; @@ -58,8 +57,7 @@ public function handle(Request $request): TextResponse return match ($finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request, $finishReason), - default => throw new PrismException('Gemini: unhandled finish reason'), + default => $this->handleStop($data, $request, $finishReason), }; } diff --git a/src/Providers/Groq/Handlers/Text.php b/src/Providers/Groq/Handlers/Text.php index a88c3007e..987d4cad0 100644 --- a/src/Providers/Groq/Handlers/Text.php +++ b/src/Providers/Groq/Handlers/Text.php @@ -9,7 +9,6 @@ use Illuminate\Support\Arr; use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Groq\Concerns\ProcessRateLimits; use Prism\Prism\Providers\Groq\Concerns\ValidateResponse; use Prism\Prism\Providers\Groq\Maps\FinishReasonMap; @@ -50,8 +49,7 @@ public function handle(Request $request): TextResponse return match ($finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request, $response), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request, $response, $finishReason), - default => throw new PrismException('Groq: unhandled finish reason'), + default => $this->handleStop($data, $request, $response, $finishReason), }; } diff --git a/src/Providers/Mistral/Handlers/Text.php b/src/Providers/Mistral/Handlers/Text.php index ebb8fc6b9..179e68635 100644 --- a/src/Providers/Mistral/Handlers/Text.php +++ b/src/Providers/Mistral/Handlers/Text.php @@ -9,7 +9,6 @@ use Illuminate\Support\Arr; use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Mistral\Concerns\ExtractsText; use Prism\Prism\Providers\Mistral\Concerns\ExtractsThinking; use Prism\Prism\Providers\Mistral\Concerns\MapsFinishReason; @@ -55,8 +54,7 @@ public function handle(Request $request): Response return match ($this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request, $response), - FinishReason::Stop => $this->handleStop($data, $request, $response), - default => throw PrismException::providerResponseError('Invalid tool choice'), + default => $this->handleStop($data, $request, $response), }; } diff --git a/src/Providers/Ollama/Handlers/Text.php b/src/Providers/Ollama/Handlers/Text.php index 1c4928fa9..c059aea25 100644 --- a/src/Providers/Ollama/Handlers/Text.php +++ b/src/Providers/Ollama/Handlers/Text.php @@ -7,8 +7,6 @@ use Illuminate\Http\Client\PendingRequest; use Illuminate\Support\Arr; use Prism\Prism\Concerns\CallsTools; -use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\Ollama\Concerns\MapsFinishReason; use Prism\Prism\Providers\Ollama\Concerns\ValidatesResponse; use Prism\Prism\Providers\Ollama\Maps\MessageMap; @@ -48,16 +46,7 @@ public function handle(Request $request): Response return $this->handleToolCalls($data, $request); } - $finishReason = $this->mapFinishReason($data); - - return match ($finishReason) { - FinishReason::Stop, - FinishReason::Length, - FinishReason::Unknown, - FinishReason::ContentFilter, - FinishReason::Other => $this->handleStop($data, $request), - default => throw new PrismException('Ollama: unknown finish reason'), - }; + return $this->handleStop($data, $request); } /** diff --git a/src/Providers/OpenAI/Handlers/Structured.php b/src/Providers/OpenAI/Handlers/Structured.php index ffa2f0e53..94c4f0076 100644 --- a/src/Providers/OpenAI/Handlers/Structured.php +++ b/src/Providers/OpenAI/Handlers/Structured.php @@ -80,18 +80,12 @@ public function handle(Request $request): StructuredResponse return match ($finishReason = $this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request, $response), - FinishReason::Stop => $this->handleFinalStop($data, $request, $response), FinishReason::Length => throw new PrismException(sprintf( 'OpenAI: max tokens exceeded (status: %s, type: %s). If using a reasoning model, increase max_tokens to account for internal reasoning token usage.', data_get($data, 'output.{last}.status', 'n/a'), data_get($data, 'output.{last}.type', 'n/a'), )), - default => throw new PrismException(sprintf( - 'OpenAI: unhandled finish reason "%s" (status: %s, type: %s)', - $finishReason->value, - data_get($data, 'output.{last}.status', 'n/a'), - data_get($data, 'output.{last}.type', 'n/a'), - )), + default => $this->handleFinalStop($data, $request, $response), }; } diff --git a/src/Providers/OpenAI/Handlers/Text.php b/src/Providers/OpenAI/Handlers/Text.php index e370964dd..22bdbd8c6 100644 --- a/src/Providers/OpenAI/Handlers/Text.php +++ b/src/Providers/OpenAI/Handlers/Text.php @@ -61,18 +61,12 @@ public function handle(Request $request): Response return match ($finishReason = $this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request, $response), - FinishReason::Stop => $this->handleStop($data, $request, $response), FinishReason::Length => throw new PrismException(sprintf( 'OpenAI: max tokens exceeded (status: %s, type: %s). If using a reasoning model, increase max_tokens to account for internal reasoning token usage.', data_get($data, 'output.{last}.status', 'n/a'), data_get($data, 'output.{last}.type', 'n/a'), )), - default => throw new PrismException(sprintf( - 'OpenAI: unhandled finish reason "%s" (status: %s, type: %s)', - $finishReason->value, - data_get($data, 'output.{last}.status', 'n/a'), - data_get($data, 'output.{last}.type', 'n/a'), - )), + default => $this->handleStop($data, $request, $response), }; } diff --git a/src/Providers/OpenRouter/Handlers/Structured.php b/src/Providers/OpenRouter/Handlers/Structured.php index 43b8376fd..44c6bcada 100644 --- a/src/Providers/OpenRouter/Handlers/Structured.php +++ b/src/Providers/OpenRouter/Handlers/Structured.php @@ -47,8 +47,7 @@ public function handle(Request $request): StructuredResponse return match ($this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request), - default => throw new PrismException('OpenRouter: unknown finish reason'), + default => $this->handleStop($data, $request), }; } diff --git a/src/Providers/OpenRouter/Handlers/Text.php b/src/Providers/OpenRouter/Handlers/Text.php index ac2b78cf0..d10ec03e6 100644 --- a/src/Providers/OpenRouter/Handlers/Text.php +++ b/src/Providers/OpenRouter/Handlers/Text.php @@ -8,7 +8,6 @@ use Illuminate\Http\Client\Response; use Prism\Prism\Concerns\CallsTools; use Prism\Prism\Enums\FinishReason; -use Prism\Prism\Exceptions\PrismException; use Prism\Prism\Providers\OpenRouter\Concerns\BuildsRequestOptions; use Prism\Prism\Providers\OpenRouter\Concerns\MapsFinishReason; use Prism\Prism\Providers\OpenRouter\Concerns\ValidatesResponses; @@ -46,8 +45,7 @@ public function handle(Request $request): TextResponse return match ($this->mapFinishReason($data)) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request), - default => throw new PrismException('OpenRouter: unknown finish reason'), + default => $this->handleStop($data, $request), }; } diff --git a/src/Providers/XAI/Handlers/Text.php b/src/Providers/XAI/Handlers/Text.php index 036b0e22b..d42b9f5f0 100644 --- a/src/Providers/XAI/Handlers/Text.php +++ b/src/Providers/XAI/Handlers/Text.php @@ -54,8 +54,7 @@ public function handle(Request $request): TextResponse return match ($finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request), - default => throw new PrismException('XAI: unknown finish reason'), + default => $this->handleStop($data, $request), }; } diff --git a/src/Providers/Z/Handlers/Text.php b/src/Providers/Z/Handlers/Text.php index dbdc356cf..c1219ed18 100644 --- a/src/Providers/Z/Handlers/Text.php +++ b/src/Providers/Z/Handlers/Text.php @@ -57,8 +57,7 @@ public function handle(Request $request): TextResponse return match ($finishReason) { FinishReason::ToolCalls => $this->handleToolCalls($data, $request), - FinishReason::Stop, FinishReason::Length => $this->handleStop($data, $request), - default => throw new PrismException('Z: unknown finish reason'), + default => $this->handleStop($data, $request), }; } diff --git a/tests/Providers/OpenAI/StructuredTest.php b/tests/Providers/OpenAI/StructuredTest.php index 052d48b2f..e16d022e1 100644 --- a/tests/Providers/OpenAI/StructuredTest.php +++ b/tests/Providers/OpenAI/StructuredTest.php @@ -6,6 +6,7 @@ use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; +use Prism\Prism\Enums\FinishReason; use Prism\Prism\Enums\Provider; use Prism\Prism\Enums\StructuredMode; use Prism\Prism\Exceptions\PrismException; @@ -642,7 +643,7 @@ } }); -it('includes status details for unknown finish reasons', function (): void { +it('handles unknown finish reasons gracefully', function (): void { FixtureResponse::fakeResponseSequence('v1/responses', 'openai/structured-unknown-finish-reason'); $schema = new ObjectSchema( @@ -654,10 +655,11 @@ ['weather'] ); - expect(fn () => Prism::structured() + $response = Prism::structured() ->withSchema($schema) ->using(Provider::OpenAI, 'gpt-4o') ->withPrompt('What is the weather?') - ->asStructured() - )->toThrow(PrismException::class, 'some_future_type'); + ->asStructured(); + + expect($response->finishReason)->toBe(FinishReason::Unknown); }); diff --git a/tests/Providers/OpenAI/TextTest.php b/tests/Providers/OpenAI/TextTest.php index 7055ec614..446657a94 100644 --- a/tests/Providers/OpenAI/TextTest.php +++ b/tests/Providers/OpenAI/TextTest.php @@ -886,12 +886,14 @@ } }); -it('includes status details for unknown finish reasons', function (): void { +it('handles unknown finish reasons gracefully', function (): void { FixtureResponse::fakeResponseSequence('v1/responses', 'openai/text-unknown-finish-reason'); - expect(fn () => Prism::text() + $response = Prism::text() ->using('openai', 'gpt-4o') ->withPrompt('Hello') - ->asText() - )->toThrow(PrismException::class, 'some_future_type'); + ->asText(); + + expect($response->text)->toBe('Some response'); + expect($response->finishReason)->toBe(FinishReason::Unknown); });