Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -233,6 +235,7 @@ export function createClient(config: CreateClientConfig): Base44Client {
serverUrl,
token,
}),
aiGateway: createAiGatewayModule({ serverUrl, token: serviceToken }),
appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
cleanup: () => {
if (socket) {
Expand Down
5 changes: 5 additions & 0 deletions src/client.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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. */
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ export type {
CreateConversationParams,
} from "./modules/agents.types.js";

export type {
AiGatewayModule,
AiGatewayConnection,
} from "./modules/ai-gateway.types.js";

export type { AppLogsModule } from "./modules/app-logs.types.js";

export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
Expand Down
20 changes: 20 additions & 0 deletions src/modules/ai-gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getAccessToken } from "../utils/auth-utils.js";
import {
AiGatewayModule,
AiGatewayModuleConfig,
AiGatewayConnection,
} from "./ai-gateway.types.js";

export function createAiGatewayModule({
serverUrl,
token,
}: AiGatewayModuleConfig): AiGatewayModule {
const connection = (): AiGatewayConnection => ({
baseURL: `${serverUrl}/api/ai/openai/v1`,
token: token ?? getAccessToken() ?? "",
});

return {
connection,
};
}
67 changes: 67 additions & 0 deletions src/modules/ai-gateway.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* 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 AiGatewayConnection {
/** 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`.
* When the caller is unauthenticated, `token` is an empty string and gateway
* requests will be rejected.
*
* @returns The gateway {@linkcode AiGatewayConnection | 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(): AiGatewayConnection;
}
1 change: 1 addition & 0 deletions src/modules/types.ts
Original file line number Diff line number Diff line change
@@ -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";
41 changes: 41 additions & 0 deletions tests/unit/ai-gateway.test.ts
Original file line number Diff line number Diff line change
@@ -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/openai/v1`;

describe("connection", () => {
test("should return the OpenAI-compatible 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",
});
});
});
});
Loading