What Is the ModelAdapter in Maka? Architecture and Responsibilities
The ModelAdapter serves as the central bridge between Maka's runtime and underlying AI-SDK providers, normalizing streaming interfaces, translating raw provider chunks into Maka-native events, and managing provider-specific configurations including error classification, tool name remapping, and token budgets.
The ModelAdapter is a foundational abstraction in the Apache Maka project that unifies interactions with large language model providers such as OpenAI and Anthropic. Located in packages/runtime/src/model-adapter.ts, this class encapsulates provider-specific complexity while exposing a consistent streaming interface for text generation. Understanding the ModelAdapter's architecture is essential for developers extending Maka's runtime capabilities or debugging provider integrations.
Bridging Maka and AI-SDK Providers
Resolving the Provider Runtime
The adapter creates a ResolvedModelRuntime based on the connection and model ID through the resolveModel method. It evaluates runtimeEventReplaySupport to determine if the current configuration supports event replay functionality. This resolution step ensures that Maka's backend receives a standardized runtime object regardless of whether the underlying provider is OpenAI, Anthropic, or another supported SDK.
Building Provider-Specific Language Models
Using a ModelFactory, the adapter constructs provider-specific language models while injecting runtime-specific state. For example, when interfacing with OpenAI, it configures the chat reasoning transport or the OpenAI-Responses transport as needed. This factory pattern allows Maka to support provider-specific features—such as OpenAI's reasoning models—without leaking implementation details into the core runtime.
Streaming Normalization and Event Translation
Normalizing Stream Contracts
The startStream method wraps the provider's native streamText call, tracking stream activity through callbacks and enforcing a consistent streaming contract across all providers. This normalization ensures that consumer code receives uniform events whether streaming from GPT-4, Claude, or other models.
Translating Raw Chunks to Maka Events
Raw AI-SDK chunks are converted into Maka-owned ModelStreamEvent types via the translateChunk method. This translation layer ensures that only Maka-native types are exposed to the backend, isolating the system from provider-specific payload structures. The protocol definitions for these events are maintained in packages/runtime/src/model-protocol.ts.
Error Handling and Provider Normalization
Error Classification and Failure Normalization
When providers return errors, the adapter invokes normalizeProviderFailure and classifyError to transform provider-specific exceptions into standardized ModelFailure objects. This classification system, implemented in packages/runtime/src/provider-error-classification.ts, enables consistent retry logic and error reporting across heterogeneous provider implementations.
Mapping Finish Reasons and Tool Names
Provider-specific finish reasons are mapped to Maka's internal stop-reason enum through mapFinishReason. Additionally, when using the OpenAI-Responses adapter, the adapter handles tool name remapping via remapModelMessageToolNames and remapProviderToolNamesInText to ensure that Maka's persisted tool names remain consistent regardless of provider-side naming conventions.
Token Management and Continuation Control
Token Budget Calculation
The adapter computes the maximum output tokens allowed for a request through selectedModelMaxOutputTokens, enforcing budget constraints before dispatching requests to the provider. This prevents runtime failures due to context window exceeded errors.
Continuation Lanes and Replay Support
For providers like OpenAI-Responses that support lane-based continuation, the adapter manages semantic request/response pairs and exposes replay-capability flags through runtimeEventReplaySupport. This functionality enables sophisticated conversation patterns where previous turns can be replayed or continued with modified parameters.
Lifecycle Management
The adapter exposes explicit lifecycle methods to manage continuation state: endContinuation, recordContinuationResponse, clearContinuation, and dispose. These methods ensure proper cleanup of resources and state when streaming operations complete or when the adapter is destroyed.
Implementation Reference
The ModelAdapter implementation spans several key files in the apache/maka repository:
packages/runtime/src/model-adapter.ts– Core class containingstartStream,translateChunk, and lifecycle management.packages/runtime/src/model-protocol.ts– TypeScript contracts definingModelStreamEvent,ModelFailure, and finish reason enums.packages/runtime/src/model-runtime.ts– Runtime resolution logic includingresolveModelandruntimeEventReplaySupport.packages/runtime/src/provider-error-classification.ts– Error normalization utilities used by the adapter's failure handling pipeline.
Usage Examples
Creating and using a Model Adapter
import { ModelAdapter } from './model-adapter.js';
import { resolveModelRuntime } from './model-runtime.js';
// Input needed to construct the adapter
const adapterInput = {
sessionId: 'sess-01',
connection: myRuntimeConnection,
apiKey: process.env.OPENAI_API_KEY,
modelId: 'gpt-4o-mini',
modelFactory: myModelFactory,
newId: () => crypto.randomUUID(),
now: () => Date.now(),
};
// Build the adapter
const adapter = new ModelAdapter(adapterInput);
// Prepare stream input
const streamInput = {
model: await adapter.resolveModel(),
messages: [{ role: 'user', content: [{ type: 'text', text: 'Hello' }] }],
tools: myToolSet,
activeTools: [],
onStreamActivity: () => console.log('stream activity'),
abortSignal: new AbortController().signal,
repairToolCall: async ({ toolCall, error }) => null,
};
// Start streaming
const result = await adapter.startStream(streamInput);
// Iterate over events
for await (const ev of result.events) {
console.log('event:', ev);
}
// Get the final outcome
const outcome = await result.outcome;
console.log('outcome:', outcome);
Handling a streamed error
adapter.startStream(streamInput).then(({ events, outcome }) => {
(async () => {
for await (const ev of events) {
if (ev.kind === 'error') {
console.error('Stream error:', ev.failure);
}
}
const final = await outcome;
if (final.kind !== 'completed') {
console.warn('Step failed:', final);
}
})();
});
Summary
- The ModelAdapter acts as the primary abstraction layer between Maka's runtime and AI-SDK providers, handling OpenAI, Anthropic, and other implementations.
- It normalizes streaming through
startStreamandtranslateChunk, converting provider-specific outputs into standardizedModelStreamEventtypes. - Error handling is unified via
normalizeProviderFailureandclassifyError, producing consistentModelFailureobjects for all providers. - The adapter manages provider-specific metadata including finish reason mapping, tool name remapping, and token budget calculations via
selectedModelMaxOutputTokens. - Continuation and replay capabilities are supported through lane-based state management and
runtimeEventReplaySupportflags. - All implementation resides in
packages/runtime/src/model-adapter.tswith supporting protocols inpackages/runtime/src/model-protocol.ts.
Frequently Asked Questions
What is the primary role of the ModelAdapter in Maka?
The ModelAdapter serves as the central integration point that bridges Maka's runtime with underlying AI-SDK providers like OpenAI and Anthropic. It resolves provider runtimes, builds language models, normalizes streaming interfaces, and translates raw provider outputs into Maka-native events, ensuring a uniform interface regardless of the specific AI provider being used.
How does ModelAdapter handle streaming errors?
The adapter catches errors from the provider's streamText implementation and processes them through normalizeProviderFailure and classifyError to create standardized ModelFailure objects. These failures are then emitted as ModelStreamEvent objects with kind 'error', allowing consumer code to handle retries or logging consistently across different providers.
What is the difference between ModelAdapter and ModelFactory?
The ModelFactory creates provider-specific language model instances (such as OpenAI chat or reasoning transports), while the ModelAdapter orchestrates these instances, manages their lifecycle, and normalizes their outputs. The factory handles construction; the adapter handles integration, streaming, error translation, and state management.
How does ModelAdapter support conversation replay functionality?
Through the runtimeEventReplaySupport flag and continuation lane methods (endContinuation, recordContinuationResponse, clearContinuation), the adapter records semantic request/response pairs for compatible providers like OpenAI-Responses. This enables the runtime to replay previous conversation turns or continue lanes with modified parameters while maintaining accurate state tracking.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →