feat: 十轮网关优化 - 安全加固/可观测性/性能/可靠性
- SSE Keepalive Ping (15s心跳防止代理断连) - Timing HTTP 头 (X-Timing-Queue/Inference/Total-Ms) - Adapter Request-ID 传播到后端 - Session 清理日志回调 - Server 安全加固 (ReadHeaderTimeout/MaxHeaderBytes 防 slowloris) - Usage Tracker 数据保留清理 (retentionDays + 定期清理) - Config Reload 后 Adapter Registry 更新 (RegisterIfAbsent + RWMutex) - Rate Limiter 空闲 Bucket 清理 (30分钟过期) - Shutdown Drain 超时可配置 (ShutdownDrainSeconds) - Config 模型字段校验增强 (provider/endpoint/actual_model) - Auth 过期 Key 自动清理 (5分钟扫描) - Admin API Rate Limiting - Adapter Health Check 独立超时 (每个 adapter 3s) - TCP 连接阶段超时 (DialContext 5s + KeepAlive 30s) - 幂等键缓存、审计日志、Gzip 中间件、CORS Expose Headers - Backpressure 响应头、熔断器 Prometheus 指标 - 连接池优化、Trace-ID 全链路传播
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edgeai/gateway/internal/adapter"
|
||||
"github.com/edgeai/gateway/pkg/api"
|
||||
)
|
||||
|
||||
// AdapterSummaryGenerator 使用 adapter 调用 LLM 生成对话摘要。
|
||||
type AdapterSummaryGenerator struct {
|
||||
adapter adapter.ModelAdapter
|
||||
model string
|
||||
maxTokens int
|
||||
temperature float64
|
||||
}
|
||||
|
||||
// NewAdapterSummaryGenerator 创建基于 adapter 的摘要生成器。
|
||||
// model 为实际模型名(如 qwen2:latest),maxTokens 限制摘要长度。
|
||||
func NewAdapterSummaryGenerator(adp adapter.ModelAdapter, model string, maxTokens int) *AdapterSummaryGenerator {
|
||||
temp := 0.3
|
||||
return &AdapterSummaryGenerator{
|
||||
adapter: adp,
|
||||
model: model,
|
||||
maxTokens: maxTokens,
|
||||
temperature: temp,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateSummary 调用 LLM 对历史消息生成摘要。
|
||||
func (g *AdapterSummaryGenerator) GenerateSummary(ctx context.Context, messages []api.Message) (string, error) {
|
||||
if len(messages) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 构建摘要请求的 prompt
|
||||
prompt := buildSummaryPrompt(messages)
|
||||
|
||||
req := &adapter.ChatRequest{
|
||||
RequestID: "summary-" + fmt.Sprintf("%d", len(messages)),
|
||||
Model: g.model,
|
||||
Messages: prompt,
|
||||
MaxTokens: g.maxTokens,
|
||||
Temperature: &g.temperature,
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
resp, err := g.adapter.ChatCompletion(ctx, req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("summary generation failed: %w", err)
|
||||
}
|
||||
|
||||
return resp.Content, nil
|
||||
}
|
||||
|
||||
// buildSummaryPrompt 构建用于生成摘要的消息列表。
|
||||
func buildSummaryPrompt(messages []api.Message) []api.Message {
|
||||
// 将历史消息拼接为文本
|
||||
var sb strings.Builder
|
||||
for _, m := range messages {
|
||||
role := m.Role
|
||||
content, _ := m.Content.(string)
|
||||
sb.WriteString(fmt.Sprintf("%s: %s\n", role, content))
|
||||
}
|
||||
|
||||
return []api.Message{
|
||||
{
|
||||
Role: "system",
|
||||
Content: "你是一个对话摘要助手。请将以下对话历史压缩为简洁的摘要," +
|
||||
"保留关键信息、用户意图和重要结论。摘要不超过200字。",
|
||||
},
|
||||
{
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf("请总结以下对话历史:\n\n%s", sb.String()),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
|
||||
// Assembler assembles context messages for a chat request.
|
||||
type Assembler struct {
|
||||
estimator *TokenEstimator
|
||||
cfg *config.ContextConfig
|
||||
estimator *TokenEstimator
|
||||
cfg *config.ContextConfig
|
||||
summarizer *Summarizer
|
||||
}
|
||||
|
||||
// NewAssembler creates a new context assembler.
|
||||
@@ -19,6 +20,11 @@ func NewAssembler(cfg *config.ContextConfig) *Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
// SetSummarizer 注入摘要器,启用后 trimSummaryAndRecent 策略将生成真实 LLM 摘要。
|
||||
func (a *Assembler) SetSummarizer(s *Summarizer) {
|
||||
a.summarizer = s
|
||||
}
|
||||
|
||||
// AssembleResult contains the assembled messages and metadata.
|
||||
type AssembleResult struct {
|
||||
Messages []api.Message
|
||||
@@ -143,13 +149,21 @@ func (a *Assembler) trimSummaryAndRecent(messages []api.Message, budget int) tri
|
||||
recentTokens += msgTokens
|
||||
}
|
||||
|
||||
// Add summary placeholder if we trimmed anything
|
||||
// Add summary if we trimmed anything
|
||||
result := make([]api.Message, 0, len(systemMsgs)+1+len(recentMsgs))
|
||||
result = append(result, systemMsgs...)
|
||||
if len(recentMsgs) < len(rest) {
|
||||
// 被裁剪的老消息
|
||||
trimmedMsgs := rest[:len(rest)-len(recentMsgs)]
|
||||
var summaryText string
|
||||
if a.summarizer != nil {
|
||||
summaryText = a.summarizer.Summarize(trimmedMsgs)
|
||||
} else {
|
||||
summaryText = "[Earlier conversation history has been summarized and omitted.]"
|
||||
}
|
||||
result = append(result, api.Message{
|
||||
Role: "system",
|
||||
Content: "[Earlier conversation history has been summarized and omitted.]",
|
||||
Content: summaryText,
|
||||
})
|
||||
}
|
||||
result = append(result, recentMsgs...)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/edgeai/gateway/pkg/api"
|
||||
)
|
||||
|
||||
// SummaryGenerator 定义摘要生成接口,允许注入不同的实现。
|
||||
type SummaryGenerator interface {
|
||||
// GenerateSummary 对给定消息生成对话摘要,返回摘要文本。
|
||||
GenerateSummary(ctx context.Context, messages []api.Message) (string, error)
|
||||
}
|
||||
|
||||
// Summarizer 使用 LLM 对历史消息生成对话摘要。
|
||||
type Summarizer struct {
|
||||
generator SummaryGenerator
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewSummarizer 创建一个新的摘要器。
|
||||
func NewSummarizer(generator SummaryGenerator, timeout time.Duration) *Summarizer {
|
||||
return &Summarizer{
|
||||
generator: generator,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Summarize 对被裁剪的老消息生成摘要。
|
||||
// 如果摘要生成失败,回退到占位符文本。
|
||||
func (s *Summarizer) Summarize(messages []api.Message) string {
|
||||
if s == nil || s.generator == nil || len(messages) == 0 {
|
||||
return "[Earlier conversation history has been summarized and omitted.]"
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
|
||||
defer cancel()
|
||||
|
||||
summary, err := s.generator.GenerateSummary(ctx, messages)
|
||||
if err != nil || strings.TrimSpace(summary) == "" {
|
||||
return "[Earlier conversation history has been summarized and omitted.]"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[Earlier conversation summary: %s]", strings.TrimSpace(summary))
|
||||
}
|
||||
Reference in New Issue
Block a user