Feature/flutter structured output#382
Conversation
📝 WalkthroughWalkthroughAdds structured-output support across the Flutter SDK (types, FFI structs, Dart FFI bridge, handler, RunAnywhere wiring) plus a new Flutter example view; also applies minor Android sample UI icon/import tweaks. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Flutter App
participant SDK as RunAnywhere SDK
participant Bridge as DartBridgeStructuredOutput
participant Native as Native Layer
participant LLM as LLM Backend
App->>SDK: generate(prompt, structuredOutput)
SDK->>Bridge: getSystemPrompt(schema)
Bridge->>Native: rac_structured_output_get_system_prompt()
Native-->>Bridge: systemPrompt
Bridge-->>SDK: systemPrompt
SDK->>Bridge: preparePrompt(userPrompt, schema)
Bridge->>Native: rac_structured_output_prepare_prompt()
Native-->>Bridge: effectivePrompt
Bridge-->>SDK: effectivePrompt
SDK->>LLM: generate(effectivePrompt, systemPrompt)
LLM-->>SDK: rawText
SDK->>Bridge: extractJson(rawText)
Bridge->>Native: rac_structured_output_extract_json()
Native-->>Bridge: jsonString
Bridge-->>SDK: jsonString
SDK->>SDK: jsonDecode & normalize
SDK-->>App: LLMGenerationResult(text, structuredData)
sequenceDiagram
participant App as Flutter App
participant SDK as RunAnywhere SDK
participant Bridge as DartBridgeStructuredOutput
participant Native as Native Layer
participant LLM as LLM Backend
App->>SDK: generateStream(prompt, structuredOutput)
SDK->>Bridge: getSystemPrompt/preparePrompt
Bridge->>Native: rac_structured_output_get_system_prompt()/prepare_prompt()
Native-->>Bridge: systemPrompt / effectivePrompt
Bridge-->>SDK: systemPrompt / effectivePrompt
SDK->>LLM: stream(effectivePrompt)
LLM-->>SDK: Stream<tokens>
loop emit tokens
SDK-->>App: StreamToken
end
SDK->>Bridge: extractJson(accumulatedText)
Bridge->>Native: rac_structured_output_extract_json()
Native-->>Bridge: jsonString
Bridge-->>SDK: jsonString
SDK->>SDK: jsonDecode & normalize
SDK-->>App: Structured stream result + parsed structuredData
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed everything up to 7da2ec4 in 27 seconds. Click for details.
- Reviewed
1461lines of code in11files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_TODIlP83Ar7dx76C
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
Outdated
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
Outdated
Show resolved
Hide resolved
| final double? timeToFirstTokenMs; | ||
| final int thinkingTokens; | ||
| final int responseTokens; | ||
| final dynamic structuredData; |
There was a problem hiding this comment.
dynamic type for structuredData violates project guidelines
The CLAUDE.md states: "Always make sure that you're using structured types, never use strings directly so that we can keep things consistent and scalable and not make mistakes." Using dynamic here loses all type safety — callers can't know what shape the data has without runtime checks.
Consider using Map<String, dynamic>? or Object? at minimum, which at least communicates the expected shape and avoids accidental misuse.
Context Used: Context from dashboard - CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/generation_types.dart
Line: 50
Comment:
**`dynamic` type for `structuredData` violates project guidelines**
The CLAUDE.md states: "Always make sure that you're using structured types, never use strings directly so that we can keep things consistent and scalable and not make mistakes." Using `dynamic` here loses all type safety — callers can't know what shape the data has without runtime checks.
Consider using `Map<String, dynamic>?` or `Object?` at minimum, which at least communicates the expected shape and avoids accidental misuse.
**Context Used:** Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=20c5a1fb-ed57-4e08-840a-9128622c3bd6))
How can I resolve this? If you propose a fix, please make it concise.| import androidx.compose.material.icons.filled.* | ||
| import androidx.compose.material.icons.outlined.* |
There was a problem hiding this comment.
Redundant wildcard imports alongside specific imports
These wildcard imports (filled.*, outlined.*) make the specific named imports below them (e.g., filled.Analytics, filled.CleaningServices) redundant. Either use only the wildcard imports and remove the named ones, or remove the wildcards and keep only the specific imports (preferred for clarity and avoiding unused import warnings).
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/settings/SettingsScreen.kt
Line: 18-19
Comment:
**Redundant wildcard imports alongside specific imports**
These wildcard imports (`filled.*`, `outlined.*`) make the specific named imports below them (e.g., `filled.Analytics`, `filled.CleaningServices`) redundant. Either use only the wildcard imports and remove the named ones, or remove the wildcards and keep only the specific imports (preferred for clarity and avoiding unused import warnings).
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
This is not Something big but would be really Good if removed 😊
|
Hey @bagusindrayana Fix all the issue / bugs plz 😊 |
…structured_output_view.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…where.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…where.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…structured_output_view.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Siddhesh2377
left a comment
There was a problem hiding this comment.
@bagusindrayana
There is just a Micro Fix in sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/generation_types.dart
Line No : 50
dynamic type for structuredData violates project guidelines
The CLAUDE.md states: "Always make sure that you're using structured types, never use strings directly so that we can keep things consistent and scalable and not make mistakes." Using dynamic here loses all type safety — callers can't know what shape the data has without runtime checks.
Consider using Map<String, dynamic>? or Object? at minimum, which at least communicates the expected shape and avoids accidental misuse.
| final double? timeToFirstTokenMs; | ||
| final int thinkingTokens; | ||
| final int responseTokens; | ||
| final dynamic structuredData; |
| import androidx.compose.material.icons.filled.* | ||
| import androidx.compose.material.icons.outlined.* |
There was a problem hiding this comment.
This is not Something big but would be really Good if removed 😊
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed everything up to ad9638f in 20 seconds. Click for details.
- Reviewed
1565lines of code in11files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_dQdKhnag4susalgK
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
There was a problem hiding this comment.
Duplicate StructuredOutputConfig class conflicts with public API
This internal StructuredOutputConfig (line 288-313) duplicates the public version in structured_output_types.dart (line 11-34), but uses Type type instead of String typeName. The public API already exports this type via types.dart, so having two conflicting definitions breaks API consistency and will cause import ambiguity.
Remove this duplicate and use the public StructuredOutputConfig from structured_output_types.dart instead. Update this handler to work with typeName rather than type.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Line: 288-313
Comment:
Duplicate `StructuredOutputConfig` class conflicts with public API
This internal `StructuredOutputConfig` (line 288-313) duplicates the public version in `structured_output_types.dart` (line 11-34), but uses `Type type` instead of `String typeName`. The public API already exports this type via `types.dart`, so having two conflicting definitions breaks API consistency and will cause import ambiguity.
Remove this duplicate and use the public `StructuredOutputConfig` from `structured_output_types.dart` instead. Update this handler to work with `typeName` rather than `type`.
How can I resolve this? If you propose a fix, please make it concise.
...utter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Duplicate StructuredOutputValidation class
This class (line 245-257) also exists in structured_output_types.dart as StructuredOutputValidation (line 69-81). Remove this duplicate to avoid conflicts.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Line: 245-257
Comment:
Duplicate `StructuredOutputValidation` class
This class (line 245-257) also exists in `structured_output_types.dart` as `StructuredOutputValidation` (line 69-81). Remove this duplicate to avoid conflicts.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Duplicate StructuredOutputError class
This class (line 259-286) also exists in structured_output_types.dart (line 85-110). Remove this duplicate and use the public version instead.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Line: 259-286
Comment:
Duplicate `StructuredOutputError` class
This class (line 259-286) also exists in `structured_output_types.dart` (line 85-110). Remove this duplicate and use the public version instead.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Duplicate StructuredOutputStreamResult class
This class (line 315-328) also exists in structured_output_types.dart (line 52-67) with slightly different generic type (Stream<String> vs Stream<StreamToken>). Remove this duplicate and use the public version.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Line: 315-328
Comment:
Duplicate `StructuredOutputStreamResult` class
This class (line 315-328) also exists in `structured_output_types.dart` (line 52-67) with slightly different generic type (`Stream<String>` vs `Stream<StreamToken>`). Remove this duplicate and use the public version.
How can I resolve this? If you propose a fix, please make it concise.| if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) { | ||
| final jsonSubstring = trimmed.substring(startIndex, endIndex + 1); | ||
| try { | ||
| jsonDecode(jsonSubstring); |
There was a problem hiding this comment.
Consider explicit FormatException handling
jsonDecode here can throw FormatException. While the surrounding try-catch handles it, being explicit about catching FormatException improves clarity.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Line: 114
Comment:
Consider explicit `FormatException` handling
`jsonDecode` here can throw `FormatException`. While the surrounding try-catch handles it, being explicit about catching `FormatException` improves clarity.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| String? effectiveSystemPrompt = opts.systemPrompt; | ||
| if (opts.structuredOutput != null) { | ||
| final jsonSystemPrompt = DartBridgeStructuredOutput.shared.getSystemPrompt( | ||
| opts.structuredOutput!.schema, | ||
| ); | ||
| // If user already provided a system prompt, prepend the JSON instructions | ||
| if (effectiveSystemPrompt != null && effectiveSystemPrompt.isNotEmpty) { | ||
| effectiveSystemPrompt = '$jsonSystemPrompt\n\n$effectiveSystemPrompt'; | ||
| } else { | ||
| effectiveSystemPrompt = jsonSystemPrompt; | ||
| } | ||
| } |
There was a problem hiding this comment.
System prompt preparation bypasses the FFI bridge
When structuredOutput is provided, the code uses the handler's preparePrompt method instead of leveraging DartBridgeStructuredOutput.shared.preparePrompt (which calls the C++ implementation with fallback). Consider using the bridge method for consistency with the rest of the structured output implementation.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Line: 1192-1203
Comment:
System prompt preparation bypasses the FFI bridge
When `structuredOutput` is provided, the code uses the handler's `preparePrompt` method instead of leveraging `DartBridgeStructuredOutput.shared.preparePrompt` (which calls the C++ implementation with fallback). Consider using the bridge method for consistency with the rest of the structured output implementation.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
…structured_output/structured_output_handler.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…where.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…where.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart (2)
65-77:⚠️ Potential issue | 🟠 MajorMisleading prompt wording: "Convert this Data" / "to JSON Schema" implies schema generation, not data generation.
The intent is to generate JSON data that conforms to the schema, but the prompt says "Convert this Data ... to JSON Schema" which asks the model to produce a schema from data — the opposite of what's wanted. This likely degrades output quality, especially on smaller models (as the PR notes).
Suggested fix
return ''' System: You are a JSON generator. You must output only valid JSON. -Convert this Data : +Generate structured data for: $originalPrompt -to JSON Schema: -${config.schema} - +Following this JSON Schema: +${config.schema} + $instructions Remember: Output ONLY the JSON object, nothing else. ''';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart` around lines 65 - 77, The system prompt wording is misleading: it currently asks the model to "Convert this Data ... to JSON Schema" which requests schema generation instead of producing data; update the prompt returned (the string that uses originalPrompt, config.schema and instructions) to explicitly instruct the model to generate JSON data conforming to the provided schema (e.g., replace "Convert this Data ... to JSON Schema" with something like "Generate JSON data that conforms to the following JSON Schema" and keep the directive "Output ONLY the JSON object, nothing else."). Ensure the change is made where the prompt string is constructed so originalPrompt, config.schema and instructions are still interpolated and the final message enforces valid JSON-only output.
247-313:⚠️ Potential issue | 🟠 MajorRemove duplicate type definitions or rename handler-internal versions to avoid future ambiguity.
This file defines
StructuredOutputValidation,StructuredOutputError,StructuredOutputConfig, andStructuredOutputStreamResult— duplicates of those instructured_output_types.dart(exported viatypes.dart). The shapes differ: handler'sStructuredOutputConfig.typeisTypevs. public'stypeNameisString; handler's stream isStream<String>vs. public'sStream<StreamToken>.While no files currently import both, this duplication creates maintenance burden and risks future conflicts. Consider either using the public types throughout the handler or renaming the internal ones (e.g., with
Handlerprefix).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart` around lines 247 - 313, The handler file defines duplicate types (StructuredOutputValidation, StructuredOutputError, StructuredOutputConfig, StructuredOutputStreamResult) that conflict with the public types exported via types.dart; update the handler to either import and use the public types (replace StructuredOutputConfig.type: Type with the public config which uses typeName: String and switch the stream to Stream<StreamToken>) or rename the internal definitions (e.g., HandlerStructuredOutputConfig, HandlerStructuredOutputValidation, HandlerStructuredOutputError, HandlerStructuredOutputStreamResult) to avoid future ambiguity; ensure all references in this file (constructors, return types, method signatures) are updated to the chosen approach so there are no duplicate symbol names.
🧹 Nitpick comments (8)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ModelRequiredOverlay.kt (1)
25-30: Replace the wildcard import with a single explicitVisibilityimport.The wildcard on line 25 was added solely to bring in
Icons.Default.Visibility(used at line 253). This makes lines 26–30 redundant duplicates, and wildcard imports are generally flagged by Android Studio lint and make dependencies harder to trace.The simpler, idiomatic fix is to drop the wildcard and add only the missing icon import:
♻️ Proposed fix
-import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.AutoAwesome import androidx.compose.material.icons.filled.GraphicEq import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Mic +import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VolumeUp🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ModelRequiredOverlay.kt` around lines 25 - 30, Remove the wildcard import import androidx.compose.material.icons.filled.* and replace it with a single explicit import for Visibility (import androidx.compose.material.icons.filled.Visibility) so Icons.Default.Visibility resolves; keep the existing explicit imports (AutoAwesome, GraphicEq, Lock, Mic, VolumeUp) and ensure no other icons rely on the wildcard before committing.sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart (1)
1093-1108: Inconsistent class modifier:final classvsbase classused elsewhere in this file.All other
Structsubclasses in this file (e.g.,RacPlatformAdapterStruct,RacMemoryInfoStruct,RacLlmOptionsStruct, etc.) usebase class, but the two new structs usefinal class. While both are valid for FFI Structs, consider usingbase classfor consistency.Proposed fix
-final class RacStructuredOutputConfigStruct extends Struct { +base class RacStructuredOutputConfigStruct extends Struct { external Pointer<Utf8> jsonSchema; `@Int32`() external int includeSchemaInPrompt; } -final class RacStructuredOutputValidationStruct extends Struct { +base class RacStructuredOutputValidationStruct extends Struct { `@Int32`() external int isValid; external Pointer<Utf8> errorMessage; external Pointer<Utf8> extractedJson; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart` around lines 1093 - 1108, The two new FFI struct declarations use the `final class` modifier while existing structs use `base class`; update the declarations for RacStructuredOutputConfigStruct and RacStructuredOutputValidationStruct to use `base class` instead of `final class` so they match the rest of the file and maintain consistency for Struct subclasses.sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (3)
1191-1203: DRY:effectiveSystemPromptcomputation is duplicated betweengenerate()andgenerateStream().The exact same logic for computing the effective system prompt appears in both methods. Consider extracting a shared helper:
Suggested helper
static String? _effectiveSystemPrompt(LLMGenerationOptions opts) { String? prompt = opts.systemPrompt; if (opts.structuredOutput != null) { final jsonSystemPrompt = DartBridgeStructuredOutput.shared .getSystemPrompt(opts.structuredOutput!.schema); if (prompt != null && prompt.isNotEmpty) { prompt = '$jsonSystemPrompt\n\n$prompt'; } else { prompt = jsonSystemPrompt; } } return prompt; }Also applies to: 1318-1330
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1191 - 1203, Extract the duplicated effective system prompt logic into a shared private helper (e.g., static String? _effectiveSystemPrompt(LLMGenerationOptions opts)) and use it from both generate() and generateStream(); the helper should take opts.systemPrompt, check opts.structuredOutput, call DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema) when present, and prepend or replace the user prompt exactly as the current logic in effectiveSystemPrompt does so existing behavior of effectiveSystemPrompt in generate() and generateStream() is preserved.
1234-1246: Silentcatch (_)swallows all JSON extraction errors with no logging.When structured output is requested but extraction fails, the error is silently discarded. This makes debugging very difficult for SDK consumers. At minimum, log the failure at debug level so it's visible during development.
Suggested improvement
try { final jsonString = DartBridgeStructuredOutput.shared.extractJson(result.text); if (jsonString != null) { final parsed = jsonDecode(jsonString); structuredData = _normalizeStructuredData(parsed); } - } catch (_) { - // JSON extraction/parse failed — return text result without structured data + } catch (e) { + SDKLogger('RunAnywhere.Generate') + .debug('Structured output extraction failed: $e'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1234 - 1246, The current silent catch in the structured output extraction (within the block checking opts.structuredOutput and calling DartBridgeStructuredOutput.shared.extractJson(result.text)) swallows errors; change the catch from catch (_) to catch (e, st) and log a debug-level message including the error and stack trace plus context (e.g., result.text and the extracted jsonString) before continuing; ensure you still return the plain text result if parsing fails and keep the call to _normalizeStructuredData unchanged when parsing succeeds.
1408-1421: Same silentcatch (_)issue in the streaming path — apply the same logging fix here.Suggested improvement
- } catch (_) { - // JSON extraction/parse failed — return text result without structured data + } catch (e) { + SDKLogger('RunAnywhere.Generate') + .debug('Structured output extraction failed (stream): $e'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1408 - 1421, Silent catch is hiding JSON extraction/parsing errors in the streaming path; change the catch (_) block in the structured output handling (around structuredData, fullText, opts.structuredOutput and DartBridgeStructuredOutput.shared.extractJson) to catch (e, st) and log the error and stack (e.g., processLogger.error('Failed to extract/parse structured output: $e', error: e, stackTrace: st) or using the project's logger), then continue returning the plain text result; ensure you still don't rethrow so behavior is unchanged but errors are recorded, and keep using _normalizeStructuredData for the parsed payload.examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart (1)
584-651:ListViewwithshrinkWrap: trueinsideSingleChildScrollViewis a known anti-pattern.The
ListViewat line 584 withshrinkWrap: trueandNeverScrollableScrollPhysicsinside the parentSingleChildScrollView(line 300) forces eager layout of all children, negating the benefit ofListView's lazy building. For this demo view with limited items it's acceptable, but aColumnwould be more idiomatic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart` around lines 584 - 651, The ListView named in this widget (currently using shrinkWrap: true and NeverScrollableScrollPhysics) inside the existing SingleChildScrollView causes eager layout and is an anti-pattern; replace the ListView with a Column (or a SingleChildScrollView -> Padding -> Column pattern) and keep the same children rendering logic that references _rawResponse, _structuredData, _isGenerating and uses _buildSectionHeader so the UI and conditionals remain identical but without shrinkWrap/physics settings.sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/structured_output_types.dart (1)
112-134:StructuredOutputResult<T>is unused—consider removing or documenting its intended purpose.This generic result type has no consumers in the codebase. The generation paths (
generate()returnsFuture<LLMGenerationResult>,generateStream()returnsFuture<LLMStreamingResult>) both useLLMGenerationResultwith itsstructuredDatafield instead. If this type is planned for future use, add documentation clarifying the intent; otherwise, remove it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/structured_output_types.dart` around lines 112 - 134, The generic StructuredOutputResult<T> type is currently unused; either remove its declaration or document its intended purpose and planned consumers; specifically, if you plan to replace or augment existing returns from generate() / generateStream(), explain how StructuredOutputResult relates to LLMGenerationResult, LLMStreamingResult and their structuredData field, otherwise delete the StructuredOutputResult<T> class to avoid dead code and update any relevant exports or README references.sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart (1)
279-309:_fallbackValidatevalidates nothing but bracket boundaries; itstry/catchis dead code.The method labels itself "Simple JSON validation" but only checks whether the trimmed string starts/ends with
{/}or[/]. A string like{invalid: garbage}passes.dart:convert'sjsonDecodeis already available to the project and is the correct tool here. Additionally,String.trim(),startsWith(), andendsWith()are non-throwing operations, making the surroundingtry/catchunreachable dead code.♻️ Proposed refactor using jsonDecode for real validation
+import 'dart:convert'; StructuredOutputValidationResult _fallbackValidate(String text) { - try { - // Simple JSON validation - final trimmed = text.trim(); - if (trimmed.startsWith('{') && trimmed.endsWith('}')) { - return const StructuredOutputValidationResult( - isValid: true, - containsJSON: true, - error: null, - ); - } - if (trimmed.startsWith('[') && trimmed.endsWith(']')) { - return const StructuredOutputValidationResult( - isValid: true, - containsJSON: true, - error: null, - ); - } - return const StructuredOutputValidationResult( - isValid: false, - containsJSON: false, - error: 'No valid JSON found', - ); - } catch (e) { - return StructuredOutputValidationResult( - isValid: false, - containsJSON: false, - error: e.toString(), - ); - } + final extracted = _fallbackExtractJson(text); + if (extracted == null) { + return const StructuredOutputValidationResult( + isValid: false, + containsJSON: false, + error: 'No JSON found', + ); + } + try { + jsonDecode(extracted); + return const StructuredOutputValidationResult( + isValid: true, + containsJSON: true, + error: null, + ); + } catch (e) { + return StructuredOutputValidationResult( + isValid: false, + containsJSON: true, // JSON fragment was found, just not parseable + error: e.toString(), + ); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart` around lines 279 - 309, The _fallbackValidate function currently only checks bracket characters and never truly validates JSON (and its try/catch is dead); replace the bracket-only logic in _fallbackValidate with a real parse using dart:convert's jsonDecode on the trimmed input, return StructuredOutputValidationResult(isValid: true, containsJSON: true, error: null) when jsonDecode succeeds, and catch FormatException (or any thrown error) to return isValid: false, containsJSON: false, error: e.toString(); ensure you still trim the input before decoding and preserve the StructuredOutputValidationResult shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart`:
- Around line 248-273: The view misses early tokens because it subscribes to
streamResult.stream after generation has already emitted tokens to a broadcast
controller; update the producer (sdk.RunAnywhere.generateStream) to buffer
emitted tokens (the internal allTokens list used when collecting finalResult)
and, when creating the broadcast StreamController, replay buffered tokens to any
new listeners (e.g., in the controller's onListen or by returning a stream
wrapper that first emits allTokens then forwards live events) so that consumers
like _generateStream which await for streamResult.stream receive all prior
tokens as well as future tokens; adjust generateStream/streamResult.stream
behavior rather than changing the view subscription logic.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`:
- Around line 155-157: preparePrompt accepts includeSchemaInPrompt but the
fallback path (_fallbackPreparePrompt) ignores it and always inlines the schema;
update _fallbackPreparePrompt to accept a bool includeSchemaInPrompt parameter,
propagate that flag from both places where _fallbackPreparePrompt is called in
preparePrompt, and change the fallback logic inside _fallbackPreparePrompt to
conditionally embed the schema only when includeSchemaInPrompt is true
(otherwise return the prompt without inlined schema or with the schema excluded
according to existing C++ behavior). Ensure function/method signatures for
_fallbackPreparePrompt and its call sites (from preparePrompt) are updated
consistently.
- Around line 131-150: The current _fallbackExtractJson implementation uses
trimmed.indexOf('{') with trimmed.lastIndexOf('}') (and similarly for '['/']'),
which can capture trailing non-JSON text and produce invalid JSON; change it to
locate the first opener (use trimmed.indexOf on '{' then '[') and scan forward
tracking a depth counter (increment on opener, decrement on closer) until depth
returns to zero, then return trimmed.substring(startIndex, i+1); handle both
object ('{','}') and array ('[',']'), support nested brackets, and return null
if no matching closer is found.
- Around line 254-275: In validate(), free the C++-allocated strings returned in
RacStructuredOutputValidationStruct: after converting validation.errorMessage to
Dart via toDartString(), call rac_free on validation.errorMessage; read
validation.extractedJson into a Dart String (if not nullptr), set containsJSON
based on extractedJson != nullptr (not isValid), then call rac_free on
validation.extractedJson; only after consuming those C strings call
calloc.free(validationPtr) as you already do; keep the existing
exception/_fallbackValidate behavior unchanged.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/structured_output_types.dart`:
- Around line 36-67: The public duplicate types StreamToken and
StructuredOutputStreamResult<T> are shadowing the real implementations used
elsewhere (see stream_token.dart, stream_accumulator.dart,
structured_output_handler.dart, runanywhere.dart and generation_types.dart);
remove the redundant definitions from
lib/public/types/structured_output_types.dart and instead re-export or reference
the single source-of-truth types: export the StreamToken from
lib/features/llm/structured_output/stream_token.dart and use the same
StructuredOutputStreamResult<T>/LLMStreamingResult shape as defined in
generation_types.dart/structured_output_handler.dart (ensure the public API uses
Stream<String> if the implementation returns Stream<String>), updating any
callers or exports so all code imports the canonical type rather than the
duplicate.
---
Outside diff comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`:
- Around line 65-77: The system prompt wording is misleading: it currently asks
the model to "Convert this Data ... to JSON Schema" which requests schema
generation instead of producing data; update the prompt returned (the string
that uses originalPrompt, config.schema and instructions) to explicitly instruct
the model to generate JSON data conforming to the provided schema (e.g., replace
"Convert this Data ... to JSON Schema" with something like "Generate JSON data
that conforms to the following JSON Schema" and keep the directive "Output ONLY
the JSON object, nothing else."). Ensure the change is made where the prompt
string is constructed so originalPrompt, config.schema and instructions are
still interpolated and the final message enforces valid JSON-only output.
- Around line 247-313: The handler file defines duplicate types
(StructuredOutputValidation, StructuredOutputError, StructuredOutputConfig,
StructuredOutputStreamResult) that conflict with the public types exported via
types.dart; update the handler to either import and use the public types
(replace StructuredOutputConfig.type: Type with the public config which uses
typeName: String and switch the stream to Stream<StreamToken>) or rename the
internal definitions (e.g., HandlerStructuredOutputConfig,
HandlerStructuredOutputValidation, HandlerStructuredOutputError,
HandlerStructuredOutputStreamResult) to avoid future ambiguity; ensure all
references in this file (constructors, return types, method signatures) are
updated to the chosen approach so there are no duplicate symbol names.
---
Nitpick comments:
In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/components/ModelRequiredOverlay.kt`:
- Around line 25-30: Remove the wildcard import import
androidx.compose.material.icons.filled.* and replace it with a single explicit
import for Visibility (import androidx.compose.material.icons.filled.Visibility)
so Icons.Default.Visibility resolves; keep the existing explicit imports
(AutoAwesome, GraphicEq, Lock, Mic, VolumeUp) and ensure no other icons rely on
the wildcard before committing.
In
`@examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart`:
- Around line 584-651: The ListView named in this widget (currently using
shrinkWrap: true and NeverScrollableScrollPhysics) inside the existing
SingleChildScrollView causes eager layout and is an anti-pattern; replace the
ListView with a Column (or a SingleChildScrollView -> Padding -> Column pattern)
and keep the same children rendering logic that references _rawResponse,
_structuredData, _isGenerating and uses _buildSectionHeader so the UI and
conditionals remain identical but without shrinkWrap/physics settings.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`:
- Around line 279-309: The _fallbackValidate function currently only checks
bracket characters and never truly validates JSON (and its try/catch is dead);
replace the bracket-only logic in _fallbackValidate with a real parse using
dart:convert's jsonDecode on the trimmed input, return
StructuredOutputValidationResult(isValid: true, containsJSON: true, error: null)
when jsonDecode succeeds, and catch FormatException (or any thrown error) to
return isValid: false, containsJSON: false, error: e.toString(); ensure you
still trim the input before decoding and preserve the
StructuredOutputValidationResult shape.
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/ffi_types.dart`:
- Around line 1093-1108: The two new FFI struct declarations use the `final
class` modifier while existing structs use `base class`; update the declarations
for RacStructuredOutputConfigStruct and RacStructuredOutputValidationStruct to
use `base class` instead of `final class` so they match the rest of the file and
maintain consistency for Struct subclasses.
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1191-1203: Extract the duplicated effective system prompt logic
into a shared private helper (e.g., static String?
_effectiveSystemPrompt(LLMGenerationOptions opts)) and use it from both
generate() and generateStream(); the helper should take opts.systemPrompt, check
opts.structuredOutput, call
DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema)
when present, and prepend or replace the user prompt exactly as the current
logic in effectiveSystemPrompt does so existing behavior of
effectiveSystemPrompt in generate() and generateStream() is preserved.
- Around line 1234-1246: The current silent catch in the structured output
extraction (within the block checking opts.structuredOutput and calling
DartBridgeStructuredOutput.shared.extractJson(result.text)) swallows errors;
change the catch from catch (_) to catch (e, st) and log a debug-level message
including the error and stack trace plus context (e.g., result.text and the
extracted jsonString) before continuing; ensure you still return the plain text
result if parsing fails and keep the call to _normalizeStructuredData unchanged
when parsing succeeds.
- Around line 1408-1421: Silent catch is hiding JSON extraction/parsing errors
in the streaming path; change the catch (_) block in the structured output
handling (around structuredData, fullText, opts.structuredOutput and
DartBridgeStructuredOutput.shared.extractJson) to catch (e, st) and log the
error and stack (e.g., processLogger.error('Failed to extract/parse structured
output: $e', error: e, stackTrace: st) or using the project's logger), then
continue returning the plain text result; ensure you still don't rethrow so
behavior is unchanged but errors are recorded, and keep using
_normalizeStructuredData for the parsed payload.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/structured_output_types.dart`:
- Around line 112-134: The generic StructuredOutputResult<T> type is currently
unused; either remove its declaration or document its intended purpose and
planned consumers; specifically, if you plan to replace or augment existing
returns from generate() / generateStream(), explain how StructuredOutputResult
relates to LLMGenerationResult, LLMStreamingResult and their structuredData
field, otherwise delete the StructuredOutputResult<T> class to avoid dead code
and update any relevant exports or README references.
examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/structured_output_types.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (1)
1305-1312:⚠️ Potential issue | 🔴 CriticalCompile error: duplicate
modelIddeclaration and call to undefinedgetLoadedModel().Line 1305 introduces
final modelId = await getLoadedModel() ...insidegenerateStream(), then line 1312 re-declaresfinal modelId = DartBridge.llm.currentModelId ?? 'unknown'in the same scope. Dart does not permit duplicatefinaldeclarations in the same block — this will not compile.getLoadedModel()is also not defined inRunAnywhere.🐛 Remove the stray line 1305
DateTime? firstTokenTime; - final modelId = await getLoadedModel() ?? throw SDKError.notInitialized(); if (!DartBridge.llm.isLoaded) { throw SDKError.componentNotReady( 'LLM model not loaded. Call loadModel() first.', ); } final modelId = DartBridge.llm.currentModelId ?? 'unknown';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1305 - 1312, In generateStream(), remove the stray undefined/duplicate declaration "final modelId = await getLoadedModel() ?? throw SDKError.notInitialized();" and rely on the existing DartBridge.llm check and "final modelId = DartBridge.llm.currentModelId ?? 'unknown'"; ensure the SDKError.componentNotReady check remains intact (keep the isLoaded guard and throw SDKError.componentNotReady if not), and delete any remaining references to getLoadedModel() so there are no duplicate modelId declarations or calls to an undefined function.
🧹 Nitpick comments (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (1)
1191-1203: Duplicate system-prompt building logic; extract to a private helper.Lines 1191–1203 and 1318–1330 are byte-for-byte identical. Extracting this into a private method eliminates the duplication and single-sources any future prompt-ordering changes.
♻️ Proposed refactor
+ /// Builds the effective system prompt by prepending JSON schema instructions + /// when [structuredOutput] is provided. + static String? _buildEffectiveSystemPrompt(LLMGenerationOptions opts) { + if (opts.structuredOutput == null) return opts.systemPrompt; + final jsonSystemPrompt = + DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema); + final userPrompt = opts.systemPrompt; + if (userPrompt != null && userPrompt.isNotEmpty) { + return '$jsonSystemPrompt\n\n$userPrompt'; + } + return jsonSystemPrompt; + }Then in both
generate()andgenerateStream()replace the duplicated block with:- String? effectiveSystemPrompt = opts.systemPrompt; - if (opts.structuredOutput != null) { - final jsonSystemPrompt = DartBridgeStructuredOutput.shared.getSystemPrompt( - opts.structuredOutput!.schema, - ); - if (effectiveSystemPrompt != null && effectiveSystemPrompt.isNotEmpty) { - effectiveSystemPrompt = '$jsonSystemPrompt\n\n$effectiveSystemPrompt'; - } else { - effectiveSystemPrompt = jsonSystemPrompt; - } - } + final effectiveSystemPrompt = _buildEffectiveSystemPrompt(opts);Also applies to: 1318-1330
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1191 - 1203, Extract the duplicated system-prompt assembly into a private helper (e.g. _buildEffectiveSystemPrompt) and call it from both generate() and generateStream(): move the logic that reads opts.systemPrompt, checks opts.structuredOutput, calls DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema) and prepends or sets the effective system prompt into the new helper; update both functions to replace their identical blocks with a call to this helper and use its return value as effectiveSystemPrompt so all prompt-ordering logic is single-sourced.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1237-1245: The current broad catch in the JSON extraction blocks
(around DartBridgeStructuredOutput.shared.extractJson, jsonDecode, and
_normalizeStructuredData) swallows all Throwables including Error subclasses;
narrow the catch to Exception (and specific parsing exceptions such as
FormatException) and log the caught exception before returning the plain text
result so developer-visible errors aren't suppressed; apply the same change to
the other occurrence (the block around lines 1412–1420) so both
extraction/parsing paths only handle expected runtime exceptions and record/log
the error details.
- Around line 1879-1881: The branch that handles "parsed is Map" currently does
an unsafe cast via Map<String, dynamic>.from(parsed) which throws a TypeError
for non-String keys; replace this with a defensive conversion that iterates the
entries of parsed (the branch where "parsed is Map" and the usage of Map<String,
dynamic>.from(parsed)), building a new Map<String, dynamic> and only adding
entries whose keys are String (casting values as dynamic), so non-String keys
are ignored or handled explicitly (or log/throw a clearer error) instead of
allowing Map<String, dynamic>.from to throw at runtime.
- Around line 1155-1157: Remove the stray executable statement "final modelId =
await getLoadedModel()..." that sits at class-body scope (between doc comments)
— move any needed model lookup into the appropriate method (e.g., inside the
generate(...) implementation) and delete the class-level statement; then fix
both usages of the undefined getLoadedModel() by replacing them with the
project’s actual API that returns the loaded model id (search for methods/fields
like loadedModel, getLoadedModelId, currentModelId, or loadModel/getModel) and
ensure modelId is declared only once in the method scope (remove the duplicate
modelId declaration at the other site and reuse the single local variable inside
the generate function).
---
Outside diff comments:
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1305-1312: In generateStream(), remove the stray
undefined/duplicate declaration "final modelId = await getLoadedModel() ?? throw
SDKError.notInitialized();" and rely on the existing DartBridge.llm check and
"final modelId = DartBridge.llm.currentModelId ?? 'unknown'"; ensure the
SDKError.componentNotReady check remains intact (keep the isLoaded guard and
throw SDKError.componentNotReady if not), and delete any remaining references to
getLoadedModel() so there are no duplicate modelId declarations or calls to an
undefined function.
---
Nitpick comments:
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1191-1203: Extract the duplicated system-prompt assembly into a
private helper (e.g. _buildEffectiveSystemPrompt) and call it from both
generate() and generateStream(): move the logic that reads opts.systemPrompt,
checks opts.structuredOutput, calls
DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema)
and prepends or sets the effective system prompt into the new helper; update
both functions to replace their identical blocks with a call to this helper and
use its return value as effectiveSystemPrompt so all prompt-ordering logic is
single-sourced.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Outdated
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
Show resolved
Hide resolved
There was a problem hiding this comment.
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)
sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart (1)
79-98:⚠️ Potential issue | 🟠 Major
StructuredOutputError.validationFailedis immediately swallowed and re-mapped toinvalidJSONTwo related issues in this method:
validationFailedis thrown and caught in the same try/catch:StructuredOutputError.validationFailed(...)is thrown inside thetryblock (lines 89–91), but the barecatch (e)on line 95 intercepts it and re-throws it asStructuredOutputError.invalidJSON. Callers that inspect the error type to distinguish a type-mismatch ("got array, expected object") from a malformed JSON string will always seeinvalidJSON, losing the semantic distinction entirely.
extractJSONis outside thetryblock: TheextractionFailederror thrown byextractJSON(line 82) escapes the catch guard, while all other failures are normalised toinvalidJSON. This asymmetry means callers must handle two different error shapes from a single method.🐛 Proposed fix: separate extraction from decode/validate, preserve error types
T parseStructuredOutput<T>( String text, T Function(Map<String, dynamic>) fromJson) { - final jsonString = extractJSON(text); - try { + final jsonString = extractJSON(text); // extractionFailed propagates as-is final jsonData = jsonDecode(jsonString); if (jsonData is! Map<String, dynamic>) { throw StructuredOutputError.validationFailed( 'Expected JSON object, got ${jsonData.runtimeType}', ); } return fromJson(jsonData); - } catch (e) { - throw StructuredOutputError.invalidJSON(e.toString()); + } on StructuredOutputError { + rethrow; // preserve validationFailed / extractionFailed + } catch (e) { + throw StructuredOutputError.invalidJSON(e.toString()); // FormatException from jsonDecode } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart` around lines 79 - 98, parseStructuredOutput currently swallows StructuredOutputError.validationFailed and lets extractJSON errors bypass the catch; fix by wrapping both extraction and decoding/validation in controlled try/catch logic in parseStructuredOutput: call extractJSON and jsonDecode inside a try, then if jsonData is not Map throw StructuredOutputError.validationFailed; in the catch, if the caught error is already a StructuredOutputError (e.g., validationFailed or extractionFailed) rethrow it unchanged, otherwise wrap non-StructuredOutput errors with StructuredOutputError.invalidJSON(e.toString()); this preserves extractionFailed/validationFailed types and only maps unexpected decode errors to invalidJSON.
🧹 Nitpick comments (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (1)
1191-1203: Extract the duplicatedeffectiveSystemPromptcomputation into a private helper.The identical 12-line block appears in both
generate()andgenerateStream(). A change to the system-prompt injection logic (e.g., separator, order, null handling) must be made in two places, making drift bugs likely.♻️ Proposed refactor
Add a private static helper (e.g., after
_inferFormat):+ static String? _buildEffectiveSystemPrompt(LLMGenerationOptions opts) { + if (opts.structuredOutput == null) return opts.systemPrompt; + final jsonSystemPrompt = + DartBridgeStructuredOutput.shared.getSystemPrompt( + opts.structuredOutput!.schema, + ); + final base = opts.systemPrompt; + if (base != null && base.isNotEmpty) { + return '$jsonSystemPrompt\n\n$base'; + } + return jsonSystemPrompt; + }Then replace both identical blocks in
generate()andgenerateStream():- // Determine effective system prompt - add JSON conversion instructions if structuredOutput is provided - String? effectiveSystemPrompt = opts.systemPrompt; - if (opts.structuredOutput != null) { - final jsonSystemPrompt = DartBridgeStructuredOutput.shared.getSystemPrompt( - opts.structuredOutput!.schema, - ); - // If user already provided a system prompt, prepend the JSON instructions - if (effectiveSystemPrompt != null && effectiveSystemPrompt.isNotEmpty) { - effectiveSystemPrompt = '$jsonSystemPrompt\n\n$effectiveSystemPrompt'; - } else { - effectiveSystemPrompt = jsonSystemPrompt; - } - } + final effectiveSystemPrompt = _buildEffectiveSystemPrompt(opts);Also applies to: 1318-1330
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 1191 - 1203, Duplicate logic computing effectiveSystemPrompt in generate() and generateStream() should be extracted into a private helper to avoid drift; add a static helper (e.g., _buildEffectiveSystemPrompt(LLMGenerationOptions opts)) near _inferFormat that returns opts.systemPrompt when opts.structuredOutput is null, otherwise calls DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema) and prepends it to a non-empty opts.systemPrompt with the current separator "\n\n" (preserving order and null/empty handling), then replace the duplicated blocks in generate() and generateStream() to call this helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`:
- Around line 66-71: The prompt text in structured_output_handler.dart is
ambiguous: change the template around originalPrompt and config.schema so it
asks for a JSON object that conforms to the given schema (not to "convert into a
JSON Schema") and remove the extra space after "Convert this Data". Update the
string that currently reads "Convert this Data :" and "to JSON Schema:" (used
with originalPrompt and ${config.schema}) to something like "Convert this data
into a JSON object that conforms to the following JSON Schema:" followed by
${config.schema}, ensuring the wording explicitly requests a JSON object that
validates against the schema.
---
Outside diff comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`:
- Around line 79-98: parseStructuredOutput currently swallows
StructuredOutputError.validationFailed and lets extractJSON errors bypass the
catch; fix by wrapping both extraction and decoding/validation in controlled
try/catch logic in parseStructuredOutput: call extractJSON and jsonDecode inside
a try, then if jsonData is not Map throw StructuredOutputError.validationFailed;
in the catch, if the caught error is already a StructuredOutputError (e.g.,
validationFailed or extractionFailed) rethrow it unchanged, otherwise wrap
non-StructuredOutput errors with
StructuredOutputError.invalidJSON(e.toString()); this preserves
extractionFailed/validationFailed types and only maps unexpected decode errors
to invalidJSON.
---
Duplicate comments:
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1237-1246: The current try/catch around
DartBridgeStructuredOutput.shared.extractJson and
jsonDecode/_normalizeStructuredData uses catch (_) which swallows Error
subclasses; change the handlers to only catch Exception (or catch (e, s) and
rethrow if e is Error) so that AssertionError/TypeError/etc. bubble up while
still handling recoverable exceptions and logging the parsing failure; update
both occurrences (the block around
DartBridgeStructuredOutput.shared.extractJson/jsonDecode and the other similar
block at the later range) and reference structuredData, jsonDecode, and
_normalizeStructuredData when making the change.
- Around line 1879-1881: The current conversion Map<String,
dynamic>.from(parsed) is unsafe because parsed may be Map<dynamic, dynamic> and
will throw a TypeError; replace it with a defensive conversion that builds a new
Map<String, dynamic> by iterating parsed's entries and producing String keys
(either by keeping only entries where key is already a String or converting each
key via key.toString()), e.g. construct a new Map and for each (k, v) put map[k
is String ? k : k.toString()] = v; update the code paths around the parsed
variable where Map<String, dynamic>.from(parsed) is used (same block as the
"else if (parsed is Map)" branch) so the helper no longer throws for
non-String-keyed maps.
---
Nitpick comments:
In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`:
- Around line 1191-1203: Duplicate logic computing effectiveSystemPrompt in
generate() and generateStream() should be extracted into a private helper to
avoid drift; add a static helper (e.g.,
_buildEffectiveSystemPrompt(LLMGenerationOptions opts)) near _inferFormat that
returns opts.systemPrompt when opts.structuredOutput is null, otherwise calls
DartBridgeStructuredOutput.shared.getSystemPrompt(opts.structuredOutput!.schema)
and prepends it to a non-empty opts.systemPrompt with the current separator
"\n\n" (preserving order and null/empty handling), then replace the duplicated
blocks in generate() and generateStream() to call this helper.
...utter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart (1)
277-282: RedundantextractedJson != nullptrcheck — prefer the already-computedcontainsJson.
containsJsonis computed at line 266 from the same pointer check; repeatingvalidation.extractedJson != nullptrat line 277 is redundant and less readable.♻️ Proposed simplification
- if (validation.extractedJson != nullptr) { + if (containsJson) { lib.lookupFunction<Void Function(Pointer<Void>), void Function(Pointer<Void>)>('rac_free')( validation.extractedJson.cast<Void>(), ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart` around lines 277 - 282, The code redundantly re-checks validation.extractedJson != nullptr before calling the FFI free function; instead use the previously computed containsJson boolean (from line where containsJson is set) to decide freeing. Update the rac_free invocation block to test containsJson rather than validation.extractedJson != nullptr so the free call (lib.lookupFunction<'rac_free'...>(validation.extractedJson.cast<Void>())) runs only when containsJson is true, keeping the pointer usage consistent and improving readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`:
- Around line 85-100: The catch-all currently wraps every non-FormatException
error as StructuredOutputError.invalidJSON, which hides
StructuredOutputError.validationFailed (and any StructuredOutputError from
fromJson); modify the try/catch in structured_output_handler.dart so that after
the on FormatException handler you either add a specific catch for
StructuredOutputError that rethrows it unchanged or, in the generic catch (e),
detect if e is a StructuredOutputError and rethrow e, otherwise wrap in
StructuredOutputError.invalidJSON (referencing jsonDecode(jsonString),
fromJson(jsonData), and StructuredOutputError). Ensure StructuredOutputError
instances are not converted to invalidJSON.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`:
- Around line 300-318: _fallbackValidate currently treats strings that merely
start/end with { } or [ ] as valid JSON causing false positives; update
_fallbackValidate to actually parse the text using jsonDecode (import
'dart:convert') within the try block, treat successful jsonDecode as isValid:
true and containsJSON: true, and catch FormatException (or any parse error) to
return isValid: false and containsJSON: false while preserving the error
message; remove the startsWith/endsWith heuristics and ensure the returned
StructuredOutputValidationResult uses the parse outcome and error details from
the caught exception.
- Around line 131-152: _fallbackExtractJson currently counts braces/brackets
even when they appear inside JSON string values, producing invalid truncated
JSON; update the implementation of _fallbackExtractJson to be string-aware by
tracking in-string state and escape characters (similar to
StructuredOutputHandler._findMatchingBrace) so that braces/brackets inside
quoted strings do not affect the depth counter, and after extracting a candidate
substring attempt a jsonDecode to validate it (return the decoded/valid string
or null on decode failure); also add import 'dart:convert'; at the top of the
file.
- Around line 334-345: Replace the duplicate internal class
StructuredOutputValidationResult with the existing public
StructuredOutputValidation type: remove the StructuredOutputValidationResult
declaration, import StructuredOutputValidation from
structured_output_types.dart, and update DartBridgeStructuredOutput.validate()
to return StructuredOutputValidation (ensuring its fields map directly to
isValid, containsJSON, error) so it matches
StructuredOutputHandler.validateStructuredOutput() and avoids duplicate
definitions.
---
Duplicate comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`:
- Around line 66-75: The prompt strings "Convert this data:" and "Use the
following JSON Schema:" in the prompt template (which references
$originalPrompt, ${config.schema}, and $instructions) have been corrected to ask
the model to output a JSON object; no code change is required—leave the prompt
template in structured_output_handler.dart (the prompt variable used by
StructuredOutputHandler) as-is and proceed with the approved changes.
---
Nitpick comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`:
- Around line 277-282: The code redundantly re-checks validation.extractedJson
!= nullptr before calling the FFI free function; instead use the previously
computed containsJson boolean (from line where containsJson is set) to decide
freeing. Update the rac_free invocation block to test containsJson rather than
validation.extractedJson != nullptr so the free call
(lib.lookupFunction<'rac_free'...>(validation.extractedJson.cast<Void>())) runs
only when containsJson is true, keeping the pointer usage consistent and
improving readability.
| try { | ||
| final jsonData = jsonDecode(jsonString); | ||
|
|
||
| if (jsonData is! Map<String, dynamic>) { | ||
| throw StructuredOutputError.validationFailed( | ||
| 'Expected JSON object, got ${jsonData.runtimeType}', | ||
| ); | ||
| } | ||
| if (jsonData is! Map<String, dynamic>) { | ||
| throw StructuredOutputError.validationFailed( | ||
| 'Expected JSON object, got ${jsonData.runtimeType}', | ||
| ); | ||
| } | ||
|
|
||
| return fromJson(jsonData); | ||
| return fromJson(jsonData); | ||
| } on FormatException catch (e) { | ||
| throw StructuredOutputError.invalidJSON( | ||
| 'Invalid JSON format: ${e.message}'); | ||
| } catch (e) { | ||
| throw StructuredOutputError.invalidJSON(e.toString()); | ||
| } |
There was a problem hiding this comment.
StructuredOutputError.validationFailed is incorrectly swallowed by the generic catch (e) and re-thrown as invalidJSON.
The StructuredOutputError.validationFailed thrown at line 89 is not a FormatException, so it bypasses on FormatException and is caught by the bare catch (e) at line 98, which unconditionally wraps it as StructuredOutputError.invalidJSON. Any caller distinguishing a type-mismatch failure (got JSON array, not object) from a parse failure will receive the wrong error type. The same problem applies to any StructuredOutputError thrown by the fromJson callback at line 94.
🐛 Proposed fix
} catch (e) {
+ if (e is StructuredOutputError) rethrow;
throw StructuredOutputError.invalidJSON(e.toString());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try { | |
| final jsonData = jsonDecode(jsonString); | |
| if (jsonData is! Map<String, dynamic>) { | |
| throw StructuredOutputError.validationFailed( | |
| 'Expected JSON object, got ${jsonData.runtimeType}', | |
| ); | |
| } | |
| if (jsonData is! Map<String, dynamic>) { | |
| throw StructuredOutputError.validationFailed( | |
| 'Expected JSON object, got ${jsonData.runtimeType}', | |
| ); | |
| } | |
| return fromJson(jsonData); | |
| return fromJson(jsonData); | |
| } on FormatException catch (e) { | |
| throw StructuredOutputError.invalidJSON( | |
| 'Invalid JSON format: ${e.message}'); | |
| } catch (e) { | |
| throw StructuredOutputError.invalidJSON(e.toString()); | |
| } | |
| try { | |
| final jsonData = jsonDecode(jsonString); | |
| if (jsonData is! Map<String, dynamic>) { | |
| throw StructuredOutputError.validationFailed( | |
| 'Expected JSON object, got ${jsonData.runtimeType}', | |
| ); | |
| } | |
| return fromJson(jsonData); | |
| } on FormatException catch (e) { | |
| throw StructuredOutputError.invalidJSON( | |
| 'Invalid JSON format: ${e.message}'); | |
| } catch (e) { | |
| if (e is StructuredOutputError) rethrow; | |
| throw StructuredOutputError.invalidJSON(e.toString()); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart`
around lines 85 - 100, The catch-all currently wraps every non-FormatException
error as StructuredOutputError.invalidJSON, which hides
StructuredOutputError.validationFailed (and any StructuredOutputError from
fromJson); modify the try/catch in structured_output_handler.dart so that after
the on FormatException handler you either add a specific catch for
StructuredOutputError that rethrows it unchanged or, in the generic catch (e),
detect if e is a StructuredOutputError and rethrow e, otherwise wrap in
StructuredOutputError.invalidJSON (referencing jsonDecode(jsonString),
fromJson(jsonData), and StructuredOutputError). Ensure StructuredOutputError
instances are not converted to invalidJSON.
| String? _fallbackExtractJson(String text) { | ||
| final trimmed = text.trim(); | ||
|
|
||
| for (final pair in [ | ||
| ('{', '}'), | ||
| ('[', ']'), | ||
| ]) { | ||
| final open = pair.$1; | ||
| final close = pair.$2; | ||
| final startIndex = trimmed.indexOf(open); | ||
| if (startIndex == -1) continue; | ||
|
|
||
| int depth = 0; | ||
| for (int i = startIndex; i < trimmed.length; i++) { | ||
| if (trimmed[i] == open) depth++; | ||
| if (trimmed[i] == close) depth--; | ||
| if (depth == 0) { | ||
| return trimmed.substring(startIndex, i + 1); | ||
| } | ||
| } | ||
| } | ||
| return null; |
There was a problem hiding this comment.
_fallbackExtractJson depth counter is confused by braces/brackets inside JSON string values.
A { or } within a quoted string (e.g., {"msg": "retry } failed"}) incorrectly increments/decrements depth, causing the method to return a truncated, invalid substring. Unlike StructuredOutputHandler._findMatchingBrace, this fallback has no string-quoting or escape tracking. Since the extracted substring is returned without a jsonDecode validation step, the caller silently receives invalid JSON.
🛡️ Proposed fix — add string-aware extraction
String? _fallbackExtractJson(String text) {
final trimmed = text.trim();
for (final pair in [
('{', '}'),
('[', ']'),
]) {
final open = pair.$1;
final close = pair.$2;
final startIndex = trimmed.indexOf(open);
if (startIndex == -1) continue;
int depth = 0;
+ bool inString = false;
+ bool escaped = false;
for (int i = startIndex; i < trimmed.length; i++) {
+ if (escaped) { escaped = false; continue; }
+ if (trimmed[i] == '\\' && inString) { escaped = true; continue; }
+ if (trimmed[i] == '"') { inString = !inString; continue; }
+ if (inString) continue;
if (trimmed[i] == open) depth++;
if (trimmed[i] == close) depth--;
if (depth == 0) {
- return trimmed.substring(startIndex, i + 1);
+ final candidate = trimmed.substring(startIndex, i + 1);
+ try {
+ jsonDecode(candidate);
+ return candidate;
+ } catch (_) {
+ break; // not valid JSON, try next opener type
+ }
}
}
}
return null;
}This requires adding import 'dart:convert'; at the top of the file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`
around lines 131 - 152, _fallbackExtractJson currently counts braces/brackets
even when they appear inside JSON string values, producing invalid truncated
JSON; update the implementation of _fallbackExtractJson to be string-aware by
tracking in-string state and escape characters (similar to
StructuredOutputHandler._findMatchingBrace) so that braces/brackets inside
quoted strings do not affect the depth counter, and after extracting a candidate
substring attempt a jsonDecode to validate it (return the decoded/valid string
or null on decode failure); also add import 'dart:convert'; at the top of the
file.
| /// Fallback validation when C++ fails | ||
| StructuredOutputValidationResult _fallbackValidate(String text) { | ||
| try { | ||
| // Simple JSON validation | ||
| final trimmed = text.trim(); | ||
| if (trimmed.startsWith('{') && trimmed.endsWith('}')) { | ||
| return const StructuredOutputValidationResult( | ||
| isValid: true, | ||
| containsJSON: true, | ||
| error: null, | ||
| ); | ||
| } | ||
| if (trimmed.startsWith('[') && trimmed.endsWith(']')) { | ||
| return const StructuredOutputValidationResult( | ||
| isValid: true, | ||
| containsJSON: true, | ||
| error: null, | ||
| ); | ||
| } |
There was a problem hiding this comment.
_fallbackValidate reports isValid: true without parsing — produces false positives for malformed JSON.
Checking startsWith('{') && endsWith('}') is not JSON validation. Input like {not valid json} passes all three positive-return branches and is flagged as both isValid: true and containsJSON: true. Downstream code that trusts this result to skip further parsing will silently consume corrupt output.
🛡️ Proposed fix — use jsonDecode for actual validation
StructuredOutputValidationResult _fallbackValidate(String text) {
try {
final trimmed = text.trim();
- if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
- return const StructuredOutputValidationResult(
- isValid: true,
- containsJSON: true,
- error: null,
- );
- }
- if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
- return const StructuredOutputValidationResult(
- isValid: true,
- containsJSON: true,
- error: null,
- );
- }
+ final extracted = _fallbackExtractJson(trimmed);
+ if (extracted != null) {
+ try {
+ jsonDecode(extracted); // validate with dart:convert
+ return const StructuredOutputValidationResult(
+ isValid: true,
+ containsJSON: true,
+ error: null,
+ );
+ } on FormatException catch (e) {
+ return StructuredOutputValidationResult(
+ isValid: false,
+ containsJSON: true, // JSON was found but malformed
+ error: e.message,
+ );
+ }
+ }
return const StructuredOutputValidationResult(
isValid: false,
containsJSON: false,
error: 'No valid JSON found',
);
} catch (e) {This requires adding import 'dart:convert'; at the top of the file.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`
around lines 300 - 318, _fallbackValidate currently treats strings that merely
start/end with { } or [ ] as valid JSON causing false positives; update
_fallbackValidate to actually parse the text using jsonDecode (import
'dart:convert') within the try block, treat successful jsonDecode as isValid:
true and containsJSON: true, and catch FormatException (or any parse error) to
return isValid: false and containsJSON: false while preserving the error
message; remove the startsWith/endsWith heuristics and ensure the returned
StructuredOutputValidationResult uses the parse outcome and error details from
the caught exception.
| /// Structured output validation result | ||
| class StructuredOutputValidationResult { | ||
| final bool isValid; | ||
| final bool containsJSON; | ||
| final String? error; | ||
|
|
||
| const StructuredOutputValidationResult({ | ||
| required this.isValid, | ||
| required this.containsJSON, | ||
| this.error, | ||
| }); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Compare StructuredOutputValidation fields (public type) vs StructuredOutputValidationResult (bridge type)
rg -n "class StructuredOutputValidation" --type=dart -A 10
rg -n "class StructuredOutputValidationResult" --type=dart -A 10Repository: RunanywhereAI/runanywhere-sdks
Length of output: 3908
🏁 Script executed:
#!/bin/bash
# Check if structured_output_types.dart is imported in dart_bridge_structured_output.dart
head -50 sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart | grep -E "^import|^export"
# Search for usage of StructuredOutputValidationResult
rg "StructuredOutputValidationResult" --type=dart
# Search for usage of StructuredOutputValidation (public type)
rg "StructuredOutputValidation[^R]" --type=dart | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 2944
🏁 Script executed:
#!/bin/bash
# Check if StructuredOutputValidationResult is exported from public APIs
rg "export.*StructuredOutputValidationResult" --type=dart
rg "StructuredOutputValidationResult" sdk/runanywhere-flutter/packages/runanywhere/lib/public/ --type=dart
# Check what is exported from the native bridge
cat sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart | grep -A 2 "^export "Repository: RunanywhereAI/runanywhere-sdks
Length of output: 56
🏁 Script executed:
#!/bin/bash
# Check the public API exports
cat sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart | head -100
# Check if DartBridgeStructuredOutput is part of public API
rg "DartBridgeStructuredOutput" --type=dart | grep -v "\.dart:" | head -10
rg "DartBridgeStructuredOutput" --type=dartRepository: RunanywhereAI/runanywhere-sdks
Length of output: 2618
Replace StructuredOutputValidationResult with the public type StructuredOutputValidation.
StructuredOutputValidationResult duplicates StructuredOutputValidation from structured_output_types.dart with identical field definitions (isValid, containsJSON, error). Since this class is internal to the bridge and not part of the public API, consolidating onto the public type would eliminate unnecessary duplication, ensure type consistency between DartBridgeStructuredOutput.validate() and StructuredOutputHandler.validateStructuredOutput(), and reduce maintenance burden. Import StructuredOutputValidation and use it as the return type instead of defining a parallel duplicate.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_structured_output.dart`
around lines 334 - 345, Replace the duplicate internal class
StructuredOutputValidationResult with the existing public
StructuredOutputValidation type: remove the StructuredOutputValidationResult
declaration, import StructuredOutputValidation from
structured_output_types.dart, and update DartBridgeStructuredOutput.validate()
to return StructuredOutputValidation (ensuring its fields map directly to
isValid, containsJSON, error) so it matches
StructuredOutputHandler.validateStructuredOutput() and avoids duplicate
definitions.
Siddhesh2377
left a comment
There was a problem hiding this comment.
Good Work @bagusindrayana 😊
* feat: implement structured output json for flutter * refactor: add return structuredData in result generate, fix some type in ffi and bride * refactor: add systemprompt from StructuredOutputHandler, impove system prompt * update: example for structured output json * Update examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * refactor: update structuredData type and fix layout in structured data example screen * fix: remove redundant import icons * Update sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix: remove duplicate class * fix: remove duplicate StreamToken class, fix memory leak issue, fix exception handle --------- Co-authored-by: Sanchit Monga <sanchitmonga22@gmail.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Description
Brief description of the changes made.
Type of Change
Testing
Platform-Specific Testing (check all that apply)
Swift SDK / iOS Sample:
Kotlin SDK / Android Sample:
Flutter SDK / Flutter Sample:
React Native SDK / React Native Sample:
Web SDK / Web Sample:
Labels
Please add the appropriate label(s):
SDKs:
Swift SDK- Changes to Swift SDK (sdk/runanywhere-swift)Kotlin SDK- Changes to Kotlin SDK (sdk/runanywhere-kotlin)React Native SDK- Changes to React Native SDK (sdk/runanywhere-react-native)Flutter SDK- Changes to Flutter SDK (sdk/runanywhere-flutter)Web SDK- Changes to Web SDK (sdk/runanywhere-web)Commons- Changes to shared native code (sdk/runanywhere-commons)Sample Apps:
iOS Sample- Changes to iOS example app (examples/ios)Android Sample- Changes to Android example app (examples/android)Flutter Sample- Changes to Flutter example app (examples/flutter)React Native Sample- Changes to React Native example app (examples/react-native)Web Sample- Changes to Web example app (examples/web)Checklist
Screenshots
Attach relevant UI screenshots for changes (if applicable):
2026-02-18.23-13-26.mp4
sample_stream_result_structured_json.mp4
need improvement for System Prompt to produce more consistent results, especially for small models.
Important
Adds structured output support to Flutter SDK with new types, FFI bindings, and a demo view, modifying core methods to handle JSON output.
generate()andgenerateStream()inrunanywhere.dart, injecting JSON system prompts and extracting JSON from text.dart_bridge_structured_output.dartfor C++ APIs with fallback logic.StructuredOutputConfig,StructuredOutputResult, etc., instructured_output_types.dart.LLMGenerationOptionsandLLMGenerationResultingeneration_types.dartto include structured output fields.StructuredOutputViewinstructured_output_view.dartfor demoing structured output with schema templates and prompt examples.StructuredOutputViewinchat_interface_view.dart.ScrollControllerinstructured_output_view.dartmay cause runtime crashes.jsonDecodecalls inrunanywhere.dartcan lead to errors.This description was created by
for ad9638f. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Style
Greptile Summary
Adds structured output functionality to the Flutter SDK, enabling JSON schema-based generation with FFI bindings to C++ APIs. The implementation includes type definitions, FFI bridge, handler logic, and a demo UI.
Key additions:
structured_output_types.dartforStructuredOutputConfig,StructuredOutputResult, and validationdart_bridge_structured_output.dartwith proper fallbacks and memory managementRunAnywhere.generate()andgenerateStream()methods with JSON extractionStructuredOutputViewwith recipe, user profile, weather, and product list examplesCritical issues found:
structured_output_handler.dart(StructuredOutputConfig,StructuredOutputValidation,StructuredOutputError,StructuredOutputStreamResult) conflict with public API types instructured_output_types.dart- these duplicates must be removed to prevent import ambiguity and API inconsistencymodelIdinrunanywhere.dart(lines 1156, 1305) could cause runtime errors ifgetLoadedModel()returns nulljsonDecodeexception instructured_output_handler.dart:86could throwFormatExceptionfor invalid JSONNotes:
runanywhere.dart- these remain unresolvedConfidence Score: 2/5
structured_output_handler.dartcreate API conflicts that will break imports and cause compile-time/runtime issues. Missing null checks onmodelIdcould crash the app. Unhandled JSON parsing exceptions could fail valid generations. These are blocking issues that must be fixed before merge.sdk/runanywhere-flutter/packages/runanywhere/lib/features/llm/structured_output/structured_output_handler.dart(remove duplicate classes) andsdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart(add null checks)Important Files Changed
StructuredOutputConfigclass conflicts with public types; JSON parsing lacks error handlingFlowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[User calls RunAnywhere.generate with structuredOutput config] --> B{structuredOutput provided?} B -->|Yes| C[StructuredOutputHandler prepares prompt with schema] B -->|No| D[Standard generation] C --> E[DartBridge.llm.generate calls C++ API] D --> E E --> F[LLM generates text response] F --> G{structuredOutput provided?} G -->|Yes| H[DartBridgeStructuredOutput.extractJson] G -->|No| I[Return text as-is] H --> J{JSON extraction successful?} J -->|Yes| K[jsonDecode parses JSON] J -->|No| L[Return text without structuredData] K --> M{Parse successful?} M -->|Yes| N[Return LLMGenerationResult with structuredData] M -->|No| L I --> O[Return LLMGenerationResult without structuredData] L --> O N --> P[User receives result] O --> P style H fill:#e1f5ff style C fill:#e1f5ff style N fill:#c8e6c9 style L fill:#fff9c4Last reviewed commit: ad9638f