Skip to content

feat: add AI chatbot assistant with RAG support#118

Open
pranavshankar1221 wants to merge 1 commit into
jpdevhub:mainfrom
pranavshankar1221:feat/ai-chatbot-clean
Open

feat: add AI chatbot assistant with RAG support#118
pranavshankar1221 wants to merge 1 commit into
jpdevhub:mainfrom
pranavshankar1221:feat/ai-chatbot-clean

Conversation

@pranavshankar1221

@pranavshankar1221 pranavshankar1221 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

closes #62

Description

Checklist

  • npm run lint passes with no errors
  • npm run build compiles without TypeScript errors
  • python -m pytest passes (including new tests I added)
  • No .env files, API keys, secrets, model weights, or __pycache__ in this diff
  • Branch is rebased on main, not merged

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • New Features
    • Added an AI chat assistant with conversation history, rich text formatting, and up/down feedback.
    • Introduced chat APIs and persisted chat transcripts (including feedback).
    • Added documentation-aware responses using a documentation retriever.
    • Enabled multiple LLM providers (Gemini, OpenAI, Claude, Ollama) with automatic fallback/demo mode.
  • Configuration
    • Updated environment configuration for selecting an LLM provider and supplying API keys.
  • Changes
    • Updated the markets endpoint request handling and adjusted its rate-limiting behavior.

@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

@pranavshankar1221 is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 10fa9705-2772-4806-b413-3f0b9d15d05d

📥 Commits

Reviewing files that changed from the base of the PR and between b2ea6c1 and 2d290da.

📒 Files selected for processing (9)
  • backend/.env.example
  • backend/chat_logger.py
  • backend/chat_router.py
  • backend/llm_provider.py
  • backend/main.py
  • backend/rag_retriever.py
  • src/components/ChatAssistant.tsx
  • src/components/Layout.tsx
  • src/lib/api.ts
✅ Files skipped from review due to trivial changes (1)
  • src/components/Layout.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/chat_logger.py
  • backend/chat_router.py
  • src/lib/api.ts
  • src/components/ChatAssistant.tsx
  • backend/main.py
  • backend/rag_retriever.py
  • backend/llm_provider.py

📝 Walkthrough

Walkthrough

This PR adds a full AI chat assistant to FreshScanAi. On the backend it introduces a TF-IDF RAG retriever over project docs, a pluggable LLM provider layer (Gemini, OpenAI, Claude, Ollama, Mock), a SQLite chat logger, and two new FastAPI endpoints. On the frontend it adds a floating ChatAssistant React component wired into the global layout via new API client methods.

Changes

AI Chat Assistant

