A unified Flutter/Dart API for multiple Generative AI providers. AI Capo simplifies LLM integration by providing a clean, pluggable architecture for OpenAI, Anthropic Claude, Google Gemini, Ollama, Mistral, and DeepSeek.
- Multi-Provider Support: Switch between OpenAI, Anthropic, Google, Ollama, Mistral, and DeepSeek.
-
Unified Interface: All providers implement the same
AIProviderabstraction. - Streaming: Full streaming support for real-time responses.
- Lazy Initialization: Providers are created on first request and cached.
-
Structured Output: JSON Schema enforcement via
ResponseFormatacross all providers.
Add ai_capo to your pubspec.yaml:
dependencies:
ai_capo: ^1.0.0Run flutter pub get.
import 'package:ai_capo/ai_capo.dart';
final client = AIClient();Pass a config when requesting a provider:
final config = AIConfig(apiKey: 'your-api-key');final provider = client.getProvider(ProviderType.openai, config);
final response = await provider.chatCompletion(
messages: [ChatMessage.user('What is Flutter?')],
options: const ChatCompletionRequest(
model: 'gpt-5-mini-2025-08-07',
temperature: 0.7,
maxTokens: 500,
),
);
print(response.choices.first.content);final anthropic = client.getProvider(ProviderType.anthropic, config);
final response = await anthropic.chatCompletion(
messages: [ChatMessage.user('Hello, Claude!')],
);final provider = client.getProvider(ProviderType.openai, config);
await for (final chunk in provider.streamChatCompletion(
messages: [ChatMessage.user('Write a short poem.')],
options: const ChatCompletionRequest(model: 'gpt-5-mini-2025-08-07', temperature: 0.8),
)) {
print(chunk.content);
}final provider = client.getProvider(ProviderType.openai, config);
final response = await provider.chatCompletion(
messages: [
ChatMessage.system('You are a helpful Python tutor.'),
ChatMessage.user('How do list comprehensions work?'),
],
);Structured output lets you enforce a JSON schema on the model's response. Use the ResponseFormat model via responseFormat. Each provider translates it into its own wire format (response_format for OpenAI/DeepSeek/Mistral, output_config for Anthropic, responseJsonSchema for Google, format for Ollama).
Pass ResponseFormatType.jsonObject to request plain JSON output:
final response = await provider.chatCompletion(
messages: [ChatMessage.user('List 3 colors.')],
options: ChatCompletionRequest(
model: 'gpt-5-mini-2025-08-07',
responseFormat: const ResponseFormat(type: ResponseFormatType.jsonObject),
),
);
// response.choices.first.content is a JSON stringSupported by: OpenAI, DeepSeek, Mistral, Ollama, Google.
Anthropic does not support schema-less JSON output — use jsonSchema instead.
Pass ResponseFormatType.jsonSchema with a schema:
final response = await provider.chatCompletion(
messages: [
ChatMessage.system('Extract info from the text.'),
ChatMessage.user('The iPhone 16 costs $999 and was released in September 2024.'),
],
options: ChatCompletionRequest(
model: 'gpt-5-mini-2025-08-07',
responseFormat: ResponseFormat(
type: ResponseFormatType.jsonSchema,
name: 'product_info',
strict: true,
schema: {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'price': {'type': 'number'},
'release_date': {'type': 'string'},
},
'required': ['name', 'price', 'release_date'],
'additionalProperties': false,
},
),
),
);
print(response.choices.first.content);Supported by: OpenAI, DeepSeek, Mistral, Ollama, Google, Anthropic.
name and strict are optional and only used by providers that support them. schema is required when type is jsonSchema; passing a null schema throws an ArgumentError.
Feel free to open an issue or submit a pull request on GitHub.
This project is licensed under the MIT License. See LICENSE for details.
