]> BookStack Code Mirror - bookstack/blob - app/Search/Vectors/Services/OpenAiVectorQueryService.php
Vectors: Added command to regenerate for all
[bookstack] / app / Search / Vectors / Services / OpenAiVectorQueryService.php
1 <?php
2
3 namespace BookStack\Search\Vectors\Services;
4
5 use BookStack\Http\HttpRequestService;
6
7 class OpenAiVectorQueryService implements VectorQueryService
8 {
9     protected string $key;
10     protected string $endpoint;
11     protected string $embeddingModel;
12     protected string $queryModel;
13
14     public function __construct(
15         protected array $options,
16         protected HttpRequestService $http,
17     ) {
18         // TODO - Some kind of validation of options
19         $this->key = $this->options['key'] ?? '';
20         $this->endpoint = $this->options['endpoint'] ?? '';
21         $this->embeddingModel = $this->options['embedding_model'] ?? '';
22         $this->queryModel = $this->options['query_model'] ?? '';
23     }
24
25     protected function jsonRequest(string $method, string $uri, array $data): array
26     {
27         $fullUrl = rtrim($this->endpoint, '/') . '/' . ltrim($uri, '/');
28         $client = $this->http->buildClient(30);
29         $request = $this->http->jsonRequest($method, $fullUrl, $data)
30             ->withHeader('Authorization', 'Bearer ' . $this->key);
31
32         $response = $client->sendRequest($request);
33         return json_decode($response->getBody()->getContents(), true);
34     }
35
36     public function generateEmbeddings(string $text): array
37     {
38         $response = $this->jsonRequest('POST', 'v1/embeddings', [
39             'input' => $text,
40             'model' => $this->embeddingModel,
41         ]);
42
43         return $response['data'][0]['embedding'];
44     }
45
46     public function query(string $input, array $context): string
47     {
48         $formattedContext = implode("\n", $context);
49
50         $response = $this->jsonRequest('POST', 'v1/chat/completions', [
51             'model' => $this->queryModel,
52             'messages' => [
53                 [
54                     'role' => 'developer',
55                     'content' => 'You are a helpful assistant providing search query responses. Be specific, factual and to-the-point in response. Don\'t try to converse or continue the conversation.'
56                 ],
57                 [
58                     'role' => 'user',
59                     'content' => "Provide a response to the below given QUERY using the below given CONTEXT. The CONTEXT is split into parts via lines. Ignore any nonsensical lines of CONTEXT.\nQUERY: {$input}\n\nCONTEXT: {$formattedContext}",
60                 ]
61             ],
62         ]);
63
64         return $response['choices'][0]['message']['content'] ?? '';
65     }
66 }