Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
InferenceClient
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
4 / 4
17
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 embed
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
6
 rerank
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
8
 listModels
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Mbvb1223\Pinecone\Inference;
6
7use GuzzleHttp\Client;
8use GuzzleHttp\Exception\GuzzleException;
9use Mbvb1223\Pinecone\Errors\PineconeException;
10use Mbvb1223\Pinecone\Errors\PineconeValidationException;
11use Mbvb1223\Pinecone\Utils\Configuration;
12use Mbvb1223\Pinecone\Utils\HandlesApiResponse;
13
14class InferenceClient
15{
16    use HandlesApiResponse;
17
18    private Client $httpClient;
19
20    public function __construct(Configuration $config)
21    {
22        $this->httpClient = new Client([
23            'base_uri' => $config->getControllerHost(),
24            'timeout' => $config->getTimeout(),
25            'headers' => $config->getDefaultHeaders(),
26        ]);
27    }
28
29    /**
30     * Generate embeddings for the given inputs.
31     *
32     * @param string $model The embedding model to use.
33     * @param array<int, string|array{text: string}> $inputs Array of input strings or objects with 'text' key.
34     * @param array<string, mixed> $parameters Optional model-specific parameters.
35     * @return array<string, mixed> The embedding response.
36     */
37    public function embed(string $model, array $inputs, array $parameters = []): array
38    {
39        if (empty($model)) {
40            throw new PineconeValidationException('Model name is required for embedding.');
41        }
42
43        if (empty($inputs)) {
44            throw new PineconeValidationException('At least one input is required for embedding.');
45        }
46
47        try {
48            // Normalize inputs: accept strings or objects with 'text' key
49            $normalizedInputs = array_map(function ($input) {
50                if (is_string($input)) {
51                    return ['text' => $input];
52                }
53
54                return $input;
55            }, $inputs);
56
57            $payload = [
58                'model' => $model,
59                'inputs' => $normalizedInputs,
60            ];
61
62            if (!empty($parameters)) {
63                $payload['parameters'] = (object) $parameters;
64            }
65
66            $response = $this->httpClient->post('/embed', [
67                'json' => $payload,
68            ]);
69
70            return $this->handleResponse($response);
71        } catch (GuzzleException $e) {
72            throw new PineconeException('Failed to generate embeddings: ' . $e->getMessage(), 0, $e);
73        }
74    }
75
76    /**
77     * Rerank documents by relevance to a query.
78     *
79     * @param string $model The reranking model to use.
80     * @param string $query The query to rank against.
81     * @param array<int, array<string, string>> $documents The documents to rerank.
82     * @param int $topN Number of top results to return (0 means return all).
83     * @param bool $returnDocuments Whether to include documents in the response.
84     * @param array<int, string> $rankFields Fields to use for ranking.
85     * @param array<string, mixed> $parameters Optional model-specific parameters.
86     * @return array<string, mixed> The reranking response.
87     */
88    public function rerank(
89        string $model,
90        string $query,
91        array $documents,
92        int $topN = 0,
93        bool $returnDocuments = true,
94        array $rankFields = [],
95        array $parameters = [],
96    ): array {
97        if (empty($model)) {
98            throw new PineconeValidationException('Model name is required for reranking.');
99        }
100
101        if (empty($query)) {
102            throw new PineconeValidationException('Query is required for reranking.');
103        }
104
105        if (empty($documents)) {
106            throw new PineconeValidationException('At least one document is required for reranking.');
107        }
108
109        try {
110            $payload = [
111                'model' => $model,
112                'query' => $query,
113                'documents' => $documents,
114                'return_documents' => $returnDocuments,
115            ];
116
117            if ($topN > 0) {
118                $payload['top_n'] = $topN;
119            }
120
121            if (!empty($rankFields)) {
122                $payload['rank_fields'] = $rankFields;
123            }
124
125            if (!empty($parameters)) {
126                $payload['parameters'] = (object) $parameters;
127            }
128
129            $response = $this->httpClient->post('/rerank', [
130                'json' => $payload,
131            ]);
132
133            return $this->handleResponse($response);
134        } catch (GuzzleException $e) {
135            throw new PineconeException('Failed to rerank documents: ' . $e->getMessage(), 0, $e);
136        }
137    }
138
139    /** @return array<string, mixed> */
140    public function listModels(): array
141    {
142        try {
143            $response = $this->httpClient->get('/models');
144
145            return $this->handleResponse($response);
146        } catch (GuzzleException $e) {
147            throw new PineconeException('Failed to list models: ' . $e->getMessage(), 0, $e);
148        }
149    }
150}