Skip to content
13 changes: 13 additions & 0 deletions packages/genui/lib/src/facade/prompt_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,19 @@ final class _BasicPromptBuilder extends PromptBuilder {
...catalog.systemPromptFragments,
...allowedOperations.systemPromptFragments,
_fenced(a2uiSchema, sectionName: 'A2UI JSON SCHEMA'),
if (catalog.functions.isNotEmpty)
_fenced(
const JsonEncoder.withIndent(' ').convert([
for (final func in catalog.functions)
{
'name': func.name,
'description': func.description,
'parameters': func.argumentSchema.value,
'returnType': func.returnType.value,
},
]),
sectionName: 'AVAILABLE FUNCTIONS',
),
Comment thread
gspencergoog marked this conversation as resolved.
Outdated
Comment thread
gspencergoog marked this conversation as resolved.
Outdated
?_encodedDataModel(clientDataModel),
];

Expand Down
85 changes: 85 additions & 0 deletions packages/genui/test/catalog/functions_rendering_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2025 The Flutter Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:genui/genui.dart';

void main() {
late SurfaceController controller;
final testCatalog = Catalog(
[BasicCatalogItems.text, BasicCatalogItems.column],
functions: BasicFunctions.all,
catalogId: 'test_catalog',
);

setUp(() {
controller = SurfaceController(catalogs: [testCatalog]);
});

tearDown(() {
controller.dispose();
});

testWidgets('Surface renders function output correctly', (
WidgetTester tester,
) async {
const surfaceId = 'testSurface';

// 1. Create surface
controller.handleMessage(
const CreateSurface(surfaceId: surfaceId, catalogId: 'test_catalog'),
);

// 2. Update data model
controller.handleMessage(
const UpdateDataModel(
surfaceId: surfaceId,
path: DataPath.root,
value: {'count': 2},
),
);

// 3. Update components with a function call
final components = [
const Component(
id: 'root',
type: 'Column',
properties: {
'children': ['cartSummaryText'],
},
),
const Component(
id: 'cartSummaryText',
type: 'Text',
properties: {
'text': {
'call': 'pluralize',
'args': {
'count': {'path': '/count'},
'zero': 'No items',
'one': 'One item',
'other': 'Multiple items',
},
'returnType': 'string',
},
},
),
];

controller.handleMessage(
UpdateComponents(surfaceId: surfaceId, components: components),
);

await tester.pumpWidget(
MaterialApp(
home: Surface(surfaceContext: controller.contextFor(surfaceId)),
),
);
await tester.pumpAndSettle();

// We expect "Multiple items" because count is 2.
expect(find.text('Multiple items'), findsOneWidget);
});
}
24 changes: 24 additions & 0 deletions packages/genui/test/facade/prompt_builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,28 @@ void main() {
});
}
});

group('Prompt with functions', () {
test('includes functions section when catalog has functions', () {
final catalogWithFunctions = Catalog(
[BasicCatalogItems.text],
functions: [BasicFunctions.pluralizeFunction],
catalogId: 'test_catalog',
);

final String prompt = PromptBuilder.chat(
catalog: catalogWithFunctions,
).systemPromptJoined();

expect(prompt, contains('AVAILABLE_FUNCTIONS'));
expect(prompt, contains('pluralize'));
expect(
prompt,
contains(
'Returns a localized string based on the Common Locale Data '
'Repository',
),
);
});
});
}
Loading