Test Failed
Push — main ( 29a1e7...86a642 )
by Rafael
61:24
created

Ai::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* Copyright (C) 2024  Laurent Destailleur     <[email protected]>
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
20
namespace DoliModules\Ai\Model;
21
22
use DoliDB;
23
24
require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php";
25
require_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php';
26
27
/**
28
 * Class for AI
29
 */
30
class Ai
31
{
32
    /**
33
     * @var DoliDB $db Database object
34
     */
35
    protected $db;
36
37
    /**
38
     * @var string $apiEndpoint
39
     */
40
    private $apiEndpoint;
41
42
    /**
43
     * @var string $apiKey
44
     */
45
    private $apiKey;
46
47
    /**
48
     * Constructor
49
     *
50
     * @param DoliDB $db Database handler
51
     *
52
     */
53
    public function __construct($db)
54
    {
55
        $this->db = $db;
56
57
        $this->apiKey = getDolGlobalString('AI_API_CHATGPT_KEY');
58
    }
59
60
    /**
61
     * Generate response of instructions
62
     *
63
     * @param string $instructions Instruction to generate content
64
     * @param string $model        Model name ('gpt-3.5-turbo', 'gpt-4-turbo', 'dall-e-3', ...)
65
     * @param string $function     Code of the feature we want to use ('textgeneration', 'transcription', 'audiotext',
66
     *                             'imagegeneration', 'translation')
67
     * @param string $format       Format for output ('', 'html', ...)
68
     *
69
     * @return  mixed       $response
70
     */
71
    public function generateContent($instructions, $model = 'auto', $function = 'textgeneration', $format = '')
72
    {
73
        if (empty($this->apiKey)) {
74
            return ['error' => true, 'message' => 'API key is no defined'];
75
        }
76
77
        if (empty($this->apiEndpoint)) {
78
            if ($function == 'textgeneration') {
79
                $this->apiEndpoint = 'https://api.openai.com/v1/chat/completions';
80
                if ($model == 'auto') {
81
                    $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TEXT', 'gpt-3.5-turbo');
82
                }
83
            }
84
            if ($function == 'imagegeneration') {
85
                $this->apiEndpoint = 'https://api.openai.com/v1/images/generations';
86
                if ($model == 'auto') {
87
                    $model = getDolGlobalString('AI_API_CHATGPT_MODEL_IMAGE', 'dall-e-3');
88
                }
89
            }
90
            if ($function == 'audiotext') {
91
                $this->apiEndpoint = 'https://api.openai.com/v1/audio/speech';
92
                if ($model == 'auto') {
93
                    $model = getDolGlobalString('AI_API_CHATGPT_MODEL_AUDIO', 'tts-1');
94
                }
95
            }
96
            if ($function == 'transcription') {
97
                $this->apiEndpoint = 'https://api.openai.com/v1/audio/transcriptions';
98
                if ($model == 'auto') {
99
                    $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TRANSCRIPT', 'whisper-1');
100
                }
101
            }
102
            if ($function == 'translation') {
103
                $this->apiEndpoint = 'https://api.openai.com/v1/audio/translations';
104
                if ($model == 'auto') {
105
                    $model = getDolGlobalString('AI_API_CHATGPT_MODEL_TRANSLATE', 'whisper-1');
106
                }
107
            }
108
        }
109
110
        dol_syslog("Call API for apiEndpoint=" . $this->apiEndpoint . " apiKey=" . substr($this->apiKey, 0, 3) . '***********, model=' . $model);
111
112
        try {
113
            $configurationsJson = getDolGlobalString('AI_CONFIGURATIONS_PROMPT');
114
            $configurations = json_decode($configurationsJson, true);
115
116
            $prePrompt = '';
117
            $postPrompt = '';
118
119
            if (isset($configurations[$function])) {
120
                if (isset($configurations[$function]['prePrompt'])) {
121
                    $prePrompt = $configurations[$function]['prePrompt'];
122
                }
123
124
                if (isset($configurations[$function]['postPrompt'])) {
125
                    $postPrompt = $configurations[$function]['postPrompt'];
126
                }
127
            }
128
            $fullInstructions = $prePrompt . ' ' . $instructions . ' .' . $postPrompt;
129
130
131
            $payload = json_encode([
132
                'messages' => [
133
                    ['role' => 'user', 'content' => $fullInstructions],
134
                ],
135
                'model' => $model,
136
            ]);
137
138
            $headers = ([
139
                'Authorization: Bearer ' . $this->apiKey,
140
                'Content-Type: application/json',
141
            ]);
142
            $response = getURLContent($this->apiEndpoint, 'POST', $payload, 1, $headers);
143
144
            if (empty($response['http_code'])) {
145
                throw new Exception('API request failed. No http received');
0 ignored issues
show
Bug introduced by
The type DoliModules\Ai\Model\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
146
            }
147
            if (!empty($response['http_code']) && $response['http_code'] != 200) {
148
                throw new Exception('API request failed with status code ' . $response['http_code']);
149
            }
150
            // Decode JSON response
151
            $decodedResponse = json_decode($response['content'], true);
152
153
            // Extraction content
154
            $generatedEmailContent = $decodedResponse['choices'][0]['message']['content'];
155
156
            // If content is not HTML, we convert it into HTML
157
            if (!dol_textishtml($generatedEmailContent)) {
158
                $generatedEmailContent = dol_nl2br($generatedEmailContent);
159
            }
160
161
            return $generatedEmailContent;
162
        } catch (Exception $e) {
163
            $errormessage = $e->getMessage();
164
            if (!empty($response['content'])) {
165
                $decodedResponse = json_decode($response['content'], true);
166
167
                // With OpenAI, error is into an object error into the content
168
                if (!empty($decodedResponse['error']['message'])) {
169
                    $errormessage .= ' - ' . $decodedResponse['error']['message'];
170
                }
171
            }
172
173
            return ['error' => true, 'message' => $errormessage, 'code' => (empty($response['http_code']) ? 0 : $response['http_code']), 'curl_error_no' => (empty($response['curl_error_no']) ? $response['curl_error_no'] : '')];
174
        }
175
    }
176
}
177