Setting Up the AI Chat SDK
ProWhat you'll learn
- Generating the AI Chat SDK with your chosen provider (OpenAI, Anthropic, or both)
- Understanding the multi-package structure: api, impl, and provider-specific packages
- Configuring API keys securely using
String.fromEnvironment - Consuming the streaming chat API in a Flutter widget
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- Network SDK already generated (see Network SDK Setup)
Step 1: Generate the AI Chat SDK
archipelago generate ai_chat_sdkYou will be prompted for:
- aiChatProvider —
openai,anthropic, orboth(default:openai) - defaultModel — e.g.
gpt-4o-miniorclaude-3-haiku-20240307(default:gpt-4o-mini)
Or use a config file:
{
"aiChatProvider": "openai",
"defaultModel": "gpt-4o-mini"
}archipelago generate ai_chat_sdk --config ai_chat_config.jsonStep 2: Understand the Generated Structure
The AI Chat SDK uses the API/Impl split because the chat contract is consumed by multiple feature packages, and the provider implementation can be swapped or combined without touching feature code:
features/ai_chat/
├── ai_chat_api/
│ └── lib/src/
│ ├── ai_chat_sdk.dart # AIChatSDK abstract interface
│ ├── chat_message.dart # ChatMessage model
│ ├── chat_chunk.dart # Streaming chunk model
│ └── message_role.dart # MessageRole enum (user/assistant/system)
├── ai_chat_impl/
│ └── lib/src/
│ ├── ai_chat_sdk_impl.dart # Orchestrates provider(s), history persistence
│ └── di/
│ └── ai_chat_module.dart # DI registration
├── ai_chat_openai_impl/ # Generated if aiChatProvider is openai or both
│ └── lib/src/
│ ├── openai_chat_provider.dart # OpenAI streaming adapter
│ └── di/
│ └── openai_module.dart
└── ai_chat_anthropic_impl/ # Generated if aiChatProvider is anthropic or both
└── lib/src/
├── anthropic_chat_provider.dart
└── di/
└── anthropic_module.dartNote: If you selected
both, both provider packages are generated, but only the first registered handler is active at runtime. Wire only the provider you need, or implement a routing strategy inai_chat_sdk_impl.dart.
Step 3: Configure Your API Key
Never hardcode API keys. Use String.fromEnvironment and pass values via --dart-define in your CI configuration.
OpenAI:
// ai_chat_openai_impl/lib/src/di/openai_module.dart
@module
abstract class AppAIModule {
@lazySingleton
AIChatConfig get aiChatConfig => const AIChatConfig(
apiKey: String.fromEnvironment('OPENAI_API_KEY'),
defaultModel: 'gpt-4o-mini',
);
}Anthropic:
@module
abstract class AppAIModule {
@lazySingleton
AIChatConfig get aiChatConfig => const AIChatConfig(
apiKey: String.fromEnvironment('ANTHROPIC_API_KEY'),
defaultModel: 'claude-3-haiku-20240307',
);
}Pass the key at build time:
flutter run --dart-define=OPENAI_API_KEY=sk-...In CI (GitHub Actions):
- name: Build
run: flutter build apk --dart-define=OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}Step 4: Register the Feature
In your shell app's bootstrap.dart:
import 'package:ai_chat_impl/ai_chat_impl.dart';
FeatureRegistry.register(AIChatSdkImpl());Step 5: Use the Streaming API in a Widget
The SDK returns a Stream<ChatChunk> so you can render tokens as they arrive:
class ChatPage extends StatefulWidget {
const ChatPage({super.key});
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
final _chatSDK = getIt<AIChatSDK>();
final _messages = <ChatMessage>[];
String _streamingResponse = '';
Future<void> _sendMessage(String text) async {
final userMessage = ChatMessage(role: MessageRole.user, content: text);
setState(() {
_messages.add(userMessage);
_streamingResponse = '';
});
final stream = _chatSDK.sendMessage(userMessage);
await for (final chunk in stream) {
setState(() => _streamingResponse += chunk.delta);
}
setState(() {
_messages.add(
ChatMessage(role: MessageRole.assistant, content: _streamingResponse),
);
_streamingResponse = '';
});
}
}Step 6: Load and Clear History
Chat history is persisted per session using SharedPreferences:
// Load history on page init
final history = await _chatSDK.loadHistory();
// Clear history (e.g. on "New chat" button tap)
await _chatSDK.clearHistory();Step 7: Swap Providers
Features import only ai_chat_api. To switch from OpenAI to Anthropic, change the registered DI module in bootstrap.dart — no feature code changes needed:
// Before (OpenAI)
FeatureRegistry.register(AIChatSdkImpl(provider: OpenAIChatProvider()));
// After (Anthropic)
FeatureRegistry.register(AIChatSdkImpl(provider: AnthropicChatProvider()));Key Configuration Points
| Customization | Where to Change |
|---|---|
| Change default model | AIChatConfig.defaultModel in the provider DI module |
| Add system prompt | ChatMessage(role: MessageRole.system, content: '...') prepended to history |
| Persist across sessions | Override loadHistory — swap SharedPreferences for a local database |
| Route between providers | Subclass AIChatSDK in ai_chat_impl, dispatch by message content or user tier |
Next Steps
- Configure networking — the AI Chat SDK routes HTTP through the Network SDK interceptor stack
- Set up feature flags to gate the chat feature by subscription tier