Skip to content

Commit

Permalink
feat: adding ai based generated solutions (#534)
Browse files Browse the repository at this point in the history
* feat: adding ai based generated solutions

* Fix styling

* fix: docs

* fix: wip

* Fix styling

---------

Co-authored-by: binaryk <[email protected]>
  • Loading branch information
binaryk and binaryk authored Feb 4, 2023
1 parent b2e00b6 commit 7f14144
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"brianium/paratest": "^6.2",
"doctrine/dbal": "^3.0",
"nunomaduro/collision": "^6.0",
"openai-php/laravel": "^0.3.1",
"orchestra/testbench": "^7.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
Expand Down
7 changes: 7 additions & 0 deletions config/restify.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,11 @@
'ttl' => 5 * 60, // seconds
],
],

/*
| Specify if restify can call OpenAI for solution generation.
|
| By default this feature is enabled, but you still have to extend the Exception handler with the Restify one and set the API key.
*/
'ai_solutions' => true,
];
61 changes: 61 additions & 0 deletions docs-v2/content/en/performance/solutions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: AI Solution
menuTitle: AI Solution
description: AI Solution
category: Advanced
position: 14
---

## Generate solution

Restify can generate an AI based solution to your problem. In order to enable that you need to extend the `App\Exceptions\Handler` with the `Binaryk\LaravelRestify\Exceptions\RestifyHandler`:

```php
use Binaryk\LaravelRestify\Exceptions\RestifyHandler;
use Throwable;

class Handler extends RestifyHandler
{
//...
}
```

<alert type="warning">
This feature is only enabled when the `app.debug` is set to `true`.
</alert>


This feature is using the [openai-php/laravel](https://github.com/openai-php/laravel#get-started), you should also publish the config file:

```
php artisan vendor:publish --provider="OpenAI\Laravel\ServiceProvider"
```

and set the `OPENAI_API_KEY` in the `.env` file.

The OpenAI key can be obtained from [here](https://platform.openai.com/account/api-keys).


Now the solution to your problems will automatically appear in the response:

```json
{
"restify-solution": "Line 67 in DocumentRepository.php file has an error because the method `resolveUsingFullPath()` is not defined. The code should look like this:\n```\n->resolveUsingTemporaryUrl($request->boolean('temporary'))\n```\n",
"message": "Call to undefined method Binaryk\\LaravelRestify\\Fields\\File::resolveUsingFullPath()",
"exception": "Error",
"file": "/Users/eduardlupacescu/Sites/binarcode/erp/app/Restify/DocumentRepository.php",
"line": 67,
"trace": [
...
}
```

## Disable solution


If you want to disable the solution feature you can set the `restify.ai_solution` to `false` in the `config/restify.php` file so Restify will not call the OpenAI API even you extended the exception handler. This might be useful in automated tests or other environments:

```php
// config/restify.php
'ai_solutions' => true,
```
24 changes: 24 additions & 0 deletions resources/views/prompts/prompt.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
You are a very good PHP developer. Use the following context to find a possible fix for the exception message at the end.

File: /Users/binarcode/Code/ai-errors/app/Documentation.php
Exception: syntax error, unexpected token "{", expecting variable
Line: 193

Snippet including line numbers:
192 public static function getDocVersions(
193 {
194 return [
195 'master' => 'Master',
196 '9.x' => '9.x',

Possible Fix:
Line 192 in Documentation.php file has a syntax error (missing a closing parenthesis). The code should look like this: `public static function getDocVersions()`

File: {!! $file !!}
Exception: {!! $exception !!}
Line: {!! $line !!}

Snippet including line numbers:
{!! $snippet !!}

Possible Fix:
1 change: 1 addition & 0 deletions resources/views/prompts/solution.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{!! $solution !!}
33 changes: 33 additions & 0 deletions src/Exceptions/RestifyHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Binaryk\LaravelRestify\Exceptions;

use Binaryk\LaravelRestify\Exceptions\Solutions\OpenAiSolution;
use Illuminate\Foundation\Exceptions\Handler;
use Throwable;

class RestifyHandler extends Handler
{
protected function convertExceptionToArray(Throwable $e): array
{
$response = parent::convertExceptionToArray($e);

if (! config('restify.ai_solutions')) {
return $response;
}

if (! config('app.debug')) {
return $response;
}

if (! config('openai.api_key')) {
return $response;
}

$solution = (new OpenAiSolution($e))->getSolutionDescription();

return array_merge([
'restify-solution' => $solution,
], $response);
}
}
66 changes: 66 additions & 0 deletions src/Exceptions/Solutions/OpenAiSolution.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Binaryk\LaravelRestify\Exceptions\Solutions;

use Illuminate\Support\Facades\Cache;
use OpenAI\Laravel\Facades\OpenAI;
use Spatie\Backtrace\Backtrace;
use Spatie\Backtrace\Frame;
use Throwable;

class OpenAiSolution
{
protected string $solution;

public function __construct(protected Throwable $throwable)
{
$this->solution = Cache::remember('restify-solution-'.sha1($this->throwable->getTraceAsString()),
now()->addHour(),
fn () => OpenAI::completions()->create([
'model' => 'text-davinci-003',
'prompt' => $this->generatePrompt($this->throwable),
'max_tokens' => 100,
'temperature' => 0,
])->choices[0]->text
);
}

public function getSolutionTitle(): string
{
return 'AI Generated Solution';
}

public function getSolutionDescription(): string
{
return view('restify::prompts.solution', [
'solution' => $this->solution,
])->render();
}

public function getDocumentationLinks(): array
{
return [];
}

protected function getApplicationFrame(Throwable $throwable): ?Frame
{
$backtrace = Backtrace::createForThrowable($throwable)->applicationPath(base_path());
$frames = $backtrace->frames();

return $frames[$backtrace->firstApplicationFrameIndex()] ?? null;
}

protected function generatePrompt(Throwable $throwable): string
{
$applicationFrame = $this->getApplicationFrame($throwable);

$snippet = $applicationFrame->getSnippet(15);

return (string) view('restify::prompts.prompt', [
'snippet' => collect($snippet)->map(fn ($line, $number) => $number.' '.$line)->join(PHP_EOL),
'file' => $applicationFrame->file,
'line' => $applicationFrame->lineNumber,
'exception' => $throwable->getMessage(),
]);
}
}
1 change: 1 addition & 0 deletions src/LaravelRestifyServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function configurePackage(Package $package): void
->hasConfigFile()
->hasMigration('create_action_logs_table')
->runsMigrations()
->hasViews('restify')
->hasCommands([
RepositoryCommand::class,
ActionCommand::class,
Expand Down

0 comments on commit 7f14144

Please sign in to comment.