From 4c1da2308e3e235961af4c3f6da6088fe0b4b03b Mon Sep 17 00:00:00 2001 From: hackerESQ Date: Thu, 31 Oct 2024 12:09:06 -0500 Subject: [PATCH] feat:adds LLM capabilities to chat with your portfolios and holdings --- app/Http/Controllers/HoldingController.php | 17 +- app/Http/Controllers/PortfolioController.php | 21 +- app/Models/AiChat.php | 37 + app/Models/Holding.php | 11 + app/Models/Portfolio.php | 11 + composer.json | 1 + composer.lock | 1281 ++++++++++++++--- config/openai.php | 31 + ...024_10_30_000001_create_ai_chats_table.php | 40 + lang/en.json | 9 +- lang/es.json | 9 +- .../components/ib-alpine-modal.blade.php | 1 + .../views/components/ib-drawer.blade.php | 2 +- .../components/ib-livewire-modal.blade.php | 1 + .../views/components/ib-textarea.blade.php | 4 +- resources/views/holding/show.blade.php | 50 + .../views/livewire/ai-chat-window.blade.php | 290 ++++ .../livewire/manage-portfolio-form.blade.php | 2 +- .../livewire/share-portfolio-form.blade.php | 1 + resources/views/portfolio/show.blade.php | 24 + 20 files changed, 1662 insertions(+), 181 deletions(-) create mode 100644 app/Models/AiChat.php create mode 100644 config/openai.php create mode 100644 database/migrations/2024_10_30_000001_create_ai_chats_table.php create mode 100644 resources/views/livewire/ai-chat-window.blade.php diff --git a/app/Http/Controllers/HoldingController.php b/app/Http/Controllers/HoldingController.php index 8f2f240..20ab552 100644 --- a/app/Http/Controllers/HoldingController.php +++ b/app/Http/Controllers/HoldingController.php @@ -24,6 +24,21 @@ class HoldingController extends Controller ->portfolio($portfolio->id) ->firstOrFail(); - return view('holding.show', compact(['portfolio', 'holding'])); + $formattedTransactions = $this->getFormattedTransactions($holding); + + return view('holding.show', compact(['portfolio', 'holding', 'formattedTransactions'])); + } + + public function getFormattedTransactions($holding) + { + $formattedTransactions = ''; + foreach($holding->transactions->where('symbol', $holding->symbol)->sortByDesc('date') as $transaction) { + $formattedTransactions .= " * ".$transaction->date->format('Y-m-d') + ." ". $transaction->transaction_type + ." ". $transaction->quantity + ." @ ". $transaction->cost_basis + ." each \n\n"; + } + return $formattedTransactions; } } diff --git a/app/Http/Controllers/PortfolioController.php b/app/Http/Controllers/PortfolioController.php index c9e249e..22d157d 100644 --- a/app/Http/Controllers/PortfolioController.php +++ b/app/Http/Controllers/PortfolioController.php @@ -39,7 +39,26 @@ class PortfolioController extends Controller ->first(); } ); + + $formattedHoldings = $this->getFormattedHoldings($portfolio); - return view('portfolio.show', compact(['portfolio', 'metrics'])); + return view('portfolio.show', compact(['portfolio', 'metrics', 'formattedHoldings'])); + } + + public function getFormattedHoldings($portfolio) + { + $formattedHoldings = ''; + foreach($portfolio->holdings as $holding) { + $formattedHoldings .= " * Holding of ".$holding->market_data->name." (".$holding->symbol.")" + ."; own ". ($holding->quantity > 0 ? $holding->quantity : 'ZERO') . " shares" + ."; avg cost basis ". $holding->average_cost_basis + ."; curr market value ". $holding->market_data->market_value + ."; unrealized gains ". $holding->market_gain_dollars + ."; realized gains ". $holding->realized_gain_dollars + ."; dividends earned ". $holding->dividends_earned + ."\n\n"; + + } + return $formattedHoldings; } } diff --git a/app/Models/AiChat.php b/app/Models/AiChat.php new file mode 100644 index 0000000..3b4e386 --- /dev/null +++ b/app/Models/AiChat.php @@ -0,0 +1,37 @@ +user_id = auth()->user()->id; + }); + } + + public function user() { + return $this->belongsTo(User::class); + } + + public function chatable() + { + return $this->morphTo(); + } +} diff --git a/app/Models/Holding.php b/app/Models/Holding.php index 098c525..e4db091 100644 --- a/app/Models/Holding.php +++ b/app/Models/Holding.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\Split; +use App\Models\AiChat; use App\Models\Dividend; use App\Models\Portfolio; use App\Models\MarketData; @@ -131,6 +132,16 @@ class Holding extends Model ->orderBy('date', 'DESC'); } + /** + * Related chats for holding + * + * @return void + */ + public function chats() + { + return $this->morphMany(AiChat::class, 'chatable'); + } + public function scopeWithMarketData($query) { return $query->withAggregate('market_data', 'name') diff --git a/app/Models/Portfolio.php b/app/Models/Portfolio.php index c7cf606..3d3e41c 100644 --- a/app/Models/Portfolio.php +++ b/app/Models/Portfolio.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Models\AiChat; use Carbon\CarbonPeriod; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; @@ -64,6 +65,16 @@ class Portfolio extends Model return $this->hasMany(DailyChange::class); } + /** + * Related chats for portfolio + * + * @return void + */ + public function chats() + { + return $this->morphMany(AiChat::class, 'chatable'); + } + public function scopeMyPortfolios() { return $this->whereHas('users', function ($query) { diff --git a/composer.json b/composer.json index a629397..b02d0cd 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "livewire/livewire": "^3.5", "livewire/volt": "^1.6", "maatwebsite/excel": "^3.1", + "openai-php/laravel": "^0.10.2", "predis/predis": "^2.2", "robsontenorio/mary": "^1.35", "scheb/yahoo-finance-api": "^4.11", diff --git a/composer.lock b/composer.lock index ecd0f51..e16a0e4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,160 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7fcc707e9612c64b40469d52efdb75e6", + "content-hash": "5db6b7e4a69c3e4bfc31d7d4f2bde9cd", "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "time": "2024-10-18T22:15:13+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.325.0", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "ea36e53745cff21519c2dadd808e2482f0bfadf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/ea36e53745cff21519c2dadd808e2482f0bfadf5", + "reference": "ea36e53745cff21519c2dadd808e2482f0bfadf5", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/promises": "^1.4.0 || ^2.0", + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "mtdowling/jmespath.php": "^2.6", + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^1.10.22", + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3 || ^4.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + }, + "exclude-from-classmap": [ + "src/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.325.0" + }, + "time": "2024-10-30T18:11:21+00:00" + }, { "name": "bacon/bacon-qr-code", "version": "v3.0.1", @@ -131,16 +283,16 @@ }, { "name": "blade-ui-kit/blade-icons", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/blade-ui-kit/blade-icons.git", - "reference": "8f787baf09d88cdfd6ec4dbaba11ebfa885f0595" + "reference": "75a54a3f5a2810fcf6574ab23e91b6cc229a1b53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/blade-ui-kit/blade-icons/zipball/8f787baf09d88cdfd6ec4dbaba11ebfa885f0595", - "reference": "8f787baf09d88cdfd6ec4dbaba11ebfa885f0595", + "url": "https://api.github.com/repos/blade-ui-kit/blade-icons/zipball/75a54a3f5a2810fcf6574ab23e91b6cc229a1b53", + "reference": "75a54a3f5a2810fcf6574ab23e91b6cc229a1b53", "shasum": "" }, "require": { @@ -208,7 +360,7 @@ "type": "paypal" } ], - "time": "2024-08-14T14:25:11+00:00" + "time": "2024-10-17T17:38:00+00:00" }, { "name": "brick/math", @@ -1292,16 +1444,16 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -1355,7 +1507,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -1371,7 +1523,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1575,6 +1727,56 @@ ], "time": "2023-12-03T19:50:20+00:00" }, + { + "name": "hollodotme/fast-cgi-client", + "version": "v3.1.7", + "source": { + "type": "git", + "url": "https://github.com/hollodotme/fast-cgi-client.git", + "reference": "062182d4eda73c161cc2839783acc83096ec0f37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hollodotme/fast-cgi-client/zipball/062182d4eda73c161cc2839783acc83096ec0f37", + "reference": "062182d4eda73c161cc2839783acc83096ec0f37", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1" + }, + "require-dev": { + "ext-xdebug": ">=2.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "hollodotme\\FastCGI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Holger Woltersdorf", + "email": "hw@hollo.me" + } + ], + "description": "A PHP fast CGI client to send requests (a)synchronously to PHP-FPM.", + "keywords": [ + "Socket", + "async", + "fastcgi", + "php-fpm" + ], + "support": { + "issues": "https://github.com/hollodotme/fast-cgi-client/issues", + "source": "https://github.com/hollodotme/fast-cgi-client/tree/v3.1.7" + }, + "time": "2021-12-07T10:10:20+00:00" + }, { "name": "jfcherng/php-color-output", "version": "3.0.0", @@ -1814,16 +2016,16 @@ }, { "name": "laravel/fortify", - "version": "v1.24.2", + "version": "v1.24.4", "source": { "type": "git", "url": "https://github.com/laravel/fortify.git", - "reference": "42695c45087e5abb3e173725b4f1ef4956a7b47d" + "reference": "5bd3bdd535acf4054865c64eec6d8bb8c60cc127" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/42695c45087e5abb3e173725b4f1ef4956a7b47d", - "reference": "42695c45087e5abb3e173725b4f1ef4956a7b47d", + "url": "https://api.github.com/repos/laravel/fortify/zipball/5bd3bdd535acf4054865c64eec6d8bb8c60cc127", + "reference": "5bd3bdd535acf4054865c64eec6d8bb8c60cc127", "shasum": "" }, "require": { @@ -1875,20 +2077,20 @@ "issues": "https://github.com/laravel/fortify/issues", "source": "https://github.com/laravel/fortify" }, - "time": "2024-09-16T19:20:52+00:00" + "time": "2024-10-29T13:59:23+00:00" }, { "name": "laravel/framework", - "version": "v11.28.0", + "version": "v11.30.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "80843d20cf9337b94fb71e7f25f33f4b1e6c7c5f" + "reference": "dff716442d9c229d716be82ccc9a7de52eb97193" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/80843d20cf9337b94fb71e7f25f33f4b1e6c7c5f", - "reference": "80843d20cf9337b94fb71e7f25f33f4b1e6c7c5f", + "url": "https://api.github.com/repos/laravel/framework/zipball/dff716442d9c229d716be82ccc9a7de52eb97193", + "reference": "dff716442d9c229d716be82ccc9a7de52eb97193", "shasum": "" }, "require": { @@ -2084,20 +2286,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-15T14:14:58+00:00" + "time": "2024-10-30T15:00:34+00:00" }, { "name": "laravel/jetstream", - "version": "v5.3.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/laravel/jetstream.git", - "reference": "c9c23e14ac6c9cef7daf4f2754382de1ba5d567e" + "reference": "d51ec6942f34e76ba4736452d5f4d6f54a186a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/jetstream/zipball/c9c23e14ac6c9cef7daf4f2754382de1ba5d567e", - "reference": "c9c23e14ac6c9cef7daf4f2754382de1ba5d567e", + "url": "https://api.github.com/repos/laravel/jetstream/zipball/d51ec6942f34e76ba4736452d5f4d6f54a186a6e", + "reference": "d51ec6942f34e76ba4736452d5f4d6f54a186a6e", "shasum": "" }, "require": { @@ -2151,7 +2353,7 @@ "issues": "https://github.com/laravel/jetstream/issues", "source": "https://github.com/laravel/jetstream" }, - "time": "2024-10-12T16:23:32+00:00" + "time": "2024-10-30T13:32:43+00:00" }, { "name": "laravel/prompts", @@ -2475,6 +2677,85 @@ }, "time": "2024-09-23T13:32:56+00:00" }, + { + "name": "laravel/vapor-core", + "version": "v2.37.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/vapor-core.git", + "reference": "9fac8cd0f9b6979887253dd676e04ecb868be615" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/vapor-core/zipball/9fac8cd0f9b6979887253dd676e04ecb868be615", + "reference": "9fac8cd0f9b6979887253dd676e04ecb868be615", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.80", + "guzzlehttp/guzzle": "^6.3|^7.0", + "guzzlehttp/promises": "^1.4|^2.0", + "hollodotme/fast-cgi-client": "^3.0", + "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/queue": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "monolog/monolog": "^1.12|^2.0|^3.2", + "nyholm/psr7": "^1.0", + "php": "^7.2|^8.0", + "riverline/multipart-parser": "^2.0.9", + "symfony/process": "^4.3|^5.0|^6.0|^7.0", + "symfony/psr-http-message-bridge": "^1.0|^2.0|^6.4|^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.0|^9.0|^10.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Vapor\\VaporServiceProvider" + ], + "aliases": { + "Vapor": "Laravel\\Vapor\\Vapor" + } + } + }, + "autoload": { + "files": [ + "src/debug.php" + ], + "psr-4": { + "Laravel\\Vapor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The kernel and invocation handlers for Laravel Vapor", + "homepage": "https://github.com/laravel/vapor-core", + "keywords": [ + "laravel", + "vapor" + ], + "support": { + "source": "https://github.com/laravel/vapor-core/tree/v2.37.1" + }, + "time": "2024-03-26T16:55:06+00:00" + }, { "name": "league/commonmark", "version": "2.5.3", @@ -2746,6 +3027,61 @@ }, "time": "2024-10-08T08:58:34+00:00" }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "3.29.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/c6ff6d4606e48249b63f269eba7fabdb584e76a9", + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.295.10", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.29.0" + }, + "time": "2024-08-17T13:10:48+00:00" + }, { "name": "league/flysystem-local", "version": "3.29.0", @@ -2929,16 +3265,16 @@ }, { "name": "livewire/livewire", - "version": "v3.5.11", + "version": "v3.5.12", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "f4909e68884b52f13920d108a80854ebcce8200f" + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/f4909e68884b52f13920d108a80854ebcce8200f", - "reference": "f4909e68884b52f13920d108a80854ebcce8200f", + "url": "https://api.github.com/repos/livewire/livewire/zipball/3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", "shasum": "" }, "require": { @@ -2993,7 +3329,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.5.11" + "source": "https://github.com/livewire/livewire/tree/v3.5.12" }, "funding": [ { @@ -3001,7 +3337,7 @@ "type": "github" } ], - "time": "2024-10-15T14:29:19+00:00" + "time": "2024-10-15T19:35:06+00:00" }, { "name": "livewire/volt", @@ -3505,6 +3841,72 @@ ], "time": "2024-06-28T09:40:51+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.33" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" + }, + "time": "2024-09-04T18:46:31+00:00" + }, { "name": "nesbot/carbon", "version": "3.8.0", @@ -3904,6 +4306,265 @@ ], "time": "2024-10-15T16:15:16+00:00" }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, + { + "name": "openai-php/client", + "version": "v0.10.2", + "source": { + "type": "git", + "url": "https://github.com/openai-php/client.git", + "reference": "efa92628ba9fb56f7877c0d616f5221c4a447856" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openai-php/client/zipball/efa92628ba9fb56f7877c0d616f5221c4a447856", + "reference": "efa92628ba9fb56f7877c0d616f5221c4a447856", + "shasum": "" + }, + "require": { + "php": "^8.1.0", + "php-http/discovery": "^1.20.0", + "php-http/multipart-stream-builder": "^1.4.2", + "psr/http-client": "^1.0.3", + "psr/http-client-implementation": "^1.0.1", + "psr/http-factory-implementation": "*", + "psr/http-message": "^1.1.0|^2.0.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.9.2", + "guzzlehttp/psr7": "^2.7.0", + "laravel/pint": "^1.18.1", + "mockery/mockery": "^1.6.12", + "nunomaduro/collision": "^7.11.0|^8.5.0", + "pestphp/pest": "^2.36.0|^3.4.1", + "pestphp/pest-plugin-arch": "^2.7|^3.0", + "pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0", + "phpstan/phpstan": "^1.12.6", + "rector/rector": "^1.2.7", + "symfony/var-dumper": "^6.4.11|^7.1.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/OpenAI.php" + ], + "psr-4": { + "OpenAI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + }, + { + "name": "Sandro Gehri" + } + ], + "description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API", + "keywords": [ + "GPT-3", + "api", + "client", + "codex", + "dall-e", + "language", + "natural", + "openai", + "php", + "processing", + "sdk" + ], + "support": { + "issues": "https://github.com/openai-php/client/issues", + "source": "https://github.com/openai-php/client/tree/v0.10.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-10-17T20:28:25+00:00" + }, + { + "name": "openai-php/laravel", + "version": "v0.10.2", + "source": { + "type": "git", + "url": "https://github.com/openai-php/laravel.git", + "reference": "24815ef1bda71cc0715f7aefe4506e77329e560f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/openai-php/laravel/zipball/24815ef1bda71cc0715f7aefe4506e77329e560f", + "reference": "24815ef1bda71cc0715f7aefe4506e77329e560f", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.9.2", + "laravel/framework": "^9.46.0|^10.34.2|^11.23.5", + "openai-php/client": "^0.10.2", + "php": "^8.1.0" + }, + "require-dev": { + "laravel/pint": "^1.17.3", + "pestphp/pest": "^2.35.1|^3.0.0", + "pestphp/pest-plugin-arch": "^2.7.0|^3.0.0", + "phpstan/phpstan": "^1.12.4", + "symfony/var-dumper": "^6.4.0|^7.1.4" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "OpenAI\\Laravel\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "OpenAI\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "OpenAI PHP for Laravel is a supercharged PHP API client that allows you to interact with the Open AI API", + "keywords": [ + "GPT-3", + "api", + "client", + "codex", + "dall-e", + "language", + "laravel", + "natural", + "openai", + "php", + "processing", + "sdk" + ], + "support": { + "issues": "https://github.com/openai-php/laravel/issues", + "source": "https://github.com/openai-php/laravel/tree/v0.10.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-10-17T20:34:07+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v3.0.0", @@ -4021,6 +4682,141 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "php-http/discovery", + "version": "1.20.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/discovery.git", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d", + "reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "*", + "psr/http-factory-implementation": "*", + "psr/http-message-implementation": "*" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "graham-campbell/phpspec-skip-example-extension": "^5.0", + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" + }, + "type": "composer-plugin", + "extra": { + "class": "Http\\Discovery\\Composer\\Plugin", + "plugin-optional": true + }, + "autoload": { + "psr-4": { + "Http\\Discovery\\": "src/" + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr17", + "psr7" + ], + "support": { + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/1.20.0" + }, + "time": "2024-10-02T11:20:13+00:00" + }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e", + "reference": "10086e6de6f53489cca5ecc45b6f468604d3460e", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2" + }, + "time": "2024-09-04T13:22:54+00:00" + }, { "name": "phpoffice/phpspreadsheet", "version": "1.29.2", @@ -5141,17 +5937,73 @@ "time": "2024-04-27T21:32:50+00:00" }, { - "name": "robsontenorio/mary", - "version": "1.40.3", + "name": "riverline/multipart-parser", + "version": "2.1.2", "source": { "type": "git", - "url": "https://github.com/robsontenorio/mary.git", - "reference": "a04203e0e428bc55253f7ae7a619c9996c1d9398" + "url": "https://github.com/Riverline/multipart-parser.git", + "reference": "7a9f4646db5181516c61b8e0225a343189beedcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robsontenorio/mary/zipball/a04203e0e428bc55253f7ae7a619c9996c1d9398", - "reference": "a04203e0e428bc55253f7ae7a619c9996c1d9398", + "url": "https://api.github.com/repos/Riverline/multipart-parser/zipball/7a9f4646db5181516c61b8e0225a343189beedcd", + "reference": "7a9f4646db5181516c61b8e0225a343189beedcd", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "laminas/laminas-diactoros": "^1.8.7 || ^2.11.1", + "phpunit/phpunit": "^5.7 || ^9.0", + "psr/http-message": "^1.0", + "symfony/psr-http-message-bridge": "^1.1 || ^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Riverline\\MultiPartParser\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Cambien", + "email": "romain@cambien.net" + }, + { + "name": "Riverline", + "homepage": "http://www.riverline.fr" + } + ], + "description": "One class library to parse multipart content with encoding and charset support.", + "keywords": [ + "http", + "multipart", + "parser" + ], + "support": { + "issues": "https://github.com/Riverline/multipart-parser/issues", + "source": "https://github.com/Riverline/multipart-parser/tree/2.1.2" + }, + "time": "2024-03-12T16:46:05+00:00" + }, + { + "name": "robsontenorio/mary", + "version": "1.41.2", + "source": { + "type": "git", + "url": "https://github.com/robsontenorio/mary.git", + "reference": "ec5661a7cb4911ffb48dbf64c4455bdb52d0f196" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robsontenorio/mary/zipball/ec5661a7cb4911ffb48dbf64c4455bdb52d0f196", + "reference": "ec5661a7cb4911ffb48dbf64c4455bdb52d0f196", "shasum": "" }, "require": { @@ -5217,7 +6069,7 @@ ], "support": { "issues": "https://github.com/robsontenorio/mary/issues", - "source": "https://github.com/robsontenorio/mary/tree/1.40.3" + "source": "https://github.com/robsontenorio/mary/tree/1.41.2" }, "funding": [ { @@ -5225,20 +6077,20 @@ "type": "github" } ], - "time": "2024-10-02T20:02:12+00:00" + "time": "2024-10-30T18:25:35+00:00" }, { "name": "scheb/yahoo-finance-api", - "version": "v4.11.0", + "version": "v4.11.1", "source": { "type": "git", "url": "https://github.com/scheb/yahoo-finance-api.git", - "reference": "34ec706ed30a807e0edad6ad96c1ca75689a5235" + "reference": "acb9ebacc057a13d69ca2b02fc88096369a5848a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scheb/yahoo-finance-api/zipball/34ec706ed30a807e0edad6ad96c1ca75689a5235", - "reference": "34ec706ed30a807e0edad6ad96c1ca75689a5235", + "url": "https://api.github.com/repos/scheb/yahoo-finance-api/zipball/acb9ebacc057a13d69ca2b02fc88096369a5848a", + "reference": "acb9ebacc057a13d69ca2b02fc88096369a5848a", "shasum": "" }, "require": { @@ -5278,9 +6130,9 @@ ], "support": { "issues": "https://github.com/scheb/yahoo-finance-api/issues", - "source": "https://github.com/scheb/yahoo-finance-api/tree/v4.11.0" + "source": "https://github.com/scheb/yahoo-finance-api/tree/v4.11.1" }, - "time": "2024-10-15T17:48:51+00:00" + "time": "2024-10-16T17:35:40+00:00" }, { "name": "spatie/laravel-package-tools", @@ -5452,16 +6304,16 @@ }, { "name": "symfony/clock", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" + "reference": "97bebc53548684c17ed696bc8af016880f0f098d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", + "url": "https://api.github.com/repos/symfony/clock/zipball/97bebc53548684c17ed696bc8af016880f0f098d", + "reference": "97bebc53548684c17ed696bc8af016880f0f098d", "shasum": "" }, "require": { @@ -5506,7 +6358,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.1" + "source": "https://github.com/symfony/clock/tree/v7.1.6" }, "funding": [ { @@ -5522,20 +6374,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { @@ -5599,7 +6451,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -5615,20 +6467,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", "shasum": "" }, "require": { @@ -5664,7 +6516,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + "source": "https://github.com/symfony/css-selector/tree/v7.1.6" }, "funding": [ { @@ -5680,7 +6532,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5751,16 +6603,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.1.3", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c" + "reference": "d60117093c2a9fe667baa8fedf84e8a09b9c592f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/d60117093c2a9fe667baa8fedf84e8a09b9c592f", + "reference": "d60117093c2a9fe667baa8fedf84e8a09b9c592f", "shasum": "" }, "require": { @@ -5806,7 +6658,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.1.3" + "source": "https://github.com/symfony/error-handler/tree/v7.1.6" }, "funding": [ { @@ -5822,20 +6674,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T13:02:51+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + "reference": "87254c78dd50721cfd015b62277a8281c5589702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", + "reference": "87254c78dd50721cfd015b62277a8281c5589702", "shasum": "" }, "require": { @@ -5886,7 +6738,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" }, "funding": [ { @@ -5902,7 +6754,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5982,16 +6834,16 @@ }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -6026,7 +6878,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" }, "funding": [ { @@ -6042,20 +6894,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b" + "reference": "3d7bbf071b25f802f7d55524d408bed414ea71e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e30ef73b1e44eea7eb37ba69600a354e553f694b", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3d7bbf071b25f802f7d55524d408bed414ea71e2", + "reference": "3d7bbf071b25f802f7d55524d408bed414ea71e2", "shasum": "" }, "require": { @@ -6103,7 +6955,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.1.5" + "source": "https://github.com/symfony/http-foundation/tree/v7.1.6" }, "funding": [ { @@ -6119,20 +6971,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-11T19:23:14+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b" + "reference": "5d8315899cd76b2e7e29179bf5fea103e41bdf03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/44204d96150a9df1fc57601ec933d23fefc2d65b", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5d8315899cd76b2e7e29179bf5fea103e41bdf03", + "reference": "5d8315899cd76b2e7e29179bf5fea103e41bdf03", "shasum": "" }, "require": { @@ -6217,7 +7069,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.1.5" + "source": "https://github.com/symfony/http-kernel/tree/v7.1.6" }, "funding": [ { @@ -6233,20 +7085,20 @@ "type": "tidelift" } ], - "time": "2024-09-21T06:09:21+00:00" + "time": "2024-10-27T13:54:21+00:00" }, { "name": "symfony/mailer", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b" + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/bbf21460c56f29810da3df3e206e38dfbb01e80b", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/69c9948451fb3a6a4d47dc8261d1794734e76cdd", + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd", "shasum": "" }, "require": { @@ -6297,7 +7149,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.5" + "source": "https://github.com/symfony/mailer/tree/v7.1.6" }, "funding": [ { @@ -6313,20 +7165,20 @@ "type": "tidelift" } ], - "time": "2024-09-08T12:32:26+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/mime", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff" + "reference": "caa1e521edb2650b8470918dfe51708c237f0598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/711d2e167e8ce65b05aea6b258c449671cdd38ff", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff", + "url": "https://api.github.com/repos/symfony/mime/zipball/caa1e521edb2650b8470918dfe51708c237f0598", + "reference": "caa1e521edb2650b8470918dfe51708c237f0598", "shasum": "" }, "require": { @@ -6381,7 +7233,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.5" + "source": "https://github.com/symfony/mime/tree/v7.1.6" }, "funding": [ { @@ -6397,7 +7249,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7037,16 +7889,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", "shasum": "" }, "require": { @@ -7078,7 +7930,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.1.6" }, "funding": [ { @@ -7094,20 +7946,103 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { - "name": "symfony/routing", - "version": "v7.1.4", + "name": "symfony/psr-http-message-bridge", + "version": "v7.1.6", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7" + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "f16471bb19f6685b9ccf0a2c03c213840ae68cd6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/f16471bb19f6685b9ccf0a2c03c213840ae68cd6", + "reference": "f16471bb19f6685b9ccf0a2c03c213840ae68cd6", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^6.4|^7.0" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "https://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "66a2c469f6c22d08603235c46a20007c0701ea0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/66a2c469f6c22d08603235c46a20007c0701ea0a", + "reference": "66a2c469f6c22d08603235c46a20007c0701ea0a", "shasum": "" }, "require": { @@ -7159,7 +8094,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.1.4" + "source": "https://github.com/symfony/routing/tree/v7.1.6" }, "funding": [ { @@ -7175,7 +8110,7 @@ "type": "tidelift" } ], - "time": "2024-08-29T08:16:25+00:00" + "time": "2024-10-01T08:31:23+00:00" }, { "name": "symfony/service-contracts", @@ -7262,16 +8197,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -7329,7 +8264,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -7345,20 +8280,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/translation", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea" + "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/235535e3f84f3dfbdbde0208ede6ca75c3a489ea", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea", + "url": "https://api.github.com/repos/symfony/translation/zipball/b9f72ab14efdb6b772f85041fa12f820dee8d55f", + "reference": "b9f72ab14efdb6b772f85041fa12f820dee8d55f", "shasum": "" }, "require": { @@ -7423,7 +8358,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.1.5" + "source": "https://github.com/symfony/translation/tree/v7.1.6" }, "funding": [ { @@ -7439,7 +8374,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T06:30:38+00:00" + "time": "2024-09-28T12:35:13+00:00" }, { "name": "symfony/translation-contracts", @@ -7521,16 +8456,16 @@ }, { "name": "symfony/uid", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317" + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/8c7bb8acb933964055215d89f9a9871df0239317", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317", + "url": "https://api.github.com/repos/symfony/uid/zipball/65befb3bb2d503bbffbd08c815aa38b472999917", + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917", "shasum": "" }, "require": { @@ -7575,7 +8510,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.5" + "source": "https://github.com/symfony/uid/tree/v7.1.6" }, "funding": [ { @@ -7591,20 +8526,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d" + "reference": "cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e20e03889539fd4e4211e14d2179226c513c010d", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c", + "reference": "cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c", "shasum": "" }, "require": { @@ -7658,7 +8593,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.5" + "source": "https://github.com/symfony/var-dumper/tree/v7.1.6" }, "funding": [ { @@ -7674,7 +8609,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T10:07:02+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8279,16 +9214,16 @@ }, { "name": "laravel/sail", - "version": "v1.36.0", + "version": "v1.37.1", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "f184d3d687155d06bc8cb9ff6dc48596a138460c" + "reference": "7efa151ea0d16f48233d6a6cd69f81270acc6e93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/f184d3d687155d06bc8cb9ff6dc48596a138460c", - "reference": "f184d3d687155d06bc8cb9ff6dc48596a138460c", + "url": "https://api.github.com/repos/laravel/sail/zipball/7efa151ea0d16f48233d6a6cd69f81270acc6e93", + "reference": "7efa151ea0d16f48233d6a6cd69f81270acc6e93", "shasum": "" }, "require": { @@ -8338,7 +9273,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-10-10T13:26:02+00:00" + "time": "2024-10-29T20:18:14+00:00" }, { "name": "mockery/mockery", @@ -9023,16 +9958,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.4.1", + "version": "11.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e" + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7875627f15f4da7e7f0823d1f323f7295a77334e", - "reference": "7875627f15f4da7e7f0823d1f323f7295a77334e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e8e8ed1854de5d36c088ec1833beae40d2dedd76", + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76", "shasum": "" }, "require": { @@ -9046,21 +9981,21 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.6", + "phpunit/php-code-coverage": "^11.0.7", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.1.0", + "sebastian/comparator": "^6.1.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.0", - "sebastian/version": "^5.0.1" + "sebastian/version": "^5.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -9103,7 +10038,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.3" }, "funding": [ { @@ -9119,7 +10054,7 @@ "type": "tidelift" } ], - "time": "2024-10-08T15:38:37+00:00" + "time": "2024-10-28T13:07:50+00:00" }, { "name": "sebastian/cli-parser", @@ -9293,16 +10228,16 @@ }, { "name": "sebastian/comparator", - "version": "6.1.0", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", "shasum": "" }, "require": { @@ -9313,12 +10248,12 @@ "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.3" + "phpunit/phpunit": "^11.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.2-dev" } }, "autoload": { @@ -9358,7 +10293,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" }, "funding": [ { @@ -9366,7 +10301,7 @@ "type": "github" } ], - "time": "2024-09-11T15:42:56+00:00" + "time": "2024-10-31T05:30:08+00:00" }, { "name": "sebastian/complexity", @@ -10046,16 +10981,16 @@ }, { "name": "symfony/yaml", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4" + "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4e561c316e135e053bd758bf3b3eb291d9919de4", - "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", + "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", "shasum": "" }, "require": { @@ -10097,7 +11032,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.1.5" + "source": "https://github.com/symfony/yaml/tree/v7.1.6" }, "funding": [ { @@ -10113,7 +11048,7 @@ "type": "tidelift" } ], - "time": "2024-09-17T12:49:58+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "theseer/tokenizer", diff --git a/config/openai.php b/config/openai.php new file mode 100644 index 0000000..02aa769 --- /dev/null +++ b/config/openai.php @@ -0,0 +1,31 @@ + env('OPENAI_API_KEY'), + 'organization' => env('OPENAI_ORGANIZATION'), + + /* + |-------------------------------------------------------------------------- + | Request Timeout + |-------------------------------------------------------------------------- + | + | The timeout may be used to specify the maximum number of seconds to wait + | for a response. By default, the client will time out after 30 seconds. + */ + + 'request_timeout' => env('OPENAI_REQUEST_TIMEOUT', 30), + + // + 'model' => env('OPENAI_MODEL', 'gpt-4o'), +]; diff --git a/database/migrations/2024_10_30_000001_create_ai_chats_table.php b/database/migrations/2024_10_30_000001_create_ai_chats_table.php new file mode 100644 index 0000000..3b6adc6 --- /dev/null +++ b/database/migrations/2024_10_30_000001_create_ai_chats_table.php @@ -0,0 +1,40 @@ +uuid('id')->primary(); + $table->foreignIdFor(User::class, 'user_id')->constrained()->onDelete('cascade'); + $table->morphs('chatable'); + $table->string('role'); + $table->text('content'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ai_chats'); + } +} diff --git a/lang/en.json b/lang/en.json index 53a60a8..f40abc4 100644 --- a/lang/en.json +++ b/lang/en.json @@ -19,6 +19,7 @@ "and": "and", "Yes": "Yes", "you": "you", + "You": "You", "Nothing to show here yet": "Nothing to show here yet", "Try again": "Try again", @@ -369,5 +370,11 @@ "Importing transactions...": "Importing transactions...", "Importing daily changes...": "Importing daily changes...", "Import completed successfully!": "Import completed successfully!", - "Your import will continue in the background": "Your import will continue in the background" + "Your import will continue in the background": "Your import will continue in the background", + + "AI Chat": "AI Chat", + "Hi, how can I help?": "Hi, how can I help?", + "Have a question? AI might be able to help...": "Have a question? AI might be able to help...", + "Feel free to ask me a question!": "Feel free to ask me a question!", + "Advice generated by AI may contain errors. Use at your own risk. Always consult a licensed investment advisor.": "Advice generated by AI may contain errors. Use at your own risk. Always consult a licensed investment advisor." } \ No newline at end of file diff --git a/lang/es.json b/lang/es.json index a09425e..13a81b2 100644 --- a/lang/es.json +++ b/lang/es.json @@ -19,6 +19,7 @@ "and": "y", "Yes": "Sí", "you": "tú", + "You": "Tú", "Nothing to show here yet": "No hay nada que mostrar aquí todavía", "Try again": "Intentar otra vez", @@ -369,5 +370,11 @@ "Importing transactions...": "Importando transacciones...", "Importing daily changes...": "Importando cambios diarios...", "Import completed successfully!": "¡La importación se completó con éxito!", - "Your import will continue in the background": "La importación continuará en segundo plano" + "Your import will continue in the background": "La importación continuará en segundo plano", + + "AI Chat": "Chat de AI", + "Hi, how can I help?": "Hola, ¿cómo puedo ayudarte?", + "Have a question? AI might be able to help...": "¿Tienes una pregunta? La AI podría ayudarte...", + "Feel free to ask me a question!": "¡No dudes en hacerme una pregunta!", + "Advice generated by AI may contain errors. Use at your own risk. Always consult a licensed investment advisor.": "Los consejos generados por AI pueden contener errores. Úsalos bajo tu propio riesgo. Consulta siempre a un asesor de inversiones con licencia." } \ No newline at end of file diff --git a/resources/views/components/ib-alpine-modal.blade.php b/resources/views/components/ib-alpine-modal.blade.php index dd60188..a987240 100644 --- a/resources/views/components/ib-alpine-modal.blade.php +++ b/resources/views/components/ib-alpine-modal.blade.php @@ -39,6 +39,7 @@ @if ($showClose) diff --git a/resources/views/components/ib-drawer.blade.php b/resources/views/components/ib-drawer.blade.php index 2d33ba2..1c75b6e 100644 --- a/resources/views/components/ib-drawer.blade.php +++ b/resources/views/components/ib-drawer.blade.php @@ -38,7 +38,7 @@ @endif @if ($showClose) - + @endif {{ $slot }} diff --git a/resources/views/components/ib-livewire-modal.blade.php b/resources/views/components/ib-livewire-modal.blade.php index 4cd3ffa..1262513 100644 --- a/resources/views/components/ib-livewire-modal.blade.php +++ b/resources/views/components/ib-livewire-modal.blade.php @@ -25,6 +25,7 @@ @if ($showClose) diff --git a/resources/views/components/ib-textarea.blade.php b/resources/views/components/ib-textarea.blade.php index 1153279..a1a7396 100644 --- a/resources/views/components/ib-textarea.blade.php +++ b/resources/views/components/ib-textarea.blade.php @@ -6,7 +6,7 @@ 'rows' => 4 ]) -
+
class([]) }}> @if($label)
diff --git a/resources/views/livewire/ai-chat-window.blade.php b/resources/views/livewire/ai-chat-window.blade.php new file mode 100644 index 0000000..e0846e3 --- /dev/null +++ b/resources/views/livewire/ai-chat-window.blade.php @@ -0,0 +1,290 @@ +messages = $this->chatable->chats()->orderByRaw('created_at, id')->get(['role', 'content'])->toArray(); + } + + public function startCompletion($suggestedPrompt = null) + { + // prevent spam + if ($this->isRateLimited() || $this->streaming) { + array_push($this->messages, [ + 'role' => 'assistant', + 'content' => __('Hang on! You\'re doing that too much.') + ]); + $this->js('scrollChatWindow(250)'); + return; + } + + if ($suggestedPrompt) { + $this->prompt = $suggestedPrompt; + } + + if (empty(trim($this->prompt))) { + $this->resetPrompt(); + + array_push($this->messages, ['role' => 'assistant', 'content' => __('Feel free to ask me a question!')]); + $this->js('scrollChatWindow(250)'); + + return; + } + + $this->chatable->chats()->save(new AiChat(['role' => 'user', 'content' => $this->prompt])); + array_push($this->messages, ['role' => 'user', 'content' => $this->prompt]); + $this->js('scrollChatWindow(250)'); + + $this->resetPrompt(); + + $this->streaming = true; + $this->js('$wire.generate()'); + } + + public function generate(): void + { + + try { + $stream = OpenAI::chat()->createStreamed([ + 'model' => config('openai.model'), + 'messages' => [ + ['role' => 'system', 'content' => $this->system_prompt], + ...array_slice($this->messages, -10) + ], + ]); + } catch (\Exception $e) { + + $this->chatable->chats()->save(new AiChat(['role' => 'assistant', 'content' => $e->getMessage()])); + array_push($this->messages, ['role' => 'assistant', 'content' => $e->getMessage()]); + $this->resetPrompt(); + return; + } + + $this->stream(to: "answer", content: '', replace: true); + + foreach($stream as $response){ + + if(!empty($response->choices[0]->delta->content)) { + $this->stream(to: 'answer', content: $response->choices[0]->delta->content, replace: false); + $this->answer .= $response->choices[0]->delta->content; + } + $this->js('scrollChatWindow()'); + } + + $this->chatable->chats()->save(new AiChat(['role' => 'assistant', 'content' => $this->answer])); + array_push($this->messages, ['role' => 'assistant', 'content' => $this->answer]); + $this->resetPrompt(); + } + + public function resetPrompt(): void + { + $this->answer = null; + $this->prompt = null; + $this->streaming = false; + } + + public function isRateLimited(): bool + { + $rateLimitKey = auth()->id() . '/' . $this->chatable->id; + + if (RateLimiter::tooManyAttempts($rateLimitKey, 20)) { + + return true; + } + + RateLimiter::hit($rateLimitKey, 60); + + return false; + } + + }; ?> + +
+ + + + + + +
+ + + {{-- close button --}} + + + {{-- chat window --}} +
+ +
+ + + +

+ AI {{ __('Hi, how can I help?') }} + +

+
+ + @foreach($messages as $message) + + @if ($message['role'] == 'user') +
+ + + + + +

+ {{ __('You') }} {{ $message['content'] }} +

+
+ + @else +
+ + + +

+ AI {{ $message['content'] }} +

+
+ @endif + + @endforeach + + @if($streaming) +
+ + + +

+ AI {{ $answer }} +

+
+ @endif +
+ + {{-- prompt input --}} +
+
+ @foreach($suggested_prompts as $prompt) + + @endforeach + +
+ +
+ +
+ + +
+ + +
+ +
+

{{ __('Advice generated by AI may contain errors. Use at your own risk. Always consult a licensed investment advisor.') }}

+
+
+
+
+
+ \ No newline at end of file diff --git a/resources/views/livewire/manage-portfolio-form.blade.php b/resources/views/livewire/manage-portfolio-form.blade.php index e70631d..5899952 100644 --- a/resources/views/livewire/manage-portfolio-form.blade.php +++ b/resources/views/livewire/manage-portfolio-form.blade.php @@ -69,7 +69,7 @@ new class extends Component { - + @if (isset($this->portfolio)) @livewire('share-portfolio-form', ['portfolio' => $portfolio]) diff --git a/resources/views/livewire/share-portfolio-form.blade.php b/resources/views/livewire/share-portfolio-form.blade.php index 2e6d136..f80b021 100644 --- a/resources/views/livewire/share-portfolio-form.blade.php +++ b/resources/views/livewire/share-portfolio-form.blade.php @@ -182,6 +182,7 @@ new class extends Component { class="btn-sm btn-ghost btn-circle" wire:click="deleteUser('{{ $user->id }}')" spinner="deleteUser('{{ $user->id }}')" + title="{{ __('Remove Access') }}" >
diff --git a/resources/views/portfolio/show.blade.php b/resources/views/portfolio/show.blade.php index b2979c9..261871a 100644 --- a/resources/views/portfolio/show.blade.php +++ b/resources/views/portfolio/show.blade.php @@ -153,6 +153,30 @@ --}} + @livewire('ai-chat-window', [ + 'chatable' => $portfolio, + 'suggested_prompts' => [ + [ + 'text' => 'Which holding is most successful?', + 'value' => 'Which holding is most successful in this portfolio?', + ], + [ + 'text' => 'Should I diversify more?', + 'value' => 'Is my portfolio diverse enough?', + ] + ], + 'system_prompt' => " + You are an investment portfolio assistant providing advice to an investor. Use the following information to provide relevant recommendations. Use the words 'likely' or 'may' instead of concrete statements (except for obvious statements of fact or common sense): + + The investor has the following holdings in this portfolio: + + {$formattedHoldings} + + Based on the current market data, quantity owned, and average cost basis, you can determine the performance of any holding. + + Below is the question from the investor. Considering these facts, provide a concise response to the following question (give a direct response). Limit your response to no more than 75 words and consider using a common decision framework:" + ]) + \ No newline at end of file