Skip to content

feat(a2ui): add catalog example, json valid, stream repair#2686

Merged
HuJean merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/agent-server-optimization
May 21, 2026
Merged

feat(a2ui): add catalog example, json valid, stream repair#2686
HuJean merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/agent-server-optimization

Conversation

@Sherry-hue
Copy link
Copy Markdown
Collaborator

@Sherry-hue Sherry-hue commented May 21, 2026

Summary by CodeRabbit

  • New Features

    • Added catalog examples demonstrating valid A2UI outputs.
    • Added automatic repair workflow that attempts to regenerate output when validation fails and reports repair status.
  • Improvements

    • Strengthened validation with richer schema checks, stricter component/property verification, and improved path coverage matching.
    • Better handling of children references and array/object validation.
  • Documentation

    • Updated A2UI v0.9 protocol wording for bound list children.

Review Change Stack

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 21, 2026

⚠️ No Changeset found

Latest commit: dc8fd2e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds catalog examples and per-prop JsonSchema to the A2UI catalog, renders catalog examples into the system prompt, implements catalog-driven component/schema validation with wildcard path matching, and adds a repair/regeneration workflow to SSE stream handlers that attempts to regenerate invalid model outputs.

Changes

A2UI Examples, Validation, and Repair Workflows

Layer / File(s) Summary
Package Configuration
packages/genui/server/.npmrc
Adds legacy-peer-deps=true flag with comments clarifying it only affects npm installs in this package directory to tolerate Next/React peer-dependency version conflicts.
Catalog Data Model and Examples
packages/genui/server/agent/a2ui-catalog.ts, packages/genui/server/agent/a2ui-examples.ts
Introduces A2UIExample interface and BASIC_CATALOG_EXAMPLES constant with four example scenarios; extends A2UIComponentProp with optional schema?: JsonSchema; exports JsonSchema and removes its prior examples member; adds examples?: A2UIExample[] to A2UICatalog, populates BASIC_CATALOG, and wires manifest prop schemas into generated component props.
Protocol Documentation and Dynamic Example Rendering
packages/genui/server/agent/a2ui-prompt.ts
Updates protocol overview to specify bound list children as children: { path, componentId } instead of template-wrapped form; removes static few-shot example; adds renderCatalogExamples(catalog) to dynamically generate "Validated examples" section from catalog examples and injects it into the system prompt.
Catalog-Driven Schema Validation
packages/genui/server/agent/a2ui-validator.ts
Imports A2UIComponentSpec and JsonSchema; tightens isRecord to exclude arrays; builds componentSpecs map for recognized components; replaces unknown-component path with validateComponentAgainstCatalog to enforce allowed keys and required props; adds isPathCovered with wildcard and segment-normalization support; updates collectChildRefs to handle both direct and template-wrapped componentId shapes; implements validateValueAgainstSchema and validateObjectAgainstSchema to recursively validate prop values, enums, oneOf variants, arrays, and objects with optional additionalProperties enforcement.
Action Stream Route Validation Repair
packages/genui/server/app/a2ui/action/stream/route.ts
After streaming finalization, introduces optional repair object to track validation repair attempts; runs configurable validation; on failure calls service.generateValidated(...) to regenerate output; emits repair SSE events with source errors and attempt metadata; on repair success updates finalText, usage, finishReason, and validation messages; includes repair metadata in final done event payload.
Main Stream Route Validation Repair
packages/genui/server/app/a2ui/stream/route.ts
After finalization, introduces optional repair state to track repair attempt metadata; validates non-empty finalText; on validation success resolves image URLs for validated messages; on failure calls service.generateValidated(...) for repair, emits repair SSE event, and conditionally overwrites finalText/usage/finishReason/validation messages on repair success or clears messages on failure; includes repair in final done event payload.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Possibly related PRs

  • lynx-family/lynx-stack#2667: Related changes to the A2UI action streaming endpoint; both touch the action SSE route and streaming behavior.
  • lynx-family/lynx-stack#2684: Overlaps in SSE stream handling and validated-message image URL resolution near the validation→messages transformation flow.
  • lynx-family/lynx-stack#2620: Prior A2UI server implementation changes that this PR builds upon (same modules adjusted: catalog, prompt, validator, stream routes).

