dolibarr 20.0.0
ai.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2024 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * or see https://www.gnu.org/
18 */
19
26require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
27require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
28
29
33class Ai
34{
38 protected $db;
39
43 private $apiService;
44
48 private $apiKey;
49
53 private $apiEndpoint;
54
55
62 public function __construct($db)
63 {
64 $this->db = $db;
65
66 // Get API key according to enabled AI
67 $this->apiService = getDolGlobalString('AI_API_SERVICE', 'chatgpt');
68 $this->apiKey = getDolGlobalString('AI_API_'.strtoupper($this->apiService).'_KEY');
69 }
70
80 public function generateContent($instructions, $model = 'auto', $function = 'textgeneration', $format = '')
81 {
82 if (empty($this->apiKey)) {
83 return array('error' => true, 'message' => 'API key is not defined for the AI enabled service '.$this->apiService);
84 }
85
86 if (empty($this->apiEndpoint)) {
87 if ($function == 'imagegeneration') {
88 if ($this->apiService == 'chatgpt') {
89 $this->apiEndpoint = 'https://api.openai.com/v1/images/generations';
90 if ($model == 'auto') {
91 $model = getDolGlobalString('AI_API_CHATGPT_MODEL_IMAGE', 'dall-e-3');
92 }
93 }
94 } elseif ($function == 'audiotext') {
95 if ($this->apiService == 'chatgpt') {
96 $this->apiEndpoint = 'https://api.openai.com/v1/audio/speech';
97 if ($model == 'auto') {
98 $model = getDolGlobalString('AI_API_CHATGPT_MODEL_AUDIO', 'tts-1');
99 }
100 }
101 } elseif ($function == 'transcription') {
102 if ($this->apiService == 'chatgpt') {
103 $this->apiEndpoint = 'https://api.openai.com/v1/audio/transcriptions';
104 if ($model == 'auto') {
105 $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TRANSCRIPT', 'whisper-1');
106 }
107 }
108 } elseif ($function == 'translation') {
109 if ($this->apiService == 'chatgpt') {
110 $this->apiEndpoint = 'https://api.openai.com/v1/audio/translations';
111 if ($model == 'auto') {
112 $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TRANSLATE', 'whisper-1');
113 }
114 }
115 } else { // else textgeneration...
116 if ($this->apiService == 'groq') {
117 $this->apiEndpoint = 'https://api.groq.com/openai/v1/chat/completions';
118 if ($model == 'auto') {
119 $model = getDolGlobalString('AI_API_GROK_MODEL_TEXT', 'mixtral-8x7b-32768'); // 'llama3-8b-8192', 'gemma-7b-it'
120 }
121 } elseif ($this->apiService == 'chatgpt') {
122 $this->apiEndpoint = 'https://api.openai.com/v1/chat/completions';
123 if ($model == 'auto') {
124 $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TEXT', 'gpt-3.5-turbo');
125 }
126 }
127 }
128 }
129
130 dol_syslog("Call API for apiEndpoint=".$this->apiEndpoint." apiKey=".substr($this->apiKey, 0, 3).'***********, model='.$model);
131
132 try {
133 if (empty($this->apiEndpoint)) {
134 throw new Exception('The AI service '.$this->apiService.' is not yet supported for the type of request '.$function);
135 }
136
137 $configurationsJson = getDolGlobalString('AI_CONFIGURATIONS_PROMPT');
138 $configurations = json_decode($configurationsJson, true);
139
140 $prePrompt = '';
141 $postPrompt = '';
142
143 if (isset($configurations[$function])) {
144 if (isset($configurations[$function]['prePrompt'])) {
145 $prePrompt = $configurations[$function]['prePrompt'];
146 }
147
148 if (isset($configurations[$function]['postPrompt'])) {
149 $postPrompt = $configurations[$function]['postPrompt'];
150 }
151 }
152 $fullInstructions = $prePrompt.' '.$instructions.' .'.$postPrompt;
153
154
155 $payload = json_encode([
156 'messages' => [
157 ['role' => 'user', 'content' => $fullInstructions]
158 ],
159 'model' => $model
160 ]);
161
162 $headers = ([
163 'Authorization: Bearer ' . $this->apiKey,
164 'Content-Type: application/json'
165 ]);
166 $response = getURLContent($this->apiEndpoint, 'POST', $payload, 1, $headers);
167
168 if (empty($response['http_code'])) {
169 throw new Exception('API request failed. No http received');
170 }
171 if (!empty($response['http_code']) && $response['http_code'] != 200) {
172 throw new Exception('API request failed with status code ' . $response['http_code']);
173 }
174 // Decode JSON response
175 $decodedResponse = json_decode($response['content'], true);
176
177 // Extraction content
178 $generatedContent = $decodedResponse['choices'][0]['message']['content'];
179
180 dol_syslog("generatedContent=".$generatedContent);
181
182 // If content is not HTML, we convert it into HTML
183 if ($format == 'html') {
184 if (!dol_textishtml($generatedContent)) {
185 dol_syslog("Result was detected as not HTML so we convert it into HTML.");
186 $generatedContent = dol_nl2br($generatedContent);
187 } else {
188 dol_syslog("Result was detected as already HTML. Do nothing.");
189 }
190
191 // TODO If content is for website module, we must
192 // - clan html header, keep body only and remove ``` ticks added by AI
193 // - add tags <section contenEditable="true"> </section>
194 }
195
196 return $generatedContent;
197 } catch (Exception $e) {
198 $errormessage = $e->getMessage();
199 if (!empty($response['content'])) {
200 $decodedResponse = json_decode($response['content'], true);
201
202 // With OpenAI, error is into an object error into the content
203 if (!empty($decodedResponse['error']['message'])) {
204 $errormessage .= ' - '.$decodedResponse['error']['message'];
205 }
206 }
207
208 return array(
209 'error' => true,
210 'message' => $errormessage,
211 'code' => (empty($response['http_code']) ? 0 : $response['http_code']),
212 'curl_error_no' => (empty($response['curl_error_no']) ? $response['curl_error_no'] : ''),
213 'format' => $format,
214 'service' => $this->apiService,
215 'function'=>$function
216 );
217 }
218 }
219}
Class for AI.
Definition ai.class.php:34
generateContent($instructions, $model='auto', $function='textgeneration', $format='')
Generate response of instructions.
Definition ai.class.php:80
__construct($db)
Constructor.
Definition ai.class.php:62
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1)
Function to get a content from an URL (use proxy if proxy defined).