Skip to content

Commit 89933a6

Browse files
committed
wip
1 parent 1fa4145 commit 89933a6

File tree

114 files changed

+1157
-70695
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+1157
-70695
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/public/storage
66
/storage/*.key
77
/storage/pail
8+
/storage/docs
89
/vendor
910
.env
1011
.env.backup

app/Console/Commands/IngestDocumentation.php

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Illuminate\Console\Command;
88
use Illuminate\Contracts\Container\BindingResolutionException;
99
use Illuminate\Support\Facades\File;
10+
use Illuminate\Support\Facades\Process;
1011
use Illuminate\Support\Str;
1112
use SplFileInfo;
1213
use Symfony\Component\Console\Exception\InvalidArgumentException;
@@ -19,7 +20,7 @@ class IngestDocumentation extends Command
1920
* The name and signature of the console command
2021
* @var string
2122
*/
22-
protected $signature = 'app:ingest:documentation {version=11}';
23+
protected $signature = 'app:ingest:documentation';
2324

2425
/**
2526
* The console command description.
@@ -34,9 +35,14 @@ class IngestDocumentation extends Command
3435

3536
public function handle()
3637
{
37-
$this->version = $this->argument('version');
38+
$this->version = '12';
3839
$path = storage_path('/docs');
3940

41+
File::deleteDirectory($path);
42+
43+
Process::run('git clone https://github.com/laravel/docs.git ' . $path);
44+
File::deleteDirectory($path . '/.git');
45+
4046
$files = File::files($path);
4147

4248
// clear the vector before indexing
@@ -91,6 +97,6 @@ private function contentIsJustHeadings(Document $document): bool
9197
{
9298

9399
return collect(explode("\n", $document->getContent()))
94-
->every(fn($line) => Str::startsWith($line, '#'));
100+
->every(fn($line) => Str::startsWith($line, '#') || Str::startsWith($line, ''));
95101
}
96102
}

app/Console/Commands/SearchVector.php

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Console\Commands;
44

5+
use App\Actions\Rerank;
56
use Illuminate\Console\Command;
67
use OpenAI\Laravel\Facades\OpenAI;
78
use Upstash\Vector\DataQuery;

app/Livewire/ChatPage.php

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
namespace App\Livewire;
4+
5+
use App\Actions\Rerank;
6+
use Livewire\Component;
7+
use OpenAI\Laravel\Facades\OpenAI;
8+
use Upstash\Vector\DataQuery;
9+
use Upstash\Vector\Enums\QueryMode;
10+
use Upstash\Vector\Laravel\Facades\Vector;
11+
use Upstash\Vector\VectorMatch;
12+
13+
class ChatPage extends Component
14+
{
15+
public string $question = '';
16+
17+
public bool $isChatLoading = false;
18+
19+
public array $chat = [
20+
[
21+
'role' => 'assistant',
22+
'content' => 'Hello! How can I help you today?',
23+
],
24+
];
25+
26+
public array $context = [];
27+
28+
public function askQuestion()
29+
{
30+
// reset and append new message to chat
31+
$this->chat = [
32+
[
33+
'role' => 'assistant',
34+
'content' => 'Hello! How can I help you today?',
35+
],
36+
[
37+
'role' => 'user',
38+
'content' => $this->question,
39+
]
40+
];
41+
42+
// loopback from JS
43+
$this->js(sprintf('$wire.processQuestion("%s")', $this->question));
44+
45+
// reset state
46+
$this->question = '';
47+
$this->context = [];
48+
$this->isChatLoading = true;
49+
}
50+
51+
public function processQuestion(string $question)
52+
{
53+
$results = Vector::queryData(new DataQuery(
54+
data: $question,
55+
topK: 8,
56+
includeMetadata: true,
57+
includeData: true,
58+
queryMode: QueryMode::DENSE,
59+
));
60+
61+
$this->context = collect($results)
62+
->map(fn(VectorMatch $result) => [
63+
'text' => $result->data,
64+
'sources' => $result->metadata['sources'],
65+
])
66+
->toArray();
67+
68+
$this->js(sprintf('$wire.generateAnswer("%s")', $question));
69+
}
70+
71+
public function generateAnswer(string $question)
72+
{
73+
$this->isChatLoading = false;
74+
75+
$context = collect($this->context)
76+
->map(fn(array $item) => $item['text'])
77+
->implode("\n---\n");
78+
79+
$messages = [
80+
[
81+
'role' => 'system',
82+
'content' => view('prompts.system', ['version' => '12.x'])->render(),
83+
],
84+
[
85+
'role' => 'assistant',
86+
'content' => 'Hello! How can I help you today?',
87+
],
88+
[
89+
'role' => 'user',
90+
'content' => view('prompts.question', ['question' => $question, 'context' => $context])->render(),
91+
],
92+
];
93+
94+
$stream = OpenAI::chat()->createStreamed([
95+
'model' => 'gpt-4o-mini',
96+
'messages' => $messages,
97+
]);
98+
99+
$text = '';
100+
foreach($stream as $response) {
101+
$text .= $response->choices[0]->delta->content;
102+
$this->stream(to: 'answer', content: $text, replace: true);
103+
}
104+
105+
$this->chat[] = [
106+
'role' => 'assistant',
107+
'content' => $text,
108+
'sources' => collect($this->context)
109+
->map(fn(array $item) => $item['sources'])
110+
->flatten()
111+
->unique()
112+
->take(3)
113+
->toArray(),
114+
];
115+
}
116+
117+
public function render()
118+
{
119+
return view('livewire.chat-page');
120+
}
121+
}

composer.json

+2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
"php": "^8.2",
1313
"laravel/framework": "^11.31",
1414
"laravel/tinker": "^2.9",
15+
"livewire/livewire": "^3.5",
1516
"openai-php/laravel": "^0.10.2",
17+
"spatie/laravel-markdown": "^2.7",
1618
"upstash/vector-laravel": "^0.2.2"
1719
},
1820
"require-dev": {

0 commit comments

Comments
 (0)