Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { tracingMiddleware } from "./middleware/tracing.js";
import { beacon } from "./routes/beacon.js";
import { routes } from "./routes/index.js";
import { internalModels } from "./routes/internal-models.js";
import { publicApps } from "./routes/public-apps.js";
import { publicChatSupport } from "./routes/public-chat-support.js";
import { publicContact } from "./routes/public-contact.js";
import { publicDiscounts } from "./routes/public-discounts.js";
Expand Down Expand Up @@ -228,6 +229,7 @@ app.route("/public/discounts", publicDiscounts);
app.route("/public/contact", publicContact);
app.route("/public/newsletter", publicNewsletter);
app.route("/public/chat-support", publicChatSupport);
app.route("/public/apps", publicApps);

app.doc("/json", config);

Expand Down
83 changes: 83 additions & 0 deletions apps/api/src/routes/public-apps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { z } from "zod";

import { db, isNotNull, sql, tables } from "@llmgateway/db";

import type { ServerTypes } from "@/vars.js";

export const publicApps = new OpenAPIHono<ServerTypes>();

const appStatSchema = z.object({
source: z.string(),
totalTokens: z.number(),
totalRequests: z.number(),
lastUsedAt: z.string().nullable(),
});

const listAppsRoute = createRoute({
method: "get",
path: "/",
request: {
query: z.object({
limit: z
.string()
.optional()
.transform((v) => (v ? Number.parseInt(v, 10) : 100))
.pipe(z.number().int().min(1).max(500)),
}),
},
responses: {
200: {
content: {
"application/json": {
schema: z.object({
apps: z.array(appStatSchema),
totalApps: z.number(),
totalTokens: z.number(),
totalRequests: z.number(),
}),
},
},
description:
"Aggregated token usage per app/source across all LLM Gateway traffic.",
},
},
});

publicApps.openapi(listAppsRoute, async (c) => {
const { limit } = c.req.valid("query");

const rows = await db
.select({
source: tables.log.source,
totalTokens: sql<string>`COALESCE(SUM(CAST(${tables.log.totalTokens} AS NUMERIC)), 0)`,
totalRequests: sql<string>`COUNT(*)`,
lastUsedAt: sql<Date | null>`MAX(${tables.log.createdAt})`,
})
.from(tables.log)
.where(isNotNull(tables.log.source))
.groupBy(tables.log.source)
.orderBy(
sql`COALESCE(SUM(CAST(${tables.log.totalTokens} AS NUMERIC)), 0) DESC`,
)
.limit(limit);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

const apps = rows
.filter((r): r is typeof r & { source: string } => r.source !== null)
.map((r) => ({
source: r.source,
totalTokens: Number(r.totalTokens) || 0,
totalRequests: Number(r.totalRequests) || 0,
lastUsedAt: r.lastUsedAt ? new Date(r.lastUsedAt).toISOString() : null,
}));

const totalTokens = apps.reduce((sum, a) => sum + a.totalTokens, 0);
const totalRequests = apps.reduce((sum, a) => sum + a.totalRequests, 0);

return c.json({
apps,
totalApps: apps.length,
totalTokens,
totalRequests,
});
});
47 changes: 47 additions & 0 deletions apps/code/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,53 @@ export interface paths {
patch?: never;
trace?: never;
};
"/public/apps": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: {
parameters: {
query?: {
limit?: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Aggregated token usage per app/source across all LLM Gateway traffic. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
apps: {
source: string;
totalTokens: number;
totalRequests: number;
lastUsedAt: string | null;
}[];
totalApps: number;
totalTokens: number;
totalRequests: number;
};
};
};
};
};
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/user/me": {
parameters: {
query?: never;
Expand Down
47 changes: 47 additions & 0 deletions apps/playground/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,53 @@ export interface paths {
patch?: never;
trace?: never;
};
"/public/apps": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: {
parameters: {
query?: {
limit?: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Aggregated token usage per app/source across all LLM Gateway traffic. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
apps: {
source: string;
totalTokens: number;
totalRequests: number;
lastUsedAt: string | null;
}[];
totalApps: number;
totalTokens: number;
totalRequests: number;
};
};
};
};
};
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/user/me": {
parameters: {
query?: never;
Expand Down
Loading
Loading