feat(M3-003): implement OpenAI provider adapter for Codex gpt-5.4
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/pr/ci Pipeline failed

Adds OpenAIAdapter implementing IProviderAdapter, replacing the legacy
registerOpenAIProvider() inline registration in ProviderService.

- Creates apps/gateway/src/agent/adapters/openai.adapter.ts with:
  - register(): initialises OpenAI client, registers codex-gpt-5-4 with
    Pi ModelRegistry using openai-completions API; skips gracefully when
    OPENAI_API_KEY is absent
  - listModels(): returns Codex gpt-5.4 ModelInfo (128k context, tools+vision)
  - healthCheck(): lightweight GET /v1/models to verify API key validity
  - createCompletion(): streaming completions via openai SDK
    chat.completions.create({ stream: true }); maps chunks to CompletionEvent
- Installs openai SDK (^6.32.0) as a dependency in apps/gateway
- Registers OpenAIAdapter in ProviderService alongside OllamaAdapter
- Removes legacy registerOpenAIProvider() private method from ProviderService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 16:30:56 -05:00
parent cfdd2b679c
commit 6f104d116b
5 changed files with 229 additions and 27 deletions

View File

@@ -8,7 +8,7 @@ import type {
ProviderHealth,
ProviderInfo,
} from '@mosaic/types';
import { OllamaAdapter } from './adapters/index.js';
import { OllamaAdapter, OpenAIAdapter } from './adapters/index.js';
import type { TestConnectionResultDto } from './provider.dto.js';
/** DI injection token for the provider adapter array. */
@@ -31,15 +31,14 @@ export class ProviderService implements OnModuleInit {
this.registry = new ModelRegistry(authStorage);
// Build the default set of adapters that rely on the registry
this.adapters = [new OllamaAdapter(this.registry)];
this.adapters = [new OllamaAdapter(this.registry), new OpenAIAdapter(this.registry)];
// Run all adapter registrations first (Ollama, and any future adapters)
await this.registerAll();
// Register API-key providers directly (Anthropic, OpenAI, Z.ai, custom)
// These do not yet have dedicated adapter classes (M3-002 through M3-005).
// Register API-key providers directly (Anthropic, Z.ai, custom)
// OpenAI now has a dedicated adapter class (M3-003).
this.registerAnthropicProvider();
this.registerOpenAIProvider();
this.registerZaiProvider();
this.registerCustomProviders();
@@ -234,7 +233,7 @@ export class ProviderService implements OnModuleInit {
// ---------------------------------------------------------------------------
// Private helpers — direct registry registration for providers without adapters yet
// (Anthropic, OpenAI, Z.ai will move to adapters in M3-002 through M3-005)
// (Anthropic, Z.ai will move to adapters in M3-002, M3-005)
// ---------------------------------------------------------------------------
private registerAnthropicProvider(): void {
@@ -257,26 +256,6 @@ export class ProviderService implements OnModuleInit {
this.logger.log('Anthropic provider registered with 3 models');
}
private registerOpenAIProvider(): void {
const apiKey = process.env['OPENAI_API_KEY'];
if (!apiKey) {
this.logger.debug('Skipping OpenAI provider registration: OPENAI_API_KEY not set');
return;
}
const models = ['gpt-4o', 'gpt-4o-mini', 'o3-mini'].map((id) =>
this.cloneBuiltInModel('openai', id),
);
this.registry.registerProvider('openai', {
apiKey,
baseUrl: 'https://api.openai.com/v1',
models,
});
this.logger.log('OpenAI provider registered with 3 models');
}
private registerZaiProvider(): void {
const apiKey = process.env['ZAI_API_KEY'];
if (!apiKey) {