Introduces the IProviderAdapter interface as the architecture foundation for multi-provider support. Extracts Ollama logic into OllamaAdapter and makes ProviderService adapter-aware while preserving the full existing API. ## What changed ### @mosaic/types — new provider adapter types - IProviderAdapter interface: register(), listModels(), healthCheck(), createCompletion() - ProviderHealth / ProviderHealthStatus — health check result - CompletionParams / CompletionMessage / CompletionTool — completion request - CompletionEvent / CompletionUsage — streamed completion response ### apps/gateway — adapter implementation - apps/gateway/src/agent/adapters/ollama.adapter.ts — OllamaAdapter (extracted from ProviderService) - apps/gateway/src/agent/adapters/index.ts — barrel export - ProviderService.onModuleInit() is now async (awaits registerAll()) - New ProviderService methods: registerAll(), getAdapter(), healthCheckAll() - PROVIDER_ADAPTERS symbol exported as future DI injection token - Anthropic/OpenAI/Z.ai remain as direct registry calls (M3-002 to M3-005 scope) - Updated provider.service.test.ts: all tests now await onModuleInit() ## Pi SDK compatibility findings Pi SDK uses ModelRegistry as central registry. The adapter pattern is a Mosaic abstraction layered on top — adapters call registry.registerProvider() during register(). Pi SDK has no native adapter concept; it does not conflict. createCompletion() is defined in the interface but not called by the Pi layer. Pi SDK's AgentSession.prompt() and ModelRegistry.getAvailable() handle all actual completions. createCompletion() is reserved for future direct-completion use cases (post-M3 scope). OllamaAdapter throws NotImplementedError for now. No Pi SDK friction was found for the adapter pattern. The registry integration point (registerProvider()) is exactly what the existing code already used. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2.3 KiB
2.3 KiB
M3-001 Provider Adapter Pattern — Scratchpad
Objective
Refactor ProviderService into an IProviderAdapter pattern without breaking existing Ollama flow.
Plan
- Add
IProviderAdapterinterface and supporting types to@mosaic/typesprovider package - Create
apps/gateway/src/agent/adapters/directory with:provider-adapter.interface.ts— IProviderAdapter + ProviderHealth + CompletionParams + CompletionEventollama.adapter.ts— extract existing Ollama logic
- Refactor ProviderService:
- Accept
IProviderAdapter[](injected via DI token) registerAll()/listModels()aggregates from all adaptersgetAdapter(name)— lookup by namehealthCheckAll()— check all adapters- Keep Pi ModelRegistry wiring (required by AgentService)
- Accept
- Wire up in AgentModule
Key Findings
Pi SDK Compatibility
- Pi SDK uses
ModelRegistryas central registry; ProviderService wraps it ModelRegistry.registerProvider()is the integration point — adapters call this- Pi doesn't have a native "IProviderAdapter" concept — adapters are a Mosaic abstraction on top
- The
createAgentSession()call in AgentService usesmodelRegistry: this.providerService.getRegistry() - OllamaAdapter should call
registry.registerProvider('ollama', {...})same as today - CompletionParams/CompletionEvent: Pi SDK streams via
AgentSession.prompt(), not raw completion — IProviderAdapter.createCompletion() is for future direct use; for now stub or leave as interface-only — ASSUMPTION: createCompletion is reserved for future M3+ work; Pi SDK owns the actual streaming
Implementation Notes
- ESM: use
.jsextensions in all imports - NestJS: use
@Inject()explicitly - Keep RoutingService working — it only uses
providerService.listAvailableModels() - Keep AgentService working — it uses
providerService.getRegistry(),findModel(),getDefaultModel(),listAvailableModels()
Progress
- Add types to @mosaic/types
- Create adapters/ directory
- Create IProviderAdapter interface file
- Create OllamaAdapter
- Refactor ProviderService
- Update AgentModule
- Run tests
- Run quality gates
Risks
- Pi SDK doesn't natively support IProviderAdapter — adapters are a layer on top
- createCompletion() is architecturally sound but requires Pi session bypass (future work)