Skip to content

Commit

Permalink
Merge pull request #4 from outl1ne/pricing
Browse files Browse the repository at this point in the history
feat: add cost metrics to OpenAI requests resource
  • Loading branch information
allantatter authored Feb 26, 2024
2 parents a031dd4 + 9b80669 commit 1a48bd0
Show file tree
Hide file tree
Showing 18 changed files with 299 additions and 7 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ This is currently an **unstable** package, breaking changes are to be expected.

If you need any features to be implemented or bump its priority in our backlog then feel free to make an inquiry via email at [email protected].

## Screenshots

![Screenshot](resources/media/screenshot1.png)

## Requirements

- `php: >=8.1`
Expand Down
11 changes: 11 additions & 0 deletions config/nova-openai.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,15 @@
'headers' => [
'OpenAI-Beta' => 'assistants=v1',
],

/*
|--------------------------------------------------------------------------
| Pricing configuration
|--------------------------------------------------------------------------
|
| Path to the pricing configuration file to be used to calculate
| the cost of requests made against OpenAI API.
*/

'pricing' => null,
];
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function up(): void

$table->string('method')->nullable()->index();
$table->string('status')->index();
$table->unsignedDecimal('cost', 16, 8)->nullable();
$table->unsignedDecimal('time_sec', 10, 4)->nullable();
$table->string('model_requested')->nullable();
$table->string('model_used')->nullable();
Expand Down
Binary file added resources/media/screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions resources/openai-pricing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"models": {
"gpt-4-0125-preview": {
"input": 0.01,
"output": 0.03
},
"gpt-4-1106-preview": {
"input": 0.01,
"output": 0.03
},
"gpt-4-1106-vision-preview": {
"input": 0.01,
"output": 0.03
},
"gpt-4": {
"input": 0.03,
"output": 0.06
},
"gpt-4-32k": {
"input": 0.06,
"output": 0.12
},
"gpt-3.5-turbo-0125": {
"input": 0.0005,
"output": 0.0015
},
"gpt-3.5-turbo-instruct": {
"input": 0.0015,
"output": 0.002
},
"gpt-3.5-turbo-1106": {
"input": 0.001,
"output": 0.002
},
"gpt-3.5-turbo-0613": {
"input": 0.0015,
"output": 0.002
},
"gpt-3.5-turbo-16k-0613": {
"input": 0.003,
"output": 0.004
},
"gpt-3.5-turbo-0301": {
"input": 0.0015,
"output": 0.002
}
},
"assistants": {
"code-interpreter": {
"input": 0.03
},
"retrieval": {
"input": 0.2
}
},
"fine-tuning": {
"gpt-3.5-turbo": {
"training": 0.008,
"input": 0.003,
"output": 0.006
},
"davinci-002": {
"training": 0.006,
"input": 0.012,
"output": 0.012
},
"babbage-002": {
"training": 0.0004,
"input": 0.0016,
"output": 0.0016
}
},
"embedding": {
"text-embedding-3-small": 0.00002,
"text-embedding-3-large": 0.00013,
"ada-v2": 0.0001
},
"base-models": {
"davinci-002": 0.002,
"babbage-002": 0.0004
},
"image": {
"dall-e-3": {
"standard-1024-1024": 0.04,
"standard-1024-1792": 0.08,
"standard-1792-1024": 0.08,
"hd-1024-1024": 0.08,
"hd-1024-1792": 0.12,
"hd-1792-1024": 0.12
},
"dall-e-2": {
"1024-1024": 0.02,
"512-512": 0.018,
"256-256": 0.016
}
},
"audio": {
"whisper": 0.006,
"tts": 0.015,
"tts-hd": 0.03
}
}
2 changes: 2 additions & 0 deletions src/Capabilities/Capability.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace Outl1ne\NovaOpenAI\Capabilities;

use Illuminate\Http\Client\PendingRequest;
use Outl1ne\NovaOpenAI\Pricing\Pricing;