Layer / File(s) Summary
TF-IDF RAG retriever
backend/rag_retriever.py
RAGChunk container, tokenize(), RAGRetriever class (doc loading, markdown header splitting, large-text sub-splitting, TF-IDF indexing, query scoring with substring fallback), and get_retriever() singleton.
LLM provider interface and implementations
backend/llm_provider.py, backend/.env.example
LLMProvider base class, concrete Gemini/OpenAI/Claude/Ollama HTTP-calling implementations, keyword-based MockProvider, and get_llm_provider() factory that reads LLM_PROVIDER env var and falls back to mock when credentials are absent. .env.example adds provider key placeholders.
SQLite chat logger
backend/chat_logger.py
init_db() creates chat_logs table; log_chat_message() inserts Q&A rows; update_chat_feedback() sets thumbs up/down by message ID; per-call open/close with error printing.
FastAPI chat endpoints and backend wiring
backend/chat_router.py, backend/main.py
Pydantic models for request/response, /message endpoint (RAG context → prompt assembly → LLM call → best-effort log), /feedback endpoint (validates up/down, writes to logger); main.py registers the router, switches dotenv to override=True, and removes the rate-limit decorator from get_markets.
Frontend API client chat methods
src/lib/api.ts
Adds api.chatMessage() (POST /api/v1/chat/message) and api.submitChatFeedback() (POST /api/v1/chat/feedback) with typed request/response shapes; minor reformatting of loginUrl and getMarkets.
ChatAssistant component and layout integration
src/components/ChatAssistant.tsx, src/components/Layout.tsx
Full floating chat panel: route-based page context, send-message flow with history building, per-message thumbs feedback, formatText markdown renderer, empty-state suggestions, loading indicator, controlled input form; Layout imports and renders <ChatAssistant />.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant ChatAssistant as ChatAssistant (React)
  participant APIClient as api.ts
  participant ChatRouter as POST /api/v1/chat/message
  participant RAGRetriever
  participant LLMProvider
  participant ChatLogger as chat_logger (SQLite)

  User->>ChatAssistant: types question, clicks SEND
  ChatAssistant->>APIClient: chatMessage(question, page, feature, history)
  APIClient->>ChatRouter: POST /api/v1/chat/message
  ChatRouter->>RAGRetriever: retrieve_relevant_context(question)
  RAGRetriever-->>ChatRouter: context string
  ChatRouter->>LLMProvider: generate_response(system_prompt, prompt+context, history)
  LLMProvider-->>ChatRouter: response text
  ChatRouter->>ChatLogger: log_chat_message(msg_id, question, response, page, feature)
  ChatRouter-->>APIClient: { message_id, response }
  APIClient-->>ChatAssistant: { message_id, response }
  ChatAssistant->>User: renders assistant message + thumbs buttons

  User->>ChatAssistant: clicks thumbs up/down
  ChatAssistant->>APIClient: submitChatFeedback(message_id, "up"|"down")
  APIClient->>ChatRouter: POST /api/v1/chat/feedback
  ChatRouter->>ChatLogger: update_chat_feedback(msg_id, feedback)
  ChatRouter-->>APIClient: { success: true }
  APIClient-->>ChatAssistant: updates local message state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A rabbit hops through docs and code,
RAG-indexing every markdown road.
Gemini, Claude, or Ollama's call—
The chat floats up to answer all!
Ask your question, thumb it right,
SQLite logs it through the night. ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Critical issue: The PR removes rate limiting decorators (@limiter.limit) from the GET /api/v1/maps/markets endpoint in backend/main.py, which is out of scope and unintended per maintainer feedback. Rebase the feature branch on the latest main branch to preserve the rate limiting functionality that was previously merged. Ensure @limiter.limit decorators are retained on the markets endpoint.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: adding an AI chatbot with RAG support, which is the core objective of the changeset.
Linked Issues check ✅ Passed The PR implements all key objectives from issue #62: interactive guidance for users, instant answers to queries, improved onboarding, and reduced documentation dependency through a fully-featured AI chat assistant with RAG support.

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

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

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
src/components/ChatAssistant.tsx (2)

98-112: 💤 Low value

Consider using message ID instead of index for state update.

The feedback handler uses idx to update the message in state, but if the messages array were to change between the button click and state update completion, the wrong message could be updated. While unlikely due to the loading guard, an ID-based lookup would be more robust.

