AiChatAdapter class

Provider-agnostic AI chat adapter.

Quick setup — use a built-in factory for zero-config streaming:

AiChatAdapter.anthropic(apiKey: myKey)
AiChatAdapter.openAi(apiKey: myKey)
AiChatAdapter.google(apiKey: myKey)

Custom backend — implement sendMessage directly:

AiChatAdapter(
  sendMessage: (request) async* {
    final stream = openai.chat.completions.createStream(
      model: 'gpt-4o',
      messages: [
        {'role': 'system', 'content': request.systemPrompt},
        ...request.history.map((m) =>
            {'role': m.role.name, 'content': m.text}),
      ],
    );
    await for (final chunk in stream) {
      yield chunk.choices.first.delta.content ?? '';
    }
  },
)

Errors — a failed reply shows a short reason in the chat ("API key rejected", "Rate limited", "Provider error", "Offline", "Can't reach the provider", else "Reply failed"); the full error text, with API keys and tokens masked, is offered through Copy error. A custom backend can throw, or add to its stream, an AiProviderException carrying the HTTP statusCode so the reason is exact; otherwise the status is read from the error text where it can be ("returned 401", "status code of 429", "statusCode: 503").

Timeouts — a reply fails when no text arrives within firstTokenTimeout, or when the text stops for longer than stallTimeout. A slow model (a local model loading, a reasoning model thinking before it writes) needs longer values, or none. An empty chunk on the stream restarts the current timeout without adding text, so an adapter can keep a slow reply alive by yielding ''.

Constructors

AiChatAdapter({required Stream<String> sendMessage(AiChatRequest request), List<String>? networkExcludePatterns, Duration? firstTokenTimeout = defaultFirstTokenTimeout, Duration? stallTimeout = defaultStallTimeout})
const
AiChatAdapter.anthropic({required String apiKey, String model = 'claude-sonnet-4-20250514', int maxTokens = 4096, Duration? firstTokenTimeout = defaultFirstTokenTimeout, Duration? stallTimeout = defaultStallTimeout})
Creates an adapter for the Anthropic Messages API.
factory
AiChatAdapter.google({required String apiKey, String model = 'gemini-2.0-flash', Duration? firstTokenTimeout = defaultFirstTokenTimeout, Duration? stallTimeout = defaultStallTimeout})
Creates an adapter for the Google Gemini API.
factory
AiChatAdapter.openAi({required String apiKey, String model = 'gpt-4o', int maxTokens = 4096, String baseUrl = 'https://api.openai.com', Duration? firstTokenTimeout = defaultFirstTokenTimeout, Duration? stallTimeout = defaultStallTimeout})
Creates an adapter for the OpenAI Chat Completions API.
factory

Properties

firstTokenTimeout → Duration?
Longest wait from sending a message to the first text of the reply; the reply then fails with "No reply in …" and offers Retry.
final
hashCode → int
The hash code for this object.
no setterinherited
networkExcludePatterns → List<String>?
URL patterns the adapter's provider uses, auto-merged with SleuthConfig.networkExcludePatterns so the network monitor ignores AI API traffic.
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
sendMessage → Stream<String> Function(AiChatRequest request)
Sends a chat request and returns a stream of text tokens.
final
stallTimeout → Duration?
Longest gap between two pieces of text once a reply is streaming; the reply then fails with "Reply stalled", the text so far kept on screen.
final

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited