会话亲和(proxy/affinity.go):同一会话粘住上次应答的渠道,命中上游提示词 缓存而非被加权随机打散后全价重算。会话身份 = 密钥 + 模型 + 请求文本稳定前缀 摘要,不依赖各客户端会话头;TTL 默认 5m(OT_PROXY_AFFINITY_TTL,0=关闭), 成功应答刷新。亲和渠道必须在候选集内(健康/未冷却/绑定),否则回退加权随机。 proxyOne 增加 served 返回值区分「应答成功」与「已终结但失败」。 opaque 透传(convert):chat↔responses 互转原样携带 metadata、service_tier、 parallel_tool_calls。此前逐字段重建请求体会丢它们,而中转上游会校验/使用。 用量细化(proxy+store):usage_logs 新增 reasoning_tokens(展示口径,不重复 计费)、cache_write_1h_tokens(Anthropic 1h 缓存写按 2× 输入价,此前统一 1.25× 少算 60%)、served_model(上游实际应答模型,映射/聚合商场景对账)。 schema v6→v7。 验证:go vet ./... 干净;go test ./... 与 go test -race ./... 全绿; 新增测试覆盖亲和粘性与冷却回退、opaque 双向透传、reasoning 不重复计费、 1h/5m 缓存写分开计价、served_model 提取(非流式与流式)。
72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
// Package cli 命令行入口。
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/openteam/server/internal/api"
|
|
"github.com/openteam/server/internal/app"
|
|
"github.com/openteam/server/internal/config"
|
|
"github.com/openteam/server/internal/proxy"
|
|
)
|
|
|
|
// serve 启动 HTTP 服务(默认子命令)。
|
|
func serve() error {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
a, err := app.New(cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer a.Shutdown(context.Background())
|
|
|
|
gw := proxy.NewGateway(a.DB, a.Enc, a.Usage, a.Limit, proxy.Options{
|
|
UserRPS: cfg.RateLimit.UserRPS,
|
|
LogRaw: cfg.Proxy.LogRaw,
|
|
GWTimeout: cfg.Proxy.Timeout,
|
|
MaxBodyMB: cfg.Proxy.MaxBodyMB,
|
|
FailoverBudget: cfg.Proxy.FailoverBudget,
|
|
StreamHeartbeat: cfg.Proxy.StreamHeartbeat,
|
|
FirstTokenWait: cfg.Proxy.FirstTokenWait,
|
|
AffinityTTL: cfg.Proxy.AffinityTTL,
|
|
})
|
|
router := api.NewRouter(a, gw)
|
|
|
|
srv := &http.Server{
|
|
Addr: ":" + strconv.Itoa(cfg.Port),
|
|
Handler: router,
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
}
|
|
|
|
go func() {
|
|
log.Printf("openteam listening on %s (env=%s)", srv.Addr, cfg.Env)
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Fatalf("server: %v", err)
|
|
}
|
|
}()
|
|
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
log.Println("shutting down...")
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
log.Printf("server shutdown: %v", err)
|
|
}
|
|
a.Usage.Close()
|
|
return nil
|
|
}
|