Skip to content

Latest commit

 

History

History
454 lines (355 loc) · 12.8 KB

File metadata and controls

454 lines (355 loc) · 12.8 KB

TokenRouter Code Wiki

项目代码知识库:数据结构、接口定义、模块边界、命名规范 最后更新:2026-04-24


1. 阅读地图

新 Agent 必读顺序:

  1. AGENT_WORKFLOW.md — 工作规范
  2. CONSTRAINTS.md — 硬边界
  3. CODE_WIKI.md — 本文件(代码结构)
  4. API_CONTRACT.md — HTTP 规范
  5. 相关模块设计文档

2. 核心数据类型

2.1 Envelope

内部统一格式,所有入站请求的收敛形态。

package envelope

type Envelope struct {
    Model       string    `json:"model"`
    Messages    []Message `json:"messages"`
    Stream      bool      `json:"stream"`
    Tools       []Tool    `json:"tools,omitempty"`
    Temperature *float64  `json:"temperature,omitempty"`
    MaxTokens   *int      `json:"max_tokens,omitempty"`
    TopP        *float64  `json:"top_p,omitempty"`
    SessionID   string    `json:"session_id,omitempty"` // 兼容字段,DeepSeek 出站会透传
    Raw         map[string]json.RawMessage `json:"-"`    // 其余字段原样保留
}

type Message struct {
    Role         string          `json:"role"`
    Content      json.RawMessage `json:"content"`      // string or []ContentPart
    Name         string          `json:"name,omitempty"`
    ToolCalls    []ToolCall      `json:"tool_calls,omitempty"`
    ToolCallID   string          `json:"tool_call_id,omitempty"`
    CacheControl *CacheControl   `json:"cache_control,omitempty"`
    Extra        map[string]json.RawMessage `json:"-"` // 厂商扩展字段原样透传
}

type Tool struct {
    Type         string        `json:"type"`
    Function     Function      `json:"function"`
    CacheControl *CacheControl `json:"cache_control,omitempty"`
}

type Function struct {
    Name        string          `json:"name"`
    Description string          `json:"description,omitempty"`
    Parameters  json.RawMessage `json:"parameters,omitempty"`
}

type ToolCall struct {
    ID       string       `json:"id"`
    Type     string       `json:"type"`
    Function CallFunction `json:"function"`
}

type CallFunction struct {
    Name      string `json:"name"`
    Arguments string `json:"arguments"`
}

type CacheControl struct {
    Type string `json:"type"` // "ephemeral"
}

关键设计

  • Contentjson.RawMessage,因为 content 可能是 string 或多模态数组
  • Message.Extra 保存消息级未知字段(如 DeepSeek reasoning_content),避免多轮请求中丢失厂商扩展字段
  • Raw 字段保存所有未标准化字段,出站时按需还原为厂商原生格式
  • CacheControl 在入站时通常为空,由 CacheInjector 填充

2.2 Block

Chunker 的输出,Arranger 和后续模块的操作单位。

package block

type BlockType string

const (
    BlockSystem  BlockType = "system"  // system 角色消息
    BlockTool    BlockType = "tool"    // 工具定义
    BlockHistory BlockType = "history" // 最后一轮 user 之前的历史消息
    BlockQuery   BlockType = "query"   // 最后一轮 user 及其后的 assistant/tool 尾部
)

type Block struct {
    Type     BlockType
    Messages []envelope.Message
    Tools    []envelope.Tool
}

3. 关键接口

3.1 入站适配器

package inbound

type InboundAdapter interface {
    Match(method, path string) bool
    Parse(req *http.Request) (*envelope.Envelope, error)
    Name() string
}

3.2 出站适配器

package outbound

type OutboundAdapter interface {
    Name() string
    BuildRequest(blocks []block.Block, env *envelope.Envelope) ([]byte, error)
    ParseStreamChunk(chunk []byte) (*StreamEvent, error)
    ParseResponse(body []byte) (*Response, error)
}

type StreamEvent struct {
    Type    string
    Delta   json.RawMessage
    Done    bool
    Usage   *Usage
}

type Response struct {
    Choices []Choice
    Usage   *Usage
    Raw     map[string]json.RawMessage
}

type Choice struct {
    Index        int
    Message      *envelope.Message
    FinishReason string
}

type Usage struct {
    PromptTokens     int
    CompletionTokens int
    CacheReadTokens  int
    CacheWriteTokens int
}

3.3 分块器与排列器