class Capability
{
public function __construct(
protected readonly PendingRequest $http,
protected readonly Pricing $pricing,
) {
}
}
2 changes: 1 addition & 1 deletion src/Capabilities/Chat/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function create(
?array $tools = null,
null|string|array $toolChoice = null,
) {
return (new CreateChat($this->http))->makeRequest(
return (new CreateChat($this->http, $this->pricing))->makeRequest(
$model,
$messages,
$responseFormat,
Expand Down
6 changes: 5 additions & 1 deletion src/Capabilities/Chat/CreateChat.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
namespace Outl1ne\NovaOpenAI\Capabilities\Chat;

use Exception;
use Outl1ne\NovaOpenAI\Pricing\Pricing;
use Illuminate\Http\Client\PendingRequest;
use Outl1ne\NovaOpenAI\Models\OpenAIRequest;
use Outl1ne\NovaOpenAI\Capabilities\Measurable;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\Messages;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\ResponseFormat;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Responses\ChatResponse;
use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\ResponseFormat;
use Outl1ne\NovaOpenAI\Capabilities\Embeddings\Responses\EmbeddingsResponse;

class CreateChat
Expand All @@ -19,6 +20,7 @@ class CreateChat

public function __construct(
protected readonly PendingRequest $http,
protected readonly Pricing $pricing,
) {
$this->request = new OpenAIRequest;
$this->request->method = 'chat';
Expand Down Expand Up @@ -90,12 +92,14 @@ public function makeRequest(

protected function handleResponse(ChatResponse $response)
{
$this->request->cost = $this->pricing->models()->model($response->model)->calculate($response->usage->promptTokens, $response->usage->completionTokens);
$this->request->time_sec = $this->measure();
$this->request->status = 'success';
$this->request->meta = $response->meta;
$this->request->model_used = $response->model;
$this->request->output = $response->choices;
$this->request->usage_prompt_tokens = $response->usage->promptTokens;
$this->request->usage_completion_tokens = $response->usage->completionTokens;
$this->request->usage_total_tokens = $response->usage->totalTokens;
$this->request->save();

Expand Down
3 changes: 3 additions & 0 deletions src/Capabilities/Embeddings/CreateEmbedding.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Outl1ne\NovaOpenAI\Capabilities\Embeddings;

use Exception;
use Outl1ne\NovaOpenAI\Pricing\Pricing;
use Illuminate\Http\Client\PendingRequest;
use Outl1ne\NovaOpenAI\Models\OpenAIRequest;
use Outl1ne\NovaOpenAI\Capabilities\Measurable;
Expand All @@ -16,6 +17,7 @@ class CreateEmbedding

public function __construct(
protected readonly PendingRequest $http,
protected readonly Pricing $pricing,
) {
$this->request = new OpenAIRequest;
$this->request->method = 'embeddings';
Expand Down Expand Up @@ -58,6 +60,7 @@ public function makeRequest(string $model, string $input, ?string $encodingForma

protected function handleResponse(EmbeddingsResponse $response)
{
$this->request->cost = $this->pricing->embedding()->model($response->model)->calculate($response->usage->promptTokens);
$this->request->time_sec = $this->measure();
$this->request->status = 'success';
$this->request->meta = $response->meta;
Expand Down
2 changes: 1 addition & 1 deletion src/Capabilities/Embeddings/Embeddings.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ class Embeddings extends Capability
{
public function create(string $model, string $input, ?string $encodingFormat = null, ?int $dimensions = null, ?string $user = null)
{
return (new CreateEmbedding($this->http))->makeRequest($model, $input, $encodingFormat, $dimensions, $user);
return (new CreateEmbedding($this->http, $this->pricing))->makeRequest($model, $input, $encodingFormat, $dimensions, $user);
}
}
17 changes: 16 additions & 1 deletion src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class Factory
*/
protected array $headers = [];

/**
* The pricing configuration to calculate the cost of requests.
*/
protected ?object $pricing = null;

/**
* Sets the API key for the requests.
*/
Expand Down Expand Up @@ -82,6 +87,16 @@ public function withHttpHeaders(?array $headers): self
return $this;
}

/**
* Adds a pricing configuration.
*/
public function withPricing(object $pricing): self
{
$this->pricing = $pricing;

return $this;
}

/**
* Creates a new Open AI Client.
*/
Expand All @@ -99,6 +114,6 @@ public function make(): OpenAI

$baseUrl = rtrim($this->baseUrl ?: 'https://api.openai.com/v1', '/') . '/';

return new OpenAI($baseUrl, $headers);
return new OpenAI($baseUrl, $headers, $this->pricing);
}
}
56 changes: 56 additions & 0 deletions src/Nova/CostMetrics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Outl1ne\NovaOpenAI\Nova;

use Laravel\Nova\Nova;
use Laravel\Nova\Metrics\Trend;
use Laravel\Nova\Http\Requests\NovaRequest;
use Outl1ne\NovaOpenAI\Models\OpenAIRequest;

class CostMetrics extends Trend
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->sumByDays($request, OpenAIRequest::class, 'cost')->prefix('$');
}

/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
30 => Nova::__('30 Days'),
60 => Nova::__('60 Days'),
90 => Nova::__('90 Days'),
];
}

/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
return now()->addMinutes(1);
}

/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'cost-metrics';
}
}
16 changes: 15 additions & 1 deletion src/Nova/OpenAIRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Laravel\Nova\Fields\Code;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Badge;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\DateTime;
use Laravel\Nova\Http\Requests\NovaRequest;
use Outl1ne\NovaOpenAI\Enums\OpenAIRequestMethod;
Expand Down Expand Up @@ -45,6 +46,16 @@ public static function label()
return 'Requests';
}

/**
* Get the URI key for the dashboard.
*
* @return string
*/
public static function uriKey()
{
return 'openai-requests';
}

/**
* Get the fields displayed by the resource.
*
Expand All @@ -69,6 +80,7 @@ public function fields(NovaRequest $request)
OpenAIRequestMethod::EMBEDDINGS->value => 'bg-amber-600 text-amber-200',
OpenAIRequestMethod::AUDIO->value => 'bg-fuchsia-600 text-fuchsia-200',
])->sortable(),
Number::make('Cost', 'cost')->sortable()->displayUsing(fn ($value) => $value === null ? null : '$' . number_format($value, 4)),
Text::make('Request time', 'time_sec')->sortable()->displayUsing(fn () => "{$this->time_sec} sec"),
Text::make('Model requested', 'model_requested')->sortable(),
Text::make('Model used', 'model_used')->sortable(),
Expand All @@ -92,7 +104,9 @@ public function fields(NovaRequest $request)
*/
public function cards(NovaRequest $request)
{
return [];
return [
new CostMetrics,
];
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/NovaOpenAIServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ public function register()
$apiKey = config('nova-openai.api_key');
$organization = config('nova-openai.organization');
$headers = config('nova-openai.headers');
$pricingPath = config('nova-openai.pricing') ?? __DIR__ . '/../resources/openai-pricing.json';
$pricing = json_decode(file_get_contents($pricingPath));

if (!is_string($apiKey)) {
throw new ApiKeyMissingException;
Expand All @@ -90,6 +92,7 @@ public function register()
->withApiKey($apiKey)
->withOrganization($organization)
->withHttpHeaders($headers)
->withPricing($pricing)
->make();
});
$this->app->alias(OpenAI::class, 'nova-openai');
Expand Down
Loading

0 comments on commit 1a48bd0

Please sign in to comment.