Skip to content

Commit 8585916

Browse files
authored
refactor: Always return ModelInfo instance (#1)
1 parent 5635950 commit 8585916

15 files changed

Lines changed: 112 additions & 66 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
## Features
88

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

1313
## Requirements

src/Contracts/ModelInfoProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ interface ModelInfoProvider
1212
/**
1313
* Get the available models for a given provider.
1414
*
15-
* @return array<array-key, string>
15+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
1616
*/
1717
public function getModels(ModelProvider $modelProvider): array;
1818

src/Data/ModelInfo.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,37 @@
99
use Cortex\ModelInfo\Enums\ModelProvider;
1010

1111
/**
12-
* @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}
12+
* @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>}
1313
*/
1414
readonly class ModelInfo
1515
{
1616
/**
1717
* @param array<\Cortex\ModelInfo\Enums\ModelFeature> $features
18+
* @param array<string, mixed> $metadata
1819
*/
1920
public function __construct(
2021
public string $name,
2122
public ModelProvider $provider,
2223
public ModelType $type,
2324
public ?int $maxInputTokens,
2425
public ?int $maxOutputTokens,
25-
public float $inputCostPerToken,
26-
public float $outputCostPerToken,
26+
public ?float $inputCostPerToken,
27+
public ?float $outputCostPerToken,
2728
public array $features,
2829
public bool $isDeprecated = false,
30+
public array $metadata = [],
2931
) {}
3032

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

38+
public function getMetadata(string $key): mixed
39+
{
40+
return $this->metadata[$key] ?? null;
41+
}
42+
3643
/**
3744
* @param ModelInfoData $data
3845
*/
@@ -52,10 +59,11 @@ public static function createFromArray(array $data): self
5259
$type,
5360
$data['max_input_tokens'] ?? null,
5461
$data['max_output_tokens'] ?? null,
55-
$data['input_cost_per_token'] ?? 0.0,
56-
$data['output_cost_per_token'] ?? 0.0,
62+
$data['input_cost_per_token'] ?? null,
63+
$data['output_cost_per_token'] ?? null,
5764
$data['features'] ?? [],
5865
$data['is_deprecated'] ?? false,
66+
$data['metadata'] ?? [],
5967
);
6068
}
6169
}

src/Enums/ModelProvider.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Cortex\ModelInfo\Data\ModelInfo;
88
use Cortex\ModelInfo\ModelInfoFactory;
9+
use Psr\Container\ContainerExceptionInterface;
910
use Cortex\ModelInfo\Providers\Concerns\DiscoversPsrImplementations;
1011

1112
enum ModelProvider: string
@@ -29,7 +30,7 @@ enum ModelProvider: string
2930
/**
3031
* @param array<array-key, \Cortex\ModelInfo\Contracts\ModelInfoProvider>|null $modelInfoProviders
3132
*
32-
* @return array<array-key, string>
33+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
3334
*/
3435
public function models(?array $modelInfoProviders = null): array
3536
{
@@ -81,11 +82,13 @@ public static function modelInfoFactory(?array $modelInfoProviders = null): Mode
8182
{
8283
$container = self::discoverContainer();
8384

84-
if ($container?->has(ModelInfoFactory::class) === true) {
85-
// @phpstan-ignore return.type
86-
return $container->get(ModelInfoFactory::class);
85+
try {
86+
/** @var \Cortex\ModelInfo\ModelInfoFactory $factory */
87+
$factory = $container?->get(ModelInfoFactory::class);
88+
} catch (ContainerExceptionInterface) {
89+
//
8790
}
8891

89-
return new ModelInfoFactory($modelInfoProviders);
92+
return $factory ?? new ModelInfoFactory($modelInfoProviders);
9093
}
9194
}

src/Enums/ModelType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ enum ModelType: string
1313
case TextToSpeech = 'text_to_speech';
1414
case SpeechToText = 'speech_to_text';
1515
case Moderation = 'moderation';
16-
case Other = 'other';
16+
case Unknown = 'unknown';
1717
}

src/ModelInfoFactory.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Cortex\ModelInfo\Exceptions\ModelInfoException;
1212
use Cortex\ModelInfo\Providers\OllamaModelInfoProvider;
1313
use Cortex\ModelInfo\Providers\LiteLLMModelInfoProvider;
14+
use Cortex\ModelInfo\Providers\LMStudioModelInfoProvider;
1415
use Cortex\ModelInfo\Providers\Concerns\DiscoversPsrImplementations;
1516

1617
class ModelInfoFactory
@@ -36,7 +37,7 @@ public function __construct(
3637
}
3738

