88 public function generateContent($instructions, $model =
'auto', $function =
'textgeneration', $format =
'')
90 global $dolibarr_main_data_root;
95 if (empty($this->apiKey) && in_array($this->apiService, array(
'chatgpt',
'groq',
'mistral'))) {
96 return array(
'error' =>
true,
'message' =>
'API key is not defined for the AI enabled service ('.$this->apiService.
')');
101 if (empty($this->apiEndpoint) && $this->apiService ==
'custom' && !
getDolGlobalString(
'AI_API_CUSTOM_URL')) {
102 return array(
'error' =>
true,
'message' =>
'API URL is not defined for the AI enabled service ('.$this->apiService.
')');
106 if (empty($this->apiEndpoint)) {
108 if ($function ==
'imagegeneration') {
109 $this->apiEndpoint =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_URL', $arrayofai[$this->apiService][
'url']);
110 $this->apiEndpoint .= (preg_match(
'/\/$/', $this->apiEndpoint) ?
'' :
'/').
'images/generations';
111 } elseif ($function ==
'audiogeneration') {
112 $this->apiEndpoint =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_URL', $arrayofai[$this->apiService][
'url']);
113 $this->apiEndpoint .= (preg_match(
'/\/$/', $this->apiEndpoint) ?
'' :
'/').
'audio/speech';
114 } elseif ($function ==
'transcription') {
115 $this->apiEndpoint =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_URL', $arrayofai[$this->apiService][
'url']);
116 $this->apiEndpoint .= (preg_match(
'/\/$/', $this->apiEndpoint) ?
'' :
'/').
'transcriptions';
118 $this->apiEndpoint =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_URL', $arrayofai[$this->apiService][
'url']);
119 $this->apiEndpoint .= (preg_match(
'/\/$/', $this->apiEndpoint) ?
'' :
'/').
'chat/completions';
125 if (empty($model) || $model ==
'auto') {
127 if ($function ==
'imagegeneration') {
128 $model =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_MODEL_IMAGE', $arrayofai[$this->apiService][$function]);
129 } elseif ($function ==
'audiogeneration') {
130 $model =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_MODEL_AUDIO', $arrayofai[$this->apiService][$function]);
131 } elseif ($function ==
'transcription') {
132 $model =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_MODEL_TRANSCRIPT', $arrayofai[$this->apiService][$function]);
133 } elseif ($function ==
'translation') {
134 $model =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_MODEL_TRANSLATE', $arrayofai[$this->apiService][$function]);
137 $model =
getDolGlobalString(
'AI_API_'.strtoupper($this->apiService).
'_MODEL_TEXT', $arrayofai[$this->apiService][
'textgeneration']);
141 dol_syslog(
"Call API for apiKey=".substr($this->apiKey, 0, 5).
'***********, apiEndpoint='.$this->apiEndpoint.
", model=".$model);
146 if (empty($this->apiEndpoint)) {
147 throw new Exception(
'The AI service '.$this->apiService.
' is not yet supported for the type of request '.$function);
151 $configurations = json_decode($configurationsJson,
true);
156 if (isset($configurations[$function])) {
157 if (isset($configurations[$function][
'prePrompt'])) {
158 $prePrompt = $configurations[$function][
'prePrompt'];
161 if (isset($configurations[$function][
'postPrompt'])) {
162 $postPrompt = $configurations[$function][
'postPrompt'];
167 if (empty($prePrompt) && $function ==
'textgenerationemail') {
168 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_EMAIL;
170 if (empty($prePrompt) && $function ==
'textgenerationwebpage') {
171 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_WEBPAGE;
173 if (empty($prePrompt) && $function ==
'textgenerationextrafield') {
174 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_EXTRAFIELD_FILLER;
176 if (empty($prePrompt) && $function ==
'texttranslation') {
177 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_TEXT_TRANSLATION;
179 if (empty($prePrompt) && $function ==
'textsummarize') {
180 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_TEXT_SUMMARIZE;
182 if (empty($prePrompt) && $function ==
'textrephraser') {
183 $prePrompt = self::AI_DEFAULT_PROMPT_FOR_TEXT_REPHRASER;
186 $fullInstructions = $instructions.($postPrompt ? (preg_match(
'/[\.\!\?]$/', $instructions) ?
'' :
'.').
' '.$postPrompt :
'');
212 $arrayforpayload = array(
213 'messages' => array(array(
'role' =>
'user',
'content' => $fullInstructions)),
218 $addDateTimeContext =
false;
219 if ($addDateTimeContext) {
220 $prePrompt = ($prePrompt ? $prePrompt.(preg_match(
'/[\.\!\?]$/', $prePrompt) ?
'' :
'.').
' ' :
'').
'Today we are '.
dol_print_date(
dol_now(),
'dayhourtext');
223 $arrayforpayload[
'messages'][] = array(
'role' =>
'system',
'content' => $prePrompt);
232 $payload = json_encode($arrayforpayload);
235 'Authorization: Bearer ' . $this->apiKey,
236 'Content-Type: application/json'
240 if (@is_writable($dolibarr_main_data_root)) {
241 $outputfile = $dolibarr_main_data_root.
"/dolibarr_ai.log";
242 $fp = fopen($outputfile,
"w");
245 fwrite($fp,
"Call endpoint ".$this->apiEndpoint.
" with POST and the following HTTP headers and Payload:\n");
246 fwrite($fp, var_export($headers,
true).
"\n");
247 fwrite($fp, var_export($payload,
true).
"\n");
257 global $dolibarr_ai_allow_local_endpoints;
258 $localurl = $dolibarr_ai_allow_local_endpoints ?? 0;
260 $response =
getURLContent($this->apiEndpoint,
'POST', $payload, 1, $headers, array(
'http',
'https'), $localurl);
262 if (empty($response[
'http_code'])) {
263 throw new Exception(
'API request failed. No http received');
265 if (!empty($response[
'http_code']) && $response[
'http_code'] != 200) {
266 if (in_array($response[
'http_code'], array(400, 401, 403, 429)) && !empty($response[
'content'])) {
267 $tmp = json_decode($response[
'content'],
true);
268 if (!empty($tmp[
'message'])) {
271 'message' => $tmp[
'message'],
272 'code' => (empty($response[
'http_code']) ? 0 : $response[
'http_code']),
273 'curl_error_no' => (empty($response[
'curl_error_no']) ? 0 : $response[
'curl_error_no']),
275 'service' => $this->apiService,
276 'function' => $function
280 throw new Exception(
'API request on AI endpoint '.$this->apiEndpoint.
' failed with status code '.$response[
'http_code']);
284 if (@is_writable($dolibarr_main_data_root)) {
285 $outputfile = $dolibarr_main_data_root.
"/dolibarr_ai.log";
286 $fp = fopen($outputfile,
"a");
289 fwrite($fp, var_export((empty($response[
'content']) ?
'No content result' : $response[
'content']),
true).
"\n");
299 $decodedResponse = json_decode($response[
'content'],
true);
302 if (!empty($decodedResponse[
'error'])) {
303 if (is_scalar($decodedResponse[
'error'])) {
304 $generatedContent = $decodedResponse[
'error'];
306 $generatedContent = var_export($decodedResponse[
'error'],
true);
309 $generatedContent = $decodedResponse[
'choices'][0][
'message'][
'content'];
314 if ($format ==
'html') {
316 dol_syslog(
"Result was detected as not HTML so we convert it into HTML.");
317 $generatedContent =
dol_nl2br($generatedContent);
319 dol_syslog(
"Result was detected as already HTML. Do nothing.");
327 return $generatedContent;
329 $errormessage = $e->getMessage();
330 $errormessagelog = $e->getMessage();
331 if (!empty($response[
'content'])) {
332 $decodedResponse = json_decode($response[
'content'],
true);
333 $errormessagelog .=
' - '.$response[
'content'];
335 if (!empty($decodedResponse[
'error'][
'message'])) {
337 $errormessage .=
' - '.$decodedResponse[
'error'][
'message'];
339 $errormessage .=
' - '.$response[
'content'];
344 if (@is_writable($dolibarr_main_data_root)) {
345 $outputfile = $dolibarr_main_data_root.
"/dolibarr_ai.log";
346 $fp = fopen($outputfile,
"a");
349 fwrite($fp,
"Error: ".$errormessagelog.
"\n");
359 'message' => $errormessage,
360 'code' => (empty($response[
'http_code']) ? 0 : $response[
'http_code']),
361 'curl_error_no' => (empty($response[
'curl_error_no']) ? 0 : $response[
'curl_error_no']),
363 'service' => $this->apiService,
364 'function' => $function