common

package
v1.0.3-preview.2 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LastMessageTypeNone     = "none"
	LastMessageTypeText     = "text"
	LastMessageTypeTools    = "tools"
	LastMessageTypeThinking = "thinking"
)

Variables

This section is empty.

Functions

func AppendRequestConversionFromRequest

func AppendRequestConversionFromRequest(info *RelayInfo, req any)

func ApplyParamOverride

func ApplyParamOverride(jsonData []byte, paramOverride map[string]interface{}, conditionContext map[string]interface{}) ([]byte, error)

func ApplyParamOverrideWithRelayInfo

func ApplyParamOverrideWithRelayInfo(jsonData []byte, info *RelayInfo) ([]byte, error)

func BuildParamOverrideContext

func BuildParamOverrideContext(info *RelayInfo) map[string]interface{}

BuildParamOverrideContext 提供 ApplyParamOverride 可用的上下文信息。 目前内置以下字段:

  • upstream_model/model:始终为通道映射后的上游模型名。
  • original_model:请求最初指定的模型名。
  • request_path:请求路径
  • is_channel_test:是否为渠道测试请求(同 is_test)。

func GetAPIVersion

func GetAPIVersion(c *gin.Context) string

func GetEffectiveHeaderOverride

func GetEffectiveHeaderOverride(info *RelayInfo) map[string]interface{}

func GetFullRequestURL

func GetFullRequestURL(baseURL string, requestURL string, channelType int) string

func GuessRelayFormatFromRequest

func GuessRelayFormatFromRequest(req any) (types.RelayFormat, bool)

func MaxAPIErrorFromParamOverride

func MaxAPIErrorFromParamOverride(err *ParamOverrideReturnError) *types.MaxAPIError

func NewOutboundJSONBody

func NewOutboundJSONBody(data []byte) (body io.Reader, size int64, closer io.Closer, err error)

NewOutboundJSONBody wraps the already-marshaled upstream request body into a BodyStorage. When disk cache is enabled and the payload exceeds the configured threshold, the data is written to a temp file and the original []byte can be GC'd, significantly reducing the heap residency while waiting for the upstream provider to respond (the dominant cost for large base64 payloads).

In memory mode the underlying memoryStorage reuses the same backing array, so this is equivalent to bytes.NewReader(data) in terms of memory usage.

The caller MUST invoke closer.Close() once the upstream call has finished (typically via defer) to release the disk file / memory accounting.

The returned reader is wrapped with common.ReaderOnly to prevent the HTTP transport from prematurely closing the underlying BodyStorage. The returned size is meant to be propagated to http.Request.ContentLength because the type-erased io.Reader prevents net/http from auto-detecting it.

func RemoveDisabledFields

func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOtherSettings, channelPassThroughEnabled bool) ([]byte, error)

RemoveDisabledFields 从请求 JSON 数据中移除渠道设置中禁用的字段 service_tier: 服务层级字段,可能导致额外计费(OpenAI、Claude、Responses API 支持) inference_geo: Claude 数据驻留推理区域字段(仅 Claude 支持,默认过滤) speed: Claude 推理速度模式字段(仅 Claude 支持,默认过滤) store: 数据存储授权字段,涉及用户隐私(仅 OpenAI、Responses API 支持,默认允许透传,禁用后可能导致 Codex 无法使用) safety_identifier: 安全标识符,用于向 OpenAI 报告违规用户(仅 OpenAI 支持,涉及用户隐私) stream_options.include_obfuscation: 响应流混淆控制字段(仅 OpenAI Responses API 支持)

func RemoveGeminiDisabledFields

func RemoveGeminiDisabledFields(jsonData []byte) ([]byte, error)

RemoveGeminiDisabledFields removes disabled fields from Gemini request JSON data Currently supports removing functionResponse.id field which Vertex AI does not support

func StoreTaskRequest

func StoreTaskRequest(c *gin.Context, info *RelayInfo, action string, requestObj TaskSubmitReq)

func ValidateBasicTaskRequest

