Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/Fim/PendingRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace Prism\Prism\Fim;

use Illuminate\Http\Client\RequestException;
use Prism\Prism\Concerns\ConfiguresClient;
use Prism\Prism\Concerns\ConfiguresModels;
use Prism\Prism\Concerns\ConfiguresProviders;
use Prism\Prism\Concerns\HasProviderOptions;

class PendingRequest
{
use ConfiguresClient;
use ConfiguresModels;
use ConfiguresProviders;
use HasProviderOptions;

protected string $prompt = '';

protected ?string $suffix = null;

protected ?int $maxTokens = null;

protected int|float|null $temperature = null;

protected int|float|null $topP = null;

/** @var array<string> */
protected array $stop = [];

public function withPrompt(string $prompt): self
{
$this->prompt = $prompt;

return $this;
}

public function withSuffix(?string $suffix): self
{
$this->suffix = $suffix;

return $this;
}

public function withMaxTokens(int $maxTokens): self
{
$this->maxTokens = $maxTokens;

return $this;
}

public function withTemperature(int|float $temperature): self
{
$this->temperature = $temperature;

return $this;
}

public function withTopP(int|float $topP): self
{
$this->topP = $topP;

return $this;
}

/**
* @param string|array<string> $stop
*/
public function withStop(string|array $stop): self
{
$this->stop = is_string($stop) ? [$stop] : $stop;

return $this;
}

public function asText(): Response
{
$request = $this->toRequest();

try {
return $this->provider->fim($request);
} catch (RequestException $e) {
$this->provider->handleRequestException($request->model(), $e);
}
}

/**
* @deprecated Use `asText` instead.
*/
public function generate(): Response
{
return $this->asText();
}

public function toRequest(): Request
{
return new Request(
model: $this->model,
providerKey: $this->providerKey(),
prompt: $this->prompt,
suffix: $this->suffix,
maxTokens: $this->maxTokens,
temperature: $this->temperature,
topP: $this->topP,
stop: $this->stop,
clientOptions: $this->clientOptions,
clientRetry: $this->clientRetry,
providerOptions: $this->providerOptions,
);
}
}
95 changes: 95 additions & 0 deletions src/Fim/Request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Prism\Prism\Fim;

use Prism\Prism\Concerns\ChecksSelf;
use Prism\Prism\Concerns\HasProviderOptions;
use Prism\Prism\Contracts\PrismRequest;

class Request implements PrismRequest
{
use ChecksSelf, HasProviderOptions;

/**
* @param array<string> $stop
* @param array<string, mixed> $clientOptions
* @param array<mixed> $clientRetry
* @param array<string, mixed> $providerOptions
*/
public function __construct(
protected string $model,
protected string $providerKey,
protected string $prompt,
protected ?string $suffix,
protected ?int $maxTokens,
protected int|float|null $temperature,
protected int|float|null $topP,
protected array $stop,
protected array $clientOptions,
protected array $clientRetry,
array $providerOptions = [],
) {
$this->providerOptions = $providerOptions;
}

public function model(): string
{
return $this->model;
}

public function provider(): string
{
return $this->providerKey;
}

public function prompt(): string
{
return $this->prompt;
}

public function suffix(): ?string
{
return $this->suffix;
}

public function maxTokens(): ?int
{
return $this->maxTokens;
}

public function temperature(): int|float|null
{
return $this->temperature;
}

public function topP(): int|float|null
{
return $this->topP;
}

/**
* @return array<string>
*/
public function stop(): array
{
return $this->stop;
}

/**
* @return array<string, mixed>
*/
public function clientOptions(): array
{
return $this->clientOptions;
}

/**
* @return array<mixed>
*/
public function clientRetry(): array
{
return $this->clientRetry;
}
}
37 changes: 37 additions & 0 deletions src/Fim/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Prism\Prism\Fim;

use Illuminate\Contracts\Support\Arrayable;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\ValueObjects\Meta;
use Prism\Prism\ValueObjects\Usage;

/**
* @implements Arrayable<string, mixed>
*/
readonly class Response implements Arrayable
{
public function __construct(
public string $text,
public FinishReason $finishReason,
public Usage $usage,
public Meta $meta,
) {}

/**
* @return array<string, mixed>
*/
#[\Override]
public function toArray(): array
{
return [
'text' => $this->text,
'finish_reason' => $this->finishReason->value,
'usage' => $this->usage->toArray(),
'meta' => $this->meta->toArray(),
];
}
}
6 changes: 6 additions & 0 deletions src/Prism.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Support\Traits\Macroable;
use Prism\Prism\Audio\PendingRequest as PendingAudioRequest;
use Prism\Prism\Embeddings\PendingRequest as PendingEmbeddingRequest;
use Prism\Prism\Fim\PendingRequest as PendingFimRequest;
use Prism\Prism\Images\PendingRequest as PendingImageRequest;
use Prism\Prism\Moderation\PendingRequest as PendingModerationRequest;
use Prism\Prism\Structured\PendingRequest as PendingStructuredRequest;
Expand Down Expand Up @@ -45,4 +46,9 @@ public function moderation(): PendingModerationRequest
{
return new PendingModerationRequest;
}

