mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-18 17:17:30 +08:00
audit node task dispatches and wire agent_task request metadata
This commit is contained in:
@@ -51,6 +51,7 @@
|
|||||||
- 设备动作响应统一:`ok/code/error/payload`(code 示例:`ok` `unsupported_action` `transport_error`)
|
- 设备动作响应统一:`ok/code/error/payload`(code 示例:`ok` `unsupported_action` `transport_error`)
|
||||||
- 设备 `payload` 规范字段:`media_type` `storage` `url|path|image` `meta`
|
- 设备 `payload` 规范字段:`media_type` `storage` `url|path|image` `meta`
|
||||||
- 支持 `agent_task`:主节点可向具备 `model` 能力的子节点下发任务,子节点返回执行结果
|
- 支持 `agent_task`:主节点可向具备 `model` 能力的子节点下发任务,子节点返回执行结果
|
||||||
|
- 节点分发审计写入:`memory/nodes-dispatch-audit.jsonl`
|
||||||
|
|
||||||
实现位置:
|
实现位置:
|
||||||
- `pkg/nodes/types.go`
|
- `pkg/nodes/types.go`
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ A `nodes` tool control-plane PoC is now available:
|
|||||||
- unified device response envelope: `ok/code/error/payload` (code examples: `ok`, `unsupported_action`, `transport_error`)
|
- unified device response envelope: `ok/code/error/payload` (code examples: `ok`, `unsupported_action`, `transport_error`)
|
||||||
- device `payload` normalized fields: `media_type` `storage` `url|path|image` `meta`
|
- device `payload` normalized fields: `media_type` `storage` `url|path|image` `meta`
|
||||||
- supports `agent_task`: parent node can dispatch tasks to child nodes with `model` capability and receive execution results
|
- supports `agent_task`: parent node can dispatch tasks to child nodes with `model` capability and receive execution results
|
||||||
|
- node dispatch audit is persisted to `memory/nodes-dispatch-audit.jsonl`
|
||||||
|
|
||||||
Implementation:
|
Implementation:
|
||||||
- `pkg/nodes/types.go`
|
- `pkg/nodes/types.go`
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
nodesRouter := &nodes.Router{P2P: &nodes.StubP2PTransport{}, Relay: &nodes.HTTPRelayTransport{Manager: nodesManager}}
|
nodesRouter := &nodes.Router{P2P: &nodes.StubP2PTransport{}, Relay: &nodes.HTTPRelayTransport{Manager: nodesManager}}
|
||||||
toolsRegistry.Register(tools.NewNodesTool(nodesManager, nodesRouter))
|
toolsRegistry.Register(tools.NewNodesTool(nodesManager, nodesRouter, filepath.Join(workspace, "memory", "nodes-dispatch-audit.jsonl")))
|
||||||
|
|
||||||
if cs != nil {
|
if cs != nil {
|
||||||
toolsRegistry.Register(tools.NewRemindTool(cs))
|
toolsRegistry.Register(tools.NewRemindTool(cs))
|
||||||
|
|||||||
@@ -4,18 +4,24 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"clawgo/pkg/nodes"
|
"clawgo/pkg/nodes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodesTool provides an OpenClaw-style control surface for paired nodes.
|
// NodesTool provides an OpenClaw-style control surface for paired nodes.
|
||||||
type NodesTool struct {
|
type NodesTool struct {
|
||||||
manager *nodes.Manager
|
manager *nodes.Manager
|
||||||
router *nodes.Router
|
router *nodes.Router
|
||||||
|
auditPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodesTool(m *nodes.Manager, r *nodes.Router) *NodesTool { return &NodesTool{manager: m, router: r} }
|
func NewNodesTool(m *nodes.Manager, r *nodes.Router, auditPath string) *NodesTool {
|
||||||
|
return &NodesTool{manager: m, router: r, auditPath: strings.TrimSpace(auditPath)}
|
||||||
|
}
|
||||||
func (t *NodesTool) Name() string { return "nodes" }
|
func (t *NodesTool) Name() string { return "nodes" }
|
||||||
func (t *NodesTool) Description() string {
|
func (t *NodesTool) Description() string {
|
||||||
return "Manage paired nodes (status/describe/run/invoke/camera/screen/location/canvas)."
|
return "Manage paired nodes (status/describe/run/invoke/camera/screen/location/canvas)."
|
||||||
@@ -107,11 +113,39 @@ func (t *NodesTool) Execute(ctx context.Context, args map[string]interface{}) (s
|
|||||||
return "", fmt.Errorf("invalid_args: canvas_action requires args.action")
|
return "", fmt.Errorf("invalid_args: canvas_action requires args.action")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp, err := t.router.Dispatch(ctx, nodes.Request{Action: action, Node: nodeID, Task: strings.TrimSpace(task), Model: strings.TrimSpace(model), Args: reqArgs}, mode)
|
req := nodes.Request{Action: action, Node: nodeID, Task: strings.TrimSpace(task), Model: strings.TrimSpace(model), Args: reqArgs}
|
||||||
|
resp, err := t.router.Dispatch(ctx, req, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.writeAudit(req, nodes.Response{OK: false, Code: "transport_error", Error: err.Error(), Node: nodeID, Action: action}, mode)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
t.writeAudit(req, resp, mode)
|
||||||
b, _ := json.Marshal(resp)
|
b, _ := json.Marshal(resp)
|
||||||
return string(b), nil
|
return string(b), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *NodesTool) writeAudit(req nodes.Request, resp nodes.Response, mode string) {
|
||||||
|
if strings.TrimSpace(t.auditPath) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = os.MkdirAll(filepath.Dir(t.auditPath), 0755)
|
||||||
|
row := map[string]interface{}{
|
||||||
|
"time": time.Now().UTC().Format(time.RFC3339),
|
||||||
|
"mode": strings.TrimSpace(mode),
|
||||||
|
"action": req.Action,
|
||||||
|
"node": req.Node,
|
||||||
|
"task": req.Task,
|
||||||
|
"model": req.Model,
|
||||||
|
"ok": resp.OK,
|
||||||
|
"code": resp.Code,
|
||||||
|
"error": resp.Error,
|
||||||
|
}
|
||||||
|
b, _ := json.Marshal(row)
|
||||||
|
f, err := os.OpenFile(t.auditPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, _ = f.Write(append(b, '\n'))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user