Conversation
Add AI-powered database assistant with chat UI in Studio and backend
endpoints in the server module. Routes registered directly in HttpServer
(same pattern as MCP). Inactive until subscription token is set in
config/ai.json.
Backend (server module):
- AiConfiguration: loads config/ai.json (subscriptionToken, gatewayUrl)
- AiChatHandler: POST /api/v1/ai/chat - collects schema, forwards to gateway
- AiChatsHandler: GET/DELETE /api/v1/ai/chats - file-based chat persistence
- AiConfigHandler: GET /api/v1/ai/config - subscription status
- ChatStorage: per-user JSON file storage under chats/{username}/
- 19 tests (unit + integration)
Frontend (studio module):
- ai.html: chat tab with sidebar history, command blocks, inactive state
- studio-ai.js: chat logic, markdown rendering, command execution
- marked.js vendor dependency (MIT) for markdown in chat messages
Fixed issue #1339
… is not available
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant new AI Assistant feature to ArcadeDB Studio, enhancing the user experience by providing intelligent, context-aware help for database management and optimization. It includes server-side APIs for AI integration, client-side UI components for interaction, and mechanisms for secure activation and chat history management. Users can now leverage AI for tasks like schema review, query optimization, and profiler analysis directly within the Studio. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
🧪 CI InsightsHere's what we observed from your CI run for 9f58abe. 🟢 All jobs passed!But CI Insights is watching 👀 |
There was a problem hiding this comment.
Code Review
This pull request introduces a significant new AI assistant feature, including backend handlers for activation, chat, and profiler analysis, as well as a comprehensive frontend UI in Studio. The implementation is extensive and well-structured, with new services, storage mechanisms, and tests. The backend code demonstrates good practices, including security checks and robust error handling. The frontend provides a rich, interactive experience for the new AI capabilities. I've identified a potential stability issue in the hardware ID generation and an opportunity to improve maintainability by reducing code duplication in the JavaScript files.
Note: Security Review did not run due to the size of the PR.
| final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); | ||
| while (interfaces.hasMoreElements()) { | ||
| final NetworkInterface ni = interfaces.nextElement(); | ||
| if (ni.isLoopback() || ni.isVirtual()) | ||
| continue; | ||
| final byte[] mac = ni.getHardwareAddress(); | ||
| if (mac != null) { | ||
| for (final byte b : mac) | ||
| raw.append(String.format("%02x", b)); | ||
| raw.append("|"); | ||
| } | ||
| } |
There was a problem hiding this comment.
The order of network interfaces returned by NetworkInterface.getNetworkInterfaces() is not guaranteed to be consistent across JVM restarts. This could lead to the hardwareId changing for the same machine, potentially causing issues with the AI subscription activation. To ensure a stable hardware ID, the collected MAC addresses should be sorted before being hashed.
| final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); | |
| while (interfaces.hasMoreElements()) { | |
| final NetworkInterface ni = interfaces.nextElement(); | |
| if (ni.isLoopback() || ni.isVirtual()) | |
| continue; | |
| final byte[] mac = ni.getHardwareAddress(); | |
| if (mac != null) { | |
| for (final byte b : mac) | |
| raw.append(String.format("%02x", b)); | |
| raw.append("|"); | |
| } | |
| } | |
| // Collect all non-loopback MAC addresses | |
| final java.util.List<String> macs = new java.util.ArrayList<>(); | |
| final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); | |
| while (interfaces.hasMoreElements()) { | |
| final NetworkInterface ni = interfaces.nextElement(); | |
| if (ni.isLoopback() || ni.isVirtual()) | |
| continue; | |
| final byte[] mac = ni.getHardwareAddress(); | |
| if (mac != null) { | |
| final StringBuilder macBuilder = new StringBuilder(); | |
| for (final byte b : mac) | |
| macBuilder.append(String.format("%02x", b)); | |
| macs.add(macBuilder.toString()); | |
| } | |
| } | |
| java.util.Collections.sort(macs); | |
| for (final String mac : macs) { | |
| raw.append(mac); | |
| raw.append("|"); | |
| } |
| function profilerAnalyzeWithAi() { | ||
| if (!profilerData) { | ||
| globalNotify("Profiler", "No profiler data to analyze", "warning"); | ||
| return; | ||
| } | ||
|
|
||
| var btn = jQuery("#profilerAiBtn"); | ||
| btn.prop("disabled", true).html('<i class="fa fa-spinner fa-spin me-1"></i>Analyzing...'); | ||
|
|
||
| jQuery("#profilerAiPanel").show(); | ||
| jQuery("#profilerAiContent").html( | ||
| '<div class="text-center py-3" style="color: var(--text-muted);">' + | ||
| '<i class="fa fa-spinner fa-spin me-1"></i> Analyzing profiler data...</div>' | ||
| ); | ||
|
|
||
| jQuery.ajax({ | ||
| type: "POST", | ||
| url: "api/v1/ai/analyze-profiler", | ||
| data: JSON.stringify({ profilerData: profilerData }), | ||
| contentType: "application/json", | ||
| headers: { Authorization: globalCredentials }, | ||
| timeout: 120000 | ||
| }) | ||
| .done(function(data) { | ||
| btn.prop("disabled", false).html('<i class="fa fa-robot"></i> Analyze with AI'); | ||
| profilerRenderAiResponse(data); | ||
| }) | ||
| .fail(function(jqXHR) { | ||
| btn.prop("disabled", false).html('<i class="fa fa-robot"></i> Analyze with AI'); | ||
| var errorMsg = "Failed to analyze profiler data."; | ||
| try { | ||
| var errData = JSON.parse(jqXHR.responseText); | ||
| if (errData.error) errorMsg = errData.error; | ||
| } catch (e) { /* ignore */ } | ||
| jQuery("#profilerAiContent").html( | ||
| '<div style="color: #dc3545;"><i class="fa fa-circle-exclamation me-1"></i>' + escapeHtml(errorMsg) + '</div>' | ||
| ); | ||
| }); | ||
| } | ||
|
|
||
| function profilerRenderAiResponse(data) { | ||
| profilerAiCommandCounter = 0; | ||
| var contentHtml = ''; | ||
|
|
||
| // Render markdown content | ||
| if (data.response) { | ||
| if (typeof marked !== "undefined") { | ||
| try { contentHtml = marked.parse(data.response); } | ||
| catch (e) { contentHtml = '<pre>' + escapeHtml(data.response) + '</pre>'; } | ||
| } else | ||
| contentHtml = '<pre style="white-space: pre-wrap;">' + escapeHtml(data.response) + '</pre>'; | ||
| } | ||
|
|
||
| var html = '<div class="ai-message-content" style="color: var(--text-primary); line-height: 1.6; font-size: 0.9rem;">' + contentHtml + '</div>'; | ||
|
|
||
| // Render command blocks | ||
| if (data.commands && data.commands.length > 0) { | ||
| if (data.commands.length > 1) | ||
| html += '<div class="mt-2 mb-2"><button class="btn btn-sm" style="background: var(--color-brand); color: white; border: none;" onclick="profilerAiExecuteAll(this)">' + | ||
| '<i class="fa fa-forward me-1"></i>Execute All</button></div>'; | ||
|
|
||
| for (var j = 0; j < data.commands.length; j++) | ||
| html += profilerRenderAiCommandBlock(data.commands[j]); | ||
| } | ||
|
|
||
| jQuery("#profilerAiContent").html(html); | ||
| } |
There was a problem hiding this comment.
There is significant code duplication between studio-profiler.js and studio-ai.js for rendering and handling AI-suggested command blocks. For example, profilerRenderAiCommandBlock and aiRenderCommandBlock are very similar, as are the helper functions for command execution (profilerAiExecuteCommand, profilerAiExecuteAll, etc.). To improve maintainability and reduce redundancy, this shared functionality should be refactored into a common utility file (e.g., studio-ai-common.js). This would ensure that any future changes to command block rendering or execution only need to be made in one place.
PR Review: Studio AI AssistantThis PR adds an AI assistant feature to ArcadeDB Studio, enabling schema analysis, query optimization, and profiler analysis via an external AI gateway. The implementation is well-structured overall, but there are several issues worth addressing before merging. 🔴 Security Issues1. XSS via Unescaped Markdown (High)
// studio-profiler.js
contentHtml = marked.parse(data.response);
// studio-ai.js
return marked.parse(text, { renderer: renderer });In
2. External Gateway Receives Live Session Tokens (High)In final HttpAuthSession authSession = httpServer.getAuthSessionManager().createSession(user);
gatewayRequest.put("arcadedb", new JSONObject()
.put("url", serverUrl)
.put("sessionId", authSession.token));This allows the external gateway to call any endpoint on the user's ArcadeDB server using the delegated session. The UI labels this as "read-only" but there is no server-side enforcement preventing the gateway from issuing write operations with this session. Consider:
3. X-Forwarded-For Header is Untrusted (Low)// AiActivateHandler.java
final String forwarded = exchange.getRequestHeaders().getFirst("X-Forwarded-For");
if (forwarded != null && !forwarded.isEmpty())
return forwarded.split(",")[0].trim();The client IP used for activation logging can be spoofed. Not critical (it's only stored for auditing), but worth noting. 🟠 Performance Issues4. Hardware ID Computed on Every Request (Medium)
// AiChatHandler.java and AiAnalyzeProfilerHandler.java
gatewayRequest.put("hardwareId", AiActivateHandler.getHardwareId());This should be computed once at startup and cached in 5. HttpClient Not Shared (Low)Each handler instance creates its own 🟡 Correctness & Reliability Issues6. Chat/Config Files Written Non-Atomically (Medium)Both try (final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
writer.write(chat.toString(2));
}A crash mid-write will corrupt the file. The standard fix is to write to a temp file first, then 7. No Limit on Chat Storage (Low)
A malicious or careless user could exhaust disk space. Consider adding a max-chats-per-user limit and a max-messages-per-chat cap. 8. SSE Streaming — Resource Cleanup in Error Path (Low)In 🔵 Minor / Code Quality9. Sanitize Username Collisions
10.
|
Coverage summary from CodacySee diff coverage on Codacy
Coverage variation details
Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: Diff coverage details
Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: See your quality gate settings Change summary preferences |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3574 +/- ##
==========================================
- Coverage 65.91% 65.80% -0.11%
==========================================
Files 1506 1514 +8
Lines 103092 103642 +550
Branches 21381 21448 +67
==========================================
+ Hits 67956 68206 +250
- Misses 25920 26174 +254
- Partials 9216 9262 +46 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
* feat: add ArcadeDBi AI assistant to Studio
Add AI-powered database assistant with chat UI in Studio and backend
endpoints in the server module. Routes registered directly in HttpServer
(same pattern as MCP). Inactive until subscription token is set in
config/ai.json.
Backend (server module):
- AiConfiguration: loads config/ai.json (subscriptionToken, gatewayUrl)
- AiChatHandler: POST /api/v1/ai/chat - collects schema, forwards to gateway
- AiChatsHandler: GET/DELETE /api/v1/ai/chats - file-based chat persistence
- AiConfigHandler: GET /api/v1/ai/config - subscription status
- ChatStorage: per-user JSON file storage under chats/{username}/
- 19 tests (unit + integration)
Frontend (studio module):
- ai.html: chat tab with sidebar history, command blocks, inactive state
- studio-ai.js: chat logic, markdown rendering, command execution
- marked.js vendor dependency (MIT) for markdown in chat messages
* Update ai.html
* feat: parallel query
Fixed issue #1339
* fx: fixed parallel query: avoided when can deadlock
Fixed issue #1339
* Added test file
* Improvements
* studio: Add ai profiler
* Implemented Profiler AI Analyze
* Misc improvements
* fix: map Cypher to OpenCypher when the old cypher module (on gremlin) is not available
* fix: display button at the bottom of the code block now
* removed ai.json cfg file
* Improved activation with disclaimer
* Fixed studio AI panel
* studio: 2 modes of execution in ai panel
* Improved agentic system
* studio: added Drop Type button
* feat: studio -> double confirmation on database deletion
(cherry picked from commit 993588d)
What does this PR do?
A brief description of the change being made with this pull request.
Motivation
What inspired you to submit this pull request?
Related issues
A list of issues either fixed, containing architectural discussions, otherwise relevant
for this Pull Request.
Additional Notes
Anything else we should know when reviewing?
Checklist
mvn clean packagecommand