Skip to content

Commit

Permalink
Merge pull request #1 from LlmLaraHub/chatUI
Browse files Browse the repository at this point in the history
Chat UI
  • Loading branch information
alnutile authored Mar 28, 2024
2 parents d59d326 + 088b71e commit bb0b331
Show file tree
Hide file tree
Showing 58 changed files with 5,244 additions and 374 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,8 @@ VITE_APP_NAME="${APP_NAME}"

OPENAI_API_KEY=
OPENAI_ORGANIZATION=


REVERB_APP_ID=SOMETHING
REVERB_APP_KEY=SOMETHING
REVERB_APP_SECRET=SOMETHING
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ CREATE EXTENSION vector;
And just thinking how to make some of those flows work in a Laravel environment

[![LaraChain Full Demo](https://img.youtube.com/vi/cz7d6d3pk4o/0.jpg)](https://www.youtube.com/watch?v=cz7d6d3pk4o)


## Make sure to setup Reverb

Per the Laravel docs https://laravel.com/docs/11.x/reverb
4 changes: 4 additions & 0 deletions app/Domains/Documents/Transformers/PdfTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace App\Domains\Documents\Transformers;

use App\Domains\Collections\CollectionStatusEnum;
use App\Events\CollectionStatusEvent;
use App\Jobs\SummarizeDataJob;
use App\Jobs\SummarizeDocumentJob;
use App\Jobs\VectorlizeDataJob;
Expand Down Expand Up @@ -48,6 +50,8 @@ public function handle(Document $document): Document
new SummarizeDataJob($DocumentChunk),
//Tagging
];

CollectionStatusEvent::dispatch($document->collection, CollectionStatusEnum::PROCESSING);
}

$batch = Bus::batch($chunks)
Expand Down
10 changes: 10 additions & 0 deletions app/Domains/Messages/RoleEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Domains\Messages;

enum RoleEnum: string
{
case User = 'user';
case System = 'system';
case Assistant = 'assistant';
}
70 changes: 70 additions & 0 deletions app/Domains/Messages/SearchOrSummarizeChatRepo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace App\Domains\Messages;

use App\LlmDriver\LlmDriverFacade;
use App\LlmDriver\Responses\CompletionResponse;
use App\LlmDriver\Responses\EmbeddingsResponseDto;
use App\Models\Chat;
use App\Models\DocumentChunk;
use Illuminate\Support\Facades\Log;

class SearchOrSummarizeChatRepo
{
public function search(Chat $chat, string $input): string
{
/**
* @TODO
* Later using the LLM we will decide if the input is best served
* by searching the data or a summary of the data.
* For now we will search.
*/
Log::info('ChatController:chat getting embedding', ['input' => $input]);

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

$results = DocumentChunk::query()
->join('documents', 'documents.id', '=', 'document_chunks.document_id')
->selectRaw(
'document_chunks.embedding <-> ? as distance, document_chunks.content, document_chunks.embedding as embedding, document_chunks.id as id',
[$embedding->embedding]
)
->where('documents.collection_id', $chat->chatable->id)
->limit(5)
->orderByRaw('distance')
->get();

$content = [];

foreach ($results as $result) {
$content[] = reduce_text_size($result->content);
}

$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;

$chat->addInput(
message: $content,
role: RoleEnum::Assistant,
systemPrompt: $chat->chatable->systemPrompt(),
show_in_thread: false
);

$chat->addInput($input, RoleEnum::User, $chat->chatable->systemPrompt());

$latestMessagesArray = $chat->getChatResponse();

/** @var CompletionResponse $response */
$response = LlmDriverFacade::driver(
$chat->chatable->getDriver()
)->chat($latestMessagesArray);

$chat->addInput($response->content, RoleEnum::Assistant);

return $response->content;
}
}
44 changes: 44 additions & 0 deletions app/Events/ChatUpdatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace App\Events;

use App\Models\Chat;
use App\Models\Collection;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ChatUpdatedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;

/**
* Create a new event instance.
*/
public function __construct(public Collection $collection, public Chat $chat)
{
//
}

/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('collection.chat.'.$this->collection->id.'.'.$this->chat->id),
];
}

/**
* The event's broadcast name.
*/
public function broadcastAs(): string
{
return 'status';
}
}
18 changes: 17 additions & 1 deletion app/Http/Controllers/ChatController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

namespace App\Http\Controllers;

use App\Events\ChatUpdatedEvent;
use App\Http\Resources\ChatResource;
use App\Http\Resources\CollectionResource;
use App\Http\Resources\MessageResource;
use App\Models\Chat;
use App\Models\Collection;
use Facades\App\Domains\Messages\SearchOrSummarizeChatRepo;

