service

package
v0.2.0-beta.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: AGPL-3.0 Imports: 79 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AccessTokenTTL      = 15 * time.Minute
	SecurityProofTTL    = 5 * time.Minute
	LoginSessionTTL     = 30 * 24 * time.Hour
	RefreshReplayWindow = 30 * time.Second
)
View Source
const (
	BillingSourceWallet       = "wallet"
	BillingSourceSubscription = "subscription"
	BillingSourceFree         = "free"
	BillingSourceSelfUse      = "self_use"
)
View Source
const (
	CodexLocalEnvironmentNative    = "native"
	CodexLocalEnvironmentContainer = "container"
)
View Source
const (
	CodexRouteKeyEligible           = "eligible"
	CodexRouteKeyQuotaExhausted     = "quota_exhausted"
	CodexRouteKeyCredentialInvalid  = "credential_invalid"
	CodexRouteKeyCredentialDisabled = "credential_disabled"
)
View Source
const (
	OpenAIPriceCheckInterval   = 24 * time.Hour
	OpenAIPriceCheckStaleAfter = 72 * time.Hour
)
View Source
const (
	QuotaETADepletesBeforeReset           = "depletes_before_reset"
	QuotaETAResetBeforeDepletion          = "reset_before_depletion"
	QuotaETAStableOrNoObservedConsumption = "stable_or_no_observed_consumption"
	QuotaETAInsufficientData              = "insufficient_data"
)
View Source
const (
	DefaultOutboxWorkerID      = "task_billing_outbox_worker"
	DefaultOutboxLeaseDuration = 60
	DefaultOutboxBatchSize     = 50
	DefaultOutboxMaxAttempts   = 5

	ErrorCodeLogDeliveryFailed         = "log_delivery_failed"
	ErrorCodeMaxAttemptsExceeded       = "max_attempts_exceeded"
	ErrorCodeBillingProjectionConflict = "billing_projection_conflict"
)
View Source
const (
	DefaultStaleDispatchThreshold    = 5 * time.Minute
	DefaultStaleReservationThreshold = 10 * time.Minute
	DefaultRecoveryBatchSize         = 50
	DefaultRecoveryLeaseDuration     = 60 * time.Second
	MaxRecoveryLeaseDuration         = 300 * time.Second
)
View Source
const (
	TaskProviderDispatchStatusAccepted = "accepted"
	TaskProviderDispatchStatusRejected = "rejected"
	TaskProviderDispatchStatusUnknown  = "unknown"
)
View Source
const (
	UserFundingWebhookAcknowledge = model.UserFundingWebhookAcknowledge
	UserFundingWebhookSettle      = model.UserFundingWebhookSettle
	UserFundingOrderUnknown       = model.UserFundingOrderUnknown
	UserFundingOrderTopUp         = model.UserFundingOrderTopUp
	UserFundingOrderSubscription  = model.UserFundingOrderSubscription
)
View Source
const (
	ViolationFeeCodePrefix     = "violation_fee."
	CSAMViolationMarker        = "Failed check: SAFETY_CHECK_TYPE"
	ContentViolatesUsageMarker = "Content violates usage guidelines"
)
View Source
const DefaultLogProjectionBackfillBatchSize = 100
View Source
const (
	DefaultTaskEngineWorkerID = "task_engine_runner"
)
View Source
const OptionDiagnosticSchemaVersion = "c09-option-diagnostic-v1"
View Source
const OptionRemediationSchemaVersion = "c09-option-remediation-v1"
View Source
const RefreshCookieName = "new_api_refresh"
View Source
const UserFundingUnavailableCode = "user_funding_unavailable"

Variables

View Source
var (
	ErrLoginSessionInvalid  = errors.New("login session is invalid")
	ErrLoginSessionRevoked  = errors.New("login session is revoked")
	ErrLoginSessionMismatch = errors.New("login session does not match the expected session")
	ErrRefreshTokenInvalid  = errors.New("refresh token is invalid")
	ErrRefreshRace          = errors.New("refresh token was already rotated")
)
View Source
var (
	ErrAuthTokenInvalid = errors.New("authentication token is invalid")
	ErrAuthTokenExpired = errors.New("authentication token has expired")
	ErrProofScope       = errors.New("security proof scope mismatch")
	ErrProofMethod      = errors.New("security proof method mismatch")
)
View Source
var (
	ErrChannelQuotaAlertDeliveryDisabled     = errors.New("channel quota alert delivery is disabled")
	ErrChannelQuotaAlertDeliveryInvalidInput = errors.New("invalid channel quota alert delivery input")
)
View Source
var (
	ErrTaskOperationNotFound    = errors.New("task operation not found")
	ErrTaskOperationUnavailable = errors.New("task operation unavailable")
)
View Source
var (
	ErrTaskResolutionInvalidInput      = errors.New("invalid task resolution input")
	ErrTaskResolutionConflict          = errors.New("task resolution audit command conflict")
	ErrTaskResolutionCASLost           = errors.New("task resolution compare-and-swap lost")
	ErrTaskResolutionInvalidTransition = errors.New("invalid task resolution state transition")
)
View Source
var (
	ErrTaskSubmissionFingerprintProtocol    = errors.New("task submission request fingerprint protocol is invalid")
	ErrTaskSubmissionFingerprintContentType = errors.New("task submission request fingerprint content type is unsupported")
	ErrTaskSubmissionFingerprintRoute       = errors.New("task submission request fingerprint route binding is invalid")
	ErrTaskSubmissionFingerprintBody        = errors.New("task submission request fingerprint JSON body is invalid")
	ErrTaskSubmissionFingerprintSecret      = errors.New("task submission request fingerprint secret is unavailable")
)
View Source
var (
	ErrTaskSubmissionProtocolMethod              = errors.New("task submission protocol requires POST")
	ErrTaskSubmissionProtocolOperationKind       = errors.New("task submission protocol operation kind is invalid")
	ErrTaskSubmissionProtocolIdempotencyMissing  = errors.New("task submission protocol idempotency key is missing")
	ErrTaskSubmissionProtocolIdempotencyMultiple = errors.New("task submission protocol idempotency key must have exactly one value")
	ErrTaskSubmissionProtocolIdempotencyAlias    = errors.New("task submission protocol does not accept an idempotency key alias")
	ErrTaskSubmissionProtocolIdempotencyInvalid  = errors.New("task submission protocol idempotency key is invalid")
	ErrTaskSubmissionProtocolIdempotencyHash     = errors.New("task submission protocol could not hash the idempotency key")
)
View Source
var (
	ErrTaskSubmissionInvalidInput    = errors.New("invalid task submission pipeline input")
	ErrTaskSubmissionDispatcherNil   = errors.New("task provider dispatcher is required")
	ErrTaskSubmissionDispatchCASLost = errors.New("task submission dispatch compare-and-swap lost")
	ErrTaskSubmissionOutcomeCASLost  = errors.New("task submission outcome compare-and-swap lost")
)
View Source
var (
	ErrTokenBudgetUnsupported      = errors.New("strict token budget requires qualified official text input and an explicit output limit")
	ErrTokenBudgetCountUnavailable = errors.New("reliable input token count is unavailable")
)
View Source
var ErrAccountQuotaThresholdUnavailable = errors.New("account quota threshold cannot be verified")
View Source
var ErrAssignedAccessDenied = errors.New("assigned_access_denied")
View Source
var ErrCodexQuotaRoutingUnavailable = errors.New("Codex quota routing state is unavailable")
View Source
var ErrFeeBudgetEvidence = errors.New("strict USD budget requires complete usage and applicable frozen published prices")
View Source
var ErrInsufficientWalletQuota = errors.New("wallet quota insufficient")

ErrInsufficientWalletQuota 钱包原子预扣失败(余额不足),未发生任何扣减。 BillingSession 据此映射为 ErrorCodeInsufficientUserQuota, 使 wallet_first 等计费偏好可以回退到订阅。

View Source
var ErrPricePublicationInput = errors.New("invalid or unqualified price publication input")
View Source
var ErrRelayEligibilityChanged = errors.New("relay_eligibility_changed")
View Source
var (
	ErrTaskRecoveryWorkerInvalidInput = errors.New("invalid task recovery worker input")
)
View Source
var ErrWaffoPancakeOrderLookupFailed = errors.New("waffo pancake order lookup failed")

ErrWaffoPancakeOrderLookupFailed identifies retryable database lookup failures, separately from permanently missing orders or invalid buyer/provider identity.

View Source
var GetTaskAdaptorFunc func(platform constant.TaskPlatform) TaskPollingAdaptor

GetTaskAdaptorFunc 由 main 包注入,用于获取指定平台的任务适配器。 打破 service -> relay -> relay/channel -> service 的循环依赖。

Functions

func AcSearch

func AcSearch(findText string, dict []string, stopImmediately bool) (bool, []string)

func AppendChannelAffinityAdminInfo

func AppendChannelAffinityAdminInfo(c *gin.Context, adminInfo map[string]interface{})

func AppendRelayFailoverAdminInfo

func AppendRelayFailoverAdminInfo(c *gin.Context, adminInfo map[string]interface{})

func ApplyChannelAffinityOverrideTemplate

func ApplyChannelAffinityOverrideTemplate(c *gin.Context, paramOverride map[string]interface{}) (map[string]interface{}, bool)

ApplyChannelAffinityOverrideTemplate merges per-rule channel override templates onto the selected channel override config.

func ApplyOpenAIPricePublication

func ApplyOpenAIPricePublication(ctx context.Context, actorID int, request PricePublicationRequest) (*model.PricePublication, error)

func ApplyRealtimeTaskPollingResponse

func ApplyRealtimeTaskPollingResponse(ctx context.Context, adaptor TaskPollingAdaptor, ch *model.Channel, task *model.Task, resp *http.Response) error

ApplyRealtimeTaskPollingResponse applies one already-fetched provider response through the same durable/legacy validation and accounting boundary as the background poller.

func AssignedAccessGroups

func AssignedAccessGroups(user *model.User, token *model.Token) ([]string, error)

func AssignedAccessProtocolSupported

func AssignedAccessProtocolSupported(channelType int, path string) bool

func AssignedModelAvailability

func AssignedModelAvailability(ctx context.Context, user *model.User, token *model.Token, groups, names []string, override *AssignedAccessPolicyView) ([]AssignedModelAccess, AssignedAccessState, error)

AssignedModelAvailability uses the same subject intersection as dispatch and returns only public names and stable reason codes. Assigned dispatch requires unambiguous post-override JSON evidence bounded by the existing 1 MiB canonical validator; this read-only projection does not promise future quota or health. A public name is available only when some real, currently enabled channel/target can serve it.

func AttachQuotaSaturation

func AttachQuotaSaturation(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, other map[string]interface{})

AttachQuotaSaturation adds the centralized admin-only saturation marker to a consume log payload for billing paths outside the service package.

func AuthSessionErrorCode

func AuthSessionErrorCode(err error) (int, string)

func BuildCodexOAuthAuthorizeURL

func BuildCodexOAuthAuthorizeURL(state string, challenge string) (string, error)

BuildCodexOAuthAuthorizeURL builds the browser URL for a caller-owned state token. This lets the dashboard bind PKCE state to its server-side AuthFlow record instead of storing the verifier in a browser cookie.

func BuildTieredTokenParams

func BuildTieredTokenParams(usage *dto.Usage, isClaudeUsageSemantic bool, usedVars map[string]bool) billingexpr.TokenParams

BuildTieredTokenParams constructs billingexpr.TokenParams from a dto.Usage, normalizing P and C so they mean "tokens not separately priced by the expression". Sub-categories (cache, image, audio) are only subtracted when the expression references them via their own variable.

GPT-format APIs report prompt_tokens / completion_tokens as totals that include all sub-categories (cache, image, audio). Claude-format APIs report them as text-only. This function normalizes to text-only when sub-categories are separately priced.

func CacheGetRandomSatisfiedChannel

func CacheGetRandomSatisfiedChannel(param *RetryParam) (*model.Channel, string, error)

CacheGetRandomSatisfiedChannel tries to get a random channel that satisfies the requirements. 尝试获取一个满足要求的随机渠道。

For "auto" tokenGroup with cross-group Retry enabled: 对于启用了跨分组重试的 "auto" tokenGroup:

  • Each group will exhaust all its priorities before moving to the next group. 每个分组会用完所有优先级后才会切换到下一个分组。

  • Uses ContextKeyAutoGroupIndex to track current group index. 使用 ContextKeyAutoGroupIndex 跟踪当前分组索引。

  • Uses ContextKeyAutoGroupRetryIndex to track the global Retry count when current group started. 使用 ContextKeyAutoGroupRetryIndex 跟踪当前分组开始时的全局重试次数。

  • priorityRetry = Retry - startRetryIndex, represents the priority level within current group. priorityRetry = Retry - startRetryIndex,表示当前分组内的优先级级别。

  • When GetRandomSatisfiedChannel returns nil (priorities exhausted), moves to next group. 当 GetRandomSatisfiedChannel 返回 nil(优先级用完)时,切换到下一个分组。

Example flow (2 groups, each with 2 priorities, RetryTimes=3): 示例流程(2个分组,每个有2个优先级,RetryTimes=3):

Retry=0: GroupA, priority0 (startRetryIndex=0, priorityRetry=0)
         分组A, 优先级0

Retry=1: GroupA, priority1 (startRetryIndex=0, priorityRetry=1)
         分组A, 优先级1

Retry=2: GroupA exhausted → GroupB, priority0 (startRetryIndex=2, priorityRetry=0)
         分组A用完 → 分组B, 优先级0

Retry=3: GroupB, priority1 (startRetryIndex=2, priorityRetry=1)
         分组B, 优先级1

func CalcOpenRouterCacheCreateTokens

func CalcOpenRouterCacheCreateTokens(usage dto.Usage, priceData types.PriceData) (int, error)

func ChargeViolationFeeIfNeeded

func ChargeViolationFeeIfNeeded(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, apiErr *types.NewAPIError) bool

ChargeViolationFeeIfNeeded charges an additional fee after the normal flow finishes (including refund). It uses Grok fee settings as the fee policy.

func ChatCompletionsRequestToResponsesRequest

func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*dto.OpenAIResponsesRequest, error)

func ChatCompletionsResponseToResponsesResponse

func ChatCompletionsResponseToResponsesResponse(resp *dto.OpenAITextResponse, id string) (*dto.OpenAIResponsesResponse, *dto.Usage, error)

func CheckNotificationLimit

func CheckNotificationLimit(userId int, notifyType string) (bool, error)

CheckNotificationLimit checks if the user has exceeded their notification limit Returns true if the user can send notification, false if limit exceeded

func CheckSensitiveMessages

func CheckSensitiveMessages(messages []dto.Message) ([]string, error)

func CheckSensitiveText

func CheckSensitiveText(text string) (bool, []string)

func ClaudeErrorWrapper

