From 0e75715befa99d4e36295ee955785911156382b7 Mon Sep 17 00:00:00 2001 From: Pierre SOUVIGNET Date: Fri, 21 Nov 2025 10:14:41 +0100 Subject: [PATCH] [Vector] Introduce to VectorTools class --- src/Agent.php | 24 ++++++++++ src/AgentFactory/Agent/Gemini.php | 3 +- src/AgentFactory/Agent/OpenAI.php | 5 +- src/AgentFactory/Agent/SelfHost.php | 3 +- src/AgentFactory/AgentInterface.php | 3 +- src/Vector/VectorTools.php | 73 +++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/Vector/VectorTools.php diff --git a/src/Agent.php b/src/Agent.php index 68f202c..2e7ff80 100644 --- a/src/Agent.php +++ b/src/Agent.php @@ -257,4 +257,28 @@ public function withHost(string $host): self return $this; } + + /** + * Embedding + * + * @param string $model + * param string $content + */ + public function embedding(string $model, string $content): array + { + return $this->agentClass::create( + key: $this->key, + model: $model, + config: ['host' => $this->host] + ) + ->execute([], true, $content)->asVectors()[0]->getData(); + } + + /** + * getAgentClass + */ + public function getAgentClass(): string + { + return $this->agentClass; + } } diff --git a/src/AgentFactory/Agent/Gemini.php b/src/AgentFactory/Agent/Gemini.php index dbc189b..0e7f94f 100644 --- a/src/AgentFactory/Agent/Gemini.php +++ b/src/AgentFactory/Agent/Gemini.php @@ -69,9 +69,10 @@ public function setMessage(array $messages): mixed * * @param array $params * @param bool $complete + * @param ?string $textInput * @return mixed */ - public function execute(array $params, bool $complete = false): mixed + public function execute(array $params, bool $complete = false,?string $textInput = null): mixed { $params = $this->normalizeParams($params); diff --git a/src/AgentFactory/Agent/OpenAI.php b/src/AgentFactory/Agent/OpenAI.php index 879c5bf..74bfe3e 100644 --- a/src/AgentFactory/Agent/OpenAI.php +++ b/src/AgentFactory/Agent/OpenAI.php @@ -69,11 +69,12 @@ public function setMessage(array $messages): mixed * * @param array $params * @param bool $complete + * @param ?string $textInput * @return mixed */ - public function execute(array $params, bool $complete = false): mixed + public function execute(array $params, bool $complete = false, ?string $textInput = null): mixed { - $result = $this->platform->invoke($this->model, $this->messages, $params); + $result = $this->platform->invoke($this->model, $textInput ?? $this->messages, $params); return $complete ? $result : $result->asText(); } diff --git a/src/AgentFactory/Agent/SelfHost.php b/src/AgentFactory/Agent/SelfHost.php index a4e56f8..762b655 100644 --- a/src/AgentFactory/Agent/SelfHost.php +++ b/src/AgentFactory/Agent/SelfHost.php @@ -103,9 +103,10 @@ public function setMessage(array $messages): mixed * * @param array $params * @param bool $complete + * @param ?string $textInput * @return mixed */ - public function execute(array $params, bool $complete = false): mixed + public function execute(array $params, bool $complete = false, ?string $textInput = null): mixed { $result = $this->platform->invoke($this->model, $this->messages, $params); return $complete ? $result : $result->asText(); diff --git a/src/AgentFactory/AgentInterface.php b/src/AgentFactory/AgentInterface.php index d13e7cc..3cee40b 100644 --- a/src/AgentFactory/AgentInterface.php +++ b/src/AgentFactory/AgentInterface.php @@ -29,9 +29,10 @@ public function setMessage(array $messages): mixed; * * @param array $params * @param bool $complete + * @param ?string $textInput * @return mixed */ - public function execute(array $params, bool $complete = false): mixed; + public function execute(array $params, bool $complete = false, ?string $textInput = null): mixed; /** * Converts raw message data into a MessageBag instance. diff --git a/src/Vector/VectorTools.php b/src/Vector/VectorTools.php new file mode 100644 index 0000000..b907d43 --- /dev/null +++ b/src/Vector/VectorTools.php @@ -0,0 +1,73 @@ + $vecA + * @param array $vecB + * + * @return float + */ + static function cosineSimilarity(array $vecA, array $vecB): float { + $dot = 0; + $normA = 0; + $normB = 0; + foreach ($vecA as $i => $val) { + $dot += $val * $vecB[$i]; + + $normA += $val * $val; + $normB += $vecB[$i] * $vecB[$i]; + } + return $dot / (sqrt($normA) * sqrt($normB) + 1e-10); + } + + /** + * Retrieve the most relevant context text for a given question embedding. + * + * @param array,content:string}> $context + * @param array $questionVector + * + * @return string + */ + static function getContext(array $context, array $questionVector): string + { + usort($context, function($a, $b) use ($questionVector) { + return VectorTools::cosineSimilarity($b['vector'], $questionVector) <=> VectorTools::cosineSimilarity($a['vector'], $questionVector); + }); + + $topChunks = array_slice($context, 0, 3); + + return implode("\n\n", array_column($topChunks, 'content')); + } + + /** + * Generate an embedding vector for the given text using the provided Agent. + * + * @param Agent $agent + * @param string $model + * @param string $content + * + * @return array + */ + static function embedding(Agent $agent, string $model, string $content): array + { + $agentClass = $agent->getAgentClass(); + + match ($agentClass) { + OpenAI::class => null, + default => throw new \RuntimeException('Embeddings are only supported for OpenAI agent.'), + }; + + return $agent->embedding($model, $content); + } +} \ No newline at end of file