class ChatController extends Controller
{
Expand All @@ -27,11 +30,24 @@ public function storeCollectionChat(Collection $collection)

public function showCollectionChat(Collection $collection, Chat $chat)
{

return inertia('Collection/Chat', [
'collection' => new CollectionResource($collection),
'chat' => new ChatResource($chat),
'system_prompt' => $collection->systemPrompt(),
'messages' => MessageResource::collection($chat->latest_messages),
]);
}

public function chat(Chat $chat)
{
$validated = request()->validate([
'input' => 'required|string',
]);

$response = SearchOrSummarizeChatRepo::search($chat, $validated['input']);

ChatUpdatedEvent::dispatch($chat->chatable, $chat);

return response()->json(['message' => $response]);
}
}
1 change: 1 addition & 0 deletions app/Http/Controllers/CollectionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function store()
$validated = request()->validate([
'name' => 'required',
'description' => 'required',
'driver' => 'required',
]);

$validated['team_id'] = auth()->user()->current_team_id;
Expand Down
5 changes: 4 additions & 1 deletion app/Http/Resources/ChatResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class ChatResource extends JsonResource
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
return [
'id' => $this->id,
'user_id' => new UserResource($this->user),
];
}
}
26 changes: 26 additions & 0 deletions app/Http/Resources/MessageResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class MessageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'from_ai' => $this->from_ai,
'initials' => ($this->from_ai) ? 'Ai' : 'You',
'type' => 'text', //@TODO
'body' => $this->body,
'diff_for_humans' => $this->created_at->diffForHumans(),
];
}
}
19 changes: 19 additions & 0 deletions app/Http/Resources/UserResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}
35 changes: 30 additions & 5 deletions app/LlmDriver/BaseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\LlmDriver;

use App\LlmDriver\Requests\MessageInDto;
use App\LlmDriver\Responses\CompletionResponse;
use App\LlmDriver\Responses\EmbeddingsResponseDto;
use Illuminate\Support\Facades\Log;
Expand All @@ -12,19 +13,43 @@ abstract class BaseClient

public function embedData(string $data): EmbeddingsResponseDto
{

if (! app()->environment('testing')) {
sleep(2);
}
Log::info('LlmDriver::MockClient::embedData');

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

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

/**
* @param MessageInDto[] $messages
*/
public function chat(array $messages): CompletionResponse
{
if (! app()->environment('testing')) {
sleep(2);
}

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

$data = <<<'EOD'
Voluptate irure cillum dolor anim officia reprehenderit dolor. Eiusmod veniam nostrud consectetur incididunt proident id. Anim adipisicing pariatur amet duis Lorem sunt veniam veniam est. Deserunt ea aliquip cillum pariatur consectetur. Dolor in reprehenderit adipisicing consectetur cupidatat ad cupidatat reprehenderit. Nostrud mollit voluptate aliqua anim pariatur excepteur eiusmod velit quis exercitation tempor quis excepteur.
EOD;

return new CompletionResponse($data);
}

public function completion(string $prompt): CompletionResponse
{
if (! app()->environment('testing')) {
sleep(2);
}

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

$data = <<<'EOD'
Expand Down
30 changes: 26 additions & 4 deletions app/LlmDriver/OpenAiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\LlmDriver;

use App\LlmDriver\Requests\MessageInDto;
use App\LlmDriver\Responses\CompletionResponse;
use App\LlmDriver\Responses\EmbeddingsResponseDto;
use OpenAI\Laravel\Facades\OpenAI;
Expand All @@ -10,6 +11,27 @@ class OpenAiClient extends BaseClient
{
protected string $driver = 'openai';

/**
* @param MessageInDto[] $messages
*/
public function chat(array $messages): CompletionResponse
{
$response = OpenAI::chat()->create([
'model' => $this->getConfig('openai')['completion_model'],
'messages' => collect($messages)->map(function ($message) {
return $message->toArray();
})->toArray(),
]);

$results = null;

foreach ($response->choices as $result) {
$results = $result->message->content;
}

return new CompletionResponse($results);
}

public function embedData(string $data): EmbeddingsResponseDto
{

Expand All @@ -24,10 +46,10 @@ public function embedData(string $data): EmbeddingsResponseDto
$results = $embedding->embedding; // [0.018990106880664825, -0.0073809814639389515, ...]
}

return new EmbeddingsResponseDto(
$results,
$response->usage->totalTokens,
);
return EmbeddingsResponseDto::from([
'embedding' => $results,
'token_count' => $response->usage->totalTokens,
]);
}

public function completion(string $prompt, int $temperature = 0): CompletionResponse
Expand Down
14 changes: 14 additions & 0 deletions app/LlmDriver/Requests/MessageInDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\LlmDriver\Requests;

use Spatie\LaravelData\Data;

class MessageInDto extends Data
{
public function __construct(
public string $content,
public string $role,
) {
}
}
Loading

0 comments on commit bb0b331

Please sign in to comment.