func ClaudeErrorWrapper(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode

func ClaudeErrorWrapperLocal

func ClaudeErrorWrapperLocal(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode

func ClaudeToOpenAIRequest

func ClaudeToOpenAIRequest(claudeRequest dto.ClaudeRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error)

func CleanupFileSources

func CleanupFileSources(c *gin.Context)

CleanupFileSources 清理请求中所有注册的 FileSource 应在请求结束时调用(通常由中间件自动调用)

func ClearChannelAffinityCacheAll

func ClearChannelAffinityCacheAll() int

func ClearChannelAffinityCacheByRuleName

func ClearChannelAffinityCacheByRuleName(ruleName string) (int, error)

func ClearCurrentChannelAffinityCache

func ClearCurrentChannelAffinityCache(c *gin.Context) bool

func ClearRefreshCookie

func ClearRefreshCookie(c *gin.Context)

func CloseResponseBodyGracefully

func CloseResponseBodyGracefully(httpResponse *http.Response)

func CodexQuotaEligibleKeys

func CodexQuotaEligibleKeys(ctx context.Context, channel *model.Channel) (map[int]bool, bool, error)

CodexQuotaEligibleKeys evaluates the provider-confirmed account attached to each enabled credential. A 100% active window or exact usage-limit marker excludes that credential until reset or a later healthy provider sample. Invalid credentials are not counted as usable; unrelated providers and generic 429 observations do not change eligibility.

func CodexQuotaSourceQualified

func CodexQuotaSourceQualified(client *http.Client, baseURL string) bool

Percentage gates accept only authenticated native-origin observations. Compatibility proxies keep their existing history/diagnostic behavior.

func ConsumeCodexWhamRateLimitResetCredit

func ConsumeCodexWhamRateLimitResetCredit(
	ctx context.Context,
	client *http.Client,
	baseURL string,
	accessToken string,
	accountID string,
) (statusCode int, body []byte, err error)

func ConvertRequest

func ConvertRequest(c *gin.Context, info *relaycommon.RelayInfo, target types.RelayFormat, request any) (*relayconvert.RequestResult, error)

func ConvertRequestByID

func ConvertRequestByID(c *gin.Context, info *relaycommon.RelayInfo, converter string, request any) (*relayconvert.RequestResult, error)

func ConvertRequestVia

func ConvertRequestVia(c *gin.Context, info *relaycommon.RelayInfo, request any, path ...types.RelayFormat) (*relayconvert.RequestResult, error)

func ConvertSimpleChangeParams

func ConvertSimpleChangeParams(content string) *dto.MidjourneyRequest

func CountAudioTokenInput

func CountAudioTokenInput(audioBase64 string, audioFormat string) (int, error)

func CountAudioTokenOutput

func CountAudioTokenOutput(audioBase64 string, audioFormat string) (int, error)

func CountTextToken

func CountTextToken(text string, model string) int

CountTextToken 统计文本的token数量,仅OpenAI模型使用tokenizer,其余模型使用估算

func CountTokenBudgetBound

func CountTokenBudgetBound(ctx context.Context, client *http.Client, req *http.Request, info *relaycommon.RelayInfo) (*model.TokenBudgetReservation, error)

CountTokenBudgetBound is called on the final outbound request, after model conversion and overrides. It never accepts the gateway's estimated count. No prompt or credential is included in the returned persistence record. The supported source contract is documented at: https://developers.openai.com/api/docs/guides/token-counting

func CountTokenInput

func CountTokenInput(input any, model string) int

func CountTokenRealtime

func CountTokenRealtime(info *relaycommon.RelayInfo, request dto.RealtimeEvent, model string) (int, int, error)

func CoverPlusActionToNormalAction

func CoverPlusActionToNormalAction(midjRequest *dto.MidjourneyRequest) *dto.MidjourneyResponse

func CoverTaskActionToModelName

func CoverTaskActionToModelName(platform constant.TaskPlatform, action string) string

func CovertMjpActionToModelName

func CovertMjpActionToModelName(mjAction string) string

func CreateWaffoPancakePrimaryProduct

func CreateWaffoPancakePrimaryProduct(ctx context.Context, merchantID, privateKey, storeID, returnURL string) (string, error)

CreateWaffoPancakePrimaryProduct mints (and publishes) the wallet-top-up OnetimeProduct under storeID. Per-checkout price overrides via PriceSnapshot are what make the "1.00" seed price irrelevant at runtime.

func CreateWaffoPancakePrimaryStore

func CreateWaffoPancakePrimaryStore(ctx context.Context, merchantID, privateKey string) (string, error)

CreateWaffoPancakePrimaryStore creates a Pancake Store using in-flight (not-yet-persisted) credentials and returns the new store ID.

func CreateWaffoPancakeProductForPlan

func CreateWaffoPancakeProductForPlan(ctx context.Context, merchantID, privateKey, storeID, name, amount, returnURL string) (string, error)

CreateWaffoPancakeProductForPlan mints (and publishes) a Pancake OnetimeProduct priced at `amount` USD, used as a subscription plan's SubscriptionPlan.WaffoPancakeProductId.

OnetimeProduct (not SubscriptionProduct) because MyAPI has no renewal- event handling; Pancake auto-renewing without MyAPI extending user access would be a UX divergence. Revisit if renewal handling is added.

func DecodeAssignedAccessPolicyJSON

func DecodeAssignedAccessPolicyJSON(raw []byte, value any) error

DecodeAssignedAccessPolicyJSON rejects ambiguous policy input before Go's case-insensitive struct decoder can apply last-wins or alias semantics. Request bodies and persisted envelopes share the portable TEXT-size bound.

func DecodeBase64AudioData

func DecodeBase64AudioData(audioBase64 string) (string, error)

func DecodeBase64FileData

func DecodeBase64FileData(base64String string) (string, string, error)

func DecodeBase64ImageData

func DecodeBase64ImageData(base64String string) (image.Config, string, string, error)

return image.Config, format, clean base64 string, error

func DecodeUrlImageData

func DecodeUrlImageData(imageUrl string) (image.Config, string, error)

func DeferTaskPolling

func DeferTaskPolling(ctx context.Context, task *model.Task, manual bool, reason string) error

DeferTaskPolling records realtime-query uncertainty through the same private disposition used by the background poller.

func DetectFileType

func DetectFileType(mimeType string) types.FileType

DetectFileType 检测文件类型

func DetectTaskOperationKind

func DetectTaskOperationKind(method, path string, params map[string]string) (operationKind string, originTaskPublicID string, ok bool)

DetectTaskOperationKind classifies incoming HTTP requests into durable task operation kinds and extracts route-bound parameters (such as origin task ID).

func DisableChannel

func DisableChannel(channelError types.ChannelError, reason string)

disable & notify

func DispatchPlatformUpdate

func DispatchPlatformUpdate(ctx context.Context, platform constant.TaskPlatform, taskChannelM map[int][]string, taskM map[string]*model.Task)

DispatchPlatformUpdate is retained for direct callers. The production pass uses dispatchPlatformUpdate with a channel-scoped composite key batch.

func DoDownloadRequest

func DoDownloadRequest(originUrl string, reason ...string) (resp *http.Response, err error)

func DoMidjourneyHttpRequest

func DoMidjourneyHttpRequest(c *gin.Context, timeout time.Duration, fullRequestURL string) (*dto.MidjourneyResponseWithStatusCode, []byte, error)

func DoWorkerRequest

func DoWorkerRequest(req *WorkerRequest) (*http.Response, error)

DoWorkerRequest 通过Worker发送请求

func DurableReleaseTaskOnFailure

func DurableReleaseTaskOnFailure(ctx context.Context, task *model.Task, reason string) (bool, error)

func DurableReleaseTaskOnFailureWithEvidence

func DurableReleaseTaskOnFailureWithEvidence(ctx context.Context, task *model.Task, reason string, evidence DurableTerminalEvidence) (bool, error)

DurableReleaseTaskOnFailure bridges async task failure to the durable accounting release system (T4). If the task is associated with a durable TaskSubmissionOperation, it executes ReleaseTaskQuotaReservation atomically and transitions the operation to failed.

func DurableSettleTaskOnComplete

func DurableSettleTaskOnComplete(ctx context.Context, task *model.Task, actualQuota int, reason string, clamps ...*common.QuotaClamp) (bool, error)

func DurableSettleTaskOnCompleteWithEvidence

func DurableSettleTaskOnCompleteWithEvidence(ctx context.Context, task *model.Task, actualQuota int, reason string, evidence DurableTerminalEvidence, clamps ...*common.QuotaClamp) (bool, error)

func DurableTerminalManualReviewWithEvidence

func DurableTerminalManualReviewWithEvidence(ctx context.Context, task *model.Task, outcome string, actualQuota int, reason string, evidence DurableTerminalEvidence, clamps ...*common.QuotaClamp) (bool, error)

func EnableChannel

func EnableChannel(channelId int, usingKey string, channelName string)

func EnqueueSystemTask

func EnqueueSystemTask(taskType string, payload any) (*model.SystemTask, bool, error)

EnqueueSystemTask creates an on-demand task of the given type. The returned bool is true only when a new pending row was created; false means an active task of the same type already exists and was returned.

func EnqueueSystemTaskContext

func EnqueueSystemTaskContext(ctx context.Context, taskType string, payload any) (*model.SystemTask, bool, error)

EnqueueSystemTaskContext is the bounded/context-aware enqueue path for accounting recovery. The task is only a scanner wake-up; durable business identity belongs in the corresponding inbox table.

func EstimateRequestToken

func EstimateRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *relaycommon.RelayInfo) (int, error)

func EstimateToken

func EstimateToken(provider Provider, text string) int

EstimateToken 计算 Token 数量

func EstimateTokenByModel

func EstimateTokenByModel(model, text string) int

func ExtractCodexAccountIDFromJWT

func ExtractCodexAccountIDFromJWT(token string) (string, bool)

func ExtractEmailFromJWT

func ExtractEmailFromJWT(token string) (string, bool)

func ExtractOutputTextFromResponses

func ExtractOutputTextFromResponses(resp *dto.OpenAIResponsesResponse) string

func FetchCodexChannelModels

func FetchCodexChannelModels(channel *model.Channel) ([]string, error)

func FetchCodexModels

func FetchCodexModels(
	ctx context.Context,
	client *http.Client,
	baseURL string,
	oauthKey *CodexOAuthKey,
	clientVersion string,
) (statusCode int, models []string, err error)

func FetchCodexWhamRateLimitResetCredits

func FetchCodexWhamRateLimitResetCredits(
	ctx context.Context,
	client *http.Client,
	baseURL string,
	accessToken string,
	accountID string,
) (statusCode int, body []byte, err error)

func FetchCodexWhamUsage

func FetchCodexWhamUsage(
	ctx context.Context,
	client *http.Client,
	baseURL string,
	accessToken string,
	accountID string,
) (statusCode int, body []byte, err error)

func FilterUserTokenAutoGroups

func FilterUserTokenAutoGroups(userGroup string, groups []string) []string

FilterUserTokenAutoGroups applies current permissions before the current per-token limit. It intentionally does not fall back to the global Auto list.

func FinalizeRealtimeUsageDispatch

func FinalizeRealtimeUsageDispatch(c *gin.Context, info *relaycommon.RelayInfo) bool

func FinalizeTextUsageDispatch

func FinalizeTextUsageDispatch(c *gin.Context, info *relaycommon.RelayInfo) bool

Call before retry decisions and on the outer relay exit. Persist uncertainty through the existing writer and review UI, rather than refunding a parse or transport failure. A failed hold is retried on the next exit check while the in-memory reservation remains protected from settlement and refund.

func FinalizeTokenBudgetDispatch

func FinalizeTokenBudgetDispatch(c *gin.Context, info *relaycommon.RelayInfo) bool

Every exit from the strict relay checks the durable pre-send record. Only a proven pre-dispatch cancellation permits the old quota refund path. Storage errors are conservative: never reinterpret an uncertain dispatch as unused.

func FingerprintTaskSubmissionFormRequest

func FingerprintTaskSubmissionFormRequest(input TaskSubmissionFingerprintInput) (string, error)

FingerprintTaskSubmissionFormRequest canonicalizes an application/x-www-form-urlencoded task request and returns only its scoped fingerprint. It is a pure protocol boundary: it neither reads a request nor invokes routing, billing, persistence, or an upstream provider.

func FingerprintTaskSubmissionJSONRequest

func FingerprintTaskSubmissionJSONRequest(input TaskSubmissionFingerprintInput) (string, error)

FingerprintTaskSubmissionJSONRequest canonicalizes and fingerprints one validated JSON task request without returning canonical body bytes. It has no router, middleware, database, billing, dispatch, or network side effects.

func FingerprintTaskSubmissionMultipartRequest

func FingerprintTaskSubmissionMultipartRequest(input TaskSubmissionFingerprintInput) (string, error)

FingerprintTaskSubmissionMultipartRequest canonicalizes a bounded multipart/form-data task request independently from its wire boundary. It accepts only form-data text and file parts with explicit, limited metadata; unknown headers, part types, duplicate names, and malformed bodies fail closed. It does not read an HTTP request or persist any source material.

func FormatAuthError

func FormatAuthError(err error) string

func GeminiToOpenAIRequest

func GeminiToOpenAIRequest(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error)

func GenerateAudioOtherInfo

func GenerateAudioOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage, modelRatio, groupRatio, completionRatio, audioRatio, audioCompletionRatio, modelPrice, userGroupRatio float64) map[string]interface{}

func GenerateClaudeOtherInfo

func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64,
	cacheTokens int, cacheRatio float64,
	cacheCreationTokens int, cacheCreationRatio float64,
	cacheCreationTokens5m int, cacheCreationRatio5m float64,
	cacheCreationTokens1h int, cacheCreationRatio1h float64,
	modelPrice float64, userGroupRatio float64) map[string]interface{}

func GenerateMjOtherInfo

func GenerateMjOtherInfo(relayInfo *relaycommon.RelayInfo, priceData hosttypes.PriceData) map[string]interface{}

func GenerateTextOtherInfo

func GenerateTextOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64,
	cacheTokens int, cacheRatio float64, modelPrice float64, userGroupRatio float64) map[string]interface{}

func GenerateWssOtherInfo

func GenerateWssOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.RealtimeUsage, modelRatio, groupRatio, completionRatio, audioRatio, audioCompletionRatio, modelPrice, userGroupRatio float64) map[string]interface{}

func GetAvailableChannelRoutingPolicy

func GetAvailableChannelRoutingPolicy(ctx context.Context, group, modelName, requestPath string) (model.ChannelRoutingPolicySnapshot, error)

GetAvailableChannelRoutingPolicy applies the existing account eligibility gate to the shared model/endpoint policy. Preview and selection use this same view; final dispatch rechecks account state because observations can change.

func GetBase64Data

func GetBase64Data(c *gin.Context, source types.FileSource, reason ...string) (string, string, error)

GetBase64Data 获取 base64 编码的数据

func GetCallbackAddress

func GetCallbackAddress() string

GetCallbackAddress captures a fresh payment runtime snapshot. Handlers that already captured one must use GetCallbackAddressFromPaymentConfig so the callback address comes from the same configuration generation as the rest of the request.

func GetCallbackAddressFromPaymentConfig

func GetCallbackAddressFromPaymentConfig(paymentConfig setting.PaymentConfig) string

GetCallbackAddressFromPaymentConfig resolves the callback address from the supplied payment runtime snapshot.

func GetFileBase64FromUrl

func GetFileBase64FromUrl(c *gin.Context, url string, reason ...string) (*types.LocalFileData, error)

GetFileBase64FromUrl 从 URL 获取文件的 base64 编码数据 Deprecated: 请使用 GetBase64Data 配合 types.NewURLFileSource 替代 此函数保留用于向后兼容,内部已重构为调用统一的文件服务

func GetFileTypeFromUrl

func GetFileTypeFromUrl(c *gin.Context, url string, reason ...string) (string, error)

GetFileTypeFromUrl 获取文件类型,返回 mime type, 例如 image/jpeg, image/png, image/gif, image/bmp, image/tiff, application/pdf 如果获取失败,返回 application/octet-stream

func GetGroupsEnabledModels

func GetGroupsEnabledModels(groups []string) []string

GetGroupsEnabledModels 按 groups 顺序获取各分组启用的模型并去重

func GetHttpClient

func GetHttpClient() *http.Client

GetHttpClient returns the general outbound client used by relay/provider integrations. Do not attach the SSRF-protected dialer here: provider base URLs are root/operator-managed deployment targets, not arbitrary user-controlled input, and may legitimately point at private networks, private-link endpoints, self-hosted services, or local proxies. Code paths that fetch arbitrary user-controlled URLs must use GetSSRFProtectedHTTPClient or ValidateSSRFProtectedFetchURL instead.

func GetHttpClientWithProxy

func GetHttpClientWithProxy(rawProxyURL string) (*http.Client, error)

GetHttpClientWithProxy returns the default client or a cached proxy-enabled client.

func GetHttpClientWithProxySettings

func GetHttpClientWithProxySettings(rawProxyURL string, settings dto.ChannelSettings) (*http.Client, error)

GetHttpClientWithProxySettings returns a cached HTTP client for the proxy URL and channel transport settings. Default auto + 1 shard shares the same client pool as GetHttpClientWithProxy / GetHttpClient for the empty-proxy case.

func GetImageConfig

func GetImageConfig(c *gin.Context, source types.FileSource) (image.Config, string, error)

GetImageConfig 获取图片配置

func GetImageFromUrl

func GetImageFromUrl(url string) (mimeType string, data string, err error)

GetImageFromUrl 获取图片的类型和base64编码的数据

func GetLatestCodexClientVersion

func GetLatestCodexClientVersion(ctx context.Context, client *http.Client) (string, error)

func GetMimeType

func GetMimeType(c *gin.Context, source types.FileSource) (string, error)

GetMimeType 获取文件的 MIME 类型

func GetMimeTypeByExtension

func GetMimeTypeByExtension(ext string) string

func GetMjRequestModel

func GetMjRequestModel(relayMode int, midjRequest *dto.MidjourneyRequest) (string, *dto.MidjourneyResponse, bool)

func GetPreferredChannelByAffinity

func GetPreferredChannelByAffinity(c *gin.Context, modelName string, usingGroup string) (int, bool)

func GetRequestAutoGroups

func GetRequestAutoGroups(c *gin.Context, userGroup string) []string

GetRequestAutoGroups resolves the ordered Auto groups for the current token. The absence of the context value means that the token inherits the complete global Auto list; a present (even empty) value is an explicit token snapshot.

func GetSSRFProtectedHTTPClient

func GetSSRFProtectedHTTPClient() *http.Client

GetSSRFProtectedHTTPClient 返回带拨号时 SSRF 校验的客户端。 ssrfProtectedHTTPClient 由 InitHttpClient 在启动时初始化,运行期只读。

func GetTaskOperation

func GetTaskOperation(publicID string, userID int, tokenID *int) (*dto.TaskOperationResponse, error)

GetTaskOperation returns the public owner-scoped view of one durable task submission. A nil tokenID scopes by dashboard user only; an API token must match both user and token IDs.

func GetUserAutoGroup

func GetUserAutoGroup(userGroup string) []string

GetUserAutoGroup 根据用户分组获取自动分组设置

func GetUserGroupRatio

func GetUserGroupRatio(userGroup, group string) float64

GetUserGroupRatio 获取用户使用某个分组的倍率 userGroup 用户分组 group 需要获取倍率的分组

func GetUserUsableGroups

func GetUserUsableGroups(userGroup string) map[string]string

func GroupInUserUsableGroups

func GroupInUserUsableGroups(userGroup, groupName string) bool

func HasCSAMViolationMarker

func HasCSAMViolationMarker(err *types.NewAPIError) bool

func HasTaskSubmissionIdempotencyHeader

func HasTaskSubmissionIdempotencyHeader(headers http.Header) bool

HasTaskSubmissionIdempotencyHeader returns true if headers contain any canonical or alias form of the idempotency key header (e.g., Idempotency-Key, X-Idempotency-Key, or case/underscore variants).

func HoldTokenBudgetUsage

func HoldTokenBudgetUsage(ctx context.Context, info *relaycommon.RelayInfo, reason string) error

func IOCopyBytesGracefully

func IOCopyBytesGracefully(c *gin.Context, src *http.Response, data []byte)

func InitAc

func InitAc(dict []string) *goahocorasick.Machine

func InitHttpClient

func InitHttpClient()

func InitTokenEncoders

func InitTokenEncoders()

func InjectTieredBillingInfo

func InjectTieredBillingInfo(other map[string]interface{}, relayInfo *relaycommon.RelayInfo, result *billingexpr.TieredResult)

InjectTieredBillingInfo overlays tiered billing fields onto an existing module-specific other map. Call this after GenerateTextOtherInfo / GenerateClaudeOtherInfo / etc. when the request used tiered_expr billing.

func InspectCodexLocalAuth

func InspectCodexLocalAuth(inContainer bool) (CodexLocalAuthInspection, *CodexOAuthKey)

InspectCodexLocalAuth reads the current process user's Codex auth file. The returned OAuthKey is for trusted server-side use only and must not be exposed in the status response.

func InvalidateProxyClient

func InvalidateProxyClient(rawProxyURL string)

InvalidateProxyClient removes every cached policy variant for one proxy and closes their idle connections (including all HTTP/2 shards).

func IsCodexUsageLimitError

func IsCodexUsageLimitError(err *types.NewAPIError) bool

