feat: implement runtime reply service and refactor related components
This commit is contained in:
@@ -0,0 +1,280 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"cs-agent/internal/ai/runtime/internal/engine"
|
||||||
|
"cs-agent/internal/ai/runtime/registry"
|
||||||
|
"cs-agent/internal/ai/runtime/tools"
|
||||||
|
"cs-agent/internal/ai/skills"
|
||||||
|
"cs-agent/internal/models"
|
||||||
|
"cs-agent/internal/pkg/toolx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
runtime *engine.Service
|
||||||
|
registry *registry.Registry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService() *Service {
|
||||||
|
return &Service{
|
||||||
|
runtime: engine.NewService(),
|
||||||
|
registry: registry.NewRegistry(
|
||||||
|
tools.NewTriageServiceRequestTool(),
|
||||||
|
tools.NewAnalyzeConversationTool(),
|
||||||
|
tools.NewPrepareTicketDraftTool(),
|
||||||
|
tools.NewCreateTicketGraphTool(),
|
||||||
|
tools.NewHandoffGraphTool(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) {
|
||||||
|
selectedSkill, skillReason, skillTrace, skillErr := s.selectSkill(ctx, req)
|
||||||
|
req.SelectedSkill = selectedSkill
|
||||||
|
req.SkillRouteReason = skillReason
|
||||||
|
req.SkillRouteTrace = skillTrace
|
||||||
|
if req.SelectedSkill != nil {
|
||||||
|
req.SelectedSkill = cloneSkillDefinition(req.SelectedSkill)
|
||||||
|
}
|
||||||
|
if err := s.prepareToolsForRun(&req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
summary, err := s.runtime.ExecuteRun(ctx, engine.RunInput{
|
||||||
|
Conversation: req.Conversation,
|
||||||
|
UserMessage: req.UserMessage,
|
||||||
|
AIAgent: req.AIAgent,
|
||||||
|
AIConfig: req.AIConfig,
|
||||||
|
SelectedSkill: req.SelectedSkill,
|
||||||
|
SkillRouteReason: req.SkillRouteReason,
|
||||||
|
SkillRouteTrace: req.SkillRouteTrace,
|
||||||
|
CheckPointID: req.CheckPointID,
|
||||||
|
ToolSet: req.ToolSet,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ret := toSummary(summary)
|
||||||
|
if ret != nil && skillErr != nil && strings.TrimSpace(ret.PlanReason) == "" {
|
||||||
|
ret.PlanReason = "skill_failed_fallback_runtime"
|
||||||
|
}
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
ret := toSummary(summary)
|
||||||
|
if ret != nil && skillErr != nil && strings.TrimSpace(ret.PlanReason) == "" {
|
||||||
|
ret.PlanReason = "skill_failed_fallback_runtime"
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
|
||||||
|
if err := s.prepareToolsForResume(&req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
summary, err := s.runtime.ExecuteResume(ctx, engine.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 (s *Service) prepareToolsForRun(req *Request) error {
|
||||||
|
if req == nil || req.ToolSet != nil || s.registry == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
toolSet, err := s.registry.Resolve(registry.Context{
|
||||||
|
Conversation: req.Conversation,
|
||||||
|
AIAgent: req.AIAgent,
|
||||||
|
AIConfig: req.AIConfig,
|
||||||
|
UserMessage: req.UserMessage,
|
||||||
|
AllowedToolCodes: resolveAllowedToolCodes(req.AIAgent, req.SelectedSkill),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.ToolSet = toolSet
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) prepareToolsForResume(req *ResumeRequest) error {
|
||||||
|
if req == nil || req.ToolSet != nil || s.registry == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
toolSet, err := s.registry.Resolve(registry.Context{
|
||||||
|
Conversation: req.Conversation,
|
||||||
|
AIAgent: req.AIAgent,
|
||||||
|
AIConfig: req.AIConfig,
|
||||||
|
AllowedToolCodes: parseAgentAllowedToolCodes(req.AIAgent),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.ToolSet = toolSet
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSummary(summary *engine.Summary) *Summary {
|
||||||
|
if summary == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ret := &Summary{
|
||||||
|
RunID: summary.RunID,
|
||||||
|
Status: summary.Status,
|
||||||
|
ReplyText: summary.ReplyText,
|
||||||
|
PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode),
|
||||||
|
PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName),
|
||||||
|
PlanReason: strings.TrimSpace(summary.SkillRouteReason),
|
||||||
|
SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace),
|
||||||
|
SkillAllowedToolCodes: append([]string(nil), summary.SkillAllowedToolCodes...),
|
||||||
|
ModelName: summary.ModelName,
|
||||||
|
PromptTokens: summary.PromptTokens,
|
||||||
|
CompletionTokens: summary.CompletionTokens,
|
||||||
|
HistoryMessageCount: summary.HistoryMessageCount,
|
||||||
|
RetrieverCount: summary.RetrieverCount,
|
||||||
|
ToolCallCount: summary.ToolCallCount,
|
||||||
|
ToolCodes: append([]string(nil), summary.ToolCodes...),
|
||||||
|
InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...),
|
||||||
|
CheckPointID: summary.CheckPointID,
|
||||||
|
Interrupted: summary.Interrupted,
|
||||||
|
TraceData: summary.TraceData,
|
||||||
|
ErrorMessage: summary.ErrorMessage,
|
||||||
|
}
|
||||||
|
if len(summary.Interrupts) > 0 {
|
||||||
|
ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts))
|
||||||
|
for _, item := range summary.Interrupts {
|
||||||
|
ret.Interrupts = append(ret.Interrupts, InterruptContextSummary{
|
||||||
|
Type: item.Type,
|
||||||
|
ID: item.ID,
|
||||||
|
InfoPreview: item.InfoPreview,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) selectSkill(ctx context.Context, req Request) (*models.SkillDefinition, string, string, error) {
|
||||||
|
if req.AIAgent == nil || req.AIConfig == nil || req.UserMessage == nil || req.Conversation == nil {
|
||||||
|
return nil, "", "", nil
|
||||||
|
}
|
||||||
|
result, err := skills.Select(ctx, skills.RuntimeContext{
|
||||||
|
AIAgentID: req.AIAgent.ID,
|
||||||
|
UserMessage: strings.TrimSpace(req.UserMessage.Content),
|
||||||
|
ConversationID: req.Conversation.ID,
|
||||||
|
ManualSkillCode: strings.TrimSpace(req.ManualSkillCode),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", err
|
||||||
|
}
|
||||||
|
if result == nil || result.Plan == nil || result.Plan.Skill == nil {
|
||||||
|
traceData := marshalSkillRouteTrace(result)
|
||||||
|
reason := ""
|
||||||
|
if result != nil && result.Plan != nil {
|
||||||
|
reason = strings.TrimSpace(result.Plan.MatchReason)
|
||||||
|
}
|
||||||
|
return nil, reason, traceData, nil
|
||||||
|
}
|
||||||
|
return result.Plan.Skill, strings.TrimSpace(result.Plan.MatchReason), marshalSkillRouteTrace(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalSkillRouteTrace(result *skills.ExecutionResult) string {
|
||||||
|
if result == nil || result.Plan == nil || result.Plan.RouteTrace == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(result.Plan.RouteTrace)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneSkillDefinition(item *models.SkillDefinition) *models.SkillDefinition {
|
||||||
|
if item == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clone := *item
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSkillAllowedToolCodes(skill *models.SkillDefinition) []string {
|
||||||
|
if skill == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
raw := strings.TrimSpace(skill.ToolWhitelist)
|
||||||
|
if raw == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var items []string
|
||||||
|
if err := json.Unmarshal([]byte(raw), &items); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ret := make([]string, 0, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
item = toolx.NormalizeToolCodeAlias(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ret = append(ret, item)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAgentAllowedToolCodes(aiAgent *models.AIAgent) []string {
|
||||||
|
if aiAgent == nil || strings.TrimSpace(aiAgent.AllowedMCPTools) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
items, err := toolx.ParseAgentMCPToolsJSON(aiAgent.AllowedMCPTools)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ret := make([]string, 0, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
toolCode := strings.TrimSpace(item.ToolCode)
|
||||||
|
toolCode = toolx.NormalizeToolCodeAlias(toolCode)
|
||||||
|
if toolCode == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ret = append(ret, toolCode)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveAllowedToolCodes(aiAgent *models.AIAgent, skill *models.SkillDefinition) []string {
|
||||||
|
agentAllowed := parseAgentAllowedToolCodes(aiAgent)
|
||||||
|
skillAllowed := parseSkillAllowedToolCodes(skill)
|
||||||
|
switch {
|
||||||
|
case len(agentAllowed) == 0:
|
||||||
|
return skillAllowed
|
||||||
|
case len(skillAllowed) == 0:
|
||||||
|
return agentAllowed
|
||||||
|
default:
|
||||||
|
skillSet := make(map[string]struct{}, len(skillAllowed))
|
||||||
|
for _, item := range skillAllowed {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
item = toolx.NormalizeToolCodeAlias(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
skillSet[item] = struct{}{}
|
||||||
|
}
|
||||||
|
ret := make([]string, 0, len(agentAllowed))
|
||||||
|
for _, item := range agentAllowed {
|
||||||
|
item = strings.TrimSpace(item)
|
||||||
|
item = toolx.NormalizeToolCodeAlias(item)
|
||||||
|
if item == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := skillSet[item]; ok {
|
||||||
|
ret = append(ret, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cs-agent/internal/ai/runtime/registry"
|
||||||
|
"cs-agent/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Conversation *models.Conversation
|
||||||
|
UserMessage *models.Message
|
||||||
|
AIAgent *models.AIAgent
|
||||||
|
AIConfig *models.AIConfig
|
||||||
|
ManualSkillCode string
|
||||||
|
SelectedSkill *models.SkillDefinition
|
||||||
|
SkillRouteReason string
|
||||||
|
SkillRouteTrace string
|
||||||
|
CheckPointID string
|
||||||
|
ToolSet *registry.ToolSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResumeRequest struct {
|
||||||
|
Conversation *models.Conversation
|
||||||
|
AIAgent *models.AIAgent
|
||||||
|
AIConfig *models.AIConfig
|
||||||
|
CheckPointID string
|
||||||
|
ResumeData map[string]any
|
||||||
|
ToolSet *registry.ToolSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type InterruptContextSummary struct {
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
InfoPreview string `json:"infoPreview,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Summary struct {
|
||||||
|
RunID string
|
||||||
|
Status string
|
||||||
|
ReplyText string
|
||||||
|
PlannedSkillCode string
|
||||||
|
PlannedSkillName string
|
||||||
|
PlanReason string
|
||||||
|
SkillRouteTrace string
|
||||||
|
SkillAllowedToolCodes []string
|
||||||
|
ModelName string
|
||||||
|
PromptTokens int
|
||||||
|
CompletionTokens int
|
||||||
|
HistoryMessageCount int
|
||||||
|
RetrieverCount int
|
||||||
|
ToolCallCount int
|
||||||
|
ToolCodes []string
|
||||||
|
InvokedToolCodes []string
|
||||||
|
CheckPointID string
|
||||||
|
Interrupted bool
|
||||||
|
Interrupts []InterruptContextSummary
|
||||||
|
TraceData string
|
||||||
|
ErrorMessage string
|
||||||
|
}
|
||||||
@@ -34,6 +34,10 @@ func NewService() *Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) {
|
func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) {
|
||||||
|
return s.ExecuteRun(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ExecuteRun(ctx context.Context, req RunInput) (*RunResult, error) {
|
||||||
summary := &Summary{
|
summary := &Summary{
|
||||||
RunID: uuid.NewString(),
|
RunID: uuid.NewString(),
|
||||||
Status: "started",
|
Status: "started",
|
||||||
@@ -182,6 +186,10 @@ func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
|
func (s *Service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
|
||||||
|
return s.ExecuteResume(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ExecuteResume(ctx context.Context, req ResumeInput) (*RunResult, error) {
|
||||||
summary := &Summary{
|
summary := &Summary{
|
||||||
RunID: uuid.NewString(),
|
RunID: uuid.NewString(),
|
||||||
Status: "started",
|
Status: "started",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"cs-agent/internal/models"
|
"cs-agent/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Request struct {
|
type RunInput struct {
|
||||||
Conversation *models.Conversation
|
Conversation *models.Conversation
|
||||||
UserMessage *models.Message
|
UserMessage *models.Message
|
||||||
AIAgent *models.AIAgent
|
AIAgent *models.AIAgent
|
||||||
@@ -17,7 +17,7 @@ type Request struct {
|
|||||||
ToolSet *registry.ToolSet
|
ToolSet *registry.ToolSet
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResumeRequest struct {
|
type ResumeInput struct {
|
||||||
Conversation *models.Conversation
|
Conversation *models.Conversation
|
||||||
AIAgent *models.AIAgent
|
AIAgent *models.AIAgent
|
||||||
AIConfig *models.AIConfig
|
AIConfig *models.AIConfig
|
||||||
@@ -32,7 +32,7 @@ type InterruptContextSummary struct {
|
|||||||
InfoPreview string `json:"infoPreview,omitempty"`
|
InfoPreview string `json:"infoPreview,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Summary struct {
|
type RunResult struct {
|
||||||
RunID string
|
RunID string
|
||||||
Status string
|
Status string
|
||||||
ReplyText string
|
ReplyText string
|
||||||
@@ -55,3 +55,7 @@ type Summary struct {
|
|||||||
TraceData string
|
TraceData string
|
||||||
ErrorMessage string
|
ErrorMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Request = RunInput
|
||||||
|
type ResumeRequest = ResumeInput
|
||||||
|
type Summary = RunResult
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cs-agent/internal/models"
|
||||||
|
"cs-agent/internal/pkg/enums"
|
||||||
|
|
||||||
|
"github.com/mlogclub/simple/common/strs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type replyEligibility struct{}
|
||||||
|
|
||||||
|
func newReplyEligibility() *replyEligibility {
|
||||||
|
return &replyEligibility{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *replyEligibility) CanReply(conversation models.Conversation, message models.Message, aiAgent models.AIAgent) bool {
|
||||||
|
if message.SenderType != enums.IMSenderTypeCustomer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if conversation.HandoffAt != nil || conversation.CurrentAssigneeID > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if aiAgent.ServiceMode == enums.IMConversationServiceModeHumanOnly {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strs.IsBlank(message.Content) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"cs-agent/internal/repositories"
|
"cs-agent/internal/repositories"
|
||||||
svc "cs-agent/internal/services"
|
svc "cs-agent/internal/services"
|
||||||
|
|
||||||
"github.com/mlogclub/simple/common/strs"
|
|
||||||
"github.com/mlogclub/simple/sqls"
|
"github.com/mlogclub/simple/sqls"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,10 +26,16 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newAIReplyService() *aiReplyService {
|
func newAIReplyService() *aiReplyService {
|
||||||
return &aiReplyService{}
|
return &aiReplyService{
|
||||||
|
eligibility: newReplyEligibility(),
|
||||||
|
executor: newRuntimeReplyExecutor(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type aiReplyService struct{}
|
type aiReplyService struct {
|
||||||
|
eligibility *replyEligibility
|
||||||
|
executor *runtimeReplyExecutor
|
||||||
|
}
|
||||||
|
|
||||||
type aiReplyTraceData struct {
|
type aiReplyTraceData struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
@@ -86,16 +91,7 @@ func (s *aiReplyService) TriggerReply(ctx context.Context, conversation models.C
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if message.SenderType != enums.IMSenderTypeCustomer {
|
if s.eligibility != nil && !s.eligibility.CanReply(conversation, message, aiAgent) {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if conversation.HandoffAt != nil || conversation.CurrentAssigneeID > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if aiAgent.ServiceMode == enums.IMConversationServiceModeHumanOnly {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strs.IsBlank(message.Content) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -104,33 +100,11 @@ func (s *aiReplyService) TriggerReply(ctx context.Context, conversation models.C
|
|||||||
if pendingInterrupt := svc.ConversationInterruptService.FindLatestPendingByConversationID(conversation.ID); pendingInterrupt != nil {
|
if pendingInterrupt := svc.ConversationInterruptService.FindLatestPendingByConversationID(conversation.ID); pendingInterrupt != nil {
|
||||||
return s.resumePendingInterrupt(ctx, conversation, message, aiAgent, pendingInterrupt, trace, &summary)
|
return s.resumePendingInterrupt(ctx, conversation, message, aiAgent, pendingInterrupt, trace, &summary)
|
||||||
}
|
}
|
||||||
aiConfig := svc.AIConfigService.Get(aiAgent.AIConfigID)
|
|
||||||
if aiConfig == nil {
|
|
||||||
return fmt.Errorf("ai config is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimeStartedAt := time.Now()
|
|
||||||
var err error
|
var err error
|
||||||
summary, err = Service.Run(ctx, Request{
|
summary, err = s.executor.Run(ctx, conversation, message, aiAgent, trace)
|
||||||
Conversation: &conversation,
|
|
||||||
UserMessage: &message,
|
|
||||||
AIAgent: &aiAgent,
|
|
||||||
AIConfig: aiConfig,
|
|
||||||
})
|
|
||||||
trace.RuntimeLatencyMs = time.Since(runtimeStartedAt).Milliseconds()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
trace.Status = "runtime_error"
|
|
||||||
trace.FinalAction = "error"
|
|
||||||
if summary != nil {
|
|
||||||
trace.Runtime = json.RawMessage(summary.TraceData)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Status = "runtime_prepared"
|
|
||||||
trace.FinalAction = toRunLogFinalAction(summary)
|
|
||||||
if summary != nil && strings.TrimSpace(summary.TraceData) != "" {
|
|
||||||
trace.Runtime = json.RawMessage(summary.TraceData)
|
|
||||||
}
|
|
||||||
if summary != nil && summary.Interrupted {
|
if summary != nil && summary.Interrupted {
|
||||||
return s.handleInterruptedSummary(conversation, message, aiAgent, summary, trace)
|
return s.handleInterruptedSummary(conversation, message, aiAgent, summary, trace)
|
||||||
}
|
}
|
||||||
@@ -152,29 +126,11 @@ func (s *aiReplyService) resumePendingInterrupt(ctx context.Context, conversatio
|
|||||||
if pendingInterrupt == nil {
|
if pendingInterrupt == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
aiConfig := svc.AIConfigService.Get(aiAgent.AIConfigID)
|
summary, err := s.executor.ResumePendingInterrupt(ctx, conversation, message, aiAgent, pendingInterrupt, trace)
|
||||||
if aiConfig == nil {
|
|
||||||
return fmt.Errorf("ai config is nil")
|
|
||||||
}
|
|
||||||
runtimeStartedAt := time.Now()
|
|
||||||
trace.ResumeSource = "pending_interrupt"
|
|
||||||
summary, err := Service.Resume(ctx, ResumeRequest{
|
|
||||||
Conversation: &conversation,
|
|
||||||
AIAgent: &aiAgent,
|
|
||||||
AIConfig: aiConfig,
|
|
||||||
CheckPointID: strings.TrimSpace(pendingInterrupt.CheckPointID),
|
|
||||||
ResumeData: map[string]any{
|
|
||||||
strings.TrimSpace(pendingInterrupt.InterruptID): strings.TrimSpace(message.Content),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
trace.RuntimeLatencyMs = time.Since(runtimeStartedAt).Milliseconds()
|
|
||||||
*summaryRef = summary
|
*summaryRef = summary
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isCheckpointMissingError(err) {
|
if isCheckpointMissingError(err) {
|
||||||
summary = &Summary{
|
summary = expiredInterruptSummary()
|
||||||
Status: "expired",
|
|
||||||
ReplyText: graphs.ConfirmationExpiredReply,
|
|
||||||
}
|
|
||||||
*summaryRef = summary
|
*summaryRef = summary
|
||||||
trace.Status = "interrupt_expired"
|
trace.Status = "interrupt_expired"
|
||||||
trace.FinalAction = "expired"
|
trace.FinalAction = "expired"
|
||||||
@@ -194,18 +150,8 @@ func (s *aiReplyService) resumePendingInterrupt(ctx context.Context, conversatio
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
trace.Status = "runtime_error"
|
|
||||||
trace.FinalAction = "error"
|
|
||||||
if summary != nil {
|
|
||||||
trace.Runtime = json.RawMessage(summary.TraceData)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Status = "runtime_prepared"
|
|
||||||
trace.FinalAction = toRunLogFinalAction(summary)
|
|
||||||
if summary != nil && strings.TrimSpace(summary.TraceData) != "" {
|
|
||||||
trace.Runtime = json.RawMessage(summary.TraceData)
|
|
||||||
}
|
|
||||||
if summary != nil && summary.Interrupted {
|
if summary != nil && summary.Interrupted {
|
||||||
return s.handleInterruptedResume(conversation, message, aiAgent, pendingInterrupt, summary, trace)
|
return s.handleInterruptedResume(conversation, message, aiAgent, pendingInterrupt, summary, trace)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cs-agent/internal/ai/runtime/graphs"
|
||||||
|
"cs-agent/internal/models"
|
||||||
|
svc "cs-agent/internal/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
type runtimeReplyExecutor struct{}
|
||||||
|
|
||||||
|
func newRuntimeReplyExecutor() *runtimeReplyExecutor {
|
||||||
|
return &runtimeReplyExecutor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *runtimeReplyExecutor) Run(ctx context.Context, conversation models.Conversation, message models.Message, aiAgent models.AIAgent, trace *aiReplyTraceData) (*Summary, error) {
|
||||||
|
aiConfig := svc.AIConfigService.Get(aiAgent.AIConfigID)
|
||||||
|
if aiConfig == nil {
|
||||||
|
return nil, fmt.Errorf("ai config is nil")
|
||||||
|
}
|
||||||
|
runtimeStartedAt := time.Now()
|
||||||
|
summary, err := Service.Run(ctx, Request{
|
||||||
|
Conversation: &conversation,
|
||||||
|
UserMessage: &message,
|
||||||
|
AIAgent: &aiAgent,
|
||||||
|
AIConfig: aiConfig,
|
||||||
|
})
|
||||||
|
if trace != nil {
|
||||||
|
trace.RuntimeLatencyMs = time.Since(runtimeStartedAt).Milliseconds()
|
||||||
|
e.fillTraceFromSummary(trace, summary, err)
|
||||||
|
}
|
||||||
|
return summary, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *runtimeReplyExecutor) ResumePendingInterrupt(ctx context.Context, conversation models.Conversation, message models.Message, aiAgent models.AIAgent, pendingInterrupt *models.ConversationInterrupt, trace *aiReplyTraceData) (*Summary, error) {
|
||||||
|
if pendingInterrupt == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
aiConfig := svc.AIConfigService.Get(aiAgent.AIConfigID)
|
||||||
|
if aiConfig == nil {
|
||||||
|
return nil, fmt.Errorf("ai config is nil")
|
||||||
|
}
|
||||||
|
runtimeStartedAt := time.Now()
|
||||||
|
if trace != nil {
|
||||||
|
trace.ResumeSource = "pending_interrupt"
|
||||||
|
}
|
||||||
|
summary, err := Service.Resume(ctx, ResumeRequest{
|
||||||
|
Conversation: &conversation,
|
||||||
|
AIAgent: &aiAgent,
|
||||||
|
AIConfig: aiConfig,
|
||||||
|
CheckPointID: strings.TrimSpace(pendingInterrupt.CheckPointID),
|
||||||
|
ResumeData: map[string]any{
|
||||||
|
strings.TrimSpace(pendingInterrupt.InterruptID): strings.TrimSpace(message.Content),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if trace != nil {
|
||||||
|
trace.RuntimeLatencyMs = time.Since(runtimeStartedAt).Milliseconds()
|
||||||
|
e.fillTraceFromSummary(trace, summary, err)
|
||||||
|
}
|
||||||
|
return summary, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *runtimeReplyExecutor) fillTraceFromSummary(trace *aiReplyTraceData, summary *Summary, runErr error) {
|
||||||
|
if trace == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if runErr != nil {
|
||||||
|
trace.Status = "runtime_error"
|
||||||
|
trace.FinalAction = "error"
|
||||||
|
if summary != nil {
|
||||||
|
trace.Runtime = json.RawMessage(summary.TraceData)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
trace.Status = "runtime_prepared"
|
||||||
|
trace.FinalAction = toRunLogFinalAction(summary)
|
||||||
|
if summary != nil && strings.TrimSpace(summary.TraceData) != "" {
|
||||||
|
trace.Runtime = json.RawMessage(summary.TraceData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expiredInterruptSummary() *Summary {
|
||||||
|
return &Summary{
|
||||||
|
Status: "expired",
|
||||||
|
ReplyText: graphs.ConfirmationExpiredReply,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,281 +2,32 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"cs-agent/internal/ai/runtime/internal/engine"
|
runtimeapp "cs-agent/internal/ai/runtime/app"
|
||||||
"cs-agent/internal/ai/runtime/registry"
|
|
||||||
"cs-agent/internal/ai/runtime/tools"
|
|
||||||
"cs-agent/internal/ai/skills"
|
|
||||||
"cs-agent/internal/models"
|
|
||||||
"cs-agent/internal/pkg/toolx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Service = newService()
|
var Service = newService()
|
||||||
|
|
||||||
func newService() *service {
|
func newService() *service {
|
||||||
return &service{
|
return &service{
|
||||||
runtime: engine.NewService(),
|
app: runtimeapp.NewService(),
|
||||||
registry: registry.NewRegistry(
|
|
||||||
tools.NewTriageServiceRequestTool(),
|
|
||||||
tools.NewAnalyzeConversationTool(),
|
|
||||||
tools.NewPrepareTicketDraftTool(),
|
|
||||||
tools.NewCreateTicketGraphTool(),
|
|
||||||
tools.NewHandoffGraphTool(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
runtime *engine.Service
|
app *runtimeapp.Service
|
||||||
registry *registry.Registry
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Run(ctx context.Context, req Request) (*Summary, error) {
|
func (s *service) Run(ctx context.Context, req Request) (*Summary, error) {
|
||||||
selectedSkill, skillReason, skillTrace, skillErr := s.selectSkill(ctx, req)
|
if s == nil || s.app == nil {
|
||||||
req.SelectedSkill = selectedSkill
|
return nil, nil
|
||||||
req.SkillRouteReason = skillReason
|
|
||||||
req.SkillRouteTrace = skillTrace
|
|
||||||
if req.SelectedSkill != nil {
|
|
||||||
req.SelectedSkill = cloneSkillDefinition(req.SelectedSkill)
|
|
||||||
}
|
}
|
||||||
if err := s.prepareToolsForRun(&req); err != nil {
|
return s.app.Run(ctx, req)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
summary, err := s.runtime.Run(ctx, engine.Request{
|
|
||||||
Conversation: req.Conversation,
|
|
||||||
UserMessage: req.UserMessage,
|
|
||||||
AIAgent: req.AIAgent,
|
|
||||||
AIConfig: req.AIConfig,
|
|
||||||
SelectedSkill: req.SelectedSkill,
|
|
||||||
SkillRouteReason: req.SkillRouteReason,
|
|
||||||
SkillRouteTrace: req.SkillRouteTrace,
|
|
||||||
CheckPointID: req.CheckPointID,
|
|
||||||
ToolSet: req.ToolSet,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
ret := toSummary(summary)
|
|
||||||
if ret != nil && skillErr != nil && strings.TrimSpace(ret.PlanReason) == "" {
|
|
||||||
ret.PlanReason = "skill_failed_fallback_runtime"
|
|
||||||
}
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
ret := toSummary(summary)
|
|
||||||
if ret != nil && skillErr != nil && strings.TrimSpace(ret.PlanReason) == "" {
|
|
||||||
ret.PlanReason = "skill_failed_fallback_runtime"
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
|
func (s *service) Resume(ctx context.Context, req ResumeRequest) (*Summary, error) {
|
||||||
if err := s.prepareToolsForResume(&req); err != nil {
|
if s == nil || s.app == nil {
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
|
||||||
summary, err := s.runtime.Resume(ctx, engine.ResumeRequest{
|
|
||||||
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 (s *service) prepareToolsForRun(req *Request) error {
|
|
||||||
if req == nil || req.ToolSet != nil || s.registry == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
toolSet, err := s.registry.Resolve(registry.Context{
|
|
||||||
Conversation: req.Conversation,
|
|
||||||
AIAgent: req.AIAgent,
|
|
||||||
AIConfig: req.AIConfig,
|
|
||||||
UserMessage: req.UserMessage,
|
|
||||||
AllowedToolCodes: resolveAllowedToolCodes(req.AIAgent, req.SelectedSkill),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.ToolSet = toolSet
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) prepareToolsForResume(req *ResumeRequest) error {
|
|
||||||
if req == nil || req.ToolSet != nil || s.registry == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
toolSet, err := s.registry.Resolve(registry.Context{
|
|
||||||
Conversation: req.Conversation,
|
|
||||||
AIAgent: req.AIAgent,
|
|
||||||
AIConfig: req.AIConfig,
|
|
||||||
AllowedToolCodes: parseAgentAllowedToolCodes(req.AIAgent),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.ToolSet = toolSet
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toSummary(summary *engine.Summary) *Summary {
|
|
||||||
if summary == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret := &Summary{
|
|
||||||
RunID: summary.RunID,
|
|
||||||
Status: summary.Status,
|
|
||||||
ReplyText: summary.ReplyText,
|
|
||||||
PlannedSkillCode: strings.TrimSpace(summary.SelectedSkillCode),
|
|
||||||
PlannedSkillName: strings.TrimSpace(summary.SelectedSkillName),
|
|
||||||
PlanReason: strings.TrimSpace(summary.SkillRouteReason),
|
|
||||||
SkillRouteTrace: strings.TrimSpace(summary.SkillRouteTrace),
|
|
||||||
SkillAllowedToolCodes: append([]string(nil), summary.SkillAllowedToolCodes...),
|
|
||||||
ModelName: summary.ModelName,
|
|
||||||
PromptTokens: summary.PromptTokens,
|
|
||||||
CompletionTokens: summary.CompletionTokens,
|
|
||||||
HistoryMessageCount: summary.HistoryMessageCount,
|
|
||||||
RetrieverCount: summary.RetrieverCount,
|
|
||||||
ToolCallCount: summary.ToolCallCount,
|
|
||||||
ToolCodes: append([]string(nil), summary.ToolCodes...),
|
|
||||||
InvokedToolCodes: append([]string(nil), summary.InvokedToolCodes...),
|
|
||||||
CheckPointID: summary.CheckPointID,
|
|
||||||
Interrupted: summary.Interrupted,
|
|
||||||
TraceData: summary.TraceData,
|
|
||||||
ErrorMessage: summary.ErrorMessage,
|
|
||||||
}
|
|
||||||
if len(summary.Interrupts) > 0 {
|
|
||||||
ret.Interrupts = make([]InterruptContextSummary, 0, len(summary.Interrupts))
|
|
||||||
for _, item := range summary.Interrupts {
|
|
||||||
ret.Interrupts = append(ret.Interrupts, InterruptContextSummary{
|
|
||||||
Type: item.Type,
|
|
||||||
ID: item.ID,
|
|
||||||
InfoPreview: item.InfoPreview,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) selectSkill(ctx context.Context, req Request) (*models.SkillDefinition, string, string, error) {
|
|
||||||
if req.AIAgent == nil || req.AIConfig == nil || req.UserMessage == nil || req.Conversation == nil {
|
|
||||||
return nil, "", "", nil
|
|
||||||
}
|
|
||||||
result, err := skills.Select(ctx, skills.RuntimeContext{
|
|
||||||
AIAgentID: req.AIAgent.ID,
|
|
||||||
UserMessage: strings.TrimSpace(req.UserMessage.Content),
|
|
||||||
ConversationID: req.Conversation.ID,
|
|
||||||
ManualSkillCode: strings.TrimSpace(req.ManualSkillCode),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", "", err
|
|
||||||
}
|
|
||||||
if result == nil || result.Plan == nil || result.Plan.Skill == nil {
|
|
||||||
traceData := marshalSkillRouteTrace(result)
|
|
||||||
reason := ""
|
|
||||||
if result != nil && result.Plan != nil {
|
|
||||||
reason = strings.TrimSpace(result.Plan.MatchReason)
|
|
||||||
}
|
|
||||||
return nil, reason, traceData, nil
|
|
||||||
}
|
|
||||||
return result.Plan.Skill, strings.TrimSpace(result.Plan.MatchReason), marshalSkillRouteTrace(result), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalSkillRouteTrace(result *skills.ExecutionResult) string {
|
|
||||||
if result == nil || result.Plan == nil || result.Plan.RouteTrace == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
buf, err := json.Marshal(result.Plan.RouteTrace)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return string(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneSkillDefinition(item *models.SkillDefinition) *models.SkillDefinition {
|
|
||||||
if item == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
clone := *item
|
|
||||||
return &clone
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSkillAllowedToolCodes(skill *models.SkillDefinition) []string {
|
|
||||||
if skill == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
raw := strings.TrimSpace(skill.ToolWhitelist)
|
|
||||||
if raw == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var items []string
|
|
||||||
if err := json.Unmarshal([]byte(raw), &items); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret := make([]string, 0, len(items))
|
|
||||||
for _, item := range items {
|
|
||||||
item = strings.TrimSpace(item)
|
|
||||||
item = toolx.NormalizeToolCodeAlias(item)
|
|
||||||
if item == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, item)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAgentAllowedToolCodes(aiAgent *models.AIAgent) []string {
|
|
||||||
if aiAgent == nil || strings.TrimSpace(aiAgent.AllowedMCPTools) == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
items, err := toolx.ParseAgentMCPToolsJSON(aiAgent.AllowedMCPTools)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ret := make([]string, 0, len(items))
|
|
||||||
for _, item := range items {
|
|
||||||
toolCode := strings.TrimSpace(item.ToolCode)
|
|
||||||
toolCode = toolx.NormalizeToolCodeAlias(toolCode)
|
|
||||||
if toolCode == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ret = append(ret, toolCode)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveAllowedToolCodes(aiAgent *models.AIAgent, skill *models.SkillDefinition) []string {
|
|
||||||
agentAllowed := parseAgentAllowedToolCodes(aiAgent)
|
|
||||||
skillAllowed := parseSkillAllowedToolCodes(skill)
|
|
||||||
switch {
|
|
||||||
case len(agentAllowed) == 0:
|
|
||||||
return skillAllowed
|
|
||||||
case len(skillAllowed) == 0:
|
|
||||||
return agentAllowed
|
|
||||||
default:
|
|
||||||
skillSet := make(map[string]struct{}, len(skillAllowed))
|
|
||||||
for _, item := range skillAllowed {
|
|
||||||
item = strings.TrimSpace(item)
|
|
||||||
item = toolx.NormalizeToolCodeAlias(item)
|
|
||||||
if item == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
skillSet[item] = struct{}{}
|
|
||||||
}
|
|
||||||
ret := make([]string, 0, len(agentAllowed))
|
|
||||||
for _, item := range agentAllowed {
|
|
||||||
item = strings.TrimSpace(item)
|
|
||||||
item = toolx.NormalizeToolCodeAlias(item)
|
|
||||||
if item == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := skillSet[item]; ok {
|
|
||||||
ret = append(ret, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
return s.app.Resume(ctx, req)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,9 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import runtimeapp "cs-agent/internal/ai/runtime/app"
|
||||||
"cs-agent/internal/ai/runtime/registry"
|
|
||||||
"cs-agent/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Request struct {
|
// TODO 这里为什么再设置一下类型别名,不能直接在外部使用 runtimeapp.Request 之类的类型呢?
|
||||||
Conversation *models.Conversation
|
type Request = runtimeapp.Request
|
||||||
UserMessage *models.Message
|
type ResumeRequest = runtimeapp.ResumeRequest
|
||||||
AIAgent *models.AIAgent
|
type InterruptContextSummary = runtimeapp.InterruptContextSummary
|
||||||
AIConfig *models.AIConfig
|
type Summary = runtimeapp.Summary
|
||||||
ManualSkillCode string
|
|
||||||
SelectedSkill *models.SkillDefinition
|
|
||||||
SkillRouteReason string
|
|
||||||
SkillRouteTrace string
|
|
||||||
CheckPointID string
|
|
||||||
ToolSet *registry.ToolSet
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResumeRequest struct {
|
|
||||||
Conversation *models.Conversation
|
|
||||||
AIAgent *models.AIAgent
|
|
||||||
AIConfig *models.AIConfig
|
|
||||||
CheckPointID string
|
|
||||||
ResumeData map[string]any
|
|
||||||
ToolSet *registry.ToolSet
|
|
||||||
}
|
|
||||||
|
|
||||||
type InterruptContextSummary struct {
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
InfoPreview string `json:"infoPreview,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Summary struct {
|
|
||||||
RunID string
|
|
||||||
Status string
|
|
||||||
ReplyText string
|
|
||||||
PlannedSkillCode string
|
|
||||||
PlannedSkillName string
|
|
||||||
PlanReason string
|
|
||||||
SkillRouteTrace string
|
|
||||||
SkillAllowedToolCodes []string
|
|
||||||
ModelName string
|
|
||||||
PromptTokens int
|
|
||||||
CompletionTokens int
|
|
||||||
HistoryMessageCount int
|
|
||||||
RetrieverCount int
|
|
||||||
ToolCallCount int
|
|
||||||
ToolCodes []string
|
|
||||||
InvokedToolCodes []string
|
|
||||||
CheckPointID string
|
|
||||||
Interrupted bool
|
|
||||||
Interrupts []InterruptContextSummary
|
|
||||||
TraceData string
|
|
||||||
ErrorMessage string
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user