diff --git a/browser-use-service/main.py b/browser-use-service/main.py index 249180b..7994527 100644 --- a/browser-use-service/main.py +++ b/browser-use-service/main.py @@ -513,26 +513,27 @@ def is_offer(text: str) -> bool: # Each run picks 2-4 random queries to vary behavior and reduce detection. FB_SEARCHES = [ "looking for web developer", - "need a website designed", - "need website built South Africa", "need someone to build my website", - "need web designer", - "looking for someone to create website", - "need ecommerce website built", - "want to hire web developer", - "need wordpress website", - "I need a website for my business", - "need a site for my business", - "looking for website designer South Africa", "need website for my small business", "who can build me a website", - "want to create a website for my business", - "looking for affordable web design", - "need a web developer South Africa", + "recommend a web developer", + "I need a website for my", + "need a site for my business", + "looking for web designer", "help me build a website", "need someone to design my website", - "looking for web designer near me", - "need a website for my startup", + "anyone know a web developer", + "need a web developer for my project", + "need website for my startup", + "want a website for my company", + "need a new website for my", + "looking for web developer for my business", + "need ecommerce website for my", + "looking for affordable web design", + "need wordpress website", + "who can design a website for my", + "looking for someone to create a website", + "recommendations for a web designer", ] VIEWPORTS = [ @@ -1401,26 +1402,38 @@ Return a JSON array like ["yes","no","yes"] matching the order above.""" logger.warning("AI classification failed, falling back to keyword filter: %s", e) if not ai_succeeded: - strict_keywords = [ + web_terms = [ "website", "web design", "web develop", "web dev", + "web designer", "web developer", "build my website", "build a website", "create a website", - "need web", "looking for web", "new website", - "landing page", "wordpress", - "need a website", "my website", "business website", - "need a designer", "help with my website", - "redesign", "update my website", + "landing page", "wordpress", "ecommerce", + "my website", "business website", + "site for my", "site for my business", + "new website", "redesign my website", + "help with my website", "update my website", + ] + request_terms = [ "looking for", "need a", "need an", "looking to", "need someone", "hire a", "want someone", "need help with", "would like", "build me", "design my", "make me a", "create my", + "looking", "need", "want", "help", + "who can", "i need", + "recommend", "anyone know", "anyone recommend", + "know a", "know any", "recommendation", + "suggest", "looking for recommendations", + "can anyone", "does anyone", ] filtered = [] for r in results: t = r['title'].lower() - if not any(kw in t for kw in strict_keywords): + has_web = any(kw in t for kw in web_terms) + has_request = any(kw in t for kw in request_terms) + if not has_web or not has_request: continue if any(kw in t for kw in ['i build website', 'i offer web', 'i am a web developer', - 'affordable web design package', 'web hosting']): + 'affordable web design package', 'web hosting', + 'i do web design', 'i develop websites']): continue filtered.append(r) return filtered[:10] diff --git a/public/uploads/voice/b2ce8bd0-6805-4544-8d34-f7c27cb57833.webm b/public/uploads/voice/b2ce8bd0-6805-4544-8d34-f7c27cb57833.webm new file mode 100644 index 0000000..c0bd07c Binary files /dev/null and b/public/uploads/voice/b2ce8bd0-6805-4544-8d34-f7c27cb57833.webm differ diff --git a/src/app/(dashboard)/chats/page.tsx b/src/app/(dashboard)/chats/page.tsx index bfc2e1c..ae65747 100644 --- a/src/app/(dashboard)/chats/page.tsx +++ b/src/app/(dashboard)/chats/page.tsx @@ -920,7 +920,7 @@ const formatPreviewContent = (content: string) => {
- -
- )} + {bootState === "ready" && readyOverlay}
{messages.map((msg, i) => ( @@ -163,7 +233,10 @@ export function AIChat() {
- + + + +
)} @@ -192,10 +265,15 @@ export function AIChat() { disabled={loading || !input.trim()} className="h-9 w-9 rounded-lg bg-[#1BB0CE] hover:bg-[#1BB0CE]/80 disabled:opacity-40 flex items-center justify-center flex-none transition-colors" > - {loading ? : } + {loading ? ( + + + + + ) : } ) -} +} \ No newline at end of file diff --git a/src/lib/ai.ts b/src/lib/ai.ts index f9573fc..91cee9c 100644 --- a/src/lib/ai.ts +++ b/src/lib/ai.ts @@ -1,41 +1,76 @@ +import http from "http" + const AI_SERVICE = process.env.AI_SERVICE_URL || "http://localhost:3001" +function parseUrl(url: string) { + const u = new URL(url) + return { hostname: u.hostname, port: parseInt(u.port) || 80, path: u.pathname + u.search } +} + export async function chatWithAI(message: string, jwtToken: string) { - const res = await fetch(`${AI_SERVICE}/ai/chat`, { - method: "POST", - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${jwtToken}` }, - body: JSON.stringify({ message }), + const body = JSON.stringify({ message }) + const { hostname, port, path } = parseUrl(`${AI_SERVICE}/ai/chat`) + + const text = await new Promise((resolve, reject) => { + const req = http.request( + { hostname, port, path, method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${jwtToken}`, "Content-Length": Buffer.byteLength(body) } }, + (res) => { + let data = "" + res.on("data", (chunk) => data += chunk) + res.on("end", () => { + if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { + resolve(data) + } else { + reject(new Error(`AI error ${res.statusCode}: ${data.substring(0, 200)}`)) + } + }) + } + ) + req.on("error", reject) + req.write(body) + req.end() }) - if (!res.ok) { - const text = await res.text() - throw new Error(`AI service error (${res.status}): ${text}`) - } - - const data = await res.json() + const data = JSON.parse(text) return data.response || "" } -export async function fetchJobs() { +export async function checkAiServiceStatus() { + const { hostname, port, path } = parseUrl(`${AI_SERVICE}/health`) try { - const res = await fetch(`${AI_SERVICE}/ai/jobs`) - if (!res.ok) return [] - const data = await res.json() + await new Promise((resolve, reject) => { + const req = http.get({ hostname, port, path, timeout: 3000 }, (res) => { + let data = "" + res.on("data", (chunk) => data += chunk) + res.on("end", () => { + try { resolve(JSON.parse(data).status === "ok" ? undefined : reject(new Error("not ok"))) } catch { reject(new Error("bad response")) } + }) + }) + req.on("error", reject) + req.on("timeout", () => { req.destroy(); reject(new Error("timeout")) }) + }) + return true + } catch { + return false + } +} + +export async function fetchJobs() { + const { hostname, port, path } = parseUrl(`${AI_SERVICE}/ai/jobs`) + try { + const text = await new Promise((resolve, reject) => { + const req = http.get({ hostname, port, path, timeout: 5000 }, (res) => { + let data = "" + res.on("data", (chunk) => data += chunk) + res.on("end", () => resolve(data)) + }) + req.on("error", reject) + req.on("timeout", () => { req.destroy(); reject(new Error("timeout")) }) + }) + const data = JSON.parse(text) return data.jobs || [] } catch { console.warn("Failed to fetch AI jobs") return [] } } - -export async function checkAiServiceStatus() { - try { - const res = await fetch(`${AI_SERVICE}/health`) - if (!res.ok) return false - const data = await res.json() - return data.status === "ok" - } catch { - console.warn("Failed to check AI service status") - return false - } -}