Files
hunjing/shenshi/internal/server/server.go
T

111 lines
3.2 KiB
Go

// Package server 神识 HTTP 服务:数据面(IR 入站 → SSE 出站)与健康面。
package server
import (
"encoding/json"
"net/http"
"hunjing/shenshi/internal/ir"
"hunjing/shenshi/internal/relay"
"hunjing/shenshi/internal/route"
)
// Config 服务配置。
type Config struct {
ServiceToken string // 灵台→神识服务令牌;为空时开发档不校验
}
// Server 神识 HTTP 服务。
type Server struct {
cfg Config
routes *route.Registry
mux *http.ServeMux
}
// New 构建服务并注册路由。
func New(cfg Config, routes *route.Registry) *Server {
s := &Server{cfg: cfg, routes: routes, mux: http.NewServeMux()}
s.mux.HandleFunc("GET /healthz", s.handleHealth)
s.mux.HandleFunc("GET /readyz", s.handleHealth)
s.mux.Handle("POST /v1/chat/completions", s.auth(http.HandlerFunc(s.handleChat)))
return s
}
// ServeHTTP 实现 http.Handler。
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.mux.ServeHTTP(w, r) }
// auth 服务令牌校验中间件;未配置令牌时放行(本地开发档)。
func (s *Server) auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if s.cfg.ServiceToken == "" {
next.ServeHTTP(w, r)
return
}
credential := r.Header.Get("Authorization")
if credential != "Bearer "+s.cfg.ServiceToken {
writeError(w, http.StatusUnauthorized, "HJ-2003", "服务令牌无效")
return
}
next.ServeHTTP(w, r)
})
}
// handleChat 数据面:解析 IR → 路由 → 适配器流出 SSE。
func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
var req ir.ChatRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "HJ-2002", "IR 请求解析失败: "+err.Error())
return
}
if err := req.Validate(); err != nil {
writeError(w, http.StatusBadRequest, "HJ-2002", err.Error())
return
}
endpoints, err := s.routes.Resolve(req.Slot)
if err != nil {
writeError(w, http.StatusServiceUnavailable, "HJ-2001", err.Error())
return
}
relay.SSEHeaders(w)
w.WriteHeader(http.StatusOK)
deltas := make(chan ir.Delta, 16)
streamErr := make(chan error, 1)
go func() {
defer close(deltas)
streamErr <- endpoints[0].Adaptor.Stream(r.Context(), &req, deltas)
}()
for d := range deltas {
if err := relay.WriteDelta(w, d); err != nil {
return // 客户端断开:取消上游由 r.Context() 传播
}
}
if err := <-streamErr; err != nil {
// 流已开始,只能如实中断:写一条错误帧后终止(不伪装 [DONE])
_ = relay.WriteDelta(w, ir.Delta{FinishReason: "error", Delta: ""})
_ = relay.WriteDone(w)
return
}
if err := relay.WriteDone(w); err != nil {
return
}
}
// handleHealth 健康面。
func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
}
// writeError 统一错误结构(API 契约 §5)。
func writeError(w http.ResponseWriter, status int, code, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
body := map[string]any{
"error": map[string]string{"code": code, "message": message},
}
_ = json.NewEncoder(w).Encode(body)
}