package chunker

type Chunker interface {
    Chunk(env *envelope.Envelope) ([]block.Block, error)
}
// Phase 1: StaticChunker
// Phase 2: DynamicChunker
package arranger

type Arranger interface {
    Arrange(blocks []block.Block) ([]block.Block, error)
}

// DefaultArranger 静态排列器实现
type DefaultArranger struct {
    maxHistoryTurns int
    toolSortEnabled bool
}

// ArrangerOption 选项函数类型
type ArrangerOption func(*DefaultArranger)

// WithToolSort 工具排序选项
func WithToolSort(enabled bool) ArrangerOption

// NewDefaultArranger 构造函数(选项模式)
func NewDefaultArranger(maxHistoryTurns int, opts ...ArrangerOption) *DefaultArranger

配置说明

  • maxHistoryTurns:历史消息最大轮数(默认 20)
  • toolSortEnabled:是否对工具按字母序排序(默认 true
    • true:工具按 Function.Name 字母序排序(推荐,提高缓存命中率)
    • false:保持工具原始顺序(适用于对工具顺序敏感的场景)

使用示例

// 默认排序(启用工具排序)
arranger.NewDefaultArranger(20)

// 禁用工具排序
arranger.NewDefaultArranger(20, arranger.WithToolSort(false))

3.4 序列化规范器

package canonicalizer

func CanonicalJSON(v interface{}) ([]byte, error)

保证:两个逻辑等价的 Block 序列,输出完全相同的 JSON 字符串。

3.5 缓存注入器

package cacheinject

type Engine interface {
    Inject(blocks []block.Block, vendor string) ([]block.Block, error)
}

type Injector interface {
    Name() string
    Supports(vendor string) bool
    Inject(blocks []block.Block) ([]block.Block, error)
}

3.6 哈希计算

package hasher

func PrefixHash(blocks []block.Block) (string, error)
func FullHash(blocks []block.Block) (string, error)
  • PrefixHash:只取 BlockSystem + BlockTool 计算 SHA256,用于厂商侧 KV Cache 命中
  • FullHash:取全部 Block 计算 SHA256,用于请求去重

3.7 请求去重器

package dedup

type Deduplicator struct {
    // unexported fields: inflight map, mutex, ttl, enabled, stopCh, stopOnce
}

func NewDeduplicator(ttl time.Duration, enabled bool) *Deduplicator

// CheckOrRegister atomically checks for an existing in-flight request or
// registers a new one. Returns (req, found): found=true means a duplicate
// is already in progress; found=false means caller owns the request.
func (d *Deduplicator) CheckOrRegister(hash string) (*InFlightRequest, bool)

func (d *Deduplicator) Complete(hash string, statusCode int, resp []byte, err error)
func (d *Deduplicator) Stop()

type InFlightRequest struct {
    Done       chan struct{}
    StatusCode int
    Resp       []byte
    Err        error
    // unexported completeOnce makes Complete idempotent.
}

3.8 计费服务

package billing

type TokenCounts struct {
    PromptTokens     int
    CompletionTokens int
    CacheReadTokens  int
    CacheWriteTokens int
}

type CostBreakdown struct {
    PromptCost     float64
    CompletionCost float64
    CacheReadCost  float64
    CacheWriteCost float64
    TotalCost      float64
}

type PriceEngine struct { /* table *PricingTable */ }

func NewPriceEngine(table *PricingTable) *PriceEngine
func (e *PriceEngine) Calculate(modelName string, counts TokenCounts) (CostBreakdown, error)

type QuotaManager struct { /* db *gorm.DB, cache */ }

func NewQuotaManager(db *gorm.DB) *QuotaManager
func (qm *QuotaManager) CheckQuota(userID string) (bool, error)
func (qm *QuotaManager) GetMonthlyUsage(userID string) (int64, error)
func (qm *QuotaManager) InvalidateCache(userID string)

4. 请求处理流水线

[入站请求]
    │
    ▼
[Inbound Adapter] ──→ Envelope
    │
    ▼
[Chunker] ──→ []Block
    │
    ▼
[Arranger] ──→ 排序后的 []Block
    │
    ▼
[Canonicalizer] ──→ 确定性 JSON bytes
    │
    ▼
[CacheInjector] ──→ 注入 cache_control
    │
    ▼
[Hasher] ──→ prefixHash / fullHash
    │
    ▼
[Dedup] ──→ 检查或等待复用
    │
    ▼
[Outbound Adapter] ──→ 厂商原生请求体
    │
    ▼
[HTTP Proxy] ──→ 上游 Provider
    │
    ▼
[响应返回] + [异步计费记录]

完整架构图参见 architecture.md


5. 目录结构与包职责

tokenrouter/                      # 计划结构
├── cmd/server/main.go            # 服务入口:配置、数据库、路由注册
├── internal/
│   ├── server/                   # ChatPipeline 与生产 chat handler
│   ├── inbound/                  # 入站适配层
│   ├── envelope/                 # Envelope / Message / Tool 定义
│   ├── block/                    # Block 定义
│   ├── chunker/                  # 分块器
│   ├── arranger/                 # 排列器
│   ├── canonicalizer/            # 序列化规范器
│   ├── cacheinject/              # 缓存注入器
│   ├── hasher/                   # 前缀/完整哈希计算
│   ├── dedup/                    # 请求去重器
│   ├── observer/                 # 流量观测(Phase 2 预留)
│   ├── outbound/                 # 出站适配层
│   ├── proxy/                    # HTTP / SSE 代理
│   ├── billing/                  # 计费与配额
│   ├── middleware/               # 认证、限流、日志
│   ├── monitor/                  # Prometheus 指标
│   └── model/                    # 数据模型(GORM)
├── pkg/
│   ├── config/                   # 配置加载
│   ├── logger/                   # Zap 封装
│   ├── httputil/                 # HTTP 工具
│   └── crypto/                   # API Key 哈希
├── migrations/                   # 数据库迁移脚本
├── deployments/                  # Docker / K8s 配置
└── docs/                         # 技术文档
包路径 职责 对应文档
internal/server/ 生产级 ChatPipeline 与 /v1/chat/completions handler modules/system-implementation.md
internal/inbound/ 入站协议解析 modules/adapter-architecture.md
internal/outbound/ 还原厂商原生请求 modules/adapter-architecture.md
internal/chunker/ 静态四分块 modules/cache-intelligence.md
internal/arranger/ System 合并 / Tool 排序 / History 截断 modules/cache-intelligence.md
internal/canonicalizer/ 确定性 JSON 序列化 modules/cache-intelligence.md
internal/cacheinject/ 按厂商策略注入缓存标记 modules/cache-intelligence.md
internal/hasher/ 计算 PrefixHash / FullHash modules/cache-intelligence.md
internal/dedup/ 非流式并发请求去重 modules/cache-intelligence.md
internal/proxy/ SSE 流式代理、连接池 modules/system-implementation.md
internal/billing/ Token 计量、价格计算 modules/system-implementation.md
internal/middleware/ API Key 认证、令牌桶限流 modules/system-implementation.md
internal/monitor/ Prometheus 指标注册与采集 modules/system-implementation.md

6. 编码规范

6.1 错误处理

  • 所有外部调用必须处理 error
  • 错误必须向上 wrap,带上模块前缀:fmt.Errorf("chunker: %w", err)
  • 禁止使用 _ 丢弃错误

6.2 日志

  • 使用结构化 Zap 日志
  • key 使用 snake_case
  • 错误日志必须包含 "error" 字段

6.3 配置

  • 所有配置项通过环境变量注入
  • pkg/config/ 为统一加载入口
  • 默认值在代码中显式声明

6.4 测试

  • 优先 TDD
  • 表格驱动测试
  • Mock 接口,不 Mock 实现

7. 扩展指南

7.1 新增厂商出站适配器

  1. 创建目录 internal/outbound/{vendor}/
  2. 实现 OutboundAdapter 接口
  3. internal/outbound/registry.go 注册
  4. 参考文档:modules/adapter-architecture.md

MVP v0.1 现状internal/outbound/deepseek/ 已实现(OpenAI 兼容适配器)。openai/anthropic/ 为预留空壳。

7.2 新增缓存注入器

  1. 创建文件 internal/cacheinject/{vendor}.go
  2. 实现 Injector 接口
  3. internal/cacheinject/registry.go 注册
  4. 参考文档:modules/cache-intelligence.md

MVP v0.1 现状internal/cacheinject/openai.go 已实现(透传策略)。anthropic.go 为预留空壳。

7.3 新增入站协议

  1. 评估是否可用 ConfigDrivenAdapter(YAML 配置)覆盖
  2. 若不行,手写 InboundAdapter 实现
  3. 在路由注册表中注册
  4. 参考文档:modules/adapter-architecture.md