$candidates */ public function rank(string $ticketMessage, array $candidates): ClassificationResultDTO { if ($candidates === []) { return new ClassificationResultDTO(null, 0.0, 'No article candidates available'); } $articlesBlock = collect($candidates) ->map(fn (ArticleCandidateDTO $item, int $idx) => sprintf( "%d. article_id=%d\nTitle: %s\nContent: %s", $idx + 1, $item->articleId, $item->title, $item->content )) ->implode("\n\n"); $prompt = <<post($baseUrl.'/api/generate', [ 'model' => config('services.ollama.chat_model', 'llama3'), 'prompt' => $prompt, 'stream' => false, ]) ->throw() ->json(); } catch (Throwable $e) { throw new OllamaUnavailableException('Ollama generate endpoint is unavailable', 0, $e); } $text = (string) ($response['response'] ?? '{}'); $decoded = json_decode($text, true); if (!is_array($decoded)) { return new ClassificationResultDTO( articleId: $candidates[0]->articleId, confidence: 0.35, explanation: 'LLM returned invalid JSON; defaulted to top semantic match.', rawResponse: ['raw' => $text] ); } return new ClassificationResultDTO( articleId: isset($decoded['article_id']) ? (int) $decoded['article_id'] : $candidates[0]->articleId, confidence: isset($decoded['confidence']) ? (float) $decoded['confidence'] : 0.35, explanation: (string) ($decoded['explanation'] ?? 'No explanation provided.'), rawResponse: $decoded ); } }