Преглед изворни кода

feat(usage-logs): add endpoint filtering UI and highlight non-billing rows

ding113 пре 4 месеци
родитељ
комит
b5f7e01c0a

+ 4 - 0
messages/en/dashboard.json

@@ -56,6 +56,7 @@
       "user": "User",
       "provider": "Provider",
       "model": "Model",
+      "endpoint": "Endpoint",
       "status": "Status",
       "timeRange": "Time Range",
       "startTime": "Start Time",
@@ -65,6 +66,7 @@
       "selectUserFirst": "Please select a user first",
       "allProviders": "All Providers",
       "allModels": "All Models",
+      "allEndpoints": "All Endpoints",
       "allStatusCodes": "All Status Codes",
       "apiKey": "API Key",
       "statusCode": "Status Code",
@@ -77,6 +79,7 @@
       "key": "Key",
       "provider": "Provider",
       "model": "Model",
+      "endpoint": "Endpoint",
       "inputTokens": "Input",
       "outputTokens": "Output",
       "cacheWrite": "Cache Write",
@@ -102,6 +105,7 @@
       "prevPage": "Previous Page",
       "nextPage": "Next Page",
       "blocked": "Blocked",
+      "nonBilling": "Non-Billing",
       "times": "times"
     },
     "actions": {

+ 4 - 0
messages/ja/dashboard.json

@@ -56,6 +56,8 @@
       "user": "ユーザー",
       "provider": "プロバイダー",
       "model": "モデル",
+      "endpoint": "エンドポイント",
+      "endpoint": "エンドポイント",
       "status": "ステータス",
       "timeRange": "時間範囲",
       "startTime": "開始時間",
@@ -65,6 +67,7 @@
       "selectUserFirst": "まずユーザーを選択してください",
       "allProviders": "すべてのプロバイダー",
       "allModels": "すべてのモデル",
+      "allEndpoints": "すべてのエンドポイント",
       "allStatusCodes": "すべてのステータスコード",
       "apiKey": "API キー",
       "statusCode": "ステータスコード",
@@ -102,6 +105,7 @@
       "prevPage": "前へ",
       "nextPage": "次へ",
       "blocked": "ブロック済み",
+      "nonBilling": "非課金",
       "times": "回"
     },
     "actions": {

+ 4 - 0
messages/ru/dashboard.json

@@ -56,6 +56,8 @@
       "user": "Пользователь",
       "provider": "Поставщик",
       "model": "Модель",
+      "endpoint": "Эндпоинт",
+      "endpoint": "Эндпоинт",
       "status": "Статус",
       "timeRange": "Диапазон времени",
       "startTime": "Время начала",
@@ -65,6 +67,7 @@
       "selectUserFirst": "Сначала выберите пользователя",
       "allProviders": "Все поставщики",
       "allModels": "Все модели",
+      "allEndpoints": "Все эндпоинты",
       "allStatusCodes": "Все коды состояния",
       "apiKey": "API ключ",
       "statusCode": "Код состояния",
@@ -102,6 +105,7 @@
       "prevPage": "Предыдущая",
       "nextPage": "Следующая",
       "blocked": "Заблокировано",
+      "nonBilling": "Не тарифицируется",
       "times": "раз"
     },
     "actions": {

+ 4 - 0
messages/zh-CN/dashboard.json

@@ -56,6 +56,8 @@
       "user": "用户",
       "provider": "供应商",
       "model": "模型",
+      "endpoint": "端点",
+      "endpoint": "端点",
       "status": "状态",
       "timeRange": "时间范围",
       "startTime": "开始时间",
@@ -65,6 +67,7 @@
       "selectUserFirst": "请先选择用户",
       "allProviders": "全部供应商",
       "allModels": "全部模型",
+      "allEndpoints": "全部端点",
       "allStatusCodes": "全部状态码",
       "apiKey": "API 密钥",
       "statusCode": "状态码",
@@ -102,6 +105,7 @@
       "prevPage": "上一页",
       "nextPage": "下一页",
       "blocked": "被拦截",
+      "nonBilling": "非计费",
       "times": "次"
     },
     "actions": {

+ 4 - 0
messages/zh-TW/dashboard.json

@@ -56,6 +56,8 @@
       "user": "使用者",
       "provider": "供應商",
       "model": "模型",
+      "endpoint": "端點",
+      "endpoint": "端點",
       "status": "狀態",
       "timeRange": "時間範圍",
       "startTime": "開始時間",
@@ -65,6 +67,7 @@
       "selectUserFirst": "請先選擇使用者",
       "allProviders": "所有供應商",
       "allModels": "所有模型",
+      "allEndpoints": "所有端點",
       "allStatusCodes": "所有狀態碼",
       "apiKey": "API 金鑰",
       "statusCode": "狀態碼",
@@ -102,6 +105,7 @@
       "prevPage": "上一頁",
       "nextPage": "下一頁",
       "blocked": "已攔截",
+      "nonBilling": "非計費",
       "times": "次"
     },
     "actions": {

+ 73 - 11
src/app/[locale]/dashboard/logs/_components/usage-logs-filters.tsx

@@ -13,7 +13,7 @@ import {
   SelectTrigger,
   SelectValue,
 } from "@/components/ui/select";
-import { getModelList, getStatusCodeList } from "@/actions/usage-logs";
+import { getEndpointList, getModelList, getStatusCodeList } from "@/actions/usage-logs";
 import { getKeys } from "@/actions/keys";
 import type { UserDisplay } from "@/types/user";
 import type { ProviderDisplay } from "@/types/provider";
@@ -55,6 +55,7 @@ interface UsageLogsFiltersProps {
     endDate?: Date;
     statusCode?: number;
     model?: string;
+    endpoint?: string;
   };
   onChange: (filters: UsageLogsFiltersProps["filters"]) => void;
   onReset: () => void;
@@ -72,23 +73,49 @@ export function UsageLogsFilters({
   const t = useTranslations("dashboard");
   const [models, setModels] = useState<string[]>([]);
   const [statusCodes, setStatusCodes] = useState<number[]>([]);
+  const [endpoints, setEndpoints] = useState<string[]>([]);
+  const [isEndpointLoading, setIsEndpointLoading] = useState(false);
+  const [endpointError, setEndpointError] = useState<string | null>(null);
   const [keys, setKeys] = useState<Key[]>(initialKeys);
   const [localFilters, setLocalFilters] = useState(filters);
 
   // 加载筛选器选项
   useEffect(() => {
     const loadOptions = async () => {
-      const [modelsResult, codesResult] = await Promise.all([
-        getModelList(),
-        getStatusCodeList(),
-      ]);
+      setIsEndpointLoading(true);
+      setEndpointError(null);
 
-      if (modelsResult.ok && modelsResult.data) {
-        setModels(modelsResult.data);
-      }
+      try {
+        const [modelsResult, codesResult, endpointsResult] = await Promise.all([
+          getModelList(),
+          getStatusCodeList(),
+          getEndpointList(),
+        ]);
+
+        if (modelsResult.ok && modelsResult.data) {
+          setModels(modelsResult.data);
+        }
+
+        if (codesResult.ok && codesResult.data) {
+          setStatusCodes(codesResult.data);
+        }
 
-      if (codesResult.ok && codesResult.data) {
-        setStatusCodes(codesResult.data);
+        if (endpointsResult.ok && endpointsResult.data) {
+          setEndpoints(endpointsResult.data);
+        } else {
+          setEndpoints([]);
+          setEndpointError(
+            !endpointsResult.ok && "error" in endpointsResult
+              ? endpointsResult.error
+              : t("logs.error.loadFailed")
+          );
+        }
+      } catch (error) {
+        console.error("Failed to load filter options:", error);
+        setEndpoints([]);
+        setEndpointError(t("logs.error.loadFailed"));
+      } finally {
+        setIsEndpointLoading(false);
       }
 
       // 管理员:如果选择了用户,加载该用户的 keys
@@ -102,7 +129,7 @@ export function UsageLogsFilters({
     };
 
     loadOptions();
-  }, [isAdmin, localFilters.userId]);
+  }, [isAdmin, localFilters.userId, t]);
 
   // 处理用户选择变更
   const handleUserChange = async (userId: string) => {
@@ -260,6 +287,41 @@ export function UsageLogsFilters({
           </Select>
         </div>
 
+        {/* Endpoint 选择 */}
+        <div className="space-y-2">
+          <Label>{t("logs.filters.endpoint")}</Label>
+          <Select
+            value={localFilters.endpoint || ""}
+            onValueChange={(value: string) =>
+              setLocalFilters({ ...localFilters, endpoint: value || undefined })
+            }
+            disabled={isEndpointLoading}
+          >
+            <SelectTrigger>
+              <SelectValue
+                placeholder={
+                  endpointError
+                    ? endpointError
+                    : isEndpointLoading
+                      ? t("logs.stats.loading")
+                      : t("logs.filters.allEndpoints")
+                }
+              />
+            </SelectTrigger>
+            <SelectContent>
+              <SelectItem value="">{t("logs.filters.allEndpoints")}</SelectItem>
+              {endpoints.map((endpoint) => (
+                <SelectItem key={endpoint} value={endpoint}>
+                  {endpoint}
+                </SelectItem>
+              ))}
+            </SelectContent>
+          </Select>
+          {endpointError && (
+            <p className="text-xs text-destructive">{endpointError}</p>
+          )}
+        </div>
+
         {/* 状态码选择 */}
         <div className="space-y-2">
           <Label>{t("logs.filters.statusCode")}</Label>

+ 147 - 123
src/app/[locale]/dashboard/logs/_components/usage-logs-table.tsx

@@ -25,7 +25,9 @@ import { formatProviderSummary } from "@/lib/utils/provider-chain-formatter";
 import { ModelDisplayWithRedirect } from "./model-display-with-redirect";
 import type { CurrencyCode } from "@/lib/utils/currency";
 import { formatCurrency } from "@/lib/utils/currency";
-import { formatTokenAmount } from "@/lib/utils";
+import { cn, formatTokenAmount } from "@/lib/utils";
+
+const NON_BILLING_ENDPOINT = "/v1/messages/count_tokens";
 
 /**
  * 格式化请求耗时
@@ -71,11 +73,12 @@ export function UsageLogsTable({
 
   return (
     <div className="space-y-4">
-      <div className="rounded-md border">
+      <div className="rounded-md border overflow-x-auto">
         <Table>
           <TableHeader>
             <TableRow>
               <TableHead>{t("logs.columns.time")}</TableHead>
+              <TableHead>{t("logs.columns.endpoint")}</TableHead>
               <TableHead>{t("logs.columns.user")}</TableHead>
               <TableHead>{t("logs.columns.key")}</TableHead>
               <TableHead>{t("logs.columns.provider")}</TableHead>
@@ -92,135 +95,156 @@ export function UsageLogsTable({
           <TableBody>
             {logs.length === 0 ? (
               <TableRow>
-                <TableCell colSpan={12} className="text-center text-muted-foreground">
+                <TableCell colSpan={13} className="text-center text-muted-foreground">
                   {t("logs.table.noData")}
                 </TableCell>
               </TableRow>
             ) : (
-              logs.map((log) => (
-                <TableRow
-                  key={log.id}
-                  className={newLogIds?.has(log.id) ? 'animate-highlight-flash' : ''}
-                >
-                  <TableCell className="font-mono text-xs">
-                    <RelativeTime date={log.createdAt} fallback="-" />
-                  </TableCell>
-                  <TableCell>{log.userName}</TableCell>
-                  <TableCell className="font-mono text-xs">{log.keyName}</TableCell>
-                  <TableCell className="text-left">
-                    {log.blockedBy ? (
-                      // 被拦截的请求显示拦截标记
-                      <span className="inline-flex items-center gap-1 rounded-md bg-orange-100 dark:bg-orange-950 px-2 py-1 text-xs font-medium text-orange-700 dark:text-orange-300">
-                        <span className="h-1.5 w-1.5 rounded-full bg-orange-600 dark:bg-orange-400" />
-                        {t("logs.table.blocked")}
-                      </span>
-                    ) : (
-                      <div className="flex items-start gap-2">
-                        <div className="flex flex-col items-start gap-0.5 min-w-0 flex-1">
-                          {log.providerChain && log.providerChain.length > 0 ? (
-                            <>
-                              <div className="w-full">
-                                <ProviderChainPopover
-                                  chain={log.providerChain}
-                                  finalProvider={
-                                    log.providerChain[log.providerChain.length - 1].name || log.providerName || tChain("circuit.unknown")
-                                  }
-                                />
-                              </div>
-                              {/* 摘要文字(第二行显示,左对齐) */}
-                              {formatProviderSummary(log.providerChain, tChain) && (
+              logs.map((log) => {
+                const isNonBilling = log.endpoint === NON_BILLING_ENDPOINT;
+
+                return (
+                  <TableRow
+                    key={log.id}
+                    className={cn(
+                      newLogIds?.has(log.id) ? "animate-highlight-flash" : "",
+                      isNonBilling ? "bg-muted/60 text-muted-foreground dark:bg-muted/20" : ""
+                    )}
+                    aria-label={isNonBilling ? t("logs.table.nonBilling") : undefined}
+                  >
+                    <TableCell className="font-mono text-xs">
+                      <RelativeTime date={log.createdAt} fallback="-" />
+                    </TableCell>
+                    <TableCell className="min-w-[200px] align-top">
+                      <div className="flex flex-col gap-1">
+                        <span className="font-mono text-xs break-words">{log.endpoint ?? "-"}</span>
+                        {isNonBilling && (
+                          <Badge
+                            variant="outline"
+                            className="w-fit border-orange-200 bg-orange-50 text-orange-700 dark:border-orange-900/60 dark:bg-orange-950/30 dark:text-orange-200"
+                          >
+                            {t("logs.table.nonBilling")}
+                          </Badge>
+                        )}
+                      </div>
+                    </TableCell>
+                    <TableCell>{log.userName}</TableCell>
+                    <TableCell className="font-mono text-xs">{log.keyName}</TableCell>
+                    <TableCell className="text-left">
+                      {log.blockedBy ? (
+                        // 被拦截的请求显示拦截标记
+                        <span className="inline-flex items-center gap-1 rounded-md bg-orange-100 dark:bg-orange-950 px-2 py-1 text-xs font-medium text-orange-700 dark:text-orange-300">
+                          <span className="h-1.5 w-1.5 rounded-full bg-orange-600 dark:bg-orange-400" />
+                          {t("logs.table.blocked")}
+                        </span>
+                      ) : (
+                        <div className="flex items-start gap-2">
+                          <div className="flex flex-col items-start gap-0.5 min-w-0 flex-1">
+                            {log.providerChain && log.providerChain.length > 0 ? (
+                              <>
                                 <div className="w-full">
-                                  <TooltipProvider>
-                                    <Tooltip delayDuration={300}>
-                                      <TooltipTrigger asChild>
-                                        <span className="text-xs text-muted-foreground cursor-help truncate max-w-[200px] block text-left">
-                                          {formatProviderSummary(log.providerChain, tChain)}
-                                        </span>
-                                      </TooltipTrigger>
-                                      <TooltipContent side="bottom" align="start" className="max-w-[500px]">
-                                        <p className="text-xs whitespace-normal break-words font-mono">
-                                          {formatProviderSummary(log.providerChain, tChain)}
-                                        </p>
-                                      </TooltipContent>
-                                    </Tooltip>
-                                  </TooltipProvider>
+                                  <ProviderChainPopover
+                                    chain={log.providerChain}
+                                    finalProvider={
+                                      log.providerChain[log.providerChain.length - 1].name || log.providerName || tChain("circuit.unknown")
+                                    }
+                                  />
                                 </div>
-                              )}
-                            </>
-                          ) : (
-                            <span>{log.providerName || "-"}</span>
-                          )}
-                        </div>
-                        {/* 显示供应商倍率 Badge(不为 1.0 时) */}
-                        {(() => {
-                          // 从决策链中找到最后一个成功的供应商,使用它的倍率
-                          const successfulProvider = log.providerChain && log.providerChain.length > 0
-                            ? [...log.providerChain]
-                                .reverse()
-                                .find(item =>
-                                  item.reason === 'request_success' ||
-                                  item.reason === 'retry_success'
-                                )
-                            : null;
+                                {/* 摘要文字(第二行显示,左对齐) */}
+                                {formatProviderSummary(log.providerChain, tChain) && (
+                                  <div className="w-full">
+                                    <TooltipProvider>
+                                      <Tooltip delayDuration={300}>
+                                        <TooltipTrigger asChild>
+                                          <span className="text-xs text-muted-foreground cursor-help truncate max-w-[200px] block text-left">
+                                            {formatProviderSummary(log.providerChain, tChain)}
+                                          </span>
+                                        </TooltipTrigger>
+                                        <TooltipContent side="bottom" align="start" className="max-w-[500px]">
+                                          <p className="text-xs whitespace-normal break-words font-mono">
+                                            {formatProviderSummary(log.providerChain, tChain)}
+                                          </p>
+                                        </TooltipContent>
+                                      </Tooltip>
+                                    </TooltipProvider>
+                                  </div>
+                                )}
+                              </>
+                            ) : (
+                              <span>{log.providerName || "-"}</span>
+                            )}
+                          </div>
+                          {/* 显示供应商倍率 Badge(不为 1.0 时) */}
+                          {(() => {
+                            // 从决策链中找到最后一个成功的供应商,使用它的倍率
+                            const successfulProvider = log.providerChain && log.providerChain.length > 0
+                              ? [...log.providerChain]
+                                  .reverse()
+                                  .find(item =>
+                                    item.reason === 'request_success' ||
+                                    item.reason === 'retry_success'
+                                  )
+                              : null;
 
-                          const actualCostMultiplier = successfulProvider?.costMultiplier ?? log.costMultiplier;
+                            const actualCostMultiplier = successfulProvider?.costMultiplier ?? log.costMultiplier;
 
-                          return actualCostMultiplier && parseFloat(String(actualCostMultiplier)) !== 1.0 ? (
-                            <Badge
-                              variant="outline"
-                              className={
-                                parseFloat(String(actualCostMultiplier)) > 1.0
-                                  ? "text-xs bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950/30 dark:text-orange-300 dark:border-orange-800 shrink-0"
-                                  : "text-xs bg-green-50 text-green-700 border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-800 shrink-0"
-                              }
-                            >
-                              ×{parseFloat(String(actualCostMultiplier)).toFixed(2)}
-                            </Badge>
-                          ) : null;
-                        })()}
-                      </div>
-                    )}
-                  </TableCell>
-                  <TableCell className="font-mono text-xs">
-                    <ModelDisplayWithRedirect
-                      originalModel={log.originalModel}
-                      currentModel={log.model}
-                    />
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {formatTokenAmount(log.inputTokens)}
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {formatTokenAmount(log.outputTokens)}
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {formatTokenAmount(log.cacheCreationInputTokens)}
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {formatTokenAmount(log.cacheReadInputTokens)}
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {log.costUsd ? formatCurrency(log.costUsd, currencyCode, 6) : "-"}
-                  </TableCell>
-                  <TableCell className="text-right font-mono text-xs">
-                    {formatDuration(log.durationMs)}
-                  </TableCell>
-                  <TableCell>
-                    <ErrorDetailsDialog
-                      statusCode={log.statusCode}
-                      errorMessage={log.errorMessage}
-                      providerChain={log.providerChain}
-                      sessionId={log.sessionId}
-                      blockedBy={log.blockedBy}
-                      blockedReason={log.blockedReason}
-                      originalModel={log.originalModel}
-                      currentModel={log.model}
-                      userAgent={log.userAgent}
-                      messagesCount={log.messagesCount}
-                    />
-                  </TableCell>
-                </TableRow>
-              ))
+                            return actualCostMultiplier && parseFloat(String(actualCostMultiplier)) !== 1.0 ? (
+                              <Badge
+                                variant="outline"
+                                className={
+                                  parseFloat(String(actualCostMultiplier)) > 1.0
+                                    ? "text-xs bg-orange-50 text-orange-700 border-orange-200 dark:bg-orange-950/30 dark:text-orange-300 dark:border-orange-800 shrink-0"
+                                    : "text-xs bg-green-50 text-green-700 border-green-200 dark:bg-green-950/30 dark:text-green-300 dark:border-green-800 shrink-0"
+                                }
+                              >
+                                ×{parseFloat(String(actualCostMultiplier)).toFixed(2)}
+                              </Badge>
+                            ) : null;
+                          })()}
+                        </div>
+                      )}
+                    </TableCell>
+                    <TableCell className="font-mono text-xs">
+                      <ModelDisplayWithRedirect
+                        originalModel={log.originalModel}
+                        currentModel={log.model}
+                      />
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {formatTokenAmount(log.inputTokens)}
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {formatTokenAmount(log.outputTokens)}
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {formatTokenAmount(log.cacheCreationInputTokens)}
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {formatTokenAmount(log.cacheReadInputTokens)}
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {isNonBilling ? "-" : log.costUsd ? formatCurrency(log.costUsd, currencyCode, 6) : "-"}
+                    </TableCell>
+                    <TableCell className="text-right font-mono text-xs">
+                      {formatDuration(log.durationMs)}
+                    </TableCell>
+                    <TableCell>
+                      <ErrorDetailsDialog
+                        statusCode={log.statusCode}
+                        errorMessage={log.errorMessage}
+                        providerChain={log.providerChain}
+                        sessionId={log.sessionId}
+                        blockedBy={log.blockedBy}
+                        blockedReason={log.blockedReason}
+                        originalModel={log.originalModel}
+                        currentModel={log.model}
+                        userAgent={log.userAgent}
+                        messagesCount={log.messagesCount}
+                      />
+                    </TableCell>
+                  </TableRow>
+                );
+              })
             )}
           </TableBody>
         </Table>