♻️ Suggested improvement
     try {
       await api.submitChatFeedback(msgId, type);
-      setMessages(prev => {
-        const updated = [...prev];
-        updated[idx] = { ...updated[idx], feedback: type };
-        return updated;
-      });
+      setMessages(prev => prev.map(m => 
+        m.id === msgId ? { ...m, feedback: type } : m
+      ));
     } catch (err) {
🤖 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 `@src/components/ChatAssistant.tsx` around lines 98 - 112, The handleFeedback
function uses the array index idx to update the message state, which is fragile
if the messages array changes between the button click and state update. Replace
the index-based approach in the setMessages callback with an ID-based lookup
that finds the message by matching its msgId property instead of using
updated[idx]. This ensures the correct message is updated regardless of array
order changes.

317-324: ⚡ Quick win

Add aria-label to the chat input for screen reader accessibility.

The input field relies on placeholder text, which is not announced by all screen readers. Adding aria-label improves accessibility for users with assistive technologies.

♿ Suggested fix
             <input
               type="text"
               value={inputValue}
               onChange={(e) => setInputValue(e.target.value)}
               placeholder="ASK CHAT ASSISTANT..."
               disabled={isLoading}
+              aria-label="Chat message input"
               className="flex-1 bg-surface-lowest text-xs text-on-surface px-4 py-3.5 border-none focus:outline-none focus:bg-surface-mid placeholder:font-mono placeholder:text-[9px] placeholder:text-outline disabled:opacity-60"
             />
🤖 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 `@src/components/ChatAssistant.tsx` around lines 317 - 324, The input field in
ChatAssistant.tsx lacks an aria-label attribute, which is necessary for screen
reader accessibility. Add an aria-label prop to the input element that has
type="text", value={inputValue}, and the onChange handler that calls
setInputValue. Provide a descriptive label such as "Chat assistant input" that
clearly describes the purpose of the input field for users relying on assistive
technologies.
🤖 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 `@backend/.env.example`:
- Around line 35-44: Remove the duplicate CORS_ALLOW_ALL configuration key from
the .env.example file. Keep the first occurrence of CORS_ALLOW_ALL=true that
appears at the beginning of the configuration section, and delete the second
duplicate occurrence that appears at the end of the LLM Provider section. This
ensures each configuration key is declared only once, removing any ambiguity
about which value takes precedence.

In `@backend/chat_logger.py`:
- Around line 62-65: The exception handling in ChatLogger is swallowing SQLite
write errors by catching exceptions, printing them, and then returning normally,
which causes API handlers to report success when writes actually fail. Instead
of just printing the exception in the except blocks (around lines 62-65 and
83-86), re-raise the exception after logging it so that the error properly
propagates to the caller and API handlers can detect and report the failure
appropriately.
- Around line 74-82: The UPDATE statement in the chat_logs feedback update logic
does not validate whether the update actually affected any rows. After the
cursor.execute() call that updates feedback, check cursor.rowcount to determine
if any rows were actually modified. Only proceed with conn.commit() if
cursor.rowcount is greater than 0, and return an appropriate response indicating
failure or success based on whether the msg_id matched an existing record in the
database.

In `@backend/chat_router.py`:
- Around line 43-44: The chat_message function and chat_feedback function are
defined as async but perform blocking synchronous I/O operations (LLM HTTP calls
and SQLite operations) that block the event loop under concurrent load. Remove
the async keyword from the function definitions of chat_message and
chat_feedback to convert them to synchronous handlers, which allows the ASGI
framework to properly schedule and execute these blocking operations on thread
pools without blocking concurrent requests.

In `@backend/llm_provider.py`:
- Around line 38-42: The Gemini API key is currently passed as a query parameter
in the URL string within the generateContent endpoint call, which poses a
security risk as query parameters are easily exposed in logs and caches. Remove
the `?key={self.api_key}` query parameter from the URL construction and instead
pass the API key as an HTTP header in the request (following the same pattern
used by the OpenAI and Claude providers in the same file). Ensure that any
exception handling and logging does not expose raw error details that might
reveal authentication information. Apply this same fix to the similar code at
lines 78-79 where the API key is also passed in the URL.

In `@backend/main.py`:
- Line 775: Add a rate limiting decorator to the `get_markets()` async function
to protect it from burst traffic, following the same pattern used by other
endpoints in the codebase. Place the `@limiter.limit("20/minute")` decorator
directly above the function definition to enforce consistent rate limiting
across all API endpoints.
- Line 22: In the load_dotenv function call, change the override parameter from
override=True to override=False. This ensures that environment variables set by
your deployment platform (Docker, Kubernetes, CI/CD) are preserved and not
overridden by values from the .env file, protecting production secrets and
configuration from being accidentally replaced by local development values.

In `@src/components/ChatAssistant.tsx`:
- Around line 114-162: The formatText function applies bold formatting to the
content variable but then returns raw text (listText, numMatch[2], and the
blockquote substring) in list items and blockquotes, losing the formatting. To
fix this, ensure inline formatting is applied before checking for
list/blockquote detection. Move the bold formatting logic to happen first, then
use the formatted content variable when returning list items (instead of
listText), numbered lists (instead of numMatch[2]), and blockquotes (instead of
the raw substring). This way, a paragraph like "- **Important** item" will
preserve the bold formatting when rendered as a list item.

---

Nitpick comments:
In `@src/components/ChatAssistant.tsx`:
- Around line 98-112: The handleFeedback function uses the array index idx to
update the message state, which is fragile if the messages array changes between
the button click and state update. Replace the index-based approach in the
setMessages callback with an ID-based lookup that finds the message by matching
its msgId property instead of using updated[idx]. This ensures the correct
message is updated regardless of array order changes.
- Around line 317-324: The input field in ChatAssistant.tsx lacks an aria-label
attribute, which is necessary for screen reader accessibility. Add an aria-label
prop to the input element that has type="text", value={inputValue}, and the
onChange handler that calls setInputValue. Provide a descriptive label such as
"Chat assistant input" that clearly describes the purpose of the input field for
users relying on assistive technologies.
🪄 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 Plus

Run ID: a0e07b89-fece-47a4-8220-d5da981a3636

📥 Commits

Reviewing files that changed from the base of the PR and between d5dab73 and b2ea6c1.

📒 Files selected for processing (9)
  • backend/.env.example
  • backend/chat_logger.py
  • backend/chat_router.py
  • backend/llm_provider.py
  • backend/main.py
  • backend/rag_retriever.py
  • src/components/ChatAssistant.tsx
  • src/components/Layout.tsx
  • src/lib/api.ts

Comment thread backend/.env.example
Comment on lines +35 to +44
CORS_ALLOW_ALL=true

# ── LLM Provider (for AI Chat Assistant) ──────────────────────────────────────
# Options: gemini (default), openai, claude, ollama
LLM_PROVIDER=gemini
GEMINI_API_KEY=
OPENAI_API_KEY=
CLAUDE_API_KEY=
OLLAMA_BASE_URL=http://localhost:11434
CORS_ALLOW_ALL=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicated CORS_ALLOW_ALL.

The key is declared twice, which makes configuration precedence ambiguous during audits/debugging.

🧹 Proposed fix
 CORS_ALLOW_ALL=true
@@
 OLLAMA_BASE_URL=http://localhost:11434
-CORS_ALLOW_ALL=true
📝 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.

Suggested change
CORS_ALLOW_ALL=true
# ── LLM Provider (for AI Chat Assistant) ──────────────────────────────────────
# Options: gemini (default), openai, claude, ollama
LLM_PROVIDER=gemini
GEMINI_API_KEY=
OPENAI_API_KEY=
CLAUDE_API_KEY=
OLLAMA_BASE_URL=http://localhost:11434
CORS_ALLOW_ALL=true
CORS_ALLOW_ALL=true
# ── LLM Provider (for AI Chat Assistant) ──────────────────────────────────────
# Options: gemini (default), openai, claude, ollama
LLM_PROVIDER=gemini
GEMINI_API_KEY=
OPENAI_API_KEY=
CLAUDE_API_KEY=
OLLAMA_BASE_URL=http://localhost:11434
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 40-40: [UnorderedKey] The GEMINI_API_KEY key should go before the LLM_PROVIDER key

(UnorderedKey)


[warning] 42-42: [UnorderedKey] The CLAUDE_API_KEY key should go before the GEMINI_API_KEY key

(UnorderedKey)


[warning] 43-43: [UnorderedKey] The OLLAMA_BASE_URL key should go before the OPENAI_API_KEY key

(UnorderedKey)


[warning] 44-44: [DuplicatedKey] The CORS_ALLOW_ALL key is duplicated

(DuplicatedKey)


[warning] 44-44: [UnorderedKey] The CORS_ALLOW_ALL key should go before the GEMINI_API_KEY key

(UnorderedKey)

🤖 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 `@backend/.env.example` around lines 35 - 44, Remove the duplicate
CORS_ALLOW_ALL configuration key from the .env.example file. Keep the first
occurrence of CORS_ALLOW_ALL=true that appears at the beginning of the
configuration section, and delete the second duplicate occurrence that appears
at the end of the LLM Provider section. This ensures each configuration key is
declared only once, removing any ambiguity about which value takes precedence.

Source: Linters/SAST tools

Comment thread backend/chat_logger.py
Comment on lines +62 to +65
except Exception as e:
print(f"ChatLogger Error logging message: {e}")
finally:
conn.close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not swallow SQLite write errors.

Both functions print exceptions and return normally, so API handlers can report success even when writes fail.

🛠️ Proposed fix
-    except Exception as e:
-        print(f"ChatLogger Error logging message: {e}")
+    except sqlite3.Error as e:
+        conn.rollback()
+        raise RuntimeError("Failed to log chat message") from e
@@
-    except Exception as e:
-        print(f"ChatLogger Error updating feedback: {e}")
+    except sqlite3.Error as e:
+        conn.rollback()
+        raise RuntimeError("Failed to update chat feedback") from e

Also applies to: 83-86

🤖 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 `@backend/chat_logger.py` around lines 62 - 65, The exception handling in
ChatLogger is swallowing SQLite write errors by catching exceptions, printing
them, and then returning normally, which causes API handlers to report success
when writes actually fail. Instead of just printing the exception in the except
blocks (around lines 62-65 and 83-86), re-raise the exception after logging it
so that the error properly propagates to the caller and API handlers can detect
and report the failure appropriately.

Comment thread backend/chat_logger.py
Comment on lines +74 to +82
cursor.execute(
"""
UPDATE chat_logs
SET feedback = ?
WHERE id = ?
""",
(feedback, msg_id)
)
conn.commit()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate that feedback updates actually match a message ID.

UPDATE can affect 0 rows (unknown msg_id) and still commit; downstream currently returns {success: true}.

✅ Proposed fix
         cursor.execute(
@@
             (feedback, msg_id)
         )
+        if cursor.rowcount == 0:
+            raise ValueError(f"Message ID not found: {msg_id}")
         conn.commit()
📝 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.

Suggested change
cursor.execute(
"""
UPDATE chat_logs
SET feedback = ?
WHERE id = ?
""",
(feedback, msg_id)
)
conn.commit()
cursor.execute(
"""
UPDATE chat_logs
SET feedback = ?
WHERE id = ?
""",
(feedback, msg_id)
)
if cursor.rowcount == 0:
raise ValueError(f"Message ID not found: {msg_id}")
conn.commit()
🤖 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 `@backend/chat_logger.py` around lines 74 - 82, The UPDATE statement in the
chat_logs feedback update logic does not validate whether the update actually
affected any rows. After the cursor.execute() call that updates feedback, check
cursor.rowcount to determine if any rows were actually modified. Only proceed
with conn.commit() if cursor.rowcount is greater than 0, and return an
appropriate response indicating failure or success based on whether the msg_id
matched an existing record in the database.

Comment thread backend/chat_router.py
Comment on lines +43 to +44
async def chat_message(request: ChatMessageRequest):
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify async route + sync call chain
rg -n -C2 'async def chat_message|async def chat_feedback|get_retriever\(|generate_response\(|log_chat_message\(|update_chat_feedback\(' backend/chat_router.py
rg -n -C2 'httpx\.post\(' backend/llm_provider.py
rg -n -C2 'sqlite3\.connect\(' backend/chat_logger.py

Repository: jpdevhub/FreshScanAi

Length of output: 2340


Remove async from blocking I/O handlers.

These routes execute synchronous LLM HTTP and SQLite calls that block the event loop under concurrent load. Converting to synchronous handlers allows proper concurrent request handling.

Proposed fix
-async def chat_message(request: ChatMessageRequest):
+def chat_message(request: ChatMessageRequest):

Also applies to chat_feedback() (line 151).

Also applies to: 115-117, 151-158

🤖 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 `@backend/chat_router.py` around lines 43 - 44, The chat_message function and
chat_feedback function are defined as async but perform blocking synchronous I/O
operations (LLM HTTP calls and SQLite operations) that block the event loop
under concurrent load. Remove the async keyword from the function definitions of
chat_message and chat_feedback to convert them to synchronous handlers, which
allows the ASGI framework to properly schedule and execute these blocking
operations on thread pools without blocking concurrent requests.

Comment thread backend/llm_provider.py
Comment on lines +38 to +42
url = (
"https://generativelanguage.googleapis.com"
f"/v1beta/models/{self.model}:generateContent"
f"?key={self.api_key}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify key-in-URL + raw exception propagation paths
rg -n -C2 'generateContent\?key=|Gemini API error: \{e\}|Gemini provider failed: \{e\}' backend/llm_provider.py
rg -n -C2 'LLM Provider execution failed' backend/chat_router.py

Repository: jpdevhub/FreshScanAi

Length of output: 585


Replace query-parameter authentication with header-based auth to prevent API key exposure.

Line 41 uses ?key={self.api_key} in the URL, which puts the Gemini API key in the query string. Query parameters are more likely to be exposed in logs, browser history, caches, and debugging output compared to HTTP headers. Additionally, exception messages should not propagate raw details that might reveal authentication info in error logs.

Update to pass the API key in a request header instead (like OpenAI and Claude providers do), and remove exception details from log messages:

🔐 Proposed fix
-        url = (
-            "https://generativelanguage.googleapis.com"
-            f"/v1beta/models/{self.model}:generateContent"
-            f"?key={self.api_key}"
-        )
+        url = "https://generativelanguage.googleapis.com/v1beta/models/{self.model}:generateContent"
+        headers = {"x-goog-api-key": self.api_key}
-        except Exception as e:
-            logger.error(f"Gemini API error: {e}")
-            raise RuntimeError(f"Gemini provider failed: {e}")
+        except Exception:
+            logger.error("Gemini API request failed")
+            raise RuntimeError("Gemini provider failed")

Also applies to: lines 78–79

🤖 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 `@backend/llm_provider.py` around lines 38 - 42, The Gemini API key is
currently passed as a query parameter in the URL string within the
generateContent endpoint call, which poses a security risk as query parameters
are easily exposed in logs and caches. Remove the `?key={self.api_key}` query
parameter from the URL construction and instead pass the API key as an HTTP
header in the request (following the same pattern used by the OpenAI and Claude
providers in the same file). Ensure that any exception handling and logging does
not expose raw error details that might reveal authentication information. Apply
this same fix to the similar code at lines 78-79 where the API key is also
passed in the URL.

Comment thread backend/main.py
from dotenv import load_dotenv

load_dotenv(Path(__file__).parent / ".env")
load_dotenv(Path(__file__).parent / ".env", override=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify dotenv loading behavior in startup code
rg -n -C3 'load_dotenv|override=' backend/main.py

Repository: jpdevhub/FreshScanAi

Length of output: 270


🏁 Script executed:

# Check if .env is in .gitignore
cat -n .gitignore | grep -i env || echo "No .env entry found in .gitignore"

Repository: jpdevhub/FreshScanAi

Length of output: 663


🏁 Script executed:

# Look for get_markets endpoint and route limiter context
rg -n 'get_markets|@.*limiter|`@limiter`' backend/ --type py -B2 -A2

Repository: jpdevhub/FreshScanAi

Length of output: 1099


🌐 Web query:

python-dotenv override=True security best practices environment variables

💡 Result:

When using python-dotenv, the override parameter determines whether values in your.env file take precedence over existing environment variables [1][2]. Setting override=True forces the application to prioritize the.env file, which is generally discouraged in production environments where system-level configuration should take precedence [3]. Security Best Practices: 1. Prevent Secret Exposure: Never commit your.env file to version control [4][3]. Always add.env to your.gitignore file [4][3]. If a secret file is accidentally committed, rotate the credentials immediately, as removing the file from history is insufficient for security [4][3]. 2. Prefer System-Level Environment Variables in Production: Avoid using override=True in production [3]. Allow actual system environment variables (injected by your hosting platform, CI/CD pipeline, or container orchestrator) to maintain priority [3]. This ensures the deployment environment controls the application configuration [3]. 3. Validate Configuration: Do not rely solely on the existence of variables. Implement startup validation to check for required keys and fail immediately with clear error messages if configuration is missing or malformed [4][3]. 4. Keep python-dotenv Updated: A security vulnerability (CVE-2026-28684) previously allowed arbitrary file overwrites via symbolic links when using set_key or unset_key [5][6]. Ensure you are using version 1.2.2 or later, which resolves this issue by defaulting to safer file handling practices [5][6]. 5. Minimize.env Usage: For complex applications, consider using robust configuration management libraries like pydantic-settings, which provide built-in type safety, validation, and advanced loading capabilities without relying solely on manual.env management [7]. 6. Disable in Production: If your deployment process manages environment variables externally, you can disable python-dotenv's automatic loading by setting the environment variable PYTHON_DOTENV_DISABLED=1 [8][2]. This prevents accidental loading of local.env files in server environments [8].

Citations:


Change override=True to override=False to avoid overriding deployment environment variables.

When override=True, .env values will override system environment variables set by your deployment platform (Docker, Kubernetes, CI/CD), potentially replacing production secrets with local development values. Use override=False (the default) to allow .env to fill missing variables while preserving deployment-provided configuration.

Proposed fix
-    load_dotenv(Path(__file__).parent / ".env", override=True)
+    load_dotenv(Path(__file__).parent / ".env", override=False)
🤖 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 `@backend/main.py` at line 22, In the load_dotenv function call, change the
override parameter from override=True to override=False. This ensures that
environment variables set by your deployment platform (Docker, Kubernetes,
CI/CD) are preserved and not overridden by values from the .env file, protecting
production secrets and configuration from being accidentally replaced by local
development values.

Comment thread backend/main.py
@app.get("/api/v1/maps/markets")
@limiter.limit("20/minute")
async def get_markets(request: Request):
async def get_markets():

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify limiter coverage for markets endpoint
rg -n -C4 '/api/v1/maps/markets|get_markets|limiter\.limit' backend/main.py

Repository: jpdevhub/FreshScanAi

Length of output: 1479


Add rate limiting to /api/v1/maps/markets endpoint.

The endpoint lacks the per-route limiter that other endpoints implement consistently (e.g., scan endpoints use @limiter.limit("20/minute")), leaving it vulnerable to burst traffic and unnecessary database load.

Proposed fix
+@limiter.limit("20/minute")
 `@app.get`("/api/v1/maps/markets")
-async def get_markets():
+async def get_markets(request: Request):
📝 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.

Suggested change
async def get_markets():
`@limiter.limit`("20/minute")
`@app.get`("/api/v1/maps/markets")
async def get_markets(request: Request):
🤖 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 `@backend/main.py` at line 775, Add a rate limiting decorator to the
`get_markets()` async function to protect it from burst traffic, following the
same pattern used by other endpoints in the codebase. Place the
`@limiter.limit("20/minute")` decorator directly above the function definition
to enforce consistent rate limiting across all API endpoints.

Comment thread src/components/ChatAssistant.tsx
@pranavshankar1221

Copy link
Copy Markdown
Contributor Author

Hi @jpdevhub ; Created a fresh PR for Feat/ai chatbot integration #112 kindly verify it

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresh-scan-ai Building Building Preview, Comment Jun 22, 2026 8:30am

@jpdevhub

Copy link
Copy Markdown
Owner

This is a great implementation! However, your branch is out of date and accidentally deletes the rate limiting feature we recently merged into backend/main.py. Please run git fetch upstream and git rebase upstream/main, ensuring you do not delete the @limiter.limit decorators. Once synced, we can merge it!

@jpdevhub

Copy link
Copy Markdown
Owner

Also dont forget to update and sync the branch in your github

@pranavshankar1221

Copy link
Copy Markdown
Contributor Author

Thanks for pointing that out. I've rebased the branch onto the latest upstream main and pushed the updated changes. I verified that the @limiter.limit decorators are still present in backend/main.py, so the rate limiting functionality has been preserved while keeping the chatbot integration intact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI chat Assistant Integration

2 participants