[741] Create the lra-ai-dashboard quickstart#740
Conversation
📝 WalkthroughWalkthroughAdds a new Quarkus module providing an LLM-driven streaming chat endpoint that uses a LangChain4j assistant with coordinator-facing tools, synchronous coordinator HTTP calls, a browser chat UI, Maven/module configuration, and comprehensive README documentation. ChangesLRA AI Dashboard Feature
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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.
Actionable comments posted: 12
🤖 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 `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 180-182: Update the guardrail paragraph to list all mutating/write
tools so it matches runtime behavior: include startLRA and deleteFailedLRA
alongside closeLRA and cancelLRA in the sentence that describes write tools
being guarded; ensure the wording still states that the LLM will only call these
tools when the operator explicitly requests it and that the LLM will echo the
target LRA ID before acting (refer to symbols startLRA, deleteFailedLRA,
closeLRA, cancelLRA).
- Around line 164-166: The README text incorrectly states "Six `@Tool`-annotated
methods" and "single blocking HTTP GET" but the actual tool surface (LraTools)
contains more than six `@Tool` methods and includes mutating HTTP methods
(PUT/POST/DELETE), e.g. deleteFailedLRA; update the paragraph and the following
lines (168–179) to enumerate or generically describe the full tool inventory and
accurately state that methods use a mix of HTTP verbs (GET, POST, PUT, DELETE)
and include mutating operations, and ensure `@Tool` usage and examples match the
implementation in LraTools and specifically mention deleteFailedLRA as a
DELETE/mutating operation.
- Line 13: Several fenced code blocks in the README are unlabeled (MD040);
locate the unlabeled triple-backtick blocks (the "unlabeled fenced code blocks"
occurrences) and add explicit language identifiers (e.g., ```text or ```bash) to
each opening fence—apply this to both occurrences mentioned so linting passes
and rendering/tooling improves.
In
`@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java`:
- Around line 50-54: The current
assistant.chat(...).onFailure().recoverWithItem(...) swallows errors and returns
HTTP 200 with error text; instead transform failures into proper HTTP errors so
clients get 4xx/5xx. Replace the onFailure().recoverWithItem(...) block on the
assistant.chat(...) call with an onFailure().transform(...) (or
onFailure().recoverWithUni(() -> Uni.createFrom().failure(...))) that wraps the
original throwable into a WebApplicationException or Response with an
appropriate status (e.g., 500) and include the original message as the entity;
refer to assistant.chat(...) and the surrounding method in LraAiChatResource to
locate and update the code.
- Line 57: Add Bean Validation to the request DTO by annotating the ChatRequest
record's message component with `@NotBlank` (e.g., public record
ChatRequest(`@NotBlank` String message) {}) and import the constraint from
jakarta.validation.constraints.NotBlank; then update the resource method that
accepts ChatRequest to use `@Valid` on the parameter (remove the manual non-blank
checks currently performed around the method that handles ChatRequest) so
validation is performed automatically and standard error responses are produced.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java`:
- Around line 17-19: Update the Javadoc in LraAssistant.java to reflect the
actual /chat response contract: change the description that currently says token
streaming is "via SSE" to state that the method returns Multi<String> which
streams tokenized text as chunked text/plain (HTTP chunked transfer encoding)
rather than Server-Sent Events; keep the note that tool calls (coordinator REST
queries, close/cancel actions) execute synchronously before the final text
starts streaming and reference the emitting behavior implemented by
LraAiChatResource to ensure wording matches actual transport.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`:
- Line 35: The current HttpClient instance (http) is created without timeouts
and the methods put, delete, and post call http.send() with no per-request
timeout; add a timeout to avoid threads blocking indefinitely by defining a
Duration constant (e.g. REQUEST_TIMEOUT) and applying it to each HttpRequest via
HttpRequest.newBuilder().timeout(REQUEST_TIMEOUT) inside the put, delete, and
post methods before calling http.send(); ensure the constant is used everywhere
those requests are built and add the necessary java.time.Duration import.
- Around line 49-52: The methods getLRADetails, getLRAStatus, closeLRA, and
cancelLRA currently accept a full LRA URI from tool input and call get/put/post
directly, creating SSRF risk; implement a helper method validatedLraUri(String
lraId) that parses the incoming lraId, ensures it is an absolute URI and that
its scheme, host, and port (and optionally path root) match the configured
coordinator origin, and throw/return an error on mismatch; then call
validatedLraUri(lraId) at the start of getLRADetails, getLRAStatus, closeLRA,
and cancelLRA and use the validated URI when building/issuing the HTTP request
so requests cannot be made to arbitrary hosts.
- Around line 113-115: The catch blocks in LraTools.java currently call
Thread.currentThread().interrupt() for both InterruptedException and
IOException; change them so only InterruptedException handlers set the thread
interrupt flag. Locate the methods/blocks where the combined catch clauses
reference IOException | InterruptedException (around the calls that return
"Error reaching coordinator at " + url) and split or separate the catches so
IOException returns its error string without interrupting the thread, while
InterruptedException both interrupts the thread and returns the error; ensure
you update all occurrences of these combined catches in the class (the ones
producing the same return message).
- Around line 43-46: The method listLRAsByStatus concatenates raw status into
the query (coordinatorUrl + "?Status=" + status) which can produce illegal URIs
and runtime IllegalArgumentException at URI.create; instead validate the input
against the allowed LRA states (e.g., Active, Closing, Closed, Cancelling,
Cancelled, FailedToClose, FailedToCancel) and then URL-encode the validated
status before composing the URL (or use a URI/URL builder used elsewhere in this
class). Update listLRAsByStatus to perform validation and encoding, and apply
the same validation/encoding approach to the other call-sites around the
URI.create usage (see the URI.create usage at lines ~101-105) and return a
controlled error or throw a clear IllegalArgumentException when validation
fails.
In `@rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html`:
- Around line 24-38: The button element with id "send" in index.html lacks an
explicit type, which can cause unexpected form submissions; update the <button
id="send"> element to include an explicit type attribute (e.g., type="button")
so it behaves as a non-submit control, ensuring any JS click handler (bound to
"send") handles actions without triggering form submission.
- Around line 69-73: The error branch in index.html currently displays the raw
response string; update the handler used where the fetch response is checked
(the block with "if (!res.ok)") to attempt parsing the response as JSON (e.g.,
call await res.json() inside a try/catch), extract a sensible message property
(e.g., json.message or json.error or join validation fields) and set
reply.textContent to "Error <status>: <extracted message>", falling back to
await res.text() if JSON parsing or expected fields are missing; keep
reply.className = 'msg error' and return as before.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 53a9d751-7a99-4970-8317-3d9b60ebd3df
📒 Files selected for processing (8)
rts/lra/lra-ai-dashboard/README.mdrts/lra/lra-ai-dashboard/pom.xmlrts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.javarts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.htmlrts/lra/lra-ai-dashboard/src/main/resources/application.propertiesrts/lra/pom.xml
|
|
||
| ## Architecture | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks.
Lines 13 and 212 use unlabeled fenced blocks (MD040). Add explicit languages (for example text) to satisfy linting and improve rendering/tooling.
Also applies to: 212-212
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@rts/lra/lra-ai-dashboard/README.md` at line 13, Several fenced code blocks in
the README are unlabeled (MD040); locate the unlabeled triple-backtick blocks
(the "unlabeled fenced code blocks" occurrences) and add explicit language
identifiers (e.g., ```text or ```bash) to each opening fence—apply this to both
occurrences mentioned so linting passes and rendering/tooling improves.
| Six `@Tool`-annotated methods that form the saga-domain tool schema described in the patent. | ||
| Each method makes a single blocking HTTP GET to the coordinator and returns the raw JSON response | ||
| for the LLM to reason over. |
There was a problem hiding this comment.
Fix tool inventory and HTTP-method description to match implementation.
Line 164 says “Six @Tool-annotated methods” and Line 165 says “single blocking HTTP GET,” but the table already lists more methods and LraTools includes mutating PUT/POST/DELETE operations too (including deleteFailedLRA). Please update this section to reflect the actual full tool surface and method types.
Also applies to: 168-179
🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 164 - 166, The README text
incorrectly states "Six `@Tool`-annotated methods" and "single blocking HTTP
GET" but the actual tool surface (LraTools) contains more than six `@Tool`
methods and includes mutating HTTP methods (PUT/POST/DELETE), e.g.
deleteFailedLRA; update the paragraph and the following lines (168–179) to
enumerate or generically describe the full tool inventory and accurately state
that methods use a mix of HTTP verbs (GET, POST, PUT, DELETE) and include
mutating operations, and ensure `@Tool` usage and examples match the
implementation in LraTools and specifically mention deleteFailedLRA as a
DELETE/mutating operation.
| The write tools (`closeLRA`, `cancelLRA`) are guarded in the system prompt: the LLM will | ||
| only call them when the operator explicitly requests it and will echo the target LRA ID | ||
| before acting. |
There was a problem hiding this comment.
Document all write tools in the guardrail note.
The guardrail paragraph only mentions closeLRA and cancelLRA, but startLRA and deleteFailedLRA are also state-changing tools. Line 180 should include all mutating tools so operator expectations match runtime behavior.
🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 180 - 182, Update the
guardrail paragraph to list all mutating/write tools so it matches runtime
behavior: include startLRA and deleteFailedLRA alongside closeLRA and cancelLRA
in the sentence that describes write tools being guarded; ensure the wording
still states that the LLM will only call these tools when the operator
explicitly requests it and that the LLM will echo the target LRA ID before
acting (refer to symbols startLRA, deleteFailedLRA, closeLRA, cancelLRA).
| return assistant.chat(request.message()) | ||
| .onFailure().recoverWithItem(t -> { | ||
| log.errorf(t, "LLM call failed"); | ||
| return "\n\n[Error: " + t.getMessage() + "]"; | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Error recovery returns HTTP 200 with error text in the stream.
The onFailure().recoverWithItem() pattern emits the error message as stream content rather than failing the HTTP request with an error status code. While this works for the browser UI (which displays the error text), programmatic API consumers may expect standard HTTP error responses (4xx/5xx) for failures. For a PoC this is acceptable, but consider returning proper error status codes for production use.
🤖 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
`@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java`
around lines 50 - 54, The current
assistant.chat(...).onFailure().recoverWithItem(...) swallows errors and returns
HTTP 200 with error text; instead transform failures into proper HTTP errors so
clients get 4xx/5xx. Replace the onFailure().recoverWithItem(...) block on the
assistant.chat(...) call with an onFailure().transform(...) (or
onFailure().recoverWithUni(() -> Uni.createFrom().failure(...))) that wraps the
original throwable into a WebApplicationException or Response with an
appropriate status (e.g., 500) and include the original message as the entity;
refer to assistant.chat(...) and the surrounding method in LraAiChatResource to
locate and update the code.
| }); | ||
| } | ||
|
|
||
| public record ChatRequest(String message) {} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider adding Bean Validation annotations to ChatRequest.
The record lacks validation annotations (e.g., @NotBlank). While the validation is handled manually at line 47-49, using Bean Validation would provide automatic validation and standard error responses.
♻️ Optional enhancement with Bean Validation
+import jakarta.validation.constraints.NotBlank;
+
-public record ChatRequest(String message) {}
+public record ChatRequest(`@NotBlank`(message = "Message must not be empty") String message) {}Then remove manual validation and add @Valid to the method parameter:
+import jakarta.validation.Valid;
+
-public Multi<String> chat(ChatRequest request) {
- if (request == null || request.message() == null || request.message().isBlank()) {
- throw new BadRequestException("Message must not be empty");
- }
+public Multi<String> chat(`@Valid` ChatRequest request) {
return assistant.chat(request.message())🤖 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
`@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java`
at line 57, Add Bean Validation to the request DTO by annotating the ChatRequest
record's message component with `@NotBlank` (e.g., public record
ChatRequest(`@NotBlank` String message) {}) and import the constraint from
jakarta.validation.constraints.NotBlank; then update the resource method that
accepts ChatRequest to use `@Valid` on the parameter (remove the manual non-blank
checks currently performed around the method that handles ChatRequest) so
validation is performed automatically and standard error responses are produced.
| public String listLRAsByStatus( | ||
| @P("The LRA lifecycle state to filter by. One of: Active, Closing, Closed, Cancelling, Cancelled, FailedToClose, FailedToCancel") String status) { | ||
| return get(coordinatorUrl + "?Status=" + status); | ||
| } |
There was a problem hiding this comment.
Encode and validate status before composing the query URL.
Line 45 concatenates raw status. If the model emits spaces/symbols, Line 104 (URI.create) can throw IllegalArgumentException and fail the chat flow; it also allows malformed query composition.
Suggested patch
+import java.util.Set;
+
+ private static final Set<String> ALLOWED_STATUSES = Set.of(
+ "Active", "Closing", "Closed", "Cancelling", "Cancelled", "FailedToClose", "FailedToCancel");
+
public String listLRAsByStatus(
`@P`("The LRA lifecycle state to filter by. One of: Active, Closing, Closed, Cancelling, Cancelled, FailedToClose, FailedToCancel") String status) {
- return get(coordinatorUrl + "?Status=" + status);
+ if (!ALLOWED_STATUSES.contains(status)) {
+ return "Invalid status: " + status;
+ }
+ return get(coordinatorUrl + "?Status=" + URLEncoder.encode(status, StandardCharsets.UTF_8));
}Also applies to: 101-105
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`
around lines 43 - 46, The method listLRAsByStatus concatenates raw status into
the query (coordinatorUrl + "?Status=" + status) which can produce illegal URIs
and runtime IllegalArgumentException at URI.create; instead validate the input
against the allowed LRA states (e.g., Active, Closing, Closed, Cancelling,
Cancelled, FailedToClose, FailedToCancel) and then URL-encode the validated
status before composing the URL (or use a URI/URL builder used elsewhere in this
class). Update listLRAsByStatus to perform validation and encoding, and apply
the same validation/encoding approach to the other call-sites around the
URI.create usage (see the URI.create usage at lines ~101-105) and return a
controlled error or throw a clear IllegalArgumentException when validation
fails.
| public String getLRADetails( | ||
| @P("The full LRA ID, which is a URI such as http://host/lra-coordinator/<uid>") String lraId) { | ||
| return get(lraId); | ||
| } |
There was a problem hiding this comment.
Restrict tool-executed LRA URIs to the configured coordinator origin (SSRF).
Line 50/56/82/88 accepts a full URI from model-controlled tool args, and Line 104 executes it directly. This allows server-side requests to arbitrary hosts (including internal metadata/private services) via prompt injection or malicious operator input.
Suggested hardening pattern
+import java.net.URISyntaxException;
+
+ private URI coordinatorBaseUri() {
+ return URI.create(coordinatorUrl);
+ }
+
+ private URI validatedLraUri(String lraId) {
+ URI candidate = URI.create(lraId);
+ URI base = coordinatorBaseUri();
+ boolean sameHost = base.getHost() != null && base.getHost().equalsIgnoreCase(candidate.getHost());
+ boolean samePort = (base.getPort() == -1 ? base.toURL().getDefaultPort() : base.getPort())
+ == (candidate.getPort() == -1 ? candidate.toURL().getDefaultPort() : candidate.getPort());
+ boolean allowedPath = candidate.getPath() != null && candidate.getPath().startsWith(base.getPath() + "/");
+ if (!sameHost || !samePort || !allowedPath) {
+ throw new IllegalArgumentException("LRA ID must belong to configured coordinator");
+ }
+ return candidate;
+ }Then use validatedLraUri(lraId) in getLRADetails, getLRAStatus, closeLRA, and cancelLRA before building requests.
Also applies to: 55-58, 81-90, 101-105
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`
around lines 49 - 52, The methods getLRADetails, getLRAStatus, closeLRA, and
cancelLRA currently accept a full LRA URI from tool input and call get/put/post
directly, creating SSRF risk; implement a helper method validatedLraUri(String
lraId) that parses the incoming lraId, ensures it is an absolute URI and that
its scheme, host, and port (and optionally path root) match the configured
coordinator origin, and throw/return an error on mismatch; then call
validatedLraUri(lraId) at the start of getLRADetails, getLRAStatus, closeLRA,
and cancelLRA and use the validated URI when building/issuing the HTTP request
so requests cannot be made to arbitrary hosts.
| } catch (IOException | InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| return "Error reaching coordinator at " + url + ": " + e.getMessage(); |
There was a problem hiding this comment.
Do not set the thread interrupt flag for IOException.
The combined catch calls Thread.currentThread().interrupt() for both InterruptedException and IOException (Lines 114/129/144/159). Interrupting on plain I/O errors can leak interrupted state into unrelated downstream work.
Suggested patch pattern
- } catch (IOException | InterruptedException e) {
- Thread.currentThread().interrupt();
- return "Error reaching coordinator at " + url + ": " + e.getMessage();
- }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return "Error reaching coordinator at " + url + ": " + e.getMessage();
+ } catch (IOException e) {
+ return "Error reaching coordinator at " + url + ": " + e.getMessage();
+ }Also applies to: 128-130, 143-145, 158-160
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`
around lines 113 - 115, The catch blocks in LraTools.java currently call
Thread.currentThread().interrupt() for both InterruptedException and
IOException; change them so only InterruptedException handlers set the thread
interrupt flag. Locate the methods/blocks where the combined catch clauses
reference IOException | InterruptedException (around the calls that return
"Error reaching coordinator at " + url) and split or separate the catches so
IOException returns its error string without interrupting the thread, while
InterruptedException both interrupts the thread and returns the error; ensure
you update all occurrences of these combined catches in the class (the ones
producing the same return message).
acc49ef to
e880fe3
Compare
Using mutiny 3.1.1 for compatibility to quarkus-langchain4j-ollama
e880fe3 to
37b3c77
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (13)
rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html (2)
37-37:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd explicit type attribute to the button element.
The
<button>element at line 37 is missing an explicittypeattribute. According to HTML5 standards, buttons should declare their type to avoid unexpected form submission behavior.🔧 Proposed fix
- <button id="send">Send</button> + <button id="send" type="button">Send</button>🤖 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 `@rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html` at line 37, The <button id="send"> lacks an explicit type and may trigger form submission unexpectedly; update the element identified by id "send" to include an explicit type attribute (e.g., set type="button" if it should not submit a form, or type="submit" if it should) so its behavior is explicit and conforms to HTML5 standards.
69-73: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueError response handling could parse JSON for better display.
When the backend returns validation errors (e.g., HTTP 400), Quarkus/RESTEasy returns a JSON error response by default. The current code displays the raw JSON string, which is functional but not ideal for UX. Consider parsing the JSON and extracting the error message for cleaner display.
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html` around lines 69 - 73, The current fetch error branch (the block using res, reply, and reply.className) shows raw response text; change it to detect JSON responses and parse them (e.g., via res.json()) to extract a user-friendly message (common fields: message, error, or validation errors array) and set reply.textContent to that formatted message; if parsing fails or content-type isn't JSON, fall back to the existing await res.text() behavior; update the error-handling branch around the res.ok check to implement this parsing and formatting logic.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java (4)
43-46:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEncode and validate
statusbefore composing the query URL.Line 45 concatenates raw
statusinto the query string. If the model emits spaces or symbols,URI.createat line 104 can throwIllegalArgumentExceptionand fail the chat flow. The status should be validated against the allowed set and URL-encoded.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 43 - 46, Validate and URL-encode the incoming status in listLRAsByStatus before building the query: check that the provided status matches one of the allowed lifecycle values (Active, Closing, Closed, Cancelling, Cancelled, FailedToClose, FailedToCancel), and if valid, use a URL-encoding routine to encode it before concatenating with coordinatorUrl for the get(...) call; this prevents IllegalArgumentException when later creating URIs (e.g., where URI.create is used) and ensures only allowed statuses are accepted.
35-35:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd HTTP timeouts to avoid indefinite blocking on coordinator calls.
Line 35 creates a default
HttpClientwithout timeouts, and the blockingsend()calls inget,put,delete, andpost(lines 108, 126, 141, 156) have no request timeout. A slow or hung coordinator can pin threads and degrade endpoint availability.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` at line 35, The HttpClient is created without timeouts (field "http") which lets blocking send() calls in methods get, put, delete, and post hang indefinitely; change the client creation to use HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(X)).build() and add per-request timeouts by calling HttpRequest.newBuilder(...).timeout(Duration.ofSeconds(Y)) when constructing requests in get, put, delete and post (use java.time.Duration). Ensure the "http" field uses the new builder and each of the methods get/put/delete/post attaches a reasonable timeout value so send(...) cannot block forever.
49-52:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRestrict tool-executed LRA URIs to the configured coordinator origin (SSRF).
Lines 50, 56, 82, and 88 accept full URIs from model-controlled tool arguments, which are executed directly via
get/put/postat line 104. This allows server-side requests to arbitrary hosts (including internal metadata or private services) via prompt injection or malicious operator input.Implement URI validation to ensure LRA IDs belong to the configured coordinator origin before executing requests.
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 49 - 52, Validate that any model-supplied LRA URI belongs to the configured coordinator origin before calling the HTTP helpers: in getLRADetails (and the other methods that accept full URIs and ultimately call get/put/post), parse the input with java.net.URI, compare scheme, host and effective port (and optionally ensure the path begins with the configured coordinator base path) against the configured coordinator origin value (e.g., coordinatorBaseUri or similar config field), and reject/throw/log an error if the check fails; also ensure the actual HTTP calls made by get/put/post do not follow redirects to other hosts. Use this centralized validation before executing the request to prevent SSRF.
113-115:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDo not set the thread interrupt flag for
IOException.The combined catch blocks call
Thread.currentThread().interrupt()for bothInterruptedExceptionandIOException(lines 114, 129, 144, 159). Interrupting the thread on plain I/O errors can leak interrupted state into unrelated downstream work. OnlyInterruptedExceptionshould restore the interrupt flag.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 113 - 115, The combined multi-catch blocks in LraTools.java (e.g., the catch declarations using "catch (IOException | InterruptedException e)") incorrectly call Thread.currentThread().interrupt() for both IOException and InterruptedException; change each combined catch into separate catches or add an instanceof check so that Thread.currentThread().interrupt() is invoked only when e is an InterruptedException, leaving IOException handlers to log/return the error without setting the thread interrupt flag; update all occurrences (the catch at the shown diff and the similar catches around the other instances) to follow this pattern so only InterruptedException restores the interrupt status.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java (1)
17-19:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign the transport wording with the actual
/chatresponse contract.Line 18 says token streaming is "via SSE", but
LraAiChatResourceemitstext/plainchunked output (not Server-Sent Events). The Javadoc should be updated to match the actual transport mechanism to avoid integration confusion.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java` around lines 17 - 19, Update the Javadoc in LraAssistant to reflect the actual /chat response contract: replace the phrase "via SSE" with a description that the method returns a Multi<String> which streams token-by-token over an HTTP chunked text/plain response (as emitted by LraAiChatResource) rather than Server-Sent Events, and mention that tool calls (coordinator REST queries, close/cancel actions) still execute synchronously before streaming begins.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java (2)
50-54: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoffError recovery returns HTTP 200 with error text in the stream.
The
onFailure().recoverWithItem()pattern emits the error message as stream content with HTTP 200 rather than failing the request with an appropriate error status code (4xx/5xx). While this works for the browser UI (which displays the error text), programmatic API consumers expect standard HTTP error responses for failures.For a PoC this is acceptable, but production use should return proper error status codes.
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java` around lines 50 - 54, The current assistant.chat(request.message()).onFailure().recoverWithItem(...) swallows errors and returns a 200 with error text; instead propagate an HTTP error so callers receive proper status codes. Replace the recoverWithItem handler in LraAiChatResource (the assistant.chat(...) chain) with a failure propagation — map the Throwable to a proper HTTP failure (e.g. create and return a failed Uni with a WebApplicationException or a Response with Response.status(500).entity(...).build()) so the reactive pipeline fails with a 4xx/5xx rather than emitting the error string.
57-57: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider adding Bean Validation annotations to ChatRequest.
The record lacks validation annotations (e.g.,
@NotBlank). While validation is handled manually at lines 47-49, using Bean Validation would provide automatic validation and standard error responses, improving consistency with JAX-RS conventions.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java` at line 57, Add Bean Validation to the ChatRequest record by annotating its message component with `@NotBlank` (use javax.validation.constraints.NotBlank) and ensure the JAX-RS resource method that accepts ChatRequest is annotated with `@Valid` so validation runs automatically; then remove the manual validation checks currently performed around the ChatRequest handling (the manual checks at lines 47-49) to rely on standard validation and error responses. Ensure imports for `@NotBlank` and `@Valid` are added and any existing manual-throw logic for blank messages is deleted.rts/lra/lra-ai-dashboard/README.md (4)
164-166:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix tool inventory and HTTP-method description to match implementation.
Line 164 says "Six
@Tool-annotated methods" butLraToolsactually has 10 tools. Line 165 says "single blocking HTTP GET" but the tools use GET, PUT, POST, and DELETE operations including mutating write methods.🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 164 - 166, Update the README text to correctly describe the LraTools implementation: change the "Six `@Tool`-annotated methods" phrase to state that LraTools exposes 10 `@Tool`-annotated methods (reference LraTools) and replace "single blocking HTTP GET" with a summary that the tools perform HTTP GET, PUT, POST and DELETE operations, noting that several tools are mutating/write methods rather than read-only calls so the doc matches the actual behavior.
180-182:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDocument all write tools in the guardrail note.
The guardrail paragraph only mentions
closeLRAandcancelLRA, butstartLRAanddeleteFailedLRAare also state-changing tools that require the same guardrails (explicit operator request and confirmation).🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 180 - 182, The guardrail note currently only mentions closeLRA and cancelLRA; update that paragraph to list all state-changing tools—startLRA, deleteFailedLRA, closeLRA, and cancelLRA—and state that each must only be invoked after an explicit operator request and that the LLM must echo the target LRA ID for confirmation before acting. Ensure the wording mirrors the existing guardrail style and includes the exact tool names startLRA and deleteFailedLRA so they are covered by the same restrictions.
13-13: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winAdd language identifiers to fenced code blocks.
Lines 13 and 212 use unlabeled fenced blocks. Add explicit language identifiers (e.g.,
textorbash) to satisfy linting and improve rendering.Also applies to: 212-212
🤖 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 `@rts/lra/lra-ai-dashboard/README.md` at line 13, Add explicit language identifiers to the unlabeled fenced code blocks currently used at the two README sections (the fenced triple-backtick blocks around the content at the earlier example and the one near line 212); replace ``` with a labeled fence such as ```text or ```bash (choose the appropriate language for the contained content) so the markdown linter and renderer correctly recognize the blocks.
168-179:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd missing
deleteFailedLRAtool to the table.The table lists 9 tools but
LraToolshas 10@Toolmethods. The missing tool isdeleteFailedLRA(DELETE on/lra-coordinator/recovery/{uid}), which is critical for removing failed LRAs after manual resolution. According to the system prompt inLraAssistant.java, this is "the ONLY way to remove a failed LRA."📝 Suggested addition to the table
| `listFailedLRAs()` | `GET /lra-coordinator/recovery/failed` | Find transactions needing manual action | +| `deleteFailedLRA(lraId)` | `DELETE /lra-coordinator/recovery/{uid}` | Remove a failed LRA after manual resolution | | `closeLRA(lraId)` | `PUT {lraId}/close` | Operator-requested completion |🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 168 - 179, Add the missing deleteFailedLRA tool entry to the README table: include a row for deleteFailedLRA with Coordinator endpoint "DELETE /lra-coordinator/recovery/{uid}" and a short description like "Remove a failed LRA after manual resolution"; update the table alongside the other LraTools entries so the README matches the 10 `@Tool` methods (references: deleteFailedLRA, LraTools, and LraAssistant.java).
🤖 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 `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 202-206: The README's description of the chat endpoint is
incorrect; update the text to state that the implementation in LraAiChatResource
returns Multi<String> and is annotated with `@Produces`(MediaType.TEXT_PLAIN)
(plain-text streaming), not `@Produces`(SERVER_SENT_EVENTS) nor
`@RestSseElementType`(TEXT_PLAIN); also remove the claim about `@Blocking` and
clarify that Multi<String> is used for streaming without SSE-specific
annotations. Locate the paragraph describing "POST /chat" and replace the
SSE/annotation claims with the exact actual annotations and return type used in
LraAiChatResource.
- Around line 28-29: Update the HTTP method description that currently reads
"plain HTTP GET requests" to accurately reflect all HTTP operations used by
LraTools: GET for reading tools, POST for start (e.g., start), PUT for
close/cancel actions (e.g., close/cancel), and DELETE for deleteFailedLRA; edit
the LraTools README entry (the line referencing java.net.http.HttpClient) to
list these methods and their corresponding actions so readers can see the full
range of operations.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`:
- Line 76: The current substring extraction "String uid =
lraId.substring(lraId.lastIndexOf('/') + 1);" in LraTools is fragile; update the
code to first validate lraId is non-null/non-empty, parse it as a java.net.URI
(catching URISyntaxException), verify the URI's host/path match the expected
coordinator URL structure (e.g., path contains the coordinator segment) and that
the final path segment matches the expected UID pattern (e.g., UUID regex)
before extracting; extract the UID from URI.getPath() by splitting on '/' (or
using Path semantics) instead of blind lastIndexOf, and if validation fails
throw a clear IllegalArgumentException or return a controlled error so
downstream delete endpoint receives only well-formed UIDs.
---
Duplicate comments:
In `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 164-166: Update the README text to correctly describe the LraTools
implementation: change the "Six `@Tool`-annotated methods" phrase to state that
LraTools exposes 10 `@Tool`-annotated methods (reference LraTools) and replace
"single blocking HTTP GET" with a summary that the tools perform HTTP GET, PUT,
POST and DELETE operations, noting that several tools are mutating/write methods
rather than read-only calls so the doc matches the actual behavior.
- Around line 180-182: The guardrail note currently only mentions closeLRA and
cancelLRA; update that paragraph to list all state-changing tools—startLRA,
deleteFailedLRA, closeLRA, and cancelLRA—and state that each must only be
invoked after an explicit operator request and that the LLM must echo the target
LRA ID for confirmation before acting. Ensure the wording mirrors the existing
guardrail style and includes the exact tool names startLRA and deleteFailedLRA
so they are covered by the same restrictions.
- Line 13: Add explicit language identifiers to the unlabeled fenced code blocks
currently used at the two README sections (the fenced triple-backtick blocks
around the content at the earlier example and the one near line 212); replace
``` with a labeled fence such as ```text or ```bash (choose the appropriate
language for the contained content) so the markdown linter and renderer
correctly recognize the blocks.
- Around line 168-179: Add the missing deleteFailedLRA tool entry to the README
table: include a row for deleteFailedLRA with Coordinator endpoint "DELETE
/lra-coordinator/recovery/{uid}" and a short description like "Remove a failed
LRA after manual resolution"; update the table alongside the other LraTools
entries so the README matches the 10 `@Tool` methods (references: deleteFailedLRA,
LraTools, and LraAssistant.java).
In
`@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java`:
- Around line 50-54: The current
assistant.chat(request.message()).onFailure().recoverWithItem(...) swallows
errors and returns a 200 with error text; instead propagate an HTTP error so
callers receive proper status codes. Replace the recoverWithItem handler in
LraAiChatResource (the assistant.chat(...) chain) with a failure propagation —
map the Throwable to a proper HTTP failure (e.g. create and return a failed Uni
with a WebApplicationException or a Response with
Response.status(500).entity(...).build()) so the reactive pipeline fails with a
4xx/5xx rather than emitting the error string.
- Line 57: Add Bean Validation to the ChatRequest record by annotating its
message component with `@NotBlank` (use javax.validation.constraints.NotBlank) and
ensure the JAX-RS resource method that accepts ChatRequest is annotated with
`@Valid` so validation runs automatically; then remove the manual validation
checks currently performed around the ChatRequest handling (the manual checks at
lines 47-49) to rely on standard validation and error responses. Ensure imports
for `@NotBlank` and `@Valid` are added and any existing manual-throw logic for blank
messages is deleted.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java`:
- Around line 17-19: Update the Javadoc in LraAssistant to reflect the actual
/chat response contract: replace the phrase "via SSE" with a description that
the method returns a Multi<String> which streams token-by-token over an HTTP
chunked text/plain response (as emitted by LraAiChatResource) rather than
Server-Sent Events, and mention that tool calls (coordinator REST queries,
close/cancel actions) still execute synchronously before streaming begins.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`:
- Around line 43-46: Validate and URL-encode the incoming status in
listLRAsByStatus before building the query: check that the provided status
matches one of the allowed lifecycle values (Active, Closing, Closed,
Cancelling, Cancelled, FailedToClose, FailedToCancel), and if valid, use a
URL-encoding routine to encode it before concatenating with coordinatorUrl for
the get(...) call; this prevents IllegalArgumentException when later creating
URIs (e.g., where URI.create is used) and ensures only allowed statuses are
accepted.
- Line 35: The HttpClient is created without timeouts (field "http") which lets
blocking send() calls in methods get, put, delete, and post hang indefinitely;
change the client creation to use
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(X)).build() and add
per-request timeouts by calling
HttpRequest.newBuilder(...).timeout(Duration.ofSeconds(Y)) when constructing
requests in get, put, delete and post (use java.time.Duration). Ensure the
"http" field uses the new builder and each of the methods get/put/delete/post
attaches a reasonable timeout value so send(...) cannot block forever.
- Around line 49-52: Validate that any model-supplied LRA URI belongs to the
configured coordinator origin before calling the HTTP helpers: in getLRADetails
(and the other methods that accept full URIs and ultimately call get/put/post),
parse the input with java.net.URI, compare scheme, host and effective port (and
optionally ensure the path begins with the configured coordinator base path)
against the configured coordinator origin value (e.g., coordinatorBaseUri or
similar config field), and reject/throw/log an error if the check fails; also
ensure the actual HTTP calls made by get/put/post do not follow redirects to
other hosts. Use this centralized validation before executing the request to
prevent SSRF.
- Around line 113-115: The combined multi-catch blocks in LraTools.java (e.g.,
the catch declarations using "catch (IOException | InterruptedException e)")
incorrectly call Thread.currentThread().interrupt() for both IOException and
InterruptedException; change each combined catch into separate catches or add an
instanceof check so that Thread.currentThread().interrupt() is invoked only when
e is an InterruptedException, leaving IOException handlers to log/return the
error without setting the thread interrupt flag; update all occurrences (the
catch at the shown diff and the similar catches around the other instances) to
follow this pattern so only InterruptedException restores the interrupt status.
In `@rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html`:
- Line 37: The <button id="send"> lacks an explicit type and may trigger form
submission unexpectedly; update the element identified by id "send" to include
an explicit type attribute (e.g., set type="button" if it should not submit a
form, or type="submit" if it should) so its behavior is explicit and conforms to
HTML5 standards.
- Around line 69-73: The current fetch error branch (the block using res, reply,
and reply.className) shows raw response text; change it to detect JSON responses
and parse them (e.g., via res.json()) to extract a user-friendly message (common
fields: message, error, or validation errors array) and set reply.textContent to
that formatted message; if parsing fails or content-type isn't JSON, fall back
to the existing await res.text() behavior; update the error-handling branch
around the res.ok check to implement this parsing and formatting logic.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: af342879-dbb5-48cf-9aad-2b97f65064b0
📒 Files selected for processing (9)
pom.xmlrts/lra/lra-ai-dashboard/README.mdrts/lra/lra-ai-dashboard/pom.xmlrts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.javarts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.htmlrts/lra/lra-ai-dashboard/src/main/resources/application.propertiesrts/lra/pom.xml
| │ java.net.http.HttpClient — plain HTTP GET requests | ||
| ▼ |
There was a problem hiding this comment.
Correct the HTTP method description.
Line 28 says "plain HTTP GET requests" but LraTools actually uses multiple HTTP methods: GET (read tools), PUT (close/cancel), POST (start), and DELETE (deleteFailedLRA). This description should reflect the full range of HTTP operations.
📝 Suggested fix
-LraTools (`@ApplicationScoped` CDI bean)
- │ java.net.http.HttpClient — plain HTTP GET requests
+LraTools (`@ApplicationScoped` CDI bean)
+ │ java.net.http.HttpClient — HTTP requests (GET, PUT, POST, DELETE)
▼
LRA Coordinator (http://localhost:8080/lra-coordinator)📝 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.
| │ java.net.http.HttpClient — plain HTTP GET requests | |
| ▼ | |
| │ java.net.http.HttpClient — HTTP requests (GET, PUT, POST, DELETE) | |
| ▼ |
🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 28 - 29, Update the HTTP
method description that currently reads "plain HTTP GET requests" to accurately
reflect all HTTP operations used by LraTools: GET for reading tools, POST for
start (e.g., start), PUT for close/cancel actions (e.g., close/cancel), and
DELETE for deleteFailedLRA; edit the LraTools README entry (the line referencing
java.net.http.HttpClient) to list these methods and their corresponding actions
so readers can see the full range of operations.
| "The LRA ID must be the full URI (e.g. http://host/lra-coordinator/<uid>).") | ||
| public String deleteFailedLRA( | ||
| @P("The full LRA ID URI of the failed LRA to delete, e.g. http://host/lra-coordinator/<uid>") String lraId) { | ||
| String uid = lraId.substring(lraId.lastIndexOf('/') + 1); |
There was a problem hiding this comment.
Validate URI structure before extracting UID substring.
The lastIndexOf('/') + 1 extraction at line 76 is fragile:
- If
lraIdis empty or malformed, this produces incorrect results - If
lraIddoesn't end with the expected coordinator path structure, the extracted UID will be wrong - The delete endpoint expects a specific UID format that may not match arbitrary substring extraction
This should be addressed as part of the broader URI validation recommended in the SSRF comment above, ensuring the LRA ID conforms to the expected coordinator URL structure before extracting components.
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` at
line 76, The current substring extraction "String uid =
lraId.substring(lraId.lastIndexOf('/') + 1);" in LraTools is fragile; update the
code to first validate lraId is non-null/non-empty, parse it as a java.net.URI
(catching URISyntaxException), verify the URI's host/path match the expected
coordinator URL structure (e.g., path contains the coordinator segment) and that
the final path segment matches the expected UID pattern (e.g., UUID regex)
before extracting; extract the UID from URI.getPath() by splitting on '/' (or
using Path semantics) instead of blind lastIndexOf, and if validation fails
throw a clear IllegalArgumentException or return a controlled error so
downstream delete endpoint receives only well-formed UIDs.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (9)
rts/lra/lra-ai-dashboard/README.md (1)
198-203: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winAdd detail about the endpoint's input and output format.
The description mentions
Multi<String>and RESTEasy Reactive handling, but doesn't document that the endpoint accepts JSON input (@Consumes(MediaType.APPLICATION_JSON)withChatRequestrecord) and produces plain-text streaming (@Produces(MediaType.TEXT_PLAIN)). Adding these details would help operators understand the API contract better.📝 Suggested addition
### `LraAiChatResource.java` -A single `POST /chat` endpoint. +A single `POST /chat` endpoint accepting JSON (`{"message": "..."}`) and producing a plain-text +stream (`@Produces(MediaType.TEXT_PLAIN)` returning `Multi<String>`). No `@Blocking` is needed: `Multi<String>` is handled natively by RESTEasy Reactive without occupying the I/O thread.🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 198 - 203, Update the LraAiChatResource.java README section to specify the POST /chat contract: document that the endpoint consumes JSON via `@Consumes`(MediaType.APPLICATION_JSON) with a ChatRequest record as the request body and that it produces plain-text streaming via `@Produces`(MediaType.TEXT_PLAIN) returning a Multi<String> (server-sent/plain streaming) handled natively by RESTEasy Reactive; mention any relevant fields on ChatRequest and note that no `@Blocking` is required.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java (1)
17-19:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAlign the transport wording with the actual
/chatresponse contract.Line 18 says token streaming is "via SSE", but
LraAiChatResourceproducestext/plainwith chunked transfer encoding, not Server-Sent Events. The Javadoc should match the actual transport mechanism.📝 Suggested fix
- * Returns Multi<String> so the response streams token-by-token to the client - * via SSE. Tool calls (coordinator REST queries, close/cancel actions) still + * Returns Multi<String> so the response streams token-by-token to the client + * as chunked text/plain. Tool calls (coordinator REST queries, close/cancel actions) still * execute synchronously before the final text starts streaming.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java` around lines 17 - 19, Update the Javadoc in LraAssistant (the method that "Returns Multi<String> so the response streams token-by-token to the client") to reflect the actual /chat response transport: replace the phrase "via SSE" with wording that it streams token-by-token over plain HTTP chunked transfer (text/plain) as produced by LraAiChatResource, so the comment accurately matches the /chat contract and not Server-Sent Events.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java (5)
49-52:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftRestrict tool-executed LRA URIs to the configured coordinator origin (SSRF).
Lines 50, 56, 82, and 88 accept full URIs from LLM tool arguments and pass them directly to HTTP calls at Line 104. This allows server-side requests to arbitrary hosts (including internal services, metadata endpoints, or private networks) via prompt injection or operator error.
🔒 Suggested validation pattern
+import java.net.URISyntaxException; + + private URI coordinatorBaseUri() { + return URI.create(coordinatorUrl); + } + + private URI validatedLraUri(String lraId) { + URI candidate; + try { + candidate = URI.create(lraId); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid LRA URI: " + lraId, e); + } + URI base = coordinatorBaseUri(); + boolean sameHost = base.getHost() != null && base.getHost().equalsIgnoreCase(candidate.getHost()); + boolean samePort = (base.getPort() == -1 ? 8080 : base.getPort()) + == (candidate.getPort() == -1 ? 8080 : candidate.getPort()); + boolean allowedPath = candidate.getPath() != null && candidate.getPath().startsWith(base.getPath() + "/"); + if (!sameHost || !samePort || !allowedPath) { + throw new IllegalArgumentException("LRA ID must belong to configured coordinator: " + coordinatorUrl); + } + return candidate; + }Then use
validatedLraUri(lraId).toString()ingetLRADetails,getLRAStatus,closeLRA, andcancelLRAbefore calling HTTP helpers.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 49 - 52, The LRA methods currently accept any full URI and forward it to HTTP helpers, enabling SSRF; fix by introducing a validatedLraUri(String lraId) utility that parses the input into a java.net.URI, ensures the scheme/host/port (origin) exactly matches the configured coordinator origin (e.g., coordinatorOrigin or coordinatorBaseUri field), and throws an IllegalArgumentException for mismatches or invalid URIs; then replace direct usage of lraId in getLRADetails, getLRAStatus, closeLRA, and cancelLRA with validatedLraUri(lraId).toString() before calling the HTTP helper so only URIs from the configured coordinator are allowed.
35-35:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd HTTP timeouts to avoid indefinite blocking on coordinator calls.
Line 35 creates a default
HttpClientwith no connect or request timeout. Lines 108, 126, 141, and 156 use blockingsend()without per-request timeouts. A hung or slow coordinator can pin threads indefinitely and degrade this endpoint's availability.⏱️ Suggested fix to add timeouts
+import java.time.Duration; + - private final HttpClient http = HttpClient.newHttpClient(); + private final HttpClient http = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(5)) + .build();Also add per-request timeout in each helper:
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) + .timeout(Duration.ofSeconds(15)) .header("Accept", "application/json")Apply the same
.timeout(...)input,delete, andpost.🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` at line 35, The HttpClient in LraTools is created without timeouts and the helper methods put/delete/post call HttpClient.send() which can block indefinitely; update the http field to use HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(...)).build() and add per-request timeouts by calling HttpRequest.Builder.timeout(Duration.ofSeconds(...)) when building requests in the put, delete, post helper methods (and any other send() usages) so each request has a bounded timeout; pick sensible timeout values and import java.time.Duration.
113-115:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDo not set the thread interrupt flag for
IOException.Lines 114, 129, 144, and 159 call
Thread.currentThread().interrupt()in combinedIOException | InterruptedExceptioncatch blocks. Setting the interrupt flag for plain I/O errors can leak interrupted state into unrelated downstream work and cause spurious failures.🔧 Suggested fix to separate exception handling
- } catch (IOException | InterruptedException e) { - Thread.currentThread().interrupt(); - return "Error reaching coordinator at " + url + ": " + e.getMessage(); - } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return "Error reaching coordinator at " + url + ": " + e.getMessage(); + } catch (IOException e) { + return "Error reaching coordinator at " + url + ": " + e.getMessage(); + }Apply the same pattern in
put(lines 128-130),delete(lines 143-145), andpost(lines 158-160).🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 113 - 115, The combined catch blocks in LraTools methods (get, put, delete, post) currently call Thread.currentThread().interrupt() for both IOException and InterruptedException; split each combined catch into two separate catches per method so that InterruptedException preserves the interrupt flag (call Thread.currentThread().interrupt() and return the error message) while IOException is handled in its own catch without setting the interrupt flag and returns the error message; update the catch blocks in the methods named put, delete, post (and the similar get handler) to follow this pattern so only InterruptedException triggers Thread.currentThread().interrupt().
43-46:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEncode and validate
statusbefore composing the query URL.Line 45 concatenates raw
statusinto the query string (coordinatorUrl + "?Status=" + status). If the LLM emits spaces, symbols, or invalid values, Line 104'sURI.create(url)will throwIllegalArgumentExceptionand fail the chat stream.🛡️ Suggested fix with validation and encoding
+import java.util.Set; + + private static final Set<String> ALLOWED_STATUSES = Set.of( + "Active", "Closing", "Closed", "Cancelling", "Cancelled", "FailedToClose", "FailedToCancel"); + public String listLRAsByStatus( `@P`("The LRA lifecycle state to filter by. One of: Active, Closing, Closed, Cancelling, Cancelled, FailedToClose, FailedToCancel") String status) { + if (!ALLOWED_STATUSES.contains(status)) { + return "Invalid status: " + status + ". Valid states: " + ALLOWED_STATUSES; + } - return get(coordinatorUrl + "?Status=" + status); + return get(coordinatorUrl + "?Status=" + URLEncoder.encode(status, StandardCharsets.UTF_8)); }🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` around lines 43 - 46, The listLRAsByStatus method currently concatenates raw status into the query (coordinatorUrl + "?Status=" + status), which can break URI.create(url); update listLRAsByStatus to first validate status against the allowed enum/values (Active, Closing, Closed, Cancelling, Cancelled, FailedToClose, FailedToCancel) and reject or normalize invalid inputs, then URL-encode the validated status (e.g., via URLEncoder.encode) before composing the query; ensure the get(...) call uses the encoded URL and add a safe fallback or throw a clear IllegalArgumentException if validation fails so URI.create(url) won't receive malformed input.
76-76:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate LRA URI structure before extracting UID substring.
Line 76's
lastIndexOf('/') + 1extraction is fragile. IflraIdis empty, malformed, or doesn't match the expected coordinator path structure, the extracted UID will be incorrect and the delete endpoint will fail or target the wrong resource.🛡️ Suggested fix with URI validation
public String deleteFailedLRA( `@P`("The full LRA ID URI of the failed LRA to delete, e.g. http://host/lra-coordinator/<uid>") String lraId) { + if (lraId == null || lraId.isEmpty()) { + return "Invalid LRA ID: must not be empty"; + } + URI uri; + try { + uri = URI.create(lraId); + } catch (IllegalArgumentException e) { + return "Invalid LRA ID URI: " + lraId; + } + String path = uri.getPath(); + if (path == null || !path.contains("/")) { + return "Invalid LRA ID: missing path segment"; + } - String uid = lraId.substring(lraId.lastIndexOf('/') + 1); + String uid = path.substring(path.lastIndexOf('/') + 1); + if (uid.isEmpty()) { + return "Invalid LRA ID: missing UID segment"; + } return delete(coordinatorUrl + "/recovery/" + uid); }🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java` at line 76, The UID extraction at LraTools (variable lraId -> uid) is fragile; validate lraId before substring: ensure lraId is non-null/non-empty, parse it as a URI or compute lastSlashIndex = lraId.lastIndexOf('/') and verify lastSlashIndex >= 0 and lastSlashIndex < lraId.length() - 1 (no trailing slash) before calling substring; if validation fails, throw a clear IllegalArgumentException or return a controlled failure, and prefer using java.net.URI to extract the path's last segment for robust behavior.rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java (2)
50-54: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoffError recovery returns HTTP 200 with error text in the stream.
The
onFailure().recoverWithItem()pattern emits the error message as stream content with HTTP 200 rather than failing the request with an error status code (4xx/5xx). While this works for the browser UI (which displays the error text), programmatic API consumers may expect standard HTTP error responses for failures.For a PoC this is acceptable, but consider returning proper error status codes for production use.
🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java` around lines 50 - 54, The current LraAiChatResource stream uses assistant.chat(request.message()).onFailure().recoverWithItem(...) which converts any LLM error into a plain string payload and yields HTTP 200; instead, change the failure path to propagate an HTTP error by transforming the failure into a failed Uni (e.g., use onFailure().transformToUni(...) or similar) that returns a WebApplicationException or a Response with an appropriate 4xx/5xx status and the error message so the request fails with a proper HTTP status for programmatic clients; update the failure handler tied to assistant.chat(...) in LraAiChatResource to throw/return a failed Uni rather than recoverWithItem.
57-57: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider adding Bean Validation annotations to ChatRequest.
The record lacks validation annotations (e.g.,
@NotBlank). While the validation is handled manually at lines 47-49, using Bean Validation would provide automatic validation and standard error responses.♻️ Optional enhancement with Bean Validation
+import jakarta.validation.constraints.NotBlank; +import jakarta.validation.Valid; + - public record ChatRequest(String message) {} + public record ChatRequest(`@NotBlank`(message = "Message must not be empty") String message) {}Then update the handler:
- public Multi<String> chat(ChatRequest request) { - if (request == null || request.message() == null || request.message().isBlank()) { - throw new BadRequestException("Message must not be empty"); - } + public Multi<String> chat(`@Valid` ChatRequest request) { return assistant.chat(request.message())🤖 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 `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java` at line 57, Add Bean Validation to the ChatRequest record and use `@Valid` in the resource method instead of manual checks: annotate the record's message component with `@NotBlank` (e.g., public record ChatRequest(`@NotBlank` String message) {}), ensure the resource method parameter that receives ChatRequest is annotated with `@Valid`, and remove the manual validation code in the handler that currently checks message emptiness so standard constraint violation responses are used; make sure the validation provider (javax.validation / jakarta.validation) is on the classpath and that any global exception mapper for ConstraintViolationException is enabled if custom handling is required.
🤖 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 `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 96-100: The README's curl example pipes output to jq but the chat
endpoint implemented in LraAiChatResource.java produces plain-text streaming
(annotated with `@Produces`(MediaType.TEXT_PLAIN) and returns a Multi<String>), so
jq is inappropriate; update the example to remove "| jq ." or add a note that
the endpoint streams plain text (not JSON) and show using curl alone or
redirecting to a file for streaming output to match the LraAiChatResource.java
behavior.
In `@rts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.html`:
- Around line 53-58: Add an in-flight guard to prevent re-entrant submits in the
submit() handler: introduce a local or module-level boolean (e.g. inFlight)
checked at the top of submit() and return early if true, set inFlight = true
before starting the fetch/stream to /chat, and clear it in a finally block; also
lock the input textarea during the request (disable the send button and either
set input.disabled = true or input.readOnly = true) so Enter cannot trigger
another submit, and ensure the same pattern is applied to the other
submit/handler block that uses the same input/send elements (the second submit
handler near the other send/Enter logic).
- Around line 75-86: The TextDecoder is used with {stream: true} in the read
loop (reader, decoder, reply, chat) but never flushed at EOF, which can drop
incomplete UTF-8 bytes; after the while loop completes call decoder.decode() (no
stream option) and append its return to reply.textContent, then scroll chat to
bottom to ensure any buffered bytes are emitted and displayed.
---
Duplicate comments:
In `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 198-203: Update the LraAiChatResource.java README section to
specify the POST /chat contract: document that the endpoint consumes JSON via
`@Consumes`(MediaType.APPLICATION_JSON) with a ChatRequest record as the request
body and that it produces plain-text streaming via
`@Produces`(MediaType.TEXT_PLAIN) returning a Multi<String> (server-sent/plain
streaming) handled natively by RESTEasy Reactive; mention any relevant fields on
ChatRequest and note that no `@Blocking` is required.
In
`@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.java`:
- Around line 50-54: The current LraAiChatResource stream uses
assistant.chat(request.message()).onFailure().recoverWithItem(...) which
converts any LLM error into a plain string payload and yields HTTP 200; instead,
change the failure path to propagate an HTTP error by transforming the failure
into a failed Uni (e.g., use onFailure().transformToUni(...) or similar) that
returns a WebApplicationException or a Response with an appropriate 4xx/5xx
status and the error message so the request fails with a proper HTTP status for
programmatic clients; update the failure handler tied to assistant.chat(...) in
LraAiChatResource to throw/return a failed Uni rather than recoverWithItem.
- Line 57: Add Bean Validation to the ChatRequest record and use `@Valid` in the
resource method instead of manual checks: annotate the record's message
component with `@NotBlank` (e.g., public record ChatRequest(`@NotBlank` String
message) {}), ensure the resource method parameter that receives ChatRequest is
annotated with `@Valid`, and remove the manual validation code in the handler that
currently checks message emptiness so standard constraint violation responses
are used; make sure the validation provider (javax.validation /
jakarta.validation) is on the classpath and that any global exception mapper for
ConstraintViolationException is enabled if custom handling is required.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.java`:
- Around line 17-19: Update the Javadoc in LraAssistant (the method that
"Returns Multi<String> so the response streams token-by-token to the client") to
reflect the actual /chat response transport: replace the phrase "via SSE" with
wording that it streams token-by-token over plain HTTP chunked transfer
(text/plain) as produced by LraAiChatResource, so the comment accurately matches
the /chat contract and not Server-Sent Events.
In `@rts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.java`:
- Around line 49-52: The LRA methods currently accept any full URI and forward
it to HTTP helpers, enabling SSRF; fix by introducing a validatedLraUri(String
lraId) utility that parses the input into a java.net.URI, ensures the
scheme/host/port (origin) exactly matches the configured coordinator origin
(e.g., coordinatorOrigin or coordinatorBaseUri field), and throws an
IllegalArgumentException for mismatches or invalid URIs; then replace direct
usage of lraId in getLRADetails, getLRAStatus, closeLRA, and cancelLRA with
validatedLraUri(lraId).toString() before calling the HTTP helper so only URIs
from the configured coordinator are allowed.
- Line 35: The HttpClient in LraTools is created without timeouts and the helper
methods put/delete/post call HttpClient.send() which can block indefinitely;
update the http field to use
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(...)).build() and add
per-request timeouts by calling
HttpRequest.Builder.timeout(Duration.ofSeconds(...)) when building requests in
the put, delete, post helper methods (and any other send() usages) so each
request has a bounded timeout; pick sensible timeout values and import
java.time.Duration.
- Around line 113-115: The combined catch blocks in LraTools methods (get, put,
delete, post) currently call Thread.currentThread().interrupt() for both
IOException and InterruptedException; split each combined catch into two
separate catches per method so that InterruptedException preserves the interrupt
flag (call Thread.currentThread().interrupt() and return the error message)
while IOException is handled in its own catch without setting the interrupt flag
and returns the error message; update the catch blocks in the methods named put,
delete, post (and the similar get handler) to follow this pattern so only
InterruptedException triggers Thread.currentThread().interrupt().
- Around line 43-46: The listLRAsByStatus method currently concatenates raw
status into the query (coordinatorUrl + "?Status=" + status), which can break
URI.create(url); update listLRAsByStatus to first validate status against the
allowed enum/values (Active, Closing, Closed, Cancelling, Cancelled,
FailedToClose, FailedToCancel) and reject or normalize invalid inputs, then
URL-encode the validated status (e.g., via URLEncoder.encode) before composing
the query; ensure the get(...) call uses the encoded URL and add a safe fallback
or throw a clear IllegalArgumentException if validation fails so URI.create(url)
won't receive malformed input.
- Line 76: The UID extraction at LraTools (variable lraId -> uid) is fragile;
validate lraId before substring: ensure lraId is non-null/non-empty, parse it as
a URI or compute lastSlashIndex = lraId.lastIndexOf('/') and verify
lastSlashIndex >= 0 and lastSlashIndex < lraId.length() - 1 (no trailing slash)
before calling substring; if validation fails, throw a clear
IllegalArgumentException or return a controlled failure, and prefer using
java.net.URI to extract the path's last segment for robust behavior.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b215f2f3-b266-4341-9a9c-74bf48415669
📒 Files selected for processing (9)
pom.xmlrts/lra/lra-ai-dashboard/README.mdrts/lra/lra-ai-dashboard/pom.xmlrts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAiChatResource.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraAssistant.javarts/lra/lra-ai-dashboard/src/main/java/io/narayana/lra/ai/LraTools.javarts/lra/lra-ai-dashboard/src/main/resources/META-INF/resources/index.htmlrts/lra/lra-ai-dashboard/src/main/resources/application.propertiesrts/lra/pom.xml
| ```bash | ||
| curl -s -X POST http://localhost:8082/chat \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"message": "Are there any stuck transactions?"}' | jq . | ||
| ``` |
There was a problem hiding this comment.
Verify jq usage with plain-text streaming response.
The curl example pipes the response to jq, but based on the implementation in LraAiChatResource.java, the endpoint returns @Produces(MediaType.TEXT_PLAIN) with Multi<String> streaming, not JSON. The jq command will likely fail or produce unexpected output. Consider removing | jq . or clarifying that the response is plain text.
📝 Suggested fix
curl -s -X POST http://localhost:8082/chat \
-H "Content-Type: application/json" \
- -d '{"message": "Are there any stuck transactions?"}' | jq .
+ -d '{"message": "Are there any stuck transactions?"}'🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 96 - 100, The README's curl
example pipes output to jq but the chat endpoint implemented in
LraAiChatResource.java produces plain-text streaming (annotated with
`@Produces`(MediaType.TEXT_PLAIN) and returns a Multi<String>), so jq is
inappropriate; update the example to remove "| jq ." or add a note that the
endpoint streams plain text (not JSON) and show using curl alone or redirecting
to a file for streaming output to match the LraAiChatResource.java behavior.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
rts/lra/lra-ai-dashboard/README.md (2)
168-177:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd the missing
deleteFailedLRAtool to the table.The table omits
deleteFailedLRA, which is implemented inLraTools.javaand is a critical tool for manual recovery of failed LRAs. This tool performs a DELETE operation on/lra-coordinator/recovery/{uid}and is the only way to remove FailedToCancel/FailedToClose LRAs from the failed store.📝 Suggested fix
Add the missing row to the table between
listFailedLRAs()andcloseLRA():| `listFailedLRAs()` | `GET /lra-coordinator/recovery/failed` | Find transactions needing manual action | +| `deleteFailedLRA(lraId)` | `DELETE /lra-coordinator/recovery/{uid}` | Remove a failed LRA from the coordinator's failed store | | `closeLRA(lraId)` | `PUT {lraId}/close` | Operator-requested completion |🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 168 - 177, The table is missing the deleteFailedLRA tool — add a row for deleteFailedLRA referencing the DELETE /lra-coordinator/recovery/{uid} endpoint (this method is implemented in LraTools.java) and place it between the listFailedLRAs() and closeLRA() rows; ensure the displayed signature is `deleteFailedLRA(uid)` and the description notes it removes FailedToCancel/FailedToClose LRAs from the failed store.
163-164:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winCorrect the HTTP method description.
Line 163 incorrectly states "Each method makes a single blocking HTTP GET" but the tools in
LraTools.javause multiple HTTP methods: GET for read operations, PUT for close/cancel, POST for start, and DELETE for deleteFailedLRA.📝 Suggested fix
-Six `@Tool`-annotated methods that form the saga-domain tool schema described in the patent. -Each method makes a single blocking HTTP GET to the coordinator and returns the raw JSON response +Ten `@Tool`-annotated methods that form the saga-domain tool schema. +Each method makes a single blocking HTTP request to the coordinator and returns the response for the LLM to reason over.🤖 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 `@rts/lra/lra-ai-dashboard/README.md` around lines 163 - 164, Update the README sentence that claims "Each method makes a single blocking HTTP GET" to accurately describe the HTTP methods used by LraTools.java: state that methods perform blocking HTTP requests using GET for read operations, POST for start, PUT for close/cancel, and DELETE for deleteFailedLRA (or other delete operations), and mention that each returns the raw JSON response for the LLM; reference LraTools.java and specific method names like start, close/cancel, and deleteFailedLRA so the reader can verify the mapping.
🤖 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.
Outside diff comments:
In `@rts/lra/lra-ai-dashboard/README.md`:
- Around line 168-177: The table is missing the deleteFailedLRA tool — add a row
for deleteFailedLRA referencing the DELETE /lra-coordinator/recovery/{uid}
endpoint (this method is implemented in LraTools.java) and place it between the
listFailedLRAs() and closeLRA() rows; ensure the displayed signature is
`deleteFailedLRA(uid)` and the description notes it removes
FailedToCancel/FailedToClose LRAs from the failed store.
- Around line 163-164: Update the README sentence that claims "Each method makes
a single blocking HTTP GET" to accurately describe the HTTP methods used by
LraTools.java: state that methods perform blocking HTTP requests using GET for
read operations, POST for start, PUT for close/cancel, and DELETE for
deleteFailedLRA (or other delete operations), and mention that each returns the
raw JSON response for the LLM; reference LraTools.java and specific method names
like start, close/cancel, and deleteFailedLRA so the reader can verify the
mapping.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b628063a-eda3-4765-9e1e-e55309b0b30c
📒 Files selected for processing (1)
rts/lra/lra-ai-dashboard/README.md
|
Further discussion is happening in the related issue (i.e. #741) |
|
I am going to close this PR at the moment as we are still to decide what is the more suitable github project for AI related quickstart/modules. I'll keep the issue open for further discussion. |
Addresses #741
Creating the lra-ai-dashboard quickstart which adds quarkus langchain4j dependency to set up an LLM assistant for working with the LRA coordinator for operations and diagnosis.
I am going to publish a blog about this quickstart and provide a little demo how to deal with a failed LRA with the help of an LLM.