Files
ai-agent/internal/ai/application/runtime/service.go
T

141 lines
3.7 KiB
Go
Raw Normal View History

package runtime
import (
"context"
"encoding/json"
"time"
"agent-desk/internal/ai/runtime/executor"
workflowexecutor "agent-desk/internal/ai/runtime/workflow"
"agent-desk/internal/models"
"agent-desk/internal/pkg/utils"
"agent-desk/internal/repositories"
"github.com/mlogclub/simple/sqls"
)
type Service struct {
runtime *executor.Service
catalog *toolCatalog
prepare *prepareService
}
func NewService() *Service {
catalog := newToolCatalog()
return &Service{
runtime: executor.NewService(),
catalog: catalog,
prepare: newPrepareService(catalog),
}
}
func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) {
req.UserMessage.Content = utils.BuildRuntimeMessageText(req.UserMessage.MessageType, req.UserMessage.Content)
aiAgent, workflow, err := prepareWorkflowAgent(req.AIAgent)
if err != nil {
return nil, err
}
req.AIAgent = aiAgent
workflowResult, err := workflowexecutor.NewExecutor().Execute(ctx, workflowexecutor.Input{
Definition: workflow.Definition,
Conversation: req.Conversation,
UserMessage: req.UserMessage,
AIAgent: req.AIAgent,
AIConfig: req.AIConfig,
})
if err != nil {
return nil, err
}
if err := writeWorkflowRun(req, workflow, workflowResult, ""); err != nil {
return nil, err
}
return toWorkflowSummary(workflowResult, req.AIConfig.ModelName), nil
}
func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
aiAgent, _, err := prepareWorkflowAgent(req.AIAgent)
if err != nil {
return nil, err
}
req.AIAgent = aiAgent
toolSet, err := s.prepare.prepareToolsForResume(req)
if err != nil {
return nil, err
}
req.ToolSet = toolSet
summary, err := s.runtime.ExecuteResume(ctx, executor.ResumeInput{
Conversation: req.Conversation,
AIAgent: req.AIAgent,
AIConfig: req.AIConfig,
CheckPointID: req.CheckPointID,
ResumeData: req.ResumeData,
ToolSet: req.ToolSet,
})
if err != nil {
return toSummary(summary), err
}
return toSummary(summary), nil
}
func toWorkflowSummary(result *workflowexecutor.Result, modelName string) *Summary {
if result == nil {
return nil
}
trace := map[string]any{
"status": result.Status,
"nodePath": result.NodePath,
}
traceData, _ := json.Marshal(trace)
return &Summary{
Status: result.Status,
ReplyText: result.ReplyText,
ModelName: modelName,
PromptTokens: result.PromptTokens,
CompletionTokens: result.CompletionTokens,
RetrieverCount: result.RetrieverCount,
TraceData: string(traceData),
}
}
func writeWorkflowRun(req Request, workflow resolvedWorkflow, result *workflowexecutor.Result, errorMessage string) error {
if result == nil {
return nil
}
now := time.Now()
endedAt := now
nodeTypes := make(map[string]string, len(workflow.Definition.Nodes))
for _, node := range workflow.Definition.Nodes {
nodeTypes[node.ID] = node.Type
}
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
run := &models.AIWorkflowRun{
WorkflowID: workflow.WorkflowID,
WorkflowVersionID: workflow.VersionID,
ConversationID: req.Conversation.ID,
AIAgentID: req.AIAgent.ID,
MessageID: req.UserMessage.ID,
Status: 1,
StartedAt: now,
EndedAt: &endedAt,
ErrorMessage: errorMessage,
}
if err := repositories.AIWorkflowRunRepository.Create(ctx.Tx, run); err != nil {
return err
}
for _, nodeID := range result.NodePath {
nodeRun := &models.AIWorkflowNodeRun{
WorkflowRunID: run.ID,
NodeID: nodeID,
NodeType: nodeTypes[nodeID],
Status: 1,
StartedAt: now,
EndedAt: &endedAt,
}
if err := repositories.AIWorkflowNodeRunRepository.Create(ctx.Tx, nodeRun); err != nil {
return err
}
}
return nil
})
}