Skip to content

fix: address PR review feedback for AI Query Builder#205

Merged
yash-pouranik merged 5 commits into
mainfrom
fix/ai-query-builder-review
May 25, 2026
Merged

fix: address PR review feedback for AI Query Builder#205
yash-pouranik merged 5 commits into
mainfrom
fix/ai-query-builder-review

Conversation

@yash-pouranik
Copy link
Copy Markdown
Collaborator

@yash-pouranik yash-pouranik commented May 25, 2026

Description

This PR introduces the AI Query Builder feature, allowing users to filter their database collections using natural language (powered by llama-3.1-8b-instant). It includes the full stack implementation across the React frontend, Node.js Dashboard API, and a new Python microservice, along with strict security measures.

✨ Key Features

  • Natural Language Filtering: Users can type queries like "show me posts having price more than 1 lakh" and the AI automatically maps it to MongoDB filters matching their specific collection schema.
  • Python Microservice: Added a dedicated FastAPI python service optimized for LLM tasks.
  • HMAC-SHA256 Internal Security: Implemented cryptographic signature verification and timestamp-based replay attack prevention for secure internal communication between the Node.js API and Python microservice.

🛠️ Review Feedback Addressed (Fixes)

  • [P0] Secret Hygiene: Replaced plaintext credentials in apps/python-service/.env with local dummy placeholders to prevent production secret exposure.
  • [P1] RLS & Schema Guard: Added explicit server-side blocking for the users collection in the AI endpoint to prevent exposing sensitive auth fields (like passwords) to the LLM context.
  • [P1] Strict Type Normalization: Added rigorous type-checking in ai.controller.js to guarantee filters is always an Array with properly shaped objects, preventing frontend runtime crashes.
  • [P1] Test Suite Fixes: Fixed broken imports in Python auth tests (test_auth.py) to correctly mock and utilize dependencies.py instead of non-existent middleware.
  • [P2] Repo Hygiene: Added comprehensive Python ignore rules (__pycache__/, venv/, .pytest_cache/) to the root .gitignore.

🧪 Verification Steps

  1. Navigate to any collection (except users) in the Dashboard.
  2. Open the AI Query Bar and type a natural language prompt.
  3. Verify that the correct filters are applied to the table.
  4. Verify that attempting to run an AI query against the users collection explicitly throws a 403 error.
  5. Run Python tests pytest apps/python-service/tests to verify internal security HMAC validations pass successfully.

Summary by CodeRabbit

  • New Features

    • AI Query Builder: generate collection filters and sorting from natural-language prompts in the dashboard UI.
  • User Experience

    • Added an AI query input in the database header to run prompts and apply generated filters/sort.
  • Infrastructure

    • Introduced a Python microservice for AI processing and secure signed communication with replay protection.
    • Updated environment and tooling examples and ignore rules for local dev.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Warning

Review limit reached

@yash-pouranik, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 7 minutes and 55 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a696e382-94c1-4770-8a95-58a8f905e487

📥 Commits

Reviewing files that changed from the base of the PR and between dee015f and 3a862f8.

📒 Files selected for processing (8)
  • apps/dashboard-api/src/controllers/ai.controller.js
  • apps/dashboard-api/test_python.js
  • apps/python-service/config.py
  • apps/python-service/dependencies.py
  • apps/python-service/requirements.txt
  • apps/python-service/routers/ai.py
  • apps/python-service/tests/test_auth.py
  • apps/web-dashboard/src/components/Database/AiQueryBar.jsx
📝 Walkthrough

Walkthrough

Adds an AI Query Builder: a Python FastAPI microservice (LangChain/Groq) and verification, a Dashboard API controller and signed forwarding client, web UI components to submit prompts and apply generated filters/sort, tests for signing/verification, and supporting config/workflow updates.

Changes

AI Query Builder Feature Implementation

Layer / File(s) Summary
Project configuration and dependencies
.aiignore, .env.example, .github/workflows/weekly-changelog.yml, .gitignore, .vscode/settings.json, apps/python-service/.env.example, apps/python-service/requirements.txt, apps/python-service/config.py, docs/ISSUES/project-rate-limiting.md
Adds ignore rules, example env variables for internal services, pinned Python deps, Pydantic settings loader, and project-rate-limiting documentation.
Inter-Service Request Signing and Verification
apps/dashboard-api/src/utils/internalPythonClient.js, apps/dashboard-api/src/__tests__/internalPythonClient.test.js, apps/python-service/dependencies.py, apps/python-service/tests/test_auth.py
Forwarding client signs JSON payloads with timestamp+HMAC-SHA256; Python dependency verifies signature/timestamp, enforces 30s freshness, and uses Redis to prevent replay. Tests cover missing secret, signature construction/validation, timestamp freshness, and replay detection.
Python Service Foundation and AI Endpoint
apps/python-service/main.py, apps/python-service/routers/ai.py
FastAPI app with /health and POST /ai/query-builder that uses LangChain ChatGroq with structured output to return filters and sort according to Pydantic models.
Dashboard API Query Builder Controller and Routes
apps/dashboard-api/src/app.js, apps/dashboard-api/src/routes/ai.routes.js, apps/dashboard-api/src/controllers/ai.controller.js
Adds authenticated /api/projects/:projectId/ai/query-builder route and controller that validates input, fetches project/collection schema, builds schema_fields, forwards signed request to Python service, normalizes and filters the response, and maps downstream errors.
Web Dashboard UI Components and State Management
apps/web-dashboard/src/components/Database/AiQueryBar.jsx, apps/web-dashboard/src/components/Database/DatabaseHeader.jsx, apps/web-dashboard/src/components/Database/DatabaseFilter.jsx, apps/web-dashboard/src/pages/Database.jsx
New AiQueryBar component, conditional rendering in DatabaseHeader (hidden for users), added built-in field options, and handleFiltersGenerated on Database page to apply filters/sort and reset pagination.
Manual Testing Script
apps/dashboard-api/test_python.js
Node.js script to sign and POST test requests to the local Python service for manual verification.

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant WebDashboard as Web Dashboard
  participant DashboardAPI as Dashboard API
  participant PythonService as Python Service
  participant Redis as Redis
  
  User->>WebDashboard: Enter prompt in AiQueryBar and submit
  WebDashboard->>DashboardAPI: POST /api/projects/:projectId/ai/query-builder (prompt, collectionName)
  DashboardAPI->>DashboardAPI: Stringify payload & compute timestamp
  DashboardAPI->>DashboardAPI: HMAC-SHA256("timestamp.payload")
  DashboardAPI->>PythonService: POST /ai/query-builder with X-Internal-Signature & X-Timestamp
  PythonService->>PythonService: Verify GROQ_API_KEY present
  PythonService->>PythonService: Verify timestamp freshness (30s) and recompute HMAC
  PythonService->>Redis: GET nonce(timestamp, signature)
  Redis-->>PythonService: null
  PythonService->>Redis: SET nonce with 30s TTL
  PythonService->>PythonService: Invoke LangChain ChatGroq -> structured filters & sort
  PythonService-->>DashboardAPI: 200 OK with filters[], sort
  DashboardAPI->>DashboardAPI: Normalize filters and validate fields/operators
  DashboardAPI-->>WebDashboard: 200 OK with validated filters & sort
  WebDashboard->>WebDashboard: Update queryParams, reset page to 1 and refetch
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

