30 public $lastRequest =
"";
33 public $lastResponse =
"";
59 public function __construct(
string $type,
string $key,
string $baseUrl,
string $model,
int $timeout)
61 $this->
type = strtolower($type);
63 $this->baseUrl = rtrim($baseUrl,
'/');
64 $this->model = $model;
65 $this->timeout = $timeout;
81 public function generate(
string $system,
string $userMsg,
string $mode =
'text', array $attachments = array()): ?
string
83 switch ($this->
type) {
85 return $this->
callAnthropic($system, $userMsg, $mode, $attachments);
87 return $this->
callGoogle($system, $userMsg, $mode, $attachments);
89 return $this->
callOpenAI($system, $userMsg, $mode, $attachments);
102 private function callOpenAI(
string $sys,
string $msg,
string $mode =
'text', array $attachments = array()): ?
string
104 $url = $this->baseUrl;
105 if (strpos($url,
'/chat/completions') ===
false && strpos($url,
'/generate') ===
false) {
106 $url .=
'/chat/completions';
112 if (!empty($attachments)) {
113 $userContent = array(array(
"type" =>
"text",
"text" => $msg));
114 foreach ($attachments as $att) {
115 $userContent[] = array(
116 "type" =>
"image_url",
117 "image_url" => array(
"url" =>
"data:".$att[
'mime'].
";base64,".$att[
'data'])
123 "model" => $this->model,
125 array(
"role" =>
"system",
"content" => $sys),
126 array(
"role" =>
"user",
"content" => $userContent)
133 if ($mode ===
'json') {
135 if (strpos($url,
'openai') !==
false || strpos($url,
'deepseek') !==
false || strpos($url,
'perplexity') !==
false || strpos($url,
'mistral') !==
false || strpos($url,
'zai') !==
false) {
136 $data[
"response_format"] = array(
"type" =>
"json_object");
140 $this->lastRequest = json_encode($data, JSON_PRETTY_PRINT);
142 return $this->
curl($url, $data, array(
"Content-Type: application/json",
"Authorization: Bearer " . $this->key));
155 private function callAnthropic(
string $sys,
string $msg,
string $mode =
'text', array $attachments = array())
158 $url = $this->baseUrl . (strpos($this->baseUrl,
'/messages') ===
false ?
'/messages' :
'');
164 if (!empty($attachments)) {
165 $userContent = array();
166 foreach ($attachments as $att) {
167 $userContent[] = array(
168 "type" => ($att[
'mime'] ===
'application/pdf' ?
"document" :
"image"),
169 "source" => array(
"type" =>
"base64",
"media_type" => $att[
'mime'],
"data" => $att[
'data'])
172 $userContent[] = array(
"type" =>
"text",
"text" => $msg);
177 "model" => $this->model,
179 "messages" => array(array(
"role" =>
"user",
"content" => $userContent)),
180 "max_tokens" => $maxTokens
183 $this->lastRequest = json_encode($data, JSON_PRETTY_PRINT);
185 return $this->
curl($url, $data, array(
"content-type: application/json",
"x-api-key: " . $this->key,
"anthropic-version: 2023-06-01"),
true);
198 private function callGoogle(
string $sys,
string $msg,
string $mode =
'text', array $attachments = array())
200 $url = $this->baseUrl;
203 if (strpos($url,
':generateContent') ===
false) {
204 if (strpos($url,
'/models/') ===
false) {
205 $url .=
"/models/" . $this->model;
207 $url .=
":generateContent";
210 $url .=
"?key=" . $this->key;
215 foreach ($attachments as $att) {
216 $parts[] = array(
"inline_data" => array(
"mime_type" => $att[
'mime'],
"data" => $att[
'data']));
218 $parts[] = array(
"text" => $sys .
"\nUser: " . $msg);
222 array(
"parts" => $parts)
224 "generationConfig" => array(
"temperature" => 0.1)
227 $this->lastRequest = json_encode($data, JSON_PRETTY_PRINT);
229 return $this->
curl($url, $data, array(
"Content-Type: application/json"),
false,
true);
242 private function curl(
string $url, array $data, array $headers,
bool $isClaude =
false,
bool $isGemini =
false): ?
string
244 include_once DOL_DOCUMENT_ROOT.
'/core/lib/geturl.lib.php';
248 global $dolibarr_ai_allow_local_endpoints;
249 $localurl = $dolibarr_ai_allow_local_endpoints ?? 0;
254 $result =
getURLContent($url,
'POST', json_encode($data), 1, $headers, array(
'http',
'https'), $localurl, -1, 0, $this->timeout);
256 $body = (
string) ($result[
'content'] ??
'');
257 $httpCode = (int) ($result[
'http_code'] ?? 0);
258 $effectiveUrl = (
string) ($result[
'url'] ?? $url);
262 $this->lastResponse =
"HTTP " . $httpCode .
" from " . $effectiveUrl .
"\n--- body (" . strlen($body) .
" bytes) ---\n" . $body;
264 if (!empty($result[
'curl_error_no'])) {
265 return "Error: cURL #" . $result[
'curl_error_no'] .
" " . $result[
'curl_error_msg'] .
" (url=" . $effectiveUrl .
")";
268 $json = json_decode($body,
true);
270 if ($json ===
null && json_last_error() !== JSON_ERROR_NONE) {
274 $snippet = substr($body, 0, 500);
275 return "Error: Invalid JSON response from API (HTTP " . $httpCode .
", " . strlen($body) .
" bytes). Body snippet: " . ($snippet !==
'' ? $snippet :
'<empty>');
278 if (isset($json[
'error'])) {
279 $msg = $json[
'error'][
'message'] ?? json_encode($json[
'error']);
280 return "Error: API " . $msg;
285 return $json[
'content'][0][
'text'] ??
null;
288 return $json[
'candidates'][0][
'content'][
'parts'][0][
'text'] ??
null;
292 return $json[
'choices'][0][
'message'][
'content'] ??
null;
curl(string $url, array $data, array $headers, bool $isClaude=false, bool $isGemini=false)
Execute HTTP Request via cURL.
__construct(string $type, string $key, string $baseUrl, string $model, int $timeout)
Constructor.
callOpenAI(string $sys, string $msg, string $mode='text', array $attachments=array())
Call OpenAI-compatible API.
callGoogle(string $sys, string $msg, string $mode='text', array $attachments=array())
Call Google Gemini API.
callAnthropic(string $sys, string $msg, string $mode='text', array $attachments=array())
Call Anthropic API (Claude)
generate(string $system, string $userMsg, string $mode='text', array $attachments=array())
Generate a response using the configured LLM provider.
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1, $timeoutconnect=0, $timeoutresponse=0, $otherCurlOptions=array(), $morelogsuffix='')
Function to get a content from an URL (use proxy if proxy defined).
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...