コンテンツにスキップ

Quickstart

This page shows how to run Vela preflight checks in a Next.js server route and call AI SDK streamText() only when execution is allowed. The key point is that blocked / deny paths do not call the model runtime, and only the allow path uses the relay-backed model.

Terminal window
pnpm add @vel4ai/sdk @vel4ai/ai-sdk ai

@vel4ai/sdk is required. @vel4ai/ai-sdk is an optional adapter that bundles the provider and callbacks for AI SDK streamText().

Terminal window
VELA_APP_ID=app_xxx
VELA_APP_TOKEN=vela_sk_xxx
# Only when using a local or preview API
VELA_BASE_URL=http://localhost:4000

Do not make VELA_CUSTOMER_TENANT_ID and VELA_DELEGATION_ID required production environment variables. Resolve execution scope from the Hosted Connect return URL, database, session, or request body.

import { createVelaStreamTextInput } from "@vel4ai/ai-sdk";
import {
createVelaClientFromEnvironment,
createVelaEnvironmentDiagnosticsResponse,
createVelaErrorResponse,
createVelaIntegration,
getVelaEnvironmentDiagnostics,
resolveVelaExecutionScope,
} from "@vel4ai/sdk";
import { streamText } from "ai";
declare const process: { env: Record<string, string | undefined> };
declare function toModelMessages(messages: unknown): Promise<unknown[]>;
const CHAT_ESTIMATED_COST_USD = 0.05;
export async function POST(req: Request): Promise<Response> {
const environment = getVelaEnvironmentDiagnostics({ env: process.env });
if (!environment.ok) {
return createVelaEnvironmentDiagnosticsResponse(environment);
}
let reporter:
| ReturnType<import("@vel4ai/sdk").PreparedExecutionAllowed["createResultReporter"]>
| null = null;
try {
const body = (await req.json()) as { messages?: unknown; vela?: unknown };
const executionScope = resolveVelaExecutionScope(body.vela);
if (!executionScope) {
return Response.json(
{ error: "Vela execution scope is missing." },
{ status: 400 },
);
}
const client = createVelaClientFromEnvironment({ env: process.env });
const vela = createVelaIntegration({ client, executionScope });
const prepared = await vela.prepareExecutionWithResponse({
estimatedCostUsd: CHAT_ESTIMATED_COST_USD,
});
if (prepared.outcome !== "allow") {
return prepared.response;
}
reporter = prepared.createResultReporter({
onReportError(error) {
console.error("Failed to record the Vela execution result", error);
},
});
const result = streamText(
createVelaStreamTextInput(
prepared,
{ messages: await toModelMessages(body.messages) },
{ reporter },
),
);
return result.toUIMessageStreamResponse();
} catch (error) {
await reporter?.tryReportFailedWithError(error);
return createVelaErrorResponse(error, {
fallbackMessage: "Vela chat failed unexpectedly.",
});
}
}
  • Return missing env before reading the request body.
  • Return 400 for requests without execution scope.
  • Return prepared.response unchanged when prepared.outcome !== "allow".
  • Call streamText() only after allow.
  • Share the same reporter between stream callbacks and the route setup catch.