ai-chat - Cloud Run function


index.js
Copied!
import { openai } from "@ai-sdk/openai";
import { frontendTools } from "@assistant-ui/react-ai-sdk";
import functions from "@google-cloud/functions-framework";
import { RecaptchaEnterpriseServiceClient } from "@google-cloud/recaptcha-enterprise";
import {
convertToModelMessages,
pipeUIMessageStreamToResponse,
safeValidateUIMessages,
streamText,
toUIMessageStream,
} from "ai";
import { z } from "zod";

const { ALLOWED_ORIGINS, INSTRUCTIONS, MODEL, REASONING, RECAPTCHA_SITE_KEY } = process.env;

const recaptchaClient = new RecaptchaEnterpriseServiceClient();

functions.http("index", async (req, res) => {
try {
if (!ALLOWED_ORIGINS) {
throw new Error("ALLOWED_ORIGINS is not set");
}
const origin = req.get("Origin");
if (ALLOWED_ORIGINS.split(",").includes(origin)) {
res.set("Access-Control-Allow-Origin", origin);
res.set("Access-Control-Allow-Methods", "POST");
res.set("Access-Control-Allow-Headers", "Content-Type");
}
if (req.method === "OPTIONS") {
res.status(204).send("");
return;
}
if (req.method !== "POST") {
res.status(405).send("Method Not Allowed");
return;
}

const { data } = z
.object({
recaptchaToken: z.string().max(4096),
tools: z.unknown(),
messages: z.unknown(),
})
.safeParse(req.body);
if (!data) {
res.status(400).send("Invalid request body");
return;
}

const tools = frontendTools(data.tools);
const { data: messages } = await safeValidateUIMessages({
tools,
messages: data.messages,
});
if (!messages) {
res.status(400).send("Invalid messages");
return;
}

if (!RECAPTCHA_SITE_KEY) {
throw new Error("RECAPTCHA_SITE_KEY is not set");
}
const [assessmentResponse] = await recaptchaClient.createAssessment({
assessment: {
event: {
siteKey: RECAPTCHA_SITE_KEY,
token: data.recaptchaToken,
},
},
parent: recaptchaClient.projectPath("kohsei-san"),
});
if (!assessmentResponse.tokenProperties.valid) {
console.log(
`The CreateAssessment call failed because the token was: ${assessmentResponse.tokenProperties.invalidReason}`,
);
res.status(403).send("");
return;
}
if (assessmentResponse.tokenProperties.action !== "GET_CLIENT_SECRET") {
console.log(
"The action attribute in your reCAPTCHA tag does not match the action you are expecting to score",
);
res.status(403).send("");
return;
}
console.log(
`The reCAPTCHA score is: ${assessmentResponse.riskAnalysis.score}`,
);
for (const reason of assessmentResponse.riskAnalysis.reasons) {
console.log(reason);
}
if (assessmentResponse.riskAnalysis.score < 0.5) {
res.status(403).send("");
return;
}

if (!MODEL) {
throw new Error("MODEL is not set");
}
const { stream } = streamText({
model: openai(MODEL),
reasoning: REASONING,
tools,
instructions: INSTRUCTIONS,
messages: await convertToModelMessages(messages),
});

pipeUIMessageStreamToResponse({
response: res,
stream: toUIMessageStream({ stream }),
});
} catch (exception) {
console.error(exception);
res.status(500).send("");
}
});