Skip to content

Commit

Permalink
ready to merge
Browse files Browse the repository at this point in the history
  • Loading branch information
alnutile committed Mar 29, 2024
1 parent a8ff8c2 commit 69fdbc3
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 54 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@ And just thinking how to make some of those flows work in a Laravel environment

Per the Laravel docs https://laravel.com/docs/11.x/reverb



## Local Dev

```bash
php artisan horizon:watch
php artisan reverb:start --debug
npm run dev
```
6 changes: 3 additions & 3 deletions app/Domains/Messages/SearchOrSummarizeChatRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function search(Chat $chat, string $input): string

/** @var EmbeddingsResponseDto $embedding */
$embedding = LlmDriverFacade::driver(
$chat->chatable->getEmddingDriver()
$chat->chatable->getEmbeddingDriver()
)->embedData($input);

$results = DocumentChunk::query()
Expand All @@ -40,12 +40,12 @@ public function search(Chat $chat, string $input): string
$content = [];

foreach ($results as $result) {
$content[] = reduce_text_size($result->content);
$content[] = remove_ascii(reduce_text_size($result->content)); //reduce_text_size seem to mess up Claude?
}

$content = implode(' ', $content);

$content = 'This is data from the search results when entering the users prompt please use this for context and only this: '.$content;
$content = 'This is data from the search results when entering the users prompt please use this for context and only this and return as markdown so I can render it: '.$content;

