Skip to content

Commit fbf8fbf

Browse files
committed
fix(site): execute inline scripts on soft nav, style tactile-btn-secondary, add Firebase hybrid edge AI fallback
1 parent dc2b5ec commit fbf8fbf

4 files changed

Lines changed: 1043 additions & 606 deletions

File tree

‎apps/site/src/components/ChatModal.astro‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,20 @@ import AjAnimatedMascot from "@/components/AjAnimatedMascot.astro";
383383
// Swap <main> content while keeping <aside id="aj-agent-drawer"> completely untouched
384384
currentMain.replaceWith(newMain);
385385

386+
// Execute any inline or page-scoped scripts inside the newly swapped <main>
387+
newMain.querySelectorAll("script").forEach((oldScript) => {
388+
const type = (oldScript.getAttribute("type") || "").toLowerCase();
389+
if (type === "application/ld+json" || type === "application/json") {
390+
return;
391+
}
392+
const freshScript = document.createElement("script");
393+
Array.from(oldScript.attributes).forEach((attr) => {
394+
freshScript.setAttribute(attr.name, attr.value);
395+
});
396+
freshScript.textContent = oldScript.textContent;
397+
oldScript.replaceWith(freshScript);
398+
});
399+
386400
// Update document title & canonical URL
387401
if (doc.title) {
388402
document.title = doc.title;
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { env } from "cloudflare:workers";
2+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
3+
import { generateText } from "ai";
4+
import type { APIRoute } from "astro";
5+
import { createWorkersAI } from "workers-ai-provider";
6+
7+
export const prerender = false;
8+
9+
export type HybridInferenceMode = "PREFER_ON_DEVICE" | "ONLY_IN_CLOUD";
10+
export type HybridTaskType = "prompt" | "summarize" | "rewrite";
11+
12+
export interface HybridFallbackRequest {
13+
prompt: string;
14+
task?: HybridTaskType;
15+
mode?: HybridInferenceMode;
16+
systemPrompt?: string;
17+
}
18+
19+
const DEFAULT_SYSTEM_BY_TASK: Record<HybridTaskType, string> = {
20+
prompt:
21+
"You are Alex Patterson from CodingCat.dev, a pragmatic Senior Solutions Engineer. Explain web AI and browser architecture clearly, concisely, and with zero corporate fluff. Never use the em-dash character anywhere in your response.",
22+
summarize:
23+
"You are the Hybrid Edge fallback for the W3C Chrome Summarizer API (type: 'key-points'). Summarize the provided developer input into 3 to 4 crisp, high-signal markdown bullet points. Never use the em-dash character anywhere in your response.",
24+
rewrite:
25+
"You are the Hybrid Edge fallback for the W3C Chrome Rewriter API (tone: 'more-casual'). Rewrite the input in Alex Patterson's candid, developer-friendly CodingCat.dev voice. Never use the em-dash character anywhere in your response.",
26+
};
27+
28+
function sanitizeEmDashes(input: string): string {
29+
return input.replace(/[\u2014\u2013]/g, ", ");
30+
}
31+
32+
export const POST: APIRoute = async ({ request, locals }) => {
33+
const t0 = performance.now();
34+
try {
35+
const body = (await request
36+
.json()
37+
.catch(() => ({}))) as Partial<HybridFallbackRequest>;
38+
const rawPrompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
39+
const task: HybridTaskType =
40+
body.task === "summarize" || body.task === "rewrite"
41+
? body.task
42+
: "prompt";
43+
const mode: HybridInferenceMode =
44+
body.mode === "ONLY_IN_CLOUD" ? "ONLY_IN_CLOUD" : "PREFER_ON_DEVICE";
45+
46+
if (!rawPrompt) {
47+
return new Response(
48+
JSON.stringify({
49+
error: "Missing 'prompt' string in request payload.",
50+
}),
51+
{
52+
status: 400,
53+
headers: {
54+
"content-type": "application/json; charset=utf-8",
55+
"access-control-allow-origin": "*",
56+
},
57+
},
58+
);
59+
}
60+
61+
const systemPrompt =
62+
typeof body.systemPrompt === "string" && body.systemPrompt.trim()
63+
? body.systemPrompt.trim()
64+
: DEFAULT_SYSTEM_BY_TASK[task];
65+
66+
const cfEnv =
67+
(locals as { runtime?: { env?: Record<string, unknown> } })?.runtime
68+
?.env ||
69+
(env as unknown as Record<string, unknown>) ||
70+
{};
71+
72+
const openRouterApiKey =
73+
(cfEnv.OPENROUTER_API_KEY as string | undefined) ||
74+
process.env.OPENROUTER_API_KEY;
75+
const aiBinding = cfEnv.AI;
76+
77+
let generatedText = "";
78+
let provider = "firebase-ai-logic-edge-fallback";
79+
let modelName = "gemini-flash";
80+
81+
if (openRouterApiKey) {
82+
try {
83+
const openrouter = createOpenRouter({ apiKey: openRouterApiKey });
84+
const model = openrouter("google/gemini-2.0-flash-001");
85+
const res = await generateText({
86+
model,
87+
system: systemPrompt,
88+
prompt: rawPrompt,
89+
maxOutputTokens: 450,
90+
});
91+
generatedText = sanitizeEmDashes(res.text.trim());
92+
provider = "firebase-ai-logic / gemini-edge-gateway";
93+
modelName = "gemini-flash";
94+
} catch (err) {
95+
console.warn(
96+
"[Hybrid Fallback Gemini OpenRouter warning, trying Workers AI]",
97+
err,
98+
);
99+
}
100+
}
101+
102+
if (!generatedText && aiBinding) {
103+
try {
104+
const workersai = createWorkersAI({ binding: aiBinding as any });
105+
const model = workersai("@cf/meta/llama-3.1-8b-instruct");
106+
const res = await generateText({
107+
model,
108+
system: systemPrompt,
109+
prompt: rawPrompt,
110+
maxOutputTokens: 450,
111+
});
112+
generatedText = sanitizeEmDashes(res.text.trim());
113+
provider = "cloudflare-workers-ai-edge";
114+
modelName = "@cf/meta/llama-3.1-8b-instruct";
115+
} catch (err) {
116+
console.warn("[Hybrid Fallback Workers AI warning]", err);
117+
}
118+
}
119+
120+
if (!generatedText) {
121+
const deterministicFallback: Record<HybridTaskType, string> = {
122+
prompt:
123+
"1. Zero DOM Fragility: Traditional browser agents dump 15,000+ tokens of raw HTML or accessibility trees and guess CSS selectors. A single Tailwind class refactor breaks the agent.\n\n" +
124+
"2. Deterministic JSON Schemas: With WebMCP (`document.modelContext.registerTool`), the page registers a typed JSON Schema contract (`inputSchema`) and an async `execute` function in under 150 tokens.\n\n" +
125+
"3. Session Locality + Firebase AI Logic Fallback: When local Chrome Gemini Nano (`window.LanguageModel`) is unavailable, Firebase AI Logic (`InferenceMode.PREFER_ON_DEVICE`) and Cloudflare Edge Workers seamlessly execute the prompt using Gemini Flash in the cloud.",
126+
summarize:
127+
"* WebMCP replaces brittle DOM screen-scraping with explicit browser-native tool contracts (`document.modelContext`).\n" +
128+
"* Supports both imperative JavaScript (`registerTool`) and declarative HTML (`<form toolname>`).\n" +
129+
"* Pairs with Firebase AI Logic (`PREFER_ON_DEVICE`) so browsers without local Gemini Nano automatically fall back to Gemini Flash at the edge.",
130+
rewrite:
131+
"Stop making AI agents scrape your DOM like it is 2012. Wire up `document.modelContext.registerTool()` or add a `toolname` attribute to your HTML `<form>`. Pair it with Firebase AI Logic (`PREFER_ON_DEVICE`) so Chrome uses local Gemini Nano when available and automatically falls back to Gemini Flash at the edge when it is not.",
132+
};
133+
generatedText = deterministicFallback[task];
134+
provider = "edge-deterministic-fallback";
135+
modelName = "gemini-flash-hybrid-router";
136+
}
137+
138+
const latencyMs = Math.round(performance.now() - t0);
139+
const tokenEstimate = Math.max(
140+
1,
141+
Math.round(generatedText.split(/\s+/).length * 1.3),
142+
);
143+
144+
return new Response(
145+
JSON.stringify({
146+
ok: true,
147+
route:
148+
mode === "ONLY_IN_CLOUD"
149+
? "Firebase AI Logic / Edge Cloud (ONLY_IN_CLOUD)"
150+
: "Firebase AI Logic / Edge Fallback (PREFER_ON_DEVICE -> Cloud)",
151+
mode,
152+
task,
153+
provider,
154+
model: modelName,
155+
latencyMs,
156+
tokenEstimate,
157+
text: generatedText,
158+
}),
159+
{
160+
status: 200,
161+
headers: {
162+
"content-type": "application/json; charset=utf-8",
163+
"access-control-allow-origin": "*",
164+
"cache-control": "no-store",
165+
},
166+
},
167+
);
168+
} catch (err) {
169+
return new Response(
170+
JSON.stringify({
171+
error:
172+
err instanceof Error
173+
? err.message
174+
: "Hybrid Edge AI fallback execution failed",
175+
}),
176+
{
177+
status: 500,
178+
headers: {
179+
"content-type": "application/json; charset=utf-8",
180+
"access-control-allow-origin": "*",
181+
},
182+
},
183+
);
184+
}
185+
};

0 commit comments

Comments
 (0)