tuul_sdk_flutter 0.1.5
tuul_sdk_flutter: ^0.1.5 copied to clipboard
Flutter SDK for Tuul agent runtime, streaming, widget config, and local tool orchestration.
Tuul SDK for Flutter #
A Flutter and Dart SDK for consuming Tuul agent endpoints with a clean, developer-friendly API.
This package mirrors the Tuul TypeScript SDK surface for Dart and Flutter projects, including:
- typed
/sdkclient access - streaming responses over SSE
- local tool orchestration with automatic continuation
- conversation and message helpers
- widget config, embed, theme, and endpoint catalog accessors
- a
ChangeNotifierchat controller - a starter Flutter chat panel widget
Features #
- Simple prompt generation with
generate()andprompt() - Streaming support with
stream() - Client-side local tools that automatically continue the same Tuul run
- Conversation helpers for listing, renaming, deleting, and auto-titling chats
- Message helpers for reading conversation or run messages
- Widget accessors for public widget configuration and embed data
- Flutter-ready UI state management through
TuulChatController - Starter widget via
TuulAgentPanel
Installation #
Add the package to your pubspec.yaml:
dependencies:
tuul_sdk: ^0.1.0
Then run:
flutter pub get
Imports #
import 'package:tuul_sdk/client.dart';
import 'package:tuul_sdk/models.dart';
import 'package:tuul_sdk/chat_controller.dart';
import 'package:tuul_sdk/widgets/tuul_agent_panel.dart';
If you export these through a barrel file, you can also use:
import 'package:tuul_sdk/tuul_sdk.dart';
Quick start #
import 'package:tuul_sdk/client.dart';
import 'package:tuul_sdk/models.dart';
final client = TuulClient(
const TuulClientConfig(
agentId: 'agent-id',
apiKey: 'sdk_key_here',
defaultSessionId: 'web-session-123',
),
);
final response = await client.generate(
const TuulPromptInput(
input: 'Summarize the latest support backlog.',
),
);
print(response.text);
Streaming #
await for (final event in client.stream(
const TuulPromptInput(
input: 'Write a release summary.',
),
)) {
if (event.type == 'delta' && event.text != null) {
print(event.text);
}
}
Local tools #
When the model emits a call for one of your registered local tools, the SDK executes it on the client, posts the tool result back to Tuul, and continues the same response in context.
final response = await client.generate(
TuulPromptInput(
input: 'Use the local calculator tool to add 4 and 9.',
localTools: [
TuulLocalTool(
name: 'calculator_add',
description: 'Adds two numbers locally on the client.',
inputSchema: {
'type': 'object',
'properties': {
'a': {'type': 'number'},
'b': {'type': 'number'},
},
'required': ['a', 'b'],
},
execute: (input, context) async {
final args = Map<String, dynamic>.from(input as Map);
final a = (args['a'] as num).toDouble();
final b = (args['b'] as num).toDouble();
return {'sum': a + b};
},
),
],
),
);
print(response.text);
Using prompt() #
prompt() gives you a single entry point for both blocking and streaming flows.
Non-streaming #
final result = await client.prompt(
const TuulPromptInput(
input: 'Explain the support trend for this week.',
stream: false,
),
);
print(result.text);
Streaming #
final result = await client.prompt(
const TuulPromptInput(
input: 'Draft a release summary.',
stream: true,
),
);
print(result.text);
print(result.metadata.finishReason);
SDK manifest #
Load the remote SDK manifest:
final manifest = await client.manifest();
print(manifest.packageName);
print(manifest.endpoints);
Or without constructing a client first:
final manifest = await TuulClient.getManifest();
Public widget endpoints #
The same client can load widget-related public resources.
Widget config #
final config = await client.getWidgetConfig();
print(config.agentId);
print(config.theme.launcherLabel);
Widget theme #
final theme = await client.getWidgetTheme();
print(theme.primaryAccentToken);
Widget embed #
final embed = await client.getWidgetEmbed();
print(embed.snippet);
Endpoint catalog #
final endpoints = await client.getEndpointCatalog();
for (final endpoint in endpoints) {
print('${endpoint.method} ${endpoint.path}');
}
Conversation helpers #
List conversations #
final conversations = await client.listConversations();
Rename a conversation #
await client.renameConversation('conversation-id', 'Quarterly planning');
Delete a conversation #
await client.deleteConversation('conversation-id');
Generate a conversation title #
await client.generateConversationTitle('conversation-id');
Message helpers #
Get messages in a conversation #
final messages = await client.getMessages('conversation-id');
Get messages for a run #
final messages = await client.getRunMessages('run-id');
Resources #
final resources = await client.getResources();
Flutter chat controller #
TuulChatController wraps the SDK in a Flutter-friendly state layer using ChangeNotifier.
final controller = TuulChatController(
client: client,
autoLoadConversations: true,
autoLoadResources: false,
);
Initialize #
await controller.initialize();
Send a message #
await controller.send('Hello from Flutter');
Load a conversation #
await controller.loadMessages('conversation-id');
Starter widget #
TuulAgentPanel provides a ready-made Flutter chat panel for quick integration.
import 'package:flutter/material.dart';
import 'package:tuul_sdk/client.dart';
import 'package:tuul_sdk/models.dart';
import 'package:tuul_sdk/chat_controller.dart';
import 'package:tuul_sdk/widgets/tuul_agent_panel.dart';
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
late final TuulChatController controller;
@override
void initState() {
super.initState();
final client = TuulClient(
const TuulClientConfig(
agentId: 'agent-id',
apiKey: 'sdk_key_here',
defaultSessionId: 'flutter-session-1',
),
);
controller = TuulChatController(client: client);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF030712),
body: Padding(
padding: const EdgeInsets.all(24),
child: TuulAgentPanel(controller: controller),
),
);
}
}
Authentication behavior #
The client supports two auth modes:
- SDK auth using
apiKey - Widget auth using
widgetKey
For SDK-protected endpoints, the client automatically sends:
x-api-keyAuthorization: Bearer <apiKey>
For widget endpoints, the client sends x-widget-key when configured.
Error handling #
The SDK throws TuulSdkError when a request fails.
try {
final response = await client.generate(
const TuulPromptInput(input: 'Hello'),
);
print(response.text);
} on TuulSdkError catch (error) {
print(error.message);
print(error.statusCode);
}
Dependencies #
This SDK currently relies on:
dependencies:
flutter:
sdk: flutter
http: ^1.0.0
Notes #
sessionIdis required for listing conversations and loading resources.apiKeyis required for SDK-authenticated endpoints.widgetKeyis optional unless your widget endpoints require it.- SSE parsing is handled internally by the client.
- Local tool execution works in both blocking and streaming modes.
License #
This project is licensed under the MIT License. See LICENSE for details.