feature, type:feature, level3

🐰 I scurried through code with a tiny hop and cheer,
Signed the payloads with secrets kept near,
LangChain hums, filters sprout from a prompt's light,
Redis guards replay in the short, safe night,
Hooray — queries bloom and the dashboard sings bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly addresses the stated objective of addressing PR review feedback for the AI Query Builder feature, accurately summarizing the primary change scope.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ai-query-builder-review

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

❤️ Share

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

Comment thread apps/dashboard-api/src/controllers/ai.controller.js Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds/finishes the AI Query Builder feature across Dashboard UI + Dashboard API + a new internal FastAPI microservice, with HMAC-signed internal calls and some repo hygiene/doc updates.

Changes:

  • Dashboard UI: add an AI query bar to generate filters/sort from natural language (blocked for users).
  • Dashboard API: add /api/projects/:projectId/ai/query-builder, forward to Python service with HMAC signature, and normalize the returned filter payload.
  • Python microservice: introduce FastAPI app with HMAC verification + replay protection and an AI router that uses Groq/LangChain structured output.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
docs/ISSUES/project-rate-limiting.md Adds an issue doc describing per-project/API-key rate limiting gaps and proposed fixes.
apps/web-dashboard/src/pages/Database.jsx Wires AI-generated filters/sort into existing query param state.
apps/web-dashboard/src/components/Database/DatabaseHeader.jsx Renders the new AI query bar for non-users collections.
apps/web-dashboard/src/components/Database/DatabaseFilter.jsx Adds _id/createdAt/updatedAt as selectable filter fields.
apps/web-dashboard/src/components/Database/AiQueryBar.jsx New UI component to submit prompts and apply returned filters/sort.
apps/python-service/tests/test_auth.py New tests for internal signature verification + replay protection.
apps/python-service/routers/ai.py New AI endpoint using Groq/LangChain structured output.
apps/python-service/requirements.txt Pins Python service dependencies (FastAPI, LangChain, Redis, etc.).
apps/python-service/main.py FastAPI app entrypoint + router inclusion.
apps/python-service/dependencies.py Implements HMAC verification and Redis-backed replay protection.
apps/python-service/config.py Adds pydantic-settings config for Python service env.
apps/python-service/.env.example Adds example env vars for the Python service.
apps/dashboard-api/test_python.js Adds a local script to manually test HMAC calls to Python service.
apps/dashboard-api/src/utils/internalPythonClient.js Adds HMAC-signed forwarding client to the Python service.
apps/dashboard-api/src/routes/ai.routes.js Adds authenticated AI route wiring under project scope.
apps/dashboard-api/src/controllers/ai.controller.js Adds controller to build schema payload, call Python, and return filters/sort.
apps/dashboard-api/src/app.js Registers the new AI routes under /api/projects/:projectId/ai.
apps/dashboard-api/src/tests/internalPythonClient.test.js Adds unit tests for HMAC signature generation and env handling.
.vscode/settings.json Suppresses Git ignore limit warning in VS Code workspace.
.gitignore Adds Python ignores (__pycache__, venv, pytest cache, etc.).
.github/workflows/weekly-changelog.yml Adjusts frontmatter file creation logic for changelog generation.
.env.example Documents Python service URL + INTERNAL_SECRET + GROQ key placeholders.
.aiignore Adds ignore patterns for env/secrets files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +56
// 4. Return the structured JSON to the frontend
// Ensure filters is always an array to prevent frontend crash
const rawFilters = Array.isArray(aiResponse.filters) ? aiResponse.filters : [];
const safeFilters = rawFilters.filter(f => f && typeof f.field === 'string' && typeof f.operator === 'string' && f.value !== undefined);
Comment on lines +16 to +19
class QueryResult(BaseModel):
filters: List[FilterItem] = Field(description="List of MongoDB filters to apply to the frontend")
sort: str = Field(description="MongoDB sort string, e.g. '-createdAt' or 'name'. Default to '-createdAt'")

Comment thread apps/python-service/routers/ai.py Outdated
Comment on lines +66 to +67
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Comment on lines +1 to +12
import pytest
import hmac
import hashlib
import time
from fastapi import FastAPI, Request
from fastapi.testclient import TestClient
from unittest.mock import AsyncMock, patch

