| 1294 |
lars |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
namespace Spatie\Ignition\Solutions\OpenAi;
|
|
|
4 |
|
|
|
5 |
use OpenAI;
|
|
|
6 |
use Psr\SimpleCache\CacheInterface;
|
|
|
7 |
use Spatie\Backtrace\Backtrace;
|
|
|
8 |
use Spatie\Backtrace\Frame;
|
|
|
9 |
use Spatie\Ignition\Contracts\Solution;
|
|
|
10 |
use Spatie\Ignition\ErrorPage\Renderer;
|
|
|
11 |
use Spatie\Ignition\Ignition;
|
|
|
12 |
use Throwable;
|
|
|
13 |
|
|
|
14 |
class OpenAiSolution implements Solution
|
|
|
15 |
{
|
|
|
16 |
public bool $aiGenerated = true;
|
|
|
17 |
|
|
|
18 |
protected string $prompt;
|
|
|
19 |
|
|
|
20 |
protected OpenAiSolutionResponse $openAiSolutionResponse;
|
|
|
21 |
|
|
|
22 |
public function __construct(
|
|
|
23 |
protected Throwable $throwable,
|
|
|
24 |
protected string $openAiKey,
|
|
|
25 |
protected CacheInterface|null $cache = null,
|
|
|
26 |
protected int|null $cacheTtlInSeconds = 60,
|
|
|
27 |
protected string|null $applicationType = null,
|
|
|
28 |
protected string|null $applicationPath = null,
|
|
|
29 |
) {
|
|
|
30 |
$this->prompt = $this->generatePrompt();
|
|
|
31 |
|
|
|
32 |
$this->openAiSolutionResponse = $this->getAiSolution();
|
|
|
33 |
}
|
|
|
34 |
|
|
|
35 |
public function getSolutionTitle(): string
|
|
|
36 |
{
|
|
|
37 |
return 'AI Generated Solution';
|
|
|
38 |
}
|
|
|
39 |
|
|
|
40 |
public function getSolutionDescription(): string
|
|
|
41 |
{
|
|
|
42 |
return $this->openAiSolutionResponse->description();
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
public function getDocumentationLinks(): array
|
|
|
46 |
{
|
|
|
47 |
return $this->openAiSolutionResponse->links();
|
|
|
48 |
}
|
|
|
49 |
|
|
|
50 |
public function getAiSolution(): ?OpenAiSolutionResponse
|
|
|
51 |
{
|
|
|
52 |
$solution = $this->cache->get($this->getCacheKey());
|
|
|
53 |
|
|
|
54 |
if ($solution) {
|
|
|
55 |
return new OpenAiSolutionResponse($solution);
|
|
|
56 |
}
|
|
|
57 |
|
|
|
58 |
$solutionText = OpenAI::client($this->openAiKey)
|
|
|
59 |
->chat()
|
|
|
60 |
->create([
|
|
|
61 |
'model' => $this->getModel(),
|
|
|
62 |
'messages' => [['role' => 'user', 'content' => $this->prompt]],
|
|
|
63 |
'max_tokens' => 1000,
|
|
|
64 |
'temperature' => 0,
|
|
|
65 |
])->choices[0]->message->content;
|
|
|
66 |
|
|
|
67 |
$this->cache->set($this->getCacheKey(), $solutionText, $this->cacheTtlInSeconds);
|
|
|
68 |
|
|
|
69 |
return new OpenAiSolutionResponse($solutionText);
|
|
|
70 |
}
|
|
|
71 |
|
|
|
72 |
protected function getCacheKey(): string
|
|
|
73 |
{
|
|
|
74 |
$hash = sha1($this->prompt);
|
|
|
75 |
|
|
|
76 |
return "ignition-solution-{$hash}";
|
|
|
77 |
}
|
|
|
78 |
|
|
|
79 |
protected function generatePrompt(): string
|
|
|
80 |
{
|
|
|
81 |
$viewPath = Ignition::viewPath('aiPrompt');
|
|
|
82 |
|
|
|
83 |
$viewModel = new OpenAiPromptViewModel(
|
|
|
84 |
file: $this->throwable->getFile(),
|
|
|
85 |
exceptionMessage: $this->throwable->getMessage(),
|
|
|
86 |
exceptionClass: get_class($this->throwable),
|
|
|
87 |
snippet: $this->getApplicationFrame($this->throwable)->getSnippetAsString(15),
|
|
|
88 |
line: $this->throwable->getLine(),
|
|
|
89 |
applicationType: $this->applicationType,
|
|
|
90 |
);
|
|
|
91 |
|
|
|
92 |
return (new Renderer())->renderAsString(
|
|
|
93 |
['viewModel' => $viewModel],
|
|
|
94 |
$viewPath,
|
|
|
95 |
);
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
protected function getModel(): string
|
|
|
99 |
{
|
|
|
100 |
return 'gpt-3.5-turbo';
|
|
|
101 |
}
|
|
|
102 |
|
|
|
103 |
protected function getApplicationFrame(Throwable $throwable): ?Frame
|
|
|
104 |
{
|
|
|
105 |
$backtrace = Backtrace::createForThrowable($throwable);
|
|
|
106 |
|
|
|
107 |
if ($this->applicationPath) {
|
|
|
108 |
$backtrace->applicationPath($this->applicationPath);
|
|
|
109 |
}
|
|
|
110 |
|
|
|
111 |
$frames = $backtrace->frames();
|
|
|
112 |
|
|
|
113 |
return $frames[$backtrace->firstApplicationFrameIndex()] ?? null;
|
|
|
114 |
}
|
|
|
115 |
}
|