3839
/**
39-
* @return array<array-key, string>
40+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
4041
*/
4142
public function getModels(ModelProvider $modelProvider): array
4243
{
@@ -59,7 +60,7 @@ public function getModelInfo(ModelProvider $modelProvider, string $model): ?Mode
5960
/**
6061
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
6162
*
62-
* @return array<array-key, string>
63+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
6364
*/
6465
public function getModelsOrFail(ModelProvider $modelProvider): array
6566
{
@@ -123,6 +124,7 @@ protected static function defaultModelInfoProviders(): array
123124
{
124125
return [
125126
new OllamaModelInfoProvider(),
127+
new LMStudioModelInfoProvider(),
126128
new LiteLLMModelInfoProvider(),
127129
];
128130
}

src/Providers/CustomModelInfoProvider.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,9 @@ public function supportedModelProviders(): array
4040
return ModelProvider::cases();
4141
}
4242

43-
/**
44-
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
45-
*
46-
* @return array<array-key, string>
47-
*/
4843
public function getModels(ModelProvider $modelProvider): array
4944
{
50-
return array_map(
51-
fn(ModelInfo $model): string => $model->name,
52-
$this->models,
53-
);
45+
return $this->models;
5446
}
5547

5648
public function getModelInfo(ModelProvider $modelProvider, string $model): ModelInfo

src/Providers/LMStudioModelInfoProvider.php

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Cortex\ModelInfo\Providers\Concerns\MakesRequests;
1515

1616
/**
17-
* @phpstan-type ModelInfoResponse array{id: string, object: string, type: string, max_context_length: int, type: ?string}
17+
* @phpstan-type LMStudioModelInfoResponse array{id: string, object: string, type: string, max_context_length: int, type: ?string}
1818
*/
1919
class LMStudioModelInfoProvider implements ModelInfoProvider
2020
{
@@ -38,34 +38,38 @@ public function supportedModelProviders(): array
3838
/**
3939
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
4040
*
41-
* @return array<array-key, string>
41+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
4242
*/
4343
public function getModels(ModelProvider $modelProvider): array
4444
{
4545
$this->checkSupportOrFail($modelProvider);
4646

4747
$body = $this->getModelsResponse();
4848

49-
$models = array_map(
50-
// @phpstan-ignore return.type,argument.type
51-
fn(array $model): string => $model['id'],
49+
return array_values(array_map(
50+
fn(array $model): ModelInfo => self::mapModelInfo($model),
5251
$body['data'],
53-
);
54-
55-
return array_values($models);
52+
));
5653
}
5754

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

62-
$body = $this->getModelInfoResponse($model);
63-
$type = $body['type'] ?? '';
59+
return self::mapModelInfo(
60+
$this->getModelInfoResponse($model),
61+
);
62+
}
6463

64+
/**
65+
* @param LMStudioModelInfoResponse $body
66+
*/
67+
protected static function mapModelInfo(array $body): ModelInfo
68+
{
6569
return new ModelInfo(
66-
name: $model,
70+
name: $body['id'],
6771
provider: ModelProvider::LMStudio,
68-
type: self::getModelType($type),
72+
type: self::getModelType($body['type'] ?? ''),
6973
maxInputTokens: self::getMaxInputTokens($body['max_context_length']),
7074
maxOutputTokens: null,
7175
inputCostPerToken: 0.0,
@@ -75,7 +79,7 @@ public function getModelInfo(ModelProvider $modelProvider, string $model): Model
7579
}
7680

7781
/**
78-
* @param ModelInfoResponse $body
82+
* @param LMStudioModelInfoResponse $body
7983
*
8084
* @return array<array-key, \Cortex\ModelInfo\Enums\ModelFeature>
8185
*/
@@ -96,7 +100,7 @@ protected static function getModelType(string $type): ModelType
96100
return match ($type) {
97101
'llm' => ModelType::Chat,
98102
'embeddings' => ModelType::Embedding,
99-
default => ModelType::Other,
103+
default => ModelType::Unknown,
100104
};
101105
}
102106

@@ -108,7 +112,7 @@ protected static function getMaxInputTokens(int $maxContextLength): ?int
108112
/**
109113
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
110114
*
111-
* @return ModelInfoResponse
115+
* @return LMStudioModelInfoResponse
112116
*/
113117
protected function getModelInfoResponse(string $model): array
114118
{
@@ -127,7 +131,7 @@ protected function getModelInfoResponse(string $model): array
127131
/**
128132
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
129133
*
130-
* @return array{data: array{id: string}}
134+
* @return array{data: array<array-key, LMStudioModelInfoResponse>}
131135
*/
132136
protected function getModelsResponse(): array
133137
{

src/Providers/LiteLLMModelInfoProvider.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public function supportedModelProviders(): array
4848
/**
4949
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
5050
*
51-
* @return array<array-key, string>
51+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
5252
*/
5353
public function getModels(ModelProvider $modelProvider): array
5454
{
@@ -61,7 +61,11 @@ public function getModels(ModelProvider $modelProvider): array
6161
fn(array $model): bool => $model['litellm_provider'] === $modelProvider->value,
6262
);
6363

64-
return array_keys($models);
64+
return array_values(array_map(
65+
fn(array $modelInfo, string $model): ModelInfo => self::mapModelInfo($modelProvider, $model, $modelInfo),
66+
$models,
67+
array_keys($models),
68+
));
6569
}
6670

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

97+
return self::mapModelInfo($modelProvider, $model, $modelInfo);
98+
}
99+
100+
protected static function mapModelInfo(
101+
ModelProvider $modelProvider,
102+
string $model,
103+
array $modelInfo,
104+
): ModelInfo {
93105
return new ModelInfo(
94106
name: $model,
95107
provider: $modelProvider,
@@ -162,7 +174,7 @@ protected static function mapModelType(string $type): ModelType
162174
'audio_speech' => ModelType::TextToSpeech,
163175
'audio_transcription' => ModelType::SpeechToText,
164176
'moderation' => ModelType::Moderation,
165-
default => ModelType::Other,
177+
default => ModelType::Unknown,
166178
};
167179
}
168180

src/Providers/OllamaModelInfoProvider.php

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
use Cortex\ModelInfo\Providers\Concerns\ChecksSupport;
1414
use Cortex\ModelInfo\Providers\Concerns\MakesRequests;
1515

16+
/**
17+
* @phpstan-type OllamaModelsResponse array{name: string}
18+
* @phpstan-type OllamaModelInfoResponse array{name: string, model_info: array<string, mixed>|null, capabilities: array<array-key, string>|null}
19+
*/
1620
class OllamaModelInfoProvider implements ModelInfoProvider
1721
{
1822
use ChecksSupport;
@@ -35,31 +39,42 @@ public function supportedModelProviders(): array
3539
/**
3640
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
3741
*
38-
* @return array<array-key, string>
42+
* @return array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
3943
*/
4044
public function getModels(ModelProvider $modelProvider): array
4145
{
4246
$this->checkSupportOrFail($modelProvider);
4347

4448
$body = $this->getModelsResponse();
4549

46-
return array_map(
47-
// @phpstan-ignore return.type,argument.type
48-
fn(array $model): string => $model['name'],
50+
return array_values(array_map(
51+
fn(array $model): ModelInfo => self::mapModelInfo($model),
4952
$body['models'],
50-
);
53+
));
5154
}
5255

56+
/**
57+
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
58+
*/
5359
public function getModelInfo(ModelProvider $modelProvider, string $model): ModelInfo
5460
{
5561
$this->checkSupportOrFail($modelProvider);
5662

57-
$body = $this->getModelInfoResponse($model);
58-
$modelInfo = $body['model_info'] ?? [];
59-
$capabilities = $body['capabilities'] ?? [];
63+
return self::mapModelInfo(
64+
$this->getModelInfoResponse($model),
65+
);
66+
}
67+
68+
/**
69+
* @param OllamaModelInfoResponse|OllamaModelsResponse $modelResponseBody
70+
*/
71+
protected static function mapModelInfo(array $modelResponseBody): ModelInfo
72+
{
73+
$modelInfo = $modelResponseBody['model_info'] ?? [];
74+
$capabilities = $modelResponseBody['capabilities'] ?? [];
6075

6176
return new ModelInfo(
62-
name: $model,
77+
name: $modelResponseBody['name'],
6378
provider: ModelProvider::Ollama,
6479
type: self::getModelType($capabilities),
6580
maxInputTokens: self::getMaxInputTokens($modelInfo),
@@ -104,7 +119,7 @@ protected static function getModelType(array $capabilities): ModelType
104119
return match (true) {
105120
in_array('completion', $capabilities, true) => ModelType::Chat,
106121
in_array('embedding', $capabilities, true) => ModelType::Embedding,
107-
default => ModelType::Other,
122+
default => ModelType::Unknown,
108123
};
109124
}
110125

@@ -135,7 +150,7 @@ protected static function getMaxInputTokens(array $modelInfo): ?int
135150
/**
136151
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
137152
*
138-
* @return array{model_info: array<string, mixed>|null, capabilities: array<array-key, string>|null}
153+
* @return OllamaModelInfoResponse
139154
*/
140155
protected function getModelInfoResponse(string $model): array
141156
{
@@ -154,7 +169,7 @@ protected function getModelInfoResponse(string $model): array
154169
/**
155170
* @throws \Cortex\ModelInfo\Exceptions\ModelInfoException
156171
*
157-
* @return array{models: array{name: string}}
172+
* @return array{models: array<array-key, OllamaModelsResponse>}
158173
*/
159174
protected function getModelsResponse(): array
160175
{

0 commit comments

Comments
 (0)