public function fim(): PendingFimRequest
{
return new PendingFimRequest;
}
}
79 changes: 79 additions & 0 deletions src/Providers/Mistral/Handlers/Fim.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Prism\Prism\Providers\Mistral\Handlers;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response as ClientResponse;
use Illuminate\Support\Arr;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\Fim\Request;
use Prism\Prism\Fim\Response;
use Prism\Prism\Providers\Mistral\Concerns\ProcessRateLimits;
use Prism\Prism\Providers\Mistral\Concerns\ValidatesResponse;
use Prism\Prism\ValueObjects\Meta;
use Prism\Prism\ValueObjects\Usage;

class Fim
{
use ProcessRateLimits;
use ValidatesResponse;

public function __construct(protected PendingRequest $client) {}

public function handle(Request $request): Response
{
$response = $this->sendRequest($request);

$this->validateResponse($response);

$data = $response->json();

return new Response(
text: data_get($data, 'choices.0.message.content', ''),
finishReason: $this->mapFinishReason($data),
usage: new Usage(
data_get($data, 'usage.prompt_tokens', 0),
data_get($data, 'usage.completion_tokens', 0),
),
meta: new Meta(
id: data_get($data, 'id', ''),
model: data_get($data, 'model', ''),
rateLimits: $this->processRateLimits($response),
)
);
}

protected function sendRequest(Request $request): ClientResponse
{
/** @var ClientResponse $response */
$response = $this->client->post(
'fim/completions',
array_merge([
'model' => $request->model(),
'prompt' => $request->prompt(),
], Arr::whereNotNull([
'suffix' => $request->suffix(),
'max_tokens' => $request->maxTokens(),
'temperature' => $request->temperature(),
'top_p' => $request->topP(),
'stop' => empty($request->stop()) ? null : $request->stop(),
]))
);

return $response;
}

/**
* @param array<string, mixed> $data
*/
protected function mapFinishReason(array $data): FinishReason
{
return match (data_get($data, 'choices.0.finish_reason')) {
'stop' => FinishReason::Stop,
'length' => FinishReason::Length,
default => FinishReason::Unknown,
};
}
}
16 changes: 16 additions & 0 deletions src/Providers/Mistral/Mistral.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
use Prism\Prism\Exceptions\PrismProviderOverloadedException;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Prism\Exceptions\PrismRequestTooLargeException;
use Prism\Prism\Fim\Request as FimRequest;
use Prism\Prism\Fim\Response as FimResponse;
use Prism\Prism\Providers\Mistral\Concerns\ProcessRateLimits;
use Prism\Prism\Providers\Mistral\Handlers\Audio;
use Prism\Prism\Providers\Mistral\Handlers\Embeddings;
use Prism\Prism\Providers\Mistral\Handlers\Fim;
use Prism\Prism\Providers\Mistral\Handlers\OCR;
use Prism\Prism\Providers\Mistral\Handlers\Stream;
use Prism\Prism\Providers\Mistral\Handlers\Structured;
Expand Down Expand Up @@ -66,6 +69,19 @@ public function structured(StructuredRequest $request): StructuredResponse
return $handler->handle($request);
}

#[\Override]
public function fim(FimRequest $request): FimResponse
{
$handler = new Fim(
$this->client(
$request->clientOptions(),
$request->clientRetry()
)
);

return $handler->handle($request);
}

#[\Override]
public function embeddings(EmbeddingRequest $request): EmbeddingResponse
{
Expand Down
7 changes: 7 additions & 0 deletions src/Providers/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Prism\Prism\Exceptions\PrismProviderOverloadedException;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Prism\Exceptions\PrismRequestTooLargeException;
use Prism\Prism\Fim\Request as FimRequest;
use Prism\Prism\Fim\Response as FimResponse;
use Prism\Prism\Images\Request as ImagesRequest;
use Prism\Prism\Images\Response as ImagesResponse;
use Prism\Prism\Moderation\Request as ModerationRequest;
Expand Down Expand Up @@ -63,6 +65,11 @@ public function speechToText(SpeechToTextRequest $request): SpeechToTextResponse
throw PrismException::unsupportedProviderAction('speechToText', class_basename($this));
}

public function fim(FimRequest $request): FimResponse
{
throw PrismException::unsupportedProviderAction('fim', class_basename($this));
}

/**
* @return Generator<StreamEvent>
*/
Expand Down
Loading