Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
## Features

- 🤖 **Model Providers** - Get detailed model information with type-safe responses from various model providers (OpenAI, Ollama, etc.)
- 💾 **Simple Cache** - PSR-16 Simple Cache support for caching model information
- 💾 **Caching** - PSR-16 Simple Cache support for caching model information
- 🔌 **Extensibility** - Easily add support for additional model providers

## Requirements
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/ModelInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ModelInfoProvider
/**
* Get the available models for a given provider.
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModels(ModelProvider $modelProvider): array;

Expand Down
18 changes: 13 additions & 5 deletions src/Data/ModelInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,37 @@
use Cortex\ModelInfo\Enums\ModelProvider;

/**
* @phpstan-type ModelInfoData array{name: string, provider: string|ModelProvider, type: string|ModelType, max_input_tokens: int|null, max_output_tokens: int|null, input_cost_per_token?: float, output_cost_per_token?: float, features?: array<array-key, ModelFeature>, is_deprecated?: bool}
* @phpstan-type ModelInfoData array{name: string, provider: string|ModelProvider, type: string|ModelType, max_input_tokens: int|null, max_output_tokens: int|null, input_cost_per_token?: float, output_cost_per_token?: float, features?: array<array-key, ModelFeature>, is_deprecated?: bool, metadata?: array<string, mixed>}
*/
readonly class ModelInfo
{
/**
* @param array<\Cortex\ModelInfo\Enums\ModelFeature> $features
* @param array<string, mixed> $metadata
*/
public function __construct(
public string $name,
public ModelProvider $provider,
public ModelType $type,
public ?int $maxInputTokens,
public ?int $maxOutputTokens,
public float $inputCostPerToken,
public float $outputCostPerToken,
public ?float $inputCostPerToken,
public ?float $outputCostPerToken,
public array $features,
public bool $isDeprecated = false,
public array $metadata = [],
) {}

public function supportsFeature(ModelFeature $modelFeature): bool
{
return in_array($modelFeature, $this->features, true);
}

public function getMetadata(string $key): mixed
{
return $this->metadata[$key] ?? null;
}

/**
* @param ModelInfoData $data
*/
Expand All @@ -52,10 +59,11 @@ public static function createFromArray(array $data): self
$type,
$data['max_input_tokens'] ?? null,
$data['max_output_tokens'] ?? null,
$data['input_cost_per_token'] ?? 0.0,
$data['output_cost_per_token'] ?? 0.0,
$data['input_cost_per_token'] ?? null,
$data['output_cost_per_token'] ?? null,
$data['features'] ?? [],
$data['is_deprecated'] ?? false,
$data['metadata'] ?? [],
);
}
}
13 changes: 8 additions & 5 deletions src/Enums/ModelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Cortex\ModelInfo\Data\ModelInfo;
use Cortex\ModelInfo\ModelInfoFactory;
use Psr\Container\ContainerExceptionInterface;
use Cortex\ModelInfo\Providers\Concerns\DiscoversPsrImplementations;

enum ModelProvider: string
Expand All @@ -29,7 +30,7 @@ enum ModelProvider: string
/**
* @param array<array-key, \Cortex\ModelInfo\Contracts\ModelInfoProvider>|null $modelInfoProviders
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function models(?array $modelInfoProviders = null): array
{
Expand Down Expand Up @@ -81,11 +82,13 @@ public static function modelInfoFactory(?array $modelInfoProviders = null): Mode
{
$container = self::discoverContainer();

if ($container?->has(ModelInfoFactory::class) === true) {
// @phpstan-ignore return.type
return $container->get(ModelInfoFactory::class);
try {
/** @var \Cortex\ModelInfo\ModelInfoFactory $factory */
$factory = $container?->get(ModelInfoFactory::class);
} catch (ContainerExceptionInterface) {
//
}

return new ModelInfoFactory($modelInfoProviders);
return $factory ?? new ModelInfoFactory($modelInfoProviders);
}
}
2 changes: 1 addition & 1 deletion src/Enums/ModelType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ enum ModelType: string
case TextToSpeech = 'text_to_speech';
case SpeechToText = 'speech_to_text';
case Moderation = 'moderation';
case Other = 'other';
case Unknown = 'unknown';
}
6 changes: 4 additions & 2 deletions src/ModelInfoFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Cortex\ModelInfo\Exceptions\ModelInfoException;
use Cortex\ModelInfo\Providers\OllamaModelInfoProvider;
use Cortex\ModelInfo\Providers\LiteLLMModelInfoProvider;
use Cortex\ModelInfo\Providers\LMStudioModelInfoProvider;
use Cortex\ModelInfo\Providers\Concerns\DiscoversPsrImplementations;

