Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a24693d
feat(genui): support functions in prompts and verify rendering
gspencergoog May 14, 2026
4134cb3
feat(genui): refactor PromptBuilder to use spec schemas and async API
gspencergoog May 14, 2026
f9f9e21
Merge branch 'main' into add_prompt_functions
gspencergoog May 15, 2026
1affaab
fix(genui): remove duplicate 'component' in required list in prompt
gspencergoog May 15, 2026
cb629b6
feat(genui): use $refs in A2uiSchemas and add custom component test
gspencergoog May 15, 2026
fb4a577
Merge branch 'main' into add_prompt_functions
gspencergoog May 15, 2026
9c47110
Fix formatting
gspencergoog May 15, 2026
ab3593a
Merge branch 'main' into add_prompt_functions
gspencergoog May 15, 2026
b7bba17
fix(genui): resolve schema references in tests and fix async hangs in…
gspencergoog May 18, 2026
341789f
Fix copyright
gspencergoog May 18, 2026
6d21e7b
refactor(genui): use platform-agnostic URIs for schema reference reso…
gspencergoog May 18, 2026
5fe5754
fix(genui): use type check instead of unsafe type cast for required p…
gspencergoog May 18, 2026
6a4ad57
refactor(genui): compose enum constraints with DynamicString schema t…
gspencergoog May 18, 2026
8aff417
feat(genui): implement secure error boundaries to prevent system stac…
gspencergoog May 18, 2026
f5c991b
fix(genui): add theme, anyComponent, and anyFunction schemas to catal…
gspencergoog May 18, 2026
ea68855
refactor(genui): extract duplicated schema asset loading into a stati…
gspencergoog May 18, 2026
c6858ab
refactor(genui): centralize A2UI schema URIs, asset keys, and local p…
gspencergoog May 18, 2026
fc669bd
Merge remote-tracking branch 'upstream/main' into add_prompt_functions
gspencergoog May 18, 2026
f27595c
refactor(genui): link schema assets to root submodule and support agn…
gspencergoog May 18, 2026
0520535
chore(genui): remove obsolete nested submodule path exclusion from an…
gspencergoog May 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev_tools/composer/lib/create_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class _CreateTabState extends State<CreateTab> {
transport: transport,
);

