config['model'] ?? 'gpt-4o-mini'; $body = [ 'model' => $model, 'messages' => [ ['role' => 'user', 'content' => $prompt], ], ]; // Ask the API to guarantee valid JSON back, for callers that need // structured output (e.g. a storyboard's scene breakdown). The // prompt itself must still ask for JSON — this only enforces it. if ($options['json_mode'] ?? false) { $body['response_format'] = ['type' => 'json_object']; } try { $response = Http::withToken($provider->api_key) ->post('https://api.openai.com/v1/chat/completions', $body); } catch (ConnectionException $e) { throw new AiGenerationException("could not connect to OpenAI: {$e->getMessage()}"); } if ($response->failed()) { throw new AiGenerationException($response->json('error.message') ?? 'unexpected response from OpenAI'); } $content = $response->json('choices.0.message.content'); if (! $content) { throw new AiGenerationException('unexpected response from OpenAI: missing message content'); } return new AiGenerationResult($content, 'text/plain', $response->json()); } }