From b96fed723955716d761b751e6060676ec5ac903a Mon Sep 17 00:00:00 2001 From: yardend Date: Wed, 1 Jul 2026 19:20:29 +0300 Subject: [PATCH 1/6] feat(ai-gateway): add base44.aiGateway.connection() Exposes the Base44 AI Gateway connection ({ baseURL, apiKey }) so apps can call the gateway from their own code with any OpenAI-compatible SDK, without hardcoding the URL or handling the token. Available on the user client and, with the service-role token, via asServiceRole. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/client.ts | 3 ++ src/client.types.ts | 5 +++ src/index.ts | 5 +++ src/modules/ai-gateway.ts | 20 ++++++++++ src/modules/ai-gateway.types.ts | 65 +++++++++++++++++++++++++++++++++ src/modules/types.ts | 1 + tests/unit/ai-gateway.test.ts | 41 +++++++++++++++++++++ 7 files changed, 140 insertions(+) create mode 100644 src/modules/ai-gateway.ts create mode 100644 src/modules/ai-gateway.types.ts create mode 100644 tests/unit/ai-gateway.test.ts diff --git a/src/client.ts b/src/client.ts index 3965f295..5b46e80e 100644 --- a/src/client.ts +++ b/src/client.ts @@ -10,6 +10,7 @@ import { import { getAccessToken } from "./utils/auth-utils.js"; import { createFunctionsModule } from "./modules/functions.js"; import { createAgentsModule } from "./modules/agents.js"; +import { createAiGatewayModule } from "./modules/ai-gateway.js"; import { createAppLogsModule } from "./modules/app-logs.js"; import { createUsersModule } from "./modules/users.js"; import { RoomsSocket, RoomsSocketConfig } from "./utils/socket-utils.js"; @@ -190,6 +191,7 @@ export function createClient(config: CreateClientConfig): Base44Client { serverUrl, token, }), + aiGateway: createAiGatewayModule({ serverUrl, token }), appLogs: createAppLogsModule(axiosClient, appId), users: createUsersModule(axiosClient, appId), analytics: createAnalyticsModule({ @@ -233,6 +235,7 @@ export function createClient(config: CreateClientConfig): Base44Client { serverUrl, token, }), + aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken }), appLogs: createAppLogsModule(serviceRoleAxiosClient, appId), cleanup: () => { if (socket) { diff --git a/src/client.types.ts b/src/client.types.ts index 44c2a6c1..611f81bc 100644 --- a/src/client.types.ts +++ b/src/client.types.ts @@ -8,6 +8,7 @@ import type { } from "./modules/connectors.types.js"; import type { FunctionsModule } from "./modules/functions.types.js"; import type { AgentsModule } from "./modules/agents.types.js"; +import type { AiGatewayModule } from "./modules/ai-gateway.types.js"; import type { AppLogsModule } from "./modules/app-logs.types.js"; import type { AnalyticsModule } from "./modules/analytics.types.js"; @@ -87,6 +88,8 @@ export interface CreateClientConfig { export interface Base44Client { /** {@link AgentsModule | Agents module} for managing AI agent conversations. */ agents: AgentsModule; + /** {@link AiGatewayModule | AI Gateway module} for connecting to the Base44 AI Gateway with your own SDK. */ + aiGateway: AiGatewayModule; /** {@link AnalyticsModule | Analytics module} for tracking custom events in your app. */ analytics: AnalyticsModule; /** {@link AppLogsModule | App logs module} for tracking app usage. */ @@ -129,6 +132,8 @@ export interface Base44Client { readonly asServiceRole: { /** {@link AgentsModule | Agents module} with elevated permissions. */ agents: AgentsModule; + /** {@link AiGatewayModule | AI Gateway module} with the service-role token. */ + aiGateway: AiGatewayModule; /** {@link AppLogsModule | App logs module} with elevated permissions. */ appLogs: AppLogsModule; /** {@link ConnectorsModule | Connectors module} for OAuth token retrieval. */ diff --git a/src/index.ts b/src/index.ts index bc531d89..a2dcdb61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -100,6 +100,11 @@ export type { CreateConversationParams, } from "./modules/agents.types.js"; +export type { + AiGatewayModule, + GatewayConnection, +} from "./modules/ai-gateway.types.js"; + export type { AppLogsModule } from "./modules/app-logs.types.js"; export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js"; diff --git a/src/modules/ai-gateway.ts b/src/modules/ai-gateway.ts new file mode 100644 index 00000000..8304f0a6 --- /dev/null +++ b/src/modules/ai-gateway.ts @@ -0,0 +1,20 @@ +import { getAccessToken } from "../utils/auth-utils.js"; +import { + AiGatewayModule, + AiGatewayModuleConfig, + GatewayConnection, +} from "./ai-gateway.types.js"; + +export function createAiGatewayModule({ + serverUrl, + token, +}: AiGatewayModuleConfig): AiGatewayModule { + const connection = (): GatewayConnection => ({ + baseURL: `${serverUrl}/api/ai/unified/v1`, + token: token ?? getAccessToken() ?? "", + }); + + return { + connection, + }; +} diff --git a/src/modules/ai-gateway.types.ts b/src/modules/ai-gateway.types.ts new file mode 100644 index 00000000..cf2aa2f6 --- /dev/null +++ b/src/modules/ai-gateway.types.ts @@ -0,0 +1,65 @@ +/** + * A connection to the Base44 AI Gateway. + * + * Contains the base URL and bearer token to use with any OpenAI-compatible client + * (the OpenAI SDK, Mastra, and others) pointed at the Base44 AI Gateway. + */ +export interface GatewayConnection { + /** Base URL of the gateway's OpenAI-compatible endpoint. */ + baseURL: string; + /** Bearer token used to authenticate requests to the gateway. */ + token: string; +} + +/** + * Configuration for the AI Gateway module. + * @internal + */ +export interface AiGatewayModuleConfig { + /** Server URL */ + serverUrl?: string; + /** Authentication token */ + token?: string; +} + +/** + * The AI Gateway module. + * + * Exposes the connection details for the Base44 AI Gateway so you can call it from + * your own code using any OpenAI-compatible SDK — for example, to build a custom + * agent on top of the gateway. + */ +export interface AiGatewayModule { + /** + * Gets the connection details for the Base44 AI Gateway. + * + * Returns the `baseURL` and `token` to pass to any OpenAI-compatible client (the + * OpenAI SDK, Mastra, and others), so you can call the gateway from your own code + * without constructing the URL or handling the token yourself. + * + * The `token` is the current caller's bearer token: the app user's token for + * `base44.aiGateway`, or the service-role token for `base44.asServiceRole.aiGateway`. + * + * @returns The gateway {@linkcode GatewayConnection | connection} (`baseURL` and `token`). + * + * @example + * ```typescript + * // Inside a backend function, call the gateway with any OpenAI-compatible SDK + * import { createClientFromRequest } from 'npm:@base44/sdk'; + * import OpenAI from 'npm:openai'; + * + * Deno.serve(async (req) => { + * const base44 = createClientFromRequest(req); + * const { baseURL, token } = base44.aiGateway.connection(); + * + * const openai = new OpenAI({ baseURL, apiKey: token }); + * const res = await openai.chat.completions.create({ + * model: 'claude_sonnet_4_6', + * messages: [{ role: 'user', content: 'Hello!' }], + * }); + * return Response.json({ text: res.choices[0].message.content }); + * }); + * ``` + */ + connection(): GatewayConnection; +} diff --git a/src/modules/types.ts b/src/modules/types.ts index c7423e66..f8f8f19a 100644 --- a/src/modules/types.ts +++ b/src/modules/types.ts @@ -1,4 +1,5 @@ export * from "./app.types.js"; export * from "./agents.types.js"; +export * from "./ai-gateway.types.js"; export * from "./connectors.types.js"; export * from "./analytics.types.js"; \ No newline at end of file diff --git a/tests/unit/ai-gateway.test.ts b/tests/unit/ai-gateway.test.ts new file mode 100644 index 00000000..37103187 --- /dev/null +++ b/tests/unit/ai-gateway.test.ts @@ -0,0 +1,41 @@ +import { describe, test, expect } from "vitest"; +import { createClient } from "../../src/index.ts"; + +describe("AI Gateway Module", () => { + const appId = "test-app-id"; + const serverUrl = "https://api.base44.com"; + const baseURL = `${serverUrl}/api/ai/unified/v1`; + + describe("connection", () => { + test("should return the unified gateway baseURL", () => { + const base44 = createClient({ serverUrl, appId }); + expect(base44.aiGateway.connection().baseURL).toBe(baseURL); + }); + + test("should return an empty token when unauthenticated", () => { + const base44 = createClient({ serverUrl, appId }); + expect(base44.aiGateway.connection().token).toBe(""); + }); + + test("should use the user token when authenticated", () => { + const base44 = createClient({ serverUrl, appId, token: "user-token" }); + expect(base44.aiGateway.connection()).toEqual({ + baseURL, + token: "user-token", + }); + }); + + test("should use the service-role token via asServiceRole", () => { + const base44 = createClient({ + serverUrl, + appId, + token: "user-token", + serviceToken: "service-token", + }); + expect(base44.asServiceRole.aiGateway.connection()).toEqual({ + baseURL, + token: "service-token", + }); + }); + }); +}); From 8560cadf24f3ab930cdc3014d8a4f6e086d1e052 Mon Sep 17 00:00:00 2001 From: yardend Date: Thu, 2 Jul 2026 10:42:08 +0300 Subject: [PATCH 2/6] refactor(ai-gateway): prefix connection type, document unauthenticated token Rename GatewayConnection to AiGatewayConnection to match the SDK's module-prefixed public type convention, and document that connection() returns an empty token for unauthenticated callers. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/index.ts | 2 +- src/modules/ai-gateway.ts | 4 ++-- src/modules/ai-gateway.types.ts | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index a2dcdb61..3c445bbe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -102,7 +102,7 @@ export type { export type { AiGatewayModule, - GatewayConnection, + AiGatewayConnection, } from "./modules/ai-gateway.types.js"; export type { AppLogsModule } from "./modules/app-logs.types.js"; diff --git a/src/modules/ai-gateway.ts b/src/modules/ai-gateway.ts index 8304f0a6..60bcfa37 100644 --- a/src/modules/ai-gateway.ts +++ b/src/modules/ai-gateway.ts @@ -2,14 +2,14 @@ import { getAccessToken } from "../utils/auth-utils.js"; import { AiGatewayModule, AiGatewayModuleConfig, - GatewayConnection, + AiGatewayConnection, } from "./ai-gateway.types.js"; export function createAiGatewayModule({ serverUrl, token, }: AiGatewayModuleConfig): AiGatewayModule { - const connection = (): GatewayConnection => ({ + const connection = (): AiGatewayConnection => ({ baseURL: `${serverUrl}/api/ai/unified/v1`, token: token ?? getAccessToken() ?? "", }); diff --git a/src/modules/ai-gateway.types.ts b/src/modules/ai-gateway.types.ts index cf2aa2f6..8f948e31 100644 --- a/src/modules/ai-gateway.types.ts +++ b/src/modules/ai-gateway.types.ts @@ -4,7 +4,7 @@ * Contains the base URL and bearer token to use with any OpenAI-compatible client * (the OpenAI SDK, Mastra, and others) pointed at the Base44 AI Gateway. */ -export interface GatewayConnection { +export interface AiGatewayConnection { /** Base URL of the gateway's OpenAI-compatible endpoint. */ baseURL: string; /** Bearer token used to authenticate requests to the gateway. */ @@ -39,8 +39,10 @@ export interface AiGatewayModule { * * The `token` is the current caller's bearer token: the app user's token for * `base44.aiGateway`, or the service-role token for `base44.asServiceRole.aiGateway`. + * When the caller is unauthenticated, `token` is an empty string and gateway + * requests will be rejected. * - * @returns The gateway {@linkcode GatewayConnection | connection} (`baseURL` and `token`). + * @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL` and `token`). * * @example * ```typescript @@ -61,5 +63,5 @@ export interface AiGatewayModule { * }); * ``` */ - connection(): GatewayConnection; + connection(): AiGatewayConnection; } From fde88a42c1921946a307cd7547b83954b0387f13 Mon Sep 17 00:00:00 2001 From: yardend Date: Thu, 2 Jul 2026 13:19:41 +0300 Subject: [PATCH 3/6] fix(ai-gateway): update gateway base URL to /api/ai/openai/v1 The backend renamed the dialect segment (base44-dev/apper#13638): /api/ai/unified/v1 -> /api/ai/openai/v1. Co-Authored-By: Claude Fable 5 --- src/modules/ai-gateway.ts | 2 +- tests/unit/ai-gateway.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/ai-gateway.ts b/src/modules/ai-gateway.ts index 60bcfa37..97a43bb5 100644 --- a/src/modules/ai-gateway.ts +++ b/src/modules/ai-gateway.ts @@ -10,7 +10,7 @@ export function createAiGatewayModule({ token, }: AiGatewayModuleConfig): AiGatewayModule { const connection = (): AiGatewayConnection => ({ - baseURL: `${serverUrl}/api/ai/unified/v1`, + baseURL: `${serverUrl}/api/ai/openai/v1`, token: token ?? getAccessToken() ?? "", }); diff --git a/tests/unit/ai-gateway.test.ts b/tests/unit/ai-gateway.test.ts index 37103187..85dc5281 100644 --- a/tests/unit/ai-gateway.test.ts +++ b/tests/unit/ai-gateway.test.ts @@ -4,10 +4,10 @@ import { createClient } from "../../src/index.ts"; describe("AI Gateway Module", () => { const appId = "test-app-id"; const serverUrl = "https://api.base44.com"; - const baseURL = `${serverUrl}/api/ai/unified/v1`; + const baseURL = `${serverUrl}/api/ai/openai/v1`; describe("connection", () => { - test("should return the unified gateway baseURL", () => { + test("should return the OpenAI-compatible gateway baseURL", () => { const base44 = createClient({ serverUrl, appId }); expect(base44.aiGateway.connection().baseURL).toBe(baseURL); }); From 03e574927107b43c6d90914498cfcedbb5b9c494 Mon Sep 17 00:00:00 2001 From: yardend Date: Mon, 6 Jul 2026 10:28:20 +0300 Subject: [PATCH 4/6] fix(ai-gateway): resolve the gateway URL from the app's domain The AI Gateway is a domain-resolved route that only answers on the app's own domain. Prefer the app's public base URL (appBaseUrl, propagated to backend functions via the Base44-App-Base-Url header) over the API host, falling back to serverUrl when it is absent. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/client.ts | 6 +++-- src/modules/ai-gateway.ts | 8 ++++++- src/modules/ai-gateway.types.ts | 3 +++ tests/unit/ai-gateway.test.ts | 41 ++++++++++++++++++++++++++++++++- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/client.ts b/src/client.ts index 5b46e80e..feeab32a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -191,7 +191,7 @@ export function createClient(config: CreateClientConfig): Base44Client { serverUrl, token, }), - aiGateway: createAiGatewayModule({ serverUrl, token }), + aiGateway: createAiGatewayModule({ serverUrl, appBaseUrl: normalizedAppBaseUrl, token }), appLogs: createAppLogsModule(axiosClient, appId), users: createUsersModule(axiosClient, appId), analytics: createAnalyticsModule({ @@ -235,7 +235,7 @@ export function createClient(config: CreateClientConfig): Base44Client { serverUrl, token, }), - aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken }), + aiGateway: createAiGatewayModule({ serverUrl, appBaseUrl: normalizedAppBaseUrl, token: serviceToken }), appLogs: createAppLogsModule(serviceRoleAxiosClient, appId), cleanup: () => { if (socket) { @@ -395,6 +395,7 @@ export function createClientFromRequest(request: Request): Base44Client { ); const appId = request.headers.get("Base44-App-Id"); const serverUrlHeader = request.headers.get("Base44-Api-Url"); + const appBaseUrlHeader = request.headers.get("Base44-App-Base-Url"); const functionsVersion = request.headers.get("Base44-Functions-Version"); const stateHeader = request.headers.get("Base44-State"); @@ -442,6 +443,7 @@ export function createClientFromRequest(request: Request): Base44Client { return createClient({ serverUrl: serverUrlHeader || "https://base44.app", + appBaseUrl: appBaseUrlHeader ?? undefined, appId, token: userToken, serviceToken: serviceRoleToken, diff --git a/src/modules/ai-gateway.ts b/src/modules/ai-gateway.ts index 97a43bb5..9d6f4bd0 100644 --- a/src/modules/ai-gateway.ts +++ b/src/modules/ai-gateway.ts @@ -7,10 +7,16 @@ import { export function createAiGatewayModule({ serverUrl, + appBaseUrl, token, }: AiGatewayModuleConfig): AiGatewayModule { + // The gateway is a domain-resolved route: it only answers on the app's own + // domain. In the browser serverUrl is that domain already; in backend + // functions serverUrl is the API host, so prefer the app's public base URL + // (propagated via the Base44-App-Base-Url header) when available. + const gatewayOrigin = appBaseUrl || serverUrl; const connection = (): AiGatewayConnection => ({ - baseURL: `${serverUrl}/api/ai/openai/v1`, + baseURL: `${gatewayOrigin}/api/ai/openai/v1`, token: token ?? getAccessToken() ?? "", }); diff --git a/src/modules/ai-gateway.types.ts b/src/modules/ai-gateway.types.ts index 8f948e31..73c4218f 100644 --- a/src/modules/ai-gateway.types.ts +++ b/src/modules/ai-gateway.types.ts @@ -18,6 +18,9 @@ export interface AiGatewayConnection { export interface AiGatewayModuleConfig { /** Server URL */ serverUrl?: string; + /** The app's own public base URL (e.g. https://my-app.base44.app). Preferred over + * serverUrl for the gateway URL, since the gateway resolves the app by domain. */ + appBaseUrl?: string; /** Authentication token */ token?: string; } diff --git a/tests/unit/ai-gateway.test.ts b/tests/unit/ai-gateway.test.ts index 85dc5281..0829a212 100644 --- a/tests/unit/ai-gateway.test.ts +++ b/tests/unit/ai-gateway.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "vitest"; -import { createClient } from "../../src/index.ts"; +import { createClient, createClientFromRequest } from "../../src/index.ts"; describe("AI Gateway Module", () => { const appId = "test-app-id"; @@ -25,6 +25,45 @@ describe("AI Gateway Module", () => { }); }); + test("should prefer appBaseUrl over serverUrl (domain-resolved gateway)", () => { + const base44 = createClient({ + serverUrl, + appBaseUrl: "https://my-app.base44.app", + appId, + }); + expect(base44.aiGateway.connection().baseURL).toBe( + "https://my-app.base44.app/api/ai/openai/v1" + ); + }); + + test("should build from the Base44-App-Base-Url header in backend functions", () => { + const request = new Request("https://functions.internal/run", { + headers: { + "Base44-App-Id": appId, + "Base44-Api-Url": serverUrl, + "Base44-App-Base-Url": "https://my-app.base44.app", + Authorization: "Bearer user-token", + }, + }); + const base44 = createClientFromRequest(request); + expect(base44.aiGateway.connection()).toEqual({ + baseURL: "https://my-app.base44.app/api/ai/openai/v1", + token: "user-token", + }); + }); + + test("should fall back to serverUrl when the app-base-url header is absent", () => { + const request = new Request("https://functions.internal/run", { + headers: { + "Base44-App-Id": appId, + "Base44-Api-Url": serverUrl, + Authorization: "Bearer user-token", + }, + }); + const base44 = createClientFromRequest(request); + expect(base44.aiGateway.connection().baseURL).toBe(baseURL); + }); + test("should use the service-role token via asServiceRole", () => { const base44 = createClient({ serverUrl, From 7eda1beb1d303271b19fa993ddc7d6a4206cebe6 Mon Sep 17 00:00:00 2001 From: yardend Date: Mon, 6 Jul 2026 10:48:12 +0300 Subject: [PATCH 5/6] chore(ai-gateway): drop redundant inline comment The domain-resolution rationale is documented on the appBaseUrl config field. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/modules/ai-gateway.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/ai-gateway.ts b/src/modules/ai-gateway.ts index 9d6f4bd0..e4c9be4f 100644 --- a/src/modules/ai-gateway.ts +++ b/src/modules/ai-gateway.ts @@ -10,10 +10,6 @@ export function createAiGatewayModule({ appBaseUrl, token, }: AiGatewayModuleConfig): AiGatewayModule { - // The gateway is a domain-resolved route: it only answers on the app's own - // domain. In the browser serverUrl is that domain already; in backend - // functions serverUrl is the API host, so prefer the app's public base URL - // (propagated via the Base44-App-Base-Url header) when available. const gatewayOrigin = appBaseUrl || serverUrl; const connection = (): AiGatewayConnection => ({ baseURL: `${gatewayOrigin}/api/ai/openai/v1`, From 333989d68fcb4448f9f40f050eea9dd53da7274e Mon Sep 17 00:00:00 2001 From: yardend Date: Mon, 6 Jul 2026 11:24:10 +0300 Subject: [PATCH 6/6] docs(ai-gateway): expand connection() JSDoc with Mastra and OpenAI examples Co-Authored-By: Claude Opus 4.8 (1M context) --- src/modules/ai-gateway.types.ts | 61 ++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/modules/ai-gateway.types.ts b/src/modules/ai-gateway.types.ts index 73c4218f..899c31db 100644 --- a/src/modules/ai-gateway.types.ts +++ b/src/modules/ai-gateway.types.ts @@ -2,7 +2,8 @@ * A connection to the Base44 AI Gateway. * * Contains the base URL and bearer token to use with any OpenAI-compatible client - * (the OpenAI SDK, Mastra, and others) pointed at the Base44 AI Gateway. + * (OpenAI SDK, Mastra, Vercel AI SDK, and others) pointed at the Base44 AI + * Gateway. */ export interface AiGatewayConnection { /** Base URL of the gateway's OpenAI-compatible endpoint. */ @@ -18,38 +19,72 @@ export interface AiGatewayConnection { export interface AiGatewayModuleConfig { /** Server URL */ serverUrl?: string; - /** The app's own public base URL (e.g. https://my-app.base44.app). Preferred over - * serverUrl for the gateway URL, since the gateway resolves the app by domain. */ + /** The app's own public base URL (e.g. https://my-app.base44.app). */ appBaseUrl?: string; /** Authentication token */ token?: string; } /** - * The AI Gateway module. + * AI Gateway module for calling Base44's managed AI models from your own code. * - * Exposes the connection details for the Base44 AI Gateway so you can call it from - * your own code using any OpenAI-compatible SDK — for example, to build a custom - * agent on top of the gateway. + * The gateway exposes an OpenAI-compatible Chat Completions endpoint, so any + * OpenAI-compatible SDK works against it: + * - Build custom AI agents with agent SDKs such as Mastra or the Vercel AI SDK + * - Uses your app's models, billing, and credit quota, no API key to manage + * + * Available in user authentication mode (`base44.aiGateway`) and with the + * service-role token via `base44.asServiceRole.aiGateway`. */ export interface AiGatewayModule { /** * Gets the connection details for the Base44 AI Gateway. * - * Returns the `baseURL` and `token` to pass to any OpenAI-compatible client (the - * OpenAI SDK, Mastra, and others), so you can call the gateway from your own code - * without constructing the URL or handling the token yourself. + * Returns the `baseURL` and `token` to pass to any OpenAI-compatible client. * * The `token` is the current caller's bearer token: the app user's token for * `base44.aiGateway`, or the service-role token for `base44.asServiceRole.aiGateway`. - * When the caller is unauthenticated, `token` is an empty string and gateway - * requests will be rejected. + * When the caller is unauthenticated, `token` is an empty string. * * @returns The gateway {@linkcode AiGatewayConnection | connection} (`baseURL` and `token`). * * @example * ```typescript - * // Inside a backend function, call the gateway with any OpenAI-compatible SDK + * // Build an AI agent with Mastra on top of the gateway, inside a backend function + * import { createClientFromRequest } from 'npm:@base44/sdk'; + * import { Agent } from 'npm:@mastra/core/agent'; + * import { createTool } from 'npm:@mastra/core/tools'; + * import { createOpenAICompatible } from 'npm:@ai-sdk/openai-compatible'; + * import { z } from 'npm:zod'; + * + * Deno.serve(async (req) => { + * const base44 = createClientFromRequest(req); + * const { baseURL, token } = base44.aiGateway.connection(); + * const models = createOpenAICompatible({ name: 'base44', baseURL, apiKey: token }); + * + * const agent = new Agent({ + * id: 'order-helper', + * name: 'order-helper', + * instructions: 'Help the user with their orders.', + * model: models('claude_sonnet_4_6'), + * tools: { + * lookupOrder: createTool({ + * id: 'lookup-order', + * description: 'Fetch an order by id', + * inputSchema: z.object({ orderId: z.string() }), + * execute: async ({ orderId }) => base44.entities.Order.get(orderId), + * }), + * }, + * }); + * + * const { text } = await agent.generate('Where is order 123?'); + * return Response.json({ text }); + * }); + * ``` + * + * @example + * ```typescript + * // Call a model directly with the OpenAI SDK * import { createClientFromRequest } from 'npm:@base44/sdk'; * import OpenAI from 'npm:openai'; *