_caller = $caller; } public function GetCaller() { if (is_null($this->_caller)) { $this->_caller = new DeepSeekCaller(); } return $this->_caller; } public abstract function Generate(); public function CreateMessage($role, $content) { return (Object) [ 'role' => $role, 'content' => $content ]; } /** * Create a message that includes file attachments as inlineData parts. * When files are present, the message includes a 'parts' property for Gemini's multimodal format. * When no files, falls back to standard text-only format. */ public function CreateMessageWithFiles($role, $content, $files = []) { if (empty($files)) { return $this->CreateMessage($role, $content); } $parts = []; // Add text part if (!empty($content)) { $parts[] = ['text' => (string) $content]; } // Add file parts as inlineData foreach ($files as $file) { $parts[] = [ 'inlineData' => [ 'mimeType' => $file['mimeType'], 'data' => $file['data'] ] ]; } return (Object) [ 'role' => $role, 'content' => $content, 'parts' => $parts ]; } /** * Best-effort repair of JSON whose string values contain unescaped double * quotes — the most common failure with AI-generated formulas, whose * language requires double quotes for string literals (e.g. GetText() == * "15 years"). Walks the text tracking string state: a double quote only * closes a string when the next non-whitespace character is a structural * token (:,}]); otherwise it is treated as a literal quote and escaped. * Escapes already present (\", \\, \n, ...) and multibyte characters are * preserved. Use as a fallback only, after a normal json_decode has failed. */ public function RepairUnescapedQuotes($text) { if (!is_string($text) || $text === '') { return $text; } $length = strlen($text); $result = ''; $inString = false; $escaped = false; for ($i = 0; $i < $length; $i++) { $ch = $text[$i]; if (!$inString) { $result .= $ch; if ($ch === '"') { $inString = true; } continue; } if ($escaped) { $result .= $ch; $escaped = false; continue; } if ($ch === '\\') { $result .= $ch; $escaped = true; continue; } if ($ch === '"') { $j = $i + 1; while ($j < $length && ctype_space($text[$j])) { $j++; } $next = $j < $length ? $text[$j] : ''; if ($next === '' || $next === ':' || $next === ',' || $next === '}' || $next === ']') { // Structural boundary: the real end of the string. $result .= $ch; $inString = false; } else { // A literal quote the AI forgot to escape. $result .= '\\"'; } continue; } $result .= $ch; } return $result; } }