@nuxthub/core
Version:
Build full-stack Nuxt applications on Cloudflare, with zero configuration.
103 lines (102 loc) • 3.5 kB
JavaScript
import { ofetch } from "ofetch";
import { joinURL } from "ufo";
import { createError } from "h3";
import { requireNuxtHubFeature } from "../../../utils/features.js";
import { getCloudflareAccessHeaders } from "../../../utils/cloudflareAccess.js";
import { requireNuxtHubLinkedProject } from "../../../utils/auth.js";
import { useRuntimeConfig } from "#imports";
let _ai;
export function hubAI() {
requireNuxtHubFeature("ai");
if (_ai) {
return _ai;
}
const hub = useRuntimeConfig().hub;
const binding = process.env.AI || globalThis.__env__?.AI || globalThis.AI;
if (hub.remote && hub.projectUrl && !binding) {
const cfAccessHeaders = getCloudflareAccessHeaders(hub.cloudflareAccess);
_ai = proxyHubAI(hub.projectUrl, hub.projectSecretKey || hub.userToken, cfAccessHeaders);
} else if (import.meta.dev) {
_ai = {
async run(model, params, options) {
requireNuxtHubLinkedProject(hub, "hubAI");
return $fetch(`/api/projects/${hub.projectKey}/ai/run`, {
baseURL: hub.url,
method: "POST",
headers: {
authorization: `Bearer ${hub.userToken}`
},
body: { model, params, options },
responseType: params?.stream ? "stream" : void 0
}).catch(handleProxyError);
},
async models(params) {
requireNuxtHubLinkedProject(hub, "hubAI");
return $fetch(`/api/projects/${hub.projectKey}/ai/models`, {
baseURL: hub.url,
method: "POST",
headers: {
authorization: `Bearer ${hub.userToken}`
},
body: { params }
}).catch(handleProxyError);
},
async toMarkdown(_files, _options) {
throw createError({
statusCode: 501,
message: "hubAI().toMarkdown() is only supported with remote storage in development mode."
});
}
};
} else if (binding) {
_ai = binding;
}
if (!_ai) {
throw createError("Missing Cloudflare AI binding (AI)");
}
return _ai;
}
export function proxyHubAI(projectUrl, secretKey, headers) {
requireNuxtHubFeature("ai");
const aiAPI = ofetch.create({
baseURL: joinURL(projectUrl, "/api/_hub/ai"),
method: "POST",
headers: {
Authorization: `Bearer ${secretKey}`,
...headers
}
});
return {
async run(model, params, options) {
return aiAPI("/run", {
body: { model, params, options },
responseType: params?.stream ? "stream" : void 0
}).catch(handleProxyError);
},
async models(params) {
return aiAPI("/models", {
body: { params }
}).catch(handleProxyError);
},
async toMarkdown(files, options) {
return aiAPI("/to-markdown", {
body: { files, options }
}).catch(handleProxyError);
}
};
}
async function handleProxyError(err) {
if (import.meta.dev && err.statusCode === 403) {
console.warn("It seems that your Cloudflare API token does not have the `Worker AI` permission.\nOpen `https://dash.cloudflare.com/profile/api-tokens` and edit your NuxtHub token.\nAdd the `Account > Worker AI > Read` permission to your token and save it.");
}
let data = err.data;
if (!err.data && typeof err.response?.json === "function") {
data = (await err.response.json())?.data || {};
}
throw createError({
statusCode: data?.statusCode || err.statusCode,
statusMessage: data?.statusMessage || err.statusMessage,
message: data?.message || err.message,
data
});
}