🎉
This commit is contained in:
48
vendor/spatie/ignition/src/Solutions/OpenAi/DummyCache.php
vendored
Normal file
48
vendor/spatie/ignition/src/Solutions/OpenAi/DummyCache.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\Ignition\Solutions\OpenAi;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class DummyCache implements CacheInterface
|
||||
{
|
||||
public function get(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMultiple(iterable $keys, mixed $default = null): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteMultiple(iterable $keys): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
46
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiPromptViewModel.php
vendored
Normal file
46
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiPromptViewModel.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\Ignition\Solutions\OpenAi;
|
||||
|
||||
class OpenAiPromptViewModel
|
||||
{
|
||||
public function __construct(
|
||||
protected string $file,
|
||||
protected string $exceptionMessage,
|
||||
protected string $exceptionClass,
|
||||
protected string $snippet,
|
||||
protected string $line,
|
||||
protected string|null $applicationType = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function file(): string
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function line(): string
|
||||
{
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
public function snippet(): string
|
||||
{
|
||||
return $this->snippet;
|
||||
}
|
||||
|
||||
public function exceptionMessage(): string
|
||||
{
|
||||
return $this->exceptionMessage;
|
||||
}
|
||||
|
||||
public function exceptionClass(): string
|
||||
{
|
||||
return $this->exceptionClass;
|
||||
}
|
||||
|
||||
public function applicationType(): string|null
|
||||
{
|
||||
return $this->applicationType;
|
||||
}
|
||||
}
|
||||
115
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolution.php
vendored
Normal file
115
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolution.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\Ignition\Solutions\OpenAi;
|
||||
|
||||
use OpenAI;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Spatie\Backtrace\Backtrace;
|
||||
use Spatie\Backtrace\Frame;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Spatie\Ignition\ErrorPage\Renderer;
|
||||
use Spatie\Ignition\Ignition;
|
||||
use Throwable;
|
||||
|
||||
class OpenAiSolution implements Solution
|
||||
{
|
||||
public bool $aiGenerated = true;
|
||||
|
||||
protected string $prompt;
|
||||
|
||||
protected OpenAiSolutionResponse $openAiSolutionResponse;
|
||||
|
||||
public function __construct(
|
||||
protected Throwable $throwable,
|
||||
protected string $openAiKey,
|
||||
protected CacheInterface|null $cache = null,
|
||||
protected int|null $cacheTtlInSeconds = 60,
|
||||
protected string|null $applicationType = null,
|
||||
protected string|null $applicationPath = null,
|
||||
) {
|
||||
$this->prompt = $this->generatePrompt();
|
||||
|
||||
$this->openAiSolutionResponse = $this->getAiSolution();
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'AI Generated Solution';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return $this->openAiSolutionResponse->description();
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return $this->openAiSolutionResponse->links();
|
||||
}
|
||||
|
||||
public function getAiSolution(): ?OpenAiSolutionResponse
|
||||
{
|
||||
$solution = $this->cache->get($this->getCacheKey());
|
||||
|
||||
if ($solution) {
|
||||
return new OpenAiSolutionResponse($solution);
|
||||
}
|
||||
|
||||
$solutionText = OpenAI::client($this->openAiKey)
|
||||
->chat()
|
||||
->create([
|
||||
'model' => $this->getModel(),
|
||||
'messages' => [['role' => 'user', 'content' => $this->prompt]],
|
||||
'max_tokens' => 1000,
|
||||
'temperature' => 0,
|
||||
])->choices[0]->message->content;
|
||||
|
||||
$this->cache->set($this->getCacheKey(), $solutionText, $this->cacheTtlInSeconds);
|
||||
|
||||
return new OpenAiSolutionResponse($solutionText);
|
||||
}
|
||||
|
||||
protected function getCacheKey(): string
|
||||
{
|
||||
$hash = sha1($this->prompt);
|
||||
|
||||
return "ignition-solution-{$hash}";
|
||||
}
|
||||
|
||||
protected function generatePrompt(): string
|
||||
{
|
||||
$viewPath = Ignition::viewPath('aiPrompt');
|
||||
|
||||
$viewModel = new OpenAiPromptViewModel(
|
||||
file: $this->throwable->getFile(),
|
||||
exceptionMessage: $this->throwable->getMessage(),
|
||||
exceptionClass: get_class($this->throwable),
|
||||
snippet: $this->getApplicationFrame($this->throwable)->getSnippetAsString(15),
|
||||
line: $this->throwable->getLine(),
|
||||
applicationType: $this->applicationType,
|
||||
);
|
||||
|
||||
return (new Renderer())->renderAsString(
|
||||
['viewModel' => $viewModel],
|
||||
$viewPath,
|
||||
);
|
||||
}
|
||||
|
||||
protected function getModel(): string
|
||||
{
|
||||
return 'gpt-3.5-turbo';
|
||||
}
|
||||
|
||||
protected function getApplicationFrame(Throwable $throwable): ?Frame
|
||||
{
|
||||
$backtrace = Backtrace::createForThrowable($throwable);
|
||||
|
||||
if ($this->applicationPath) {
|
||||
$backtrace->applicationPath($this->applicationPath);
|
||||
}
|
||||
|
||||
$frames = $backtrace->frames();
|
||||
|
||||
return $frames[$backtrace->firstApplicationFrameIndex()] ?? null;
|
||||
}
|
||||
}
|
||||
62
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolutionProvider.php
vendored
Normal file
62
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolutionProvider.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\Ignition\Solutions\OpenAi;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Throwable;
|
||||
|
||||
class OpenAiSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function __construct(
|
||||
protected string $openAiKey,
|
||||
protected ?CacheInterface $cache = null,
|
||||
protected int $cacheTtlInSeconds = 60 * 60,
|
||||
protected string|null $applicationType = null,
|
||||
protected string|null $applicationPath = null,
|
||||
) {
|
||||
$this->cache ??= new DummyCache();
|
||||
}
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
new OpenAiSolution(
|
||||
$throwable,
|
||||
$this->openAiKey,
|
||||
$this->cache,
|
||||
$this->cacheTtlInSeconds,
|
||||
$this->applicationType,
|
||||
$this->applicationPath,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function applicationType(string $applicationType): self
|
||||
{
|
||||
$this->applicationType = $applicationType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function applicationPath(string $applicationPath): self
|
||||
{
|
||||
$this->applicationPath = $applicationPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useCache(CacheInterface $cache, int $cacheTtlInSeconds = 60 * 60): self
|
||||
{
|
||||
$this->cache = $cache;
|
||||
|
||||
$this->cacheTtlInSeconds = $cacheTtlInSeconds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
58
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolutionResponse.php
vendored
Normal file
58
vendor/spatie/ignition/src/Solutions/OpenAi/OpenAiSolutionResponse.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\Ignition\Solutions\OpenAi;
|
||||
|
||||
class OpenAiSolutionResponse
|
||||
{
|
||||
protected string $rawText;
|
||||
|
||||
public function __construct(string $rawText)
|
||||
{
|
||||
$this->rawText = trim($rawText);
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return $this->between('FIX', 'ENDFIX', $this->rawText);
|
||||
}
|
||||
|
||||
public function links(): array
|
||||
{
|
||||
$textLinks = $this->between('LINKS', 'ENDLINKS', $this->rawText);
|
||||
|
||||
$textLinks = explode(PHP_EOL, $textLinks);
|
||||
|
||||
$textLinks = array_map(function ($textLink) {
|
||||
$textLink = str_replace('\\', '\\\\', $textLink);
|
||||
$textLink = str_replace('\\\\\\', '\\\\', $textLink);
|
||||
|
||||
return json_decode($textLink, true);
|
||||
}, $textLinks);
|
||||
|
||||
array_filter($textLinks);
|
||||
|
||||
$links = [];
|
||||
foreach ($textLinks as $textLink) {
|
||||
$links[$textLink['title']] = $textLink['url'];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
protected function between(string $start, string $end, string $text): string
|
||||
{
|
||||
$startPosition = strpos($text, $start);
|
||||
if ($startPosition === false) {
|
||||
return "";
|
||||
}
|
||||
|
||||
$startPosition += strlen($start);
|
||||
$endPosition = strpos($text, $end, $startPosition);
|
||||
|
||||
if ($endPosition === false) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return trim(substr($text, $startPosition, $endPosition - $startPosition));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user