IsCodexUsageLimitError intentionally distinguishes account exhaustion from a generic 429 caused by short-term request throttling.

func IsUserFundingWebhookAcknowledge

func IsUserFundingWebhookAcknowledge(err error) bool

func IsUserSelectableGroup

func IsUserSelectableGroup(userGroup, groupName string) bool

func IsViolationFeeCode

func IsViolationFeeCode(code types.ErrorCode) bool

func IssueAccessToken

func IssueAccessToken(identity AuthIdentity) (string, int64, error)

func IssueSecurityProof

func IssueSecurityProof(identity AuthIdentity, method string, scopes []string) (string, int64, error)

func ListOptionRemediations

func ListOptionRemediations(startIdx int, pageSize int) ([]model.OptionRemediation, int64, error)

func LoadFileSource

func LoadFileSource(c *gin.Context, source types.FileSource, reason ...string) (*types.CachedFileData, error)

LoadFileSource 加载文件源数据 这是统一的入口,会自动处理缓存和不同的来源类型

func LogTaskConsumption

func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo)

LogTaskConsumption 记录任务消费日志和统计信息(仅记录,不涉及实际扣费)。 实际扣费已由 BillingSession(PreConsumeBilling + SettleBilling)完成。

func MarkChannelAffinityUsed

func MarkChannelAffinityUsed(c *gin.Context, selectedGroup string, channelID int)

func MidjourneyErrorWithStatusCodeWrapper

func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) *taskdto.MidjourneyResponseWithStatusCode

func MidjourneyErrorWrapper

func MidjourneyErrorWrapper(code int, desc string) *taskdto.MidjourneyResponse

func NewProxyHttpClient

func NewProxyHttpClient(proxyURL string) (*http.Client, error)

NewProxyHttpClient is kept for compatibility. Deprecated: use GetHttpClientWithProxy.

func NewSystemTaskProgressReporter

func NewSystemTaskProgressReporter(task *model.SystemTask, runnerID string) func(processed, total int)

NewSystemTaskProgressReporter returns a throttled progress callback bound to a running task. Handlers call it with (processed, total) as they iterate work; it persists a {processed,total,progress} state at most once every ~2s, always emitting the first update and the final 100%. Lock-loss errors are ignored: the lease heartbeat cancels the handler ctx on loss, so progress writes are best-effort and never abort the run themselves. The returned func is single-goroutine only (call it from the handler loop).

func NewSystemTaskProgressReporterWithContext

func NewSystemTaskProgressReporterWithContext(ctx context.Context, task *model.SystemTask, runnerID string) func(processed, total int)

NewSystemTaskProgressReporterWithContext is the bounded opt-in used by quota sampling. Each best-effort write respects the run deadline and takes at most five seconds; other handlers retain the legacy background-context reporter.

func NormalizeCacheCreationSplit

func NormalizeCacheCreationSplit(totalTokens int, tokens5m int, tokens1h int) (int, int)

func NormalizeProxyURL

func NormalizeProxyURL(rawProxyURL string) (string, error)

NormalizeProxyURL validates a proxy URL using runtime-compatible rules and returns its canonical cache key.

func NormalizeViolationFeeError

func NormalizeViolationFeeError(err *types.NewAPIError) *types.NewAPIError

NormalizeViolationFeeError ensures: - if the CSAM marker is present, error.code is set to a stable violation-fee code and skip-retry is enabled. - if error.code already has the violation-fee prefix, skip-retry is enabled.

It must be called before retry decision logic.

func NoteQuotaClamp

func NoteQuotaClamp(relayInfo *relaycommon.RelayInfo, clamp *common.QuotaClamp)

NoteQuotaClamp records the first quota saturation on relay metadata for billing paths implemented outside this package (for example channel tests).

func NotifyRootUser

func NotifyRootUser(t string, subject string, content string)

func NotifyUpstreamModelUpdateWatchers

func NotifyUpstreamModelUpdateWatchers(subject string, content string)

func NotifyUser

func NotifyUser(userId int, userEmail string, userSetting dto.UserSetting, data dto.Notify) error

func ObserveChannelAffinityUsageCacheByRelayFormat

func ObserveChannelAffinityUsageCacheByRelayFormat(c *gin.Context, usage *dto.Usage, relayFormat types.RelayFormat)

ObserveChannelAffinityUsageCacheByRelayFormat records usage cache stats with a stable rate mode derived from relay format.

func ObserveChannelAffinityUsageCacheFromContext

func ObserveChannelAffinityUsageCacheFromContext(c *gin.Context, usage *dto.Usage, cachedTokenRateMode string)

func ObserveRelayFailoverResponse

func ObserveRelayFailoverResponse(c *gin.Context, status int)

ObserveRelayFailoverResponse uses only the raw transport status. A 5xx, redirect, parse failure or missing response cannot prove no generation took place. Generic 429 permits bounded failover but is not an exhaustion marker.

func ObserveRelayTransientFailure

func ObserveRelayTransientFailure(c *gin.Context, response *http.Response, transportErr error)

ObserveRelayTransientFailure changes FUTURE eligibility only. It never clears dispatch uncertainty, refunds funds, or authorizes a current retry.

func ObserveTextUsageDispatchResponse

func ObserveTextUsageDispatchResponse(info *relaycommon.RelayInfo, status int)

Preserve existing explicit request/auth/validation/rate-limit refusals. Timeouts, conflicts, redirects, server failures and accepted responses are not evidence of zero consumption. Use the raw transport status, not a mapped client status. Strict budgets deliberately do not use these exceptions.

func PaymentReturnURL

func PaymentReturnURL(suffix string) string

func PostAudioConsumeQuota

func PostAudioConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage, extraContent string)

func PostConsumeQuota

func PostConsumeQuota(relayInfo *relaycommon.RelayInfo, quota int, preConsumedQuota int, sendEmail bool) error

func PostTextConsumeQuota

func PostTextConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.Usage, extraContent []string)

func PostWssConsumeQuota

func PostWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelName string,
	usage *dto.RealtimeUsage, extraContent string)

func PreConsumeBilling

func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycommon.RelayInfo) *types.NewAPIError

PreConsumeBilling 根据用户计费偏好创建 BillingSession 并执行预扣费。 会话存储在 relayInfo.Billing 上,供后续 Settle / Refund 使用。

func PreConsumeTokenQuota

func PreConsumeTokenQuota(relayInfo *relaycommon.RelayInfo, quota int) error

PreConsumeTokenQuota 仅服务 legacy 计费路径(BillingSession.preConsume / reserveToken)。authoritative 模式下 BillingSession 的 token 预扣经 ReserveAccountQuota 内核完成(见 newAuthoritativeBillingSession); model.TryReserveTokenQuota 在非 legacy 模式下经 requireLegacyQuotaWriterCall fail-closed,二者共同保证本函数不会在 authoritative/bridge 模式下生效。

func PreWssConsumeQuota

func PreWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.RealtimeUsage) error

func PreferredChannelAccountIndex

func PreferredChannelAccountIndex(c *gin.Context, channel *model.Channel) *int

PreferredChannelAccountIndex selects the current key, never a cached raw key. Final dispatch still validates current permissions, configuration and quota.

func PrepareMidjourneyTaskBilling

func PrepareMidjourneyTaskBilling(relayInfo *relaycommon.RelayInfo, task *model.Midjourney, quota int, shouldBill bool) (bool, error)

PrepareMidjourneyTaskBilling sets the durable refund marker before the task is inserted.

func PrepareRealtimeUsageDispatch

func PrepareRealtimeUsageDispatch(c *gin.Context, info *relaycommon.RelayInfo) error

The caller has an accepted upstream WebSocket, but has not started frame forwarding. Persist first, including when cancellation races the upgrade. No timeout or crash may subsequently be mistaken for proof of zero usage.

func PrepareTextUsageDispatch

func PrepareTextUsageDispatch(c *gin.Context, request *http.Request, info *relaycommon.RelayInfo) error

Ordinary text dispatch preserves evidence on existing writer rows before sending. The separate strict Token/USD journal is unchanged. Neither path treats a timeout or process crash as evidence of zero consumption.

func PrepareTieredBillingForSelectedGroup

func PrepareTieredBillingForSelectedGroup(c *gin.Context, relayInfo *relaycommon.RelayInfo) *types.NewAPIError

PrepareTieredBillingForSelectedGroup refreshes routing-dependent billing state for tiered, token and fixed-price quotes before an upstream attempt. An existing session reserves any higher estimate before sending. If the initial group was free and skipped pre-consume, switching to a paid group creates the session at that point.

func PrepareTokenBudgetDispatch

func PrepareTokenBudgetDispatch(c *gin.Context, client *http.Client, req *http.Request, info *relaycommon.RelayInfo) error

func QuotaSnapshotUsable

func QuotaSnapshotUsable(snapshot model.ChannelQuotaSnapshot) bool

QuotaSnapshotUsable rejects malformed numeric fields before they participate in either a chart metric or a consumption calculation.

func RecalculateTaskQuota

func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int, reason string, clamps ...*common.QuotaClamp)

RecalculateTaskQuota 通用的异步差额结算。 actualQuota 是任务完成后的实际应扣额度,与预扣额度 (task.Quota) 做差额结算。 reason 用于日志记录(例如 "token重算" 或 "adaptor调整")。 clamps 按发生顺序传入,首个非空事件记入 admin_info(仅管理员可见)。 存在饱和事件但差额为零时,仅记录 quota=0 的审计日志,不调整账务或请求次数。

func RecalculateTaskQuotaByTokens

func RecalculateTaskQuotaByTokens(ctx context.Context, task *model.Task, totalTokens int, clamps ...*common.QuotaClamp)

RecalculateTaskQuotaByTokens 根据实际 token 消耗重新计费(异步差额结算)。 当任务成功且返回了 totalTokens 时,根据模型倍率和分组倍率重新计算实际扣费额度, 与预扣费的差额进行补扣或退还。支持钱包和订阅计费来源。

func RecordChannelAffinity

func RecordChannelAffinity(c *gin.Context, channelID int)

func RecordCodexUsageLimit

func RecordCodexUsageLimit(ctx context.Context, channelID int, credential string) error

RecordCodexUsageLimit stores only an opaque account identity and a bounded routing hold. A known WHAM reset governs release; when only the 429 is known, ordinary traffic remains held until a later healthy provider sample. Administrative diagnostics and WHAM sampling can still probe recovery.

func RecordRealtimeTieredResponse

func RecordRealtimeTieredResponse(info *relaycommon.RelayInfo, usage *dto.RealtimeUsage) error

RecordRealtimeTieredResponse runs only for provider-reported response.done usage. Local estimates never establish a confirmed per-response quote.

func RecordRealtimeUsageCheckpoint

func RecordRealtimeUsageCheckpoint(c *gin.Context, info *relaycommon.RelayInfo, usage *dto.RealtimeUsage) error

Called only after a complete reported response has passed existing pricing validation, and before that response is forwarded to the client. Estimates never enter this cumulative checkpoint or its manual-recovery lower bound.

func RefreshTokenSID

func RefreshTokenSID(rawRefreshToken string) (string, bool)

func RefundMidjourneyQuota

func RefundMidjourneyQuota(ctx context.Context, task *model.Midjourney, reason string) bool

RefundMidjourneyQuota reverses every accounting element recorded for a billed legacy task. 幂等护栏不再只依赖 task.Quota 内存值:legacy 模式经持久退款事实 "mj-refund:{mjId}",authoritative 模式经同键 receipt,重复 notify/并发 poll 下最多退一次;bridge 模式 fail-closed。UpdateWithStatus CAS 仍是并发入口守卫。

func RefundTaskQuota

func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) bool

RefundTaskQuota 统一的任务失败退款逻辑。 当异步任务失败时,退还资金与令牌额度,并回减用户和渠道用量。 返回资金来源是否已成功退还;失败时保留 quota,供显式重试或人工对账。

func RegisterSystemTaskHandler

func RegisterSystemTaskHandler(h SystemTaskHandler)

RegisterSystemTaskHandler registers a handler keyed by its Type(). It must be called before StartSystemTaskRunner (or any time, since the runner snapshots the registry every pass). Re-registering a type replaces the previous handler.

func RelayAccountIdentity

func RelayAccountIdentity(channel *model.Channel, key string) string

RelayAccountIdentity reuses Codex's existing opaque account reference. Generic providers have only credential identity: no claim of provider account linkage. Neither reference is exposed in diagnostics or API responses.

func RelayAccountSchedulingSupported

func RelayAccountSchedulingSupported(channel *model.Channel, path string) bool

func RelayCooldownEligibleKeys

func RelayCooldownEligibleKeys(ctx context.Context, channel *model.Channel, path string, excluded map[int]bool) (map[int]bool, bool, int64, error)

RelayCooldownEligibleKeys is shared by preview, initial selection and final dispatch. Disabled means no new database read, preserving the legacy default.

func RelayErrorHandler

func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFail bool) (newApiErr *types.NewAPIError)

func RelayFailoverEligibleKeys

func RelayFailoverEligibleKeys(ctx context.Context, channel *model.Channel, excluded map[int]bool) (map[int]bool, bool)

RelayFailoverEligibleKeys intersects previous definite failures with the existing quota/window exclusions. Replacing a credential retires its old request-local failure; moving it to another index does not.

func RemoveDuplicate

func RemoveDuplicate(s []string) []string

func ReportCurrentSystemInstance

func ReportCurrentSystemInstance() error

func ResetProxyClientCache

func ResetProxyClientCache()

ResetProxyClientCache clears cached proxy and non-default direct policy clients and closes idle connections on every transport/shard. The package-level default httpClient pointer stays stable after InitHttpClient; it is only closed and re-registered in the policy cache so concurrent GetHttpClient readers never race a pointer replacement.

func ResetStatusCode

func ResetStatusCode(newApiErr *types.NewAPIError, statusCodeMappingStr string)

func ResolveWaffoPancakeSubscriptionTradeNo

func ResolveWaffoPancakeSubscriptionTradeNo(event *WaffoPancakeWebhookEvent) (string, error)

ResolveWaffoPancakeSubscriptionTradeNo is the SubscriptionOrder counterpart of ResolveWaffoPancakeTradeNo.

func ResolveWaffoPancakeTradeNo

func ResolveWaffoPancakeTradeNo(event *WaffoPancakeWebhookEvent) (string, error)

ResolveWaffoPancakeTradeNo maps a verified webhook event to a local TopUp trade_no via OrderMerchantExternalID, and rejects buyer-identity mismatches.

func ResponseOpenAI2Claude

func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.ClaudeResponse

func ResponseOpenAI2Gemini

func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse

func ResponseText2Usage

func ResponseText2Usage(c *gin.Context, responseText string, modeName string, promptTokens int) *dto.Usage

func ResponsesFinishReasonFromStatus

func ResponsesFinishReasonFromStatus(resp *dto.OpenAIResponsesResponse) (string, bool)

func ResponsesRequestToChatCompletionsRequest

func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) (*dto.GeneralOpenAIRequest, error)

func ResponsesResponseToChatCompletionsResponse

func ResponsesResponseToChatCompletionsResponse(resp *dto.OpenAIResponsesResponse, id string) (*dto.OpenAITextResponse, *dto.Usage, error)

func RevokeByRefreshToken

func RevokeByRefreshToken(rawRefreshToken, expectedSID, reason string) error

func SaveWaffoPancakeConfig

func SaveWaffoPancakeConfig(ctx context.Context, merchantID, privateKey, returnURL, storeID, productID string) error

SaveWaffoPancakeConfig persists the operator-controlled fields atomically at the end of the configuration flow via model.UpdateOptionsBulk (single DB transaction). A blank privateKey is treated as "keep current" (Stripe-style API-secret UX) and is omitted from the bulk payload.

func SendWebhookNotify

func SendWebhookNotify(webhookURL string, secret string, data dto.Notify) error

SendWebhookNotify 发送 webhook 通知

func SensitiveWordContains

func SensitiveWordContains(text string) (bool, []string)

SensitiveWordContains 是否包含敏感词,返回是否包含敏感词和敏感词列表

func SensitiveWordReplace

func SensitiveWordReplace(text string, returnImmediately bool) (bool, []string, string)

SensitiveWordReplace 敏感词替换,返回是否包含敏感词和替换后的文本

func SetHttpClientForTest

func SetHttpClientForTest(client *http.Client) func()

SetHttpClientForTest overrides the package-level HTTP client for deterministic unit testing.

func SettleBilling

func SettleBilling(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuota int) error

SettleBilling 执行计费结算。如果 RelayInfo 上有 BillingSession 则通过 session 结算, 否则回退到旧的 PostConsumeQuota 路径(兼容按次计费等场景)。

func SettleMidjourneyTaskBilling

func SettleMidjourneyTaskBilling(relayInfo *relaycommon.RelayInfo, task *model.Midjourney, prepared bool) (bool, error)

SettleMidjourneyTaskBilling charges a persisted legacy task and records the applied stages. 稳定业务键为 "mj-billing:{mjId}"(mjId 缺失时回退请求 ID):legacy 模式保持 迁移前直写机制与余额结果;authoritative 模式经 receipt 内核按事件键幂等; bridge 模式 fail-closed。

func SettleTokenBudgetUsage

func SettleTokenBudgetUsage(ctx *gin.Context, info *relaycommon.RelayInfo, usage *dto.Usage) error

func ShouldChatCompletionsUseResponsesGlobal

func ShouldChatCompletionsUseResponsesGlobal(channelID int, channelType int, model string) bool

func ShouldChatCompletionsUseResponsesPolicy

func ShouldChatCompletionsUseResponsesPolicy(policy model_setting.ChatCompletionsToResponsesPolicy, channelID int, channelType int, model string) bool

func ShouldCopyUpstreamHeader

func ShouldCopyUpstreamHeader(c *gin.Context, k string, v []string) bool

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 ShouldDisableChannel

func ShouldDisableChannel(err *types.NewAPIError) bool

func ShouldEnableChannel

func ShouldEnableChannel(newAPIError *types.NewAPIError, status int) bool

func ShouldKeepChannelAffinityOnChannelDisabled

func ShouldKeepChannelAffinityOnChannelDisabled() bool

