Enable Gemini thinking stream

This commit is contained in:
2026-06-15 11:37:29 -07:00
parent b3ec779058
commit e58972d594
5 changed files with 72 additions and 17 deletions

View File

@@ -114,6 +114,12 @@ export async function callGeminiChat(opts: {
maxOutputTokens: 8192, maxOutputTokens: 8192,
}; };
if (GEMINI_MODEL.includes("thinking") || GEMINI_MODEL.includes("pro")) {
config.thinkingConfig = {
thinkingBudget: 1024, includeThoughts: true
};
}
if (opts.systemPrompt) { if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt; config.systemInstruction = opts.systemPrompt;
} }
@@ -197,6 +203,12 @@ export async function* streamGeminiChat(opts: {
maxOutputTokens: 8192, maxOutputTokens: 8192,
}; };
if (GEMINI_MODEL.includes("thinking") || GEMINI_MODEL.includes("pro")) {
config.thinkingConfig = {
thinkingBudget: 1024, includeThoughts: true
};
}
if (opts.systemPrompt) { if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt; config.systemInstruction = opts.systemPrompt;
} }

View File

@@ -161,7 +161,12 @@ export default function PreviewTab() {
if (loading || !anatomy) return; if (loading || !anatomy) return;
ensureCalledRef.current = true; 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", method: "POST",
credentials: "include", credentials: "include",
}) })
@@ -238,7 +243,9 @@ export default function PreviewTab() {
onStart={() => { onStart={() => {
setIsForceStarting(true); setIsForceStarting(true);
fetch( fetch(
`/api/projects/${projectId}/dev-server/ensure?forceStart=true`, `/api/projects/${projectId}/dev-server/ensure?forceStart=true${
selectedPort ? `&port=${selectedPort}` : ""
}`,
{ {
method: "POST", method: "POST",
}, },

View File

@@ -56,7 +56,18 @@ export async function POST(
const projectSlug = (project.data?.slug as string) || project.id; const projectSlug = (project.data?.slug as string) || project.id;
const projectName = (project.data?.name as string) || "Project"; 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<{ const active = await queryOne<{
id: string; id: string;
state: string; state: string;
@@ -67,13 +78,13 @@ export async function POST(
`SELECT id, state, preview_url, command, port `SELECT id, state, preview_url, command, port
FROM fs_dev_servers FROM fs_dev_servers
WHERE project_id = $1 WHERE project_id = $1
AND port = 3000 ${portFilterSql}
AND ( AND (
state = 'running' OR state = 'running' OR
(state = 'starting' AND started_at > NOW() - INTERVAL '15 minutes') (state = 'starting' AND started_at > NOW() - INTERVAL '15 minutes')
) )
ORDER BY started_at DESC LIMIT 1`, ORDER BY started_at DESC LIMIT 1`,
[projectId], queryParams,
); );
// A `starting` row is mid cold-boot; the readiness probe will promote it to // 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? // 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<{ const last = await queryOne<{
command: string; command: string;
port: number; port: number;
@@ -132,13 +153,12 @@ export async function POST(
}>( }>(
`SELECT command, port, preview_url `SELECT command, port, preview_url
FROM fs_dev_servers FROM fs_dev_servers
WHERE project_id = $1 AND port = 3000 WHERE project_id = $1 ${lastPortFilterSql}
ORDER BY started_at DESC LIMIT 1`, ORDER BY started_at DESC LIMIT 1`,
[projectId], lastQueryParams,
); );
const forceStart = const forceStartQuery = forceStart; // we already parsed this above
new URL(request.url).searchParams.get("forceStart") === "true";
// If there's no history, we STILL want to auto-start! We just assume it's a standard // 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 // Next.js app on port 3000. Forcing the user to hit "Start Preview" on a new project

View File

@@ -635,9 +635,23 @@ function Timeline({ entries }: { entries: TimelineEntry[] }) {
<div style={{ marginBottom: 6 }}> <div style={{ marginBottom: 6 }}>
{items.map((item, i) => { {items.map((item, i) => {
if (item.kind === "thought") { if (item.kind === "thought") {
// Reasoning/thought bubbles are intentionally not rendered — they're return (
// internal and add noise to the chat. <div
return null; key={i}
style={{
fontSize: "0.85rem",
color: "#6b7280",
fontStyle: "italic",
padding: "8px 12px",
background: "#f9fafb",
borderLeft: "2px solid #d1d5db",
marginBottom: 8,
whiteSpace: "pre-wrap",
}}
>
Thinking: {item.text}
</div>
);
} }
if (item.kind === "text") { if (item.kind === "text") {
return <TimelineText key={i} text={item.text} />; return <TimelineText key={i} text={item.text} />;

View File

@@ -413,9 +413,9 @@ export async function suspendDevContainer(projectId: string): Promise<void> {
[projectId], [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( 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], [projectId],
).catch(() => {}); ).catch(() => {});
} }
@@ -435,9 +435,9 @@ export async function resumeDevContainer(projectId: string): Promise<void> {
[projectId], [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( 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], [projectId],
).catch(() => {}); ).catch(() => {});
} }
@@ -1065,10 +1065,12 @@ export async function startDevServer(
const logFile = `/var/log/vibn-dev/${id}.log`; const logFile = `/var/log/vibn-dev/${id}.log`;
const listenSafeCommand = ensurePreviewListenAllInterfaces(opts.command); const listenSafeCommand = ensurePreviewListenAllInterfaces(opts.command);
const secret = devAuthSecret(opts.projectId);
const launch = const launch =
`mkdir -p /var/log/vibn-dev && ` + `mkdir -p /var/log/vibn-dev && ` +
`cd /workspace && ` + `cd /workspace && ` +
`nohup env PORT=${opts.port} VIBN_DEV_SERVER_ID=${id} ` + `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 & ` + `bash -lc ${shellEscape(listenSafeCommand)} > ${logFile} 2>&1 & ` +
`echo $!`; `echo $!`;