Feature SDKs (Pro)
ai_chat_sdk Pro
- Tier
- Proversion 2.0.5
- Command
archipelago generate ai_chat_sdk- Generates
features/ai_chat/ai_chat_anthropic_implfeatures/ai_chat/ai_chat_apifeatures/ai_chat/ai_chat_implfeatures/ai_chat/ai_chat_openai_impl
- Generates first
- storage_sdkwhen needed and missing, so there is nothing to install first
- Depends on
- shared_identifiersgenerate these first
Streaming LLM chat UI with multi-provider support (OpenAI, Anthropic). Includes chat bubbles, a streaming indicator, and
storage_sdk-backed conversation persistence.
Variables
| Variable | Type | Default | Description |
|---|---|---|---|
| uiKitPackageName | string | app_ui_kit | Name of the UI kit package (resolved from archipelago.yaml project.ui_kit_package_name, auto-detected from packages/*_ui_kit when missing) |
| aiChatProvider | enum | openai | Which AI provider(s) to include: openai, anthropic, both |
| defaultModel | string | gpt-4o-mini | Default model ID sent to the provider (e.g. gpt-4o-mini, claude-3-haiku-20240307) |
Prerequisite: storage_sdk
ai_chat_impl persists conversation history through storage_sdk_api's StorageProvider rather than registering its own storage instance. hooks/pre_gen.dart self-generates storage_sdk automatically (SubBrickGenerator) if it isn't already installed — archipelago generate ai_chat_sdk works standalone into a bare workspace, no manual prerequisite step.
Generated Packages
| Package | Condition | Description |
|---|---|---|
ai_chat_api | Always | AiChatSDK contract, AiChatProvider interface, models (ChatMessage, ChatRequest, ChatStreamEvent, ChatRole, AiChatConfig, AiChatProviderId) |
ai_chat_impl | Always | Orchestrator (AiChatSDKImpl), chat screen + input bar/message list/message bubble/streaming-indicator widgets (built on the ui_kit typed context extensions), storage_sdk-backed history, router, DI |
ai_chat_openai_impl | openai or both | OpenAI streaming provider (Chat Completions SSE) |
ai_chat_anthropic_impl | anthropic or both | Anthropic streaming provider (Messages API SSE) |
The provider package(s) you did not select are removed by the post-generation hook, not merely left ungenerated.
Usage
Interactive
archipelago generate ai_chat_sdkNon-interactive (CI)
archipelago generate ai_chat_sdk --config my_config.jsonGenerated Structure
features/
└── ai_chat/
├── ai_chat_api/
│ └── lib/src/
│ ├── models/
│ ├── utils/ # AiChatInjectorKey
│ └── ai_chat_sdk.dart # AiChatSDK contract
├── ai_chat_impl/
│ └── lib/src/
│ ├── data/ # ChatHistoryStorage
│ ├── di/ # AiChatImplGlobalModule
│ ├── presentation/
│ │ └── widgets/
│ ├── router/
│ └── ai_chat_sdk_impl.dart
├── ai_chat_openai_impl/ # if aiChatProvider=openai or both
│ └── lib/src/
│ ├── di/register_module.dart # vendor-configured Dio
│ └── openai_chat_provider.dart
└── ai_chat_anthropic_impl/ # if aiChatProvider=anthropic or both
└── lib/src/
├── di/register_module.dart
└── anthropic_chat_provider.dartAPI
abstract interface class AiChatSDK {
Future<void> launch(BuildContext context); // opens /ai-chat
Set<AiChatProviderId> get availableProviders;
AiChatProviderId get currentProvider;
void setProvider(AiChatProviderId provider); // throws ArgumentError if not registered
Stream<List<ChatMessage>> get conversationStream;
List<ChatMessage> get currentMessages;
Future<String> sendMessage(String content); // returns the request id
void cancelInflight();
Future<void> clearConversation();
Future<void> retryLastFailed();
AiChatConfig get config;
}final chat = getIt<AiChatSDK>();
await chat.launch(context); // navigate to the chat screen
// Or drive it programmatically:
await chat.sendMessage('Hello!'); // streams the assistant reply
chat.conversationStream.listen((messages) => setState(() => _messages = messages));
chat.cancelInflight(); // cancel an in-flight request
await chat.clearConversation(); // clear memory + persistenceMulti-provider registration
Each provider package implements AiChatProvider (Stream<ChatStreamEvent> streamChat(ChatRequest request)) and registers itself tagged with a distinct @Named(AiChatInjectorKey.openai / .anthropic) key. This is required, not cosmetic: get_it throws on a duplicate unnamed type+registration, so aiChatProvider: both would otherwise crash at boot (fixed in 1.3.2 — before that, both provider packages registered AiChatProvider unnamed). The orchestrator discovers every registered provider via getIt.getAll<AiChatProvider>() and switches with setProvider():
chat.setProvider(AiChatProviderId.anthropic); // throws if not registeredDI and wiring notes
ai_chat_impl/pubspec.yaml declares its archipelago: di_modules block for monorepo_toolkit generate host-registry:
archipelago:
di_modules:
- class: AiChatImplPackageModule # generated; registers AiChatConfig only
- class: AiChatImplGlobalModule # hand-written orchestrator
depends_on:
- ai_chat_openai_impl # if useOpenai
- ai_chat_anthropic_impl # if useAnthropic
- storage_sdk_shared_preferencesAiChatImplGlobalModule registers AiChatManifest (the /ai-chat route) in FeatureSDKRegistry and AiChatSDK as a lazy singleton. Its init resolves nothing: getIt.getAll<AiChatProvider>() and StorageProvider are read the first time something resolves AiChatSDK, such as opening /ai-chat or calling launch(). The depends_on list still places it after the provider modules.
AiChatSDKImpl starts with an empty conversation and loads the stored history in the background. When the load finishes, the history is emitted on conversationStream, placed before anything sent in the meantime. sendMessage, clearConversation and retryLastFailed wait for the load first, so the request context and a clear always see the full history. A failed read starts an empty chat.
API keys
Neither provider package wires a real secret by default. Each registers its own vendor-configured Dio (base URL + auth header) as a named singleton (@Named('openai') / @Named('anthropic')) from String.fromEnvironment('OPENAI_API_KEY', defaultValue: 'YOUR_OPENAI_API_KEY'), with a per-request Dio interceptor that rejects calls while the placeholder key is still in place (checked per request, not at DI build time, so the graph resolves without a key):
// ai_chat_openai_impl/lib/src/di/register_module.dart
@Named('openai')
@lazySingleton
Dio get openAiDio {
const key = String.fromEnvironment('OPENAI_API_KEY', defaultValue: 'YOUR_OPENAI_API_KEY');
return Dio(BaseOptions(baseUrl: 'https://api.openai.com', headers: {'Authorization': 'Bearer $key'}))
..interceptors.add(/* rejects the request while key is the placeholder */);
}After generation, set OPENAI_API_KEY / ANTHROPIC_API_KEY in .env and run melos run env:generate, then replace the String.fromEnvironment read with the generated Env.openAiApiKey / Env.anthropicApiKey per the TODO(user) left in each register_module.dart.
The provider classes take their own Dio constructor parameter with an explicit @Named(...) qualifier, so each resolves its vendor-configured client even when an unnamed Dio is registered elsewhere (fixed in 1.3.3).
Persistence
Conversation history is persisted via StorageProvider.forNamespace('ai_chat') — non-secure, StorageScope.application (the default). It is not StorageScope.user, so it is never swept by a logout-clear coordinator; clear it explicitly with clearConversation().
Dependencies
storage_sdk_api— self-generated automatically if not already installedstorage_sdk_shared_preferences— listed in thedepends_onof the DI module- ui_kit package (
uiKitPackageName) — the chat widgets usecontext.colors,context.typography,context.spacing,context.radius dio— both provider packages speak HTTP viaDio, independent of the project'snetworkLibrarychoice (they do not usenetwork_sdk_api'sHttpClientport)
Known Limitations
- No integration tests ship for either provider's SSE parsing — both source files carry a
VERIFYcomment noting Dio's streamed-response type is hard to mock, and ask you to add a live-API integration test before shipping to production. - Both provider packages talk to
Diodirectly; they don't go through the project's chosennetwork_sdktransport, so network-layer concerns configured there (auth interceptor, logging) don't apply to chat requests.
Changelog
Run archipelago brick info ai_chat_sdk to see the latest published version. Enterprise customers can read the full CHANGELOG.md in the brick source repository.