class ModelInfoFactory
Expand All @@ -36,7 +37,7 @@ public function __construct(
}

/**
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModels(ModelProvider $modelProvider): array
{
Expand All @@ -59,7 +60,7 @@ public function getModelInfo(ModelProvider $modelProvider, string $model): ?Mode
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModelsOrFail(ModelProvider $modelProvider): array
{
Expand Down Expand Up @@ -123,6 +124,7 @@ protected static function defaultModelInfoProviders(): array
{
return [
new OllamaModelInfoProvider(),
new LMStudioModelInfoProvider(),
new LiteLLMModelInfoProvider(),
];
}
Expand Down
10 changes: 1 addition & 9 deletions src/Providers/CustomModelInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,9 @@ public function supportedModelProviders(): array
return ModelProvider::cases();
}

/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array<array-key, string>
*/
public function getModels(ModelProvider $modelProvider): array
{
return array_map(
fn(ModelInfo $model): string => $model->name,
$this->models,
);
return $this->models;
}

public function getModelInfo(ModelProvider $modelProvider, string $model): ModelInfo
Expand Down
36 changes: 20 additions & 16 deletions src/Providers/LMStudioModelInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Cortex\ModelInfo\Providers\Concerns\MakesRequests;

/**
* @phpstan-type ModelInfoResponse array{id: string, object: string, type: string, max_context_length: int, type: ?string}
* @phpstan-type LMStudioModelInfoResponse array{id: string, object: string, type: string, max_context_length: int, type: ?string}
*/
class LMStudioModelInfoProvider implements ModelInfoProvider
{
Expand All @@ -38,34 +38,38 @@ public function supportedModelProviders(): array
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModels(ModelProvider $modelProvider): array
{
$this->checkSupportOrFail($modelProvider);

$body = $this->getModelsResponse();

$models = array_map(
// @phpstan-ignore return.type,argument.type
fn(array $model): string => $model['id'],
return array_values(array_map(
fn(array $model): ModelInfo => self::mapModelInfo($model),
$body['data'],
);

return array_values($models);
));
}

public function getModelInfo(ModelProvider $modelProvider, string $model): ModelInfo
{
$this->checkSupportOrFail($modelProvider);

$body = $this->getModelInfoResponse($model);
$type = $body['type'] ?? '';
return self::mapModelInfo(
$this->getModelInfoResponse($model),
);
}

/**
* @param LMStudioModelInfoResponse $body
*/
protected static function mapModelInfo(array $body): ModelInfo
{
return new ModelInfo(
name: $model,
name: $body['id'],
provider: ModelProvider::LMStudio,
type: self::getModelType($type),
type: self::getModelType($body['type'] ?? ''),
maxInputTokens: self::getMaxInputTokens($body['max_context_length']),
maxOutputTokens: null,
inputCostPerToken: 0.0,
Expand All @@ -75,7 +79,7 @@ public function getModelInfo(ModelProvider $modelProvider, string $model): Model
}

/**
* @param ModelInfoResponse $body
* @param LMStudioModelInfoResponse $body
*
* @return array<array-key, \Cortex\ModelInfo\Enums\ModelFeature>
*/
Expand All @@ -96,7 +100,7 @@ protected static function getModelType(string $type): ModelType
return match ($type) {
'llm' => ModelType::Chat,
'embeddings' => ModelType::Embedding,
default => ModelType::Other,
default => ModelType::Unknown,
};
}

Expand All @@ -108,7 +112,7 @@ protected static function getMaxInputTokens(int $maxContextLength): ?int
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return ModelInfoResponse
* @return LMStudioModelInfoResponse
*/
protected function getModelInfoResponse(string $model): array
{
Expand All @@ -127,7 +131,7 @@ protected function getModelInfoResponse(string $model): array
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array{data: array{id: string}}
* @return array{data: array<array-key, LMStudioModelInfoResponse>}
*/
protected function getModelsResponse(): array
{
Expand Down
18 changes: 15 additions & 3 deletions src/Providers/LiteLLMModelInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function supportedModelProviders(): array
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModels(ModelProvider $modelProvider): array
{
Expand All @@ -61,7 +61,11 @@ public function getModels(ModelProvider $modelProvider): array
fn(array $model): bool => $model['litellm_provider'] === $modelProvider->value,
);

return array_keys($models);
return array_values(array_map(
fn(array $modelInfo, string $model): ModelInfo => self::mapModelInfo($modelProvider, $model, $modelInfo),
$models,
array_keys($models),
));
}

/**
Expand Down Expand Up @@ -90,6 +94,14 @@ public function getModelInfo(ModelProvider $modelProvider, string $model): Model
throw new ModelInfoException('Model not found');
}

return self::mapModelInfo($modelProvider, $model, $modelInfo);
}

protected static function mapModelInfo(
ModelProvider $modelProvider,
string $model,
array $modelInfo,
): ModelInfo {
return new ModelInfo(
name: $model,
provider: $modelProvider,
Expand Down Expand Up @@ -162,7 +174,7 @@ protected static function mapModelType(string $type): ModelType
'audio_speech' => ModelType::TextToSpeech,
'audio_transcription' => ModelType::SpeechToText,
'moderation' => ModelType::Moderation,
default => ModelType::Other,
default => ModelType::Unknown,
};
}

Expand Down
39 changes: 27 additions & 12 deletions src/Providers/OllamaModelInfoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
use Cortex\ModelInfo\Providers\Concerns\ChecksSupport;
use Cortex\ModelInfo\Providers\Concerns\MakesRequests;

/**
* @phpstan-type OllamaModelsResponse array{name: string}
* @phpstan-type OllamaModelInfoResponse array{name: string, model_info: array<string, mixed>|null, capabilities: array<array-key, string>|null}
*/
class OllamaModelInfoProvider implements ModelInfoProvider
{
use ChecksSupport;
Expand All @@ -35,31 +39,42 @@ public function supportedModelProviders(): array
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array<array-key, string>
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
*/
public function getModels(ModelProvider $modelProvider): array
{
$this->checkSupportOrFail($modelProvider);

$body = $this->getModelsResponse();

return array_map(
// @phpstan-ignore return.type,argument.type
fn(array $model): string => $model['name'],
return array_values(array_map(
fn(array $model): ModelInfo => self::mapModelInfo($model),
$body['models'],
);
));
}

/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*/
public function getModelInfo(ModelProvider $modelProvider, string $model): ModelInfo
{
$this->checkSupportOrFail($modelProvider);

$body = $this->getModelInfoResponse($model);
$modelInfo = $body['model_info'] ?? [];
$capabilities = $body['capabilities'] ?? [];
return self::mapModelInfo(
$this->getModelInfoResponse($model),
);
}

/**
* @param OllamaModelInfoResponse|OllamaModelsResponse $modelResponseBody
*/
protected static function mapModelInfo(array $modelResponseBody): ModelInfo
{
$modelInfo = $modelResponseBody['model_info'] ?? [];
$capabilities = $modelResponseBody['capabilities'] ?? [];

return new ModelInfo(
name: $model,
name: $modelResponseBody['name'],
provider: ModelProvider::Ollama,
type: self::getModelType($capabilities),
maxInputTokens: self::getMaxInputTokens($modelInfo),
Expand Down Expand Up @@ -104,7 +119,7 @@ protected static function getModelType(array $capabilities): ModelType
return match (true) {
in_array('completion', $capabilities, true) => ModelType::Chat,
in_array('embedding', $capabilities, true) => ModelType::Embedding,
default => ModelType::Other,
default => ModelType::Unknown,
};
}

Expand Down Expand Up @@ -135,7 +150,7 @@ protected static function getMaxInputTokens(array $modelInfo): ?int
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array{model_info: array<string, mixed>|null, capabilities: array<array-key, string>|null}
* @return OllamaModelInfoResponse
*/
protected function getModelInfoResponse(string $model): array
{
Expand All @@ -154,7 +169,7 @@ protected function getModelInfoResponse(string $model): array
/**
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
*
* @return array{models: array{name: string}}
* @return array{models: array<array-key, OllamaModelsResponse>}
*/
protected function getModelsResponse(): array
{
Expand Down
Loading
Loading