final promptBuilder = PromptBuilder.chat(
final promptBuilder = await PromptBuilder.createChat(
catalog: catalog,
systemPromptFragments: [
'You are a UI generator. The user will describe a UI they want. '
Expand Down
34 changes: 17 additions & 17 deletions examples/simple_chat/lib/chat_session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,20 @@ final Catalog _customCatalog = _basicCatalog.copyWith(
newItems: [climbingLocationItem],
);

PromptBuilder _promptBuilderFor(Catalog catalog) => PromptBuilder.chat(
catalog: catalog,
systemPromptFragments: [
Prompts.summary,
PromptFragments.acknowledgeUser(),
PromptFragments.requireAtLeastOneSubmitElement(
prefix: PromptBuilder.defaultImportancePrefix,
),
PromptFragments.uiGenerationRestriction(
prefix: PromptBuilder.defaultImportancePrefix,
),
],
);
Future<PromptBuilder> _promptBuilderFor(Catalog catalog) async =>
await PromptBuilder.createChat(
catalog: catalog,
systemPromptFragments: [
Prompts.summary,
PromptFragments.acknowledgeUser(),
PromptFragments.requireAtLeastOneSubmitElement(
prefix: PromptBuilder.defaultImportancePrefix,
),
PromptFragments.uiGenerationRestriction(
prefix: PromptBuilder.defaultImportancePrefix,
),
],
);

sealed class ChatSession extends ChangeNotifier {
ChatSession._();
Expand Down Expand Up @@ -188,7 +189,7 @@ class A2uiChatSession extends ChatSession {
late final StreamSubscription<ChatMessage> _submitSub;
late final StreamSubscription<SurfaceUpdate> _surfaceSub;

void _init() {
Future<void> _init() async {
_messageSub = _transport.incomingMessages.listen(
_surfaceController.handleMessage,
);
Expand All @@ -198,9 +199,8 @@ class A2uiChatSession extends ChatSession {
);
_surfaceSub = _surfaceController.surfaceUpdates.listen(_onSurfaceUpdate);

_transport.addSystemMessage(
_promptBuilderFor(_catalog).systemPromptJoined(),
);
final PromptBuilder pb = await _promptBuilderFor(_catalog);
_transport.addSystemMessage(pb.systemPromptJoined());
}

void _onSurfaceUpdate(SurfaceUpdate update) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)

set(PLUGIN_BUNDLED_LIBRARIES)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin"))
}
1 change: 1 addition & 0 deletions packages/genui/assets/schemas/common_types.json
1 change: 1 addition & 0 deletions packages/genui/assets/schemas/server_to_client.json
29 changes: 28 additions & 1 deletion packages/genui/lib/src/catalog/basic_catalog_widgets/button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import 'package:json_schema_builder/json_schema_builder.dart';

import '../../model/a2ui_exceptions.dart';
import '../../model/a2ui_schemas.dart';
import '../../model/catalog_item.dart';
import '../../model/data_model.dart';
Expand Down Expand Up @@ -233,7 +234,33 @@ Future<void> _handlePress(
try {
await resultStream.first;
} catch (exception, stackTrace) {
itemContext.reportError(exception, stackTrace);
genUiLogger.severe(
'Error executing function call "$callName" on button press',
exception,
stackTrace,
);

if (exception is A2uiFunctionException) {
itemContext.reportError(exception, stackTrace);
} else if (exception is ArgumentError) {
itemContext.reportError(
A2uiFunctionException(
exception.message.toString(),
functionName: callName,
cause: exception,
),
stackTrace,
);
Comment thread
gspencergoog marked this conversation as resolved.
} else {
itemContext.reportError(
A2uiFunctionException(
'Function execution failed. Please check arguments and try again.',
functionName: callName,
cause: exception,
),
stackTrace,
);
}
}
} else {
genUiLogger.warning(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,7 @@ final dateTimeInput = CatalogItem(
{
"id": "root",
"component": "DateTimeInput",
"value": {
"path": "/myDateTime"
}
"value": "2026-05-15"
}
]
''',
Expand All @@ -354,7 +352,7 @@ final dateTimeInput = CatalogItem(
"value": {
"path": "/myDate"
},
"enableTime": false
"variant": "date"
}
]
''',
Expand All @@ -366,7 +364,7 @@ final dateTimeInput = CatalogItem(
"value": {
"path": "/myTime"
},
"enableDate": false
"variant": "time"
}
]
''',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ final CatalogItem image = CatalogItem(
{
"id": "root",
"component": "Image",
"url": "https://storage.googleapis.com/cms-storage-bucket/lockup_flutter_horizontal.c823e53b3a1a7b0d36a9.png",
"url": {
"path": "/imageUrl"
},
"variant": "mediumFeature"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ final text = CatalogItem(
{
"id": "root",
"component": "Text",
"text": "Hello World",
"variant": "h1"
"text": "Hello World"
}
]
''',
Expand Down
25 changes: 17 additions & 8 deletions packages/genui/lib/src/engine/surface_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '../interfaces/a2ui_message_sink.dart';
import '../interfaces/surface_context.dart';
import '../interfaces/surface_host.dart';
import '../model/a2ui_client_capabilities.dart';
import '../model/a2ui_exceptions.dart';
import '../model/a2ui_message.dart';
import '../model/catalog.dart';
import '../model/chat_message.dart';
Expand Down Expand Up @@ -120,16 +121,21 @@ interface class SurfaceController implements SurfaceHost, A2uiMessageSink {

/// Reports an error to the AI service.
void reportError(Object error, StackTrace? stack) {
var errorCode = 'RUNTIME_ERROR';
var message = error.toString();
var errorCode = 'INTERNAL_ERROR';
var message = 'An unexpected system error occurred.';
String? surfaceId;
String? path;
String? functionName;

if (error is A2uiValidationException) {
errorCode = 'VALIDATION_FAILED';
message = error.message;
surfaceId = error.surfaceId;
path = error.path;
} else if (error is A2uiFunctionException) {
errorCode = 'FUNCTION_EXECUTION_FAILED';
message = error.message;
functionName = error.functionName;
}

final Map<String, Object> errorMsg = {
Expand All @@ -138,15 +144,18 @@ interface class SurfaceController implements SurfaceHost, A2uiMessageSink {
'code': errorCode,
'surfaceId': ?surfaceId,
'path': ?path,
'functionName': ?functionName,
'message': message,
},
};
_onSubmit.add(
ChatMessage.user(
'',
parts: [UiInteractionPart.create(jsonEncode(errorMsg))],
),
);
if (!_onSubmit.isClosed) {
_onSubmit.add(
ChatMessage.user(
'',
parts: [UiInteractionPart.create(jsonEncode(errorMsg))],
),
);
}
Comment thread
gspencergoog marked this conversation as resolved.
}

void _handleMessageInternal(A2uiMessage message) {
Expand Down
Loading
Loading