+ 3 - 0
src/app/[locale]/dashboard/logs/_components/usage-logs-view.tsx

@@ -70,6 +70,7 @@ export function UsageLogsView({
     endDate?: Date;
     statusCode?: number;
     model?: string;
+    endpoint?: string;
     page: number;
   } = {
     userId: searchParams.userId ? parseInt(searchParams.userId as string) : undefined,
@@ -79,6 +80,7 @@ export function UsageLogsView({
     endDate: searchParams.endDate ? new Date(searchParams.endDate as string) : undefined,
     statusCode: searchParams.statusCode ? parseInt(searchParams.statusCode as string) : undefined,
     model: searchParams.model as string | undefined,
+    endpoint: searchParams.endpoint as string | undefined,
     page: searchParams.page ? parseInt(searchParams.page as string) : 1,
   };
 
@@ -173,6 +175,7 @@ export function UsageLogsView({
     if (newFilters.endDate) query.set("endDate", formatDateTimeLocal(newFilters.endDate));
     if (newFilters.statusCode) query.set("statusCode", newFilters.statusCode.toString());
     if (newFilters.model) query.set("model", newFilters.model);
+    if (newFilters.endpoint) query.set("endpoint", newFilters.endpoint);
 
     router.push(`/dashboard/logs?${query.toString()}`);
   };

+ 9 - 0
src/repository/usage-logs.ts

@@ -14,6 +14,7 @@ export interface UsageLogFilters {
   endDate?: Date;
   statusCode?: number;
   model?: string;
+  endpoint?: string;
   page?: number;
   pageSize?: number;
 }
@@ -27,6 +28,7 @@ export interface UsageLogRow {
   providerName: string | null; // 改为可选:被拦截的请求没有 provider
   model: string | null;
   originalModel: string | null; // 原始模型(重定向前)
+  endpoint: string | null;
   statusCode: number | null;
   inputTokens: number | null;
   outputTokens: number | null;
@@ -72,6 +74,7 @@ export async function findUsageLogsWithDetails(filters: UsageLogFilters): Promis
     endDate,
     statusCode,
     model,
+    endpoint,
     page = 1,
     pageSize = 50,
   } = filters;
@@ -156,6 +159,10 @@ export async function findUsageLogsWithDetails(filters: UsageLogFilters): Promis
     conditions.push(eq(messageRequest.model, model));
   }
 
+  if (endpoint) {
+    conditions.push(eq(messageRequest.endpoint, endpoint));
+  }
+
   // 查询总数和统计数据
   const [summaryResult] = await db
     .select({
@@ -189,6 +196,7 @@ export async function findUsageLogsWithDetails(filters: UsageLogFilters): Promis
       providerName: providers.name, // 被拦截的请求为 null
       model: messageRequest.model,
       originalModel: messageRequest.originalModel, // 原始模型(重定向前)
+      endpoint: messageRequest.endpoint,
       statusCode: messageRequest.statusCode,
       inputTokens: messageRequest.inputTokens,
       outputTokens: messageRequest.outputTokens,
@@ -225,6 +233,7 @@ export async function findUsageLogsWithDetails(filters: UsageLogFilters): Promis
       totalTokens: totalRowTokens,
       costUsd: row.costUsd?.toString() ?? null,
       providerChain: row.providerChain as ProviderChainItem[] | null,
+      endpoint: row.endpoint,
     };
   });