mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 15:40:38 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package multiagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudwego/eino/adk"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
einoTurnLoopInterruptPreemptTimeout = 3 * time.Second
|
||||
einoTurnLoopIdleStop = 250 * time.Millisecond
|
||||
)
|
||||
|
||||
// EinoTurnLoopItem is the conversation-level input unit consumed by an Eino
|
||||
// TurnLoop. The item is gob-friendly so it can be checkpointed by TurnLoop when
|
||||
// a CheckPointStore is configured.
|
||||
type EinoTurnLoopItem struct {
|
||||
Messages []*schema.Message
|
||||
Kind string
|
||||
Note string
|
||||
}
|
||||
|
||||
// EinoTurnLoopRuntime wraps Eino's native TurnLoop with the semantics this
|
||||
// project needs: persistent per-conversation runtime, user-supplement preempt,
|
||||
// and graceful idle shutdown.
|
||||
type EinoTurnLoopRuntime struct {
|
||||
loop *adk.TurnLoop[EinoTurnLoopItem, *schema.Message]
|
||||
interruptTimeout time.Duration
|
||||
}
|
||||
|
||||
type EinoTurnLoopRuntimeConfig struct {
|
||||
Agent adk.Agent
|
||||
InitialMessages []*schema.Message
|
||||
Store adk.CheckPointStore
|
||||
CheckpointID string
|
||||
EnableStreaming bool
|
||||
PrepareAgent func(context.Context, *adk.TurnLoop[EinoTurnLoopItem, *schema.Message], []EinoTurnLoopItem) (adk.Agent, error)
|
||||
OnAgentEvents func(context.Context, *adk.TurnContext[EinoTurnLoopItem, *schema.Message], *adk.AsyncIterator[*adk.AgentEvent]) error
|
||||
InterruptTimeout time.Duration
|
||||
}
|
||||
|
||||
func NewEinoTurnLoopRuntime(cfg EinoTurnLoopRuntimeConfig) *EinoTurnLoopRuntime {
|
||||
timeout := cfg.InterruptTimeout
|
||||
if timeout <= 0 {
|
||||
timeout = einoTurnLoopInterruptPreemptTimeout
|
||||
}
|
||||
enableStreaming := cfg.EnableStreaming
|
||||
prepareAgent := cfg.PrepareAgent
|
||||
if prepareAgent == nil {
|
||||
prepareAgent = func(context.Context, *adk.TurnLoop[EinoTurnLoopItem, *schema.Message], []EinoTurnLoopItem) (adk.Agent, error) {
|
||||
return cfg.Agent, nil
|
||||
}
|
||||
}
|
||||
loop := adk.NewTurnLoop[EinoTurnLoopItem, *schema.Message](adk.TurnLoopConfig[EinoTurnLoopItem, *schema.Message]{
|
||||
Store: cfg.Store,
|
||||
CheckpointID: cfg.CheckpointID,
|
||||
GenInput: func(ctx context.Context, _ *adk.TurnLoop[EinoTurnLoopItem, *schema.Message], items []EinoTurnLoopItem) (*adk.GenInputResult[EinoTurnLoopItem, *schema.Message], error) {
|
||||
msgs := mergeEinoTurnLoopMessages(items)
|
||||
return &adk.GenInputResult[EinoTurnLoopItem, *schema.Message]{
|
||||
RunCtx: ctx,
|
||||
Input: &adk.AgentInput{
|
||||
Messages: msgs,
|
||||
EnableStreaming: enableStreaming,
|
||||
},
|
||||
Consumed: items,
|
||||
}, nil
|
||||
},
|
||||
GenResume: func(ctx context.Context, _ *adk.TurnLoop[EinoTurnLoopItem, *schema.Message], interruptedItems, unhandledItems, newItems []EinoTurnLoopItem) (*adk.GenResumeResult[EinoTurnLoopItem, *schema.Message], error) {
|
||||
consumed := make([]EinoTurnLoopItem, 0, len(interruptedItems)+len(newItems))
|
||||
consumed = append(consumed, interruptedItems...)
|
||||
consumed = append(consumed, newItems...)
|
||||
remaining := append([]EinoTurnLoopItem(nil), unhandledItems...)
|
||||
return &adk.GenResumeResult[EinoTurnLoopItem, *schema.Message]{
|
||||
RunCtx: ctx,
|
||||
Consumed: consumed,
|
||||
Remaining: remaining,
|
||||
}, nil
|
||||
},
|
||||
PrepareAgent: prepareAgent,
|
||||
OnAgentEvents: cfg.OnAgentEvents,
|
||||
})
|
||||
if len(cfg.InitialMessages) > 0 {
|
||||
loop.Push(EinoTurnLoopItem{Kind: "initial", Messages: cloneSchemaMessages(cfg.InitialMessages)})
|
||||
}
|
||||
return &EinoTurnLoopRuntime{loop: loop, interruptTimeout: timeout}
|
||||
}
|
||||
|
||||
func (r *EinoTurnLoopRuntime) Run(ctx context.Context) {
|
||||
if r == nil || r.loop == nil {
|
||||
return
|
||||
}
|
||||
r.loop.Run(ctx)
|
||||
}
|
||||
|
||||
func (r *EinoTurnLoopRuntime) PushInterruptContinue(note string) bool {
|
||||
if r == nil || r.loop == nil {
|
||||
return false
|
||||
}
|
||||
item := EinoTurnLoopItem{
|
||||
Kind: "interrupt_continue",
|
||||
Note: strings.TrimSpace(note),
|
||||
Messages: []*schema.Message{schema.UserMessage(formatInterruptContinuePrompt(note))},
|
||||
}
|
||||
ok, ack := r.loop.Push(item, adk.WithPreemptTimeout[EinoTurnLoopItem, *schema.Message](adk.AnySafePoint, r.interruptTimeout))
|
||||
if ack != nil {
|
||||
go func() { <-ack }()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *EinoTurnLoopRuntime) StopImmediate(cause string) {
|
||||
if r == nil || r.loop == nil {
|
||||
return
|
||||
}
|
||||
r.loop.Stop(adk.WithImmediate(), adk.WithStopCause(cause))
|
||||
}
|
||||
|
||||
func (r *EinoTurnLoopRuntime) StopWhenIdle() {
|
||||
if r == nil || r.loop == nil {
|
||||
return
|
||||
}
|
||||
r.loop.Stop(adk.UntilIdleFor(einoTurnLoopIdleStop))
|
||||
}
|
||||
|
||||
func (r *EinoTurnLoopRuntime) Wait() *adk.TurnLoopExitState[EinoTurnLoopItem, *schema.Message] {
|
||||
if r == nil || r.loop == nil {
|
||||
return nil
|
||||
}
|
||||
return r.loop.Wait()
|
||||
}
|
||||
|
||||
func mergeEinoTurnLoopMessages(items []EinoTurnLoopItem) []*schema.Message {
|
||||
var msgs []*schema.Message
|
||||
for _, item := range items {
|
||||
msgs = append(msgs, cloneSchemaMessages(item.Messages)...)
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
func formatInterruptContinuePrompt(note string) string {
|
||||
note = strings.TrimSpace(note)
|
||||
if note == "" {
|
||||
return "用户请求中断当前推理并继续。请基于已经完成的步骤继续,不要重复已完成工具调用。"
|
||||
}
|
||||
return "用户请求中断当前推理并补充上下文后继续:\n" + note +
|
||||
"\n\n请基于已经完成的步骤继续,不要重复已完成工具调用。"
|
||||
}
|
||||
|
||||
func cloneSchemaMessages(in []*schema.Message) []*schema.Message {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]*schema.Message, 0, len(in))
|
||||
for _, msg := range in {
|
||||
if msg == nil {
|
||||
continue
|
||||
}
|
||||
cp := *msg
|
||||
if len(msg.ToolCalls) > 0 {
|
||||
cp.ToolCalls = append([]schema.ToolCall(nil), msg.ToolCalls...)
|
||||
}
|
||||
if len(msg.MultiContent) > 0 {
|
||||
cp.MultiContent = append([]schema.ChatMessagePart(nil), msg.MultiContent...)
|
||||
}
|
||||
if len(msg.UserInputMultiContent) > 0 {
|
||||
cp.UserInputMultiContent = append([]schema.MessageInputPart(nil), msg.UserInputMultiContent...)
|
||||
}
|
||||
if len(msg.AssistantGenMultiContent) > 0 {
|
||||
cp.AssistantGenMultiContent = append([]schema.MessageOutputPart(nil), msg.AssistantGenMultiContent...)
|
||||
}
|
||||
cp.Extra = cloneAnyMap(msg.Extra)
|
||||
out = append(out, &cp)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user