$chat->addInput(
message: $content,
Expand Down
84 changes: 51 additions & 33 deletions app/LlmDriver/ClaudeClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

class ClaudeClient
{

protected string $baseUrl = 'https://api.anthropic.com/v1';

protected string $version = '2023-06-01';
Expand All @@ -20,48 +19,66 @@ class ClaudeClient

public function embedData(string $data): EmbeddingsResponseDto
{

Log::info('LlmDriver::ClaudeClient::embedData');

return EmbeddingsResponseDto::from([
'embedding' => data_get($data, 'data.0.embedding'),
'token_count' => 1000,
]);
throw new \Exception('Not implemented');
}

/**
* @param MessageInDto[] $messages
*/
public function chat(array $messages): CompletionResponse
{
$model = $this->getConfig('claude')['models']['completion_model'];
$maxTokens = $this->getConfig('claude')['max_tokens'];
$model = $this->getConfig('claude')['models']['completion_model'];
$maxTokens = $this->getConfig('claude')['max_tokens'];

Log::info('LlmDriver::Claude::completion');

$messages = collect($messages)->map(function($item) {
if($item->role === 'system') {
/**
* I need to iterate over each item
* then if there are two rows with role assistant I need to insert
* in betwee a user row with some copy to make it work like "And the user search results had"
* using the Laravel Collection library
*/
$messages = collect($messages)->map(function ($item) {
if ($item->role === 'system') {
$item->role = 'assistant';
}

return $item->toArray();
})->reverse()->values()->all();

})->reverse()->values();

$messages = $messages->flatMap(function ($item, $index) use ($messages) {
if ($index > 0 && $item['role'] === 'assistant' && optional($messages->get($index + 1))['role'] === 'assistant') {
return [
$item,
['role' => 'user', 'content' => 'Continuation of search results'],
];
}

return [$item];
})->toArray();

put_fixture('claude_messages_debug.json', $messages);

$results = $this->getClient()->post('/messages', [
'model' => $model,
"max_tokens" => $maxTokens,
'system' => 'Return a markdown response.',
'max_tokens' => $maxTokens,
'messages' => $messages,
]);

if(!$results->ok()) {
if (! $results->ok()) {
$error = $results->json()['error']['type'];
Log::error('Claude API Error ' . $error);
throw new \Exception('Claude API Error ' . $error);
$message = $results->json()['error']['message'];
Log::error('Claude API Error ', [
'type' => $error,
'message' => $message,
]);
throw new \Exception('Claude API Error '.$message);
}

$data = null;

foreach($results->json()['content'] as $content) {
foreach ($results->json()['content'] as $content) {
$data = $content['text'];
}

Expand All @@ -72,32 +89,31 @@ public function chat(array $messages): CompletionResponse

public function completion(string $prompt): CompletionResponse
{
$model = $this->getConfig('claude')['models']['completion_model'];
$maxTokens = $this->getConfig('claude')['max_tokens'];
$model = $this->getConfig('claude')['models']['completion_model'];
$maxTokens = $this->getConfig('claude')['max_tokens'];

Log::info('LlmDriver::Claude::completion');

$results = $this->getClient()->post('/messages', [
'model' => $model,
"max_tokens" => $maxTokens,
'max_tokens' => $maxTokens,
'messages' => [
[
'role' => "user",
'content' => $prompt
]
'role' => 'user',
'content' => $prompt,
],
],
]);


if(!$results->ok()) {
if (! $results->ok()) {
$error = $results->json()['error']['type'];
Log::error('Claude API Error ' . $error);
throw new \Exception('Claude API Error ' . $error);
Log::error('Claude API Error '.$error);
throw new \Exception('Claude API Error '.$error);
}

$data = null;

foreach($results->json()['content'] as $content) {
foreach ($results->json()['content'] as $content) {
$data = $content['text'];
}

Expand All @@ -106,13 +122,15 @@ public function completion(string $prompt): CompletionResponse
]);
}

protected function getError(Response $response) {
protected function getError(Response $response)
{
return $response->json()['error']['type'];
}

protected function getClient() {
protected function getClient()
{
$api_token = $this->getConfig('claude')['api_key'];
if(!$api_token) {
if (! $api_token) {
throw new \Exception('Claude API Token not found');
}

Expand Down
8 changes: 4 additions & 4 deletions app/LlmDriver/DriversEnum.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php
<?php

namespace App\LlmDriver;

enum DriversEnum : string {

enum DriversEnum: string
{
case Mock = 'mock';
case OpenAi = 'openai';
case OpenAiAzure = 'openai_azure';
case Ollama = 'ollama';
case Gemini = 'gemini';
case Claude = 'claude';
}
}
6 changes: 3 additions & 3 deletions app/LlmDriver/HasDrivers.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?php
<?php

namespace App\LlmDriver;


interface HasDrivers
{
public function getDriver(): string;

public function getEmbeddingDriver(): string;
}
}
7 changes: 3 additions & 4 deletions app/Models/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

namespace App\Models;

use App\LlmDriver\DriversEnum;
use App\LlmDriver\HasDrivers;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use App\LlmDriver\DriversEnum;
use App\LlmDriver\HasDrivers;

/**
* Class Project
Expand All @@ -32,10 +32,9 @@ class Collection extends Model implements HasDrivers
protected $casts = [
'active' => 'boolean',
'driver' => DriversEnum::class,
'embedding_driver' => DriversEnum::class
'embedding_driver' => DriversEnum::class,
];


public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
Expand Down
5 changes: 2 additions & 3 deletions app/Models/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class Document extends Model implements HasDrivers
'summary_status' => StatusEnum::class,
];


public function collection(): BelongsTo
{
return $this->belongsTo(Collection::class);
Expand Down Expand Up @@ -63,8 +62,8 @@ public function getDriver(): string
return $this->collection->driver->value;
}


public function getEmbeddingDriver(): string {
public function getEmbeddingDriver(): string
{
return $this->collection->embedding_driver->value;
}
}
2 changes: 1 addition & 1 deletion app/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function put_fixture($file_name, $content = [], $json = true)
if (! function_exists('remove_ascii')) {
function remove_ascii($string): string
{
return preg_replace('/[^\x00-\x7F]+/', '', $string);
return str_replace("\u2019", ' ', preg_replace('/[^\x00-\x7F]+/', '', $string));
}
}

Expand Down
2 changes: 1 addition & 1 deletion config/llmdriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
'models' => [
//@see https://www.anthropic.com/news/claude-3-family
'completion_model' => env('CLAUDE_COMPLETION_MODEL', 'claude-3-opus-20240229'),
]
],
],
'ollama' => [

Expand Down
46 changes: 45 additions & 1 deletion tests/Feature/ClaudeClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Tests\Feature;

use App\LlmDriver\ClaudeClient;
use App\LlmDriver\MockClient;
use App\LlmDriver\Requests\MessageInDto;
use App\LlmDriver\Responses\CompletionResponse;
use App\LlmDriver\Responses\EmbeddingsResponseDto;
Expand All @@ -18,6 +17,8 @@ class ClaudeClientTest extends TestCase
public function test_embeddings(): void
{

$this->markTestSkipped('@TODO: Requires another server');

$client = new ClaudeClient();

$results = $client->embedData('test');
Expand Down Expand Up @@ -68,9 +69,52 @@ public function test_chat(): void
Http::assertSent(function ($request) {
$message1 = $request->data()['messages'][0]['role'];
$message2 = $request->data()['messages'][1]['role'];

return $message2 === 'assistant' &&
$message1 === 'user';
});

}

public function test_chat_with_multiple_assistant_messages(): void
{
$client = new ClaudeClient();

$data = get_fixture('claude_completion.json');

Http::fake([
'api.anthropic.com/*' => Http::response($data, 200),
]);

$results = $client->chat([
MessageInDto::from([
'content' => 'test 3',
'role' => 'assistant',
]),
MessageInDto::from([
'content' => 'test 2',
'role' => 'assistant',
]),
MessageInDto::from([
'content' => 'test 1',
'role' => 'assistant',
]),
MessageInDto::from([
'content' => 'test',
'role' => 'user',
]),
]);

$this->assertInstanceOf(CompletionResponse::class, $results);

Http::assertSent(function ($request) {
$message1 = $request->data()['messages'][0]['role'];
$message2 = $request->data()['messages'][1]['role'];
$message3 = $request->data()['messages'][1]['role'];

return $message2 === 'assistant' &&
$message1 === 'user' && $message3 === 'assistant';
});

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Inertia\Testing\AssertableInertia as Assert;
use Laravel\Pennant\Contracts\Driver;
use Tests\TestCase;

class CollectionControllerTest extends TestCase
Expand Down
26 changes: 26 additions & 0 deletions tests/fixtures/claude_messages_debug.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"content": "test",
"role": "user"
},
{
"content": "test 1",
"role": "assistant"
},
{
"role": "user",
"content": "Continuation of search results"
},
{
"content": "test 2",
"role": "assistant"
},
{
"role": "user",
"content": "Continuation of search results"
},
{
"content": "test 3",
"role": "assistant"
}
]

0 comments on commit 69fdbc3

Please sign in to comment.