1+ <?php
2+
3+ namespace Vladvildanov \PredisVl \Vectorizer ;
4+
5+ use Exception ;
6+ use GuzzleHttp \Client ;
7+ use Psr \Http \Message \ResponseInterface ;
8+
9+ class OpenAIVectorizer implements VectorizerInterface
10+ {
11+ /**
12+ * @var Client
13+ */
14+ private Client $ client ;
15+
16+ /**
17+ * @var string
18+ */
19+ private string $ model ;
20+
21+ /**
22+ * @var string
23+ */
24+ private string $ apiUrl ;
25+
26+ /**
27+ * API configuration accepts token and optional request parameters specified in documentation.
28+ *
29+ * Example: $apiConfiguration = ['token' => $token, 'requestParams' => [...]]
30+ *
31+ * @link https://platform.openai.com/docs/api-reference/embeddings/create
32+ * @param string|null $model
33+ * @param array $apiConfiguration
34+ * @param Client|null $client
35+ */
36+ public function __construct (
37+ string $ model = null ,
38+ private array $ apiConfiguration = [],
39+ Client $ client = null
40+ ) {
41+ $ this ->client = $ client ?? new Client ();
42+ $ this ->model = $ model ?? 'text-embedding-ada-002 ' ;
43+ $ this ->apiUrl = getenv ('OPENAI_API_URL ' ) ?: 'https://api.openai.com/v1/embeddings ' ;
44+ }
45+
46+ /**
47+ * @inheritDoc
48+ */
49+ public function embed (string $ text ): array
50+ {
51+ $ jsonResponse = $ this ->sendRequest (
52+ [
53+ 'model ' => $ this ->model ,
54+ 'input ' => $ text ,
55+ ]
56+ )->getBody ()->getContents ();
57+
58+ return json_decode ($ jsonResponse , true , 512 , JSON_THROW_ON_ERROR );
59+ }
60+
61+ /**
62+ * @inheritDoc
63+ */
64+ public function batchEmbed (array $ texts ): array
65+ {
66+ $ jsonResponse = $ this ->sendRequest (
67+ [
68+ 'model ' => $ this ->model ,
69+ 'input ' => $ texts ,
70+ ]
71+ )->getBody ()->getContents ();
72+
73+ return json_decode ($ jsonResponse , true , 512 , JSON_THROW_ON_ERROR );
74+ }
75+
76+ /**
77+ * @inheritDoc
78+ */
79+ public function getModel (): string
80+ {
81+ return $ this ->model ;
82+ }
83+
84+ /**
85+ * @inheritDoc
86+ */
87+ public function getConfiguration (): array
88+ {
89+ return $ this ->apiConfiguration ;
90+ }
91+
92+ /**
93+ * Returns API token associated with current vectorizer.
94+ *
95+ * @return string
96+ * @throws Exception
97+ */
98+ private function getApiToken (): string
99+ {
100+ if (array_key_exists ('token ' , $ this ->apiConfiguration )) {
101+ return $ this ->apiConfiguration ['token ' ];
102+ }
103+
104+ if (false !== $ token = getenv ('OPENAI_API_TOKEN ' )) {
105+ return $ token ;
106+ }
107+
108+ throw new Exception (
109+ 'API token should be provided in API configuration or as an environment variable. '
110+ );
111+ }
112+
113+ /**
114+ * Sends an actual request to API endpoint.
115+ *
116+ * @param array $requestBody
117+ * @return ResponseInterface
118+ * @throws Exception|\GuzzleHttp\Exception\GuzzleException
119+ */
120+ private function sendRequest (array $ requestBody ): ResponseInterface
121+ {
122+ $ requestParams = (array_key_exists ('requestParams ' , $ this ->apiConfiguration ))
123+ ? $ this ->apiConfiguration ['requestParams ' ]
124+ : [];
125+ $ requestBody = array_merge ($ requestBody , $ requestParams );
126+
127+ return $ this ->client ->post ($ this ->apiUrl , [
128+ 'headers ' => [
129+ 'Authorization ' => 'Bearer ' . $ this ->getApiToken (),
130+ 'Content-Type ' => 'application/json ' ,
131+ 'Accept ' => 'application/json ' ,
132+ ],
133+ 'json ' => $ requestBody
134+ ]);
135+ }
136+ }
0 commit comments