El patrón Adapter traduce la interfaz de un servicio incompatible a la interfaz que espera el resto de la aplicación.
1. El Problema de Partida
El asistente del proyecto necesita poder responder con distintos proveedores (Anthropic, OpenAI) según coste o disponibilidad. Cada SDK expone un formato de mensajes y de streaming diferente: client.messages.create() de Anthropic no es intercambiable con client.chat.completions.create() de OpenAI. El código de la app tendría que ramificar con if (provider === 'openai') en cada punto donde se envía un mensaje.
2. La Solución Adapter
Se define una interfaz interna única ChatProvider.send(messages) y un adaptador por proveedor que traduce hacia el SDK real, devolviendo siempre la misma forma de respuesta.
// src/lib/chat/ChatProvider.js — interfaz esperada por la app
export class ChatProvider {
async send(messages) {
throw new Error('send() no implementado');
}
}// src/lib/chat/AnthropicAdapter.js
import Anthropic from '@anthropic-ai/sdk';
import { ChatProvider } from './ChatProvider';
export class AnthropicAdapter extends ChatProvider {
constructor(apiKey) {
super();
this.client = new Anthropic({ apiKey });
}
async send(messages) {
const res = await this.client.messages.create({
model: 'claude-sonnet-5',
max_tokens: 1024,
messages: messages.map((m) => ({ role: m.role, content: m.text })),
});
return { text: res.content[0].text, provider: 'anthropic' };
}
}// src/lib/chat/OpenAIAdapter.js
import OpenAI from 'openai';
import { ChatProvider } from './ChatProvider';
export class OpenAIAdapter extends ChatProvider {
constructor(apiKey) {
super();
this.client = new OpenAI({ apiKey });
}
async send(messages) {
const res = await this.client.chat.completions.create({
model: 'gpt-4.1',
messages: messages.map((m) => ({ role: m.role, content: m.text })),
});
return { text: res.choices[0].message.content, provider: 'openai' };
}
}3. Uso en el cliente
// El resto de la app solo conoce ChatProvider, nunca los SDKs concretos
const provider = useOpenAI ? new OpenAIAdapter(key) : new AnthropicAdapter(key);
const { text } = await provider.send(conversationMessages);4. Ventajas
- Cambio de proveedor sin tocar la UI: basta con inyectar otro adaptador.
- Respuesta uniforme: el resto del código siempre lee
{ text, provider }. - Aislamiento de dependencias: actualizar el SDK de un proveedor solo afecta a su adaptador.