mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 07:30:53 +02:00
210 lines
6.2 KiB
Go
210 lines
6.2 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/cloudwego/eino/adk"
|
|
"github.com/cloudwego/eino/schema"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// appendEinoAgenticChatModelTailMiddlewares appends protocol-neutral handlers for
|
|
// TypedChatModelAgent[*schema.AgenticMessage]. Classic ReAct history repair
|
|
// handlers stay on the schema.Message path because AgenticMessage has native
|
|
// content blocks for function calls/results.
|
|
func appendEinoAgenticChatModelTailMiddlewares(
|
|
handlers []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage],
|
|
cfg einoChatModelTailConfig,
|
|
) []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage] {
|
|
handlers = append(handlers, newAgenticSystemMessageNormalizerMiddleware(cfg.logger, cfg.phase))
|
|
handlers = append(handlers, newAgenticContinuationUserDedupMiddleware(cfg.logger, cfg.phase))
|
|
if cfg.agenticSummarization != nil {
|
|
handlers = append(handlers, cfg.agenticSummarization)
|
|
}
|
|
if !cfg.skipTrace && cfg.trace != nil {
|
|
if capMw := newAgenticModelFacingTraceMiddleware(cfg.trace); capMw != nil {
|
|
handlers = append(handlers, capMw)
|
|
}
|
|
}
|
|
return handlers
|
|
}
|
|
|
|
type agenticSystemMessageNormalizerMiddleware struct {
|
|
*adk.TypedBaseChatModelAgentMiddleware[*schema.AgenticMessage]
|
|
logger *zap.Logger
|
|
phase string
|
|
}
|
|
|
|
func newAgenticSystemMessageNormalizerMiddleware(logger *zap.Logger, phase string) adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage] {
|
|
return &agenticSystemMessageNormalizerMiddleware{
|
|
TypedBaseChatModelAgentMiddleware: &adk.TypedBaseChatModelAgentMiddleware[*schema.AgenticMessage]{},
|
|
logger: logger,
|
|
phase: phase,
|
|
}
|
|
}
|
|
|
|
func (m *agenticSystemMessageNormalizerMiddleware) BeforeModelRewriteState(
|
|
ctx context.Context,
|
|
state *adk.TypedChatModelAgentState[*schema.AgenticMessage],
|
|
mc *adk.TypedModelContext[*schema.AgenticMessage],
|
|
) (context.Context, *adk.TypedChatModelAgentState[*schema.AgenticMessage], error) {
|
|
_ = mc
|
|
if m == nil || state == nil || len(state.Messages) == 0 {
|
|
return ctx, state, nil
|
|
}
|
|
before := countAgenticSystemMessages(state.Messages)
|
|
if before <= 1 {
|
|
return ctx, state, nil
|
|
}
|
|
normalized := normalizeSingleLeadingAgenticSystemMessage(state.Messages)
|
|
if len(normalized) == len(state.Messages) && countAgenticSystemMessages(normalized) >= before {
|
|
return ctx, state, nil
|
|
}
|
|
if m.logger != nil {
|
|
m.logger.Info("eino agentic system messages merged",
|
|
zap.String("phase", m.phase),
|
|
zap.Int("system_before", before),
|
|
zap.Int("system_after", countAgenticSystemMessages(normalized)),
|
|
zap.Int("messages_before", len(state.Messages)),
|
|
zap.Int("messages_after", len(normalized)),
|
|
)
|
|
}
|
|
out := *state
|
|
out.Messages = normalized
|
|
return ctx, &out, nil
|
|
}
|
|
|
|
type agenticContinuationUserDedupMiddleware struct {
|
|
*adk.TypedBaseChatModelAgentMiddleware[*schema.AgenticMessage]
|
|
logger *zap.Logger
|
|
phase string
|
|
}
|
|
|
|
func newAgenticContinuationUserDedupMiddleware(logger *zap.Logger, phase string) adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage] {
|
|
return &agenticContinuationUserDedupMiddleware{
|
|
TypedBaseChatModelAgentMiddleware: &adk.TypedBaseChatModelAgentMiddleware[*schema.AgenticMessage]{},
|
|
logger: logger,
|
|
phase: phase,
|
|
}
|
|
}
|
|
|
|
func (m *agenticContinuationUserDedupMiddleware) BeforeModelRewriteState(
|
|
ctx context.Context,
|
|
state *adk.TypedChatModelAgentState[*schema.AgenticMessage],
|
|
mc *adk.TypedModelContext[*schema.AgenticMessage],
|
|
) (context.Context, *adk.TypedChatModelAgentState[*schema.AgenticMessage], error) {
|
|
_ = mc
|
|
if m == nil || state == nil || len(state.Messages) == 0 {
|
|
return ctx, state, nil
|
|
}
|
|
deduped, dropped := dedupAgenticContinuationUserMessages(state.Messages)
|
|
if dropped == 0 {
|
|
return ctx, state, nil
|
|
}
|
|
if m.logger != nil {
|
|
m.logger.Info("eino agentic continuation user messages deduplicated",
|
|
zap.String("phase", m.phase),
|
|
zap.Int("dropped", dropped),
|
|
zap.Int("messages_before", len(state.Messages)),
|
|
zap.Int("messages_after", len(deduped)),
|
|
)
|
|
}
|
|
out := *state
|
|
out.Messages = deduped
|
|
return ctx, &out, nil
|
|
}
|
|
|
|
func countAgenticSystemMessages(msgs []*schema.AgenticMessage) int {
|
|
n := 0
|
|
for _, msg := range msgs {
|
|
if msg != nil && msg.Role == schema.AgenticRoleTypeSystem {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
func normalizeSingleLeadingAgenticSystemMessage(msgs []*schema.AgenticMessage) []*schema.AgenticMessage {
|
|
var systemParts []string
|
|
out := make([]*schema.AgenticMessage, 0, len(msgs))
|
|
for _, msg := range msgs {
|
|
if msg == nil {
|
|
continue
|
|
}
|
|
if msg.Role == schema.AgenticRoleTypeSystem {
|
|
if text := strings.TrimSpace(agenticMessageText(msg)); text != "" {
|
|
systemParts = append(systemParts, text)
|
|
}
|
|
continue
|
|
}
|
|
out = append(out, msg)
|
|
}
|
|
if len(systemParts) == 0 {
|
|
return out
|
|
}
|
|
merged := schema.SystemAgenticMessage(strings.Join(systemParts, "\n\n"))
|
|
return append([]*schema.AgenticMessage{merged}, out...)
|
|
}
|
|
|
|
func dedupAgenticContinuationUserMessages(msgs []*schema.AgenticMessage) ([]*schema.AgenticMessage, int) {
|
|
lastIdx := -1
|
|
contCount := 0
|
|
for i, msg := range msgs {
|
|
if !isAgenticContinuationUserMessage(msg) {
|
|
continue
|
|
}
|
|
contCount++
|
|
lastIdx = i
|
|
}
|
|
if contCount <= 1 {
|
|
return msgs, 0
|
|
}
|
|
out := make([]*schema.AgenticMessage, 0, len(msgs)-(contCount-1))
|
|
dropped := 0
|
|
for i, msg := range msgs {
|
|
if isAgenticContinuationUserMessage(msg) && i != lastIdx {
|
|
dropped++
|
|
continue
|
|
}
|
|
out = append(out, msg)
|
|
}
|
|
return out, dropped
|
|
}
|
|
|
|
func isAgenticContinuationUserMessage(msg *schema.AgenticMessage) bool {
|
|
if msg == nil || msg.Role != schema.AgenticRoleTypeUser {
|
|
return false
|
|
}
|
|
return strings.Contains(agenticMessageText(msg), continuationSessionMarker)
|
|
}
|
|
|
|
func agenticMessageText(msg *schema.AgenticMessage) string {
|
|
if msg == nil {
|
|
return ""
|
|
}
|
|
var b strings.Builder
|
|
for _, block := range msg.ContentBlocks {
|
|
if block == nil {
|
|
continue
|
|
}
|
|
switch {
|
|
case block.UserInputText != nil:
|
|
if s := strings.TrimSpace(block.UserInputText.Text); s != "" {
|
|
if b.Len() > 0 {
|
|
b.WriteByte('\n')
|
|
}
|
|
b.WriteString(s)
|
|
}
|
|
case block.AssistantGenText != nil:
|
|
if s := strings.TrimSpace(block.AssistantGenText.Text); s != "" {
|
|
if b.Len() > 0 {
|
|
b.WriteByte('\n')
|
|
}
|
|
b.WriteString(s)
|
|
}
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|