Version: 0.9.75.dev.260505
后端: 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 迁移面
This commit is contained in:
194
backend/services/agent/sv/task_rpc_adapter.go
Normal file
194
backend/services/agent/sv/task_rpc_adapter.go
Normal file
@@ -0,0 +1,194 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user