adachsoft / ai-model-list
Framework-agnostic library exposing a public API to list available AI models from multiple providers (SPI-enabled).
Installs: 3
Dependents: 2
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/ai-model-list
Requires
- php: >=8.3
- adachsoft/collection: ^2.1
- guzzlehttp/guzzle: ^7.10
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3
- phpstan/phpstan: ^1
- phpunit/phpunit: ^12.0
This package is not auto-updated.
Last update: 2025-10-19 12:35:28 UTC
README
Framework-agnostic PHP library that exposes a clean Public API (Facade) to list available AI models from multiple providers using a Domain Port for core providers and an SPI for external extensions.
Status: Work in Progress. There is no package version yet; versioning will be done via Git tags only.
Requirements
- PHP >= 8.3
- adachsoft/collection >= 2.1
- For real provider integrations: guzzlehttp/guzzle
- For .env support (CLI, tests): vlucas/phpdotenv
Installation
This library is currently under development and may not be published to Packagist yet.
- If available on Packagist:
- composer require adachsoft/ai-model-list
- If you use it from source, make sure your composer.json autoload maps the namespace:
- PSR-4: "AdachSoft\AIModelList\": "src/"
Optional dependencies if you want real OpenAI and Google Gemini API integration and .env support for CLI:
- composer require guzzlehttp/guzzle
- composer require vlucas/phpdotenv
Dev tools (optional but recommended):
- composer require --dev friendsofphp/php-cs-fixer phpstan/phpstan
Architecture Overview
- PublicApi
- Facade, DTOs, Enums, concrete Collections (immutable)
- Clean entrypoint that throws PublicApi exceptions only
- Application
- Use case(s), mapping Domain → PublicApi
- Adapter from SPI → Domain (wraps external SPI providers)
- Optional integration of SPI Enrichment (enricher chain + mapper)
- Domain
- Value Objects (e.g., ProviderId, ModelId), Enums, concrete Collections, ModelDefinition (readonly)
- Port: Domain\Port\AiModelProviderPortInterface used by core providers
- Optional ModelMetadata (enrichment) attached to ModelDefinition
- SPI
- Contract for external providers: Spi\AiModelProvider\AiModelProviderInterface
- Contract for enrichment: Spi\Enrichment (enrichers, chain, DTO/Enum/Collections)
- SPI types are mapped to Domain via the adapter/mapper in Application
- Infrastructure
- Core providers (OpenAI, Google Gemini) implement the Domain port and return Domain models
Key idea: Core providers implement the Domain port. External providers implement the SPI and are adapted at registration time via the Application adapter, ensuring strict isolation between layers. Enrichment is also SPI-only (no Domain/Public types in SPI).
Collections
All collections use adachsoft/collection. For immutable list collections (AbstractImmutableCollection), the item type is passed via parent constructor:
public function __construct(iterable $items = [])
{
parent::__construct($items, Foo::class);
}
Do not implement getItemType() methods in this project. For maps (AbstractImmutableMap), override getKeyType() and getValueType().
Quick Start (built-in providers)
Register providers with real API keys (e.g., from .env):
use AdachSoft\AIModelList\PublicApi\Builder\AiModelCatalogFacadeBuilder;
use AdachSoft\AIModelList\PublicApi\AiModelCatalogFacadeInterface;
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();
$openAiKey = $_ENV['OPENAI_API_KEY'] ?? getenv('OPENAI_API_KEY');
$geminiKey = $_ENV['GEMINI_API_KEY'] ?? getenv('GEMINI_API_KEY');
$builder = new AiModelCatalogFacadeBuilder();
if (is_string($openAiKey) && $openAiKey !== '') {
$builder->withOpenAi($openAiKey);
}
if (is_string($geminiKey) && $geminiKey !== '') {
$builder->withGoogleGemini($geminiKey);
}
/** @var AiModelCatalogFacadeInterface $facade */
$facade = $builder->build();
$providers = $facade->listProviders(); // e.g. ["openai", "google_gemini"]
$models = $facade->listModels('google_gemini'); // PublicApi\Collection\ModelDefinitionCollection
SPI: External Providers
Implement AdachSoft\AIModelList\Spi\AiModelProvider\AiModelProviderInterface:
- getProviderId(): string – unique provider ID (e.g., openai, acme)
- listModels(ProviderId $providerId): ModelDefinitionCollection – returns SPI model definitions using SPI mirror types
- On provider failures throw Spi\AiModelProvider\Exception\ProviderSpiException
Register your provider via the builder; it will be wrapped by the SPI→Domain adapter automatically:
$facade = (new AiModelCatalogFacadeBuilder())
->withSpiProvider($yourSpiProvider)
->build();
$models = $facade->listModels($yourSpiProvider->getProviderId());
Bulk registration via typed collection
For multi-package integrations or autodiscovery tooling, provide a typed collection of SPI providers (the collection enforces the item type):
use AdachSoft\AIModelList\Spi\AiModelProvider\Collection\AiModelProviderCollection;
$providers = new AiModelProviderCollection([
$providerA, // implements AiModelProviderInterface
$providerB,
]);
$facade = (new AiModelCatalogFacadeBuilder())
->withProviders($providers)
->build();
Notes:
- The builder deduplicates providers by ID and throws PublicApi\Exception\DuplicateProviderIdException on conflicts.
- The collection guarantees all entries implement the SPI interface.
SPI Enrichment (optional)
To provide additional metadata (description, token limits, supportedGenerationMethods, etc.) implement:
- Spi\Enrichment\AiModelMetadataEnricherInterface – your enricher(s) producing SPI ModelMetadataDto
- Spi\Enrichment\AiModelMetadataEnricherChainInterface – orchestrates one or more enrichers
- Provide an Application-level mapper: Application\Mapper\SpiToDomain\SpiEnrichmentToDomainMetadataMapper to convert SPI DTOs to Domain ModelMetadata
Then register them in the builder:
$facade = (new AiModelCatalogFacadeBuilder())
->withSpiProvider($yourSpiProvider)
->withEnricherChain($yourEnricherChain)
->withEnrichmentMapper($yourSpiToDomainMetadataMapper)
->build();
$models = $facade->listModels($yourSpiProvider->getProviderId());
// Public DTOs will now contain enrichment fields (e.g., description) if provided by the chain.
Examples
Runnable examples are provided under the examples/ directory:
- examples/spi_provider_minimal.php – minimal external SPI provider registration and listing models
- examples/spi_enrichment_minimal.php – SPI provider + enrichment chain + mapper; shows enrichment in Public DTOs
Run examples:
- php examples/spi_provider_minimal.php
- php examples/spi_enrichment_minimal.php
Providers implemented out of the box
- OpenAI (providerId: "openai")
- HTTP: GET https://api.openai.com/v1/models with Authorization: Bearer
- Model ID is taken from response "id"; displayName equals the model ID
- Capabilities are inferred heuristically from model ID (CHAT/IMAGE/AUDIO/EMBEDDING)
- On failures provider throws Domain\Exception\ProviderFailureException
- Google Gemini (providerId: "google_gemini")
- HTTP: GET https://generativelanguage.googleapis.com/v1beta/models?key=&pageToken=&pageSize=1000
- The provider fetches all pages using nextPageToken (pagination) and consolidates them into a single collection.
- A safety limit of 50 pages is applied; exceeding it throws Domain\Exception\ProviderFailureException.
- Model ID is the last segment of "name" (e.g., models/gemini-1.5-pro-001 → gemini-1.5-pro-001); displayName equals the model ID
- Capabilities inferred from supportedGenerationMethods and modelId (CHAT/EMBEDDING/IMAGE/AUDIO)
- On failures provider throws Domain\Exception\ProviderFailureException
Exceptions and layering
- Domain Port providers throw Domain\Exception\ProviderFailureException for network/HTTP/format errors.
- The Application ListModelsService wraps provider failures into Public API exceptions:
- PublicApi\AiModelCatalogFacade::listModels() throws PublicApi\Exception\AiModelCatalogException on provider failure.
.env configuration
Copy .env.example to .env and set your secrets:
OPENAI_API_KEY=sk-your-openai-key
GEMINI_API_KEY=AIza-your-gemini-key
CLI (bin/list-models)
The CLI script lists available models for a provider using .env configuration.
Usage:
- php bin/list-models
Examples:
- php bin/list-models openai (requires OPENAI_API_KEY)
- php bin/list-models google_gemini (requires GEMINI_API_KEY)
Behavior:
- Providers are registered conditionally based on available API keys
- Helpful error messages are printed when a required key is missing or the provider is not registered
- Output format (tab-separated):
- \t
Exit codes:
- 0: success
- 1: usage error or missing configuration (.env not found or API key missing) or provider not registered
- 2: runtime error
Testing
- PHPUnit >= 12 is used. A default phpunit.xml.dist is provided.
- Run tests: vendor/bin/phpunit
- Tests follow .docs/UNIT_TESTING_GUIDELINES.md (AAA, PHP attributes like #[CoversClass], avoid testing simple DTOs and exceptions).
- Functional tests include datasets conditionally for providers based on OPENAI_API_KEY and GEMINI_API_KEY.
Code Style & Static Analysis
- PHP CS Fixer: vendor/bin/php-cs-fixer fix src
- PHPStan (level 8 recommended): vendor/bin/phpstan analyse src --level=8
License
MIT