func ShouldScheduleLogProjectionBackfill

func ShouldScheduleLogProjectionBackfill(ctx context.Context, mainDB *gorm.DB) bool

func ShouldSkipRetryAfterChannelAffinityFailure

func ShouldSkipRetryAfterChannelAffinityFailure(c *gin.Context) bool

func StartAuthArtifactCleanup

func StartAuthArtifactCleanup()

StartAuthArtifactCleanup removes expired dashboard Sessions and old one-time authentication flows. Only the master instance performs cleanup.

func StartChannelQuotaAlertDeliveryWorker

func StartChannelQuotaAlertDeliveryWorker()

StartChannelQuotaAlertDeliveryWorker starts the master process loop. Claims remain database-CAS protected if more than one master is briefly active.

func StartCodexCredentialAutoRefreshTask

func StartCodexCredentialAutoRefreshTask()

func StartLogCleanupTask

func StartLogCleanupTask(targetTimestamp int64) (*model.SystemTask, error)

func StartSubscriptionQuotaResetTask

func StartSubscriptionQuotaResetTask()

func StartSystemInstanceReporter

func StartSystemInstanceReporter()

func StartSystemTaskRunner

func StartSystemTaskRunner()

func StreamResponseOpenAI2Claude

func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) []*dto.ClaudeResponse

func StreamResponseOpenAI2Gemini

func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse

func SundaySearch

func SundaySearch(text string, pattern string) bool

func SupportsSelfUseMeteredRequest

func SupportsSelfUseMeteredRequest(request *http.Request) bool

Shared by early middleware and the frozen-source check before dispatch.

func TaskErrorFromAPIError

func TaskErrorFromAPIError(apiErr *types.NewAPIError) *taskdto.TaskError

TaskErrorFromAPIError 将 PreConsumeBilling 返回的 NewAPIError 转换为 TaskError。

func TaskErrorWrapper

func TaskErrorWrapper(err error, code string, statusCode int) *taskdto.TaskError

func TaskErrorWrapperLocal

func TaskErrorWrapperLocal(err error, code string, statusCode int) *taskdto.TaskError

func TaskInitialQuota

func TaskInitialQuota(version int, estimatedQuota, reservedQuota int64) int

TaskInitialQuota returns the quota persisted on a formal Task. V2 receipts separate the estimated initial charge from the actual reservation; historical receipts use the reservation amount for both meanings.

func TextUsageDispatchNeedsReview

func TextUsageDispatchNeedsReview(info *relaycommon.RelayInfo) bool

func TokenBudgetRelayError

func TokenBudgetRelayError(c *gin.Context, err error) *types.NewAPIError

func TryTieredSettle

func TryTieredSettle(relayInfo *relaycommon.RelayInfo, params billingexpr.TokenParams) (ok bool, quota int, result *billingexpr.TieredResult)

TryTieredSettle checks if the request uses tiered_expr billing and, if so, computes the actual quota using the captured BillingSnapshot. Returns:

  • ok=true, quota, result when tiered billing applies
  • ok=false, 0, nil when it doesn't (caller should fall through to existing logic)

func UpdateSunoTasks

func UpdateSunoTasks(ctx context.Context, taskChannelM map[int][]string, taskM map[string]*model.Task) error

func UpdateVideoTasks

func UpdateVideoTasks(ctx context.Context, platform constant.TaskPlatform, taskChannelM map[int][]string, taskM map[string]*model.Task) error

UpdateVideoTasks is retained for direct callers. The production polling pass uses updateVideoPollingBatch so identical upstream IDs remain channel-scoped.

func UserFundingMutationsAvailable

func UserFundingMutationsAvailable() bool

func ValidUsage

func ValidUsage(usage *dto.Usage) bool

func ValidateAccountQuotaThresholdDispatch

func ValidateAccountQuotaThresholdDispatch(ctx context.Context, client *http.Client, request *http.Request, info *relaycommon.RelayInfo) error

Final DB-only check on the exact credential/header/URL that will be sent. Never rotate credentials, poll upstream, or manufacture account consumption.

func ValidateAssignedAccessDispatch

