mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 15:40:38 +02:00
373 lines
11 KiB
Go
373 lines
11 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
|
|
"github.com/cloudwego/eino/adk"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type einoRunRuntimeSessionConfig struct {
|
|
Context context.Context
|
|
Args *einoADKRunLoopArgs
|
|
Drain *einoRunEventDrain
|
|
BaseMessages []adk.Message
|
|
EmptyHint string
|
|
SnapshotMCPIDs func() []string
|
|
EinoRoleTag func(agent string) string
|
|
}
|
|
|
|
type einoRunRuntimeErrorResult struct {
|
|
Restarted bool
|
|
Result *RunResult
|
|
Err error
|
|
}
|
|
|
|
type einoRunRuntimeSession struct {
|
|
ctx context.Context
|
|
args *einoADKRunLoopArgs
|
|
orchMode string
|
|
conversationID string
|
|
progress func(eventType, message string, data interface{})
|
|
logger *zap.Logger
|
|
baseMsgs []adk.Message
|
|
msgs []adk.Message
|
|
drain *einoRunEventDrain
|
|
runMessages *einoRunMessageAccumulator
|
|
usage *einoRunUsageAccumulator
|
|
|
|
iter *adk.AsyncIterator[*adk.AgentEvent]
|
|
startFreshIter einoAgentEventIteratorStarter
|
|
|
|
unregisterAgentCancel func()
|
|
unregisterTurnLoopInterrupt func()
|
|
nativeCancelCause atomic.Value
|
|
|
|
transientRetry *einoTransientRunRetryHandler
|
|
runRecoveryHandler *einoRunRecoveryHandler
|
|
resultBuilder *einoRunResultBuilder
|
|
streamErrorHandler *einoStreamErrorHandler
|
|
completionHandler *einoRunCompletionHandler
|
|
cancellationHandler *einoRunCancellationHandler
|
|
}
|
|
|
|
func newEinoRunRuntimeSession(cfg einoRunRuntimeSessionConfig) *einoRunRuntimeSession {
|
|
if cfg.Context == nil {
|
|
cfg.Context = context.Background()
|
|
}
|
|
if cfg.Args == nil {
|
|
cfg.Args = &einoADKRunLoopArgs{}
|
|
}
|
|
if cfg.SnapshotMCPIDs == nil {
|
|
cfg.SnapshotMCPIDs = func() []string { return nil }
|
|
}
|
|
s := &einoRunRuntimeSession{
|
|
ctx: cfg.Context,
|
|
args: cfg.Args,
|
|
orchMode: cfg.Args.OrchMode,
|
|
conversationID: cfg.Args.ConversationID,
|
|
progress: cfg.Args.Progress,
|
|
logger: cfg.Args.Logger,
|
|
baseMsgs: cfg.BaseMessages,
|
|
msgs: append([]adk.Message(nil), cfg.BaseMessages...),
|
|
drain: cfg.Drain,
|
|
}
|
|
if s.drain != nil {
|
|
s.runMessages = s.drain.RunMessages()
|
|
s.usage = s.drain.Usage()
|
|
}
|
|
if s.runMessages == nil {
|
|
s.runMessages = newEinoRunMessageAccumulator(s.msgs)
|
|
}
|
|
s.initIteratorRuntime()
|
|
s.initRecoveryRuntime()
|
|
s.initResultRuntime(cfg.EmptyHint, cfg.SnapshotMCPIDs, cfg.EinoRoleTag)
|
|
return s
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) Iterator() *adk.AsyncIterator[*adk.AgentEvent] {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
return s.iter
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) Close() {
|
|
if s == nil {
|
|
return
|
|
}
|
|
callAndClearUnregister(&s.unregisterAgentCancel)
|
|
callAndClearUnregister(&s.unregisterTurnLoopInterrupt)
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) HandleIteratorContextError(err error) (*RunResult, error) {
|
|
if s == nil || s.cancellationHandler == nil {
|
|
return nil, err
|
|
}
|
|
return s.cancellationHandler.Handle(err)
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) HandleIteratorEnd() (completed bool, result *RunResult, err error) {
|
|
if s == nil {
|
|
return true, nil, nil
|
|
}
|
|
if ctxErr := s.ctx.Err(); ctxErr != nil {
|
|
result, err = s.HandleIteratorContextError(ctxErr)
|
|
return false, result, err
|
|
}
|
|
if s.completionHandler != nil {
|
|
s.completionHandler.Complete()
|
|
}
|
|
return true, nil, nil
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) HandleRunError(runErr error) einoRunRuntimeErrorResult {
|
|
if s == nil || runErr == nil {
|
|
return einoRunRuntimeErrorResult{}
|
|
}
|
|
restarted, fatal := s.maybeRestart(runErr)
|
|
if fatal != nil {
|
|
result, err := s.takePartial(fatal)
|
|
return einoRunRuntimeErrorResult{Result: result, Err: err}
|
|
}
|
|
return einoRunRuntimeErrorResult{Restarted: restarted}
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) HandleStreamError(streamErr error, agentName string) einoRunRuntimeErrorResult {
|
|
if s == nil || s.streamErrorHandler == nil || streamErr == nil {
|
|
return einoRunRuntimeErrorResult{}
|
|
}
|
|
handled := s.streamErrorHandler.Handle(streamErr, agentName)
|
|
return einoRunRuntimeErrorResult{
|
|
Restarted: handled.Restarted,
|
|
Result: handled.Result,
|
|
Err: handled.Err,
|
|
}
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) ConfirmRecovery() {
|
|
if s != nil && s.transientRetry != nil {
|
|
s.transientRetry.ConfirmRecovery()
|
|
}
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) BuildFinalResult() *RunResult {
|
|
if s == nil || s.resultBuilder == nil {
|
|
return &RunResult{}
|
|
}
|
|
s.emitUsageSummary("final")
|
|
return s.resultBuilder.BuildFinal()
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) takePartial(err error) (*RunResult, error) {
|
|
if s == nil || s.resultBuilder == nil {
|
|
return nil, err
|
|
}
|
|
s.emitUsageSummary("partial")
|
|
return s.resultBuilder.BuildPartial(err)
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) maybeRestart(runErr error) (restarted bool, fatal error) {
|
|
if s == nil || s.runRecoveryHandler == nil {
|
|
return false, runErr
|
|
}
|
|
recovery := s.runRecoveryHandler.Handle(runErr, s.runMessages.Messages(), s.runMessages.BaseCount())
|
|
if recovery.Fatal != nil {
|
|
return false, recovery.Fatal
|
|
}
|
|
if !recovery.Restarted {
|
|
return false, nil
|
|
}
|
|
s.msgs = recovery.RestartMsgs
|
|
s.iter = s.startFreshIter(s.msgs)
|
|
return true, nil
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) initIteratorRuntime() {
|
|
if s == nil || s.args == nil {
|
|
return
|
|
}
|
|
runnerCfg := adk.RunnerConfig{
|
|
Agent: s.args.DA,
|
|
// 启用 ADK 流式事件:plan_execute 也需要输出 reasoning/response 流,
|
|
// 与 deep/supervisor/eino_single 的前端体验保持一致。
|
|
EnableStreaming: true,
|
|
}
|
|
var cpStore *fileCheckPointStore
|
|
var checkPointID string
|
|
if checkpoint := newEinoCheckpointRuntime(s.args.CheckpointDir, s.conversationID, s.orchMode, s.logger); checkpoint != nil {
|
|
cpStore = checkpoint.Store
|
|
checkPointID = checkpoint.CheckPointID
|
|
runnerCfg.CheckPointStore = checkpoint.Store
|
|
}
|
|
runner := adk.NewRunner(s.ctx, runnerCfg)
|
|
runtimeCancelRegistrar := agentRuntimeCancelRegistrarFromContext(s.ctx)
|
|
turnLoopInterruptRegistrar := agentTurnLoopInterruptRegistrarFromContext(s.ctx)
|
|
runnerStarter := newEinoRunnerIteratorStarter(einoRunnerIteratorStarterConfig{
|
|
Context: s.ctx,
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Logger: s.logger,
|
|
Runner: runner,
|
|
CheckPointID: checkPointID,
|
|
NativeCancelCause: &s.nativeCancelCause,
|
|
UnregisterAgentCancel: &s.unregisterAgentCancel,
|
|
RuntimeCancelRegistrar: runtimeCancelRegistrar,
|
|
})
|
|
turnLoopStarter := newEinoTurnLoopIteratorStarter(einoTurnLoopIteratorStarterConfig{
|
|
Context: s.ctx,
|
|
Agent: s.args.DA,
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
Store: cpStore,
|
|
CheckPointID: checkPointID,
|
|
InterruptTimeout: s.args.TurnLoopInterruptTimeout,
|
|
NativeCancelCause: &s.nativeCancelCause,
|
|
UnregisterAgentCancel: &s.unregisterAgentCancel,
|
|
UnregisterTurnLoopInterrupt: &s.unregisterTurnLoopInterrupt,
|
|
RuntimeCancelRegistrar: runtimeCancelRegistrar,
|
|
TurnLoopInterruptRegistrar: turnLoopInterruptRegistrar,
|
|
})
|
|
useTurnLoop := turnLoopInterruptRegistrar != nil
|
|
s.startFreshIter = runnerStarter.Start
|
|
if useTurnLoop {
|
|
s.startFreshIter = turnLoopStarter.Start
|
|
}
|
|
if !useTurnLoop && cpStore != nil && checkPointID != "" {
|
|
s.iter = newEinoCheckpointResumeHandler(einoCheckpointResumeHandlerConfig{
|
|
Context: s.ctx,
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
Store: cpStore,
|
|
CheckPointID: checkPointID,
|
|
Resume: runnerStarter.Resume,
|
|
}).TryResume()
|
|
}
|
|
s.iter = newEinoInitialIteratorStartHandler(einoInitialIteratorStartHandlerConfig{
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Progress: s.progress,
|
|
UseTurnLoop: useTurnLoop,
|
|
StartRunner: runnerStarter.Start,
|
|
StartTurnLoop: turnLoopStarter.Start,
|
|
}).StartIfNeeded(s.iter, s.msgs)
|
|
|
|
pending := s.pending()
|
|
s.completionHandler = newEinoRunCompletionHandler(einoRunCompletionHandlerConfig{
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
Pending: pending,
|
|
Checkpoint: cpStore,
|
|
CheckpointID: checkPointID,
|
|
})
|
|
s.cancellationHandler = newEinoRunCancellationHandler(einoRunCancellationHandlerConfig{
|
|
Context: s.ctx,
|
|
ConversationID: s.conversationID,
|
|
Progress: s.progress,
|
|
Pending: pending,
|
|
TakePartial: s.takePartial,
|
|
})
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) initRecoveryRuntime() {
|
|
if s == nil || s.args == nil {
|
|
return
|
|
}
|
|
pending := s.pending()
|
|
contextOverflowRetry := newEinoContextOverflowRetryHandler(einoContextOverflowRetryConfig{
|
|
Context: s.ctx,
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Args: s.args,
|
|
BaseMsgs: s.baseMsgs,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
})
|
|
s.transientRetry = newEinoTransientRunRetryHandler(einoTransientRunRetryHandlerConfig{
|
|
Context: s.ctx,
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Args: s.args,
|
|
BaseMsgs: s.baseMsgs,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
Pending: pending,
|
|
})
|
|
runErrorHandler := newEinoRunErrorHandler(einoRunErrorHandlerConfig{
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Progress: s.progress,
|
|
Pending: pending,
|
|
NativeCancelFallback: s.nativeCancelCauseOrCanceled,
|
|
})
|
|
s.runRecoveryHandler = newEinoRunRecoveryHandler(einoRunRecoveryHandlerConfig{
|
|
ConversationID: s.conversationID,
|
|
OrchMode: s.orchMode,
|
|
Args: s.args,
|
|
BaseMsgs: s.baseMsgs,
|
|
Progress: s.progress,
|
|
Logger: s.logger,
|
|
RunError: runErrorHandler,
|
|
ContextOverflow: contextOverflowRetry,
|
|
Transient: s.transientRetry,
|
|
})
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) initResultRuntime(emptyHint string, snapshotMCPIDs func() []string, einoRoleTag func(agent string) string) {
|
|
if s == nil {
|
|
return
|
|
}
|
|
var assistantOutput *einoAssistantOutputAccumulator
|
|
if s.drain != nil {
|
|
assistantOutput = s.drain.AssistantOutput()
|
|
}
|
|
s.resultBuilder = newEinoRunResultBuilder(einoRunResultBuilderConfig{
|
|
OrchMode: s.orchMode,
|
|
EmptyHint: emptyHint,
|
|
RunMessages: s.runMessages,
|
|
AssistantOutput: assistantOutput,
|
|
SnapshotMCPIDs: snapshotMCPIDs,
|
|
ModelFacingTrace: func() []adk.Message { return modelFacingTraceSnapshot(s.args) },
|
|
})
|
|
s.streamErrorHandler = newEinoStreamErrorHandler(
|
|
s.ctx,
|
|
s.conversationID,
|
|
s.progress,
|
|
einoRoleTag,
|
|
s.maybeRestart,
|
|
s.takePartial,
|
|
)
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) pending() *einoPendingToolCalls {
|
|
if s == nil || s.drain == nil {
|
|
return nil
|
|
}
|
|
return s.drain.PendingToolCalls()
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) nativeCancelCauseOrCanceled() error {
|
|
if s != nil {
|
|
if v := s.nativeCancelCause.Load(); v != nil {
|
|
if err, ok := v.(error); ok && err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return context.Canceled
|
|
}
|
|
|
|
func (s *einoRunRuntimeSession) emitUsageSummary(reason string) bool {
|
|
if s == nil || s.usage == nil {
|
|
return false
|
|
}
|
|
return s.usage.EmitOnce(s.conversationID, s.orchMode, reason, s.progress, s.logger)
|
|
}
|