This commit is contained in:
@@ -77,6 +77,42 @@ function ai_backend_fail(int $httpStatus, string $message): never
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dekodiert eine JSON-Antwort des LLM. Modelle setzen trotz entsprechender Anweisung gelegentlich
|
||||
* Markdown-Codezäune oder einen kurzen Begleittext um das eigentliche JSON. Diese rein
|
||||
* syntaktischen Zusätze sollen eine ansonsten gültige Antwort nicht unbrauchbar machen.
|
||||
*/
|
||||
function ai_backend_decode_json_response(string $content): ?array
|
||||
{
|
||||
$content = trim($content, "\xEF\xBB\xBF \t\n\r\0\x0B");
|
||||
$candidates = [$content];
|
||||
|
||||
if (preg_match('/```(?:json)?\s*([\s\S]*?)\s*```/i', $content, $match) === 1) {
|
||||
$candidates[] = trim($match[1]);
|
||||
}
|
||||
|
||||
$firstBrace = strpos($content, '{');
|
||||
$lastBrace = strrpos($content, '}');
|
||||
if ($firstBrace !== false && $lastBrace !== false && $lastBrace >= $firstBrace) {
|
||||
$candidates[] = substr($content, $firstBrace, $lastBrace - $firstBrace + 1);
|
||||
}
|
||||
|
||||
foreach (array_unique($candidates) as $candidate) {
|
||||
$decoded = json_decode($candidate, true);
|
||||
if (is_array($decoded) && json_last_error() === JSON_ERROR_NONE) {
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
error_log(sprintf(
|
||||
'LLM JSON parse failed: %s; length=%d; sha256=%s',
|
||||
json_last_error_msg(),
|
||||
strlen($content),
|
||||
hash('sha256', $content)
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft das konfigurierte LLM (oder den FakeProvider für lokale Tests, siehe README.md) auf und
|
||||
* verrechnet die echten Token-Kosten gegen das Guthaben des Nutzers. Gemeinsame Logik für jeden
|
||||
|
||||
Reference in New Issue
Block a user