_common library

Classes

AiBroker
Vendor-neutral AI broker. One implementation per provider (Claude, OpenAI, Gemini). Callers depend on this interface; the AiBrokerRegistry is the indirection that lets the active provider change at runtime.
AiBrokerRegistry
Process-wide registry. Wire brokers once at startup, then look them up by AiBroker.id from anywhere in the app.
AiMessage
AnthropicBroker
Anthropic Claude (/v1/messages). system is a top-level field, not a role — keep it out of ChatRequest.messages.
ChatRequest
What every broker call boils down to. system is the persistent instruction; messages is the turn history (oldest first). Both complete* and chat* build the same wire payload — complete* is just chat* with one user message.
Client
The interface for HTTP clients that take care of maintaining persistent connections across multiple requests to the same server.
EnvKeyResolver
Reads keys from Platform.environment using a per-broker env var name. Defaults follow the SDK conventions each provider documents:
GeminiBroker
Google Gemini (generateContent / streamGenerateContent). Pagination on listModels caps at 5 pages × 50; only gemini-* ids that support generateContent are kept.
KeyResolver
Where API keys come from. Flutter apps with secure storage implement this against their own store; CLI / backend code can use the built-in EnvKeyResolver or MapKeyResolver.
LineSplitter
A StreamTransformer that splits a String into individual lines.
MapKeyResolver
In-memory resolver — for tests, or for apps that already hold keys in a runtime-built map.
OpenAiBroker
OpenAI chat-completions. Filters listModels to gpt-* / o<digit>* so the picker doesn't show whisper / embeddings / dall-e.
Platform
Information about the environment in which the current program is running.
Request
An HTTP request where the entire request body is known in advance.
Response
An HTTP response where the entire response body is known in advance.
SseEvent
One parsed Server-Sent Event. OpenAI and Anthropic both use SSE for their streaming endpoints (Gemini uses a JSON-array stream — see the Gemini broker for that codepath).
StreamController<T>
A controller with the stream it controls.

Enums

AiRole
Who authored a message in a chat. system lives on ChatRequest.system, not here — every provider treats system instructions specially (Anthropic puts them at top-level; Gemini uses systemInstruction; OpenAI uses a role:system message). We only model the back-and-forth.

Constants

immutable → const Immutable
Annotation on an immutable class.
retryableStatusCodes → const Set<int>
Status codes that mean "try again later, the request itself is fine": rate limits and transient upstream overload. Quota-exceeded (a hard 429 with quota in the body) is not retryable — callers should surface that to the user instead of burning attempts.
utf8 → const Utf8Codec
An instance of the default implementation of the Utf8Codec.
visibleForTesting → const Object
Annotation on a public declaration that should only be used in tests.

Functions

decodeSseStream(Stream<List<int>> source) Stream<SseEvent>
Decodes a UTF-8 byte stream into SseEvents. Buffers across chunk boundaries (one event may span several Stream<List<int>> chunks), joins multi-line data: payloads with \n per the SSE spec, and filters out keep-alive comments.
jsonDecode(String source, {Object? reviver(Object? key, Object? value)?}) → dynamic
Parses the string and returns the resulting Json object.
jsonEncode(Object? object, {Object? toEncodable(Object? nonEncodable)?}) String
Converts object to a JSON string.
openSsePost({required Uri uri, required Map<String, String> headers, required String body, required String providerLabel, Client? client}) Future<Stream<List<int>>>
Opens a POST request and returns the response body as a byte stream. Throws AiBrokerException on non-2xx (the body is buffered in that path so we can include the error message).
retryRequest({required Future<Response> send(), required String providerLabel, int maxAttempts = 5, Duration baseDelay = const Duration(seconds: 5), bool isHardFailure(Response)?}) Future<Response>
HTTP retry with exponential backoff capped at 5 attempts × 5s × 2ⁿ. Matches the loop used in chitbot's OpenAiBot / GeminiBot / ClaudeClient, generalised so all three brokers share one path.
stripCodeFence(String text) String
Strips a leading and trailing markdown code fence from text. Model output for "give me code" prompts usually arrives wrapped in ```lang\n...\n```, even after an explicit "code only" instruction. Call this on the broker's raw text before writing it to a .dart / .ts / etc. file.

Exceptions / Errors

AiBrokerException
Thrown for any broker-layer failure that callers can present to the user — auth, rate-limit-after-retries, empty response, malformed response.
MissingKeyException