Skip to content

Commit 1651a02

Browse files
Update dot guides based on latest API surface (#531)
1 parent 65b0632 commit 1651a02

File tree

5 files changed

+133
-96
lines changed

5 files changed

+133
-96
lines changed

.gemini/GEMINI.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,16 @@ The project uses standard `flutter` and `dart` commands. A comprehensive script
7373
- The examples and the `genui_firebase_ai` package use Firebase.
7474
- A script at `tool/stub_firebase_options.sh` is used in CI to create a stub `firebase_options.dart` file. For local development, developers need to configure their own Firebase project by following the instructions in `packages/genui/USAGE.md`.
7575

76-
## Folder `spikes`
76+
## Updating the Guides (`packages/genui/.guides`)
7777

78-
The folder `spikes` contains experiments and proof of concepts,
79-
that does not have to be of good quality.
78+
When asked to update the developer guides located in `packages/genui/.guides`, it is critical to ensure the documentation accurately reflects the current state of the codebase. Before making any changes to the guides, you must read *all* the Dart code in the following packages:
8079

81-
Skip this folder when reviewing code.
80+
- `packages/genui`
81+
- `packages/genui_firebase_ai`
82+
- `packages/genui_google_generative_ai`
83+
- `packages/genui_a2ui`
84+
85+
This ensures that any code examples, API references, and architectural explanations in the guides are up-to-date and consistent with the actual implementation.
8286

8387
## Draft pull requests
8488

packages/genui/.guides/docs/connect_to_agent_provider.md

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
title: Connecting to an agent provider
33
description: |
44
Instructions for connecting `genui` to the agent provider of your
5-
choice.
5+
choice. See `setup.md` for a description of the different `ContentGenerator`
6+
implementations that are available.
67
---
78

89
Follow these steps to connect `genui` to an agent provider and give
@@ -32,65 +33,60 @@ and a `ContentGenerator`.
3233
```dart
3334
import 'package:flutter/material.dart';
3435
import 'package:genui/genui.dart';
35-
36-
// Replace with your actual ContentGenerator implementation
37-
class YourContentGenerator implements ContentGenerator {
38-
// ... implementation details ...
39-
@override
40-
Stream<A2uiMessage> get a2uiMessageStream => Stream.empty(); // Replace
41-
@override
42-
Stream<String> get textResponseStream => Stream.empty(); // Replace
43-
@override
44-
Stream<ContentGeneratorError> get errorStream => Stream.empty(); // Replace
45-
@override
46-
ValueListenable<bool> get isProcessing => ValueNotifier(false); // Replace
47-
@override
48-
Future<void> sendRequest(
49-
ChatMessage message, {
50-
Iterable<ChatMessage>? history,
51-
}) async { /* ... */ }
52-
@override
53-
void dispose() { /* ... */ }
54-
}
36+
import 'package:genui_firebase_ai/genui_firebase_ai.dart';
5537
5638
class _MyHomePageState extends State<MyHomePage> {
5739
late final GenUiManager _genUiManager;
5840
late final GenUiConversation _genUiConversation;
59-
final _surfaceIds = <String>[];
41+
final _messages = <ChatMessage>[];
6042
6143
@override
6244
void initState() {
6345
super.initState();
6446
6547
_genUiManager = GenUiManager(catalog: CoreCatalogItems.asCatalog());
6648
67-
// NOTE: You need a concrete implementation of ContentGenerator here.
68-
// The 'genui_firebase_ai' package provides implementations
69-
// like FirebaseAiContentGenerator.
70-
final contentGenerator = YourContentGenerator();
49+
// Use a concrete implementation of ContentGenerator.
50+
final contentGenerator = FirebaseAiContentGenerator(
51+
catalog: _genUiManager.catalog,
52+
systemInstruction: 'You are a helpful assistant.',
53+
additionalTools: const [],
54+
);
7155
7256
_genUiConversation = GenUiConversation(
7357
genUiManager: _genUiManager,
7458
contentGenerator: contentGenerator,
7559
onSurfaceAdded: _onSurfaceAdded,
76-
onSurfaceUpdated: _onSurfaceUpdated, // Example callback
60+
onSurfaceUpdated: _onSurfaceUpdated,
7761
onSurfaceDeleted: _onSurfaceDeleted,
7862
onTextResponse: (text) => print('AI Text: $text'),
7963
onError: (error) => print('AI Error: ${error.error}'),
8064
);
8165
}
8266
8367
void _onSurfaceAdded(SurfaceAdded update) {
84-
setState(() => _surfaceIds.add(update.surfaceId));
68+
setState(() {
69+
_messages.add(
70+
AiUiMessage(
71+
definition: update.definition,
72+
surfaceId: update.surfaceId,
73+
),
74+
);
75+
});
8576
}
8677
8778
void _onSurfaceUpdated(SurfaceUpdated update) {
88-
// Handle surface updates if needed
79+
// Handle surface updates if needed. For example, you might want to
80+
// scroll to the bottom of a list when a surface is updated.
8981
print('Surface ${update.surfaceId} updated');
9082
}
9183
9284
void _onSurfaceDeleted(SurfaceRemoved update) {
93-
setState(() => _surfaceIds.remove(update.surfaceId));
85+
setState(
86+
() => _messages.removeWhere(
87+
(m) => m is AiUiMessage && m.surfaceId == update.surfaceId,
88+
),
89+
);
9490
}
9591
9692
@override
@@ -122,18 +118,28 @@ To receive and display generated UI:
122118
// ...
123119
124120
final _textController = TextEditingController();
125-
final _surfaceIds = <String>[];
121+
final _messages = <ChatMessage>[];
126122
127123
// Send a message containing the user's text to the agent.
128124
void _sendMessage(String text) {
129125
if (text.trim().isEmpty) return;
130-
_genUiConversation.sendRequest(UserMessage.text(text));
126+
final message = UserMessage.text(text);
127+
setState(() => _messages.add(message));
128+
_genUiConversation.sendRequest(message);
129+
}
130+
131+
void _onSurfaceAdded(SurfaceAdded update) {
132+
setState(() {
133+
_messages.add(
134+
AiUiMessage(
135+
definition: update.definition,
136+
surfaceId: update.surfaceId,
137+
),
138+
);
139+
});
131140
}
132141
133-
// Callbacks for GenUiConversation (defined in initState example above)
134-
// void _onSurfaceAdded(SurfaceAdded update) { ... }
135-
// void _onSurfaceUpdated(SurfaceUpdated update) { ... }
136-
// void _onSurfaceDeleted(SurfaceRemoved update) { ... }
142+
// ... other callbacks
137143
138144
@override
139145
Widget build(BuildContext context) {
@@ -146,40 +152,22 @@ To receive and display generated UI:
146152
children: [
147153
Expanded(
148154
child: ListView.builder(
149-
itemCount: _surfaceIds.length,
155+
itemCount: _messages.length,
150156
itemBuilder: (context, index) {
151-
// For each surface, create a GenUiSurface to display it.
152-
final id = _surfaceIds[index];
153-
return GenUiSurface(host: _genUiConversation.host, surfaceId: id);
154-
},
155-
),
156-
),
157-
SafeArea(
158-
child: Padding(
159-
padding: const EdgeInsets.symmetric(horizontal: 16.0),
160-
child: Row(
161-
children: [
162-
Expanded(
163-
child: TextField(
164-
controller: _textController,
165-
decoration: const InputDecoration(
166-
hintText: 'Enter a message',
167-
),
157+
final message = _messages[index];
158+
return switch (message) {
159+
AiUiMessage() => GenUiSurface(
160+
host: _genUiConversation.host,
161+
surfaceId: message.surfaceId,
168162
),
169-
),
170-
const SizedBox(width: 16),
171-
ElevatedButton(
172-
onPressed: () {
173-
// Send the user's text to the agent.
174-
_sendMessage(_textController.text);
175-
_textController.clear();
176-
},
177-
child: const Text('Send'),
178-
),
179-
],
180-
),
163+
AiTextMessage() => ListTile(title: Text(message.text)),
164+
UserMessage() => ListTile(title: Text(message.text)),
165+
_ => const SizedBox.shrink(),
166+
};
167+
},
181168
),
182169
),
170+
// ... text input row
183171
],
184172
),
185173
);

packages/genui/.guides/docs/create_a_custom_catalogitem.md

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,10 @@ final riddleCard = CatalogItem(
6060
}) {
6161
final json = data as Map<String, Object?>;
6262
63-
// 1. Resolve the question reference
64-
final questionRef = json['question'] as Map<String, Object?>;
65-
final questionPath = questionRef['path'] as String?;
66-
final questionLiteral = questionRef['literalString'] as String?;
67-
final questionNotifier = questionPath != null
68-
? dataContext.subscribe<String>(questionPath)
69-
: ValueNotifier<String?>(questionLiteral);
70-
71-
// 2. Resolve the answer reference
72-
final answerRef = json['answer'] as Map<String, Object?>;
73-
final answerPath = answerRef['path'] as String?;
74-
final answerLiteral = answerRef['literalString'] as String?;
75-
final answerNotifier = answerPath != null
76-
? dataContext.subscribe<String>(answerPath)
77-
: ValueNotifier<String?>(answerLiteral);
63+
final questionNotifier =
64+
dataContext.subscribeToString(json['question'] as Map<String, Object?>?);
65+
final answerNotifier =
66+
dataContext.subscribeToString(json['answer'] as Map<String, Object?>?);
7867
7968
// 3. Use ValueListenableBuilder to build the UI reactively
8069
return ValueListenableBuilder<String?>(

packages/genui/.guides/examples/riddles.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ final riddleCard = CatalogItem(
254254
required context,
255255
required dataContext,
256256
}) {
257-
final json = data as Map<String, Object?>;
258-
259257
final questionNotifier =
260258
dataContext.subscribeToString(json['question'] as Map<String, Object?>?);
261259
final answerNotifier =

packages/genui/.guides/setup.md

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,25 @@ Use the following instructions to add `genui` to your Flutter app. The
44
code examples show how to perform the instructions on a brand new app created by
55
running `flutter create`.
66

7-
## 1. Configure your agent provider
7+
## 1. Choosing a ContentGenerator
8+
9+
`genui` provides three `ContentGenerator` implementations for connecting to
10+
different types of agent providers. Use the following guide to choose the one
11+
that best fits your needs.
12+
13+
- **`FirebaseAiContentGenerator`**: Use this when you are ready to deploy your
14+
app. This generator connects to Gemini via Firebase AI Logic, and is the
15+
recommended approach for production apps.
16+
17+
- **`GoogleGenerativeAiContentGenerator`**: Use this for prototyping and local
18+
development. This generator connects to the Google Generative AI API using an
19+
API key, and is a good choice for getting started quickly.
20+
21+
- **`A2uiContentGenerator`**: Use this to connect to a server that implements
22+
the A2UI protocol. This is a good choice if you have an existing agent that
23+
you want to connect to `genui`.
24+
25+
## 2. Configure your agent provider
826

927
`genui` can connect to a variety of agent providers. Choose the section
1028
below for your preferred provider.
@@ -20,15 +38,16 @@ Logic, follow these instructions:
2038
for that project. This should also be done by a human.
2139
3. Follow the first three steps in
2240
[Firebase's Flutter Setup guide](https://firebase.google.com/docs/flutter/setup)
23-
to add Firebase to your app. **Ask me to run `flutterfire configure` for you.**
41+
to add Firebase to your app. Run `flutterfire configure` to configure your
42+
app.
2443
4. In `pubspec.yaml`, add `genui` and `genui_firebase_ai` to the
2544
`dependencies` section. As of this writing, it's best to use pub's git
2645
dependency to refer directly to this project's source.
2746

2847
```yaml
2948
dependencies:
30-
genui: ^0.5.1 # Or the latest version
31-
genui_firebase_ai: ^0.5.1 # Or the latest version
49+
genui: ^0.5.1
50+
genui_firebase_ai: ^0.5.1
3251
```
3352
3453
5. In your app's `main` method, ensure that the widget bindings are initialized,
@@ -42,12 +61,51 @@ Logic, follow these instructions:
4261
}
4362
```
4463

45-
### Configure another agent provider
64+
### Configure with the A2UI protocol
65+
66+
To use `genui` with a generic agent provider that supports the A2UI protocol,
67+
use the `genui_a2ui` package.
68+
69+
1. In `pubspec.yaml`, add `genui` and `genui_a2ui` to the `dependencies`
70+
section.
71+
72+
```yaml
73+
dependencies:
74+
genui: ^0.5.1
75+
genui_a2ui: ^0.5.1
76+
```
77+
78+
2. Use the `A2uiContentGenerator` to connect to your agent provider.
79+
80+
### Configure with Google Generative AI
81+
82+
To use `genui` with the Google Generative AI API, use the
83+
`genui_google_generative_ai` package.
84+
85+
1. In `pubspec.yaml`, add `genui` and `genui_google_generative_ai` to the
86+
`dependencies` section.
87+
88+
```yaml
89+
dependencies:
90+
genui: ^0.5.1
91+
genui_google_generative_ai: ^0.5.1
92+
```
93+
94+
2. Use the `GoogleGenerativeAiContentGenerator` to connect to the Google
95+
Generative AI API. You will need to provide your own API key.
96+
97+
### Connecting to a custom backend
98+
99+
There are two ways to connect to a custom backend.
100+
101+
1. Use the `genui_a2ui` package to connect to a server that implements the
102+
A2UI protocol. This is the recommended approach if you have an existing
103+
agent that you want to connect to `genui`.
46104

47-
To use `genui` with another agent provider, you need to follow that
48-
provider's instructions to configure your app, and then create your own subclass
49-
of `ContentGenerator` to connect to that provider. Use `FirebaseContentGenerator` as an example
50-
of how to do so.
105+
2. Implement your own `ContentGenerator`. This is a good choice if you have a
106+
custom backend that does not implement the A2UI protocol. This will require
107+
you to convert your UI data source into a stream of `A2uiMessage` messages
108+
that can be rendered by the Gen UI SDK.
51109

52110
## 2. Create the connection to an agent
53111

0 commit comments

Comments
 (0)