Files
Agent-System/internal/app/chat_adapters.go
T
WeiCHEN b33753d717 refactor(chat): break chat→app cycle with narrow ports
Invert chat dependencies onto composition-root adapters, replace
AssembleRequest coordinator embeds with local DTOs, and drop the
chat→app/* allowlist edges so arch-check can enforce the boundary.
2026-09-22 21:25:59 +08:00

141 lines
4.0 KiB
Go

package app
import (
"context"
"chat-agent-system/internal/app/chatflow"
"chat-agent-system/internal/app/intentflow"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/chat"
"chat-agent-system/internal/ctxmgr"
"chat-agent-system/internal/taskrun"
)
// AdaptChatMemory wraps chatflow.ChatLog as the chat-owned ChatMemory port.
func AdaptChatMemory(log chatflow.ChatLog) chat.ChatMemory {
if log == nil {
return nil
}
return chatMemoryAdapter{inner: log}
}
type chatMemoryAdapter struct {
inner chatflow.ChatLog
}
func (a chatMemoryAdapter) SearchMemory(ctx context.Context, userID int64, q chat.MemoryQuery) ([]ctxmgr.RecalledMemoryItem, error) {
return a.inner.SearchMemory(ctx, chatflow.Principal{UserID: userID, Action: chatflow.ActionAssemble}, chatflow.MemoryQuery{
Query: q.Query,
ProjectID: q.ProjectID,
SessionID: q.SessionID,
ExcludeSessionID: q.ExcludeSessionID,
TopK: q.TopK,
MinScore: q.MinScore,
})
}
// AdaptChatIntentPlans wraps intentflow.Service as the chat-owned IntentPlans port.
func AdaptChatIntentPlans(svc intentflow.Service) chat.IntentPlans {
if svc == nil {
return nil
}
return chatIntentPlansAdapter{inner: svc}
}
type chatIntentPlansAdapter struct {
inner intentflow.Service
}
func (a chatIntentPlansAdapter) HandleTurn(ctx context.Context, in chat.IntentTurnInput) (*chat.IntentTurnResult, error) {
tr, err := a.inner.HandleTurn(ctx, intentflow.TurnInput{
Principal: intentflow.Principal{
UserID: in.UserID,
Environment: in.Environment,
ProjectID: in.ProjectID,
SessionID: in.SessionID,
},
Text: in.Text,
ClientTurnID: in.ClientTurnID,
RequestDigest: in.RequestDigest,
})
if err != nil {
return nil, err
}
if tr == nil {
return &chat.IntentTurnResult{}, nil
}
return &chat.IntentTurnResult{Plan: mapIntentPlanView(tr.Plan)}, nil
}
func mapIntentPlanView(p *intentflow.PlanView) *chat.IntentPlanView {
if p == nil {
return nil
}
out := &chat.IntentPlanView{
PlanID: p.PlanID,
Revision: p.Revision,
Status: string(p.Status),
ExecutionMode: string(p.ExecutionMode),
ExpiresAt: p.ExpiresAt,
SafeSummary: p.SafeSummary,
}
if len(p.Steps) > 0 {
out.Steps = make([]chat.IntentStepView, 0, len(p.Steps))
for _, s := range p.Steps {
out.Steps = append(out.Steps, chat.IntentStepView{
StepID: s.StepID,
ActionKey: s.ActionKey,
Status: string(s.Status),
ExecutionMode: string(s.ExecutionMode),
DependsOn: append([]string(nil), s.DependsOn...),
SafeSummary: s.SafeSummary,
})
}
}
return out
}
// AdaptChatTaskStarter wraps taskrun.Manager as the chat-owned TaskStarter port.
func AdaptChatTaskStarter(m *taskrun.Manager) chat.TaskStarter {
if m == nil {
return nil
}
return chatTaskStarter{inner: m}
}
type chatTaskStarter struct {
inner *taskrun.Manager
}
func (a chatTaskStarter) CreateAndStart(ctx context.Context, runID string, record *taskrun.RunRecord, req chat.TaskStartRequest, resumeDir string) (*taskrun.RunRecord, error) {
return a.inner.CreateAndStart(ctx, runID, record, bizdev.TaskRunRequest{
TaskKey: req.TaskKey,
ProjectName: req.ProjectName,
UserID: req.UserID,
ProjectID: req.ProjectID,
SessionID: req.SessionID,
}, bizdev.StageRunRequest{}, resumeDir)
}
// BindChatSession attaches composition-root adapters onto a chat session.
// Callers that keep a mutable intentflow.Service (e.g. server test hooks) should
// prefer AdaptChat* helpers with their own fields so swaps stay visible.
func (rt *Runtime) BindChatSession(sess *chat.Session, env string) {
if rt == nil || sess == nil {
return
}
if env == "" {
env = "dev"
if rt.Cfg != nil && rt.Cfg.Env != "" {
env = rt.Cfg.Env
}
}
sess.ChatLog = AdaptChatMemory(rt.ChatLog)
sess.Actions = &chat.ActionServices{
DocGov: rt.Gov,
TaskRuns: AdaptChatTaskStarter(rt.TaskRuns),
IntentPlans: AdaptChatIntentPlans(rt.IntentPlans),
Environment: env,
}
}