Skip to content

Feat/incremental catalog loading#949

Draft
leoafarias wants to merge 2 commits into
flutter:mainfrom
leoafarias:feat/incremental-catalog-loading
Draft

Feat/incremental catalog loading#949
leoafarias wants to merge 2 commits into
flutter:mainfrom
leoafarias:feat/incremental-catalog-loading

Conversation

@leoafarias
Copy link
Copy Markdown

@leoafarias leoafarias commented Jun 1, 2026

Issue: #946

Description

Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.

List which issues are fixed by this PR. For larger changes, raising an issue first helps reduce redundant work.

Pre-launch Checklist

  • I read the Flutter Style Guide recently, and have followed its advice.
  • I signed the CLA.
  • I read the Contributors Guide.
  • I have added sample code updates to the changelog.
  • I updated/added relevant documentation (doc comments with ///).
  • If my PR is a fork PR, I've checked that [e2e tests] passed.

If you need help, consider asking for advice on the #hackers-devrel channel on Discord.

Introduces CatalogContext to support incremental catalog building in the
facade, and updates PromptBuilder to consume it. Includes tests and golden
output for the new incremental loading behavior.
The removed test asserted the manifest excludes schema/examples/properties keys, which is already guaranteed by the adjacent test pinning the keys to exactly name+description. Also trims comment noise in the incremental catalog prompt assembly.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Package publishing

Package Version Status Publish tag (post-merge)
package:a2ui_core 0.0.1-dev002 already published at pub.dev
package:genai_primitives 0.2.4-dev001 ready to publish genai_primitives-v0.2.4-dev001
package:genui 0.9.1 ready to publish genui-v0.9.1
package:genui_a2a 0.9.0 already published at pub.dev
package:json_schema_builder 0.1.4 ready to publish json_schema_builder-v0.1.4

Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an incremental catalog prompt mode to optimize system prompts for LLMs. It adds CatalogContext to manage a compact catalog manifest and load detailed component schemas and examples on demand via a new loadCatalogItems tool. It also updates PromptBuilder to support both full-schema and incremental modes, adjusting the system prompt structure and tool policies accordingly. The reviewer's feedback suggests improving robustness by supporting case-insensitive catalog item lookups, enhancing error handling during example JSON decoding to provide clearer debugging context, and adding corresponding unit tests.

Comment on lines +179 to +190
final Map<String, CatalogItem> byName = {
for (final item in catalog.items) item.name: item,
};

final seen = <String>{};
final details = <CatalogItemDetails>[];
for (final name in names) {
if (!seen.add(name)) continue;
final CatalogItem? item = byName[name];
if (item == null) {
throw CatalogItemNotFoundException(name, catalogId: catalog.catalogId);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make the catalog item loading more robust against minor casing variations from the LLM (e.g., requesting card instead of Card), consider falling back to a case-insensitive lookup if an exact match is not found. This is a great defensive programming practice for LLM-facing APIs.

Suggested change
final Map<String, CatalogItem> byName = {
for (final item in catalog.items) item.name: item,
};
final seen = <String>{};
final details = <CatalogItemDetails>[];
for (final name in names) {
if (!seen.add(name)) continue;
final CatalogItem? item = byName[name];
if (item == null) {
throw CatalogItemNotFoundException(name, catalogId: catalog.catalogId);
}
final Map<String, CatalogItem> byName = {
for (final item in catalog.items) item.name: item,
};
final Map<String, CatalogItem> byNameLower = {
for (final item in catalog.items) item.name.toLowerCase(): item,
};
final seen = <String>{};
final details = <CatalogItemDetails>[];
for (final name in names) {
if (!seen.add(name)) continue;
final CatalogItem? item = byName[name] ?? byNameLower[name.toLowerCase()];
if (item == null) {
throw CatalogItemNotFoundException(name, catalogId: catalog.catalogId);
}

Comment on lines +247 to +251
static List<Object?> _examplesFor(CatalogItem item) {
return [
for (final buildExample in item.exampleData) jsonDecode(buildExample()),
];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If a developer-defined example contains invalid JSON, jsonDecode will throw a generic FormatException. Wrapping this exception to include the component's name will make debugging custom catalog items significantly easier.

  static List<Object?> _examplesFor(CatalogItem item) {
    final examples = <Object?>[];
    for (final buildExample in item.exampleData) {
      final json = buildExample();
      try {
        examples.add(jsonDecode(json));
      } on FormatException catch (e) {
        throw FormatException(
          'Failed to parse example JSON for component "${item.name}": ${e.message}',
          e.source,
          e.offset,
        );
      }
    }
    return examples;
  }

Comment on lines +125 to +135
test('preserves request order and removes duplicates', () {
final LoadCatalogItemsResult result = CatalogContext.loadItems(
catalog,
<String>['Button', 'Card', 'Button'],
);

expect(result.items.map((CatalogItemDetails e) => e.name), <String>[
'Button',
'Card',
]);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a unit test to verify that CatalogContext.loadItems successfully performs case-insensitive lookups and returns the correct casing in the result.

    test('preserves request order and removes duplicates', () {
      final LoadCatalogItemsResult result = CatalogContext.loadItems(
        catalog,
        <String>['Button', 'Card', 'Button'],
      );

      expect(result.items.map((CatalogItemDetails e) => e.name), <String>[
        'Button',
        'Card',
      ]);
    });

    test('supports case-insensitive lookup and returns correct casing', () {
      final LoadCatalogItemsResult result = CatalogContext.loadItems(
        catalog,
        <String>['card', 'TEXT'],
      );

      expect(result.items.map((CatalogItemDetails e) => e.name), <String>[
        'Card',
        'Text',
      ]);
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant