From e58972d5945f2c22435d6b94b9e48eeae773f343 Mon Sep 17 00:00:00 2001 From: mawkone Date: Mon, 15 Jun 2026 11:37:29 -0700 Subject: [PATCH] Enable Gemini thinking stream --- vibn-agent-runner/src/llm/gemini-chat.ts | 12 +++++++ .../[projectId]/(home)/preview/page.tsx | 11 ++++-- .../[projectId]/dev-server/ensure/route.ts | 36 ++++++++++++++----- .../components/vibn-chat/chat-panel.tsx | 20 +++++++++-- vibn-frontend/lib/dev-container.ts | 10 +++--- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/vibn-agent-runner/src/llm/gemini-chat.ts b/vibn-agent-runner/src/llm/gemini-chat.ts index 0c124a7..b18222e 100644 --- a/vibn-agent-runner/src/llm/gemini-chat.ts +++ b/vibn-agent-runner/src/llm/gemini-chat.ts @@ -114,6 +114,12 @@ export async function callGeminiChat(opts: { maxOutputTokens: 8192, }; + if (GEMINI_MODEL.includes("thinking") || GEMINI_MODEL.includes("pro")) { + config.thinkingConfig = { + thinkingBudget: 1024, includeThoughts: true + }; + } + if (opts.systemPrompt) { config.systemInstruction = opts.systemPrompt; } @@ -197,6 +203,12 @@ export async function* streamGeminiChat(opts: { maxOutputTokens: 8192, }; + if (GEMINI_MODEL.includes("thinking") || GEMINI_MODEL.includes("pro")) { + config.thinkingConfig = { + thinkingBudget: 1024, includeThoughts: true + }; + } + if (opts.systemPrompt) { config.systemInstruction = opts.systemPrompt; } diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx index 90c4259..a80e9c3 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/preview/page.tsx @@ -161,7 +161,12 @@ export default function PreviewTab() { if (loading || !anatomy) return; ensureCalledRef.current = true; - fetch(`/api/projects/${projectId}/dev-server/ensure`, { + let url = `/api/projects/${projectId}/dev-server/ensure`; + if (selectedPort) { + url += `?port=${selectedPort}`; + } + + fetch(url, { method: "POST", credentials: "include", }) @@ -238,7 +243,9 @@ export default function PreviewTab() { onStart={() => { setIsForceStarting(true); fetch( - `/api/projects/${projectId}/dev-server/ensure?forceStart=true`, + `/api/projects/${projectId}/dev-server/ensure?forceStart=true${ + selectedPort ? `&port=${selectedPort}` : "" + }`, { method: "POST", }, diff --git a/vibn-frontend/app/api/projects/[projectId]/dev-server/ensure/route.ts b/vibn-frontend/app/api/projects/[projectId]/dev-server/ensure/route.ts index 73902e3..7625935 100644 --- a/vibn-frontend/app/api/projects/[projectId]/dev-server/ensure/route.ts +++ b/vibn-frontend/app/api/projects/[projectId]/dev-server/ensure/route.ts @@ -56,7 +56,18 @@ export async function POST( const projectSlug = (project.data?.slug as string) || project.id; const projectName = (project.data?.name as string) || "Project"; - // 1. Is a dev server already active on the primary port? + const url = new URL(request.url); + const requestedPortStr = url.searchParams.get("port"); + const forceStart = url.searchParams.get("forceStart") === "true"; + + // 1. Is a dev server already active on the requested port (or most recent port)? + let portFilterSql = ""; + const queryParams: any[] = [projectId]; + if (requestedPortStr) { + portFilterSql = "AND port = $2"; + queryParams.push(parseInt(requestedPortStr, 10)); + } + const active = await queryOne<{ id: string; state: string; @@ -67,13 +78,13 @@ export async function POST( `SELECT id, state, preview_url, command, port FROM fs_dev_servers WHERE project_id = $1 - AND port = 3000 + ${portFilterSql} AND ( state = 'running' OR (state = 'starting' AND started_at > NOW() - INTERVAL '15 minutes') ) ORDER BY started_at DESC LIMIT 1`, - [projectId], + queryParams, ); // A `starting` row is mid cold-boot; the readiness probe will promote it to @@ -124,7 +135,17 @@ export async function POST( } // 2. Do we have a previous config to restart from? - // (Limit to port 3000 since that's what the preview pane embeds) + // (Limit to requested port or most recent) + let lastPortFilterSql = ""; + const lastQueryParams: any[] = [projectId]; + if (requestedPortStr) { + lastPortFilterSql = "AND port = $2"; + lastQueryParams.push(parseInt(requestedPortStr, 10)); + } else if (active?.port) { + lastPortFilterSql = "AND port = $2"; + lastQueryParams.push(active.port); + } + const last = await queryOne<{ command: string; port: number; @@ -132,13 +153,12 @@ export async function POST( }>( `SELECT command, port, preview_url FROM fs_dev_servers - WHERE project_id = $1 AND port = 3000 + WHERE project_id = $1 ${lastPortFilterSql} ORDER BY started_at DESC LIMIT 1`, - [projectId], + lastQueryParams, ); - const forceStart = - new URL(request.url).searchParams.get("forceStart") === "true"; + const forceStartQuery = forceStart; // we already parsed this above // If there's no history, we STILL want to auto-start! We just assume it's a standard // Next.js app on port 3000. Forcing the user to hit "Start Preview" on a new project diff --git a/vibn-frontend/components/vibn-chat/chat-panel.tsx b/vibn-frontend/components/vibn-chat/chat-panel.tsx index ff4fcbf..9274e04 100644 --- a/vibn-frontend/components/vibn-chat/chat-panel.tsx +++ b/vibn-frontend/components/vibn-chat/chat-panel.tsx @@ -635,9 +635,23 @@ function Timeline({ entries }: { entries: TimelineEntry[] }) {
{items.map((item, i) => { if (item.kind === "thought") { - // Reasoning/thought bubbles are intentionally not rendered — they're - // internal and add noise to the chat. - return null; + return ( +
+ Thinking: {item.text} +
+ ); } if (item.kind === "text") { return ; diff --git a/vibn-frontend/lib/dev-container.ts b/vibn-frontend/lib/dev-container.ts index 6d5d590..5e602c1 100644 --- a/vibn-frontend/lib/dev-container.ts +++ b/vibn-frontend/lib/dev-container.ts @@ -413,9 +413,9 @@ export async function suspendDevContainer(projectId: string): Promise { [projectId], ); - // Also mark the fixed port-3000 app as stopped so the UI knows + // Also mark ALL preview servers as stopped so the UI knows await query( - `UPDATE fs_dev_servers SET state = 'stopped' WHERE project_id = $1 AND port = 3000`, + `UPDATE fs_dev_servers SET state = 'stopped' WHERE project_id = $1 AND state != 'stopped'`, [projectId], ).catch(() => {}); } @@ -435,9 +435,9 @@ export async function resumeDevContainer(projectId: string): Promise { [projectId], ); - // Mark the fixed port-3000 app as running again since the container boots it + // Mark the last run preview server as starting again since we may need to boot it await query( - `UPDATE fs_dev_servers SET state = 'running' WHERE project_id = $1 AND port = 3000`, + `UPDATE fs_dev_servers SET state = 'starting' WHERE project_id = $1 AND state = 'stopped' AND id = (SELECT id FROM fs_dev_servers WHERE project_id = $1 ORDER BY started_at DESC LIMIT 1)`, [projectId], ).catch(() => {}); } @@ -1065,10 +1065,12 @@ export async function startDevServer( const logFile = `/var/log/vibn-dev/${id}.log`; const listenSafeCommand = ensurePreviewListenAllInterfaces(opts.command); + const secret = devAuthSecret(opts.projectId); const launch = `mkdir -p /var/log/vibn-dev && ` + `cd /workspace && ` + `nohup env PORT=${opts.port} VIBN_DEV_SERVER_ID=${id} ` + + `AUTH_SECRET=${secret} NEXTAUTH_SECRET=${secret} AUTH_TRUST_HOST=true ` + `bash -lc ${shellEscape(listenSafeCommand)} > ${logFile} 2>&1 & ` + `echo $!`;