Skip to content

Commit 0b9481b

Browse files
authored
feat: add cost column to generations table (langfuse#725)
1 parent 603bd2a commit 0b9481b

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

src/components/table/use-cases/generations.tsx

+39-23
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { type LangfuseColumnDef } from "@/src/components/table/types";
3030
import { type ObservationLevel } from "@prisma/client";
3131
import { cn } from "@/src/utils/tailwind";
3232
import { LevelColors } from "@/src/components/level-colors";
33+
import { usdFormatter } from "@/src/utils/numbers";
3334

3435
export type GenerationsTableRow = {
3536
id: string;
@@ -200,6 +201,18 @@ export default function GenerationsTable({ projectId }: GenerationsTableProps) {
200201
},
201202
enableHiding: true,
202203
},
204+
{
205+
accessorKey: "cost",
206+
header: "Cost",
207+
cell: ({ row }) => {
208+
const value: number | undefined = row.getValue("cost");
209+
console.log(value);
210+
return value !== undefined ? (
211+
<span>{usdFormatter(value)}</span>
212+
) : undefined;
213+
},
214+
enableHiding: true,
215+
},
203216
{
204217
accessorKey: "level",
205218
header: "Level",
@@ -293,29 +306,32 @@ export default function GenerationsTable({ projectId }: GenerationsTableProps) {
293306
);
294307

295308
const rows: GenerationsTableRow[] = generations.isSuccess
296-
? generations.data.map((generation) => ({
297-
id: generation.id,
298-
traceId: generation.traceId,
299-
traceName: generation.traceName,
300-
startTime: generation.startTime.toLocaleString(),
301-
endTime: generation.endTime?.toLocaleString() ?? undefined,
302-
latency: generation.latency === null ? undefined : generation.latency,
303-
name: generation.name ?? undefined,
304-
version: generation.version ?? "",
305-
model: generation.model ?? "",
306-
input: generation.input,
307-
output: generation.output,
308-
level: generation.level,
309-
metadata: generation.metadata
310-
? JSON.stringify(generation.metadata)
311-
: undefined,
312-
statusMessage: generation.statusMessage ?? undefined,
313-
usage: {
314-
promptTokens: generation.promptTokens,
315-
completionTokens: generation.completionTokens,
316-
totalTokens: generation.totalTokens,
317-
},
318-
}))
309+
? generations.data.map((generation) => {
310+
return {
311+
id: generation.id,
312+
traceId: generation.traceId,
313+
traceName: generation.traceName,
314+
startTime: generation.startTime.toLocaleString(),
315+
endTime: generation.endTime?.toLocaleString() ?? undefined,
316+
latency: generation.latency === null ? undefined : generation.latency,
317+
cost: generation.cost,
318+
name: generation.name ?? undefined,
319+
version: generation.version ?? "",
320+
model: generation.model ?? "",
321+
input: generation.input,
322+
output: generation.output,
323+
level: generation.level,
324+
metadata: generation.metadata
325+
? JSON.stringify(generation.metadata)
326+
: undefined,
327+
statusMessage: generation.statusMessage ?? undefined,
328+
usage: {
329+
promptTokens: generation.promptTokens,
330+
completionTokens: generation.completionTokens,
331+
totalTokens: generation.totalTokens,
332+
},
333+
};
334+
})
319335
: [];
320336

321337
return (

src/pages/api/public/ingestion.ts

-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ const handleSingleEvent = async (
206206

207207
await persistEventMiddleware(
208208
prisma,
209-
210209
apiScope.projectId,
211210
req,
212211
cleanedEvent,

src/server/api/routers/generations.ts

+51-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import {
1616
type ObservationOptions,
1717
observationsTableCols,
1818
} from "@/src/server/api/definitions/observationsTable";
19+
import { calculateTokenCost } from "@/src/features/ingest/lib/usage";
20+
import Decimal from "decimal.js";
21+
import { usdFormatter } from "@/src/utils/numbers";
1922

2023
const exportFileFormats = ["CSV", "JSON", "OPENAI-JSONL"] as const;
2124
export type ExportFileFormats = (typeof exportFileFormats)[number];
@@ -118,7 +121,25 @@ export const generationsRouter = createTRPCRouter({
118121
`,
119122
);
120123

121-
return generations;
124+
const pricings = await ctx.prisma.pricing.findMany();
125+
126+
return generations.map(({ input, output, ...rest }) => {
127+
return {
128+
...rest,
129+
input,
130+
output,
131+
cost: rest.model
132+
? calculateTokenCost(pricings, {
133+
model: rest.model,
134+
totalTokens: new Decimal(rest.totalTokens),
135+
promptTokens: new Decimal(rest.promptTokens),
136+
completionTokens: new Decimal(rest.completionTokens),
137+
input: input,
138+
output: output,
139+
})
140+
: undefined,
141+
};
142+
});
122143
}),
123144

124145
export: protectedProjectProcedure
@@ -175,6 +196,28 @@ export const generationsRouter = createTRPCRouter({
175196
`,
176197
);
177198

199+
const pricings = await ctx.prisma.pricing.findMany();
200+
201+
const enrichedGenerations = generations.map(
202+
({ input, output, ...rest }) => {
203+
return {
204+
...rest,
205+
input,
206+
output,
207+
cost: rest.model
208+
? calculateTokenCost(pricings, {
209+
model: rest.model,
210+
totalTokens: new Decimal(rest.totalTokens),
211+
promptTokens: new Decimal(rest.promptTokens),
212+
completionTokens: new Decimal(rest.completionTokens),
213+
input: input,
214+
output: output,
215+
})
216+
: undefined,
217+
};
218+
},
219+
);
220+
178221
// create csv
179222
switch (input.fileFormat) {
180223
case "CSV":
@@ -185,19 +228,23 @@ export const generationsRouter = createTRPCRouter({
185228
"model",
186229
"startTime",
187230
"endTime",
231+
"cost",
188232
"prompt",
189233
"completion",
190234
"metadata",
191235
],
192236
]
193237
.concat(
194-
generations.map((generation) =>
238+
enrichedGenerations.map((generation) =>
195239
[
196240
generation.traceId,
197241
generation.name ?? "",
198242
generation.model ?? "",
199243
generation.startTime.toISOString(),
200244
generation.endTime?.toISOString() ?? "",
245+
generation.cost
246+
? usdFormatter(generation.cost.toNumber())
247+
: "",
201248
JSON.stringify(generation.input),
202249
JSON.stringify(generation.output),
203250
JSON.stringify(generation.metadata),
@@ -210,7 +257,7 @@ export const generationsRouter = createTRPCRouter({
210257
.map((row) => row.join(","))
211258
.join("\n");
212259
case "JSON":
213-
return JSON.stringify(generations);
260+
return JSON.stringify(enrichedGenerations);
214261
case "OPENAI-JSONL":
215262
const inputSchemaOpenAI = z.array(
216263
z.object({
@@ -223,7 +270,7 @@ export const generationsRouter = createTRPCRouter({
223270
});
224271

225272
return (
226-
generations
273+
enrichedGenerations
227274
.map((generation) => ({
228275
parsedInput: inputSchemaOpenAI.safeParse(generation.input),
229276
parsedOutput: outputSchema.safeParse(generation.output),

0 commit comments

Comments
 (0)