@johnhenry/aimatey-frontend
Frontend adapters define the input format for your AI requests. Write code in any API format you prefer - OpenAI, Anthropic, Google Gemini, or others.
Installation
Section titled “Installation”npm install @johnhenry/aimatey-frontendOverview
Section titled “Overview”Frontend adapters translate your chosen API format into aimatey’s Intermediate Representation (IR). This allows you to write code in whatever format you’re most comfortable with.
Available Adapters:
- OpenAI (
/openai) - Anthropic (
/anthropic) - Google Gemini (
/gemini) - Ollama (
/ollama) - Mistral (
/mistral) - Chrome built-in AI (
/chrome-ai) - Generic IR (
/generic)
OpenAI Frontend Adapter
Section titled “OpenAI Frontend Adapter”Use OpenAI’s API format as input.
Installation
Section titled “Installation”import { OpenAIFrontendAdapter } from '@johnhenry/aimatey-frontend/openai';import { Bridge } from '@johnhenry/aimatey-core';import { OpenAIFrontendAdapter } from '@johnhenry/aimatey-frontend/openai';import { AnthropicBackendAdapter } from '@johnhenry/aimatey-backend/anthropic';
const bridge = new Bridge( new OpenAIFrontendAdapter(), new AnthropicBackendAdapter({ apiKey: 'your-key' }));
// Write in OpenAI formatconst response = await bridge.chat({ model: 'gpt-4', messages: [ { role: 'system', content: 'You are helpful.' }, { role: 'user', content: 'Hello!' } ], temperature: 0.7, max_tokens: 100});Supported Features
Section titled “Supported Features”- ✅ Chat completions
- ✅ Streaming
- ✅ Function calling/tools
- ✅ Vision (image inputs)
- ✅ System messages
- ✅ Temperature, top_p, max_tokens
- ✅ Stop sequences
- ✅ Presence/frequency penalties
Request Format
Section titled “Request Format”interface OpenAIRequest { model: string; messages: OpenAIMessage[]; temperature?: number; max_tokens?: number; top_p?: number; frequency_penalty?: number; presence_penalty?: number; stop?: string | string[]; stream?: boolean; user?: string; seed?: number; tools?: Array<{ type: 'function'; function: { name: string; description?: string; parameters?: object } }>; tool_choice?: 'auto' | 'none' | 'required' | { type: 'function'; function: { name: string } };}Anthropic Frontend Adapter
Section titled “Anthropic Frontend Adapter”Use Anthropic’s API format as input.
Installation
Section titled “Installation”import { AnthropicFrontendAdapter } from '@johnhenry/aimatey-frontend/anthropic';import { Bridge } from '@johnhenry/aimatey-core';import { AnthropicFrontendAdapter } from '@johnhenry/aimatey-frontend/anthropic';import { OpenAIBackendAdapter } from '@johnhenry/aimatey-backend/openai';
const bridge = new Bridge( new AnthropicFrontendAdapter(), new OpenAIBackendAdapter({ apiKey: 'your-key' }));
// Write in Anthropic formatconst response = await bridge.chat({ model: 'claude-haiku-4-5-20251001', max_tokens: 100, messages: [ { role: 'user', content: 'Hello!' } ], system: 'You are helpful.', // System message separate temperature: 0.7});Key Differences from OpenAI
Section titled “Key Differences from OpenAI”- System messages are a separate
systemparameter (not in messages array) max_tokensis required- No
presence_penaltyorfrequency_penalty - Different tool/function calling format
Supported Features
Section titled “Supported Features”- ✅ Chat completions
- ✅ Streaming
- ✅ Tool use
- ✅ Vision (image inputs)
- ✅ System messages (as parameter)
- ✅ Temperature, top_p, top_k
- ✅ Stop sequences
Google Gemini Frontend Adapter
Section titled “Google Gemini Frontend Adapter”Use Google’s Gemini API format as input.
Installation
Section titled “Installation”import { GeminiFrontendAdapter } from '@johnhenry/aimatey-frontend/gemini';import { Bridge } from '@johnhenry/aimatey-core';import { GeminiFrontendAdapter } from '@johnhenry/aimatey-frontend/gemini';import { OpenAIBackendAdapter } from '@johnhenry/aimatey-backend/openai';
const bridge = new Bridge( new GeminiFrontendAdapter(), new OpenAIBackendAdapter({ apiKey: 'your-key' }));
// Write in Gemini formatconst response = await bridge.chat({ model: 'gemini-1.5-pro', contents: [ { role: 'user', parts: [{ text: 'Hello!' }] } ], generationConfig: { temperature: 0.7, maxOutputTokens: 100 }});Key Differences
Section titled “Key Differences”- Uses
contentsinstead ofmessages - Uses
partsfor multi-modal content - Role is
modelinstead ofassistant - Configuration in
generationConfigobject - System instructions separate parameter
Supported Features
Section titled “Supported Features”- ✅ Chat completions
- ✅ Streaming
- ✅ Function calling
- ✅ Vision (native multi-modal support)
- ✅ System instructions
- ✅ Temperature, topP, topK
- ✅ Stop sequences
- ✅ Safety settings
Ollama Frontend Adapter
Section titled “Ollama Frontend Adapter”Use Ollama’s API format (compatible with local models).
Installation
Section titled “Installation”import { OllamaFrontendAdapter } from '@johnhenry/aimatey-frontend/ollama';import { Bridge } from '@johnhenry/aimatey-core';import { OllamaFrontendAdapter } from '@johnhenry/aimatey-frontend/ollama';import { OpenAIBackendAdapter } from '@johnhenry/aimatey-backend/openai';
const bridge = new Bridge( new OllamaFrontendAdapter(), new OpenAIBackendAdapter({ apiKey: 'your-key' }));
// Write in Ollama formatconst response = await bridge.chat({ model: 'llama3.2', messages: [ { role: 'user', content: 'Hello!' } ], stream: false});Supported Features
Section titled “Supported Features”- ✅ Chat completions
- ✅ Streaming
- ✅ System messages
- ✅ Temperature, top_p, top_k
- ⚠️ Limited tool support (model-dependent)
Mistral Frontend Adapter
Section titled “Mistral Frontend Adapter”Use Mistral’s API format (very similar to OpenAI).
Installation
Section titled “Installation”import { MistralFrontendAdapter } from '@johnhenry/aimatey-frontend/mistral';import { Bridge } from '@johnhenry/aimatey-core';import { MistralFrontendAdapter } from '@johnhenry/aimatey-frontend/mistral';import { OpenAIBackendAdapter } from '@johnhenry/aimatey-backend/openai';
const bridge = new Bridge( new MistralFrontendAdapter(), new OpenAIBackendAdapter({ apiKey: 'your-key' }));
// Mistral format is very similar to OpenAIconst response = await bridge.chat({ model: 'mistral-large-latest', messages: [ { role: 'user', content: 'Hello!' } ], temperature: 0.7});Groq and Other OpenAI-Compatible APIs
Section titled “Groq and Other OpenAI-Compatible APIs”Groq’s API is OpenAI-compatible, so there is no separate Groq frontend adapter -
use OpenAIFrontendAdapter for Groq-shaped requests. (Groq as a provider is
available on the backend side, as @johnhenry/aimatey-backend/groq.)
Choosing a Frontend Adapter
Section titled “Choosing a Frontend Adapter”Use OpenAI Frontend Adapter if:
Section titled “Use OpenAI Frontend Adapter if:”- You’re familiar with OpenAI’s API
- You want the most widely-used format
- Your codebase already uses OpenAI
- You need maximum compatibility
Use Anthropic Frontend Adapter if:
Section titled “Use Anthropic Frontend Adapter if:”- You prefer Anthropic’s API design
- You want explicit system message separation
- Your codebase uses Claude
Use Gemini Frontend Adapter if:
Section titled “Use Gemini Frontend Adapter if:”- You’re working with Google AI Platform
- You need native multi-modal support
- You use Vertex AI
Use Ollama Frontend Adapter if:
Section titled “Use Ollama Frontend Adapter if:”- You’re working with local models
- You want a simple format
- You’re developing locally
Feature Compatibility Matrix
Section titled “Feature Compatibility Matrix”| Feature | OpenAI | Anthropic | Gemini | Ollama | Mistral | Chrome AI |
|---|---|---|---|---|---|---|
| Chat | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Streaming | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Tools/Functions | ✅ | ✅ | ✅ | ⚠️ | ✅ | ❌ |
| Vision | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ❌ |
| System Messages | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
✅ Fully supported | ⚠️ Partially supported | ❌ Not supported
Creating Custom Frontend Adapters
Section titled “Creating Custom Frontend Adapters”You can create your own frontend adapter:
toIR() and fromIR() are async, and fromIRStream() is required.
import type { AdapterMetadata, FrontendAdapter, IRChatRequest, IRChatResponse, IRChatStream} from '@johnhenry/aimatey-types';
export class CustomFrontendAdapter implements FrontendAdapter<CustomRequest, CustomResponse, CustomChunk>{ readonly metadata: AdapterMetadata = { name: 'custom', version: '1.0.0', provider: 'Custom', capabilities: { streaming: true, multiModal: false, systemMessageStrategy: 'in-messages', supportsMultipleSystemMessages: true } };
// Convert custom format to IR async toIR(request: CustomRequest): Promise<IRChatRequest> { return { messages: request.conversation.map((msg) => ({ role: msg.sender === 'human' ? 'user' : 'assistant', content: msg.text })), parameters: { model: request.modelName, temperature: request.temp, maxTokens: request.maxLength }, metadata: { requestId: crypto.randomUUID(), timestamp: Date.now(), provenance: { frontend: 'custom' } } }; }
// Convert IR back to custom format async fromIR(response: IRChatResponse): Promise<CustomResponse> { return { reply: typeof response.message.content === 'string' ? response.message.content : '', tokens: response.usage?.totalTokens ?? 0 }; }
// Stream support async *fromIRStream(stream: IRChatStream) { for await (const chunk of stream) { if (chunk.type === 'content') { yield { text: chunk.delta, done: false }; } else if (chunk.type === 'done') { yield { text: '', done: true }; } } }}Best Practices
Section titled “Best Practices”1. Choose Based on Your Codebase
Section titled “1. Choose Based on Your Codebase”If your codebase already uses a specific API format, use that frontend adapter:
// Existing OpenAI codeconst openaiResponse = await openai.chat.completions.create({ model: 'gpt-4', messages: [{ role: 'user', content: 'Hello' }]});
// Easy migration - use OpenAI frontend adapterconst bridge = new Bridge( new OpenAIFrontendAdapter(), // Same format! new AnthropicBackendAdapter({ apiKey }));2. Frontend is Independent of Backend
Section titled “2. Frontend is Independent of Backend”Mix and match any frontend with any backend:
// OpenAI format → Anthropic backendnew Bridge(new OpenAIFrontendAdapter(), new AnthropicBackendAdapter({ apiKey }));
// Anthropic format → OpenAI backendnew Bridge(new AnthropicFrontendAdapter(), new OpenAIBackendAdapter({ apiKey }));
// Gemini format → Groq backendnew Bridge(new GeminiFrontendAdapter(), new GroqBackendAdapter({ apiKey }));3. Type Safety
Section titled “3. Type Safety”Use TypeScript for frontend-specific request types:
import type { OpenAIRequest, OpenAIResponse, OpenAIStreamChunk} from '@johnhenry/aimatey-frontend/openai';
const bridge = new Bridge( new OpenAIFrontendAdapter(), new AnthropicBackendAdapter({ apiKey }));
const request: OpenAIRequest = { model: 'gpt-4', messages: [{ role: 'user', content: 'Hello' }]};
const response: OpenAIResponse = await bridge.chat(request);
// Streaming chunks are typed as OpenAIStreamChunkfor await (const chunk of bridge.chatStream({ ...request, stream: true })) { const delta: OpenAIStreamChunk = chunk; process.stdout.write(delta.choices[0]?.delta?.content ?? '');}Semantic Drift
Section titled “Semantic Drift”When converting between formats, some features may not map perfectly. This is called “semantic drift.”
Example: System Messages
Section titled “Example: System Messages”// OpenAI: system messages in array{ messages: [ { role: 'system', content: 'You are helpful.' }, { role: 'user', content: 'Hello' } ]}
// Anthropic: system separate{ system: 'You are helpful.', messages: [ { role: 'user', content: 'Hello' } ]}The adapter handles this automatically, but be aware of potential drift.
Handling Drift
Section titled “Handling Drift”Frontend adapters track and warn about semantic drift:
const response = await bridge.chat(request);
if (response.warnings) { console.warn('Semantic drift detected:', response.warnings);}See Also
Section titled “See Also”- Backend Adapters - Available backend providers
- IR Format - Intermediate representation details
- Core Package - Bridge and Router documentation
- Tutorial: Simple Bridge - Step-by-step guide