Suggested reviewers

  • PupilTong
  • gaoachao
  • fzx2666-fz

Poem

🐰 I hopped through catalogs, schemas in paw,
I tucked examples into prompts with a law,
When outputs erred, I nudged them to try—
Repair, regenerate, then watch them fly!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the three main features added: catalog examples, JSON schema validation, and stream repair workflow functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/genui/server/agent/a2ui-validator.ts`:
- Around line 409-421: The isPathCovered function does pure prefix string checks
and thus misses wildcard segment semantics (e.g., referencedPath "/items/*/name"
should match providedPath "/items/0/name"); update isPathCovered to split
referencedPath and providedPath into path segments (normalize leading/trailing
slashes), then compare segment-by-segment: treat a segment equal if they are
identical or either segment is the wildcard "*" (match single segment), and
allow coverage when all segments match or when one path is a prefix of the other
with remaining segments being empty; use the function name isPathCovered and the
referencedPath/providedPath parameters to locate and replace the current logic.
- Around line 579-585: The object branch in the switch (case 'object') currently
uses isRecord(value) which treats arrays as records; change the check to
explicitly reject arrays (e.g., add an Array.isArray check) or update isRecord
to return false for arrays so arrays no longer pass type: "object" validation;
ensure the logic in the case 'object' (and subsequent call to
validateObjectAgainstSchema) only runs for true plain objects and not for
arrays.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ef72c936-4bf3-4a77-9e19-989b82ada49b

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6ff74 and 1d7284c.

📒 Files selected for processing (7)
  • packages/genui/server/.npmrc
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-examples.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts

Comment thread packages/genui/server/agent/a2ui-validator.ts
Comment thread packages/genui/server/agent/a2ui-validator.ts
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch from 1d7284c to 3794e14 Compare May 21, 2026 11:10
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/genui/server/app/a2ui/action/stream/route.ts (1)

142-150: 💤 Low value

Consider extracting shared repair type.

The repair object type is defined identically in both route files. Extracting to a shared type in _shared.ts would reduce duplication and ensure consistency.

// In _shared.ts
export interface RepairResult {
  attempted: true;
  sourceErrors: string[];
  ok: boolean;
  attempts: number;
}

Also applies to: 95-102

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/action/stream/route.ts` around lines 142 -
150, The inline union type for the local variable repair is duplicated and
should be extracted to a shared interface; create an exported interface
RepairResult (fields: attempted: true, sourceErrors: string[], ok: boolean,
attempts: number) in _shared.ts, import it into this route
(packages/genui/server/app/a2ui/action/stream/route.ts) and replace the local
inline type declaration for repair with RepairResult | undefined, and do the
same replacement in the other route file where the same type appears (lines
referenced 95-102) to remove duplication and ensure consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/genui/server/agent/a2ui-prompt.ts`:
- Line 74: Update the prompt text in a2ui-prompt.ts so the overview and the
example use the same canonical wording for list bindings: replace references to
a “template object” with the explicit shape children: { path, componentId } (or
explicitly document both allowed forms), and ensure the JSON example
("children": { "path": "/items", "componentId": "itemRow" }) and any description
of children-binding use identical terminology and field names so the model
receives a single, consistent spec.

---

Nitpick comments:
In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 142-150: The inline union type for the local variable repair is
duplicated and should be extracted to a shared interface; create an exported
interface RepairResult (fields: attempted: true, sourceErrors: string[], ok:
boolean, attempts: number) in _shared.ts, import it into this route
(packages/genui/server/app/a2ui/action/stream/route.ts) and replace the local
inline type declaration for repair with RepairResult | undefined, and do the
same replacement in the other route file where the same type appears (lines
referenced 95-102) to remove duplication and ensure consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ce19da09-4df3-464d-bba3-c81445da6658

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7284c and 3794e14.

📒 Files selected for processing (7)
  • packages/genui/server/.npmrc
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-examples.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/genui/server/.npmrc

Comment thread packages/genui/server/agent/a2ui-prompt.ts
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch from 3794e14 to e523a40 Compare May 21, 2026 11:37
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 176-205: Wrap the call to service.generateValidated inside a
try/catch so any exception during the repair step is handled as non-fatal: call
service.generateValidated(...) in a try block and build the repair object and
enqueue('repair', repair) on success as before; in the catch, set repair = {
attempted: true, sourceErrors: v.errors, ok: false, attempts: 0, error: <caught
error> } (or otherwise record the exception), enqueue('repair', repair), and
leave validation set to the original validation failure (i.e., keep validation =
{ ok: false, errors: v.errors, messages: [] }) without emitting an error or
closing the stream; ensure finalText/usage/finishReason/resolvedMessages remain
unchanged when repair fails or throws.

In `@packages/genui/server/app/a2ui/stream/route.ts`:
- Around line 123-151: The repair call to service.generateValidated inside the
try block must not be allowed to throw out and convert a finished SSE into an
error; wrap the generateValidated(...) call in its own try/catch so any
exception is handled locally: on success keep the existing logic (set repair,
enqueue('repair', repair), and update finalText/usage/finishReason/validation),
but on catch create a repair object marking attempted: true, ok: false,
sourceErrors: v.errors and include the caught error details (or a stringified
error) in repair.errors or attempts as appropriate, call enqueue('repair',
repair), set validation to reflect the failed repair (ok: false, errors: [ ...
]) and do not rethrow so finalText and the original generation result remain
returned and the stream can still emit done.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6e5cc69-d326-4d1e-b3e8-3264af0a7959

📥 Commits

Reviewing files that changed from the base of the PR and between 3794e14 and e523a40.

📒 Files selected for processing (8)
  • .github/lynx-stack-pr.instructions.md
  • packages/genui/server/.npmrc
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-examples.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/genui/server/.npmrc
  • .github/lynx-stack-pr.instructions.md

Comment thread packages/genui/server/app/a2ui/action/stream/route.ts Outdated
Comment thread packages/genui/server/app/a2ui/stream/route.ts Outdated
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch from e523a40 to 8dd5938 Compare May 21, 2026 11:48
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/genui/server/app/a2ui/stream/route.ts (1)

123-135: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep repair failures non-fatal.

If service.generateValidated(...) throws here, the outer catch turns a completed generation into an error event and skips done. The repair pass should be best-effort: handle its failure locally and preserve the original validation result in the final SSE payload.

💡 Minimal fix
           if (!v.ok) {
-            const repaired = await service.generateValidated(
-              messages,
-              opts,
-              validatedConversation.conversation,
-            );
-            repair = {
-              attempted: true,
-              sourceErrors: v.errors,
-              ok: repaired.ok,
-              attempts: repaired.attempts,
-            };
-            enqueue('repair', repair);
-            if (repaired.ok) {
-              finalText = repaired.text;
-              usage = repaired.usage;
-              finishReason = repaired.finishReason;
-              resolvedMessages = repaired.messages;
-              validation = {
-                ok: true,
-                errors: [],
-                messages: resolvedMessages,
-              };
-            } else {
-              validation = {
-                ok: false,
-                errors: repaired.errors,
-                messages: [],
-              };
-            }
+            try {
+              const repaired = await service.generateValidated(
+                messages,
+                opts,
+                validatedConversation.conversation,
+              );
+              repair = {
+                attempted: true,
+                sourceErrors: v.errors,
+                ok: repaired.ok,
+                attempts: repaired.attempts,
+              };
+              enqueue('repair', repair);
+              if (repaired.ok) {
+                finalText = repaired.text;
+                usage = repaired.usage;
+                finishReason = repaired.finishReason;
+                resolvedMessages = repaired.messages;
+                validation = {
+                  ok: true,
+                  errors: [],
+                  messages: resolvedMessages,
+                };
+              } else {
+                validation = {
+                  ok: false,
+                  errors: repaired.errors,
+                  messages: [],
+                };
+              }
+            } catch {
+              repair = {
+                attempted: true,
+                sourceErrors: v.errors,
+                ok: false,
+                attempts: 0,
+              };
+              enqueue('repair', repair);
+            }
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/stream/route.ts` around lines 123 - 135, Wrap
the call to service.generateValidated(...) in a try/catch so any exception is
handled locally and does not bubble out; on success set repair as you already
do, and on catch set repair to a non-fatal failure object (e.g., { attempted:
true, sourceErrors: v.errors, ok: false, attempts: [], error: err?.message ||
String(err) }) and still call enqueue('repair', repair); do not rethrow the
error so the original validatedConversation/result is preserved and the final
SSE will still emit done.
packages/genui/server/app/a2ui/action/stream/route.ts (1)