func ValidateAssignedAccessDispatch(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error

ValidateAssignedAccessDispatch is independent from failover. It reloads the authoritative user, Key, assignments and actual channel before new send; cached auth or a pre-mutation request context cannot grant assigned access. It cannot recall a request which an external provider has already accepted.

func ValidateAssignedAccessSelection

func ValidateAssignedAccessSelection(c *gin.Context, channel *model.Channel, publicModel string) error

ValidateAssignedAccessSelection protects every selected channel, including affinity, explicit channel IDs, retries and fixed-key task paths. Diagnostic callers are excluded by the middleware entry point, not by client input.

func ValidateAssignedModelRouteDispatch

func ValidateAssignedModelRouteDispatch(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error

ValidateAssignedModelRouteDispatch requires an unambiguous, exact target in the bytes being sent. Struct decoding alone accepts case-insensitive keys and last-wins duplicates which can disagree with a provider's JSON parser. The existing canonical validator bounds assigned target proof to 1 MiB; larger evidence is rejected, never truncated or silently treated as inherit.

func ValidateLoginSession

func ValidateLoginSession(identity AuthIdentity) (*model.UserSession, *model.UserBase, error)

func ValidateModelRouteDispatch

func ValidateModelRouteDispatch(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error

ValidateModelRouteDispatch checks the final post-override body, not just the caller DTO. It never sends a request, counts tokens or consumes quota.

func ValidateProxyURL

func ValidateProxyURL(rawProxyURL string) error

ValidateProxyURL validates a channel proxy URL without connecting to it.

func ValidatePublishedPriceRequest

func ValidatePublishedPriceRequest(info *relaycommon.RelayInfo) error

This first publication contract is a Standard text reference tariff. It cannot silently price multimodal, tools, or a different service tier. The reference remains distinct from actual API fees, particularly on subscription channels. Later fee budgets must establish their own upstream applicability.

func ValidatePublishedPriceSelectedChannel

func ValidatePublishedPriceSelectedChannel(ctx *gin.Context, info *relaycommon.RelayInfo) error

Runs for each selected channel, including retries, before sending upstream. Overrides can change the qualified request after quote construction, so this bounded contract rejects them rather than claiming unverified applicability.

func ValidateRelayFailoverDispatch

func ValidateRelayFailoverDispatch(c *gin.Context, info *relaycommon.RelayInfo) error

ValidateRelayFailoverDispatch re-reads admission and the selected credential just before HTTP dispatch. It never rotates keys or recalculates a frozen price. Revocations/configuration changes stop this request; a new request may obtain a fresh admission. The DB check cannot promise atomicity with an external provider accepting the subsequently sent request.

func ValidateSSRFProtectedFetchURL

func ValidateSSRFProtectedFetchURL(urlStr string) error

func ValidateTokenBudgetSelectedChannel

func ValidateTokenBudgetSelectedChannel(info *relaycommon.RelayInfo) error

func VerifySecurityProof

func VerifySecurityProof(raw string, identity AuthIdentity, requiredScope string, allowedMethods []string) (string, error)

func WaffoPancakeBuyerIdentityFromUserID

func WaffoPancakeBuyerIdentityFromUserID(userID int) string

WaffoPancakeBuyerIdentityFromUserID renders the canonical buyer identity for new checkouts. Webhook handlers accept both this value and the legacy value below so in-flight orders created before the branding migration keep working.

func WrapAsViolationFeeGrokCSAM

func WrapAsViolationFeeGrokCSAM(err *types.NewAPIError) *types.NewAPIError

func WriteRefreshCookie

func WriteRefreshCookie(c *gin.Context, rawToken string)

Types

type AssignedAccessPolicyView

type AssignedAccessPolicyView struct {
	Subject   string `json:"subject"`
	SubjectID int    `json:"subject_id"`
	Revision  int64  `json:"revision"`
	Assigned  bool   `json:"assigned"`
	accesspolicy.AssignedPolicy
}

AssignedAccessPolicyView is a detached administrative view. A removal retains its revision tombstone, so an old editor cannot resurrect a removed policy.

func ReadAssignedAccessPolicy

func ReadAssignedAccessPolicy(ctx context.Context, subject string, id int) (AssignedAccessPolicyView, error)

type AssignedAccessState

type AssignedAccessState struct {
	User  AssignedAccessPolicyView
	Token AssignedAccessPolicyView
}

func LoadAssignedAccessState

func LoadAssignedAccessState(ctx context.Context, userID, tokenID int) (AssignedAccessState, error)

func (AssignedAccessState) Assigned

func (state AssignedAccessState) Assigned() bool

func (AssignedAccessState) Reasons

func (state AssignedAccessState) Reasons(publicModel, upstreamModel string, channelID int) []string

type AssignedModelAccess

type AssignedModelAccess struct {
	Model   string   `json:"model"`
	Allowed bool     `json:"allowed"`
	Reasons []string `json:"reasons"`
}

type AuthBundle

type AuthBundle struct {
	AccessToken     string           `json:"access_token"`
	TokenType       string           `json:"token_type"`
	AccessExpiresAt int64            `json:"access_expires_at"`
	Session         LoginSessionView `json:"session"`
	RefreshToken    string           `json:"-"`
}

func AdvanceCurrentSessionSecurity

func AdvanceCurrentSessionSecurity(identity AuthIdentity, reason string) (*AuthBundle, error)

AdvanceCurrentSessionSecurity increments the user's global auth version, preserves only the current browser session at a new session version and returns a replacement access token. Call after a successful 2FA/passkey security-setting mutation that did not already advance AuthVersion.

func AdvanceCurrentSessionToUserVersion

func AdvanceCurrentSessionToUserVersion(identity AuthIdentity, reason string) (*AuthBundle, error)

AdvanceCurrentSessionToUserVersion is used when the security mutation and AuthVersion increment were committed in the same transaction (for example, a password change).

func CreateLoginSession

func CreateLoginSession(userID int, loginMethod, ip, userAgent string) (*AuthBundle, error)

func CreateLoginSessionAtAuthVersion

func CreateLoginSessionAtAuthVersion(userID int, expectedAuthVersion int64, loginMethod, ip, userAgent string) (*AuthBundle, error)

func RefreshLoginSession

func RefreshLoginSession(rawRefreshToken, expectedSID, ip, userAgent string) (*AuthBundle, *model.User, error)

type AuthIdentity

type AuthIdentity struct {
	UserID          int
	SessionID       string
	UserAuthVersion int64
	SessionVersion  int64
}

AuthIdentity is the server-validated identity attached to dashboard requests. Role, status and group are deliberately loaded from the user cache instead of JWT claims.

func ParseAccessToken

func ParseAccessToken(raw string) (AuthIdentity, error)

func ParseDashboardAccessToken

func ParseDashboardAccessToken(raw string) (identity AuthIdentity, internal bool, err error)

ParseDashboardAccessToken distinguishes new-api dashboard JWTs from opaque credentials. A token carrying the dashboard issuer, audience and a known token use is always treated as internal, even when its signature, lifetime or requested purpose is invalid, so it can never fall through to PAT or relay-token authentication.

func ValidateSessionReference

func ValidateSessionReference(userID int, sid string) (AuthIdentity, error)

ValidateSessionReference validates a server-side flow bound to an existing dashboard session without requiring an access token on the callback request.

type BillingSession

type BillingSession struct {
	// contains filtered or unexported fields
}

BillingSession 封装单次请求的预扣费/结算/退款生命周期。 实现 relaycommon.BillingSettler 接口。

func NewBillingSession

func NewBillingSession(c *gin.Context, relayInfo *relaycommon.RelayInfo, preConsumedQuota int) (*BillingSession, *types.NewAPIError)

NewBillingSession 根据用户计费偏好创建 BillingSession,处理 subscription_first / wallet_first 的回退。 创建成功的会话计入 quota writer 进程级在途计数,由 Settle/Refund 收尾对称归还。

func (*BillingSession) GetPreConsumedQuota

func (s *BillingSession) GetPreConsumedQuota() int

GetPreConsumedQuota 返回实际预扣的额度。

func (*BillingSession) HoldUnknownUsage

func (s *BillingSession) HoldUnknownUsage(parent context.Context, reason string, metadata ...string) error

HoldUnknownUsage prevents this session from turning an estimate into a terminal charge/refund. The durable writer freezes its existing lifecycle. Legacy sessions retain their pre-dispatch reservation journal. Failure to write the hold never falls back to a guessed charge or refund.

func (*BillingSession) NeedsRefund

func (s *BillingSession) NeedsRefund() bool

NeedsRefund 返回是否存在需要退还的预扣状态。

func (*BillingSession) Refund

func (s *BillingSession) Refund(c *gin.Context) (resultErr error)

Refund records a durable failure fact before applying a refund. A nil result proves the refund is terminally applied; ErrAccountQuotaRefundPending proves that recovery is durable but not complete.

func (*BillingSession) Reserve

func (s *BillingSession) Reserve(targetQuota int) error

func (*BillingSession) Settle

func (s *BillingSession) Settle(actualQuota int) error

Settle 根据实际消耗额度进行结算。结算意图先持久化;若同步完成失败, 后台恢复使用同一事实身份重放,不再改写已确认的 actualQuota。

func (*BillingSession) SettleWithContext

func (s *BillingSession) SettleWithContext(parent context.Context, actualQuota int) (resultErr error)

SettleWithContext preserves context values but detaches request cancellation. Once upstream usage is known, settlement is an accounting obligation that must be persisted even if the client disconnects.

type ChannelAffinityCacheParams

type ChannelAffinityCacheParams struct {
	Capacity          int  `json:"capacity"`
	DefaultTTLSeconds int  `json:"default_ttl_seconds"`
	RebuildRequired   bool `json:"rebuild_required"`
}

ChannelAffinityCacheParams 描述亲和缓存实例的生效参数。

func RebuildChannelAffinityCache

func RebuildChannelAffinityCache() ChannelAffinityCacheParams

RebuildChannelAffinityCache 按当前配置代立即重建亲和缓存(维护操作)。

线程安全:新实例构造完成后一次性原子换入;换入前已取到旧实例指针的 进行中请求继续使用旧实例直到结束(不迁移),新请求使用新实例。 内存模式下旧实例条目随实例一起废弃(相当于清空重建);Redis 模式下 容量/TTL 本就只影响内存回退层,Redis 键不受影响。

返回重建后的生效参数。

type ChannelAffinityCacheStats

type ChannelAffinityCacheStats struct {
	Enabled       bool           `json:"enabled"`
	Total         int            `json:"total"`
	Unknown       int            `json:"unknown"`
	ByRuleName    map[string]int `json:"by_rule_name"`
	CacheCapacity int            `json:"cache_capacity"`
	CacheAlgo     string         `json:"cache_algo"`
	// ActiveCacheCapacity 当前生效缓存实例构造时使用的容量参数。
	ActiveCacheCapacity int `json:"active_cache_capacity"`
	// ActiveDefaultTTLSeconds 当前生效缓存实例构造时使用的默认 TTL(秒)。
	ActiveDefaultTTLSeconds int `json:"active_default_ttl_seconds"`
	// ConfiguredMaxEntries 配置代(最近保存)的 max_entries。
	ConfiguredMaxEntries int `json:"configured_max_entries"`
	// ConfiguredDefaultTTLSeconds 配置代(最近保存)的 default_ttl_seconds。
	ConfiguredDefaultTTLSeconds int `json:"configured_default_ttl_seconds"`
	// RebuildRequired 为 true 表示容量/TTL 已保存但未生效,需要维护重建或重启。
	RebuildRequired bool `json:"rebuild_required"`
}

func GetChannelAffinityCacheStats

func GetChannelAffinityCacheStats() ChannelAffinityCacheStats

type ChannelAffinityStatsContext

type ChannelAffinityStatsContext struct {
	RuleName       string
	UsingGroup     string
	KeyFingerprint string
	TTLSeconds     int64
}

func GetChannelAffinityStatsContext

func GetChannelAffinityStatsContext(c *gin.Context) (ChannelAffinityStatsContext, bool)

type ChannelAffinityUsageCacheCounters

type ChannelAffinityUsageCacheCounters struct {
	CachedTokenRateMode string `json:"cached_token_rate_mode"`

	Hit           int64 `json:"hit"`
	Total         int64 `json:"total"`
	WindowSeconds int64 `json:"window_seconds"`

	PromptTokens         int64 `json:"prompt_tokens"`
	CompletionTokens     int64 `json:"completion_tokens"`
	TotalTokens          int64 `json:"total_tokens"`
	CachedTokens         int64 `json:"cached_tokens"`
	PromptCacheHitTokens int64 `json:"prompt_cache_hit_tokens"`
	LastSeenAt           int64 `json:"last_seen_at"`
}

type ChannelAffinityUsageCacheStats

type ChannelAffinityUsageCacheStats struct {
	RuleName            string `json:"rule_name"`
	UsingGroup          string `json:"using_group"`
	KeyFingerprint      string `json:"key_fp"`
	CachedTokenRateMode string `json:"cached_token_rate_mode"`

	Hit           int64 `json:"hit"`
	Total         int64 `json:"total"`
	WindowSeconds int64 `json:"window_seconds"`

	PromptTokens         int64 `json:"prompt_tokens"`
	CompletionTokens     int64 `json:"completion_tokens"`
	TotalTokens          int64 `json:"total_tokens"`
	CachedTokens         int64 `json:"cached_tokens"`
	PromptCacheHitTokens int64 `json:"prompt_cache_hit_tokens"`
	LastSeenAt           int64 `json:"last_seen_at"`
}

func GetChannelAffinityUsageCacheStats

func GetChannelAffinityUsageCacheStats(ruleName, usingGroup, keyFp string) ChannelAffinityUsageCacheStats

type ChannelQuotaAlertDeliveryEvent

type ChannelQuotaAlertDeliveryEvent struct {
	SeriesRef     string                           `json:"series_ref"`
	Evidence      *model.ChannelQuotaAlertEvidence `json:"evidence"`
	ID            int64                            `json:"id"`
	EventKey      string                           `json:"event_key"`
	SnapshotID    int                              `json:"snapshot_id"`
	ChannelID     int                              `json:"channel_id"`
	Status        string                           `json:"status"`
	Kind          string                           `json:"kind"`
	State         string                           `json:"state"`
	AttemptCount  int                              `json:"attempt_count"`
	NextAttemptAt int64                            `json:"next_attempt_at,omitempty"`
	LastErrorCode string                           `json:"last_error_code,omitempty"`
	LastErrorAt   int64                            `json:"last_error_at,omitempty"`
	DeliveredAt   *int64                           `json:"delivered_at,omitempty"`
	ObservedAt    int64                            `json:"observed_at"`
	CreatedAt     int64                            `json:"created_at"`
	UpdatedAt     int64                            `json:"updated_at"`
}

func ListChannelQuotaAlertDeliveryEvents

func ListChannelQuotaAlertDeliveryEvents(ctx context.Context, filter model.ChannelQuotaAlertEventFilter, offset, limit int) ([]ChannelQuotaAlertDeliveryEvent, int64, error)

type ChannelQuotaAlertDeliveryStatus

type ChannelQuotaAlertDeliveryStatus struct {
	PolicyEnabled bool   `json:"policy_enabled"`
	Configured    bool   `json:"configured"`
	EndpointHost  string `json:"endpoint_host,omitempty"`
	HTTPSOnly     bool   `json:"https_only"`
	Redirects     bool   `json:"redirects_allowed"`
	TimeoutMillis int64  `json:"timeout_ms"`
	MaxAttempts   int    `json:"max_attempts"`
}

func GetChannelQuotaAlertDeliveryStatus

func GetChannelQuotaAlertDeliveryStatus() (ChannelQuotaAlertDeliveryStatus, error)

func UpdateChannelQuotaAlertDeliverySettings

func UpdateChannelQuotaAlertDeliverySettings(webhookURL, webhookSecret string) (ChannelQuotaAlertDeliveryStatus, error)

UpdateChannelQuotaAlertDeliverySettings persists URL and signing secret as one hidden option. Empty values disable delivery and clear both fields.

type ChannelQuotaAlertDeliverySummary

type ChannelQuotaAlertDeliverySummary struct {
	Enabled     bool `json:"enabled"`
	Claimed     int  `json:"claimed"`
	Delivered   int  `json:"delivered"`
	Retryable   int  `json:"retryable"`
	Quarantined int  `json:"quarantined"`
}

func RunChannelQuotaAlertDeliveryPass

func RunChannelQuotaAlertDeliveryPass(ctx context.Context, workerID string) (ChannelQuotaAlertDeliverySummary, error)

type ChannelQuotaAlertDeliveryWorker

type ChannelQuotaAlertDeliveryWorker struct {
	// contains filtered or unexported fields
}

func NewChannelQuotaAlertDeliveryWorker

func NewChannelQuotaAlertDeliveryWorker(workerID string) *ChannelQuotaAlertDeliveryWorker

func (*ChannelQuotaAlertDeliveryWorker) RunOnce

type CodexCredentialPersistenceError

type CodexCredentialPersistenceError struct {
	// contains filtered or unexported fields
}

CodexCredentialPersistenceError identifies a failure after the upstream has already rotated a credential, allowing background sampling to record the persistence failure separately from an upstream outage.

func (*CodexCredentialPersistenceError) Error

func (*CodexCredentialPersistenceError) Unwrap

func (err *CodexCredentialPersistenceError) Unwrap() error

type CodexCredentialRefreshOptions

type CodexCredentialRefreshOptions struct {
	// ResetCaches is retained for caller compatibility. Credential persistence
	// now updates the affected channel cache entry directly, so a full cache
	// rebuild is neither required nor performed here.
	ResetCaches bool
	// ExpectedKey identifies the credential snapshot that produced an upstream
	// authentication failure. If another operation already replaced it before
	// this refresh acquires the local gate and distributed lease, the latest
	// persisted credential is returned without another token rotation.
	ExpectedKey *string
}

type CodexLocalAuthInspection

type CodexLocalAuthInspection struct {
	State                 CodexLocalAuthState `json:"state"`
	Platform              string              `json:"platform"`
	Environment           string              `json:"environment"`
	CodexInstalled        bool                `json:"codex_installed"`
	CLIVersion            string              `json:"cli_version,omitempty"`
	AuthFileExists        bool                `json:"auth_file_exists"`
	AuthReadable          bool                `json:"auth_readable"`
	LoggedIn              bool                `json:"logged_in"`
	AutoImportAvailable   bool                `json:"auto_import_available"`
	ManualImportAvailable bool                `json:"manual_import_available"`
	AccountHint           string              `json:"account_hint,omitempty"`
	EmailHint             string              `json:"email_hint,omitempty"`
	LastRefresh           string              `json:"last_refresh,omitempty"`
	CanRefresh            bool                `json:"can_refresh"`
}

CodexLocalAuthInspection is safe to return from an API. It deliberately contains neither the credential nor the resolved auth.json path.

type CodexLocalAuthState

type CodexLocalAuthState string
const (
	CodexLocalAuthReady                 CodexLocalAuthState = "ready"
	CodexLocalAuthContainerUnavailable  CodexLocalAuthState = "container_host_unavailable"
	CodexLocalAuthHomeUnavailable       CodexLocalAuthState = "home_unavailable"
	CodexLocalAuthNotFound              CodexLocalAuthState = "not_found"
	CodexLocalAuthUnreadable            CodexLocalAuthState = "unreadable"
	CodexLocalAuthUnsafeFile            CodexLocalAuthState = "unsafe_file"
	CodexLocalAuthTooLarge              CodexLocalAuthState = "too_large"
	CodexLocalAuthChangedDuringRead     CodexLocalAuthState = "changed_during_read"
	CodexLocalAuthInvalidJSON           CodexLocalAuthState = "invalid_json"
	CodexLocalAuthUnsupportedAuthMethod CodexLocalAuthState = "unsupported_auth_method"
	CodexLocalAuthIncompleteCredential  CodexLocalAuthState = "incomplete_credential"
)

type CodexOAuthAuthorizationFlow

type CodexOAuthAuthorizationFlow struct {
	State        string
	Verifier     string
	Challenge    string
	AuthorizeURL string
}

func CreateCodexOAuthAuthorizationFlow

func CreateCodexOAuthAuthorizationFlow() (*CodexOAuthAuthorizationFlow, error)

type CodexOAuthKey

type CodexOAuthKey struct {
	IDToken      string `json:"id_token,omitempty"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`

	AccountID   string `json:"account_id,omitempty"`
	LastRefresh string `json:"last_refresh,omitempty"`
	Email       string `json:"email,omitempty"`
	Type        string `json:"type,omitempty"`
	Expired     string `json:"expired,omitempty"`
}

func RefreshCodexChannelCredential

func RefreshCodexChannelCredential(ctx context.Context, channelID int, opts CodexCredentialRefreshOptions) (*CodexOAuthKey, *model.Channel, error)

func RefreshCodexChannelCredentialElement

func RefreshCodexChannelCredentialElement(ctx context.Context, channelID int, expectedCredential, proxyURL string) (*CodexOAuthKey, error)

RefreshCodexChannelCredentialElement rotates one expected credential without replacing a channel's other keys. The credential gate and distributed lease serialize refresh-token use; stale replacement is rejected without overwriting other credentials. Reordering preserves the matching credential's identity.

type CodexOAuthTokenResult

type CodexOAuthTokenResult struct {
	AccessToken  string
	RefreshToken string
	ExpiresAt    time.Time
}

func ExchangeCodexAuthorizationCode

func ExchangeCodexAuthorizationCode(ctx context.Context, code string, verifier string) (*CodexOAuthTokenResult, error)

func ExchangeCodexAuthorizationCodeWithProxy

func ExchangeCodexAuthorizationCodeWithProxy(ctx context.Context, code string, verifier string, proxyURL string) (*CodexOAuthTokenResult, error)

func RefreshCodexOAuthToken

func RefreshCodexOAuthToken(ctx context.Context, refreshToken string) (*CodexOAuthTokenResult, error)

func RefreshCodexOAuthTokenWithProxy

func RefreshCodexOAuthTokenWithProxy(ctx context.Context, refreshToken string, proxyURL string) (*CodexOAuthTokenResult, error)

type CodexQuotaKeyDiagnostic

type CodexQuotaKeyDiagnostic struct {
	Index      int    `json:"-"`
	ReasonCode string `json:"-"`
	ResetAt    int64  `json:"-"`
	ObservedAt int64  `json:"-"`
	Source     string `json:"-"`
}

Diagnostic records contain only key indexes and normalized routing facts. Raw keys, provider account IDs and non-reversible identity references never leave this service boundary or appear in default JSON/debug output.

type CodexQuotaRoutingDiagnostic

type CodexQuotaRoutingDiagnostic struct {
	ChannelID      int                       `json:"-"`
	ChannelEnabled bool                      `json:"-"`
	HasEligibleKey bool                      `json:"-"`
	RouteEligible  bool                      `json:"-"`
	AsOf           int64                     `json:"-"`
	Keys           []CodexQuotaKeyDiagnostic `json:"-"`
	// contains filtered or unexported fields
}

func InspectCodexQuotaRouting

func InspectCodexQuotaRouting(ctx context.Context, channel *model.Channel) (*CodexQuotaRoutingDiagnostic, error)

InspectCodexQuotaRouting uses exactly the same account eligibility decision as ordinary relay routing. It is DB-only: no upstream request, key rotation, polling cursor, channel status or quota marker is changed by a read.

func (CodexQuotaRoutingDiagnostic) GoString

func (d CodexQuotaRoutingDiagnostic) GoString() string

func (CodexQuotaRoutingDiagnostic) String

type DurableTerminalEvidence

type DurableTerminalEvidence struct {
	ID      string
	Hash    string
	Version int
}

DurableSettleTaskOnComplete bridges async task completion to the durable accounting settlement system (T3). If the task is associated with a durable TaskSubmissionOperation, it executes SettleTaskQuotaReservation atomically and transitions the operation to succeeded.

Returns (handled=true, nil) if processed by durable accounting. Returns (handled=false, nil) if the task is not backed by a durable operation (caller should use legacy settlement).

type FreeFunding

type FreeFunding struct{}

func (*FreeFunding) PreConsume

func (*FreeFunding) PreConsume(int) error

func (*FreeFunding) Refund

func (*FreeFunding) Refund() error

func (*FreeFunding) Settle

func (*FreeFunding) Settle(int) error

func (*FreeFunding) Source

func (*FreeFunding) Source() string

type FundingSource

type FundingSource interface {
	// Source 返回资金来源标识:"wallet"、"subscription" 或 "free"
	Source() string
	// PreConsume 从该资金来源预扣 amount 额度
	PreConsume(amount int) error
	// Settle 根据差额调整资金来源(正数补扣,负数退还)
	Settle(delta int) error
	// Refund 退还所有预扣费
	Refund() error
}

FundingSource 抽象了预扣费的资金来源。

type HTTPTransportPolicy

type HTTPTransportPolicy struct {
	Protocol string // dto.HTTPProtocolAuto or dto.HTTPProtocolHTTP1
	Shards   int    // 1..dto.MaxHTTP2ConnectionShards
}

HTTPTransportPolicy is the runtime-normalized outbound HTTP transport policy for a channel. Unknown or out-of-range stored values are clamped safely.

func NormalizeHTTPTransportPolicy

func NormalizeHTTPTransportPolicy(settings dto.ChannelSettings) HTTPTransportPolicy

NormalizeHTTPTransportPolicy converts channel settings into a safe runtime policy. Invalid stored values never panic; they clamp to defaults and warn once per bad value.

func (HTTPTransportPolicy) String

func (p HTTPTransportPolicy) String() string

type LogCleanupPayload

type LogCleanupPayload struct {
	TargetTimestamp int64 `json:"target_timestamp"`
	BatchSize       int   `json:"batch_size"`
}

type LogCleanupResult

type LogCleanupResult struct {
	DeletedCount int64    `json:"deleted_count"`
	SkippedCount int64    `json:"skipped_count,omitempty"`
	Errors       []string `json:"errors,omitempty"`
}

type LogCleanupState

type LogCleanupState struct {
	Total     int64    `json:"total"`
	Processed int64    `json:"processed"`
	Progress  int      `json:"progress"`
	Remaining int64    `json:"remaining"`
	Skipped   int64    `json:"skipped,omitempty"`
	Errors    []string `json:"errors,omitempty"`
}

type LogProjectionBackfillBatchResult

type LogProjectionBackfillBatchResult struct {
	Phase                 string `json:"phase"`
	Status                string `json:"status"`
	Processed             int    `json:"processed"`
	LastID                int    `json:"last_id"`
	LastEventID           string `json:"last_event_id"`
	QuarantinedEvents     int64  `json:"quarantined_events,omitempty"`
	MaterializeMutationID string `json:"materialize_mutation_id,omitempty"`
	MaterializeStatus     string `json:"materialize_status,omitempty"`
	NoOp                  bool   `json:"no_op,omitempty"`
}

func RunLogProjectionBackfillBatch

func RunLogProjectionBackfillBatch(ctx context.Context, mainDB *gorm.DB, logDB *gorm.DB, taskID string, runnerID string, fenceToken int64, limit int) (LogProjectionBackfillBatchResult, error)

type LoginSessionView

type LoginSessionView struct {
	SID          string `json:"sid"`
	Current      bool   `json:"current"`
	LoginMethod  string `json:"login_method"`
	IP           string `json:"ip"`
	UserAgent    string `json:"user_agent"`
	CreatedAt    int64  `json:"created_at"`
	LastActiveAt int64  `json:"last_active_at"`
	ExpiresAt    int64  `json:"expires_at"`
}

func ListLoginSessions

func ListLoginSessions(userID int, currentSID string) ([]LoginSessionView, error)

type ManualAuditResolutionInput

type ManualAuditResolutionInput struct {
	DB             *gorm.DB
	OperationID    int64
	AuditCommandID string // 1-48 characters
	OperatorUserID int
	ReasonCode     string
	TargetStatus   model.TaskSubmissionOperationStatus // "rejected" or "canceled"
	BillingContext model.TaskBillingContext
}

ManualAuditResolutionInput defines the parameters for administrator manual audit resolution.

type ManualAuditResolutionResult

type ManualAuditResolutionResult struct {
	Operation      *model.TaskSubmissionOperation
	Attempt        *model.TaskSubmissionAttempt
	BillingEvent   *model.TaskBillingEvent
	ReleaseReceipt *model.QuotaMutationReceipt
}

ManualAuditResolutionResult holds the result of a manual audit resolution.

func ResolveOperationManualAudit

func ResolveOperationManualAudit(ctx context.Context, input ManualAuditResolutionInput) (*ManualAuditResolutionResult, error)

ResolveOperationManualAudit is the package-level handler for manual audit resolution.

type ModelHistoryModel

type ModelHistoryModel struct {
	Name   string `json:"name"`
	Vendor string `json:"vendor"`
	Total  int64  `json:"total"`
}

type ModelHistoryPoint

type ModelHistoryPoint struct {
	Ts     string `json:"ts"`
	Label  string `json:"label"`
	Model  string `json:"model"`
	Vendor string `json:"vendor"`
	Tokens int64  `json:"tokens"`
}

type ModelHistorySeries

type ModelHistorySeries struct {
	Points  []ModelHistoryPoint `json:"points"`
	Models  []ModelHistoryModel `json:"models"`
	Buckets int                 `json:"buckets"`
}

type OpenAIOfficialModelPrice

type OpenAIOfficialModelPrice struct {
	Model        string                    `json:"model"`
	SourceLabel  string                    `json:"source_label"`
	ShortContext OpenAIOfficialTokenRates  `json:"short_context"`
	LongContext  *OpenAIOfficialTokenRates `json:"long_context"`
}

type OpenAIOfficialPriceSnapshot

type OpenAIOfficialPriceSnapshot struct {
	SourceURL     string                     `json:"source_url"`
	FetchedAt     int64                      `json:"fetched_at"`
	ContentSHA256 string                     `json:"content_sha256"`
	Currency      string                     `json:"currency"`
	UnitTokens    int                        `json:"unit_tokens"`
	ServiceTier   string                     `json:"service_tier"`
	Scope         string                     `json:"scope"`
	Models        []OpenAIOfficialModelPrice `json:"models"`
	// contains filtered or unexported fields
}

func FetchOpenAIOfficialPricing

func FetchOpenAIOfficialPricing(ctx context.Context) (*OpenAIOfficialPriceSnapshot, error)

func FreezeOpenAIPriceSource

func FreezeOpenAIPriceSource(ctx context.Context, db *gorm.DB, snapshot *OpenAIOfficialPriceSnapshot) (*OpenAIOfficialPriceSnapshot, error)

FreezeOpenAIPriceSource accepts only source material retained by the server fetcher, not client-posted rates or a mutation of the displayed model list.

func LoadFrozenOpenAIPriceSource

func LoadFrozenOpenAIPriceSource(ctx context.Context, db *gorm.DB, digest string) (*OpenAIOfficialPriceSnapshot, error)

type OpenAIOfficialTokenRates

type OpenAIOfficialTokenRates struct {
	Input       string  `json:"input_usd_per_million"`
	CachedInput *string `json:"cached_input_usd_per_million"`
	CacheWrite  *string `json:"cache_write_usd_per_million"`
	Output      string  `json:"output_usd_per_million"`
}

Decimal strings preserve the source precision. Null means not quoted by the source, not a zero-price capability or a guessed cache multiplier.

type OpenAIPriceCheckDiffRow

type OpenAIPriceCheckDiffRow struct {
	ModelSHA256             string                           `json:"model_sha256"`
	Model                   string                           `json:"model"`
	ModelNameTruncated      bool                             `json:"model_name_truncated"`
	Change                  string                           `json:"change"`
	CurrentMode             string                           `json:"current_mode"`
	CurrentExpressionSHA256 string                           `json:"current_expression_sha256"`
	Candidate               *OpenAIPricePublicationCandidate `json:"candidate"`
	Locked                  bool                             `json:"locked"`
	Eligible                bool                             `json:"eligible"`
	PendingReview           bool                             `json:"pending_review"`
}

type OpenAIPriceCheckHandler

type OpenAIPriceCheckHandler struct{}

OpenAIPriceCheckHandler reuses the scheduler's active key, DB lease and completion-to-start cadence. Manual checks run even while scheduling is off.

func (OpenAIPriceCheckHandler) Enabled

func (OpenAIPriceCheckHandler) Enabled() bool

func (OpenAIPriceCheckHandler) Interval

func (OpenAIPriceCheckHandler) NewPayload

func (OpenAIPriceCheckHandler) NewPayload() any

func (OpenAIPriceCheckHandler) Run

func (OpenAIPriceCheckHandler) Run(ctx context.Context, task *model.SystemTask, runnerID string)

func (OpenAIPriceCheckHandler) Type

type OpenAIPriceCheckStatus

type OpenAIPriceCheckStatus struct {
	Enabled             bool   `json:"enabled"`
	IntervalSeconds     int64  `json:"interval_seconds"`
	StaleAfterSeconds   int64  `json:"stale_after_seconds"`
	Stale               bool   `json:"stale"`
	LastAttemptAt       int64  `json:"last_attempt_at"`
	LastAttemptStatus   string `json:"last_attempt_status"`
	LastTaskID          string `json:"last_task_id"`
	LastErrorCode       string `json:"last_error_code"`
	LastSuccessAt       int64  `json:"last_success_at"`
	NextCheckAt         int64  `json:"next_check_at"`
	SourceSHA256        string `json:"source_sha256"`
	SourceFetchedAt     int64  `json:"source_fetched_at"`
	PendingSourceSHA256 string `json:"pending_source_sha256"`
	ExpectedDigest      string `json:"expected_digest"`
	Revision            int64  `json:"revision"`
	DiffTotal           int    `json:"diff_total"`
	DiffTruncated       bool   `json:"diff_truncated"`
	DiffReviewRequired  bool   `json:"diff_review_required"`
	// Counts apply only to the returned rows. Truncation always leaves review
	// pending; it must never be presented as a complete/no-change comparison.
	DiffCounts map[string]int            `json:"diff_counts"`
	Diff       []OpenAIPriceCheckDiffRow `json:"diff"`
}

func ReadOpenAIPriceCheckStatus

func ReadOpenAIPriceCheckStatus(ctx context.Context, now int64) (*OpenAIPriceCheckStatus, error)

type OpenAIPricePublicationCandidate

type OpenAIPricePublicationCandidate struct {
	Model                      string `json:"model"`
	SourceSHA256               string `json:"source_sha256"`
	Expression                 string `json:"expression"`
	ExpressionSHA256           string `json:"expression_sha256"`
	ShortContextMaxInputTokens int    `json:"short_context_max_input_tokens"`
	ServiceTier                string `json:"service_tier"`
	Scope                      string `json:"scope"`
}

A candidate is only a proposed administrator tariff. Constructing it does not publish prices, change options, or establish an actual upstream bill.

func BuildOpenAIPricePublicationCandidate

func BuildOpenAIPricePublicationCandidate(source *OpenAIOfficialPriceSnapshot, modelName string) (*OpenAIPricePublicationCandidate, error)

BuildOpenAIPricePublicationCandidate deliberately supports only the current explicitly documented context contract and bare, fully quoted text models. New source layouts/qualifiers require reviewed support, never inference from a column heading or client-posted price. Applicability must also be enforced at admission and settlement before any candidate becomes an effective price.

type OpenAITextCostUsage

type OpenAITextCostUsage struct {
	InputTokens      *int
	OutputTokens     *int
	CacheReadTokens  *int
	CacheWriteTokens *int
	ReasoningTokens  *int
	Reported         bool
	TextOnly         bool
	ContextQualified bool
}

All mandatory counters are optional at the type boundary so a missing report is not converted into an explicit zero. Callers must also establish text-only usage and the applicability of the selected context profile.

type OpenAITextSourceCost

type OpenAITextSourceCost struct {
	Model               string `json:"model"`
	ContextProfile      string `json:"context_profile"`
	SourceSHA256        string `json:"source_sha256"`
	Currency            string `json:"currency"`
	Scope               string `json:"scope"`
	UncachedInputTokens int    `json:"uncached_input_tokens"`
	CacheReadTokens     int    `json:"cache_read_tokens"`
	CacheWriteTokens    int    `json:"cache_write_tokens"`
	OutputTokens        int    `json:"output_tokens"`
	UncachedInputUSD    string `json:"uncached_input_usd"`
	CacheReadUSD        string `json:"cache_read_usd"`
	CacheWriteUSD       string `json:"cache_write_usd"`
	OutputUSD           string `json:"output_usd"`
	TotalUSD            string `json:"total_usd"`
	CacheHitNumerator   int    `json:"cache_hit_numerator"`
	CacheHitDenominator int    `json:"cache_hit_denominator"`
}

func CalculateFrozenOpenAITextCost

func CalculateFrozenOpenAITextCost(ctx context.Context, db *gorm.DB, digest, modelName, profile string, usage OpenAITextCostUsage) (*OpenAITextSourceCost, error)

func CalculateOpenAITextSourceCost

func CalculateOpenAITextSourceCost(snapshot *OpenAIOfficialPriceSnapshot, modelName, profile string, usage OpenAITextCostUsage) (*OpenAITextSourceCost, error)

CalculateOpenAITextSourceCost computes source USD, not wallet quota or an effective administrator tariff. It never publishes/applies this source.

type OptionDiagnosticAliasGroup

type OptionDiagnosticAliasGroup struct {
	Name            string   `json:"name"`
	Keys            []string `json:"keys"`
	Status          string   `json:"status"`
	EffectiveSource string   `json:"effective_source"`
}

type OptionDiagnosticCoverage

type OptionDiagnosticCoverage struct {
	RowsScanned        int      `json:"rows_scanned"`
	RowLimit           int      `json:"row_limit"`
	KeyParseCapChars   int      `json:"key_parse_cap_chars"`
	ValueParseCapChars int      `json:"value_parse_cap_chars"`
	Complete           bool     `json:"complete"`
	IncompleteReasons  []string `json:"incomplete_reasons"`
}

type OptionDiagnosticItem

type OptionDiagnosticItem struct {
	Key             string `json:"key,omitempty"`
	KeyRedacted     bool   `json:"key_redacted,omitempty"`
	KeyClass        string `json:"key_class"`
	ValueState      string `json:"value_state"`
	Validation      string `json:"validation,omitempty"`
	EffectiveSource string `json:"effective_source,omitempty"`
	LoadOrder       string `json:"load_order,omitempty"`
}

type OptionDiagnosticSummary

type OptionDiagnosticSummary struct {
	ByKeyClass    map[string]int `json:"by_key_class"`
	ByValueState  map[string]int `json:"by_value_state"`
	ByValidation  map[string]int `json:"by_validation"`
	ByAliasStatus map[string]int `json:"by_alias_status"`
}

type OptionDiagnostics

type OptionDiagnostics struct {
	SchemaVersion string                       `json:"schema_version"`
	Source        string                       `json:"source"`
	Mode          string                       `json:"mode"`
	Coverage      OptionDiagnosticCoverage     `json:"coverage"`
	Summary       OptionDiagnosticSummary      `json:"summary"`
	Items         []OptionDiagnosticItem       `json:"items"`
	AliasGroups   []OptionDiagnosticAliasGroup `json:"alias_groups"`
}

func GetOptionDiagnostics

func GetOptionDiagnostics(includeValid bool) (*OptionDiagnostics, error)

GetOptionDiagnostics builds a metadata-only snapshot. includeValid controls whether entries without a stable diagnostic finding are returned; values are never copied into the response, errors, or logs.

type OptionRemediationApplyResponse

type OptionRemediationApplyResponse struct {
	SchemaVersion string                          `json:"schema_version"`
	Mode          string                          `json:"mode"`
	Summary       OptionRemediationApplySummary   `json:"summary"`
	Items         []model.OptionRemediationResult `json:"items"`
}

func ApplyOptionRemediations

func ApplyOptionRemediations(operatorUserId int, items []model.OptionRemediationApplyItem) (*OptionRemediationApplyResponse, error)

ApplyOptionRemediations applies caller-claimed dry-run action instances. The model rebuilds every judgment and enforces the per-row value-hash CAS; results are per row and already value-free.

type OptionRemediationApplySummary

type OptionRemediationApplySummary struct {
	Items          int `json:"items"`
	Applied        int `json:"applied"`
	Skipped        int `json:"skipped"`
	Failed         int `json:"failed"`
	AlreadyApplied int `json:"already_applied"`
}

type OptionRemediationDryRun

type OptionRemediationDryRun struct {
	SchemaVersion string                          `json:"schema_version"`
	Mode          string                          `json:"mode"`
	Coverage      model.OptionRemediationCoverage `json:"coverage"`
	Summary       OptionRemediationDryRunSummary  `json:"summary"`
	Items         []OptionRemediationDryRunItem   `json:"items"`
}

func DryRunOptionRemediations

func DryRunOptionRemediations(keys []string, includeAll bool, actions []string) (*OptionRemediationDryRun, error)

DryRunOptionRemediations previews whitelist judgments without writing anything: no options row, no OptionMap/runtime mutation, no registry row. Explicit keys return one item per requested key (plus their pair partners); a bounded full snapshot returns only rows with a finding or a non-keep decision and reports the rest in the summary.

type OptionRemediationDryRunItem

type OptionRemediationDryRunItem struct {
	Key              string `json:"key,omitempty"`
	KeyRedacted      bool   `json:"key_redacted,omitempty"`
	Action           string `json:"action,omitempty"`
	Decision         string `json:"decision"`
	Reason           string `json:"reason"`
	Risk             string `json:"risk,omitempty"`
	ValueState       string `json:"value_state"`
	OldValueHash     string `json:"old_value_hash,omitempty"`
	NewValueHash     string `json:"new_value_hash,omitempty"`
	RuntimeValueHash string `json:"runtime_value_hash,omitempty"`
	RuntimeMatchesDB bool   `json:"runtime_matches_db"`
}

OptionRemediationDryRunItem is the metadata-only preview of one planned judgment: hashes, decision, and runtime conflict markers. Values are never copied into this layer, matching the diagnostics privacy contract.

type OptionRemediationDryRunSummary

type OptionRemediationDryRunSummary struct {
	Planned    int            `json:"planned"`
	Returned   int            `json:"returned"`
	ByDecision map[string]int `json:"by_decision"`
	ByAction   map[string]int `json:"by_action"`
}

type PricePublicationPreview

type PricePublicationPreview struct {
	SourceSHA256   string                       `json:"source_sha256"`
	ExpectedDigest string                       `json:"expected_digest"`
	Revision       int64                        `json:"revision"`
	Rows           []PricePublicationPreviewRow `json:"rows"`
}

func PreviewOpenAIPricePublication

func PreviewOpenAIPricePublication(ctx context.Context, digest string) (*PricePublicationPreview, error)

type PricePublicationPreviewRow

type PricePublicationPreviewRow struct {
	Model             string                           `json:"model"`
	CurrentMode       string                           `json:"current_mode"`
	CurrentExpression string                           `json:"current_expression"`
	Locked            bool                             `json:"locked"`
	Candidate         *OpenAIPricePublicationCandidate `json:"candidate"`
	Eligible          bool                             `json:"eligible"`
}

type PricePublicationRequest

type PricePublicationRequest struct {
	ID             string                      `json:"id"`
	ExpectedDigest string                      `json:"expected_digest"`
	Action         string                      `json:"action"`
	SourceSHA256   string                      `json:"source_sha256,omitempty"`
	RollbackOf     string                      `json:"rollback_of,omitempty"`
	Models         []PricePublicationSelection `json:"models,omitempty"`
	Confirmed      bool                        `json:"confirmed"`
}

type PricePublicationSelection

type PricePublicationSelection struct {
	Model  string `json:"model"`
	Locked *bool  `json:"locked"`
}

type Provider

type Provider string

Provider 定义模型厂商大类

const (
	OpenAI  Provider = "openai"  // 代表 GPT-3.5, GPT-4, GPT-4o
	Gemini  Provider = "gemini"  // 代表 Gemini 1.0, 1.5 Pro/Flash
	Claude  Provider = "claude"  // 代表 Claude 3, 3.5 Sonnet
	Unknown Provider = "unknown" // 兜底默认
)

type ProviderVerifiedResolutionInput

type ProviderVerifiedResolutionInput struct {
	DB                  *gorm.DB
	OperationID         int64
	ProviderStatus      string // "accepted" or "rejected"
	ProviderOperationID string
	UpstreamRequestID   string
	ReasonCode          string
	BillingContext      model.TaskBillingContext
	TaskPlatform        string
	TaskAction          string
	ActualQuota         *int64
	EvidenceID          string
	EvidenceHash        string
	EvidenceVersion     int
}

ProviderVerifiedResolutionInput defines the parameters for upstream-verified task resolution.

type ProviderVerifiedResolutionResult

type ProviderVerifiedResolutionResult struct {
	Operation      *model.TaskSubmissionOperation
	Attempt        *model.TaskSubmissionAttempt
	Task           *model.Task
	ReleaseReceipt *model.QuotaMutationReceipt
}

ProviderVerifiedResolutionResult holds the result of a provider-verified resolution.

func ResolveOperationProviderVerified

func ResolveOperationProviderVerified(ctx context.Context, input ProviderVerifiedResolutionInput) (*ProviderVerifiedResolutionResult, error)

ResolveOperationProviderVerified is the package-level handler for upstream-verified resolution.

type QuotaAnalysis

type QuotaAnalysis struct {
	DefaultMethod       string           `json:"default_method"`
	RateWindowSeconds   int64            `json:"rate_window_seconds"`
	WindowStart         int64            `json:"window_start"`
	WindowEnd           int64            `json:"window_end"`
	AsOf                int64            `json:"as_of"`
	EWMAHalfLifeSeconds int64            `json:"ewma_half_life_seconds"`
	Complete            bool             `json:"complete"`
	Methods             QuotaRateMethods `json:"methods"`
}

QuotaAnalysis exposes all supported methods together so changing the presentation method does not require another history query.

func AnalyzeQuotaConsumption

func AnalyzeQuotaConsumption(consumption QuotaConsumptionResult, windowStart, windowEnd, halfLifeSeconds int64) QuotaAnalysis

AnalyzeQuotaConsumption calculates rates only from valid raw intervals produced by DeriveQuotaConsumption. A continuity boundary discards the preceding segment, preventing reset, recovery, failure, gap, or baseline changes from influencing the current estimate.

type QuotaConsumptionObservation

type QuotaConsumptionObservation struct {
	Snapshot                 model.ChannelQuotaSnapshot
	Consumption              *float64
	RatePerMinute            *float64
	AvailableChangePerMinute *float64
	ObservedSeconds          int64
	Reset                    bool
	Recovery                 bool
	Gap                      bool
	BaselineChange           bool
	ContinuityBreak          bool
}

QuotaConsumptionObservation attaches interval measurements to their ending observation. It does not interpolate provider data into unobserved minutes.

type QuotaConsumptionResult

type QuotaConsumptionResult struct {
	Observations []QuotaConsumptionObservation
	Summary      QuotaConsumptionSummary
}

func DeriveQuotaConsumption

func DeriveQuotaConsumption(snapshots []model.ChannelQuotaSnapshot, unit string) QuotaConsumptionResult

DeriveQuotaConsumption is the sole consumption/rate calculation used by the history endpoint and the cross-channel summary. A reset, failure, changed baseline, or unusually long sampling gap cannot create a charge or recovery.

type QuotaConsumptionSummary

type QuotaConsumptionSummary struct {
	Observed             *float64 `json:"observed,omitempty"`
	Basis                string   `json:"basis"`
	PairCount            int      `json:"pair_count"`
	ObservedSeconds      int64    `json:"observed_seconds"`
	AverageRatePerMinute *float64 `json:"average_rate_per_minute,omitempty"`
	PeakRatePerMinute    *float64 `json:"peak_rate_per_minute,omitempty"`
	PeakRateObservedAt   *int64   `json:"peak_rate_observed_at,omitempty"`
	ResetBoundaries      int      `json:"reset_boundaries"`
	RecoveryCount        int      `json:"recovery_count"`
	InterruptedCount     int      `json:"interrupted_count"`
	GapCount             int      `json:"gap_count"`
	BaselineChangeCount  int      `json:"baseline_change_count"`
	Unit                 string   `json:"unit,omitempty"`
	Allocation           string   `json:"allocation"`
}

type QuotaETA

type QuotaETA struct {
	Outcome              string `json:"outcome"`
	EstimatedDepletionAt *int64 `json:"estimated_depletion_at,omitempty"`
	SecondsToDepletion   *int64 `json:"seconds_to_depletion,omitempty"`
	ResetAt              *int64 `json:"reset_at,omitempty"`
	SecondsUntilReset    *int64 `json:"seconds_until_reset,omitempty"`
}

QuotaETA describes whether an observed consumption rate would exhaust the current provider quota before its next reported reset. A missing ResetAt means no future reset was reported, so a finite depletion remains the first known boundary.

type QuotaInfo

type QuotaInfo struct {
	InputDetails  TokenDetails
	OutputDetails TokenDetails
	ModelName     string
	UsePrice      bool
	ModelPrice    float64
	ModelRatio    float64
	GroupRatio    float64
	// contains filtered or unexported fields
}

type QuotaOverviewPoint

type QuotaOverviewPoint struct {
	Timestamp       int64    `json:"timestamp"`
	Available       *float64 `json:"available"`
	ContinuityBreak bool     `json:"continuity_break"`
}

func BuildQuotaOverviewPoints

func BuildQuotaOverviewPoints(observations []QuotaConsumptionObservation, limit int) []QuotaOverviewPoint

BuildQuotaOverviewPoints creates a bounded remaining-quota projection from already loaded observations. Contiguous index buckets retain their latest sample and OR every continuity marker, so compression never draws across a failure, reset, recovery, gap, or baseline change and never interpolates an unobserved value.

type QuotaRateMethodAnalysis

type QuotaRateMethodAnalysis struct {
	RatePerMinute   *float64 `json:"rate_per_minute"`
	RatePerHour     *float64 `json:"rate_per_hour"`
	Coverage        float64  `json:"coverage"`
	ObservedSeconds int64    `json:"observed_seconds"`
	IntervalCount   int      `json:"interval_count"`
	ObservedAt      *int64   `json:"observed_at"`
	ETA             QuotaETA `json:"eta"`
}

QuotaRateMethodAnalysis is one independently usable rate estimate. Coverage is the fraction of the requested rate window backed by valid raw intervals; it is never inferred from display buckets or sampler configuration.

type QuotaRateMethods

type QuotaRateMethods struct {
	LatestInterval QuotaRateMethodAnalysis `json:"latest_interval"`
	ObservedWindow QuotaRateMethodAnalysis `json:"observed_window"`
	EWMA           QuotaRateMethodAnalysis `json:"ewma"`
}

type RankedModel

type RankedModel struct {
	Rank         int     `json:"rank"`
	PreviousRank *int    `json:"previous_rank,omitempty"`
	ModelName    string  `json:"model_name"`
	Vendor       string  `json:"vendor"`
	VendorIcon   string  `json:"vendor_icon,omitempty"`
	Category     string  `json:"category"`
	TotalTokens  int64   `json:"total_tokens"`
	Share        float64 `json:"share"`
	GrowthPct    float64 `json:"growth_pct"`
}

type RankedVendor

type RankedVendor struct {
	Rank        int     `json:"rank"`
	Vendor      string  `json:"vendor"`
	VendorIcon  string  `json:"vendor_icon,omitempty"`
	TotalTokens int64   `json:"total_tokens"`
	Share       float64 `json:"share"`
	GrowthPct   float64 `json:"growth_pct"`
	ModelsCount int     `json:"models_count"`
	TopModel    string  `json:"top_model"`
}

type RankingMover

type RankingMover struct {
	ModelName   string  `json:"model_name"`
	Vendor      string  `json:"vendor"`
	VendorIcon  string  `json:"vendor_icon,omitempty"`
	RankDelta   int     `json:"rank_delta"`
	CurrentRank int     `json:"current_rank"`
	GrowthPct   float64 `json:"growth_pct"`
}

type RankingsResponse

type RankingsResponse struct {
	Models             []RankedModel      `json:"models"`
	Vendors            []RankedVendor     `json:"vendors"`
	TopMovers          []RankingMover     `json:"top_movers"`
	TopDroppers        []RankingMover     `json:"top_droppers"`
	ModelsHistory      ModelHistorySeries `json:"models_history"`
	VendorShareHistory VendorShareSeries  `json:"vendor_share_history"`
}

func GetRankingsSnapshot

func GetRankingsSnapshot(period string) (*RankingsResponse, error)

type RelayAttempt

type RelayAttempt struct {
	CooldownSeconds int    `json:"cooldown_seconds,omitempty"`
	ChannelID       int    `json:"channel_id"`
	KeyIndex        int    `json:"key_index"`
	Target          string `json:"upstream_model"`
	Outcome         string `json:"outcome"`
	Status          int    `json:"status,omitempty"`
}

type RelayFailover

type RelayFailover struct {
	Target string

	Attempts         []RelayAttempt `json:"attempts"`
	ResponseRefused  bool
	DispatchPossible bool
	// contains filtered or unexported fields
}

RelayFailover is request-local evidence, not a second scheduler or ledger. Credentials never enter logs; their digests only prevent retrying the same credential after a definite refusal, including after key reordering.

func BeginRelayFailover

func BeginRelayFailover(c *gin.Context) *RelayFailover

func RelayFailoverFromContext

func RelayFailoverFromContext(ctx context.Context) *RelayFailover

func (*RelayFailover) Close

func (state *RelayFailover) Close()

func (*RelayFailover) FinishAttempt

func (state *RelayFailover) FinishAttempt(c *gin.Context, outcome string, status int, exclude bool)

func (*RelayFailover) StartAttempt

func (state *RelayFailover) StartAttempt(c *gin.Context)

type RetryParam

type RetryParam struct {
	Ctx         *gin.Context
	TokenGroup  string
	ModelName   string
	RequestPath string
	Retry       *int
	// contains filtered or unexported fields
}

func (*RetryParam) GetRetry

func (p *RetryParam) GetRetry() int

func (*RetryParam) IncreaseRetry

func (p *RetryParam) IncreaseRetry()

func (*RetryParam) ResetRetryNextTry

func (p *RetryParam) ResetRetryNextTry()

func (*RetryParam) SetRetry

func (p *RetryParam) SetRetry(retry int)

type ScheduledSystemTaskHandler

type ScheduledSystemTaskHandler interface {
	SystemTaskHandler
	Enabled() bool
	Interval() time.Duration
	NewPayload() any
}

ScheduledSystemTaskHandler is a SystemTaskHandler that the scheduler also creates periodically when enabled and the configured interval has elapsed since the last run.

type SelfUseFunding

type SelfUseFunding struct{}

SelfUseFunding is metered, not free: pricing/usage/Key reservations remain active, while the captured policy does not consume a user wallet.

func (*SelfUseFunding) PreConsume

func (*SelfUseFunding) PreConsume(int) error

func (*SelfUseFunding) Refund

func (*SelfUseFunding) Refund() error

func (*SelfUseFunding) Settle

func (*SelfUseFunding) Settle(int) error

func (*SelfUseFunding) Source

func (*SelfUseFunding) Source() string

type StartScheduledSystemTaskHandler

type StartScheduledSystemTaskHandler interface {
	ScheduledSystemTaskHandler
	ScheduleFromStart() bool
}

StartScheduledSystemTaskHandler opts a periodic sampler into start-to-start cadence. CreatedAt is the durable schedule anchor (tasks are claimed on the same scheduler pass); active rows still prevent overlapping runs. Other scheduled tasks keep their existing completion-to-start cooldown.

type SubscriptionFunding

type SubscriptionFunding struct {

	// 以下字段在 PreConsume 成功后填充,供 RelayInfo 同步使用
	AmountTotal     int64
	AmountUsedAfter int64
	PlanId          int
	PlanTitle       string
	// contains filtered or unexported fields
}

func (*SubscriptionFunding) PreConsume

func (s *SubscriptionFunding) PreConsume(_ int) error

func (*SubscriptionFunding) Refund

func (s *SubscriptionFunding) Refund() error

func (*SubscriptionFunding) Settle

func (s *SubscriptionFunding) Settle(delta int) error

func (*SubscriptionFunding) Source

func (s *SubscriptionFunding) Source() string

type SystemInstanceHostInfo

type SystemInstanceHostInfo struct {
	Hostname string `json:"hostname"`
}

type SystemInstanceInfo

type SystemInstanceInfo struct {
	SchemaVersion int                       `json:"schema_version"`
	Node          common.NodeIdentity       `json:"node"`
	Role          SystemInstanceRoleInfo    `json:"role"`
	Runtime       SystemInstanceRuntimeInfo `json:"runtime"`
	Host          SystemInstanceHostInfo    `json:"host"`
	Resources     SystemInstanceResources   `json:"resources,omitempty"`
	Extra         map[string]any            `json:"extra,omitempty"`
}

type SystemInstanceResourceUsage

type SystemInstanceResourceUsage struct {
	UsagePercent float64 `json:"usage_percent"`
}

type SystemInstanceResources

type SystemInstanceResources struct {
	CPU     SystemInstanceResourceUsage  `json:"cpu"`
	Memory  SystemInstanceResourceUsage  `json:"memory"`
	Storage SystemInstanceStorageMetrics `json:"storage"`
}

type SystemInstanceRoleInfo

type SystemInstanceRoleInfo struct {
	IsMaster bool `json:"is_master"`
}

type SystemInstanceRuntimeInfo

type SystemInstanceRuntimeInfo struct {
	Version   string `json:"version"`
	GOOS      string `json:"goos"`
	GOARCH    string `json:"goarch"`
	StartedAt int64  `json:"started_at"`
}

type SystemInstanceStorageMetrics

type SystemInstanceStorageMetrics struct {
	TotalBytes  uint64  `json:"total_bytes"`
	UsedBytes   uint64  `json:"used_bytes"`
	FreeBytes   uint64  `json:"free_bytes"`
	UsedPercent float64 `json:"used_percent"`
}

type SystemTaskHandler

type SystemTaskHandler interface {
	Type() string
	Run(ctx context.Context, task *model.SystemTask, runnerID string)
}

SystemTaskHandler executes a claimed task of a specific type. Run owns the task lifecycle from claim to terminal state: it MUST call model.FinishSystemTask (succeeded/failed) before returning and MUST honor ctx cancellation, which the runner triggers if the per-type lock is lost.

type SystemTaskProgress

type SystemTaskProgress struct {
	Total     int `json:"total"`
	Processed int `json:"processed"`
	Progress  int `json:"progress"`
}

SystemTaskProgress is the state shape used by handlers that report percentage progress (channel test, model update). The frontend reads the progress field (0-100) to render a per-task progress indicator.

type TaskBillingOutboxService

type TaskBillingOutboxService struct {
	WorkerID      string
	LeaseDuration int64 // 默认 60s
	BatchSize     int   // 默认 50
	MaxAttempts   int   // 默认 5
	LogDB         *gorm.DB
	NowFunc       func() int64
}

TaskBillingOutboxService delivers pending and retryable task billing outbox items to the logs table with at-least-once durability and idempotent deduplication.

func NewTaskBillingOutboxService

func NewTaskBillingOutboxService(workerID string) *TaskBillingOutboxService

NewTaskBillingOutboxService creates a new TaskBillingOutboxService with default settings.

func (*TaskBillingOutboxService) ProcessClaimableBatch

func (s *TaskBillingOutboxService) ProcessClaimableBatch(ctx context.Context, db *gorm.DB) (int, error)

ProcessClaimableBatch queries claimable task billing outbox records, claims processing leases via CAS, delivers the records idempotently to the logs sink, and advances their outbox state to delivered or retryable.

type TaskEngineConfig

type TaskEngineConfig struct {
	WorkerID                  string
	StaleDispatchThreshold    time.Duration
	StaleReservationThreshold time.Duration
	BatchSize                 int
	LeaseDuration             time.Duration
	OutboxMaxAttempts         int
	LogDB                     *gorm.DB
}

TaskEngineConfig defines the configuration parameters for TaskEngineRunner.

type TaskEngineReport

type TaskEngineReport struct {
	StaleDispatchingRecovered int           `json:"stale_dispatching_recovered"`
	StaleUnfinishedRecovered  int           `json:"stale_unfinished_recovered"`
	ExpiredBillingReclaimed   int           `json:"expired_billing_reclaimed"`
	OutboxDelivered           int           `json:"outbox_delivered"`
	QuotaProjectionProcessed  int           `json:"quota_projection_processed"`
	Duration                  time.Duration `json:"duration"`
	Errors                    []string      `json:"errors,omitempty"`
}

TaskEngineReport contains the execution metrics and errors from a runner pass.

func (TaskEngineReport) HasErrors

func (r TaskEngineReport) HasErrors() bool

HasErrors returns true if any errors occurred during the runner execution.

func (TaskEngineReport) ToJSON

func (r TaskEngineReport) ToJSON() (string, error)

ToJSON serializes the report to a JSON string using common.Marshal.

type TaskEngineRunner

type TaskEngineRunner struct {
	// contains filtered or unexported fields
}

TaskEngineRunner coordinates background task recovery scans and billing log outbox deliveries.

func NewTaskEngineRunner

func NewTaskEngineRunner(cfg TaskEngineConfig, db *gorm.DB) *TaskEngineRunner

NewTaskEngineRunner creates a new TaskEngineRunner instance with the provided configuration and database.

func (*TaskEngineRunner) Config

func (r *TaskEngineRunner) Config() TaskEngineConfig

Config returns a copy of the configuration used by the runner.

func (*TaskEngineRunner) DB

func (r *TaskEngineRunner) DB() *gorm.DB

DB returns the database handle used by the runner.

func (*TaskEngineRunner) OutboxService

func (r *TaskEngineRunner) OutboxService() *TaskBillingOutboxService

OutboxService returns the underlying TaskBillingOutboxService instance.

func (*TaskEngineRunner) RecoveryWorker

func (r *TaskEngineRunner) RecoveryWorker() *TaskRecoveryWorker

RecoveryWorker returns the underlying TaskRecoveryWorker instance.

func (*TaskEngineRunner) RunOnce

RunOnce executes a single unified engine pass consisting of recovery and outbox delivery, recording metrics and errors in the returned report.

func (*TaskEngineRunner) RunOutboxPass

func (r *TaskEngineRunner) RunOutboxPass(ctx context.Context) (delivered int, err error)

RunOutboxPass executes a single pass delivering claimable task billing log outbox entries.

func (*TaskEngineRunner) RunRecoveryPass

func (r *TaskEngineRunner) RunRecoveryPass(ctx context.Context) (dispatching int, unfinished int, expiredBilling int, errs []error)

RunRecoveryPass executes a single recovery pass across stale dispatching operations, stale unfinished prepared or reserved operations, and expired billing event leases.

type TaskIngressRequest

type TaskIngressRequest struct {
	UserID             int
	TokenID            int
	ChannelID          int
	Provider           string
	OperationKind      string
	OriginTaskPublicID string
	HTTPMethod         string
	Header             http.Header
	ContentType        string
	Body               []byte
	RequestID          string
	EstimatedQuota     int
	FreeModel          bool
	InitialQuotaClamp  *common.QuotaClamp
	BillingContext     model.TaskBillingContext
	Dispatcher         TaskProviderDispatcher
	DB                 *gorm.DB
}

TaskIngressRequest encapsulates the parsed HTTP parameters and payload required to process a durable task submission ingress.

type TaskIngressResult

type TaskIngressResult struct {
	IsReplay  bool
	Response  *dto.TaskOperationResponse
	Conflict  bool
	ErrorCode string
	ErrorMsg  string
}

TaskIngressResult represents the outcome of durable ingress processing.

func ExecuteTaskIngress

func ExecuteTaskIngress(ctx context.Context, req TaskIngressRequest) (*TaskIngressResult, error)

ExecuteTaskIngress executes the complete durable task ingress flow: 1. Validates and extracts the Idempotency-Key from headers via ParseTaskSubmissionProtocol. 2. Selects the canonical fingerprint generator based on Content-Type (JSON, Form, Multipart). 3. Constructs operationCandidate and attemptCandidate, calling CreateOrLoadTaskSubmissionIntent. 4. If an idempotency fingerprint conflict occurs, returns Conflict=true (HTTP 409). 5. If replaying (!intent.Owner), immediately constructs and returns dto.TaskOperationResponse (IsReplay=true). 6. If a new submission (intent.Owner), runs ExecuteTaskSubmissionPipeline (T1 Reserve -> T2 Outbound -> Dispatcher -> T3 Outcome). 7. Constructs and returns the final dto.TaskOperationResponse.

type TaskIngressService

type TaskIngressService struct {
	DB *gorm.DB
}

TaskIngressService provides durable task ingress lifecycle execution.

func NewTaskIngressService

func NewTaskIngressService(db *gorm.DB) *TaskIngressService

NewTaskIngressService creates a new TaskIngressService instance.

func (*TaskIngressService) Execute

Execute executes task ingress on the service instance.

type TaskPollSummary

type TaskPollSummary struct {
	UnfinishedTasks  int `json:"unfinished_tasks"`
	PlatformsScanned int `json:"platforms_scanned"`
	NullTasksFailed  int `json:"null_tasks_failed"`
}

TaskPollSummary is the result recorded on an async_task_poll system task row, summarizing one polling pass.

func RunTaskPollingOnce

func RunTaskPollingOnce(ctx context.Context, report func(processed, total int)) TaskPollSummary

RunTaskPollingOnce performs one async-task (Suno/video) polling pass.

type TaskPollingAdaptor

type TaskPollingAdaptor interface {
	Init(info *relaycommon.RelayInfo)
	FetchTask(baseURL string, key string, body map[string]any, proxy string) (*http.Response, error)
	ParseTaskResult(body []byte) (*relaycommon.TaskInfo, error)
	// AdjustBillingOnComplete 在任务到达终态(成功/失败)时由轮询循环调用。
	// 返回正数触发差额结算(补扣/退还),返回 0 保持预扣费金额不变。
	AdjustBillingOnComplete(task *model.Task, taskResult *relaycommon.TaskInfo) int
}

TaskPollingAdaptor 定义轮询所需的最小适配器接口,避免 service -> relay 的循环依赖

type TaskPollingContext

type TaskPollingContext struct {
	Operation      *model.TaskSubmissionOperation
	Attempt        *model.TaskSubmissionAttempt
	ReserveReceipt *model.QuotaMutationReceipt
	ValidationErr  error
}

TaskPollingContext is the immutable durable metadata loaded for one public task ID. Polling code must use this snapshot instead of re-reading mutable Task billing fields or issuing per-task durable metadata queries.

func LoadTaskPollingContext

func LoadTaskPollingContext(db *gorm.DB, task *model.Task) (*TaskPollingContext, error)

func (*TaskPollingContext) Durable

func (c *TaskPollingContext) Durable() bool

type TaskPollingContextSet

type TaskPollingContextSet struct {
	// contains filtered or unexported fields
}

func LoadTaskPollingContexts

func LoadTaskPollingContexts(db *gorm.DB, tasks []*model.Task) (*TaskPollingContextSet, error)

LoadTaskPollingContexts performs one error-reporting schema capability check and three batch reads. A confirmed pre-durable schema is legacy-only; partial schema and probe/query failures are fail-closed.

func (*TaskPollingContextSet) ForTask

func (set *TaskPollingContextSet) ForTask(task *model.Task) *TaskPollingContext

type TaskPollingSchemaCapability

type TaskPollingSchemaCapability struct {
	Durable bool
}

func InspectTaskPollingSchema

func InspectTaskPollingSchema(db *gorm.DB) (TaskPollingSchemaCapability, error)

type TaskProviderDispatchResult

type TaskProviderDispatchResult struct {
	Status         string // "accepted", "rejected", "unknown"
	ProviderTaskID string
	TaskCandidate  *model.Task
	ErrorCode      string
	ErrorMessage   string
}

TaskProviderDispatchResult is the upstream provider dispatch result.

type TaskProviderDispatcher

type TaskProviderDispatcher func(ctx context.Context, op *model.TaskSubmissionOperation, attempt *model.TaskSubmissionAttempt) (*TaskProviderDispatchResult, error)

TaskProviderDispatcher defines the function signature for dispatching an operation attempt to a provider. This function must execute outside of any database transaction.

type TaskRecoveryWorker

type TaskRecoveryWorker struct {
	WorkerID                  string
	StaleDispatchThreshold    time.Duration
	StaleReservationThreshold time.Duration
	BatchSize                 int
	LeaseDuration             time.Duration
}

TaskRecoveryWorker manages background recovery inspection scans for stale task submissions and expired billing event processing leases.

func NewTaskRecoveryWorker

func NewTaskRecoveryWorker(workerID string) *TaskRecoveryWorker

NewTaskRecoveryWorker constructs a TaskRecoveryWorker with production-safe defaults.

func (*TaskRecoveryWorker) RecoverAccountQuotaRefundFacts

func (w *TaskRecoveryWorker) RecoverAccountQuotaRefundFacts(ctx context.Context, db *gorm.DB) (int, error)

func (*TaskRecoveryWorker) RecoverExpiredBillingEvents

func (w *TaskRecoveryWorker) RecoverExpiredBillingEvents(ctx context.Context, db *gorm.DB) (int, error)

RecoverExpiredBillingEvents scans for TaskBillingEvent records stuck in 'claimed' beyond their lease expiry. It reclaims the lease using model.ReclaimExpiredTaskBillingEvent.

func (*TaskRecoveryWorker) RecoverQuotaProjectionObligations

func (w *TaskRecoveryWorker) RecoverQuotaProjectionObligations(ctx context.Context, db *gorm.DB) (int, error)

RecoverQuotaProjectionObligations replays one due projection batch together with account refund recovery obligations. The shared model gate defaults off.

func (*TaskRecoveryWorker) RecoverStaleDispatching

func (w *TaskRecoveryWorker) RecoverStaleDispatching(ctx context.Context, db *gorm.DB) (int, error)

RecoverStaleDispatching scans for operations stuck in 'dispatching' beyond StaleDispatchThreshold. It strictly enforces fail-closed semantics: atomically moves both operation and attempt to 'submission_unknown' with reason "dispatch_timeout_fail_closed". It NEVER automatically resends or refunds.

func (*TaskRecoveryWorker) RecoverStaleUnfinished

func (w *TaskRecoveryWorker) RecoverStaleUnfinished(ctx context.Context, db *gorm.DB) (int, error)

RecoverStaleUnfinished scans for operations in 'prepared' or 'reserved' older than StaleReservationThreshold. It verifies that no attempt ever entered dispatching (attempt is prepared without dispatch started). If the operation is 'reserved', it releases the reserved quota back to the user/token/subscription via model.ReleaseTaskQuotaReservation and transitions the operation to 'canceled'. If the operation is 'prepared', no reservation exists, so it transitions the operation directly to 'canceled'.

func (*TaskRecoveryWorker) RecoverTerminalObservations

func (w *TaskRecoveryWorker) RecoverTerminalObservations(ctx context.Context, db *gorm.DB) (int, error)

RecoverTerminalObservations applies durable terminal observations and imports only provable historical terminal Task projections. It never recalculates price.

type TaskResolutionService

type TaskResolutionService struct {
	DB *gorm.DB
}

TaskResolutionService provides deterministic resolution for unknown task submission operations.

func NewTaskResolutionService

func NewTaskResolutionService(db *gorm.DB) *TaskResolutionService

NewTaskResolutionService creates a new TaskResolutionService.

func (*TaskResolutionService) ResolveOperationManualAudit

ResolveOperationManualAudit applies administrator manual audit resolution. Parameters: AuditCommandID (1-48 chars), OperatorUserID, ReasonCode, TargetStatus (rejected/canceled). If target is rejected/canceled, reserved quota is refunded via ReleaseTaskQuotaReservation, status is transitioned to rejected/canceled, and an immutable TaskBillingEvent is recorded with resolution_source="manual_audit" and AuditCommandID. Idempotency: Repeating the same AuditCommandID returns the original result. Conflicting parameters are rejected.

func (*TaskResolutionService) ResolveOperationProviderVerified

ResolveOperationProviderVerified resolves submission_unknown or outcome_unknown operations based on upstream provider confirmation: - Accepted: associates a formal Task, transitions attempt & operation CAS to accepted. - Rejected: releases reserved quota via model.ReleaseTaskQuotaReservation and transitions attempt & operation CAS to rejected.

type TaskSubmissionFingerprintInput

type TaskSubmissionFingerprintInput struct {
	TokenID            int
	Protocol           *TaskSubmissionProtocol
	ContentType        string
	Body               []byte
	OriginTaskPublicID string
}

TaskSubmissionFingerprintInput is the pure B2 request-fingerprint boundary. OriginTaskPublicID is required only for video.remix and prohibited for every other v1 operation. Body must be a JSON object; form, multipart, and binary request families require separate future canonicalization contracts.

type TaskSubmissionPipelineInput

type TaskSubmissionPipelineInput struct {
	DB             *gorm.DB
	OperationID    int64
	AttemptID      int64
	UserID         int
	TokenID        int
	ChannelID      int
	Quota          int64
	FreeModel      bool
	BillingSource  string
	SubscriptionID int
	BillingContext model.TaskBillingContext
	Dispatcher     TaskProviderDispatcher
}

TaskSubmissionPipelineInput encapsulates all inputs required to run the submission pipeline.

type TaskSubmissionPipelineResult

type TaskSubmissionPipelineResult struct {
	Operation      *model.TaskSubmissionOperation
	Attempt        *model.TaskSubmissionAttempt
	DispatchResult *TaskProviderDispatchResult
	ReserveReceipt *model.QuotaMutationReceipt
	ReleaseReceipt *model.QuotaMutationReceipt
}

TaskSubmissionPipelineResult contains the final execution result of the submission pipeline.

func ExecuteTaskSubmissionPipeline

func ExecuteTaskSubmissionPipeline(ctx context.Context, input TaskSubmissionPipelineInput) (*TaskSubmissionPipelineResult, error)

ExecuteTaskSubmissionPipeline executes the end-to-end task submission pipeline: 1. T1 Reserve: atomically reserves quota via model.ReserveTaskQuota in a DB transaction. 2. T2 Outbound: atomically starts dispatch via model.StartTaskSubmissionDispatch in a DB transaction. 3. Dispatch: calls dispatcher(ctx, op, attempt) OUTSIDE of any database transaction. 4. T3 Outcome: handles the provider outcome (accepted, rejected, unknown) in a DB transaction:

  • accepted: transitions attempt & operation to accepted, records ProviderTaskID, ensures formal task exists.
  • rejected: releases quota reservation (full refund) and transitions attempt & operation to rejected.
  • unknown: transitions attempt & operation to submission_unknown; retains reserved quota (fail-closed).

type TaskSubmissionProtocol

type TaskSubmissionProtocol struct {
	HTTPMethod         string
	OperationKind      string
	IdempotencyKeyHash string
}

TaskSubmissionProtocol is the stable B2 scope material after a request's client Idempotency-Key has been validated and digested. The caller combines it with the authenticated token ID and a canonical request fingerprint; this pure boundary intentionally does not parse a body, access a database, or decide routing, billing, or dispatch.

func ParseTaskSubmissionProtocol

func ParseTaskSubmissionProtocol(headers http.Header, method, operationKind string) (*TaskSubmissionProtocol, error)

ParseTaskSubmissionProtocol accepts exactly one canonical Idempotency-Key field and binds it to the deployment-scoped task-recovery HMAC. Header names are case-insensitive, but the historical X-Idempotency-Key alias is deliberately rejected: accepting both spellings would make a client-visible idempotency scope ambiguous. On success it removes every actual-casing canonical key from headers, so later request contexts and accidental forwarding cannot retain the raw client key. The returned value and every error contain no raw key.

type TaskSubmissionService

type TaskSubmissionService struct {
	DB *gorm.DB
}

TaskSubmissionService provides task submission dispatch and lifecycle execution.

func NewTaskSubmissionService

func NewTaskSubmissionService(db *gorm.DB) *TaskSubmissionService

NewTaskSubmissionService creates a new TaskSubmissionService instance.

func (*TaskSubmissionService) Execute

Execute runs the complete task submission pipeline.

type TieredResultWrapper

type TieredResultWrapper = billingexpr.TieredResult

TieredResultWrapper wraps billingexpr.TieredResult for use at the service layer.

type TokenDetails

type TokenDetails struct {
	TextTokens  int
	AudioTokens int
}

type ToolSurchargeItem

type ToolSurchargeItem struct {
	Name  string  `json:"name"`
	Count int     `json:"count"`
	Price float64 `json:"price"`
}

ToolSurchargeItem is one billable tool-call line for consume logs.

type UserFundingCapabilities

type UserFundingCapabilities struct {
	Mode                        operation_setting.UserFundingMode `json:"mode"`
	Epoch                       uint64                            `json:"epoch"`
	Ready                       bool                              `json:"ready"`
	CanTopUp                    bool                              `json:"can_top_up"`
	CanRedeem                   bool                              `json:"can_redeem"`
	CanTransferAffiliateRewards bool                              `json:"can_transfer_affiliate_rewards"`
	CanPurchaseSubscription     bool                              `json:"can_purchase_subscription"`
	CanViewFundingHistory       bool                              `json:"can_view_funding_history"`
}

func CurrentUserFundingCapabilities

func CurrentUserFundingCapabilities() UserFundingCapabilities

type UserFundingOrderKind

type UserFundingOrderKind = model.UserFundingOrderKind

type UserFundingSnapshot

type UserFundingSnapshot struct {
	State        model.UserFundingStateSnapshot
	Capabilities UserFundingCapabilities
}

func CurrentUserFundingSnapshot

func CurrentUserFundingSnapshot() UserFundingSnapshot

type UserFundingWebhookAction

type UserFundingWebhookAction = model.UserFundingWebhookAction

type UserFundingWebhookDecision

type UserFundingWebhookDecision = model.UserFundingWebhookDecision

func DecideUserFundingWebhook

func DecideUserFundingWebhook(tradeNo, provider string) (UserFundingWebhookDecision, error)

type VendorSharePoint

type VendorSharePoint struct {
	Ts     string  `json:"ts"`
	Label  string  `json:"label"`
	Vendor string  `json:"vendor"`
	Share  float64 `json:"share"`
	Tokens int64   `json:"tokens"`
}

type VendorShareSeries

type VendorShareSeries struct {
	Points  []VendorSharePoint  `json:"points"`
	Vendors []VendorShareVendor `json:"vendors"`
	Buckets int                 `json:"buckets"`
}

type VendorShareVendor

type VendorShareVendor struct {
	Name  string  `json:"name"`
	Total int64   `json:"total"`
	Share float64 `json:"share"`
}

type WaffoPancakeCatalog

type WaffoPancakeCatalog struct {
	Stores []WaffoPancakeCatalogStore `json:"stores"`
}

func ListWaffoPancakeCatalog

func ListWaffoPancakeCatalog(ctx context.Context, merchantID, privateKey string) (*WaffoPancakeCatalog, error)

ListWaffoPancakeCatalog queries Pancake's GraphQL `stores` for the merchant's stores + onetime products. A successful call also proves the supplied credentials authenticate (doubles as a credential probe).

type WaffoPancakeCatalogProduct

type WaffoPancakeCatalogProduct struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Status string `json:"status"`
}

type WaffoPancakeCatalogStore

type WaffoPancakeCatalogStore struct {
	ID              string                       `json:"id"`
	Name            string                       `json:"name"`
	Status          string                       `json:"status"`
	ProdEnabled     bool                         `json:"prodEnabled"`
	OnetimeProducts []WaffoPancakeCatalogProduct `json:"onetimeProducts"`
}

WaffoPancakeCatalogStore nests its OnetimeProducts so the UI can render a dependent store→product select without a second round-trip.

type WaffoPancakeCheckoutSession

type WaffoPancakeCheckoutSession struct {
	SessionID      string
	CheckoutURL    string
	ExpiresAt      string
	OrderID        string
	Token          string
	TokenExpiresAt string
}

WaffoPancakeCheckoutSession is the response of CreateWaffoPancakeCheckoutSession. CheckoutURL already carries the `#token=...` fragment; Token / TokenExpiresAt are exposed separately for self-service flows driven from MyAPI's own UI.

func CreateWaffoPancakeCheckoutSession

func CreateWaffoPancakeCheckoutSession(ctx context.Context, params *WaffoPancakeCreateSessionParams) (*WaffoPancakeCheckoutSession, error)

CreateWaffoPancakeCheckoutSession creates an Authenticated-mode checkout session: the order is bound to BuyerIdentity (stable per user) so it stays attributable even if the buyer edits the email on Waffo's checkout form. It captures a fresh payment runtime snapshot; order handlers that already captured one should call CreateWaffoPancakeCheckoutSessionWithPaymentConfig so the credentials come from the same generation as the rest of the request.

func CreateWaffoPancakeCheckoutSessionWithCredentials

func CreateWaffoPancakeCheckoutSessionWithCredentials(ctx context.Context, merchantID, privateKey string, params *WaffoPancakeCreateSessionParams) (*WaffoPancakeCheckoutSession, error)

Callers resolve effective legacy/runtime credentials before external I/O.

func CreateWaffoPancakeCheckoutSessionWithPaymentConfig

func CreateWaffoPancakeCheckoutSessionWithPaymentConfig(ctx context.Context, paymentConfig setting.PaymentConfig, params *WaffoPancakeCreateSessionParams) (*WaffoPancakeCheckoutSession, error)

CreateWaffoPancakeCheckoutSessionWithPaymentConfig is CreateWaffoPancakeCheckoutSession with merchant credentials read from the supplied payment runtime snapshot.

type WaffoPancakeCreateSessionParams

type WaffoPancakeCreateSessionParams struct {
	ProductID               string
	BuyerIdentity           string
	PriceSnapshot           *WaffoPancakePriceSnapshot
	BuyerEmail              string
	ExpiresInSeconds        *int
	OrderMerchantExternalID string
}

WaffoPancakeCreateSessionParams is the input to CreateWaffoPancakeCheckoutSession. BuyerIdentity must be stable per user (see WaffoPancakeBuyerIdentityFromUserID). OrderMerchantExternalID = our trade_no; Pancake echoes it back in webhooks.

type WaffoPancakePairResult

type WaffoPancakePairResult struct {
	StoreID     string
	StoreName   string
	ProductID   string
	ProductName string
	OrphanStore bool
}

WaffoPancakePairResult is the response of CreateWaffoPancakePrimaryPair. When OrphanStore is true the store was created but the product wasn't, so the caller can surface a partial-failure message with StoreID.

func CreateWaffoPancakePrimaryPair

func CreateWaffoPancakePrimaryPair(ctx context.Context, merchantID, privateKey, returnURL string) (*WaffoPancakePairResult, error)

CreateWaffoPancakePrimaryPair mints a Store + OnetimeProduct in one round-trip — the canonical "+ Create" entry point. Nothing is persisted to settings; the operator's final Save commits the chosen IDs.

type WaffoPancakePriceSnapshot

type WaffoPancakePriceSnapshot struct {
	Amount      string
	TaxCategory string
}

WaffoPancakePriceSnapshot is the per-session price override sent with checkout.

type WaffoPancakeWebhookData

type WaffoPancakeWebhookData struct {
	// OrderID = Pancake ORD_* (logs); OrderMerchantExternalID = our trade_no (lookup).
	OrderID                       string
	OrderMerchantExternalID       string
	BuyerEmail                    string
	Currency                      string
	Amount                        string
	TaxAmount                     string
	ProductName                   string
	MerchantProvidedBuyerIdentity string
}

type WaffoPancakeWebhookEvent

type WaffoPancakeWebhookEvent struct {
	ID        string
	Timestamp string
	EventType string
	EventID   string
	StoreID   string
	Mode      string
	Data      WaffoPancakeWebhookData
}

WaffoPancakeWebhookEvent mirrors the SDK's WebhookEvent shape using plain strings so controllers don't have to import the SDK package.

func VerifyConfiguredWaffoPancakeWebhook

func VerifyConfiguredWaffoPancakeWebhook(payload string, signatureHeader string) (*WaffoPancakeWebhookEvent, error)

VerifyConfiguredWaffoPancakeWebhook verifies the signature header. The SDK picks the matching test / prod public key from the payload's `mode` field.

func (*WaffoPancakeWebhookEvent) NormalizedEventType

func (e *WaffoPancakeWebhookEvent) NormalizedEventType() string

NormalizedEventType returns the event type or empty string for a nil event.

type WalletFunding

type WalletFunding struct {
	// contains filtered or unexported fields
}

func (*WalletFunding) PreConsume

func (w *WalletFunding) PreConsume(amount int) error

func (*WalletFunding) Refund

func (w *WalletFunding) Refund() error

func (*WalletFunding) Settle

func (w *WalletFunding) Settle(delta int) error

func (*WalletFunding) Source

func (w *WalletFunding) Source() string

type WebhookPayload

type WebhookPayload struct {
	Type      string        `json:"type"`
	Title     string        `json:"title"`
	Content   string        `json:"content"`
	Values    []interface{} `json:"values,omitempty"`
	Timestamp int64         `json:"timestamp"`
}

WebhookPayload webhook 通知的负载数据

type WorkerRequest

type WorkerRequest struct {
	URL     string            `json:"url"`
	Key     string            `json:"key"`
	Method  string            `json:"method,omitempty"`
	Headers map[string]string `json:"headers,omitempty"`
	Body    json.RawMessage   `json:"body,omitempty"`
}

WorkerRequest Worker请求的数据结构

Source Files

Directories

Path Synopsis
Package accesspolicy defines the detached, side-effect-free S3 policy envelope used before policy enforcement is enabled.
Package accesspolicy defines the detached, side-effect-free S3 policy envelope used before policy enforcement is enabled.
Package promptlearning contains the pure admission, sanitization, and fingerprinting boundary for prompt-learning samples.
Package promptlearning contains the pure admission, sanitization, and fingerprinting boundary for prompt-learning samples.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL