Feat(backend): add Groq AI integration and secure API route#147
Conversation
WalkthroughNew database tables Changes
Sequence DiagramsequenceDiagram
participant Client
participant FastAPI Route
participant Settings
participant GROQ API
participant Client2
Client->>FastAPI Route: POST /groq/generate<br/>(GroqRequest)
FastAPI Route->>Settings: Retrieve groq_api_key
alt API Key Missing
FastAPI Route->>Client: HTTPException 500
else
FastAPI Route->>GROQ API: POST /openai/v1/chat/completions<br/>(timeout: 30s)
alt Success
GROQ API->>FastAPI Route: JSON Response
FastAPI Route->>Client2: {"result": response_json}
else Error
GROQ API->>FastAPI Route: HTTP Error
FastAPI Route->>Client2: HTTPException 502
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
backend/SQL(1 hunks)backend/app/api/routes/groq_generate.py(1 hunks)backend/app/main.py(2 hunks)backend/requirements.txt(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-08T12:24:11.575Z
Learnt from: Saahi30
Repo: AOSSIE-Org/InPactAI PR: 145
File: backend/SQL:98-112
Timestamp: 2025-11-08T12:24:11.575Z
Learning: In the InPactAI repository, the backend/SQL file is a documentation reference that points to schema-reference.md and is not meant to be executed directly. Table ordering and FK constraint issues should not be flagged for this file.
Applied to files:
backend/SQL
🪛 Ruff (0.14.3)
backend/app/api/routes/groq_generate.py
16-16: Unused function argument: request
(ARG001)
41-41: Consider moving this statement to an else block
(TRY300)
43-43: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
43-43: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🔇 Additional comments (3)
backend/app/main.py (1)
9-9: LGTM!The import and router registration follow the established pattern used for other routes in the application.
Also applies to: 36-36
backend/SQL (1)
341-427: Well-structured collaboration schema.The new tables are properly designed with:
- Appropriate foreign key constraints to
creators(id)- Check constraint preventing self-collaboration (
creator1_id != creator2_id)- Unique constraints preventing duplicate collaborations
- Flexible JSONB fields for metadata and AI suggestions
- Proper timestamp tracking with defaults
- TTL mechanism via
expires_atin the suggestions cacheBased on learnings.
backend/app/api/routes/groq_generate.py (1)
11-11: Model identifier verified as valid.The model identifier
meta-llama/llama-4-scout-17b-16e-instructis confirmed as a valid model for GROQ API chat completions according to official Groq documentation. No changes required.
| import os | ||
| import requests |
There was a problem hiding this comment.
Remove unused import and replace synchronous requests with async httpx.
The os module is imported but never used. More critically, using the synchronous requests library in an async FastAPI endpoint will block the event loop, severely degrading performance under load.
Apply this diff to use the async httpx library (already in requirements.txt):
-import os
-import requests
+import httpxThen update the request call (see separate comment on lines 33-43).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/app/api/routes/groq_generate.py lines 1-2, remove the unused "import
os" and replace the synchronous "import requests" with "import httpx" (async
usage). Update the endpoint to perform an awaited async HTTP call using
httpx.AsyncClient or httpx.post with "await" (e.g., async with
httpx.AsyncClient() as client: resp = await client.post(...)) and
propagate/handle response errors appropriately; ensure you pass timeouts and
headers the same way and replace any synchronous resp.json() with "await
resp.aread()/resp.json()" as needed. Make sure the function signature is async
so it doesn't block the event loop.
| async def generate_groq_response(data: GroqRequest, request: Request): | ||
| api_key = settings.groq_api_key | ||
| if not api_key: | ||
| raise HTTPException(status_code=500, detail="GROQ API key not configured.") | ||
| headers = { | ||
| "Authorization": f"Bearer {api_key}", | ||
| "Content-Type": "application/json" | ||
| } | ||
| payload = { | ||
| "model": data.model, | ||
| "messages": [ | ||
| {"role": "user", "content": data.prompt} | ||
| ], | ||
| "max_tokens": data.max_tokens, | ||
| "temperature": data.temperature | ||
| } | ||
| try: | ||
| response = requests.post( | ||
| "https://api.groq.com/openai/v1/chat/completions", | ||
| headers=headers, | ||
| json=payload, | ||
| timeout=30 | ||
| ) | ||
| response.raise_for_status() | ||
| result = response.json() | ||
| return {"result": result} | ||
| except requests.RequestException as e: | ||
| raise HTTPException(status_code=502, detail=f"GROQ API error: {str(e)}") |
There was a problem hiding this comment.
Fix blocking I/O, remove unused parameter, and improve exception handling.
Several issues in this function:
- The
request: Requestparameter is declared but never used. - Using synchronous
requests.post()blocks the async event loop for up to 30 seconds per call, preventing the server from handling other requests. - Exception chaining should use
from efor better debugging context.
Apply this diff to address all issues:
-async def generate_groq_response(data: GroqRequest, request: Request):
+async def generate_groq_response(data: GroqRequest):
api_key = settings.groq_api_key
if not api_key:
raise HTTPException(status_code=500, detail="GROQ API key not configured.")
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
payload = {
"model": data.model,
"messages": [
{"role": "user", "content": data.prompt}
],
"max_tokens": data.max_tokens,
"temperature": data.temperature
}
try:
- response = requests.post(
+ async with httpx.AsyncClient() as client:
+ response = await client.post(
- "https://api.groq.com/openai/v1/chat/completions",
- headers=headers,
- json=payload,
- timeout=30
- )
- response.raise_for_status()
- result = response.json()
+ "https://api.groq.com/openai/v1/chat/completions",
+ headers=headers,
+ json=payload,
+ timeout=30.0
+ )
+ response.raise_for_status()
+ return {"result": response.json()}
+ except httpx.HTTPStatusError as e:
+ raise HTTPException(
+ status_code=502,
+ detail=f"GROQ API error: {e.response.status_code} - {e.response.text}"
+ ) from e
+ except httpx.RequestError as e:
- return {"result": result}
- except requests.RequestException as e:
- raise HTTPException(status_code=502, detail=f"GROQ API error: {str(e)}")
+ raise HTTPException(status_code=502, detail=f"GROQ API error: {e!r}") from e📝 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.
| async def generate_groq_response(data: GroqRequest, request: Request): | |
| api_key = settings.groq_api_key | |
| if not api_key: | |
| raise HTTPException(status_code=500, detail="GROQ API key not configured.") | |
| headers = { | |
| "Authorization": f"Bearer {api_key}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": data.model, | |
| "messages": [ | |
| {"role": "user", "content": data.prompt} | |
| ], | |
| "max_tokens": data.max_tokens, | |
| "temperature": data.temperature | |
| } | |
| try: | |
| response = requests.post( | |
| "https://api.groq.com/openai/v1/chat/completions", | |
| headers=headers, | |
| json=payload, | |
| timeout=30 | |
| ) | |
| response.raise_for_status() | |
| result = response.json() | |
| return {"result": result} | |
| except requests.RequestException as e: | |
| raise HTTPException(status_code=502, detail=f"GROQ API error: {str(e)}") | |
| async def generate_groq_response(data: GroqRequest): | |
| api_key = settings.groq_api_key | |
| if not api_key: | |
| raise HTTPException(status_code=500, detail="GROQ API key not configured.") | |
| headers = { | |
| "Authorization": f"Bearer {api_key}", | |
| "Content-Type": "application/json" | |
| } | |
| payload = { | |
| "model": data.model, | |
| "messages": [ | |
| {"role": "user", "content": data.prompt} | |
| ], | |
| "max_tokens": data.max_tokens, | |
| "temperature": data.temperature | |
| } | |
| try: | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| "https://api.groq.com/openai/v1/chat/completions", | |
| headers=headers, | |
| json=payload, | |
| timeout=30.0 | |
| ) | |
| response.raise_for_status() | |
| return {"result": response.json()} | |
| except httpx.HTTPStatusError as e: | |
| raise HTTPException( | |
| status_code=502, | |
| detail=f"GROQ API error: {e.response.status_code} - {e.response.text}" | |
| ) from e | |
| except httpx.RequestError as e: | |
| raise HTTPException(status_code=502, detail=f"GROQ API error: {e!r}") from e |
🧰 Tools
🪛 Ruff (0.14.3)
16-16: Unused function argument: request
(ARG001)
41-41: Consider moving this statement to an else block
(TRY300)
43-43: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
43-43: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🤖 Prompt for AI Agents
In backend/app/api/routes/groq_generate.py around lines 16 to 43, remove the
unused request: Request parameter, convert the synchronous requests.post call to
an async HTTP client (e.g., use httpx.AsyncClient or aiohttp) with an async
context manager and an appropriate timeout so the event loop is not blocked,
handle non-2xx responses by calling raise_for_status (or check status_code) and
parse JSON asynchronously, and when raising HTTPException on errors include
exception chaining using "from e" (e.g., raise HTTPException(... ) from e); also
add any required imports for the async client and timeout.
|
|
||
| requests |
There was a problem hiding this comment.
Pin the version and consider using existing httpx instead.
The requests library is added without version pinning, which can lead to reproducibility issues and potential security vulnerabilities. More importantly, httpx is already available in your dependencies (line 17) and provides async HTTP client capabilities, making it a better fit for FastAPI's async context.
If you proceed with requests, apply this diff to pin the version:
-
-requests
+requests==2.32.3However, consider removing requests entirely and using httpx in groq_generate.py instead (see comments in that file).
📝 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.
| requests | |
| requests==2.32.3 |
🤖 Prompt for AI Agents
In backend/requirements.txt around lines 24-25, the pull request adds "requests"
without a pinned version and duplicates functionality already provided by the
existing "httpx" entry (line 17); either pin requests to a specific, supported
version (e.g., requests==2.31.0) to ensure reproducible installs, or preferably
remove the requests entry and update groq_generate.py to use httpx (async client
or sync compatibility layer) so the project relies on the already-listed httpx
and avoids introducing an unpinned dependency.
📝 Description
This pull request introduces Groq AI integration to the backend. It adds a secure FastAPI route that allows the frontend to make API calls to Groq, leveraging the configured GROQ_API_KEY from environment variables. The endpoint is designed for safe, server-side access to Groq's chat completion API.
🔧 Changes Made
Added groq_generate.py route for Groq AI chat completions.
Registered the new route in main.py
Updated requirements.txt to include the requests library for external API calls.
✅ Checklist
Summary by CodeRabbit