176-189: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep repair failures non-fatal.

If service.generateValidated(...) throws here, the outer catch emits error and the stream never sends done, even though the original generation already finished. Catch the repair attempt locally and fall back to the original validation result instead of aborting the SSE.

💡 Minimal fix
           if (!v.ok) {
-            const repaired = await service.generateValidated(
-              [userMessage],
-              opts,
-              validatedConversation.conversation,
-              validationOptions,
-            );
-            repair = {
-              attempted: true,
-              sourceErrors: v.errors,
-              ok: repaired.ok,
-              attempts: repaired.attempts,
-            };
-            enqueue('repair', repair);
-            if (repaired.ok) {
-              finalText = repaired.text;
-              usage = repaired.usage;
-              finishReason = repaired.finishReason;
-              resolvedMessages = repaired.messages;
-              validation = {
-                ok: true,
-                errors: [],
-                messages: resolvedMessages,
-              };
-            } else {
-              validation = {
-                ok: false,
-                errors: repaired.errors,
-                messages: [],
-              };
-            }
+            try {
+              const repaired = await service.generateValidated(
+                [userMessage],
+                opts,
+                validatedConversation.conversation,
+                validationOptions,
+              );
+              repair = {
+                attempted: true,
+                sourceErrors: v.errors,
+                ok: repaired.ok,
+                attempts: repaired.attempts,
+              };
+              enqueue('repair', repair);
+              if (repaired.ok) {
+                finalText = repaired.text;
+                usage = repaired.usage;
+                finishReason = repaired.finishReason;
+                resolvedMessages = repaired.messages;
+                validation = {
+                  ok: true,
+                  errors: [],
+                  messages: resolvedMessages,
+                };
+              } else {
+                validation = {
+                  ok: false,
+                  errors: repaired.errors,
+                  messages: [],
+                };
+              }
+            } catch {
+              repair = {
+                attempted: true,
+                sourceErrors: v.errors,
+                ok: false,
+                attempts: 0,
+              };
+              enqueue('repair', repair);
+            }
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/action/stream/route.ts` around lines 176 -
189, Wrap the call to service.generateValidated(...) (the block that sets repair
and calls enqueue('repair', repair)) in a local try/catch so any exception from
generateValidated is handled locally; on catch, set repair to a non-fatal
fallback (e.g. { attempted: true, sourceErrors: v.errors, ok: false, attempts:
[] } or similar), optionally log the caught error, and still call
enqueue('repair', repair) so the stream can finish and emit done instead of
letting the outer catch abort the SSE; target the code that references v,
userMessage, opts, validatedConversation, validationOptions, repair and enqueue.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/genui/server/agent/a2ui-validator.ts`:
- Around line 409-425: isPathCovered currently returns true for any matching
prefix; change it so a provided path only covers a referenced path when segments
themselves match (respecting '*' wildcards) and the provided path either has the
same length as the referenced path or explicitly ends with a '*' to indicate it
replaces the entire subtree. Concretely, inside isPathCovered (and using
normalizePathSegments) keep the existing per-segment comparison loop, but after
it finishes add checks: if providedSegments.length === referencedSegments.length
return true; if providedSegments.length < referencedSegments.length return true
only if the last segment of providedSegments === '*'; otherwise return false;
similarly if providedSegments.length > referencedSegments.length return false.
This ensures prefixes alone don’t imply subtree coverage.

---

Duplicate comments:
In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 176-189: Wrap the call to service.generateValidated(...) (the
block that sets repair and calls enqueue('repair', repair)) in a local try/catch
so any exception from generateValidated is handled locally; on catch, set repair
to a non-fatal fallback (e.g. { attempted: true, sourceErrors: v.errors, ok:
false, attempts: [] } or similar), optionally log the caught error, and still
call enqueue('repair', repair) so the stream can finish and emit done instead of
letting the outer catch abort the SSE; target the code that references v,
userMessage, opts, validatedConversation, validationOptions, repair and enqueue.

In `@packages/genui/server/app/a2ui/stream/route.ts`:
- Around line 123-135: Wrap the call to service.generateValidated(...) in a
try/catch so any exception is handled locally and does not bubble out; on
success set repair as you already do, and on catch set repair to a non-fatal
failure object (e.g., { attempted: true, sourceErrors: v.errors, ok: false,
attempts: [], error: err?.message || String(err) }) and still call
enqueue('repair', repair); do not rethrow the error so the original
validatedConversation/result is preserved and the final SSE will still emit
done.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 853ab9b3-58d9-456a-af52-5628d2938d31

📥 Commits

Reviewing files that changed from the base of the PR and between e523a40 and 8dd5938.

📒 Files selected for processing (7)
  • packages/genui/server/.npmrc
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-examples.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts

Comment thread packages/genui/server/agent/a2ui-validator.ts Outdated
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch from 8dd5938 to a655d94 Compare May 21, 2026 12:04
@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 21, 2026

React MTF Example

#1693 Bundle Size — 208.75KiB (0%).

dc8fd2e(current) vs 7e6ff74 main#1678(baseline)

Bundle metrics  no changes
                 Current
#1693
     Baseline
#1678
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 195 195
No change  Duplicate Modules 77 77
No change  Duplicate Code 44.17% 44.17%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1693
     Baseline
#1678
No change  IMG 111.23KiB 111.23KiB
No change  Other 97.52KiB 97.52KiB

Bundle analysis reportBranch Sherry-hue:feat/agent-server-opt...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 21, 2026

React External

#1675 Bundle Size — 698.01KiB (0%).

dc8fd2e(current) vs 7e6ff74 main#1660(baseline)

Bundle metrics  no changes
                 Current
#1675
     Baseline
#1660
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1675
     Baseline
#1660
No change  Other 698.01KiB 698.01KiB

Bundle analysis reportBranch Sherry-hue:feat/agent-server-opt...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 21, 2026

Web Explorer

#10134 Bundle Size — 903.53KiB (0%).

dc8fd2e(current) vs 7e6ff74 main#10119(baseline)

Bundle metrics  Change 2 changes
                 Current
#10134
     Baseline
#10119
No change  Initial JS 45.06KiB 45.06KiB
No change  Initial CSS 2.22KiB 2.22KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 9 9
No change  Assets 11 11
Change  Modules 231(+0.43%) 230
No change  Duplicate Modules 11 11
Change  Duplicate Code 27.12%(-0.04%) 27.13%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#10134
     Baseline
#10119
No change  JS 499.15KiB 499.15KiB
No change  Other 402.16KiB 402.16KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch Sherry-hue:feat/agent-server-opt...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 21, 2026

React Example

#8560 Bundle Size — 237.81KiB (0%).

dc8fd2e(current) vs 7e6ff74 main#8545(baseline)

Bundle metrics  no changes
                 Current
#8560
     Baseline
#8545
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 200 200
No change  Duplicate Modules 80 80
No change  Duplicate Code 44.68% 44.68%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#8560
     Baseline
#8545
No change  IMG 145.76KiB 145.76KiB
No change  Other 92.05KiB 92.05KiB

Bundle analysis reportBranch Sherry-hue:feat/agent-server-opt...Project dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/genui/server/app/a2ui/stream/route.ts (1)

111-124: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Repair empty responses as well.

This if (finalText) guard means an empty finalized response never reaches service.generateValidated(...); the route just returns validation.errors = ['no text produced']. That leaves a recoverable invalid-output case outside the new repair flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/stream/route.ts` around lines 111 - 124, The
current if (finalText) guard prevents empty finalized responses from entering
the repair flow; always run validateA2UIOutput on finalText (even when it's
empty), use the resulting v to populate validation and to await
resolveA2UIImageUrls(v.messages) (empty array if invalid), and ensure that when
v.ok is false you call service.generateValidated(...) (the same repair path used
for other invalid outputs) so empty outputs are repairable; adjust the code
around validateA2UIOutput, resolveA2UIImageUrls, and the
service.generateValidated invocation to remove the finalText-only shortcut and
always feed validation into the repair logic.
♻️ Duplicate comments (1)
packages/genui/server/agent/a2ui-validator.ts (1)

409-425: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Tighten path coverage semantics to avoid false positives.

At Line 425, returning true after only shared-prefix matching lets shallow updates (for example, "/status" or "/items") satisfy deeper bindings like "/status/message" or "/items/*/name" even when nested fields are not populated.

Suggested direction
 function isPathCovered(referencedPath: string, providedPath: string): boolean {
   const referencedSegments = normalizePathSegments(referencedPath);
   const providedSegments = normalizePathSegments(providedPath);
   const comparableLength = Math.min(
     referencedSegments.length,
     providedSegments.length,
   );

   for (let i = 0; i < comparableLength; i++) {
     const referenced = referencedSegments[i];
     const provided = providedSegments[i];
     if (referenced !== provided && referenced !== '*' && provided !== '*') {
       return false;
     }
   }

-  return true;
+  // Only treat as covered when provided path is at least as specific
+  // as the referenced path.
+  return providedSegments.length >= referencedSegments.length;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/agent/a2ui-validator.ts` around lines 409 - 425, The
isPathCovered function currently returns true as long as the shared prefix
matches, causing shallow paths like "/status" to incorrectly cover deeper
bindings; update isPathCovered (and keep using normalizePathSegments) so that
after the for-loop you also verify there are no unmatched non-wildcard segments
remaining: require that either both referencedSegments and providedSegments
lengths are equal, or any extra segments on one side are all '*' (i.e., check
referencedSegments.slice(comparableLength).every(s => s==='*') ||
providedSegments.slice(comparableLength).every(s => s==='*')); if not, return
false—this ensures deeper bindings like "/status/message" or "/items/*/name" are
not satisfied by shallower updates.
🧹 Nitpick comments (2)
packages/genui/server/app/a2ui/action/stream/route.ts (1)

192-200: ⚡ Quick win

Run repaired messages through the same image post-processing path.

The first-pass success branch resolves image URLs before filling validation.messages, but the repaired-success branch assigns repaired.messages directly. Keeping both branches on the same post-processing path avoids divergent SSE payloads.

♻️ Proposed fix
               if (repaired.ok) {
                 finalText = repaired.text;
                 usage = repaired.usage;
                 finishReason = repaired.finishReason;
-                resolvedMessages = repaired.messages;
+                resolvedMessages = await resolveA2UIImageUrls(
+                  repaired.messages,
+                );
                 validation = {
                   ok: true,
                   errors: [],
                   messages: resolvedMessages,
                 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/action/stream/route.ts` around lines 192 -
200, The repaired-success branch currently assigns repaired.messages directly to
validation.messages and resolvedMessages; instead, run repaired.messages through
the same image post-processing path used in the first-pass success branch (the
code that produced resolvedMessages) — process repaired.messages with that
image-resolution/post-processing function, then set resolvedMessages to the
processed array and set validation.messages to it so both branches emit the same
SSE payload shape.
packages/genui/server/app/a2ui/stream/route.ts (1)

138-146: ⚡ Quick win

Keep repaired messages on the same resolver path.

The non-repair success branch resolves image URLs before returning validation.messages, but the repaired-success branch skips that step. Reusing the same post-processing here keeps repaired and non-repaired payloads consistent.

♻️ Proposed fix
               if (repaired.ok) {
                 finalText = repaired.text;
                 usage = repaired.usage;
                 finishReason = repaired.finishReason;
-                resolvedMessages = repaired.messages;
+                resolvedMessages = await resolveA2UIImageUrls(
+                  repaired.messages,
+                );
                 validation = {
                   ok: true,
                   errors: [],
                   messages: resolvedMessages,
                 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/genui/server/app/a2ui/stream/route.ts` around lines 138 - 146, The
repaired-success branch (when repaired.ok is true) sets finalText, usage,
finishReason, resolvedMessages and validation but skips the image-URL
post-processing that the non-repair branch runs; update the repaired.ok path to
run the same image URL resolution step used elsewhere (i.e., call the same
resolver function used for the non-repair success branch to transform
validation.messages / resolvedMessages) before returning so repaired and
non-repaired payloads are consistent (refer to repaired.ok, repaired.text,
resolvedMessages, validation).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 159-177: The code currently skips validation and repair when
finalize() returns an empty string because of the if (finalText) guard; remove
or adjust that guard so service.generateValidated(...) and
validateA2UIOutput(...) are invoked even when finalText === '' (i.e., call the
same validation flow for empty finalText), ensuring the validation result v and
subsequent repair logic (including resolveA2UIImageUrls and the repair branch)
run for the empty-output case; update the block around finalText /
validateA2UIOutput / service.generateValidated to handle empty string inputs
rather than short-circuiting them.

---

Outside diff comments:
In `@packages/genui/server/app/a2ui/stream/route.ts`:
- Around line 111-124: The current if (finalText) guard prevents empty finalized
responses from entering the repair flow; always run validateA2UIOutput on
finalText (even when it's empty), use the resulting v to populate validation and
to await resolveA2UIImageUrls(v.messages) (empty array if invalid), and ensure
that when v.ok is false you call service.generateValidated(...) (the same repair
path used for other invalid outputs) so empty outputs are repairable; adjust the
code around validateA2UIOutput, resolveA2UIImageUrls, and the
service.generateValidated invocation to remove the finalText-only shortcut and
always feed validation into the repair logic.

---

Duplicate comments:
In `@packages/genui/server/agent/a2ui-validator.ts`:
- Around line 409-425: The isPathCovered function currently returns true as long
as the shared prefix matches, causing shallow paths like "/status" to
incorrectly cover deeper bindings; update isPathCovered (and keep using
normalizePathSegments) so that after the for-loop you also verify there are no
unmatched non-wildcard segments remaining: require that either both
referencedSegments and providedSegments lengths are equal, or any extra segments
on one side are all '*' (i.e., check
referencedSegments.slice(comparableLength).every(s => s==='*') ||
providedSegments.slice(comparableLength).every(s => s==='*')); if not, return
false—this ensures deeper bindings like "/status/message" or "/items/*/name" are
not satisfied by shallower updates.

---

Nitpick comments:
In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 192-200: The repaired-success branch currently assigns
repaired.messages directly to validation.messages and resolvedMessages; instead,
run repaired.messages through the same image post-processing path used in the
first-pass success branch (the code that produced resolvedMessages) — process
repaired.messages with that image-resolution/post-processing function, then set
resolvedMessages to the processed array and set validation.messages to it so
both branches emit the same SSE payload shape.

In `@packages/genui/server/app/a2ui/stream/route.ts`:
- Around line 138-146: The repaired-success branch (when repaired.ok is true)
sets finalText, usage, finishReason, resolvedMessages and validation but skips
the image-URL post-processing that the non-repair branch runs; update the
repaired.ok path to run the same image URL resolution step used elsewhere (i.e.,
call the same resolver function used for the non-repair success branch to
transform validation.messages / resolvedMessages) before returning so repaired
and non-repaired payloads are consistent (refer to repaired.ok, repaired.text,
resolvedMessages, validation).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b6f94c0-4857-43d5-81fb-9b57860adb9a

📥 Commits

Reviewing files that changed from the base of the PR and between 8dd5938 and a655d94.

📒 Files selected for processing (7)
  • packages/genui/server/.npmrc
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-examples.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts

Comment thread packages/genui/server/app/a2ui/action/stream/route.ts Outdated
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch 2 times, most recently from f998b6a to ab96b21 Compare May 21, 2026 12:18
@Sherry-hue Sherry-hue force-pushed the feat/agent-server-optimization branch from ab96b21 to dc8fd2e Compare May 21, 2026 12:27
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 21, 2026

Merging this PR will degrade performance by 14.91%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 80 untouched benchmarks
⏩ 26 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
transform 1000 view elements 40 ms 47 ms -14.91%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Sherry-hue:feat/agent-server-optimization (dc8fd2e) with main (7e6ff74)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 21, 2026

React Example with Element Template

#828 Bundle Size — 202.16KiB (0%).

dc8fd2e(current) vs 7e6ff74 main#814(baseline)

Bundle metrics  no changes
                 Current
#828
     Baseline
#814
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 100 100
No change  Duplicate Modules 30 30
No change  Duplicate Code 39.22% 39.22%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#828
     Baseline
#814
No change  IMG 145.76KiB 145.76KiB
No change  Other 56.41KiB 56.41KiB

Bundle analysis reportBranch Sherry-hue:feat/agent-server-opt...Project dashboard


Generated by RelativeCIDocumentationReport issue

@HuJean HuJean merged commit 0c8d6b4 into lynx-family:main May 21, 2026
52 of 53 checks passed
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.

2 participants