feat: 添加缓存 Token 独立展示(cache_creation / cache_read)
从 logs 表 other JSON 字段提取 cache_creation_tokens 和 cache_tokens, 在排名、日志、聚合、详情页分别展示,total_tokens 包含缓存部分。
This commit is contained in:
@@ -11,10 +11,10 @@ import { useI18n } from "@/lib/i18n";
|
|||||||
|
|
||||||
interface AggItem {
|
interface AggItem {
|
||||||
rank: number; name: string; calls: number;
|
rank: number; name: string; calls: number;
|
||||||
prompt_tokens: number; completion_tokens: number; total_tokens: number;
|
prompt_tokens: number; completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number; total_tokens: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "ratio";
|
type SortKey = "total_tokens" | "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "ratio";
|
||||||
|
|
||||||
function RatioTooltip({ text }: { text: string }) {
|
function RatioTooltip({ text }: { text: string }) {
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
@@ -122,6 +122,8 @@ export default function AggregationPage() {
|
|||||||
const sortHeaders: { key: SortKey; label: string }[] = [
|
const sortHeaders: { key: SortKey; label: string }[] = [
|
||||||
{ key: "calls", label: t("th.calls") },
|
{ key: "calls", label: t("th.calls") },
|
||||||
{ key: "prompt_tokens", label: t("th.input") },
|
{ key: "prompt_tokens", label: t("th.input") },
|
||||||
|
{ key: "cache_creation_tokens", label: t("th.cacheCreation") },
|
||||||
|
{ key: "cache_read_tokens", label: t("th.cacheRead") },
|
||||||
{ key: "completion_tokens", label: t("th.output") },
|
{ key: "completion_tokens", label: t("th.output") },
|
||||||
{ key: "total_tokens", label: t("th.totalToken") },
|
{ key: "total_tokens", label: t("th.totalToken") },
|
||||||
];
|
];
|
||||||
@@ -181,7 +183,7 @@ export default function AggregationPage() {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<tr><td colSpan={8} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
<tr><td colSpan={10} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
||||||
) : sorted.map((item, i) => {
|
) : sorted.map((item, i) => {
|
||||||
const pct = totals.tokens > 0 ? (item.total_tokens / totals.tokens * 100) : 0;
|
const pct = totals.tokens > 0 ? (item.total_tokens / totals.tokens * 100) : 0;
|
||||||
const ratio = item.prompt_tokens > 0 ? (item.completion_tokens / item.prompt_tokens) : 0;
|
const ratio = item.prompt_tokens > 0 ? (item.completion_tokens / item.prompt_tokens) : 0;
|
||||||
@@ -191,6 +193,8 @@ export default function AggregationPage() {
|
|||||||
<td className="px-4 py-3 font-medium transition-colors" style={{ color: "var(--text-accent)" }}>{item.name}</td>
|
<td className="px-4 py-3 font-medium transition-colors" style={{ color: "var(--text-accent)" }}>{item.name}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-secondary)" }}>{formatNumber(item.calls)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-secondary)" }}>{formatNumber(item.calls)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.prompt_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.prompt_tokens)}</td>
|
||||||
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_creation_tokens)}</td>
|
||||||
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_read_tokens)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: ratio >= 1 ? "var(--accent)" : "var(--text-muted)" }}>{ratio.toFixed(2)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: ratio >= 1 ? "var(--accent)" : "var(--text-muted)" }}>{ratio.toFixed(2)}</td>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useEffect, useState, useCallback, startTransition } from "react";
|
import { useEffect, useState, useCallback, startTransition } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { motion } from "motion/react";
|
import { motion } from "motion/react";
|
||||||
import { ArrowLeft, Hash, Zap, MessageSquare } from "lucide-react";
|
import { ArrowLeft, Hash, Zap, MessageSquare, DatabaseZap, BookOpen } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { StatsCard } from "@/components/StatsCard";
|
import { StatsCard } from "@/components/StatsCard";
|
||||||
import { TimeRangeSelector } from "@/components/TimeRangeSelector";
|
import { TimeRangeSelector } from "@/components/TimeRangeSelector";
|
||||||
@@ -14,6 +14,7 @@ import { useI18n } from "@/lib/i18n";
|
|||||||
|
|
||||||
interface DetailData {
|
interface DetailData {
|
||||||
calls: number; prompt_tokens: number; completion_tokens: number;
|
calls: number; prompt_tokens: number; completion_tokens: number;
|
||||||
|
cache_creation_tokens: number; cache_read_tokens: number;
|
||||||
total_tokens: number; quota: number; display_name?: string;
|
total_tokens: number; quota: number; display_name?: string;
|
||||||
models?: { name: string; calls: number; total_tokens: number; quota: number }[];
|
models?: { name: string; calls: number; total_tokens: number; quota: number }[];
|
||||||
users?: { name: string; calls: number; total_tokens: number; quota: number }[];
|
users?: { name: string; calls: number; total_tokens: number; quota: number }[];
|
||||||
@@ -74,10 +75,12 @@ export default function DetailPage() {
|
|||||||
<div className="flex h-64 items-center justify-center"><div className="h-6 w-6 animate-spin rounded-full spinner" /></div>
|
<div className="flex h-64 items-center justify-center"><div className="h-6 w-6 animate-spin rounded-full spinner" /></div>
|
||||||
) : data ? (
|
) : data ? (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
|
<div className="grid grid-cols-2 gap-4 md:grid-cols-5">
|
||||||
<StatsCard title={t("dash.totalCalls")} value={data.calls} icon={Hash} delay={0} />
|
<StatsCard title={t("dash.totalCalls")} value={data.calls} icon={Hash} delay={0} />
|
||||||
<StatsCard title={t("th.totalToken")} value={data.total_tokens} format="tokens" icon={Zap} delay={0.05} />
|
<StatsCard title={t("th.totalToken")} value={data.total_tokens} format="tokens" icon={Zap} delay={0.05} />
|
||||||
<StatsCard title={t("th.input")} value={data.prompt_tokens} format="tokens" icon={MessageSquare} delay={0.1} />
|
<StatsCard title={t("th.input")} value={data.prompt_tokens} format="tokens" icon={MessageSquare} delay={0.1} />
|
||||||
|
<StatsCard title={t("th.cacheCreation")} value={data.cache_creation_tokens} format="tokens" icon={DatabaseZap} delay={0.15} />
|
||||||
|
<StatsCard title={t("th.cacheRead")} value={data.cache_read_tokens} format="tokens" icon={BookOpen} delay={0.2} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }} className="glass p-5">
|
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }} className="glass p-5">
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import { useI18n } from "@/lib/i18n";
|
|||||||
interface LogEntry {
|
interface LogEntry {
|
||||||
id: number; created_at: string; display_name: string;
|
id: number; created_at: string; display_name: string;
|
||||||
real_model: string; channel_name: string; prompt_tokens: number;
|
real_model: string; channel_name: string; prompt_tokens: number;
|
||||||
completion_tokens: number; total_tokens: number;
|
completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number;
|
||||||
use_time: number; is_stream: boolean;
|
total_tokens: number; use_time: number; is_stream: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LogsPage() {
|
export default function LogsPage() {
|
||||||
@@ -37,7 +37,7 @@ export default function LogsPage() {
|
|||||||
useEffect(() => { startTransition(() => setPage(1)); }, [getEffectiveRange, filters]);
|
useEffect(() => { startTransition(() => setPage(1)); }, [getEffectiveRange, filters]);
|
||||||
const totalPages = Math.ceil(total / pageSize);
|
const totalPages = Math.ceil(total / pageSize);
|
||||||
|
|
||||||
const headers = [t("th.time"), t("th.user"), t("th.realModel"), t("th.channel"), t("th.input"), t("th.output"), t("th.totalToken"), t("th.latency"), ""];
|
const headers = [t("th.time"), t("th.user"), t("th.realModel"), t("th.channel"), t("th.input"), t("th.cacheCreation"), t("th.cacheRead"), t("th.output"), t("th.totalToken"), t("th.latency"), ""];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -78,7 +78,7 @@ export default function LogsPage() {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<tr><td colSpan={9} className="px-3 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
<tr><td colSpan={11} className="px-3 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
||||||
) : logs.map((log) => (
|
) : logs.map((log) => (
|
||||||
<tr key={log.id} className="row-glow transition-colors" style={{ borderBottom: "1px solid var(--surface-border)" }}>
|
<tr key={log.id} className="row-glow transition-colors" style={{ borderBottom: "1px solid var(--surface-border)" }}>
|
||||||
<td className="px-3 py-2.5 text-xs font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-muted)" }}>{formatDate(log.created_at)}</td>
|
<td className="px-3 py-2.5 text-xs font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-muted)" }}>{formatDate(log.created_at)}</td>
|
||||||
@@ -86,6 +86,8 @@ export default function LogsPage() {
|
|||||||
<td className="px-3 py-2.5 font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-accent)", opacity: 0.7 }}>{log.real_model}</td>
|
<td className="px-3 py-2.5 font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-accent)", opacity: 0.7 }}>{log.real_model}</td>
|
||||||
<td className="px-3 py-2.5" style={{ color: "var(--text-muted)" }}>{log.channel_name}</td>
|
<td className="px-3 py-2.5" style={{ color: "var(--text-muted)" }}>{log.channel_name}</td>
|
||||||
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.prompt_tokens)}</td>
|
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.prompt_tokens)}</td>
|
||||||
|
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.cache_creation_tokens)}</td>
|
||||||
|
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.cache_read_tokens)}</td>
|
||||||
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.completion_tokens)}</td>
|
<td className="px-3 py-2.5 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatNumber(log.completion_tokens)}</td>
|
||||||
<td className="px-3 py-2.5 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-primary)" }}>{formatNumber(log.total_tokens)}</td>
|
<td className="px-3 py-2.5 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-primary)" }}>{formatNumber(log.total_tokens)}</td>
|
||||||
<td className="px-3 py-2.5 text-right tabular-nums text-xs" style={{ color: "var(--text-muted)" }}>{log.use_time}ms</td>
|
<td className="px-3 py-2.5 text-right tabular-nums text-xs" style={{ color: "var(--text-muted)" }}>{log.use_time}ms</td>
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import { useTimeRange } from "@/lib/time-range-context";
|
|||||||
import { useI18n } from "@/lib/i18n";
|
import { useI18n } from "@/lib/i18n";
|
||||||
|
|
||||||
type Tab = "user" | "model" | "channel";
|
type Tab = "user" | "model" | "channel";
|
||||||
type SortKey = "calls" | "prompt_tokens" | "completion_tokens" | "total_tokens";
|
type SortKey = "calls" | "prompt_tokens" | "completion_tokens" | "cache_creation_tokens" | "cache_read_tokens" | "total_tokens";
|
||||||
|
|
||||||
interface RankItem {
|
interface RankItem {
|
||||||
rank: number; name: string; username?: string; id?: number; calls: number;
|
rank: number; name: string; username?: string; id?: number; calls: number;
|
||||||
prompt_tokens: number; completion_tokens: number; total_tokens: number;
|
prompt_tokens: number; completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number; total_tokens: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RankingsPage() {
|
export default function RankingsPage() {
|
||||||
@@ -70,6 +70,8 @@ export default function RankingsPage() {
|
|||||||
{ key: null, label: t("th.name"), align: "left" },
|
{ key: null, label: t("th.name"), align: "left" },
|
||||||
{ key: "calls", label: t("th.calls"), align: "right" },
|
{ key: "calls", label: t("th.calls"), align: "right" },
|
||||||
{ key: "prompt_tokens", label: t("th.input"), align: "right" },
|
{ key: "prompt_tokens", label: t("th.input"), align: "right" },
|
||||||
|
{ key: "cache_creation_tokens", label: t("th.cacheCreation"), align: "right" },
|
||||||
|
{ key: "cache_read_tokens", label: t("th.cacheRead"), align: "right" },
|
||||||
{ key: "completion_tokens", label: t("th.output"), align: "right" },
|
{ key: "completion_tokens", label: t("th.output"), align: "right" },
|
||||||
{ key: "total_tokens", label: t("th.totalToken"), align: "right" },
|
{ key: "total_tokens", label: t("th.totalToken"), align: "right" },
|
||||||
];
|
];
|
||||||
@@ -125,7 +127,7 @@ export default function RankingsPage() {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<tr><td colSpan={6} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
<tr><td colSpan={8} className="px-4 py-16 text-center"><div className="inline-block h-5 w-5 animate-spin rounded-full spinner" /></td></tr>
|
||||||
) : sorted.map((item, i) => (
|
) : sorted.map((item, i) => (
|
||||||
<motion.tr key={item.name} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: i * 0.02 }}
|
<motion.tr key={item.name} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: i * 0.02 }}
|
||||||
className="row-glow transition-colors" style={{ borderBottom: "1px solid var(--surface-border)" }}>
|
className="row-glow transition-colors" style={{ borderBottom: "1px solid var(--surface-border)" }}>
|
||||||
@@ -135,6 +137,8 @@ export default function RankingsPage() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums" style={{ color: "var(--text-secondary)" }}>{formatNumber(item.calls)}</td>
|
<td className="px-4 py-3 text-right tabular-nums" style={{ color: "var(--text-secondary)" }}>{formatNumber(item.calls)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.prompt_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.prompt_tokens)}</td>
|
||||||
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_creation_tokens)}</td>
|
||||||
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.cache_read_tokens)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-[family-name:var(--font-geist-mono)] text-xs" style={{ color: "var(--text-muted)" }}>{formatTokens(item.completion_tokens)}</td>
|
||||||
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
|
<td className="px-4 py-3 text-right tabular-nums font-medium font-[family-name:var(--font-geist-mono)]" style={{ color: "var(--text-primary)" }}>{formatTokens(item.total_tokens)}</td>
|
||||||
</motion.tr>
|
</motion.tr>
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ const translations = {
|
|||||||
"th.calls": "调用次数",
|
"th.calls": "调用次数",
|
||||||
"th.input": "输入",
|
"th.input": "输入",
|
||||||
"th.output": "输出",
|
"th.output": "输出",
|
||||||
|
"th.cacheCreation": "缓存创建",
|
||||||
|
"th.cacheRead": "缓存读取",
|
||||||
"th.totalToken": "总 Token",
|
"th.totalToken": "总 Token",
|
||||||
"th.time": "时间",
|
"th.time": "时间",
|
||||||
"th.realModel": "真实模型",
|
"th.realModel": "真实模型",
|
||||||
@@ -171,6 +173,8 @@ const translations = {
|
|||||||
"th.calls": "Calls",
|
"th.calls": "Calls",
|
||||||
"th.input": "Input",
|
"th.input": "Input",
|
||||||
"th.output": "Output",
|
"th.output": "Output",
|
||||||
|
"th.cacheCreation": "Cache Write",
|
||||||
|
"th.cacheRead": "Cache Read",
|
||||||
"th.totalToken": "Total Token",
|
"th.totalToken": "Total Token",
|
||||||
"th.time": "Time",
|
"th.time": "Time",
|
||||||
"th.realModel": "Real Model",
|
"th.realModel": "Real Model",
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ const REAL_MODEL = `COALESCE(
|
|||||||
THEN other::jsonb->>'upstream_model_name' END,
|
THEN other::jsonb->>'upstream_model_name' END,
|
||||||
model_name)`;
|
model_name)`;
|
||||||
|
|
||||||
|
const CACHE_CREATION = `COALESCE(
|
||||||
|
CASE WHEN other IS NOT NULL AND other != '' AND other::jsonb ? 'cache_creation_tokens'
|
||||||
|
THEN (other::jsonb->>'cache_creation_tokens')::bigint END,
|
||||||
|
0)`;
|
||||||
|
|
||||||
|
const CACHE_READ = `COALESCE(
|
||||||
|
CASE WHEN other IS NOT NULL AND other != '' AND other::jsonb ? 'cache_tokens'
|
||||||
|
THEN (other::jsonb->>'cache_tokens')::bigint END,
|
||||||
|
0)`;
|
||||||
|
|
||||||
// ── 数据时间边界 ────────────────────────────────────────────────
|
// ── 数据时间边界 ────────────────────────────────────────────────
|
||||||
|
|
||||||
export async function getDateRange(): Promise<{ minDate: string; maxDate: string }> {
|
export async function getDateRange(): Promise<{ minDate: string; maxDate: string }> {
|
||||||
@@ -43,6 +53,8 @@ export interface OverviewData {
|
|||||||
total_tokens: number;
|
total_tokens: number;
|
||||||
total_prompt: number;
|
total_prompt: number;
|
||||||
total_completion: number;
|
total_completion: number;
|
||||||
|
total_cache_creation: number;
|
||||||
|
total_cache_read: number;
|
||||||
total_quota: number;
|
total_quota: number;
|
||||||
active_users: number;
|
active_users: number;
|
||||||
active_models: number;
|
active_models: number;
|
||||||
@@ -62,6 +74,8 @@ export async function getOverview(
|
|||||||
COALESCE(SUM(prompt_tokens + completion_tokens), 0)::bigint as total_tokens,
|
COALESCE(SUM(prompt_tokens + completion_tokens), 0)::bigint as total_tokens,
|
||||||
COALESCE(SUM(prompt_tokens), 0)::bigint as total_prompt,
|
COALESCE(SUM(prompt_tokens), 0)::bigint as total_prompt,
|
||||||
COALESCE(SUM(completion_tokens), 0)::bigint as total_completion,
|
COALESCE(SUM(completion_tokens), 0)::bigint as total_completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as total_cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}), 0)::bigint as total_cache_read,
|
||||||
COALESCE(SUM(quota), 0)::bigint as total_quota,
|
COALESCE(SUM(quota), 0)::bigint as total_quota,
|
||||||
COUNT(DISTINCT user_id)::int as active_users,
|
COUNT(DISTINCT user_id)::int as active_users,
|
||||||
COUNT(DISTINCT ${REAL_MODEL})::int as active_models,
|
COUNT(DISTINCT ${REAL_MODEL})::int as active_models,
|
||||||
@@ -72,9 +86,11 @@ export async function getOverview(
|
|||||||
const r = rows[0];
|
const r = rows[0];
|
||||||
return {
|
return {
|
||||||
total_calls: Number(r.total_calls),
|
total_calls: Number(r.total_calls),
|
||||||
total_tokens: Number(r.total_tokens),
|
total_tokens: Number(r.total_tokens) + Number(r.total_cache_creation) + Number(r.total_cache_read),
|
||||||
total_prompt: Number(r.total_prompt),
|
total_prompt: Number(r.total_prompt),
|
||||||
total_completion: Number(r.total_completion),
|
total_completion: Number(r.total_completion),
|
||||||
|
total_cache_creation: Number(r.total_cache_creation),
|
||||||
|
total_cache_read: Number(r.total_cache_read),
|
||||||
total_quota: Number(r.total_quota),
|
total_quota: Number(r.total_quota),
|
||||||
active_users: Number(r.active_users),
|
active_users: Number(r.active_users),
|
||||||
active_models: Number(r.active_models),
|
active_models: Number(r.active_models),
|
||||||
@@ -89,6 +105,8 @@ export interface TrendPoint {
|
|||||||
calls: number;
|
calls: number;
|
||||||
prompt_tokens: number;
|
prompt_tokens: number;
|
||||||
completion_tokens: number;
|
completion_tokens: number;
|
||||||
|
cache_creation_tokens: number;
|
||||||
|
cache_read_tokens: number;
|
||||||
total_tokens: number;
|
total_tokens: number;
|
||||||
quota: number;
|
quota: number;
|
||||||
}
|
}
|
||||||
@@ -112,6 +130,8 @@ export async function getTrends(
|
|||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt_tokens,
|
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt_tokens,
|
||||||
COALESCE(SUM(completion_tokens), 0)::bigint as completion_tokens,
|
COALESCE(SUM(completion_tokens), 0)::bigint as completion_tokens,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation_tokens,
|
||||||
|
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read_tokens,
|
||||||
COALESCE(SUM(quota), 0)::bigint as quota
|
COALESCE(SUM(quota), 0)::bigint as quota
|
||||||
FROM logs WHERE ${where}
|
FROM logs WHERE ${where}
|
||||||
GROUP BY date ORDER BY date`,
|
GROUP BY date ORDER BY date`,
|
||||||
@@ -123,7 +143,9 @@ export async function getTrends(
|
|||||||
calls: Number(r.calls),
|
calls: Number(r.calls),
|
||||||
prompt_tokens: Number(r.prompt_tokens),
|
prompt_tokens: Number(r.prompt_tokens),
|
||||||
completion_tokens: Number(r.completion_tokens),
|
completion_tokens: Number(r.completion_tokens),
|
||||||
total_tokens: Number(r.prompt_tokens) + Number(r.completion_tokens),
|
cache_creation_tokens: Number(r.cache_creation_tokens),
|
||||||
|
cache_read_tokens: Number(r.cache_read_tokens),
|
||||||
|
total_tokens: Number(r.prompt_tokens) + Number(r.completion_tokens) + Number(r.cache_creation_tokens) + Number(r.cache_read_tokens),
|
||||||
quota: Number(r.quota),
|
quota: Number(r.quota),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -137,6 +159,8 @@ export interface RankingItem {
|
|||||||
calls: number;
|
calls: number;
|
||||||
prompt_tokens: number;
|
prompt_tokens: number;
|
||||||
completion_tokens: number;
|
completion_tokens: number;
|
||||||
|
cache_creation_tokens: number;
|
||||||
|
cache_read_tokens: number;
|
||||||
total_tokens: number;
|
total_tokens: number;
|
||||||
quota: number;
|
quota: number;
|
||||||
quota_usd: number;
|
quota_usd: number;
|
||||||
@@ -172,10 +196,12 @@ export async function getUserRanking(
|
|||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota), 0)::bigint as quota
|
COALESCE(SUM(quota), 0)::bigint as quota
|
||||||
FROM logs WHERE ${where}
|
FROM logs WHERE ${where}
|
||||||
GROUP BY user_id, username
|
GROUP BY user_id, username
|
||||||
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) DESC
|
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
|
||||||
LIMIT $${params.length}`,
|
LIMIT $${params.length}`,
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@@ -188,7 +214,9 @@ export async function getUserRanking(
|
|||||||
calls: Number(r.calls),
|
calls: Number(r.calls),
|
||||||
prompt_tokens: Number(r.prompt),
|
prompt_tokens: Number(r.prompt),
|
||||||
completion_tokens: Number(r.completion),
|
completion_tokens: Number(r.completion),
|
||||||
total_tokens: Number(r.prompt) + Number(r.completion),
|
cache_creation_tokens: Number(r.cache_creation),
|
||||||
|
cache_read_tokens: Number(r.cache_read),
|
||||||
|
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
|
||||||
quota: Number(r.quota),
|
quota: Number(r.quota),
|
||||||
quota_usd: Number(r.quota) / 500000,
|
quota_usd: Number(r.quota) / 500000,
|
||||||
}));
|
}));
|
||||||
@@ -208,10 +236,12 @@ export async function getModelRanking(
|
|||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota), 0)::bigint as quota
|
COALESCE(SUM(quota), 0)::bigint as quota
|
||||||
FROM logs WHERE ${where}
|
FROM logs WHERE ${where}
|
||||||
GROUP BY model
|
GROUP BY model
|
||||||
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) DESC
|
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
|
||||||
LIMIT $${params.length}`,
|
LIMIT $${params.length}`,
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@@ -222,7 +252,9 @@ export async function getModelRanking(
|
|||||||
calls: Number(r.calls),
|
calls: Number(r.calls),
|
||||||
prompt_tokens: Number(r.prompt),
|
prompt_tokens: Number(r.prompt),
|
||||||
completion_tokens: Number(r.completion),
|
completion_tokens: Number(r.completion),
|
||||||
total_tokens: Number(r.prompt) + Number(r.completion),
|
cache_creation_tokens: Number(r.cache_creation),
|
||||||
|
cache_read_tokens: Number(r.cache_read),
|
||||||
|
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
|
||||||
quota: Number(r.quota),
|
quota: Number(r.quota),
|
||||||
quota_usd: Number(r.quota) / 500000,
|
quota_usd: Number(r.quota) / 500000,
|
||||||
}));
|
}));
|
||||||
@@ -244,10 +276,12 @@ export async function getChannelRanking(
|
|||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens), 0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
COALESCE(SUM(completion_tokens), 0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota), 0)::bigint as quota
|
COALESCE(SUM(quota), 0)::bigint as quota
|
||||||
FROM logs WHERE ${where}
|
FROM logs WHERE ${where}
|
||||||
GROUP BY channel_id
|
GROUP BY channel_id
|
||||||
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) DESC
|
ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC
|
||||||
LIMIT $${params.length}`,
|
LIMIT $${params.length}`,
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@@ -259,7 +293,9 @@ export async function getChannelRanking(
|
|||||||
calls: Number(r.calls),
|
calls: Number(r.calls),
|
||||||
prompt_tokens: Number(r.prompt),
|
prompt_tokens: Number(r.prompt),
|
||||||
completion_tokens: Number(r.completion),
|
completion_tokens: Number(r.completion),
|
||||||
total_tokens: Number(r.prompt) + Number(r.completion),
|
cache_creation_tokens: Number(r.cache_creation),
|
||||||
|
cache_read_tokens: Number(r.cache_read),
|
||||||
|
total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read),
|
||||||
quota: Number(r.quota),
|
quota: Number(r.quota),
|
||||||
quota_usd: Number(r.quota) / 500000,
|
quota_usd: Number(r.quota) / 500000,
|
||||||
}));
|
}));
|
||||||
@@ -288,6 +324,8 @@ export async function getUserDetail(
|
|||||||
`SELECT COUNT(*)::int as calls,
|
`SELECT COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where} AND username = $${params.length}`,
|
FROM logs WHERE ${where} AND username = $${params.length}`,
|
||||||
params
|
params
|
||||||
@@ -302,6 +340,8 @@ export async function getUserDetail(
|
|||||||
`SELECT ${REAL_MODEL} as model,
|
`SELECT ${REAL_MODEL} as model,
|
||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where2} AND username = $${params2.length}
|
FROM logs WHERE ${where2} AND username = $${params2.length}
|
||||||
GROUP BY model
|
GROUP BY model
|
||||||
@@ -323,12 +363,14 @@ export async function getUserDetail(
|
|||||||
calls: Number(o.calls),
|
calls: Number(o.calls),
|
||||||
prompt_tokens: Number(o.prompt),
|
prompt_tokens: Number(o.prompt),
|
||||||
completion_tokens: Number(o.completion),
|
completion_tokens: Number(o.completion),
|
||||||
total_tokens: Number(o.prompt) + Number(o.completion),
|
cache_creation_tokens: Number(o.cache_creation),
|
||||||
|
cache_read_tokens: Number(o.cache_read),
|
||||||
|
total_tokens: Number(o.prompt) + Number(o.completion) + Number(o.cache_creation) + Number(o.cache_read),
|
||||||
quota: Number(o.quota),
|
quota: Number(o.quota),
|
||||||
models: models.map((m) => ({
|
models: models.map((m) => ({
|
||||||
name: m.model,
|
name: m.model,
|
||||||
calls: Number(m.calls),
|
calls: Number(m.calls),
|
||||||
total_tokens: Number(m.tokens),
|
total_tokens: Number(m.tokens) + Number(m.cache_creation) + Number(m.cache_read),
|
||||||
quota: Number(m.quota),
|
quota: Number(m.quota),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -347,6 +389,8 @@ export async function getModelDetail(
|
|||||||
`SELECT COUNT(*)::int as calls,
|
`SELECT COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where} AND ${REAL_MODEL} = $${params.length}`,
|
FROM logs WHERE ${where} AND ${REAL_MODEL} = $${params.length}`,
|
||||||
params
|
params
|
||||||
@@ -361,6 +405,8 @@ export async function getModelDetail(
|
|||||||
`SELECT user_id, username,
|
`SELECT user_id, username,
|
||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where2} AND ${REAL_MODEL} = $${params2.length}
|
FROM logs WHERE ${where2} AND ${REAL_MODEL} = $${params2.length}
|
||||||
GROUP BY user_id, username
|
GROUP BY user_id, username
|
||||||
@@ -373,12 +419,14 @@ export async function getModelDetail(
|
|||||||
calls: Number(o.calls),
|
calls: Number(o.calls),
|
||||||
prompt_tokens: Number(o.prompt),
|
prompt_tokens: Number(o.prompt),
|
||||||
completion_tokens: Number(o.completion),
|
completion_tokens: Number(o.completion),
|
||||||
total_tokens: Number(o.prompt) + Number(o.completion),
|
cache_creation_tokens: Number(o.cache_creation),
|
||||||
|
cache_read_tokens: Number(o.cache_read),
|
||||||
|
total_tokens: Number(o.prompt) + Number(o.completion) + Number(o.cache_creation) + Number(o.cache_read),
|
||||||
quota: Number(o.quota),
|
quota: Number(o.quota),
|
||||||
users: users.map((u) => ({
|
users: users.map((u) => ({
|
||||||
name: displayNames[u.user_id] || u.username,
|
name: displayNames[u.user_id] || u.username,
|
||||||
calls: Number(u.calls),
|
calls: Number(u.calls),
|
||||||
total_tokens: Number(u.tokens),
|
total_tokens: Number(u.tokens) + Number(u.cache_creation) + Number(u.cache_read),
|
||||||
quota: Number(u.quota),
|
quota: Number(u.quota),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -399,6 +447,8 @@ export async function getChannelDetail(
|
|||||||
`SELECT COUNT(*)::int as calls,
|
`SELECT COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
COALESCE(SUM(prompt_tokens),0)::bigint as prompt,
|
||||||
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
COALESCE(SUM(completion_tokens),0)::bigint as completion,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where} AND channel_id = $${params.length}`,
|
FROM logs WHERE ${where} AND channel_id = $${params.length}`,
|
||||||
params
|
params
|
||||||
@@ -412,6 +462,8 @@ export async function getChannelDetail(
|
|||||||
`SELECT ${REAL_MODEL} as model,
|
`SELECT ${REAL_MODEL} as model,
|
||||||
COUNT(*)::int as calls,
|
COUNT(*)::int as calls,
|
||||||
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
COALESCE(SUM(prompt_tokens + completion_tokens),0)::bigint as tokens,
|
||||||
|
COALESCE(SUM(${CACHE_CREATION}),0)::bigint as cache_creation,
|
||||||
|
COALESCE(SUM(${CACHE_READ}),0)::bigint as cache_read,
|
||||||
COALESCE(SUM(quota),0)::bigint as quota
|
COALESCE(SUM(quota),0)::bigint as quota
|
||||||
FROM logs WHERE ${where2} AND channel_id = $${params2.length}
|
FROM logs WHERE ${where2} AND channel_id = $${params2.length}
|
||||||
GROUP BY model
|
GROUP BY model
|
||||||
@@ -425,12 +477,14 @@ export async function getChannelDetail(
|
|||||||
calls: Number(o.calls),
|
calls: Number(o.calls),
|
||||||
prompt_tokens: Number(o.prompt),
|
prompt_tokens: Number(o.prompt),
|
||||||
completion_tokens: Number(o.completion),
|
completion_tokens: Number(o.completion),
|
||||||
total_tokens: Number(o.prompt) + Number(o.completion),
|
cache_creation_tokens: Number(o.cache_creation),
|
||||||
|
cache_read_tokens: Number(o.cache_read),
|
||||||
|
total_tokens: Number(o.prompt) + Number(o.completion) + Number(o.cache_creation) + Number(o.cache_read),
|
||||||
quota: Number(o.quota),
|
quota: Number(o.quota),
|
||||||
models: models.map((m) => ({
|
models: models.map((m) => ({
|
||||||
name: m.model,
|
name: m.model,
|
||||||
calls: Number(m.calls),
|
calls: Number(m.calls),
|
||||||
total_tokens: Number(m.tokens),
|
total_tokens: Number(m.tokens) + Number(m.cache_creation) + Number(m.cache_read),
|
||||||
quota: Number(m.quota),
|
quota: Number(m.quota),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -449,6 +503,8 @@ export interface LogEntry {
|
|||||||
channel_id: number;
|
channel_id: number;
|
||||||
prompt_tokens: number;
|
prompt_tokens: number;
|
||||||
completion_tokens: number;
|
completion_tokens: number;
|
||||||
|
cache_creation_tokens: number;
|
||||||
|
cache_read_tokens: number;
|
||||||
total_tokens: number;
|
total_tokens: number;
|
||||||
quota: number;
|
quota: number;
|
||||||
quota_usd: number;
|
quota_usd: number;
|
||||||
@@ -513,6 +569,8 @@ export async function getLogs(options: {
|
|||||||
`SELECT id, created_at, user_id, username, model_name,
|
`SELECT id, created_at, user_id, username, model_name,
|
||||||
${REAL_MODEL} as real_model,
|
${REAL_MODEL} as real_model,
|
||||||
channel_id, prompt_tokens, completion_tokens, quota,
|
channel_id, prompt_tokens, completion_tokens, quota,
|
||||||
|
${CACHE_CREATION} as cache_creation,
|
||||||
|
${CACHE_READ} as cache_read,
|
||||||
use_time, is_stream, token_name
|
use_time, is_stream, token_name
|
||||||
FROM logs WHERE ${where}
|
FROM logs WHERE ${where}
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
@@ -535,7 +593,9 @@ export async function getLogs(options: {
|
|||||||
channel_id: Number(r.channel_id),
|
channel_id: Number(r.channel_id),
|
||||||
prompt_tokens: Number(r.prompt_tokens),
|
prompt_tokens: Number(r.prompt_tokens),
|
||||||
completion_tokens: Number(r.completion_tokens),
|
completion_tokens: Number(r.completion_tokens),
|
||||||
total_tokens: Number(r.prompt_tokens) + Number(r.completion_tokens),
|
cache_creation_tokens: Number(r.cache_creation),
|
||||||
|
cache_read_tokens: Number(r.cache_read),
|
||||||
|
total_tokens: Number(r.prompt_tokens) + Number(r.completion_tokens) + Number(r.cache_creation) + Number(r.cache_read),
|
||||||
quota: Number(r.quota),
|
quota: Number(r.quota),
|
||||||
quota_usd: Number(r.quota) / 500000,
|
quota_usd: Number(r.quota) / 500000,
|
||||||
use_time: Number(r.use_time),
|
use_time: Number(r.use_time),
|
||||||
|
|||||||
Reference in New Issue
Block a user