Skip to content

⚡ Bolt: Prevent event loop blocking with AsyncGroq#71

Open
Adityasingh-8858 wants to merge 1 commit into
mainfrom
bolt-async-groq-8588919464142089387
Open

⚡ Bolt: Prevent event loop blocking with AsyncGroq#71
Adityasingh-8858 wants to merge 1 commit into
mainfrom
bolt-async-groq-8588919464142089387

Conversation

@Adityasingh-8858
Copy link
Copy Markdown
Collaborator

💡 What: Replaced the synchronous Groq client with AsyncGroq in FastAPI endpoints (/ai-voice and /initiate-transfer) and added await to network calls.

🎯 Why: Calling a synchronous network API (like Groq) inside an async def FastAPI endpoint blocks the main asyncio event loop. This prevents the server from processing any other concurrent requests while waiting for the LLM response, causing severe bottlenecks under load.

📊 Impact: Significantly improves concurrency. The server can now handle multiple simultaneous requests without being stalled by long-running LLM inferences.

🔬 Measurement: Run a concurrency benchmark (e.g., using ab or wrk) against /initiate-transfer or /ai-voice with multiple parallel connections. The server will remain responsive, unlike before where requests would queue sequentially.


PR created automatically by Jules for task 8588919464142089387 started by @Deepaksingh7238

Replaces the synchronous Groq client with AsyncGroq in the `ai-voice` and `initiate-transfer` endpoints. This ensures that the FastAPI asyncio event loop is not blocked during network calls to Groq API, allowing the server to handle concurrent requests efficiently.

Co-authored-by: Deepaksingh7238 <110552872+Deepaksingh7238@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 17, 2026 16:21
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown

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

Replaces the synchronous Groq client usage inside async FastAPI endpoints with AsyncGroq and awaits LLM network calls to avoid blocking the asyncio event loop and improve concurrency under load.

Changes:

  • Switched Groq client import/initialization from Groq to AsyncGroq in backend/main.py.
  • Added await to Groq chat completion calls in /ai-voice and /initiate-transfer.
  • Added a small async verification script and a short Bolt learning note.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
backend/main.py Uses AsyncGroq and awaits LLM calls in async endpoints to prevent event loop blocking.
backend/tests/verify_groq_async.py Adds a standalone script to verify AsyncGroq calls work end-to-end.
.jules/bolt.md Documents the learning/action about avoiding sync network calls in async FastAPI handlers.

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

Comment thread backend/main.py
Comment on lines 317 to +321
global groq_client
if groq_client is None:
groq_client = Groq(api_key=GROQ_API_KEY)
chat_completion = groq_client.chat.completions.create(
groq_client = AsyncGroq(api_key=GROQ_API_KEY)
# Use await to avoid blocking the event loop
chat_completion = await groq_client.chat.completions.create(
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The global groq_client is now an AsyncGroq client but it’s never closed during app shutdown. If AsyncGroq holds an underlying async HTTP client, this can leak connections/file descriptors and produce “unclosed client” warnings. Consider closing it in the FastAPI lifespan shutdown block (e.g., await groq_client.aclose()/close() guarded with hasattr) and resetting groq_client = None.

Copilot uses AI. Check for mistakes.
Comment thread backend/main.py
Comment on lines 504 to +510
global groq_client
if groq_client is None:
groq_client = Groq(api_key=GROQ_API_KEY)
groq_client = AsyncGroq(api_key=GROQ_API_KEY)

try:
chat_completion = groq_client.chat.completions.create(
# Use await to avoid blocking the event loop
chat_completion = await groq_client.chat.completions.create(
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

Same as above: AsyncGroq is created lazily and stored globally, but there’s no corresponding shutdown cleanup. Please ensure the client is properly closed in the app lifespan shutdown hook to avoid leaking network resources across reloads/tests.

Copilot uses AI. Check for mistakes.
Comment thread backend/main.py
Comment on lines +10 to 13
from groq import AsyncGroq
from livekit import api
from contextlib import asynccontextmanager
try:
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

asynccontextmanager is imported twice in this module (once near the top and again later). Since this PR touches the import block, consider removing the duplicate import to avoid confusion and keep the import section tidy.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +18
start = time.time()
res = await client.chat.completions.create(
messages=[{"role": "user", "content": "Say hi"}],
model="llama3-8b-8192",
temperature=0.1,
max_tokens=10
)
print(f"Time: {time.time() - start:.2f}s, Res: {res.choices[0].message.content}")
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

This script creates an AsyncGroq client but never closes it. To avoid unclosed-connection warnings (especially when running repeatedly), close the client in a finally block (or use an async context manager if supported).

Suggested change
start = time.time()
res = await client.chat.completions.create(
messages=[{"role": "user", "content": "Say hi"}],
model="llama3-8b-8192",
temperature=0.1,
max_tokens=10
)
print(f"Time: {time.time() - start:.2f}s, Res: {res.choices[0].message.content}")
try:
start = time.time()
res = await client.chat.completions.create(
messages=[{"role": "user", "content": "Say hi"}],
model="llama3-8b-8192",
temperature=0.1,
max_tokens=10
)
print(f"Time: {time.time() - start:.2f}s, Res: {res.choices[0].message.content}")
finally:
await client.close()

Copilot uses AI. Check for mistakes.
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.

2 participants