feat: track upstream request ID and prevent response header override

When proxying through another new-api instance, the upstream
X-Oneapi-Request-Id was overwriting the local one in client responses.
This adds a new `upstream_request_id` field to the logs table, captures
the upstream ID during relay, and filters it from being copied back to
the client. Frontend gains search/filter and detail display support.
This commit is contained in:
CaIon 2026-05-12 21:53:37 +08:00
parent 428e3d91f2
commit aa56667b8f
No known key found for this signature in database
GPG Key ID: 0CFA613529A9921D
20 changed files with 112 additions and 32 deletions

View File

@ -179,7 +179,8 @@ var GeminiSafetySetting string
var CohereSafetySetting string
const (
RequestIdKey = "X-Oneapi-Request-Id"
RequestIdKey = "X-Oneapi-Request-Id"
UpstreamRequestIdKey = "X-Upstream-Request-Id"
)
const (

View File

@ -21,7 +21,8 @@ func GetAllLogs(c *gin.Context) {
channel, _ := strconv.Atoi(c.Query("channel"))
group := c.Query("group")
requestId := c.Query("request_id")
logs, total, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), channel, group, requestId)
upstreamRequestId := c.Query("upstream_request_id")
logs, total, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), channel, group, requestId, upstreamRequestId)
if err != nil {
common.ApiError(c, err)
return
@ -42,7 +43,8 @@ func GetUserLogs(c *gin.Context) {
modelName := c.Query("model_name")
group := c.Query("group")
requestId := c.Query("request_id")
logs, total, err := model.GetUserLogs(userId, logType, startTimestamp, endTimestamp, modelName, tokenName, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), group, requestId)
upstreamRequestId := c.Query("upstream_request_id")
logs, total, err := model.GetUserLogs(userId, logType, startTimestamp, endTimestamp, modelName, tokenName, pageInfo.GetStartIdx(), pageInfo.GetPageSize(), group, requestId, upstreamRequestId)
if err != nil {
common.ApiError(c, err)
return

View File

@ -35,8 +35,9 @@ type Log struct {
TokenId int `json:"token_id" gorm:"default:0;index"`
Group string `json:"group" gorm:"index"`
Ip string `json:"ip" gorm:"index;default:''"`
RequestId string `json:"request_id,omitempty" gorm:"type:varchar(64);index:idx_logs_request_id;default:''"`
Other string `json:"other"`
RequestId string `json:"request_id,omitempty" gorm:"type:varchar(64);index:idx_logs_request_id;default:''"`
UpstreamRequestId string `json:"upstream_request_id,omitempty" gorm:"type:varchar(128);index:idx_logs_upstream_request_id;default:''"`
Other string `json:"other"`
}
// don't use iota, avoid change log type value
@ -147,6 +148,7 @@ func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string,
logger.LogInfo(c, fmt.Sprintf("record error log: userId=%d, channelId=%d, modelName=%s, tokenName=%s, content=%s", userId, channelId, modelName, tokenName, content))
username := c.GetString("username")
requestId := c.GetString(common.RequestIdKey)
upstreamRequestId := c.GetString(common.UpstreamRequestIdKey)
otherStr := common.MapToJsonStr(other)
// 判断是否需要记录 IP
needRecordIp := false
@ -177,8 +179,9 @@ func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string,
}
return ""
}(),
RequestId: requestId,
Other: otherStr,
RequestId: requestId,
UpstreamRequestId: upstreamRequestId,
Other: otherStr,
}
err := LOG_DB.Create(log).Error
if err != nil {
@ -208,6 +211,7 @@ func RecordConsumeLog(c *gin.Context, userId int, params RecordConsumeLogParams)
logger.LogInfo(c, fmt.Sprintf("record consume log: userId=%d, params=%s", userId, common.GetJsonString(params)))
username := c.GetString("username")
requestId := c.GetString(common.RequestIdKey)
upstreamRequestId := c.GetString(common.UpstreamRequestIdKey)
otherStr := common.MapToJsonStr(params.Other)
// 判断是否需要记录 IP
needRecordIp := false
@ -238,8 +242,9 @@ func RecordConsumeLog(c *gin.Context, userId int, params RecordConsumeLogParams)
}
return ""
}(),
RequestId: requestId,
Other: otherStr,
RequestId: requestId,
UpstreamRequestId: upstreamRequestId,
Other: otherStr,
}
err := LOG_DB.Create(log).Error
if err != nil {
@ -295,7 +300,7 @@ func RecordTaskBillingLog(params RecordTaskBillingLogParams) {
}
}
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int, group string, requestId string) (logs []*Log, total int64, err error) {
func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int, group string, requestId string, upstreamRequestId string) (logs []*Log, total int64, err error) {
var tx *gorm.DB
if logType == LogTypeUnknown {
tx = LOG_DB
@ -315,6 +320,9 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
if requestId != "" {
tx = tx.Where("logs.request_id = ?", requestId)
}
if upstreamRequestId != "" {
tx = tx.Where("logs.upstream_request_id = ?", upstreamRequestId)
}
if startTimestamp != 0 {
tx = tx.Where("logs.created_at >= ?", startTimestamp)
}
@ -381,7 +389,7 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
const logSearchCountLimit = 10000
func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int, group string, requestId string) (logs []*Log, total int64, err error) {
func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int, group string, requestId string, upstreamRequestId string) (logs []*Log, total int64, err error) {
var tx *gorm.DB
if logType == LogTypeUnknown {
tx = LOG_DB.Where("logs.user_id = ?", userId)
@ -402,6 +410,9 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int
if requestId != "" {
tx = tx.Where("logs.request_id = ?", requestId)
}
if upstreamRequestId != "" {
tx = tx.Where("logs.upstream_request_id = ?", upstreamRequestId)
}
if startTimestamp != 0 {
tx = tx.Where("logs.created_at >= ?", startTimestamp)
}

View File

@ -524,6 +524,10 @@ func doRequest(c *gin.Context, req *http.Request, info *common.RelayInfo) (*http
return nil, errors.New("resp is nil")
}
if upID := resp.Header.Get(common2.RequestIdKey); upID != "" {
c.Set(common2.UpstreamRequestIdKey, upID)
}
_ = req.Body.Close()
_ = c.Request.Body.Close()
return resp, nil

View File

@ -11,6 +11,7 @@ import (
"github.com/QuantumNous/new-api/dto"
relaycommon "github.com/QuantumNous/new-api/relay/common"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/types"
"github.com/gin-gonic/gin"
)
@ -184,6 +185,9 @@ func handleChatCompletionResponse(c *gin.Context, resp *http.Response, info *rel
// Set response headers
for key, values := range resp.Header {
if !service.ShouldCopyUpstreamHeader(c, key, values) {
continue
}
for _, value := range values {
c.Header(key, value)
}

View File

@ -30,6 +30,9 @@ func OpenaiTTSHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
usage.PromptTokens = info.GetEstimatePromptTokens()
usage.TotalTokens = info.GetEstimatePromptTokens()
for k, v := range resp.Header {
if !service.ShouldCopyUpstreamHeader(c, k, v) {
continue
}
c.Writer.Header().Set(k, v[0])
}
c.Writer.WriteHeader(resp.StatusCode)

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net/http"
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/logger"
@ -22,6 +23,24 @@ func CloseResponseBodyGracefully(httpResponse *http.Response) {
}
}
// ShouldCopyUpstreamHeader checks whether a given upstream response header
// should be copied to the client response. It returns false for Content-Length
// (managed separately) and X-Oneapi-Request-Id (to preserve the local instance
// ID). When the upstream header is X-Oneapi-Request-Id, the value is captured
// into the Gin context for later logging.
func ShouldCopyUpstreamHeader(c *gin.Context, k string, v []string) bool {
if strings.EqualFold(k, "Content-Length") {
return false
}
if strings.EqualFold(k, common.RequestIdKey) {
if c != nil && len(v) > 0 {
c.Set(common.UpstreamRequestIdKey, v[0])
}
return false
}
return true
}
func IOCopyBytesGracefully(c *gin.Context, src *http.Response, data []byte) {
if c.Writer == nil {
return
@ -35,8 +54,7 @@ func IOCopyBytesGracefully(c *gin.Context, src *http.Response, data []byte) {
// For example, Postman will report error, and we cannot check the response at all.
if src != nil {
for k, v := range src.Header {
// avoid setting Content-Length
if k == "Content-Length" {
if !ShouldCopyUpstreamHeader(c, k, v) {
continue
}
c.Writer.Header().Set(k, v[0])

View File

@ -88,6 +88,8 @@ export function CommonLogsFilterBar<TData>(
if (searchParams.group) next.group = searchParams.group
if (searchParams.username) next.username = searchParams.username
if (searchParams.requestId) next.requestId = searchParams.requestId
if (searchParams.upstreamRequestId)
next.upstreamRequestId = searchParams.upstreamRequestId
if (Object.keys(next).length > 0) {
setFilters((prev) => ({ ...prev, ...next }))
@ -106,6 +108,7 @@ export function CommonLogsFilterBar<TData>(
searchParams.group,
searchParams.username,
searchParams.requestId,
searchParams.upstreamRequestId,
searchParams.type,
])
@ -161,7 +164,8 @@ export function CommonLogsFilterBar<TData>(
!!filters.token ||
!!filters.username ||
!!filters.channel ||
!!filters.requestId
!!filters.requestId ||
!!filters.upstreamRequestId
const hasAdditionalFilters =
!!filters.model || !!filters.group || !!logType || hasExpandedFilters
@ -290,6 +294,15 @@ export function CommonLogsFilterBar<TData>(
onKeyDown={handleKeyDown}
className={inputClass}
/>
<Input
placeholder={t('Upstream Request ID')}
value={filters.upstreamRequestId || ''}
onChange={(e) =>
handleChange('upstreamRequestId', e.target.value)
}
onKeyDown={handleKeyDown}
className={inputClass}
/>
</>
}
hasExpandedActiveFilters={hasExpandedFilters}

View File

@ -519,6 +519,13 @@ export function DetailsDialog(props: DetailsDialogProps) {
mono
/>
)}
{props.log.upstream_request_id && (
<DetailRow
label={t('Upstream Request ID')}
value={props.log.upstream_request_id}
mono
/>
)}
{props.isAdmin && props.log.channel > 0 && (
<DetailRow

View File

@ -44,6 +44,7 @@ export const usageLogSchema = z.object({
ip: z.string().default(''),
other: z.string().default(''),
request_id: z.string().default(''),
upstream_request_id: z.string().default(''),
})
export type UsageLog = z.infer<typeof usageLogSchema>

View File

@ -55,6 +55,9 @@ export function buildSearchParams(
...(commonFilters.group && { group: commonFilters.group }),
...(commonFilters.username && { username: commonFilters.username }),
...(commonFilters.requestId && { requestId: commonFilters.requestId }),
...(commonFilters.upstreamRequestId && {
upstreamRequestId: commonFilters.upstreamRequestId,
}),
}
}
case 'drawing': {

View File

@ -204,6 +204,9 @@ export function buildApiParams(config: {
...(searchParams.requestId
? { request_id: String(searchParams.requestId) }
: {}),
...(searchParams.upstreamRequestId
? { upstream_request_id: String(searchParams.upstreamRequestId) }
: {}),
...buildTimeRangeParams(searchParams, false),
}

View File

@ -52,6 +52,7 @@ export interface CommonLogFilters extends CommonFilters {
group?: string
username?: string
requestId?: string
upstreamRequestId?: string
}
/**
@ -267,6 +268,7 @@ export interface GetLogsParams {
channel?: number
group?: string
request_id?: string
upstream_request_id?: string
}
export interface GetLogsResponse {
@ -290,6 +292,7 @@ export interface GetLogStatsParams {
channel?: number
group?: string
request_id?: string
upstream_request_id?: string
}
export interface GetLogStatsResponse {

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "Generation quality preset",
"Generic cache": "Generic cache",
"Get notified when balance falls below this value": "Get notified when balance falls below this value",
"Get one here": "Get one here",
"Get started": "Get started",
"Get Started": "Get Started",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "IP Filter Mode",
"IP Restriction": "IP Restriction",
"IP Whitelist (supports CIDR)": "IP Whitelist (supports CIDR)",
"Power AI applications, manage digital assets, connect the Future": "Power AI applications, manage digital assets, connect the Future",
"is less than the configured maximum cache size": "is less than the configured maximum cache size",
"is the default price; ": "is the default price; ",
"It seems like the page you're looking for": "It seems like the page you're looking for",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "PostgreSQL detected",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL offers advanced reliability and data integrity for production workloads.",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.",
"Power AI applications, manage digital assets, connect the Future": "Power AI applications, manage digital assets, connect the Future",
"Powerful API Management Platform": "Powerful API Management Platform",
"Pre-Consume for Free Models": "Pre-Consume for Free Models",
"Pre-consumed": "Pre-consumed",
@ -3073,8 +3074,6 @@
"Pull": "Pull",
"Pull model": "Pull model",
"Pulling...": "Pulling...",
"Subscribe to a plan for model access": "Subscribe to a plan for model access",
"Get one here": "Get one here",
"Purchase Limit": "Purchase Limit",
"Purchase limit reached": "Purchase limit reached",
"Purchase Subscription": "Purchase Subscription",
@ -3702,6 +3701,7 @@
"Submitting...": "Submitting...",
"Submodel": "Submodel",
"Subscribe Now": "Subscribe Now",
"Subscribe to a plan for model access": "Subscribe to a plan for model access",
"Subscription": "Subscription",
"Subscription Billing": "Subscription Billing",
"Subscription First": "Subscription First",
@ -4140,6 +4140,7 @@
"Upstream price sync": "Upstream price sync",
"Upstream prices fetched successfully": "Upstream prices fetched successfully",
"Upstream ratios fetched successfully": "Upstream ratios fetched successfully",
"Upstream Request ID": "Upstream Request ID",
"Upstream Response": "Upstream Response",
"upstream services integrated": "upstream services integrated",
"Upstream Updates": "Upstream Updates",

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "Préréglage de qualité de génération",
"Generic cache": "Cache générique",
"Get notified when balance falls below this value": "Recevoir une notification lorsque le solde tombe en dessous de cette valeur",
"Get one here": "Obtenir ici",
"Get started": "Commencer",
"Get Started": "Commencer",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "Mode de filtre IP",
"IP Restriction": "Restriction IP",
"IP Whitelist (supports CIDR)": "Liste blanche IP (supporte CIDR)",
"Power AI applications, manage digital assets, connect the Future": "Propulser les applications IA, gérer les actifs numériques, connecter l'Avenir",
"is less than the configured maximum cache size": "est inférieur à la taille maximale du cache configurée",
"is the default price; ": "est le prix par défaut ; ",
"It seems like the page you're looking for": "Il semble que la page que vous recherchez",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "PostgreSQL détecté",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL offre une fiabilité avancée et une intégrité des données pour les charges de travail en production.",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQL offre de solides garanties de fiabilité. Vérifiez votre fenêtre de maintenance et vos politiques de rétention avant la mise en production.",
"Power AI applications, manage digital assets, connect the Future": "Propulser les applications IA, gérer les actifs numériques, connecter l'Avenir",
"Powerful API Management Platform": "Plateforme puissante de gestion d'API",
"Pre-Consume for Free Models": "Pré-consommation pour les modèles gratuits",
"Pre-consumed": "Pré-consommé",
@ -3073,8 +3074,6 @@
"Pull": "Télécharger",
"Pull model": "Télécharger le modèle",
"Pulling...": "Téléchargement en cours...",
"Subscribe to a plan for model access": "Souscrivez à un plan pour accéder aux modèles",
"Get one here": "Obtenir ici",
"Purchase Limit": "Limite d'achat",
"Purchase limit reached": "Limite d'achat atteinte",
"Purchase Subscription": "Acheter un abonnement",
@ -3702,6 +3701,7 @@
"Submitting...": "Envoi...",
"Submodel": "Submodel",
"Subscribe Now": "S'abonner maintenant",
"Subscribe to a plan for model access": "Souscrivez à un plan pour accéder aux modèles",
"Subscription": "Abonnement",
"Subscription Billing": "Facturation d'abonnement",
"Subscription First": "Abonnement en priorité",
@ -4140,6 +4140,7 @@
"Upstream price sync": "Synchronisation amont des prix",
"Upstream prices fetched successfully": "Prix amont récupérés avec succès",
"Upstream ratios fetched successfully": "Ratios en amont récupérés avec succès",
"Upstream Request ID": "ID de requête en amont",
"Upstream Response": "Réponse amont",
"upstream services integrated": "services en amont intégrés",
"Upstream Updates": "Mises à jour en amont",

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "生成品質プリセット",
"Generic cache": "汎用キャッシュ",
"Get notified when balance falls below this value": "残高がこの値を下回ったときに通知を受け取る",
"Get one here": "こちらから取得",
"Get started": "はじめる",
"Get Started": "開始する",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "IP フィルターモード",
"IP Restriction": "IP制限",
"IP Whitelist (supports CIDR)": "IP ホワイトリストCIDR対応",
"Power AI applications, manage digital assets, connect the Future": "AIアプリケーションを支え、デジタル資産を管理し、未来をつなぐ",
"is less than the configured maximum cache size": "設定された最大キャッシュサイズより小さい",
"is the default price; ": "はデフォルト価格です; ",
"It seems like the page you're looking for": "お探しのページは",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "PostgreSQLが検出されました",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL は本番ワークロード向けの高度な信頼性とデータ整合性を提供します。",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQLは強力な信頼性保証を提供します。本番稼働する前に、メンテナンスウィンドウと保持ポリシーを再確認してください。",
"Power AI applications, manage digital assets, connect the Future": "AIアプリケーションを支え、デジタル資産を管理し、未来をつなぐ",
"Powerful API Management Platform": "強力なAPI管理プラットフォーム",
"Pre-Consume for Free Models": "無料モデルの事前消費",
"Pre-consumed": "事前消費",
@ -3073,8 +3074,6 @@
"Pull": "プル",
"Pull model": "モデルをプル",
"Pulling...": "プル中...",
"Subscribe to a plan for model access": "モデルアクセスのためにプランを登録",
"Get one here": "こちらから取得",
"Purchase Limit": "購入上限",
"Purchase limit reached": "購入上限に達しました",
"Purchase Subscription": "サブスクリプションを購入",
@ -3702,6 +3701,7 @@
"Submitting...": "送信中...",
"Submodel": "Submodel",
"Subscribe Now": "今すぐ購読",
"Subscribe to a plan for model access": "モデルアクセスのためにプランを登録",
"Subscription": "サブスクリプション",
"Subscription Billing": "サブスクリプション課金",
"Subscription First": "サブスクリプション優先",
@ -4140,6 +4140,7 @@
"Upstream price sync": "アップストリーム価格同期",
"Upstream prices fetched successfully": "上流価格を正常に取得しました",
"Upstream ratios fetched successfully": "アップストリーム比率が正常に取得されました",
"Upstream Request ID": "上流リクエストID",
"Upstream Response": "アップストリームレスポンス",
"upstream services integrated": "アップストリームサービス連携",
"Upstream Updates": "アップストリーム更新",

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "Пресет качества генерации",
"Generic cache": "Общий кэш",
"Get notified when balance falls below this value": "Получать уведомления, когда баланс опускается ниже этого значения",
"Get one here": "Получить здесь",
"Get started": "Начало работы",
"Get Started": "Начать",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "Режим фильтрации IP",
"IP Restriction": "Ограничение IP",
"IP Whitelist (supports CIDR)": "Белый список IP (поддерживает CIDR)",
"Power AI applications, manage digital assets, connect the Future": "Обеспечивайте AI-приложения, управляйте цифровыми активами, соединяйте Будущее",
"is less than the configured maximum cache size": "меньше настроенного максимального размера кэша",
"is the default price; ": "— цена по умолчанию; ",
"It seems like the page you're looking for": "Похоже, страница, которую вы ищете",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "PostgreSQL обнаружен",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL обеспечивает высокую надёжность и целостность данных для продакшен-нагрузок.",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQL предлагает надежные гарантии. Дважды проверьте окно обслуживания и политики хранения данных перед запуском.",
"Power AI applications, manage digital assets, connect the Future": "Обеспечивайте AI-приложения, управляйте цифровыми активами, соединяйте Будущее",
"Powerful API Management Platform": "Мощная платформа управления API",
"Pre-Consume for Free Models": "Предварительное потребление для бесплатных моделей",
"Pre-consumed": "Предоплата",
@ -3073,8 +3074,6 @@
"Pull": "Загрузить",
"Pull model": "Загрузить модель",
"Pulling...": "Загрузка...",
"Subscribe to a plan for model access": "Подпишитесь на план для доступа к моделям",
"Get one here": "Получить здесь",
"Purchase Limit": "Лимит покупок",
"Purchase limit reached": "Достигнут лимит покупок",
"Purchase Subscription": "Приобрести подписку",
@ -3702,6 +3701,7 @@
"Submitting...": "Отправка...",
"Submodel": "Submodel",
"Subscribe Now": "Подписаться сейчас",
"Subscribe to a plan for model access": "Подпишитесь на план для доступа к моделям",
"Subscription": "Подписка",
"Subscription Billing": "Биллинг подписки",
"Subscription First": "Подписка в приоритете",
@ -4140,6 +4140,7 @@
"Upstream price sync": "Синхронизация цен upstream",
"Upstream prices fetched successfully": "Цены провайдера успешно получены",
"Upstream ratios fetched successfully": "Коэффициенты upstream успешно получены",
"Upstream Request ID": "ID вышестоящего запроса",
"Upstream Response": "Ответ Upstream",
"upstream services integrated": "интеграций с вышестоящими сервисами",
"Upstream Updates": "Обновления вышестоящих моделей",

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "Mức chất lượng sinh",
"Generic cache": "Bộ đệm chung",
"Get notified when balance falls below this value": "Nhận thông báo khi số dư giảm xuống dưới giá trị này",
"Get one here": "Nhận tại đây",
"Get started": "Bắt đầu",
"Get Started": "Bắt đầu",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "Lọc IP",
"IP Restriction": "Giới hạn IP",
"IP Whitelist (supports CIDR)": "Danh sách trắng IP (hỗ trợ CIDR)",
"Power AI applications, manage digital assets, connect the Future": "Vận hành ứng dụng AI, quản lý tài sản số, kết nối Tương lai",
"is less than the configured maximum cache size": "nhỏ hơn kích thước bộ nhớ đệm tối đa đã cấu hình",
"is the default price; ": "là giá mặc định; ",
"It seems like the page you're looking for": "Có vẻ như trang bạn đang tìm kiếm",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "Phát hiện PostgreSQL",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL cung cấp độ tin cậy cao và tính toàn vẹn dữ liệu cho khối lượng công việc production.",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQL cung cấp các đảm bảo độ tin cậy cao. Hãy kiểm tra kỹ lưỡng cửa sổ bảo trì và các chính sách lưu giữ của bạn trước khi vận hành chính thức.",
"Power AI applications, manage digital assets, connect the Future": "Vận hành ứng dụng AI, quản lý tài sản số, kết nối Tương lai",
"Powerful API Management Platform": "Nền tảng Quản lý API mạnh mẽ",
"Pre-Consume for Free Models": "Dùng trước các mô hình miễn phí",
"Pre-consumed": "Khấu trừ trước",
@ -3073,8 +3074,6 @@
"Pull": "Tải",
"Pull model": "Tải mô hình",
"Pulling...": "Đang tải...",
"Subscribe to a plan for model access": "Đăng ký gói để truy cập mô hình",
"Get one here": "Nhận tại đây",
"Purchase Limit": "Giới hạn mua",
"Purchase limit reached": "Đã đạt giới hạn mua",
"Purchase Subscription": "Mua gói đăng ký",
@ -3702,6 +3701,7 @@
"Submitting...": "Đang gửi...",
"Submodel": "Submodel",
"Subscribe Now": "Đăng ký ngay",
"Subscribe to a plan for model access": "Đăng ký gói để truy cập mô hình",
"Subscription": "Đăng ký",
"Subscription Billing": "Thanh toán gói đăng ký",
"Subscription First": "Ưu tiên đăng ký",
@ -4140,6 +4140,7 @@
"Upstream price sync": "Đồng bộ giá thượng nguồn",
"Upstream prices fetched successfully": "Lấy giá upstream thành công",
"Upstream ratios fetched successfully": "Đã lấy tỷ lệ upstream thành công",
"Upstream Request ID": "ID yêu cầu thượng nguồn",
"Upstream Response": "Upstream feedback",
"upstream services integrated": "dịch vụ thượng nguồn tích hợp",
"Upstream Updates": "Cập nhật nguồn",

View File

@ -1817,6 +1817,7 @@
"Generation quality preset": "生成质量预设",
"Generic cache": "通用缓存",
"Get notified when balance falls below this value": "当余额低于此值时接收通知",
"Get one here": "点此获取",
"Get started": "开始使用",
"Get Started": "开始使用",
"GitHub": "GitHub",
@ -2059,7 +2060,6 @@
"IP Filter Mode": "IP 过滤模式",
"IP Restriction": "IP 限制",
"IP Whitelist (supports CIDR)": "IP 白名单(支持 CIDR 表达式)",
"Power AI applications, manage digital assets, connect the Future": "承载 AI 应用,管理数字资产,连接未来",
"is less than the configured maximum cache size": "小于配置的最大缓存大小",
"is the default price; ": "为默认价格;",
"It seems like the page you're looking for": "您要查找的页面似乎",
@ -2946,6 +2946,7 @@
"PostgreSQL detected": "检测到 PostgreSQL",
"PostgreSQL offers advanced reliability and data integrity for production workloads.": "PostgreSQL 为生产工作负载提供高级可靠性和数据完整性。",
"PostgreSQL offers strong reliability guarantees. Double check your maintenance window and retention policies before going live.": "PostgreSQL 提供强大的可靠性保证。在上线之前,请仔细检查您的维护窗口和保留策略。",
"Power AI applications, manage digital assets, connect the Future": "承载 AI 应用,管理数字资产,连接未来",
"Powerful API Management Platform": "强大的 API 管理平台",
"Pre-Consume for Free Models": "免费模型预消耗",
"Pre-consumed": "预扣费",
@ -3073,8 +3074,6 @@
"Pull": "拉取",
"Pull model": "拉取模型",
"Pulling...": "拉取中...",
"Subscribe to a plan for model access": "订阅套餐以获取模型访问权限",
"Get one here": "点此获取",
"Purchase Limit": "限购",
"Purchase limit reached": "已达到购买上限",
"Purchase Subscription": "购买订阅套餐",
@ -3702,6 +3701,7 @@
"Submitting...": "提交中...",
"Submodel": "Submodel",
"Subscribe Now": "立即订阅",
"Subscribe to a plan for model access": "订阅套餐以获取模型访问权限",
"Subscription": "订阅",
"Subscription Billing": "订阅计费",
"Subscription First": "优先订阅",
@ -4140,6 +4140,7 @@
"Upstream price sync": "上游价格同步",
"Upstream prices fetched successfully": "已成功获取上游价格",
"Upstream ratios fetched successfully": "上游比率获取成功",
"Upstream Request ID": "上游请求 ID",
"Upstream Response": "上游返回",
"upstream services integrated": "上游服务适配",
"Upstream Updates": "上游更新",

View File

@ -37,6 +37,7 @@ const usageLogsSearchSchema = z.object({
group: z.string().optional().catch(''),
username: z.string().optional().catch(''),
requestId: z.string().optional().catch(''),
upstreamRequestId: z.string().optional().catch(''),
startTime: z.number().optional(),
endTime: z.number().optional(),
})