Skip to content

Commit

Permalink
refactor: remove visitor infavor of single converter class
Browse files Browse the repository at this point in the history
  • Loading branch information
chr-hertel committed Feb 21, 2025
1 parent 9271a5b commit 95b21cb
Show file tree
Hide file tree
Showing 22 changed files with 281 additions and 194 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ PINECONE_HOST=
RUN_EXPENSIVE_EXAMPLES=false

# For using Gemini
GOOGLE_API_KEY=
GOOGLE_API_KEY=
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ LLM Chain categorizes two main types of models: **Language Models** and **Embedd
Language Models, like GPT, Claude and Llama, as essential centerpiece of LLM applications
and Embeddings Models as supporting models to provide vector representations of text.

Those models are provided by different **platforms**, like OpenAI, Azure, Replicate, and others.
Those models are provided by different **platforms**, like OpenAI, Azure, Google, Replicate, and others.

#### Example Instantiation

Expand All @@ -63,6 +63,7 @@ $embeddings = new Embeddings();
* [OpenAI's GPT](https://platform.openai.com/docs/models/overview) with [OpenAI](https://platform.openai.com/docs/overview) and [Azure](https://learn.microsoft.com/azure/ai-services/openai/concepts/models) as Platform
* [Anthropic's Claude](https://www.anthropic.com/claude) with [Anthropic](https://www.anthropic.com/) as Platform
* [Meta's Llama](https://www.llama.com/) with [Ollama](https://ollama.com/) and [Replicate](https://replicate.com/) as Platform
* [Google's Gemini](https://gemini.google.com/) with [Google](https://ai.google.dev/) as Platform
* [Google's Gemini](https://gemini.google.com/) with [OpenRouter](https://www.openrouter.com/) as Platform
* [DeepSeek's R1](https://www.deepseek.com/) with [OpenRouter](https://www.openrouter.com/) as Platform
* Embeddings Models
Expand Down
4 changes: 2 additions & 2 deletions examples/chat-gemini-google.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

use PhpLlm\LlmChain\Bridge\Google\GoogleModel;
use PhpLlm\LlmChain\Bridge\Google\Gemini;
use PhpLlm\LlmChain\Bridge\Google\PlatformFactory;
use PhpLlm\LlmChain\Chain;
use PhpLlm\LlmChain\Model\Message\Message;
Expand All @@ -16,7 +16,7 @@
}

$platform = PlatformFactory::create($_ENV['GOOGLE_API_KEY']);
$llm = new GoogleModel(GoogleModel::GEMINI_2_FLASH);
$llm = new Gemini(Gemini::GEMINI_2_FLASH);

$chain = new Chain($platform, $llm);
$messages = new MessageBag(
Expand Down
32 changes: 32 additions & 0 deletions examples/image-describer-binary-gemini.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use PhpLlm\LlmChain\Bridge\Google\Gemini;
use PhpLlm\LlmChain\Bridge\Google\PlatformFactory;
use PhpLlm\LlmChain\Chain;
use PhpLlm\LlmChain\Model\Message\Content\Image;
use PhpLlm\LlmChain\Model\Message\Message;
use PhpLlm\LlmChain\Model\Message\MessageBag;
use Symfony\Component\Dotenv\Dotenv;

require_once dirname(__DIR__).'/vendor/autoload.php';
(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');

if (empty($_ENV['GOOGLE_API_KEY'])) {
echo 'Please set the GOOGLE_API_KEY environment variable.'.PHP_EOL;
exit(1);
}

$platform = PlatformFactory::create($_ENV['GOOGLE_API_KEY']);
$llm = new Gemini(Gemini::GEMINI_1_5_FLASH);

$chain = new Chain($platform, $llm);
$messages = new MessageBag(
Message::forSystem('You are an image analyzer bot that helps identify the content of images.'),
Message::ofUser(
'Describe the image as a comedian would do it.',
new Image(dirname(__DIR__).'/tests/Fixture/image.jpg'),
),
);
$response = $chain->call($messages);

echo $response->getContent().PHP_EOL;
33 changes: 33 additions & 0 deletions examples/stream-google-gemini.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

use PhpLlm\LlmChain\Bridge\Google\Gemini;
use PhpLlm\LlmChain\Bridge\Google\PlatformFactory;
use PhpLlm\LlmChain\Chain;
use PhpLlm\LlmChain\Model\Message\Message;
use PhpLlm\LlmChain\Model\Message\MessageBag;
use Symfony\Component\Dotenv\Dotenv;

require_once dirname(__DIR__).'/vendor/autoload.php';
(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');

if (empty($_ENV['GOOGLE_API_KEY'])) {
echo 'Please set the GOOGLE_API_KEY environment variable.'.PHP_EOL;
exit(1);
}

$platform = PlatformFactory::create($_ENV['GOOGLE_API_KEY']);
$llm = new Gemini(Gemini::GEMINI_2_FLASH);

$chain = new Chain($platform, $llm);
$messages = new MessageBag(
Message::forSystem('You are a funny clown that entertains people.'),
Message::ofUser('What is the purpose of an ant?'),
);
$response = $chain->call($messages, [
'stream' => true, // enable streaming of response text
]);

foreach ($response->getContent() as $word) {
echo $word;
}
echo PHP_EOL;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use PhpLlm\LlmChain\Model\LanguageModel;

final readonly class GoogleModel implements LanguageModel
final readonly class Gemini implements LanguageModel
{
public const GEMINI_2_FLASH = 'gemini-2.0-flash';
public const GEMINI_2_PRO = 'gemini-2.0-pro-exp-02-05';
Expand Down Expand Up @@ -35,12 +35,12 @@ public function getOptions(): array

public function supportsAudioInput(): bool
{
return false; // it does, but implementation here is still open; in_array($this->version, [self::GEMINI_2_FLASH, self::GEMINI_2_PRO, self::GEMINI_1_5_FLASH], true);
return false; // it does, but implementation here is still open
}

public function supportsImageInput(): bool
{
return false; // it does, but implementation here is still open;in_array($this->version, [self::GEMINI_2_FLASH, self::GEMINI_2_PRO, self::GEMINI_2_FLASH_LITE, self::GEMINI_2_FLASH_THINKING, self::GEMINI_1_5_FLASH], true);
return true;
}

public function supportsStreaming(): bool
Expand All @@ -50,11 +50,11 @@ public function supportsStreaming(): bool

public function supportsStructuredOutput(): bool
{
return false;
return false; // it does, but implementation here is still open
}

public function supportsToolCalling(): bool
{
return false;
return false; // it does, but implementation here is still open
}
}
77 changes: 77 additions & 0 deletions src/Bridge/Google/GooglePromptConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Bridge\Google;

use PhpLlm\LlmChain\Model\Message\AssistantMessage;
use PhpLlm\LlmChain\Model\Message\Content\Image;
use PhpLlm\LlmChain\Model\Message\Content\Text;
use PhpLlm\LlmChain\Model\Message\MessageBagInterface;
use PhpLlm\LlmChain\Model\Message\MessageInterface;
use PhpLlm\LlmChain\Model\Message\Role;
use PhpLlm\LlmChain\Model\Message\UserMessage;

use function Symfony\Component\String\u;

final class GooglePromptConverter
{
/**
* @return array{
* contents: list<array{
* role: 'model'|'user',
* parts: list<array{inline_data?: array{mime_type: string, data: string}|array{text: string}}>
* }>,
* system_instruction?: array{parts: array{text: string}}
* }
*/
public function convertToPrompt(MessageBagInterface $bag): array
{
$body = ['contents' => []];

$systemMessage = $bag->getSystemMessage();
if (null !== $systemMessage) {
$body['system_instruction'] = [
'parts' => ['text' => $systemMessage->content],
];
}

foreach ($bag->withoutSystemMessage()->getMessages() as $message) {
$body['contents'][] = [
'role' => $message->getRole()->equals(Role::Assistant) ? 'model' : 'user',
'parts' => $this->convertMessage($message),
];
}

return $body;
}

/**
* @return list<array{inline_data?: array{mime_type: string, data: string}|array{text: string}}>
*/
private function convertMessage(MessageInterface $message): array
{
if ($message instanceof AssistantMessage) {
return [['text' => $message->content]];
}

if ($message instanceof UserMessage) {
$parts = [];
foreach ($message->content as $content) {
if ($content instanceof Text) {
$parts[] = ['text' => $content->text];
}
if ($content instanceof Image) {
$parts[] = ['inline_data' => [
'mime_type' => u($content->url)->after('data:')->before(';')->toString(),
'data' => u($content->url)->after('base64,')->toString(),
]];
}
}

return $parts;
}

return [];
}
}
97 changes: 0 additions & 97 deletions src/Bridge/Google/GoogleRequestBodyProducer.php

This file was deleted.

Loading

0 comments on commit 95b21cb

Please sign in to comment.