Skip to content

Commit 8f034b3

Browse files
committed
chore: Add test and update readme
1 parent 422f067 commit 8f034b3

File tree

4 files changed

+153
-19
lines changed

4 files changed

+153
-19
lines changed

README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@
66

77
## Features
88

9-
- Get model information from various AI providers (Ollama, LiteLLM)
10-
- PSR-16 Simple Cache support for caching model information
11-
- Retrieve available models for supported providers
12-
- Get detailed model information for specific models
13-
- Extensible provider system
9+
- 🤖 **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
11+
- 🔌 **Extensibility** - Easily add support for additional model providers
1412

1513
## Requirements
1614

@@ -33,24 +31,37 @@ $factory = new ModelInfoFactory();
3331

3432
// Get all available models for a provider
3533
$models = $factory->getModels(ModelProvider::Ollama);
34+
// ['llama3.1', 'llama3.1:8b', 'llama3.1:70b']
3635

3736
// Get information about a specific model
3837
$modelInfo = $factory->getModelInfo(ModelProvider::Ollama, 'llama3.1');
3938

39+
// Accessing model information properties
40+
echo $modelInfo->name; // 'llama3.1'
41+
echo $modelInfo->provider; // ModelProvider::Ollama
42+
echo $modelInfo->type; // ModelType::Chat
43+
echo $modelInfo->maxInputTokens; // 8000
44+
echo $modelInfo->maxOutputTokens; // null
45+
echo $modelInfo->inputCostPerToken; // 0.0
46+
echo $modelInfo->outputCostPerToken; // 0.0
47+
echo $modelInfo->isDeprecated; // false
48+
echo $modelInfo->features; // [ModelFeature::ToolCalling, ModelFeature::JsonOutput, etc]
49+
50+
// Check if model supports specific features
51+
if ($modelInfo->supportsFeature(ModelFeature::ToolCalling)) {
52+
// Use tool calling feature
53+
}
4054
```
4155

4256
```php
43-
use Psr\SimpleCache\CacheInterface;
44-
45-
// Using with custom cache implementation
57+
// Using with custom PSR-16 cache implementation
4658
$factory = new ModelInfoFactory(
47-
cache: $yourPsr16CacheImplementation
59+
cache: $yourPsr16CacheImplementation // `Psr\SimpleCache\CacheInterface`
4860
);
4961

50-
// Force fetch (bypass cache) with exception handling
62+
// With exception handling
5163
try {
52-
$models = $factory->getModelsOrFail(ModelProvider::LiteLLM);
53-
$modelInfo = $factory->getModelInfoOrFail(ModelProvider::LiteLLM, 'gpt-4');
64+
$modelInfo = $factory->getModelInfoOrFail(ModelProvider::OpenAI, 'gpt-4o');
5465
} catch (ModelInfoException $e) {
5566
// Handle exception
5667
}

src/Data/ModelInfo.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Cortex\ModelInfo\Enums\ModelFeature;
99
use Cortex\ModelInfo\Enums\ModelProvider;
1010

