Skip to content

Commit

Permalink
fix: integration with providers
Browse files Browse the repository at this point in the history
  • Loading branch information
bernard-ng committed Nov 22, 2024
1 parent 66fce6a commit ddc8f2b
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 55 deletions.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,32 @@
[![License](https://poser.pugx.org/devscast/lugha/license)](https://packagist.org/packages/devscast/lugha)

> [!NOTE]
> Lugha was primarily designed to work with Symfony.
> Work in progress.
## Installation
The main idea behind this project is to provide a provider/model agnostic framework for PHP developers to build AI powered applications.
The framework is designed to be modular and extensible, allowing developers to easily add new providers.

This project is highly inspired by [LangChain](https://www.langchain.com/) and [LLPhant](https://github.com/theodo-group/LLPhant/), designed
for RAG (Retrieval-Augmented Generation) based applications with integration of Embeddings, Completion and Reranking models.

*supported providers:*

| Provider | Link | Features |
|---------------|-------------------------------------------------------|------------------------------|
| OpenAI | [openai.com](https://openai.com) | Completion, Embeddings |
| Mistral | [mistral.ai](https://mistral.ai/) | Completion, Embeddings |
| Google | [ai.google](https://ai.google/) | Completion, Embeddings |
| GitHub | [github.com](https://github.com/marketplace/models) | Completion, Embeddings |
| Anthropic | [anthropic.com](https://www.anthropic.com/) | Completion |
| Voyager.ai | [voyageai.com](https://www.voyageai.com/) | Embeddings, Reranking |
| Ollama | [ollama.com](https://ollama.com/) | Completion, Embeddings |


## Installation
```bash
composer require devscast/lugha
```


## Contributors

<a href="https://github.com/devscast/lugha/graphs/contributors" title="show all contributors">
Expand Down
2 changes: 1 addition & 1 deletion src/Model/Completion/ChatInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ interface ChatInterface
{
public function setSystemMessage(PromptTemplate|Message $message): void;

public function completion(PromptTemplate|string $prompt, ?History $history = null): string;
public function completion(PromptTemplate|History|string $input): string;
}
18 changes: 9 additions & 9 deletions src/Provider/SupportedProvider.php → src/Provider/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
namespace Devscast\Lugha\Provider;

/**
* Class SupportedProvider.
* Class Provider.
*
* @author bernard-ng <bernard@devscast.tech>
*/
enum SupportedProvider: string
enum Provider: string
{
case GOOGLE = 'google';
case MISTRAL = 'mistral';
case OPENAI = 'openai';
case OLLAMA = 'ollama';
case GITHUB = 'github';
case ANTHROPIC = 'anthropic';
case VOYAGER = 'voyager';
case GOOGLE = 'GOOGLE';
case MISTRAL = 'MISTRAL';
case OPENAI = 'OPENAI';
case OLLAMA = 'OLLAMA';
case GITHUB = 'GITHUB';
case ANTHROPIC = 'ANTHROPIC';
case VOYAGER = 'VOYAGER';
}
14 changes: 7 additions & 7 deletions src/Provider/ProviderConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@
*
* @author bernard-ng <bernard@devscast.tech>
*/
final readonly class ProviderConfig
final class ProviderConfig
{
/**
* @param string $apiKey The API key to use for the provider.
* @param string|null $apiKey The API key to use for the provider.
* @param string|null $baseUri The base URI for the provider.
* @param int|null $maxRetries The maximum number of retries to attempt in case of failure.
* @param bool $providerResponse Whether to return the full provider's response along with the result.
*/
public function __construct(
#[\SensitiveParameter]
public string $apiKey,
public ?string $baseUri = null,
public ?int $maxRetries = 2,
public bool $providerResponse = false,
public ?string $apiKey = null,
public readonly ?string $baseUri = null,
public readonly ?int $maxRetries = 2,
public readonly bool $providerResponse = false,
) {
Assert::notEmpty($this->apiKey);
Assert::nullOrNotEmpty($this->apiKey);
Assert::nullOrNotEmpty($this->baseUri);
Assert::positiveInteger($this->maxRetries);
}
Expand Down
3 changes: 3 additions & 0 deletions src/Provider/Response/CompletionResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Devscast\Lugha\Provider\Response;

use Devscast\Lugha\Provider\Provider;

/**
* Class CompletionResponse.
*
Expand All @@ -12,6 +14,7 @@
final readonly class CompletionResponse
{
public function __construct(
public Provider $provider,
public string $model,
public string $completion,
public array $providerResponse = []
Expand Down
3 changes: 3 additions & 0 deletions src/Provider/Response/EmbeddingResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace Devscast\Lugha\Provider\Response;

use Devscast\Lugha\Provider\Provider;

/**
* Class EmbeddingResponse.
*
Expand All @@ -21,6 +23,7 @@
final readonly class EmbeddingResponse
{
public function __construct(
public Provider $provider,
public string $model,
public array $embedding,
public array $providerResponse = [],
Expand Down
2 changes: 2 additions & 0 deletions src/Provider/Response/RerankingResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Devscast\Lugha\Provider\Response;

use Devscast\Lugha\Model\Reranking\RankedDocument;
use Devscast\Lugha\Provider\Provider;

/**
* Class RerankingResponse.
Expand All @@ -17,6 +18,7 @@
* @param array<RankedDocument> $documents
*/
public function __construct(
public Provider $provider,
public string $model,
public array $documents,
public array $providerResponse = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* Class AbstractClient.
* Class Client.
*
* @author bernard-ng <bernard@devscast.tech>
*/
abstract class AbstractClient
abstract class Client
{
protected const string BASE_URI = '';

Expand Down
6 changes: 3 additions & 3 deletions src/Provider/Service/Client/AnthropicClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@

namespace Devscast\Lugha\Provider\Service\Client;

use Devscast\Lugha\Provider\Service\AbstractClient;
use Devscast\Lugha\Provider\Service\Client;

/**
* Class OllamaClient.
* Class AnthropicClient.
*
* @see https://docs.anthropic.com/en/api/getting-started
*
* @author bernard-ng <bernard@devscast.tech>
*/
final class AnthropicClient extends AbstractClient
final class AnthropicClient extends Client
{
protected const string BASE_URI = 'https://api.anthropic.com/v1/';
}
6 changes: 3 additions & 3 deletions src/Provider/Service/Client/GithubClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@

namespace Devscast\Lugha\Provider\Service\Client;

use Devscast\Lugha\Provider\Service\AbstractClient;
use Devscast\Lugha\Provider\Service\Client;

/**
* Class OllamaClient.
* Class GithubClient.
*
* @see https://github.com/marketplace/models/
*
* @author bernard-ng <bernard@devscast.tech>
*/
final class GithubClient extends AbstractClient
final class GithubClient extends Client
{
protected const string BASE_URI = 'https://models.inference.ai.azure.com/';
}
20 changes: 11 additions & 9 deletions src/Provider/Service/Client/GoogleClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,26 @@
namespace Devscast\Lugha\Provider\Service\Client;

use Devscast\Lugha\Model\Completion\Chat\History;
use Devscast\Lugha\Model\Completion\Chat\Role;
use Devscast\Lugha\Model\Completion\CompletionConfig;
use Devscast\Lugha\Model\Embedding\EmbeddingConfig;
use Devscast\Lugha\Provider\Provider;
use Devscast\Lugha\Provider\Response\CompletionResponse;
use Devscast\Lugha\Provider\Response\EmbeddingResponse;
use Devscast\Lugha\Provider\Service\AbstractClient;
use Devscast\Lugha\Provider\Service\Client;
use Devscast\Lugha\Provider\Service\HasCompletionSupport;
use Devscast\Lugha\Provider\Service\HasEmbeddingSupport;
use Devscast\Lugha\Provider\Service\IntegrationException;
use Webmozart\Assert\Assert;

/**
* Class OllamaClient.
* Class GoogleClient.
*
* @see https://ai.google.dev/api
* @see https://ai.google.dev/gemini-api/docs/embeddings#curl
*
* @author bernard-ng <bernard@devscast.tech>
*/
final class GoogleClient extends AbstractClient implements HasEmbeddingSupport, HasCompletionSupport
final class GoogleClient extends Client implements HasEmbeddingSupport, HasCompletionSupport
{
protected const string BASE_URI = 'https://generativelanguage.googleapis.com/v1beta/';

Expand All @@ -45,20 +45,20 @@ public function embeddings(string $prompt, EmbeddingConfig $config): EmbeddingRe
try {
/** @var array{embedding: array{values: array<float>}} $response */
$response = $this->http->request('POST', "models/{$config->model}:embedContent?key={$this->config->apiKey}", [
'auth_bearer' => null, // Google uses API key in query instead of Bearer token (Come on Google!)
'json' => [
'model' => "models/{$config->model}",
'content' => [
'parts' => [
[
'text' => $prompt,
],
],
'parts' => [[
'text' => $prompt,
]],
],
...$config->additionalParameters,
],
])->toArray();

return new EmbeddingResponse(
provider: Provider::GOOGLE,
model: $config->model,
embedding: $response['embedding']['values'],
providerResponse: $this->config->providerResponse ? $response : [],
Expand Down Expand Up @@ -91,13 +91,15 @@ public function completion(string|History $input, CompletionConfig $config): Com
* } $response
*/
$response = $this->http->request('POST', "models/{$config->model}:generateContent?key={$this->config->apiKey}", [
'auth_bearer' => null,
'json' => [
...$this->buildCompletionContents($input),
...$config->additionalParameters,
],
])->toArray();

return new CompletionResponse(
provider: Provider::GOOGLE,
model: $config->model,
completion: $response['candidates'][0]['content']['parts'][0]['text'],
providerResponse: $this->config->providerResponse ? $response : [],
Expand Down
15 changes: 9 additions & 6 deletions src/Provider/Service/Client/MistralClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@
use Devscast\Lugha\Model\Completion\Chat\History;
use Devscast\Lugha\Model\Completion\CompletionConfig;
use Devscast\Lugha\Model\Embedding\EmbeddingConfig;
use Devscast\Lugha\Provider\Provider;
use Devscast\Lugha\Provider\Response\CompletionResponse;
use Devscast\Lugha\Provider\Response\EmbeddingResponse;
use Devscast\Lugha\Provider\Service\AbstractClient;
use Devscast\Lugha\Provider\Service\Client;
use Devscast\Lugha\Provider\Service\HasCompletionSupport;
use Devscast\Lugha\Provider\Service\HasEmbeddingSupport;
use Devscast\Lugha\Provider\Service\IntegrationException;
use Webmozart\Assert\Assert;

/**
* Class OllamaClient.
* Class MistralClient.
*
* @see https://docs.mistral.ai/getting-started/quickstart/
* @see https://docs.mistral.ai/api/#tag/embeddings/operation/embeddings_v1_embeddings_post
*
* @author bernard-ng <bernard@devscast.tech>
*/
final class MistralClient extends AbstractClient implements HasEmbeddingSupport, HasCompletionSupport
final class MistralClient extends Client implements HasEmbeddingSupport, HasCompletionSupport
{
protected const string BASE_URI = 'https://api.mistral.ai/v1/';

Expand Down Expand Up @@ -61,8 +62,9 @@ public function embeddings(string $prompt, EmbeddingConfig $config): EmbeddingRe
])->toArray();

return new EmbeddingResponse(
provider: Provider::MISTRAL,
model: $config->model,
embedding: $response['data']['embedding'],
embedding: $response['data'][0]['embedding'],
providerResponse: $this->config->providerResponse ? $response : [],
);
} catch (\Throwable $e) {
Expand Down Expand Up @@ -107,15 +109,16 @@ public function completion(History|string $input, CompletionConfig $config): Com
},
'max_tokens' => $config->maxTokens,
'temperature' => $config->temperature,
'top_p' => $config->topP,
'stop' => $config->stopSequences,
'top_p' => $config->topP ?? 1,
'stop' => $config->stopSequences ?? [],
'presence_penalty' => $config->presencePenalty ?? 0,
'frequency_penalty' => $config->frequencyPenalty ?? 0,
...$config->additionalParameters,
],
])->toArray();

return new CompletionResponse(
provider: Provider::MISTRAL,
model: $config->model,
completion: $response['choices'][0]['message']['content'],
providerResponse: $this->config->providerResponse ? $response : [],
Expand Down
17 changes: 12 additions & 5 deletions src/Provider/Service/Client/OllamaClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
use Devscast\Lugha\Model\Completion\Chat\History;
use Devscast\Lugha\Model\Completion\CompletionConfig;
use Devscast\Lugha\Model\Embedding\EmbeddingConfig;
use Devscast\Lugha\Provider\Provider;
use Devscast\Lugha\Provider\Response\CompletionResponse;
use Devscast\Lugha\Provider\Response\EmbeddingResponse;
use Devscast\Lugha\Provider\Service\AbstractClient;
use Devscast\Lugha\Provider\Service\Client;
use Devscast\Lugha\Provider\Service\HasCompletionSupport;
use Devscast\Lugha\Provider\Service\HasEmbeddingSupport;
use Devscast\Lugha\Provider\Service\IntegrationException;
Expand All @@ -31,7 +32,7 @@
*
* @author bernard-ng <bernard@devscast.tech>
*/
final class OllamaClient extends AbstractClient implements HasEmbeddingSupport, HasCompletionSupport
final class OllamaClient extends Client implements HasEmbeddingSupport, HasCompletionSupport
{
protected const string BASE_URI = 'http://localhost:11434/api/';

Expand All @@ -48,9 +49,13 @@ public function embeddings(string $prompt, EmbeddingConfig $config): EmbeddingRe
'prompt' => $prompt,
...$config->additionalParameters,
],
])->getContent();
])->toArray();

return new EmbeddingResponse($config->model, $response['embedding']);
return new EmbeddingResponse(
provider: Provider::OLLAMA,
model: $config->model,
embedding: $response['embedding']
);
} catch (\Throwable $e) {
throw new IntegrationException('Unable to generate embeddings.', previous: $e);
}
Expand All @@ -76,7 +81,8 @@ public function completion(History|string $input, CompletionConfig $config): Com
* eval_duration: int
* } $response
*/
$response = $this->http->request('POST', 'chat/completions', [
$response = $this->http->request('POST', 'chat', [
'timeout' => -1,
'json' => [
'stream' => false, // TODO: add support for streaming
'model' => $config->model,
Expand All @@ -99,6 +105,7 @@ public function completion(History|string $input, CompletionConfig $config): Com
])->toArray();

return new CompletionResponse(
provider: Provider::OLLAMA,
model: $config->model,
completion: $response['message']['content'],
providerResponse: $this->config->providerResponse ? $response : [],
Expand Down
Loading

0 comments on commit ddc8f2b

Please sign in to comment.