func ValidateBasicTaskRequest(c *gin.Context, info *RelayInfo, action string) *dto.TaskError

func ValidateMultipartDirect

func ValidateMultipartDirect(c *gin.Context, info *RelayInfo) *dto.TaskError

Types

type BillingSettler

type BillingSettler interface {
	// Settle 根据实际消耗额度进行结算,计算 delta = actualQuota - preConsumedQuota,
	// 同时调整资金来源(钱包/订阅)和令牌额度。
	Settle(actualQuota int) error

	// Refund 退还所有预扣费额度(资金来源 + 令牌),幂等安全。
	// 通过 gopool 异步执行。如果已经结算或退款则不做任何操作。
	Refund(c *gin.Context)

	// NeedsRefund 返回会话是否存在需要退还的预扣状态(未结算且未退款)。
	NeedsRefund() bool

	// GetPreConsumedQuota 返回实际预扣的额度值(信任用户可能为 0)。
	GetPreConsumedQuota() int

	// Reserve 将预扣额度补到目标值;若目标值不高于当前预扣额度则不做任何事。
	Reserve(targetQuota int) error
}

BillingSettler 抽象计费会话的生命周期操作。 由 service.BillingSession 实现,存储在 RelayInfo 上以避免循环引用。

type BuildInToolInfo

type BuildInToolInfo struct {
	ToolName          string
	CallCount         int
	SearchContextSize string
}

type ChannelMeta

type ChannelMeta struct {
	ChannelType          int
	ChannelId            int
	ChannelIsMultiKey    bool
	ChannelMultiKeyIndex int
	ChannelBaseUrl       string
	ApiType              int
	ApiVersion           string
	ApiKey               string
	Organization         string
	ChannelCreateTime    int64
	ParamOverride        map[string]interface{}
	HeadersOverride      map[string]interface{}
	ChannelSetting       dto.ChannelSettings
	ChannelOtherSettings dto.ChannelOtherSettings
	UpstreamModelName    string
	IsModelMapped        bool
	SupportStreamOptions bool // 是否支持流式选项
}

type ClaudeConvertInfo

type ClaudeConvertInfo struct {
	LastMessagesType string
	Index            int
	Usage            *dto.Usage
	FinishReason     string
	Done             bool

	ToolCallBaseIndex      int
	ToolCallMaxIndexOffset int
}

type ConditionOperation

type ConditionOperation struct {
	Path           string      `json:"path"`             // JSON路径
	Mode           string      `json:"mode"`             // full, prefix, suffix, contains, gt, gte, lt, lte
	Value          interface{} `json:"value"`            // 匹配的值
	Invert         bool        `json:"invert"`           // 反选功能,true表示取反结果
	PassMissingKey bool        `json:"pass_missing_key"` // 未获取到json key时的行为
}

type HasImage

type HasImage interface {
	HasImage() bool
}

type HasPrompt

type HasPrompt interface {
	GetPrompt() string
}

type ParamOperation

type ParamOperation struct {
	Path       string               `json:"path"`
	Mode       string               `json:"mode"` // delete, set, move, copy, prepend, append, trim_prefix, trim_suffix, ensure_prefix, ensure_suffix, trim_space, to_lower, to_upper, replace, regex_replace, return_error, prune_objects, set_header, delete_header, copy_header, move_header, pass_headers, sync_fields
	Value      interface{}          `json:"value"`
	KeepOrigin bool                 `json:"keep_origin"`
	From       string               `json:"from,omitempty"`
	To         string               `json:"to,omitempty"`
	Conditions []ConditionOperation `json:"conditions,omitempty"` // 条件列表
	Logic      string               `json:"logic,omitempty"`      // AND, OR (默认OR)
}

type ParamOverrideReturnError

type ParamOverrideReturnError struct {
	Message    string
	StatusCode int
	Code       string
	Type       string
	SkipRetry  bool
}

func AsParamOverrideReturnError

func AsParamOverrideReturnError(err error) (*ParamOverrideReturnError, bool)