11+
/**
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}
13+
*/
1114
readonly class ModelInfo
1215
{
1316
/**
@@ -31,7 +34,7 @@ public function supportsFeature(ModelFeature $modelFeature): bool
3134
}
3235

3336
/**
34-
* @param 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} $data
37+
* @param ModelInfoData $data
3538
*/
3639
public static function createFromArray(array $data): self
3740
{
@@ -49,8 +52,8 @@ public static function createFromArray(array $data): self
4952
$type,
5053
$data['max_input_tokens'] ?? null,
5154
$data['max_output_tokens'] ?? null,
52-
$data['input_cost_per_token'],
53-
$data['output_cost_per_token'],
55+
$data['input_cost_per_token'] ?? 0.0,
56+
$data['output_cost_per_token'] ?? 0.0,
5457
$data['features'] ?? [],
5558
$data['is_deprecated'] ?? false,
5659
);

src/Providers/CustomModelInfoProvider.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,30 @@
1010
use Cortex\ModelInfo\Exceptions\ModelInfoException;
1111
use Cortex\ModelInfo\Providers\Concerns\ChecksSupport;
1212

13+
/**
14+
* @phpstan-import-type ModelInfoData from \Cortex\ModelInfo\Data\ModelInfo
15+
*/
1316
class CustomModelInfoProvider implements ModelInfoProvider
1417
{
1518
use ChecksSupport;
1619

1720
/**
18-
* @param array<array-key, \Cortex\ModelInfo\Data\ModelInfo> $models
21+
* @var array<array-key, \Cortex\ModelInfo\Data\ModelInfo>
1922
*/
20-
public function __construct(
21-
protected array $models,
22-
) {}
23+
protected array $models;
24+
25+
/**
26+
* @param array<array-key, \Cortex\ModelInfo\Data\ModelInfo|ModelInfoData> $models
27+
*/
28+
public function __construct(array $models)
29+
{
30+
$this->models = array_map(
31+
fn(ModelInfo|array $model): ModelInfo => $model instanceof ModelInfo
32+
? $model
33+
: ModelInfo::createFromArray($model),
34+
$models,
35+
);
36+
}
2337

2438
public function supportedModelProviders(): array
2539
{
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cortex\ModelInfo\Tests\Unit;
6+
7+
use GuzzleHttp\Psr7\Response;
8+
use Cortex\ModelInfo\Data\ModelInfo;
9+
use Cortex\ModelInfo\Enums\ModelType;
10+
use Cortex\ModelInfo\ModelInfoFactory;
11+
use Cortex\ModelInfo\Enums\ModelFeature;
12+
use Cortex\ModelInfo\Enums\ModelProvider;
13+
use Cortex\ModelInfo\Providers\CustomModelInfoProvider;
14+
use Cortex\ModelInfo\Providers\OllamaModelInfoProvider;
15+
16+
covers(ModelInfoFactory::class);
17+
18+
test('it can get the available models', function (): void {
19+
$client = $this->mockHttpClient(
20+
new Response(body: json_encode([
21+
'models' => [
22+
[
23+
'name' => 'gemma3:12b',
24+
'model' => 'gemma3:12b',
25+
'modified_at' => '2025-03-19T08:07:16.962438556Z',
26+
'size' => 8149190199,
27+
'digest' => '6fd036cefda5093cc827b6c16be5e447f23857d4a472ce0bdba0720573d4dcd9',
28+
'details' => [
29+
'parent_model' => '',
30+
'format' => 'gguf',
31+
'family' => 'gemma3',
32+
'families' => [
33+
'gemma3',
34+
],
35+
'parameter_size' => '12.2B',
36+
'quantization_level' => 'Q4_K_M',
37+
],
38+
],
39+
],
40+
])),
41+
new Response(body: json_encode([
42+
'model_info' => [
43+
'mock.context_length' => 1024 * 8,
44+
],
45+
'capabilities' => [
46+
'completion',
47+
'tools',
48+
],
49+
])),
50+
);
51+
52+
$factory = new ModelInfoFactory([
53+
new OllamaModelInfoProvider(httpClient: $client),
54+
new CustomModelInfoProvider([
55+
[
56+
'name' => 'foobar',
57+
'provider' => ModelProvider::Custom,
58+
'type' => ModelType::Chat,
59+
'max_input_tokens' => 8000,
60+
'max_output_tokens' => 16000,
61+
'input_cost_per_token' => 0.00123,
62+
'output_cost_per_token' => 0.00123,
63+
'features' => [
64+
ModelFeature::Vision,
65+
ModelFeature::Reasoning,
66+
],
67+
],
68+
]),
69+
]);
70+
71+
$ollamaModels = $factory->getModels(ModelProvider::Ollama);
72+
$customModels = $factory->getModels(ModelProvider::Custom);
73+
74+
expect($ollamaModels)->toBeArray()->toHaveCount(1);
75+
expect($ollamaModels[0])->toEqual('gemma3:12b');
76+
77+
expect($customModels)->toBeArray()->toHaveCount(1);
78+
expect($customModels[0])->toEqual('foobar');
79+
80+
$ollamaModelInfo = $factory->getModelInfo(ModelProvider::Ollama, 'gemma3:12b');
81+
$customModelInfo = $factory->getModelInfo(ModelProvider::Custom, 'foobar');
82+
83+
expect($ollamaModelInfo)->toBeInstanceOf(ModelInfo::class);
84+
expect($ollamaModelInfo->name)->toEqual('gemma3:12b');
85+
expect($ollamaModelInfo->provider)->toEqual(ModelProvider::Ollama);
86+
expect($ollamaModelInfo->type)->toEqual(ModelType::Chat);
87+
expect($ollamaModelInfo->features)->toEqual([
88+
ModelFeature::JsonOutput,
89+
ModelFeature::StructuredOutput,
90+
ModelFeature::ToolCalling,
91+
ModelFeature::ToolChoice,
92+
]);
93+
94+
expect($customModelInfo)->toBeInstanceOf(ModelInfo::class);
95+
expect($customModelInfo->name)->toEqual('foobar');
96+
expect($customModelInfo->provider)->toEqual(ModelProvider::Custom);
97+
expect($customModelInfo->type)->toEqual(ModelType::Chat);
98+
expect($customModelInfo->maxInputTokens)->toEqual(8000);
99+
expect($customModelInfo->maxOutputTokens)->toEqual(16000);
100+
expect($customModelInfo->inputCostPerToken)->toEqual(0.00123);
101+
expect($customModelInfo->outputCostPerToken)->toEqual(0.00123);
102+
expect($customModelInfo->features)->toEqual([
103+
ModelFeature::Vision,
104+
ModelFeature::Reasoning,
105+
]);
106+
});

0 commit comments

Comments
 (0)