# How the Image Model Adapter Registry Manages Multiple Image Generation Services in Prompt-Optimizer

> Learn how the image model adapter registry in prompt-optimizer unifies Gemini, OpenAI, and other services. Discover centralized error handling and dynamic model discovery.

- Repository: [且炼时光/prompt-optimizer](https://github.com/linshenkx/prompt-optimizer)
- Tags: internals
- Published: 2026-02-23

---

**The image model adapter registry in the linshenkx/prompt-optimizer repository provides a unified abstraction layer that discovers, registers, and orchestrates multiple image generation services—including Gemini, OpenAI, SiliconFlow, and Ollama—behind a consistent TypeScript interface with centralized error handling and support for both static and dynamic model discovery.**

The **image model adapter registry** serves as the central nervous system for the prompt-optimizer project's image generation capabilities. Located in the `packages/core/src/services/image/adapters/` directory, this architecture allows the application to treat heterogeneous services—from cloud providers like OpenAI and Gemini to local deployments like Ollama—as interchangeable components through a common `IImageProviderAdapter` interface.

## Core Architecture of the Image Model Adapter Registry

The registry implementation separates concerns between image-specific logic and shared adapter management functionality. This design enables consistent behavior across both image and LLM adapter registries while allowing domain-specific error handling.

### ImageAdapterRegistry and AbstractAdapterRegistry

The concrete implementation resides in [`packages/core/src/services/image/adapters/registry.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/registry.ts), where the `ImageAdapterRegistry` class extends `AbstractAdapterRegistry<IImageProviderAdapter>` from [`packages/core/src/services/adapters/abstract-registry.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/adapters/abstract-registry.ts). This inheritance provides shared functionality for caching, provider discovery, and model metadata management while the concrete implementation handles image-specific errors through the `ImageError` class defined in [`packages/core/src/services/image/errors.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/errors.ts).

The abstract base manages an internal `Map<string, IImageProviderAdapter>` that stores all registered adapters using lowercase provider keys for case-insensitive lookups.

### Provider-Specific Adapters

Each supported service implements the `IImageProviderAdapter` interface. For example, [`packages/core/src/services/image/adapters/gemini.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/gemini.ts) contains the `GeminiImageAdapter` class, while [`packages/core/src/services/image/adapters/openai.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/openai.ts) implements `OpenAIImageAdapter`. These adapters encapsulate provider-specific logic for:

- Exposing provider metadata via `getProvider()`
- Returning static model lists via `getModels()`
- Optionally supporting dynamic model fetching via `getModelsAsync(config)`
- Declaring `supportsDynamicModels` boolean flags

## Registry Initialization and Adapter Registration

When instantiated, the registry automatically discovers and registers all available adapters through the `initializeAdapters()` method. This process occurs in the constructor of the abstract base class, which invokes the concrete implementation:

```typescript
// packages/core/src/services/adapters/abstract-registry.ts
constructor() {
  this.initializeAdapters(); // Automatically invoked
}

```

The concrete `ImageAdapterRegistry` implements this method to instantiate each adapter and store it in an internal Map using lowercase provider keys:

```typescript
// packages/core/src/services/image/adapters/registry.ts
protected initializeAdapters(): void {
  const geminiAdapter = new GeminiImageAdapter();
  const openaiAdapter = new OpenAIImageAdapter();
  const ollamaAdapter = new OllamaImageAdapter();
  // ... additional adapters for Seedream, SiliconFlow, OpenRouter, DashScope, ModelScope

  this.adapters.set('gemini', geminiAdapter);
  this.adapters.set('openai', openaiAdapter);
  this.adapters.set('ollama', ollamaAdapter);
  
  // Pre-load static models for fast look-ups
  this.preloadStaticModels();
}

```

## Unified API for Provider and Model Discovery

The **image model adapter registry** exposes a consistent API for querying providers and their models, abstracting whether the underlying service supports dynamic model listing or only static definitions.

### Key Registry Methods

The following methods in [`packages/core/src/services/adapters/abstract-registry.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/adapters/abstract-registry.ts) provide the primary interface:

- **`getAdapter(providerId: string)`**: Retrieves the concrete adapter instance or throws an `ImageError` with code `PROVIDER_NOT_FOUND`.
- **`getAllProviders()`**: Aggregates metadata from all registered adapters into an array of `ImageProvider` objects.
- **`getStaticModels(providerId: string)`**: Returns cached static models or loads them lazily from the adapter's `getModels()` method.
- **`getModels(providerId: string, connectionConfig?)`**: Unified entry point that prefers dynamic fetching (via `getModelsAsync()`) when `supportsDynamicModels` is true and configuration is provided; otherwise falls back to static models.
- **`validateProviderModel(providerId, modelId)`**: Confirms that a specific model ID exists within the specified provider's catalog.

### Static vs Dynamic Model Retrieval

*Static models* are hard-coded arrays defined within each adapter (e.g., the list of available Gemini models in [`gemini.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/gemini.ts)). The registry caches these during initialization via `preloadStaticModels()` for fast synchronous access.

*Dynamic models* require authenticated API calls. When `getModels()` is invoked with a `connectionConfig` object containing API keys and endpoints, the registry checks `supportsDynamicModels()` and calls `adapter.getModelsAsync(connectionConfig)` if available. Failures are caught and logged, with automatic graceful fallback to the cached static list.

```typescript
// Example: Fetching dynamic models with fallback
const connectionConfig = {
  apiKey: process.env.OPENAI_API_KEY,
  endpoint: 'https://api.openai.com/v1'
};

try {
  const models = await imageRegistry.getModels('openai', connectionConfig);
  console.log('Dynamic models:', models.map(m => m.id));
} catch (error) {
  // Registry automatically falls back to static models on failure
  const staticModels = imageRegistry.getStaticModels('openai');
  console.log('Static fallback:', staticModels.map(m => m.id));
}

```

## Error Handling and Validation

The registry centralizes error handling through the `ImageError` class defined in [`packages/core/src/services/image/errors.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/errors.ts). When operations fail, the registry generates specific error codes:

- **`PROVIDER_NOT_FOUND`**: Returned when `getAdapter()` is called with an unregistered provider ID.
- **`DYNAMIC_MODELS_NOT_SUPPORTED`**: Returned when dynamic model fetching is requested for a provider that only supports static lists.

The concrete registry implements `createUnknownProviderError()` to wrap these codes in `ImageError` instances:

```typescript
// packages/core/src/services/image/adapters/registry.ts
protected createUnknownProviderError(providerId: string): Error {
  return new ImageError(
    IMAGE_ERROR_CODES.PROVIDER_NOT_FOUND,
    undefined,
    { providerId }
  );
}

```

## Extending the Registry with New Providers

Adding support for a new image generation service requires no modifications to the core registry logic. The architecture supports extension through three steps:

1. **Create the adapter class** implementing `IImageProviderAdapter` in a new file (e.g., [`packages/core/src/services/image/adapters/mynewservice.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/mynewservice.ts)). The class must provide:
   - `getProvider()` returning `ImageProvider` metadata
   - `getModels()` returning static `ImageModel[]`
   - Optional `getModelsAsync(config)` if dynamic fetching is supported
   - `supportsDynamicModels` boolean flag

2. **Register the adapter** in [`packages/core/src/services/image/adapters/registry.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/registry.ts) by importing the class and adding it to `initializeAdapters()`:

```typescript
import { MyNewServiceImageAdapter } from './mynewservice';

protected initializeAdapters(): void {
  // ... existing adapters ...
  const myNewAdapter = new MyNewServiceImageAdapter();
  this.adapters.set('mynewservice', myNewAdapter);
  this.preloadStaticModels();
}

```

3. **Export the adapter** (if needed) and ensure it follows the naming convention. The registry automatically handles the rest, including static model caching and error mapping.

## Summary

- The **image model adapter registry** in `linshenkx/prompt-optimizer` provides a unified abstraction over heterogeneous image generation services including Gemini, OpenAI, Ollama, SiliconFlow, Seedream, OpenRouter, DashScope, and ModelScope.
- The architecture separates concerns between the concrete `ImageAdapterRegistry` (handling image-specific errors) and the shared `AbstractAdapterRegistry` (managing caching, discovery, and model retrieval logic).
- Adapters implement `IImageProviderAdapter` to expose provider metadata, static model lists, and optional dynamic model fetching via `getModelsAsync()`.
- The registry automatically pre-loads static models during initialization for performance and gracefully falls back from dynamic to static model lists when API calls fail or are unsupported.
- Adding new providers requires only implementing the adapter interface and registering the instance in `initializeAdapters()`, with zero changes needed to the core registry logic.

## Frequently Asked Questions

### What is the difference between static and dynamic model lists in the image model adapter registry?

Static model lists are hard-coded arrays defined within each adapter (such as `GeminiImageAdapter` or `OpenAIImageAdapter`) that describe the models a service offers without requiring API authentication. Dynamic model lists are fetched at runtime via the adapter's optional `getModelsAsync()` method, which requires a `connectionConfig` object containing API keys and endpoints. The registry prefers dynamic lists when available and configured, but automatically falls back to static lists if the dynamic request fails or is unsupported.

### How does the registry handle unsupported or unknown image providers?

When a caller requests an adapter for an unregistered provider ID via `getAdapter()`, the registry invokes `createUnknownProviderError()` to generate an `ImageError` with the code `PROVIDER_NOT_FOUND`. This error includes the requested provider ID in its metadata, allowing applications to display meaningful feedback to users. Similarly, attempting to fetch dynamic models from a provider that only supports static lists returns an error with code `DYNAMIC_MODELS_NOT_SUPPORTED`.

### Can I add support for a custom image generation API without modifying the core registry code?

Yes, extending the registry to support a new image generation service requires no changes to the abstract or concrete registry classes. You only need to create a new adapter class implementing the `IImageProviderAdapter` interface in a file such as [`packages/core/src/services/image/adapters/custom.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/custom.ts), then import and register that adapter in the `initializeAdapters()` method of [`packages/core/src/services/image/adapters/registry.ts`](https://github.com/linshenkx/prompt-optimizer/blob/main/packages/core/src/services/image/adapters/registry.ts). The registry automatically handles caching, error mapping, and API unification for the new provider.

### What performance optimizations does the registry implement for model lookups?

The registry optimizes model lookups by pre-loading static model lists during initialization via the `preloadStaticModels()` method, which caches all static models in memory to avoid repeated disk access or computation. For dynamic model fetching, the registry implements a fallback mechanism that returns cached static models immediately if the asynchronous API call fails, ensuring that the application remains responsive even when external services are unavailable. All provider lookups use a `Map<string, IImageProviderAdapter>` with lowercase keys for O(1) case-insensitive access.