func (*ParamOverrideReturnError) Error

func (e *ParamOverrideReturnError) Error() string

type RelayInfo

type RelayInfo struct {
	TokenId           int
	TokenKey          string
	TokenGroup        string
	UserId            int
	UsingGroup        string // 使用的分组,当auto跨分组重试时,会变动
	UserGroup         string // 用户所在分组
	TokenUnlimited    bool
	StartTime         time.Time
	FirstResponseTime time.Time

	//SendLastReasoningResponse bool
	IsStream               bool
	IsGeminiBatchEmbedding bool
	IsPlayground           bool
	UsePrice               bool
	RelayMode              int
	OriginModelName        string
	RequestURLPath         string
	RequestHeaders         map[string]string
	ShouldIncludeUsage     bool
	DisablePing            bool // 是否禁止向下游发送自定义 Ping
	ClientWs               *websocket.Conn
	TargetWs               *websocket.Conn
	InputAudioFormat       string
	OutputAudioFormat      string
	RealtimeTools          []dto.RealTimeTool
	IsFirstRequest         bool
	AudioUsage             bool
	ReasoningEffort        string
	UserSetting            dto.UserSetting
	UserEmail              string
	UserQuota              int
	RelayFormat            types.RelayFormat
	SendResponseCount      int
	ReceivedResponseCount  int
	FinalPreConsumedQuota  int // 最终预消耗的配额
	// ForcePreConsume 为 true 时禁用 BillingSession 的信任额度旁路,
	// 强制预扣全额。用于异步任务(视频/音乐生成等),因为请求返回后任务仍在运行,
	// 必须在提交前锁定全额。
	ForcePreConsume bool
	// Billing 是计费会话,封装了预扣费/结算/退款的统一生命周期。
	// 免费模型时为 nil。
	Billing BillingSettler
	// BillingSource indicates whether this request is billed from wallet quota or subscription.
	// "" or "wallet" => wallet; "subscription" => subscription
	BillingSource string
	// SubscriptionId is the user_subscriptions.id used when BillingSource == "subscription"
	SubscriptionId int
	// SubscriptionPreConsumed is the amount pre-consumed on subscription item (quota units or 1)
	SubscriptionPreConsumed int64
	// SubscriptionPostDelta is the post-consume delta applied to amount_used (quota units; can be negative).
	SubscriptionPostDelta int64
	// SubscriptionPlanId / SubscriptionPlanTitle are used for logging/UI display.
	SubscriptionPlanId    int
	SubscriptionPlanTitle string
	// RequestId is used for idempotent pre-consume/refund
	RequestId string
	// SubscriptionAmountTotal / SubscriptionAmountUsedAfterPreConsume are used to compute remaining in logs.
	SubscriptionAmountTotal               int64
	SubscriptionAmountUsedAfterPreConsume int64
	IsClaudeBetaQuery                     bool // /v1/messages?beta=true
	IsChannelTest                         bool // channel test request
	RetryIndex                            int
	LastError                             *types.MaxAPIError
	RuntimeHeadersOverride                map[string]interface{}
	UseRuntimeHeadersOverride             bool
	ParamOverrideAudit                    []string

	// UpstreamRequestBodySize is the byte size of the marshaled upstream request
	// body. It is set when the body is wrapped in a BodyStorage (see
	// relay/common/outbound_body.go), so that DoApiRequest can populate
	// http.Request.ContentLength manually (net/http only auto-detects it for
	// *bytes.Reader/Buffer/strings.Reader). 0 means "let net/http decide".
	UpstreamRequestBodySize int64
	AuditResponseContent    string
	AuditResponseTruncated  bool

	PriceData   types.PriceData
	TaskBilling *types.TaskBillingResult

	// TieredBillingSnapshot is a frozen snapshot of tiered billing rules
	// captured at pre-consume time. Non-nil only when billing mode is "tiered_expr".
	TieredBillingSnapshot *billingexpr.BillingSnapshot
	BillingRequestInput   *billingexpr.RequestInput

	Request dto.Request

	// RequestConversionChain records request format conversions in order, e.g.
	// ["openai", "openai_responses"] or ["openai", "claude"].
	RequestConversionChain []types.RelayFormat
	// 最终请求到上游的格式。可由 adaptor 显式设置;
	// 若为空,调用 GetFinalRequestRelayFormat 会回退到 RequestConversionChain 的最后一项或 RelayFormat。
	FinalRequestRelayFormat types.RelayFormat

	StreamStatus *StreamStatus

	ThinkingContentInfo
	TokenCountMeta
	*ClaudeConvertInfo
	*RerankerInfo
	*ResponsesUsageInfo
	*ChannelMeta
	*TaskRelayInfo
	// contains filtered or unexported fields
}

func GenRelayInfo

func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error)

func GenRelayInfoClaude

func GenRelayInfoClaude(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoEmbedding

func GenRelayInfoEmbedding(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoGemini

func GenRelayInfoGemini(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoImage

func GenRelayInfoImage(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoOpenAI

func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoOpenAIAudio

func GenRelayInfoOpenAIAudio(c *gin.Context, request dto.Request) *RelayInfo

func GenRelayInfoRerank

func GenRelayInfoRerank(c *gin.Context, request *dto.RerankRequest) *RelayInfo

func GenRelayInfoResponses

func GenRelayInfoResponses(c *gin.Context, request *dto.OpenAIResponsesRequest) *RelayInfo

func GenRelayInfoResponsesCompaction

func GenRelayInfoResponsesCompaction(c *gin.Context, request *dto.OpenAIResponsesCompactionRequest) *RelayInfo

func GenRelayInfoWs

func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo

func (*RelayInfo) AppendRequestConversion

func (info *RelayInfo) AppendRequestConversion(format types.RelayFormat)

func (*RelayInfo) GetEstimatePromptTokens

func (info *RelayInfo) GetEstimatePromptTokens() int

func (*RelayInfo) GetFinalRequestRelayFormat

func (info *RelayInfo) GetFinalRequestRelayFormat() types.RelayFormat

func (*RelayInfo) HasSendResponse

func (info *RelayInfo) HasSendResponse() bool

func (*RelayInfo) InitChannelMeta

func (info *RelayInfo) InitChannelMeta(c *gin.Context)

func (*RelayInfo) InitRequestConversionChain

func (info *RelayInfo) InitRequestConversionChain()

func (*RelayInfo) SetEstimatePromptTokens

func (info *RelayInfo) SetEstimatePromptTokens(promptTokens int)

func (*RelayInfo) SetFirstResponseTime

func (info *RelayInfo) SetFirstResponseTime()

func (*RelayInfo) ToString

func (info *RelayInfo) ToString() string

type RerankerInfo

type RerankerInfo struct {
	Documents       []any
	ReturnDocuments bool
}

type ResponsesUsageInfo

type ResponsesUsageInfo struct {
	BuiltInTools map[string]*BuildInToolInfo
}

type StreamEndReason

type StreamEndReason string
const (
	StreamEndReasonNone        StreamEndReason = ""
	StreamEndReasonDone        StreamEndReason = "done"
	StreamEndReasonTimeout     StreamEndReason = "timeout"
	StreamEndReasonClientGone  StreamEndReason = "client_gone"
	StreamEndReasonScannerErr  StreamEndReason = "scanner_error"
	StreamEndReasonHandlerStop StreamEndReason = "handler_stop"
	StreamEndReasonEOF         StreamEndReason = "eof"
	StreamEndReasonPanic       StreamEndReason = "panic"
	StreamEndReasonPingFail    StreamEndReason = "ping_fail"
)

type StreamErrorEntry

type StreamErrorEntry struct {
	Message   string
	Timestamp time.Time
}

type StreamStatus

type StreamStatus struct {
	EndReason StreamEndReason
	EndError  error

	Errors     []StreamErrorEntry
	ErrorCount int
	// contains filtered or unexported fields
}

func NewStreamStatus

func NewStreamStatus() *StreamStatus

func (*StreamStatus) HasErrors

func (s *StreamStatus) HasErrors() bool

func (*StreamStatus) IsNormalEnd

func (s *StreamStatus) IsNormalEnd() bool

func (*StreamStatus) RecordError

func (s *StreamStatus) RecordError(msg string)

func (*StreamStatus) SetEndReason

func (s *StreamStatus) SetEndReason(reason StreamEndReason, err error)

func (*StreamStatus) Summary

func (s *StreamStatus) Summary() string

func (*StreamStatus) TotalErrorCount

func (s *StreamStatus) TotalErrorCount() int

type TaskInfo

type TaskInfo struct {
	Code             int    `json:"code"`
	TaskID           string `json:"task_id"`
	Status           string `json:"status"`
	Reason           string `json:"reason,omitempty"`
	Url              string `json:"url,omitempty"`
	RemoteUrl        string `json:"remote_url,omitempty"`
	Progress         string `json:"progress,omitempty"`
	CompletionTokens int    `json:"completion_tokens,omitempty"` // 用于按倍率计费
	TotalTokens      int    `json:"total_tokens,omitempty"`      // 用于按倍率计费
}

func FailTaskInfo

func FailTaskInfo(reason string) *TaskInfo

type TaskRelayInfo

type TaskRelayInfo struct {
	Action       string
	OriginTaskID string
	// PublicTaskID 是提交时预生成的 task_xxxx 格式公开 ID,
	// 供 DoResponse 在返回给客户端时使用(避免暴露上游真实 ID)。
	PublicTaskID string

	ConsumeQuota bool

	// LockedChannel holds the full channel object when the request is bound to
	// a specific channel (e.g., remix on origin task's channel). Stored as any
	// to avoid an import cycle with model; callers type-assert to *model.Channel.
	LockedChannel any
}

type TaskSubmitReq

type TaskSubmitReq struct {
	Prompt          string                 `json:"prompt"`
	Model           string                 `json:"model,omitempty"`
	Mode            string                 `json:"mode,omitempty"`
	Image           string                 `json:"image,omitempty"`
	Images          []string               `json:"images,omitempty"`
	Size            string                 `json:"size,omitempty"`
	Duration        int                    `json:"duration,omitempty"`
	Seconds         string                 `json:"seconds,omitempty"`
	InputReference  string                 `json:"input_reference,omitempty"`
	AspectRatio     string                 `json:"aspect_ratio,omitempty"`
	Capability      string                 `json:"capability,omitempty"`
	ControlMode     string                 `json:"control_mode,omitempty"`
	DurationSeconds *int                   `json:"duration_seconds,omitempty"`
	EndImage        string                 `json:"end_image,omitempty"`
	InputMode       string                 `json:"input_mode,omitempty"`
	ReferenceImages []string               `json:"reference_images,omitempty"`
	Resolution      string                 `json:"resolution,omitempty"`
	WithAudio       *bool                  `json:"with_audio,omitempty"`
	Metadata        map[string]interface{} `json:"metadata,omitempty"`
}

func GetTaskRequest

func GetTaskRequest(c *gin.Context) (TaskSubmitReq, error)

func (*TaskSubmitReq) GetPrompt

func (t *TaskSubmitReq) GetPrompt() string

func (*TaskSubmitReq) HasImage

func (t *TaskSubmitReq) HasImage() bool

func (*TaskSubmitReq) UnmarshalJSON

func (t *TaskSubmitReq) UnmarshalJSON(data []byte) error

func (*TaskSubmitReq) UnmarshalMetadata

func (t *TaskSubmitReq) UnmarshalMetadata(v any) error

type ThinkingContentInfo

type ThinkingContentInfo struct {
	IsFirstThinkingContent  bool
	SendLastThinkingContent bool
	HasSentThinkingContent  bool
}

type TokenCountMeta

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

Jump to

Keyboard shortcuts

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