Spaces:
Sleeping
Sleeping
Update app.py
#2
by luckytec - opened
app.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
import os
|
| 2 |
import asyncio
|
| 3 |
import uvicorn
|
| 4 |
from fastapi import FastAPI
|
|
@@ -7,6 +6,10 @@ from playwright.async_api import async_playwright
|
|
| 7 |
from contextlib import asynccontextmanager
|
| 8 |
|
| 9 |
# --- Configuration ---
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
class GeminiBot:
|
| 11 |
def __init__(self):
|
| 12 |
self.playwright = None
|
|
@@ -14,50 +17,49 @@ class GeminiBot:
|
|
| 14 |
|
| 15 |
async def start(self):
|
| 16 |
self.playwright = await async_playwright().start()
|
| 17 |
-
# Launch browser with flags to help avoid detection
|
| 18 |
self.browser = await self.playwright.chromium.launch(
|
| 19 |
-
headless=True,
|
| 20 |
args=["--no-sandbox", "--disable-blink-features=AutomationControlled"]
|
| 21 |
)
|
| 22 |
-
print("--- Gemini Bot (Stateless) Ready ---")
|
| 23 |
|
| 24 |
async def process_chat(self, message: str):
|
| 25 |
-
#
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
try:
|
| 32 |
-
# BLOCK unnecessary assets to speed up loading
|
| 33 |
-
await page.route("**/*", lambda route: route.abort() if route.request.resource_type in
|
| 34 |
-
["image", "font", "media", "stylesheet"] else route.continue_())
|
| 35 |
-
|
| 36 |
-
# Navigate and wait for network idle to ensure JS loads
|
| 37 |
-
await page.goto("https://gemini.google.com", wait_until="networkidle")
|
| 38 |
-
|
| 39 |
-
# Interact with the chat box
|
| 40 |
-
chat_box = page.locator("div[contenteditable='true']")
|
| 41 |
-
await chat_box.wait_for(timeout=20000)
|
| 42 |
-
await chat_box.fill(message)
|
| 43 |
-
await page.keyboard.press("Enter")
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
async def stop(self):
|
| 63 |
if self.browser: await self.browser.close()
|
|
@@ -76,13 +78,9 @@ app = FastAPI(lifespan=lifespan)
|
|
| 76 |
class ChatRequest(BaseModel):
|
| 77 |
message: str
|
| 78 |
|
| 79 |
-
@app.get("/")
|
| 80 |
-
def health_check():
|
| 81 |
-
return {"status": "online", "message": "Gemini API is running"}
|
| 82 |
-
|
| 83 |
@app.post("/chat")
|
| 84 |
async def chat_api(request: ChatRequest):
|
| 85 |
-
# This
|
| 86 |
return await bot.process_chat(request.message)
|
| 87 |
|
| 88 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import uvicorn
|
| 3 |
from fastapi import FastAPI
|
|
|
|
| 6 |
from contextlib import asynccontextmanager
|
| 7 |
|
| 8 |
# --- Configuration ---
|
| 9 |
+
# Limit simultaneous browser instances to prevent memory crashes
|
| 10 |
+
MAX_CONCURRENT_REQUESTS = 3
|
| 11 |
+
semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
|
| 12 |
+
|
| 13 |
class GeminiBot:
|
| 14 |
def __init__(self):
|
| 15 |
self.playwright = None
|
|
|
|
| 17 |
|
| 18 |
async def start(self):
|
| 19 |
self.playwright = await async_playwright().start()
|
|
|
|
| 20 |
self.browser = await self.playwright.chromium.launch(
|
| 21 |
+
headless=True,
|
| 22 |
args=["--no-sandbox", "--disable-blink-features=AutomationControlled"]
|
| 23 |
)
|
| 24 |
+
print("--- Gemini Bot (Concurrent Stateless Mode) Ready ---")
|
| 25 |
|
| 26 |
async def process_chat(self, message: str):
|
| 27 |
+
# The semaphore ensures only N requests run at once
|
| 28 |
+
async with semaphore:
|
| 29 |
+
context = await self.browser.new_context(
|
| 30 |
+
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
| 31 |
+
)
|
| 32 |
+
page = await context.new_page()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
try:
|
| 35 |
+
# Block heavy assets for speed
|
| 36 |
+
await page.route("**/*", lambda route: route.abort() if route.request.resource_type in
|
| 37 |
+
["image", "font", "media", "stylesheet"] else route.continue_())
|
| 38 |
+
|
| 39 |
+
await page.goto("https://gemini.google.com", wait_until="networkidle")
|
| 40 |
+
|
| 41 |
+
# Logic: If not logged in, this will wait until timeout
|
| 42 |
+
chat_box = page.locator("div[contenteditable='true']")
|
| 43 |
+
await chat_box.wait_for(timeout=30000)
|
| 44 |
+
await chat_box.fill(message)
|
| 45 |
+
await page.keyboard.press("Enter")
|
| 46 |
+
|
| 47 |
+
# Wait for the response container
|
| 48 |
+
await page.wait_for_selector("message-content", timeout=45000)
|
| 49 |
+
|
| 50 |
+
# Extract the last message
|
| 51 |
+
current_text = await page.evaluate("""() => {
|
| 52 |
+
const msgs = document.querySelectorAll('message-content');
|
| 53 |
+
return msgs.length > 0 ? msgs[msgs.length - 1].innerText : "No response";
|
| 54 |
+
}""")
|
| 55 |
+
|
| 56 |
+
return {"text": current_text}
|
| 57 |
+
|
| 58 |
+
except Exception as e:
|
| 59 |
+
return {"text": f"Error: {str(e)}"}
|
| 60 |
+
finally:
|
| 61 |
+
# Session is wiped here, regardless of success or failure
|
| 62 |
+
await context.close()
|
| 63 |
|
| 64 |
async def stop(self):
|
| 65 |
if self.browser: await self.browser.close()
|
|
|
|
| 78 |
class ChatRequest(BaseModel):
|
| 79 |
message: str
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
@app.post("/chat")
|
| 82 |
async def chat_api(request: ChatRequest):
|
| 83 |
+
# This call is non-blocking to other concurrent requests
|
| 84 |
return await bot.process_chat(request.message)
|
| 85 |
|
| 86 |
if __name__ == "__main__":
|