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

v0.2.0 2025-10-19 14:26 UTC

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