from dependencies import verify_signature
from config import settings
from fastapi import Depends

Comment on lines +10 to +25
const handleSubmit = async (e) => {
e.preventDefault();
if (!prompt.trim()) return;

setIsLoading(true);
try {
const res = await api.post(`/api/projects/${projectId}/ai/query-builder`, {
collectionName: activeCollection?.name,
prompt: prompt.trim()
});

if (res.data?.success && res.data?.data) {
const { filters, sort } = res.data.data;
onFiltersGenerated(filters, sort);
toast.success('AI query applied!');
setPrompt('');
Comment on lines +56 to +62
<input
type="text"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Ask AI to filter data..."
disabled={isLoading || !activeCollection}
style={{
… from user-controlled sources'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Agent-Logs-Url: https://github.com/geturbackend/urBackend/sessions/a5a8938b-e932-4e90-ab1f-ef1546aaf66e

Co-authored-by: yash-pouranik <172860064+yash-pouranik@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (5)
apps/python-service/routers/ai.py (2)

59-62: ⚡ Quick win

Consider adding a timeout to the LLM invocation.

The chain.ainvoke call has no timeout, so if the Groq API is slow or unresponsive, the request could hang indefinitely. This could exhaust connection pools or cause cascading timeouts upstream.

♻️ Suggested improvement using asyncio.wait_for
+import asyncio
+
         # Invoke the chain
-        result = await chain.ainvoke({
-            "schema": str(request.schema_fields),
-            "user_prompt": request.prompt
-        })
+        try:
+            result = await asyncio.wait_for(
+                chain.ainvoke({
+                    "schema": str(request.schema_fields),
+                    "user_prompt": request.prompt
+                }),
+                timeout=30.0  # 30 second timeout
+            )
+        except asyncio.TimeoutError:
+            raise HTTPException(status_code=504, detail="LLM request timed out")
🤖 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 `@apps/python-service/routers/ai.py` around lines 59 - 62, The chain.ainvoke
call can hang indefinitely; wrap the call in an asyncio.wait_for with a sensible
timeout (e.g., configurable constant) and handle asyncio.TimeoutError to
return/raise an appropriate error response; update the code around chain.ainvoke
in routers/ai.py (the async handler that calls chain.ainvoke) to import asyncio,
call await asyncio.wait_for(chain.ainvoke({...}), TIMEOUT_SECONDS), and add a
try/except to catch TimeoutError and log/return a controlled error instead of
letting the request hang.

20-22: 💤 Low value

Add type annotation for schema_fields.

schema_fields: list lacks a specific type annotation, reducing API documentation clarity and validation. Consider specifying the expected element type.

♻️ Suggested improvement
+from typing import List, Union, Any
+
 class QueryBuilderRequest(BaseModel):
     prompt: str
-    schema_fields: list
+    schema_fields: List[str]  # or List[dict] if complex schema objects
🤖 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 `@apps/python-service/routers/ai.py` around lines 20 - 22, The
QueryBuilderRequest model's schema_fields is declared as an untyped list; change
it to a typed sequence (e.g., List[str] or list[str]) to improve validation and
OpenAPI docs: update the QueryBuilderRequest class to use a concrete element
type for schema_fields (and add the necessary typing import such as List from
typing if using Python <3.9) so Pydantic knows the expected item type.
apps/web-dashboard/src/pages/Database.jsx (1)

159-166: ⚡ Quick win

Add defensive validation for aiFilters parameter.

While the backend controller is documented to enforce that filters is always an Array, the frontend should defensively validate the parameter to prevent potential runtime errors if the API contract changes or returns unexpected data. Line 107 calls queryParams.filters.forEach, which will throw if filters is not an array.

🛡️ Proposed validation
 const handleFiltersGenerated = (aiFilters, aiSort) => {
+    if (!Array.isArray(aiFilters)) {
+        console.warn('AI filters returned non-array value:', aiFilters);
+        return;
+    }
     setQueryParams(prev => ({
         ...prev,
         page: 1,
         filters: aiFilters,
         sort: aiSort || prev.sort
     }));
 };
🤖 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 `@apps/web-dashboard/src/pages/Database.jsx` around lines 159 - 166,
handleFiltersGenerated should defensively ensure aiFilters is an Array before
writing it into state to avoid runtime errors where queryParams.filters.forEach
is called; update handleFiltersGenerated to set filters to
Array.isArray(aiFilters) ? aiFilters : (prev.filters || []) when calling
setQueryParams and leave sort logic unchanged (refer to handleFiltersGenerated
and the setQueryParams call and the later usage of queryParams.filters.forEach).
apps/web-dashboard/src/components/Database/AiQueryBar.jsx (1)

72-81: 💤 Low value

Consider extracting the inline <style> block to avoid DOM duplication.

The inline <style> tag will be duplicated in the DOM for each instance of AiQueryBar, which can increase memory footprint if multiple instances are rendered. React 19 allows inline styles, but using a module-level CSS file, CSS modules, or a single global stylesheet would be more efficient.

♻️ Suggested approach using a separate CSS module

Create AiQueryBar.module.css:

`@keyframes` shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.aiQueryBar:focus-within {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
}

Then import and apply:

+import styles from './AiQueryBar.module.css';

 <form 
   onSubmit={handleSubmit} 
-  className="ai-query-bar"
+  className={styles.aiQueryBar}
   ...
 >
   ...
-  <style>{`...`}</style>
 </form>
🤖 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 `@apps/web-dashboard/src/components/Database/AiQueryBar.jsx` around lines 72 -
81, Extract the inline <style> block out of the AiQueryBar component into a
separate CSS module (e.g., AiQueryBar.module.css) to avoid DOM duplication; move
the `@keyframes` shimmer and the focus-within rule there, rename the selector to
match a module-safe class (e.g., .aiQueryBar) and then import that module into
AiQueryBar.jsx and apply the class via className (replace the literal
"ai-query-bar" usage with the imported module class) so the animation and focus
styling are provided from the module stylesheet instead of being injected inline
for each instance.
apps/dashboard-api/test_python.js (1)

5-13: ⚡ Quick win

Use environment variables for secret and URL in the test script.

Hardcoded values make local verification brittle and can encourage unsafe secret handling.

♻️ Proposed refactor
 const axios = require('axios');
 const crypto = require('crypto');
 
 const run = async () => {
     const timestamp = Date.now().toString();
     const payload = JSON.stringify({ prompt: "test", schema_fields: [] });
-    const secret = "test-secret";
+    const secret = process.env.INTERNAL_SECRET;
+    const pythonUrl = process.env.PYTHON_SERVICE_URL || 'http://127.0.0.1:8000';
+
+    if (!secret) {
+        throw new Error('INTERNAL_SECRET is required');
+    }
@@
-        const res = await axios.post('http://127.0.0.1:8000/ai/query-builder', payload, {
+        const res = await axios.post(`${pythonUrl}/ai/query-builder`, payload, {
🤖 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 `@apps/dashboard-api/test_python.js` around lines 5 - 13, Replace hardcoded
secret and URL with environment variables: read the secret into the existing
secret variable (e.g., process.env.TEST_SECRET or process.env.AI_TEST_SECRET)
and read the request URL to use in axios.post (e.g., process.env.API_URL or
process.env.AI_API_URL), validate they exist (throw or exit with a clear message
if missing), then keep using those variables when computing signature (signature
= crypto.createHmac(..., secret)...) and when calling axios.post (use the URL
variable instead of 'http://127.0.0.1:8000/ai/query-builder').
🤖 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 `@apps/dashboard-api/src/controllers/ai.controller.js`:
- Around line 13-24: Validate and normalize inputs before calling
Project.findOne: ensure collectionName and prompt are strings and non-empty
after trimming (e.g., const collectionNameTrim = String(collectionName).trim();
const promptTrim = String(prompt).trim()), throwing AppError(400, ...) if
invalid; validate projectId using mongoose.Types.ObjectId.isValid (or
equivalent) and throw AppError(400, ...) if not a valid ObjectId; then use the
normalized collectionNameTrim and validated projectId in the Project.findOne
selector (replace "collections.name": collectionName with "collections.name":
collectionNameTrim and _id: projectId with the validated ObjectId) and keep the
users check against the trimmed value.

In `@apps/python-service/config.py`:
- Around line 4-5: The INTERNAL_SECRET field currently allows any string; update
the settings validation in the config (the BaseSettings/Settings class that
defines INTERNAL_SECRET) to require a minimum length of at least 32 characters
(e.g., use pydantic.constr(min_length=32) or add a `@validator` for
INTERNAL_SECRET) so weak HMAC keys are rejected; ensure the validation produces
a clear error message and update any tests or instantiation sites that expect a
shorter default.

In `@apps/python-service/dependencies.py`:
- Around line 20-21: Update the except block to preserve the original traceback
by capturing the ValueError and re-raising HTTPException with exception
chaining: change the handler to "except ValueError as e:" and then "raise
HTTPException(status_code=403, detail='Invalid timestamp format') from e" so the
original error context is retained (modify the except ValueError block in
apps/python-service/dependencies.py).
- Around line 41-48: Replace the non-atomic check-then-set nonce logic using
redis_client.get(...) and redis_client.set(...) with a single atomic Redis SET
using NX and EX options (use redis_client.set with nx=True, ex=30 or equivalent)
to ensure only the first request succeeds when creating nonce_key; if the SET
returns a falsy result, raise HTTPException(status_code=403, detail="Replay
attack detected"). Also update the ValueError handler to chain the original
exception when re-raising the HTTPException (e.g., in the except ValueError as
err: block raise HTTPException(...) from err) so ruff B904 is addressed; look
for references to nonce_key, redis_client, and the except ValueError handler to
locate the code.

In `@apps/python-service/requirements.txt`:
- Around line 3-4: The requirements.txt pins vulnerable LangChain packages;
update langchain and langchain-groq to non-vulnerable versions (or remove exact
pins) and re-run a dependency audit; specifically edit
apps/python-service/requirements.txt to replace langchain==0.1.16 and
langchain-groq==0.1.3 with safe versions (or use >= constraints) determined by
running pip-audit, then run project tests and the provided pip_audit command to
confirm advisories are resolved before merging.

In `@apps/python-service/routers/ai.py`:
- Around line 66-67: Replace the bare except block that does "except Exception
as e: raise HTTPException(status_code=500, detail=str(e))" so you preserve the
exception chain and avoid leaking internals: log the caught exception (e.g.,
using the module logger with logger.exception or logger.error(...,
exc_info=True)) and raise a generic HTTPException like
HTTPException(status_code=500, detail="Internal server error") from e to keep
the original traceback. Update the except block in the ai router where
HTTPException is raised to use logging + "from e" and remove str(e) from the
client-facing message.

In `@apps/python-service/tests/test_auth.py`:
- Around line 87-89: In test_replay_attack remove the unused mock_set unpacking
to fix RUF059: change the unpacking of mock_redis in test_replay_attack so only
the used mock_get is extracted (e.g., assign mock_get, _ = mock_redis or just
mock_get = mock_redis[0]) or otherwise replace mock_set with an underscore;
update references to mock_get accordingly in the test_replay_attack function.

In `@apps/web-dashboard/src/components/Database/AiQueryBar.jsx`:
- Around line 56-70: The input in AiQueryBar.jsx (the text field bound to prompt
with onChange calling setPrompt and disabled by isLoading || !activeCollection)
lacks an accessible label; add one by giving the input a unique id and either
adding a <label htmlFor="...">Ask AI to filter data</label> (styled
visually-hidden) or adding an aria-label/aria-labelledby that conveys "Ask AI to
filter data", ensuring the label is present for screen readers and remains
visually hidden if desired.

---

Nitpick comments:
In `@apps/dashboard-api/test_python.js`:
- Around line 5-13: Replace hardcoded secret and URL with environment variables:
read the secret into the existing secret variable (e.g., process.env.TEST_SECRET
or process.env.AI_TEST_SECRET) and read the request URL to use in axios.post
(e.g., process.env.API_URL or process.env.AI_API_URL), validate they exist
(throw or exit with a clear message if missing), then keep using those variables
when computing signature (signature = crypto.createHmac(..., secret)...) and
when calling axios.post (use the URL variable instead of
'http://127.0.0.1:8000/ai/query-builder').

In `@apps/python-service/routers/ai.py`:
- Around line 59-62: The chain.ainvoke call can hang indefinitely; wrap the call
in an asyncio.wait_for with a sensible timeout (e.g., configurable constant) and
handle asyncio.TimeoutError to return/raise an appropriate error response;
update the code around chain.ainvoke in routers/ai.py (the async handler that
calls chain.ainvoke) to import asyncio, call await
asyncio.wait_for(chain.ainvoke({...}), TIMEOUT_SECONDS), and add a try/except to
catch TimeoutError and log/return a controlled error instead of letting the
request hang.
- Around line 20-22: The QueryBuilderRequest model's schema_fields is declared
as an untyped list; change it to a typed sequence (e.g., List[str] or list[str])
to improve validation and OpenAPI docs: update the QueryBuilderRequest class to
use a concrete element type for schema_fields (and add the necessary typing
import such as List from typing if using Python <3.9) so Pydantic knows the
expected item type.

In `@apps/web-dashboard/src/components/Database/AiQueryBar.jsx`:
- Around line 72-81: Extract the inline <style> block out of the AiQueryBar
component into a separate CSS module (e.g., AiQueryBar.module.css) to avoid DOM
duplication; move the `@keyframes` shimmer and the focus-within rule there, rename
the selector to match a module-safe class (e.g., .aiQueryBar) and then import
that module into AiQueryBar.jsx and apply the class via className (replace the
literal "ai-query-bar" usage with the imported module class) so the animation
and focus styling are provided from the module stylesheet instead of being
injected inline for each instance.

In `@apps/web-dashboard/src/pages/Database.jsx`:
- Around line 159-166: handleFiltersGenerated should defensively ensure
aiFilters is an Array before writing it into state to avoid runtime errors where
queryParams.filters.forEach is called; update handleFiltersGenerated to set
filters to Array.isArray(aiFilters) ? aiFilters : (prev.filters || []) when
calling setQueryParams and leave sort logic unchanged (refer to
handleFiltersGenerated and the setQueryParams call and the later usage of
queryParams.filters.forEach).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 064f7a65-498d-4d96-a203-fe4fe8748d04

📥 Commits

Reviewing files that changed from the base of the PR and between efe95a8 and 6ba8993.

📒 Files selected for processing (23)
  • .aiignore
  • .env.example
  • .github/workflows/weekly-changelog.yml
  • .gitignore
  • .vscode/settings.json
  • apps/dashboard-api/src/__tests__/internalPythonClient.test.js
  • apps/dashboard-api/src/app.js
  • apps/dashboard-api/src/controllers/ai.controller.js
  • apps/dashboard-api/src/routes/ai.routes.js
  • apps/dashboard-api/src/utils/internalPythonClient.js
  • apps/dashboard-api/test_python.js
  • apps/python-service/.env.example
  • apps/python-service/config.py
  • apps/python-service/dependencies.py
  • apps/python-service/main.py
  • apps/python-service/requirements.txt
  • apps/python-service/routers/ai.py
  • apps/python-service/tests/test_auth.py
  • apps/web-dashboard/src/components/Database/AiQueryBar.jsx
  • apps/web-dashboard/src/components/Database/DatabaseFilter.jsx
  • apps/web-dashboard/src/components/Database/DatabaseHeader.jsx
  • apps/web-dashboard/src/pages/Database.jsx
  • docs/ISSUES/project-rate-limiting.md

Comment thread apps/dashboard-api/src/controllers/ai.controller.js Outdated
Comment thread apps/python-service/config.py Outdated
Comment thread apps/python-service/dependencies.py Outdated
Comment thread apps/python-service/dependencies.py Outdated
Comment thread apps/python-service/requirements.txt Outdated
Comment thread apps/python-service/routers/ai.py Outdated
Comment thread apps/python-service/tests/test_auth.py Outdated
Comment thread apps/web-dashboard/src/components/Database/AiQueryBar.jsx
Agent-Logs-Url: https://github.com/geturbackend/urBackend/sessions/a5a8938b-e932-4e90-ab1f-ef1546aaf66e

Co-authored-by: yash-pouranik <172860064+yash-pouranik@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (4)
apps/dashboard-api/src/controllers/ai.controller.js (2)

24-25: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make the users collection guard case-insensitive.

The guard is case-sensitive; normalizing before compare ensures blocked access consistently returns 403 for users variants.

🔧 Proposed fix
-        if (safeCollectionName === 'users') {
+        if (safeCollectionName.toLowerCase() === 'users') {
             throw new AppError(403, "Cannot query the users collection via AI");
         }
🤖 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 `@apps/dashboard-api/src/controllers/ai.controller.js` around lines 24 - 25,
Normalize safeCollectionName to a consistent case before comparing to 'users' so
the guard is case-insensitive: update the conditional that throws AppError(403,
"Cannot query the users collection via AI") (referencing safeCollectionName and
the AppError throw site in ai.controller.js) to compare a lowercase version of
safeCollectionName (e.g., (safeCollectionName || '').toLowerCase()) against
'users'.

10-31: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate projectId before Project.findOne to avoid cast-path failures.

projectId is still used directly in the Mongo selector. Invalid IDs can fall into Mongoose cast/error flow and currently degrade into a generic 500 instead of a clear 400.

🔧 Proposed fix
+const mongoose = require('mongoose');
 const { Project } = require('`@urbackend/common/src/models`');
 const { forwardToPythonService } = require('../utils/internalPythonClient');
 const { AppError } = require('`@urbackend/common`');
@@
         const { projectId } = req.params;
         const { collectionName, prompt } = req.body;
+
+        if (!mongoose.Types.ObjectId.isValid(projectId)) {
+            throw new AppError(400, "Invalid project id");
+        }
#!/bin/bash
# Verify whether ObjectId validation exists before Project.findOne usage.
rg -n -C3 "Types\.ObjectId\.isValid|Project\.findOne\(" apps/dashboard-api/src/controllers/ai.controller.js

As per coding guidelines, “Use AppError class for errors — never raw throw, never expose MongoDB errors to client.”

🤖 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 `@apps/dashboard-api/src/controllers/ai.controller.js` around lines 10 - 31,
Validate projectId before calling Project.findOne: check that
req.params.projectId is a valid Mongo ObjectId (e.g., using
mongoose.Types.ObjectId.isValid) and if not throw new AppError(400, "Invalid
projectId") instead of letting Mongoose cast; then proceed to use the validated
projectId in Project.findOne({ _id: projectId, owner: req.user._id,
"collections.name": safeCollectionName }, { "collections.$": 1 }) so Mongo
errors never surface as a 500 and the AppError pattern is preserved.
apps/web-dashboard/src/components/Database/AiQueryBar.jsx (1)

59-64: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add an explicit accessible label for the prompt input.

The input still relies on placeholder text only; add a programmatic label (aria-label or <label htmlFor>).

♿ Proposed fix
             <input 
+                aria-label="Ask AI to filter data"
                 type="text" 
                 value={prompt}
                 onChange={(e) => setPrompt(e.target.value)}
                 placeholder="Ask AI to filter data..."
                 disabled={isLoading || !activeCollection || !projectId}
🤖 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 `@apps/web-dashboard/src/components/Database/AiQueryBar.jsx` around lines 59 -
64, Add an explicit accessible label for the prompt input in AiQueryBar.jsx:
update the input that uses the prompt state and setPrompt handler to include a
programmatic label (for example add aria-label="AI query input" or create a
<label htmlFor="ai-prompt"> and give the input id="ai-prompt"). Ensure the label
is descriptive and remains present when the input is disabled; keep existing
props (value={prompt}, onChange={(e) => setPrompt(e.target.value)},
disabled={isLoading || !activeCollection || !projectId}) unchanged.
apps/python-service/routers/ai.py (1)

66-67: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve the original exception chain.

This still rethrows a new HTTPException without from, so the underlying LangChain/Groq traceback is lost when debugging failures. Catch the exception as exc and raise from it.

Proposed fix
-    except Exception:
-        raise HTTPException(status_code=500, detail="Failed to generate query")
+    except Exception as exc:
+        raise HTTPException(status_code=500, detail="Failed to generate query") from exc
🤖 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 `@apps/python-service/routers/ai.py` around lines 66 - 67, The except block
that currently does "except Exception: raise HTTPException(status_code=500,
detail='Failed to generate query')" should capture the original exception and
re-raise the HTTPException with exception chaining so the original traceback is
preserved; change the handler to "except Exception as exc" and raise the
HTTPException(...) from exc (preserve status_code and detail) so LangChain/Groq
errors remain visible in traces (look for the except Exception and HTTPException
in the ai router function that generates the query).
🤖 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.

Duplicate comments:
In `@apps/dashboard-api/src/controllers/ai.controller.js`:
- Around line 24-25: Normalize safeCollectionName to a consistent case before
comparing to 'users' so the guard is case-insensitive: update the conditional
that throws AppError(403, "Cannot query the users collection via AI")
(referencing safeCollectionName and the AppError throw site in ai.controller.js)
to compare a lowercase version of safeCollectionName (e.g., (safeCollectionName
|| '').toLowerCase()) against 'users'.
- Around line 10-31: Validate projectId before calling Project.findOne: check
that req.params.projectId is a valid Mongo ObjectId (e.g., using
mongoose.Types.ObjectId.isValid) and if not throw new AppError(400, "Invalid
projectId") instead of letting Mongoose cast; then proceed to use the validated
projectId in Project.findOne({ _id: projectId, owner: req.user._id,
"collections.name": safeCollectionName }, { "collections.$": 1 }) so Mongo
errors never surface as a 500 and the AppError pattern is preserved.

In `@apps/python-service/routers/ai.py`:
- Around line 66-67: The except block that currently does "except Exception:
raise HTTPException(status_code=500, detail='Failed to generate query')" should
capture the original exception and re-raise the HTTPException with exception
chaining so the original traceback is preserved; change the handler to "except
Exception as exc" and raise the HTTPException(...) from exc (preserve
status_code and detail) so LangChain/Groq errors remain visible in traces (look
for the except Exception and HTTPException in the ai router function that
generates the query).

In `@apps/web-dashboard/src/components/Database/AiQueryBar.jsx`:
- Around line 59-64: Add an explicit accessible label for the prompt input in
AiQueryBar.jsx: update the input that uses the prompt state and setPrompt
handler to include a programmatic label (for example add aria-label="AI query
input" or create a <label htmlFor="ai-prompt"> and give the input
id="ai-prompt"). Ensure the label is descriptive and remains present when the
input is disabled; keep existing props (value={prompt}, onChange={(e) =>
setPrompt(e.target.value)}, disabled={isLoading || !activeCollection ||
!projectId}) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 973fdb24-ce3e-4cff-a88b-d6dad838ba1e

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba8993 and dee015f.

📒 Files selected for processing (4)
  • apps/dashboard-api/src/controllers/ai.controller.js
  • apps/python-service/routers/ai.py
  • apps/python-service/tests/test_auth.py
  • apps/web-dashboard/src/components/Database/AiQueryBar.jsx

@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

HOw many change remaining?? @coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@yash-pouranik There are 13 unresolved review comments remaining. Here's a summary by file:

apps/dashboard-api/src/controllers/ai.controller.js (3)

  1. Harden collectionName/prompt/projectId inputs before Project.findOne (type checks, trim, ObjectId validation)
  2. Make the users guard case-insensitive (.toLowerCase())
  3. safeFilters needs stricter validation — enforce field is in the collection schema and operator is in the allowed set

apps/python-service/config.py (1)

  1. Enforce a minimum length (32+) on INTERNAL_SECRET to prevent weak HMAC keys

apps/python-service/dependencies.py (2)

  1. Chain the original ValueError when re-raising HTTPException (Ruff B904)
  2. Make replay-attack protection atomic using redis.set(key, "1", nx=True, ex=30) instead of separate get+set

apps/python-service/routers/ai.py (3)

  1. QueryResult.sort is required but the prompt implies it can default — make it optional to avoid 500s when the model omits it
  2. Exception handler exposes detail=str(e) — return a generic message and log server-side instead
  3. Preserve the exception chain (except Exception as e: raise HTTPException(...) from e)

apps/python-service/tests/test_auth.py (2)

  1. Test imports (dependencies/config) won't work unless apps/python-service is on sys.path and INTERNAL_SECRET is set — add a bootstrap section
  2. Unused mock_set unpacking in test_replay_attack — prefix with _ (Ruff RUF059)

apps/web-dashboard/src/components/Database/AiQueryBar.jsx (2)

  1. onFiltersGenerated is called unconditionally — guard with typeof onFiltersGenerated === 'function'
  2. Input is enabled even when projectId is absent, which can submit to /api/projects/undefined/... — disable until projectId is available; also add an accessible aria-label

@yash-pouranik yash-pouranik temporarily deployed to fix/ai-query-builder-review - urBackend-frankfrut PR #205 May 25, 2026 20:00 — with Render Destroyed
@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

everything done?
@coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@yash-pouranik Not quite — 8 review comments are still unresolved. Here's the current list:

apps/dashboard-api/src/controllers/ai.controller.js (1)

  1. Harden collectionName/prompt/projectId inputs — type checks, trim, and ObjectId validation before Project.findOne

apps/python-service/config.py (1)

  1. Enforce a minimum length (32+) on INTERNAL_SECRET at settings validation

apps/python-service/dependencies.py (2)

  1. Chain the original ValueError when re-raising HTTPException (Ruff B904)
  2. Make replay-attack protection atomic — use redis.set(key, "1", nx=True, ex=30) instead of separate get + set

apps/python-service/requirements.txt (1) ⚠️ New

  1. langchain==0.1.16 has multiple high/critical CVEs — upgrade the LangChain stack before release

apps/python-service/routers/ai.py (1)

  1. Exception handler exposes str(e) — return a generic message, log server-side, and preserve the chain with raise ... from e

apps/python-service/tests/test_auth.py (1)

  1. Unused mock_set unpacking in test_replay_attack — prefix with _ (Ruff RUF059)

apps/web-dashboard/src/components/Database/AiQueryBar.jsx (1)

  1. Text input lacks an accessible aria-label (or associated <label>) for screen readers

The good news — several items from the original 13 appear to have been resolved (case-insensitive users guard, QueryResult.sort optional, onFiltersGenerated guard, projectId disable, test imports bootstrap). Keep going! 🐇


🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the current state of review threads on PR `#205`
gh pr view 205 --repo geturbackend/urBackend --json reviewThreads --jq '
  .reviewThreads[] | select(.isResolved == false) | {
    path: .path,
    isResolved: .isResolved,
    body: (.comments[0].body | .[0:200])
  }
'

Length of output: 1202


🏁 Script executed:

#!/bin/bash
# Use GitHub REST API to get pull request review comments
gh api repos/geturbackend/urBackend/pulls/205/comments --jq '
  .[] | select(.user.login == "coderabbitai[bot]") | {
    path: .path,
    body: (.body | .[0:300])
  }
' 2>/dev/null | head -200

echo "---"
# Also check reviews
gh api repos/geturbackend/urBackend/pulls/205/reviews --jq '
  .[] | select(.user.login == "coderabbitai[bot]") | {state: .state, submitted_at: .submitted_at}
' 2>/dev/null

Length of output: 3796


@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

verify them against code please, I think they are outdated?
@coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

@yash-pouranik You were right — all 8 items have been addressed. Here's the verification:

# File Item Status
1 ai.controller.js ObjectId validation, type checks, trim, field+operator allowlist ✅ Resolved
2 config.py INTERNAL_SECRET: str = Field(min_length=32) ✅ Resolved
3 dependencies.py raise HTTPException(...) from e chaining ✅ Resolved
4 dependencies.py Atomic redis_client.set(key, "1", ex=30, nx=True) ✅ Resolved
5 requirements.txt langchain==0.1.16 removed; replaced with langchain-groq==1.1.2 ✅ Resolved
6 routers/ai.py Generic "Internal server error" detail, logger.error(..., exc_info=True), raise ... from e ✅ Resolved
7 tests/test_auth.py Unused variable prefixed as _, mock_set = mock_redis ✅ Resolved
8 AiQueryBar.jsx aria-label="Ask AI to filter data" on the input ✅ Resolved

All previously flagged review comments are now resolved. 🎉


🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== ai.controller.js ==="
cat apps/dashboard-api/src/controllers/ai.controller.js

echo ""
echo "=== config.py ==="
cat apps/python-service/config.py

echo ""
echo "=== dependencies.py ==="
cat apps/python-service/dependencies.py

echo ""
echo "=== requirements.txt ==="
cat apps/python-service/requirements.txt

echo ""
echo "=== routers/ai.py ==="
cat apps/python-service/routers/ai.py

echo ""
echo "=== tests/test_auth.py ==="
cat apps/python-service/tests/test_auth.py

echo ""
echo "=== AiQueryBar.jsx ==="
cat apps/web-dashboard/src/components/Database/AiQueryBar.jsx

Length of output: 17993


✅ Actions performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

OK to merge? @coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@yash-pouranik Yes, this PR looks good to merge! Here's the final status:

Check Result
Mergeable state ✅ CLEAN
frontend-lint ✅ SUCCESS
dashboard-api-tests ✅ SUCCESS
public-api-tests ✅ SUCCESS
sdk-tests ✅ SUCCESS
python-sdk-tests ✅ SUCCESS
CodeQL Advanced ✅ SUCCESS
CodeRabbit ✅ SUCCESS
All review comments ✅ Resolved

All CI checks are green, there are no open review concerns, and all previously flagged issues have been addressed. You're clear to merge! 🐇 🎉


🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== PR status ==="
gh pr view 205 --repo geturbackend/urBackend --json state,mergeable,mergeStateStatus,isDraft,reviewDecision,statusCheckRollup \
  --jq '{state,mergeable,mergeStateStatus,isDraft,reviewDecision,statusCheckRollup}'

echo ""
echo "=== Open/unresolved review threads ==="
gh pr view 205 --repo geturbackend/urBackend --json reviewThreads \
  --jq '[.reviewThreads[] | select(.isResolved == false) | {path: .path, body: (.comments[0].body | .[0:200])}]'

Length of output: 4046


@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

yash-pouranik commented May 25, 2026

for prod
What should i update?
as there is new server
Env vars?
to update.

@coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@yash-pouranik Here's everything you need to update for production:


1. Dashboard API — root .env

Add these three new variables:

Variable Value
PYTHON_SERVICE_URL URL of your deployed Python service, e.g. https://python.yourserver.com
INTERNAL_SECRET A random 32+ character secret (generate with openssl rand -hex 32)
GROQ_API_KEY Your Groq API key from console.groq.com

2. Python Microservice — apps/python-service/.env (new server)

Copy apps/python-service/.env.example.env and fill in:

Variable Value
INTERNAL_SECRET Must be identical to the one set in the Dashboard API
GROQ_API_KEY Same Groq API key as above
REDIS_URL Your production Redis URL (e.g. redis://your-redis-host:6379) — don't leave it as localhost

3. Deploy & wire the Python service

The Python microservice is a new FastAPI server that needs to be running separately:

# Install deps
pip install -r apps/python-service/requirements.txt

# Start the service
uvicorn main:app --host 0.0.0.0 --port 8000

Make sure PYTHON_SERVICE_URL in your Dashboard API .env points to this server's address.


🔑 Key constraint

INTERNAL_SECRET must match exactly on both sides — the Node.js Dashboard API uses it to sign requests and the Python service uses it to verify them. If they differ, all AI Query Builder calls will get 403 Forbidden.

ʕ•ᴥ•ʔ 🐇


🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Root .env.example ==="
cat .env.example

echo ""
echo "=== apps/python-service/.env.example ==="
cat apps/python-service/.env.example

echo ""
echo "=== apps/python-service/config.py ==="
cat apps/python-service/config.py

echo ""
echo "=== apps/dashboard-api/src/utils/internalPythonClient.js ==="
cat apps/dashboard-api/src/utils/internalPythonClient.js

Length of output: 5621


@yash-pouranik
Copy link
Copy Markdown
Collaborator Author

groq api is only needed in python service not in dashboard api
@coderabbitai

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@yash-pouranik You're absolutely right, my apologies for the incorrect information. GROQ_API_KEY is only consumed by the Python service (in apps/python-service/config.py and routers/ai.py) — the Dashboard API never reads it directly; it just signs and forwards requests to the Python service.

Here's the corrected production env summary:

Dashboard API — root .env (only 2 new vars)

Variable Value
PYTHON_SERVICE_URL URL of your deployed Python service
INTERNAL_SECRET A random 32+ character secret (openssl rand -hex 32)

Python Microservice — apps/python-service/.env (new server)

Variable Value
INTERNAL_SECRET Must match exactly the one in the Dashboard API
GROQ_API_KEY Your Groq API key from console.groq.com
REDIS_URL Your production Redis URL

✏️ Learnings added
Learnt from: yash-pouranik
URL: https://github.com/geturbackend/urBackend/pull/205

Timestamp: 2026-05-25T20:42:16.076Z
Learning: In the urBackend project (geturbackend/urBackend), GROQ_API_KEY is only needed in the Python microservice (`apps/python-service/.env`), NOT in the Dashboard API root `.env`. The Dashboard API (`apps/dashboard-api`) only needs `PYTHON_SERVICE_URL` and `INTERNAL_SECRET` for communicating with the Python service. The Python service handles all direct Groq API calls.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@yash-pouranik yash-pouranik merged commit af2209c into main May 25, 2026
9 checks passed
@yash-pouranik yash-pouranik deleted the fix/ai-query-builder-review branch May 25, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants