Files

287 lines
11 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package common
import (
"context"
"errors"
"time"
)
// ErrChannelUnavailable 没有可用渠道
var ErrChannelUnavailable = errors.New("no available channel")
// ErrModelNotFound 模型不存在
var ErrModelNotFound = errors.New("model not found")
// ErrStreamInterrupted 流式响应被客户端中断
var ErrStreamInterrupted = errors.New("stream interrupted by client disconnect")
// ErrTenantModelNotEnabled 租户未启用该模型
var ErrTenantModelNotEnabled = errors.New("model not enabled for this tenant")
// ErrMemberModelNotAllowed 成员无权使用该模型
var ErrMemberModelNotAllowed = errors.New("model not allowed for this member")
// ErrApiKeyModelNotAllowed API Key 无权使用该模型
var ErrApiKeyModelNotAllowed = errors.New("model not allowed for this API key")
// DeprecationInfo 模型弃用信息
type DeprecationInfo struct {
Deprecated bool
SunsetDate string // 格式: "2006-01-02",空表示未设置
ReplacementModel string
}
// DataProvider 是 relay 层访问业务数据的接口。
// 实现在 internal/logic/relay/provider.go 中,使用 GoFrame ORM。
// relay/ 层通过此接口获取数据,不直接依赖 GoFrame。
//
// 渠道调度(选号/亲和/健康/容量)已由 relaykit/dispatch 调度引擎统一承担
// (Route/Next/Report/Finish),本接口只保留数据访问与选择物化。
type DataProvider interface {
// ValidateApiKey 验证 API Key 并返回认证信息。
// rawKey 是完整的 API Key 原值(如 sk-a1b2c3d4e5f6...)。
ValidateApiKey(ctx context.Context, rawKey string) (*ApiKeyInfo, error)
// GetModelMapping 获取模型映射信息。
// 返回标准模型名和分类(chat/embedding/image 等)。
GetModelMapping(ctx context.Context, modelName string) (standardName string, category string, err error)
// RecordUsage 异步记录 API 调用用量日志。
RecordUsage(ctx context.Context, record *UsageRecord)
// RecordAudit 异步记录请求审计日志。
RecordAudit(ctx context.Context, record *AuditRecord)
// UpdateTaskAudit 更新异步任务的审计记录(任务完成时调用)。
// 通过 task_id 查找提交阶段写入的审计记录,补充最终结果。
UpdateTaskAudit(ctx context.Context, record *AuditRecord)
// GetAvailableModels 获取指定租户可用的模型列表。
// apiKeyID > 0 时进一步按 API Key 的模型范围过滤。
// userID > 0 时进一步按成员的模型范围过滤。
GetAvailableModels(ctx context.Context, tenantID, apiKeyID, userID int64) ([]ModelInfo, error)
// GetModelDetail 获取单个模型的详细信息。
// tenantID 用于权限校验(检查租户是否有权使用该模型)。
// apiKeyID / userID 大于 0 时进一步校验 API Key 与成员的模型范围(与 GetAvailableModels 口径一致)。
GetModelDetail(ctx context.Context, tenantID, apiKeyID, userID int64, modelName string) (*ModelDetail, error)
// CheckTenantModelAccess 检查租户是否有权使用指定模型。
// 返回是否启用和渠道范围(nil/空表示不限渠道)。
CheckTenantModelAccess(ctx context.Context, tenantID int64, modelName string) (enabled bool, channelScope []int64, err error)
// GetModelDeprecationInfo 获取模型弃用信息。
// 返回 nil 表示模型未弃用。
GetModelDeprecationInfo(ctx context.Context, modelName string) (*DeprecationInfo, error)
// InvalidateModelCache 清除指定模型的缓存。
InvalidateModelCache(modelName string)
// CheckMemberModelAccess 检查成员是否有权使用指定模型。
// 空 scope(无记录)表示不限制。
CheckMemberModelAccess(ctx context.Context, tenantID, userID int64, modelName string) (bool, error)
// CheckApiKeyModelAccess 检查 API Key 是否有权使用指定模型。
// 无 scope 记录表示不限制(向后兼容)。
CheckApiKeyModelAccess(ctx context.Context, apiKeyID int64, modelName string) (bool, error)
// InvalidateMemberModelCache 清除指定成员的模型范围缓存。
InvalidateMemberModelCache(ctx context.Context, tenantID, userID int64)
// InvalidateApiKeyModelCache 清除指定 API Key 的模型权限缓存。
InvalidateApiKeyModelCache(ctx context.Context, apiKeyID int64)
// MaterializeSelection 由新调度引擎的决策构造 ChannelSelection:
// 从目录快照取转发元数据,按 keyID 解密渠道 Key(keyID=0 时取渠道首个 active Key)。
MaterializeSelection(ctx context.Context, channelID, keyID int64, modelName string) (*ChannelSelection, error)
}
// ApiKeyInfo API Key 验证结果
type ApiKeyInfo struct {
ID int64
TenantID int64
UserID int64
ProjectID int64 // 项目密钥关联的项目 ID,个人密钥为 0
Scope string // full / chat_only / embeddings_only / images_only / read_only / custom
Status string // active / disabled / expired
RateLimitQps int // 0 表示使用全局默认
RateLimitConcurrency int // 0 表示不限制
IpWhitelist string // 逗号分隔 IP/CIDR 白名单
TotalQuota float64 // 0 表示不限制
UsedQuota float64
}
// ChannelSelection 渠道选择结果
type ChannelSelection struct {
ChannelID int64
ChannelType int
ChannelName string
BaseURL string
ApiKey string // 解密后的上游 API Key
KeyID int64 // 渠道 Key 记录 ID(凭证级归因;0=未知)
UpstreamModelName string
IsModelMapped bool
MaxConcurrency int // 该渠道最大并发(0/负值表示不限),供各转发入口做容量控制
Settings ChannelSettings
// 协议能力(chn_abilities 渠道×模型级):
// SupportsResponses:responses 入站原样直连 /v1/responses;
// ChatViaResponses:responses-only 上游,chat 入站经桥接发送 /v1/responses。
SupportsResponses bool
ChatViaResponses bool
SelectionReason string // bind / hrw / overflow / probe / cred_rotate
Priority int
Weight int
HealthScore float64
}
// UsageRecord API 调用用量记录
type UsageRecord struct {
TenantID int64
UserID int64
ApiKeyID int64
ProjectID int64 // 通过 API Key 关联的项目 ID
ChannelID int64
ModelName string
RelayMode int
PromptTokens int
CompletionTokens int
TotalTokens int
CachedTokens int // 缓存读取 token 数
AudioTokens int // 音频 token 数(输入+输出)
ImageTokens int // 图像 token 数
ReasoningTokens int // 推理 token 数
LatencyMs float64
IsStream bool
Success bool
RequestID string
Status string // success / error / timeout / cancelled
ErrorMessage string
// Cache token 细分
CacheCreationTokens int // 写入缓存的 token(Claude cache_creation / OpenAI Responses cache_write)
CacheCreation5mTokens int // Claude 5分钟缓存创建
CacheCreation1hTokens int // Claude 1小时缓存创建
CacheReadTokens int // 缓存读取 token
// 音频 token 分离
AudioInputTokens int
AudioOutputTokens int
// 其他 token
ImageOutputTokens int // 图像输出 token(DALL-E)
// 按秒计费任务的视频时长(秒),来自任务提交时的 spec.duration;非时长类任务为 0
DurationSeconds int
// 请求元数据
RequestedModel string // 用户请求的模型名
UpstreamModel string // 上游实际模型名
RequestType int // 1=sync, 2=stream, 3=async, 4=websocket
UserAgent string
ClientIP string
FirstTokenMs int
ServiceTier string
ReasoningEffort string
InboundEndpoint string // 客户端请求路径
UpstreamEndpoint string // 上游实际请求路径
// 上游请求 ID:对话=上游响应头中的请求 ID(ExtractUpstreamRequestID 提取);
// 任务=上游提交响应体中的任务 ID(tsk_model_tasks.private_data 的 upstream_task_id)。
// 排障时凭此 ID 在上游日志中定位同一次调用;上游未返回时为空。
UpstreamRequestID string
// 渠道详情
ChannelName string
ChannelType int
// 计费元数据
BillingMode string
BillingSource string
RateMultiplier float64
RetryIndex int
// 流式追踪
StreamEndReason string
// 图像生成
ImageCount int
ImageSize string
// 费用数据(由 relay handler 从结算结果填充)
InputCost float64
OutputCost float64
CacheCreationCost float64
CacheReadCost float64
TotalCost float64
ActualCost float64
Currency string
PreDeductAmount float64
RefundAmount float64
SupplementAmount float64
// 计费快照
BillingSnapshot string // JSON 字符串
BillingSummary string // 人类可读中文文本
// 异步任务关联
TaskID string // 异步任务公开ID(task_xxxxx),普通请求为空
}
// AuditRecord 请求审计日志记录
type AuditRecord struct {
TenantID int64
UserID int64
ApiKeyID int64
ProjectID int64 // 通过 API Key 关联的项目 ID
RequestID string
Method string
Path string
QueryParams string
StatusCode int
ClientIP string
UserAgent string
Model string // 请求使用的模型名称
RequestBody string
ResponseBody string
LatencyMs int
FirstTokenMs int
IsStream bool
RequestHeaders map[string]string // 请求头(仅审计级别为 all 时记录)
ResponseHeaders map[string]string // 响应头(仅审计级别为 all 时记录)
ForwardingTrace *ForwardingTrace // 转发路径追踪(仅管理员可见)
// 异步任务完成字段(仅 UpdateTaskAudit 时填充)
TaskID string // 异步任务公开ID,关联 tsk_model_tasks.public_task_id
TaskStatus string // SUCCESS / FAILURE
TaskResult string // 上游最终响应体
TaskUpstreamHeaders map[string]string // 上游最终轮询的响应头
TaskCompletedAt *time.Time // 任务到达终态的时间
}
// ModelInfo 模型信息(用于 /v1/models 端点)
type ModelInfo struct {
ModelId string `json:"model_id"`
ModelName string `json:"model_name"`
Category string `json:"category"`
Status string `json:"status"`
MaxContextTokens int `json:"max_context_tokens"`
MaxOutputTokens int `json:"max_output_tokens"`
Capabilities map[string]bool `json:"capabilities"`
}
// ModelDetail 模型详细信息(用于 /v1/models/{model_id} 端点)
type ModelDetail struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
OwnedBy string `json:"owned_by"`
ModelName string `json:"model_name,omitempty"`
Category string `json:"category,omitempty"`
Status string `json:"status,omitempty"`
MaxContextTokens int `json:"max_context_tokens,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
Description string `json:"description,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
}