mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-30 13:09:40 +02:00
fix: avoid duplicate summarization token limits
This commit is contained in:
1 parent
a89f21b49e
commit
bbfcb87103
5 files changed
+65
-15
No files matched your search
@@ -62,7 +62,7 @@ const (
|
||||
DefaultLatestUserMessageMaxRunes = 48000
|
||||
DefaultLatestUserMessageHeadRunes = 24000
|
||||
DefaultLatestUserMessageTailRunes = 24000
|
||||
DefaultSummarizationOutputReserveTokens = 8192
|
||||
DefaultSummarizationOutputReserveTokens = 40960
|
||||
)
|
||||
|
||||
// ProjectConfig 项目黑板(跨对话共享事实)配置。
|
||||
@@ -277,7 +277,7 @@ type MultiAgentEinoMiddlewareConfig struct {
|
||||
ReductionSubAgents bool `yaml:"reduction_sub_agents,omitempty" json:"reduction_sub_agents,omitempty"` // also attach to sub-agents
|
||||
// SummarizationTriggerRatio controls summarization trigger threshold as max_total_tokens * ratio (default 0.8).
|
||||
SummarizationTriggerRatio float64 `yaml:"summarization_trigger_ratio,omitempty" json:"summarization_trigger_ratio,omitempty"`
|
||||
// SummarizationOutputReserveTokens reserves completion headroom for the summarization model call (default 8192).
|
||||
// SummarizationOutputReserveTokens reserves completion headroom for the summarization model call (default 40960).
|
||||
SummarizationOutputReserveTokens int `yaml:"summarization_output_reserve_tokens,omitempty" json:"summarization_output_reserve_tokens,omitempty"`
|
||||
// SummarizationEmitInternalEvents controls middleware internal event emission (default true).
|
||||
SummarizationEmitInternalEvents *bool `yaml:"summarization_emit_internal_events,omitempty" json:"summarization_emit_internal_events,omitempty"`
|
||||
|
||||
@@ -301,9 +301,13 @@ func newEinoSummarizationModelOptions(outputReserve int, modelName, kind string,
|
||||
if strings.TrimSpace(kind) != "" && kind != "classic" {
|
||||
label = "eino " + kind + " summarization generate request"
|
||||
}
|
||||
return []model.Option{
|
||||
model.WithMaxTokens(outputReserve),
|
||||
einoopenai.WithMaxCompletionTokens(outputReserve),
|
||||
opts := make([]model.Option, 0, 4)
|
||||
if oa != nil && isEinoAgenticClaudeProvider(oa.Provider) {
|
||||
opts = append(opts, model.WithMaxTokens(outputReserve))
|
||||
} else {
|
||||
opts = append(opts, einoopenai.WithMaxCompletionTokens(outputReserve))
|
||||
}
|
||||
opts = append(opts,
|
||||
einoopenai.WithExtraHeader(map[string]string{
|
||||
copenai.SummarizationRequestHeader: "1",
|
||||
}),
|
||||
@@ -317,7 +321,8 @@ func newEinoSummarizationModelOptions(outputReserve int, modelName, kind string,
|
||||
}
|
||||
return stripReasoningFromSummarizationPayload(rawBody, oa)
|
||||
}),
|
||||
}
|
||||
)
|
||||
return opts
|
||||
}
|
||||
|
||||
// summarizationInputBudgetOpts controls spill/truncation behavior when a round alone exceeds budget.
|
||||
|
||||
@@ -260,12 +260,13 @@ func TestClaudeSummaryLargeBudgetStreamsThroughNativeSDK(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
factory := newEinoAgenticChatModelFactory(server.Client(), nil, nil)
|
||||
native, err := factory(ctx, config.OpenAIConfig{Provider: "claude", APIKey: "test-key", BaseURL: server.URL, Model: "claude-sonnet-4-20250514"}, einoModelModeNormal)
|
||||
oa := config.OpenAIConfig{Provider: "claude", APIKey: "test-key", BaseURL: server.URL, Model: "claude-sonnet-4-20250514"}
|
||||
native, err := factory(ctx, oa, einoModelModeNormal)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
input := EinoMessagesToAgentic([]*schema.Message{schema.UserMessage("summarize history")})
|
||||
opts := newEinoSummarizationModelOptions(64000, "claude-sonnet-4-20250514", "agentic", nil, nil)
|
||||
opts := newEinoSummarizationModelOptions(64000, "claude-sonnet-4-20250514", "agentic", &oa, nil)
|
||||
if _, err = native.Generate(ctx, input, opts...); err == nil || !strings.Contains(err.Error(), "streaming is required") {
|
||||
t.Fatalf("expected original SDK rejection, got %v", err)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package multiagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cyberstrike-ai/internal/config"
|
||||
|
||||
einoopenai "github.com/cloudwego/eino-ext/components/model/openai"
|
||||
"github.com/cloudwego/eino/components/model"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
)
|
||||
|
||||
func TestStripReasoningFromSummarizationPayload(t *testing.T) {
|
||||
@@ -93,14 +99,52 @@ func TestStripReasoningFromSummarizationPayloadHonorsOpenAICompatProfileForNonDe
|
||||
}
|
||||
}
|
||||
|
||||
func TestEinoSummarizationModelOptionsSetCommonMaxTokens(t *testing.T) {
|
||||
const outputReserve = 4096
|
||||
func TestEinoSummarizationModelOptionsSetOnlyMaxCompletionTokens(t *testing.T) {
|
||||
const outputReserve = 40960
|
||||
opts := newEinoSummarizationModelOptions(outputReserve, "minimax-m3", "agentic", nil, nil)
|
||||
common := model.GetCommonOptions(nil, opts...)
|
||||
if common == nil || common.MaxTokens == nil {
|
||||
t.Fatal("expected summarization options to set common max_tokens")
|
||||
if common != nil && common.MaxTokens != nil {
|
||||
t.Fatalf("common max_tokens = %d, want unset", *common.MaxTokens)
|
||||
}
|
||||
if *common.MaxTokens != outputReserve {
|
||||
t.Fatalf("max_tokens = %d, want %d", *common.MaxTokens, outputReserve)
|
||||
|
||||
bodyCh := make(chan map[string]any, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Errorf("decode request body: %v", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
bodyCh <- body
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Write([]byte("data: {\"id\":\"test\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"summary\"},\"finish_reason\":null}]}\n\n"))
|
||||
w.Write([]byte("data: {\"id\":\"test\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}]}\n\n"))
|
||||
w.Write([]byte("data: [DONE]\n\n"))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
chatModel, err := einoopenai.NewChatModel(context.Background(), &einoopenai.ChatModelConfig{
|
||||
APIKey: "test-key",
|
||||
BaseURL: server.URL,
|
||||
Model: "gpt-4o",
|
||||
HTTPClient: server.Client(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := newNonEmptySummaryChatModel(chatModel).Generate(context.Background(), []*schema.Message{schema.UserMessage("summarize")}, opts...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.TrimSpace(out.Content) != "summary" {
|
||||
t.Fatalf("summary content = %q", out.Content)
|
||||
}
|
||||
|
||||
body := <-bodyCh
|
||||
if _, ok := body["max_tokens"]; ok {
|
||||
t.Fatalf("request contained max_tokens: %#v", body)
|
||||
}
|
||||
if got, ok := body["max_completion_tokens"].(float64); !ok || int(got) != outputReserve {
|
||||
t.Fatalf("max_completion_tokens = %#v, want %d", body["max_completion_tokens"], outputReserve)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user