后端: 1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent - 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge - 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段 - 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流 - 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
195 lines
5.8 KiB
Go
195 lines
5.8 KiB
Go
package sv
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/LoveLosita/smartflow/backend/model"
|
|
"github.com/LoveLosita/smartflow/backend/respond"
|
|
agentmodel "github.com/LoveLosita/smartflow/backend/services/agent/model"
|
|
taskcontracts "github.com/LoveLosita/smartflow/backend/shared/contracts/task"
|
|
)
|
|
|
|
const quickTaskCreateRPCTimeout = 3 * time.Second
|
|
|
|
// TaskRPCClient 描述 agent 快捷任务链路访问 task zrpc 所需的最小能力。
|
|
//
|
|
// 职责边界:
|
|
// 1. 只覆盖快捷任务的创建和查询,不暴露 task DAO 或其它写接口;
|
|
// 2. 不要求 agent 编排层感知 pb / grpc 细节;
|
|
// 3. 错误原样返回,由 quick_task 节点转换成面向用户的失败文案。
|
|
type TaskRPCClient interface {
|
|
AddTask(ctx context.Context, req taskcontracts.AddTaskRequest) (json.RawMessage, error)
|
|
GetUserTasks(ctx context.Context, userID int) (json.RawMessage, error)
|
|
}
|
|
|
|
type taskRPCAdapter struct {
|
|
client TaskRPCClient
|
|
}
|
|
|
|
// NewTaskRPCQuickTaskDeps 把 task zrpc client 适配成 agent 快捷任务依赖。
|
|
//
|
|
// 职责边界:
|
|
// 1. 只替换 agent quick task 的 task DAO 直连路径;
|
|
// 2. 不迁移 task-class upsert、schedule provider 或 agent 编排本体;
|
|
// 3. client 为空时返回零值依赖,让 quick_task 节点沿用既有“依赖缺失则报错”语义。
|
|
func NewTaskRPCQuickTaskDeps(client TaskRPCClient) agentmodel.QuickTaskDeps {
|
|
if client == nil {
|
|
return agentmodel.QuickTaskDeps{}
|
|
}
|
|
adapter := &taskRPCAdapter{client: client}
|
|
return agentmodel.QuickTaskDeps{
|
|
CreateTask: adapter.CreateTask,
|
|
QueryTasks: adapter.QueryTasks,
|
|
}
|
|
}
|
|
|
|
// CreateTask 通过 task zrpc 创建四象限任务,返回 task_id。
|
|
func (a *taskRPCAdapter) CreateTask(
|
|
userID int,
|
|
title string,
|
|
priorityGroup int,
|
|
estimatedSections int,
|
|
deadlineAt *time.Time,
|
|
urgencyThresholdAt *time.Time,
|
|
) (int, error) {
|
|
if a == nil || a.client == nil {
|
|
return 0, errors.New("task rpc client is nil")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), quickTaskCreateRPCTimeout)
|
|
defer cancel()
|
|
|
|
// 调用目的:把 quick_task 节点产出的结构化任务写入 task 服务,避免 agent 继续直连 tasks 表。
|
|
raw, err := a.client.AddTask(ctx, taskcontracts.AddTaskRequest{
|
|
UserID: userID,
|
|
Title: strings.TrimSpace(title),
|
|
PriorityGroup: priorityGroup,
|
|
EstimatedSections: estimatedSections,
|
|
DeadlineAt: deadlineAt,
|
|
UrgencyThresholdAt: urgencyThresholdAt,
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var resp taskcontracts.AddTaskResponse
|
|
if err := json.Unmarshal(raw, &resp); err != nil {
|
|
return 0, err
|
|
}
|
|
if resp.ID <= 0 {
|
|
return 0, errors.New("task rpc add task returned invalid task id")
|
|
}
|
|
return resp.ID, nil
|
|
}
|
|
|
|
// QueryTasks 通过 task zrpc 读取用户任务,再复用 agent 侧既有过滤、排序和展示转换语义。
|
|
func (a *taskRPCAdapter) QueryTasks(
|
|
ctx context.Context,
|
|
userID int,
|
|
params agentmodel.TaskQueryParams,
|
|
) ([]agentmodel.TaskQueryResult, error) {
|
|
if a == nil || a.client == nil {
|
|
return nil, errors.New("task rpc client is nil")
|
|
}
|
|
raw, err := a.client.GetUserTasks(ctx, userID)
|
|
if err != nil {
|
|
if errors.Is(err, respond.UserTasksEmpty) {
|
|
return []agentmodel.TaskQueryResult{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
var items []taskcontracts.TaskListItem
|
|
if len(raw) > 0 && string(raw) != "null" {
|
|
if err := json.Unmarshal(raw, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
tasks := taskListItemsToModels(items)
|
|
|
|
req := agentmodel.TaskQueryRequest{
|
|
UserID: userID,
|
|
Quadrant: params.Quadrant,
|
|
SortBy: params.SortBy,
|
|
Order: params.Order,
|
|
Limit: params.Limit,
|
|
IncludeCompleted: params.IncludeCompleted,
|
|
Keyword: params.Keyword,
|
|
DeadlineBefore: params.DeadlineBefore,
|
|
DeadlineAfter: params.DeadlineAfter,
|
|
}
|
|
|
|
filtered := make([]model.Task, 0, len(tasks))
|
|
for _, task := range tasks {
|
|
if !taskMatchesQueryFilter(task, req) {
|
|
continue
|
|
}
|
|
filtered = append(filtered, task)
|
|
}
|
|
|
|
sortTasksForQuery(filtered, req)
|
|
if req.Limit > 0 && len(filtered) > req.Limit {
|
|
filtered = filtered[:req.Limit]
|
|
}
|
|
return taskModelsToQueryResults(filtered), nil
|
|
}
|
|
|
|
func taskListItemsToModels(items []taskcontracts.TaskListItem) []model.Task {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]model.Task, 0, len(items))
|
|
for _, item := range items {
|
|
result = append(result, model.Task{
|
|
ID: item.ID,
|
|
UserID: item.UserID,
|
|
Title: item.Title,
|
|
Priority: item.PriorityGroup,
|
|
EstimatedSections: model.NormalizeEstimatedSections(&item.EstimatedSections),
|
|
IsCompleted: item.IsCompleted,
|
|
DeadlineAt: parseTaskListTime(item.Deadline),
|
|
UrgencyThresholdAt: parseTaskListTime(item.UrgencyThresholdAt),
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
func taskModelsToQueryResults(tasks []model.Task) []agentmodel.TaskQueryResult {
|
|
if len(tasks) == 0 {
|
|
return []agentmodel.TaskQueryResult{}
|
|
}
|
|
results := make([]agentmodel.TaskQueryResult, 0, len(tasks))
|
|
for _, task := range tasks {
|
|
deadlineStr := ""
|
|
if task.DeadlineAt != nil {
|
|
deadlineStr = task.DeadlineAt.In(time.Local).Format("2006-01-02 15:04")
|
|
}
|
|
results = append(results, agentmodel.TaskQueryResult{
|
|
ID: task.ID,
|
|
Title: task.Title,
|
|
PriorityGroup: task.Priority,
|
|
EstimatedSections: model.NormalizeEstimatedSections(&task.EstimatedSections),
|
|
IsCompleted: task.IsCompleted,
|
|
DeadlineAt: deadlineStr,
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
func parseTaskListTime(value string) *time.Time {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return nil
|
|
}
|
|
for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02 15:04:05", "2006-01-02 15:04"} {
|
|
parsed, err := time.ParseInLocation(layout, value, time.Local)
|
|
if err == nil {
|
|
return &parsed
|
|
}
|
|
}
|
|
return nil
|
|
}
|