mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 21:57:29 +08:00
add child-model agent_task flow with node model capability and stricter device contracts
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
- `nodes` 工具支持设备快捷参数:`facing`、`duration_ms`、`command`
|
||||
- 设备动作响应统一:`ok/code/error/payload`(code 示例:`ok` `unsupported_action` `transport_error`)
|
||||
- 设备 `payload` 规范字段:`media_type` `storage` `url|path|image` `meta`
|
||||
- 支持 `agent_task`:主节点可向具备 `model` 能力的子节点下发任务,子节点返回执行结果
|
||||
|
||||
实现位置:
|
||||
- `pkg/nodes/types.go`
|
||||
|
||||
@@ -50,6 +50,7 @@ A `nodes` tool control-plane PoC is now available:
|
||||
- `nodes` tool supports device shortcuts: `facing`, `duration_ms`, `command`
|
||||
- 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`
|
||||
- supports `agent_task`: parent node can dispatch tasks to child nodes with `model` capability and receive execution results
|
||||
|
||||
Implementation:
|
||||
- `pkg/nodes/types.go`
|
||||
|
||||
@@ -162,19 +162,20 @@ func statusCmd() {
|
||||
ns := nodes.DefaultManager().List()
|
||||
if len(ns) > 0 {
|
||||
online := 0
|
||||
caps := map[string]int{"run": 0, "camera": 0, "screen": 0, "location": 0, "canvas": 0}
|
||||
caps := map[string]int{"run": 0, "model": 0, "camera": 0, "screen": 0, "location": 0, "canvas": 0}
|
||||
for _, n := range ns {
|
||||
if n.Online {
|
||||
online++
|
||||
}
|
||||
if n.Capabilities.Run { caps["run"]++ }
|
||||
if n.Capabilities.Model { caps["model"]++ }
|
||||
if n.Capabilities.Camera { caps["camera"]++ }
|
||||
if n.Capabilities.Screen { caps["screen"]++ }
|
||||
if n.Capabilities.Location { caps["location"]++ }
|
||||
if n.Capabilities.Canvas { caps["canvas"]++ }
|
||||
}
|
||||
fmt.Printf("Nodes: total=%d online=%d\n", len(ns), online)
|
||||
fmt.Printf("Nodes Capabilities: run=%d camera=%d screen=%d location=%d canvas=%d\n", caps["run"], caps["camera"], caps["screen"], caps["location"], caps["canvas"])
|
||||
fmt.Printf("Nodes Capabilities: run=%d model=%d camera=%d screen=%d location=%d canvas=%d\n", caps["run"], caps["model"], caps["camera"], caps["screen"], caps["location"], caps["canvas"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
||||
toolsRegistry.Register(tools.NewProcessTool(processManager))
|
||||
nodesManager := nodes.DefaultManager()
|
||||
nodesManager.SetAuditPath(filepath.Join(workspace, "memory", "nodes-audit.jsonl"))
|
||||
nodesManager.Upsert(nodes.NodeInfo{ID: "local", Name: "local", Capabilities: nodes.Capabilities{Run: true, Invoke: true, Camera: true, Screen: true, Location: true, Canvas: true}, Online: true})
|
||||
nodesManager.Upsert(nodes.NodeInfo{ID: "local", Name: "local", Capabilities: nodes.Capabilities{Run: true, Invoke: true, Model: true, Camera: true, Screen: true, Location: true, Canvas: true}, Models: []string{"local-sim"}, Online: true})
|
||||
nodesManager.RegisterHandler("local", func(req nodes.Request) nodes.Response {
|
||||
switch req.Action {
|
||||
case "run":
|
||||
@@ -92,6 +92,8 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
||||
payload["command"] = parts
|
||||
}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: payload}
|
||||
case "agent_task":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "simulated": true, "model": req.Model, "task": req.Task, "result": "local child-model simulated execution completed"}}
|
||||
case "camera_snap":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "image", "storage": "inline", "facing": req.Args["facing"], "simulated": true, "meta": map[string]interface{}{"width": 1280, "height": 720}}}
|
||||
case "camera_clip":
|
||||
|
||||
@@ -137,6 +137,8 @@ func (m *Manager) SupportsAction(nodeID, action string) bool {
|
||||
switch action {
|
||||
case "run":
|
||||
return n.Capabilities.Run
|
||||
case "agent_task":
|
||||
return n.Capabilities.Model
|
||||
case "camera_snap", "camera_clip":
|
||||
return n.Capabilities.Camera
|
||||
case "screen_record", "screen_snapshot":
|
||||
@@ -162,6 +164,10 @@ func (m *Manager) PickFor(action string) (NodeInfo, bool) {
|
||||
if n.Capabilities.Run {
|
||||
return n, true
|
||||
}
|
||||
case "agent_task":
|
||||
if n.Capabilities.Model {
|
||||
return n, true
|
||||
}
|
||||
case "camera_snap", "camera_clip":
|
||||
if n.Capabilities.Camera {
|
||||
return n, true
|
||||
|
||||
@@ -75,6 +75,8 @@ func actionHTTPPath(action string) string {
|
||||
return "/run"
|
||||
case "invoke":
|
||||
return "/invoke"
|
||||
case "agent_task":
|
||||
return "/agent/task"
|
||||
case "camera_snap":
|
||||
return "/camera/snap"
|
||||
case "camera_clip":
|
||||
|
||||
@@ -6,6 +6,7 @@ import "time"
|
||||
type Capabilities struct {
|
||||
Run bool `json:"run"`
|
||||
Invoke bool `json:"invoke"`
|
||||
Model bool `json:"model"`
|
||||
Camera bool `json:"camera"`
|
||||
Screen bool `json:"screen"`
|
||||
Location bool `json:"location"`
|
||||
@@ -23,6 +24,7 @@ type NodeInfo struct {
|
||||
Token string `json:"token,omitempty"`
|
||||
Capabilities Capabilities `json:"capabilities"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
Models []string `json:"models,omitempty"`
|
||||
RegisteredAt time.Time `json:"registered_at,omitempty"`
|
||||
LastSeenAt time.Time `json:"last_seen_at"`
|
||||
Online bool `json:"online"`
|
||||
@@ -32,6 +34,8 @@ type NodeInfo struct {
|
||||
type Request struct {
|
||||
Action string `json:"action"`
|
||||
Node string `json:"node,omitempty"`
|
||||
Task string `json:"task,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Args map[string]interface{} `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,12 @@ func (t *NodesTool) Description() string {
|
||||
}
|
||||
func (t *NodesTool) Parameters() map[string]interface{} {
|
||||
return map[string]interface{}{"type": "object", "properties": map[string]interface{}{
|
||||
"action": map[string]interface{}{"type": "string", "description": "status|describe|run|invoke|camera_snap|camera_clip|screen_record|screen_snapshot|location_get|canvas_snapshot|canvas_action"},
|
||||
"action": map[string]interface{}{"type": "string", "description": "status|describe|run|invoke|agent_task|camera_snap|camera_clip|screen_record|screen_snapshot|location_get|canvas_snapshot|canvas_action"},
|
||||
"node": map[string]interface{}{"type": "string", "description": "target node id"},
|
||||
"mode": map[string]interface{}{"type": "string", "description": "auto|p2p|relay (default auto)"},
|
||||
"args": map[string]interface{}{"type": "object", "description": "action args"},
|
||||
"task": map[string]interface{}{"type": "string", "description": "agent_task content for child node model"},
|
||||
"model": map[string]interface{}{"type": "string", "description": "optional model for agent_task"},
|
||||
"command": map[string]interface{}{"type": "array", "description": "run command array shortcut"},
|
||||
"facing": map[string]interface{}{"type": "string", "description": "camera facing: front|back|both"},
|
||||
"duration_ms": map[string]interface{}{"type": "integer", "description": "clip/record duration"},
|
||||
@@ -95,12 +97,17 @@ func (t *NodesTool) Execute(ctx context.Context, args map[string]interface{}) (s
|
||||
}
|
||||
reqArgs["duration_ms"] = di
|
||||
}
|
||||
task, _ := args["task"].(string)
|
||||
model, _ := args["model"].(string)
|
||||
if action == "agent_task" && strings.TrimSpace(task) == "" {
|
||||
return "", fmt.Errorf("invalid_args: agent_task requires task")
|
||||
}
|
||||
if action == "canvas_action" {
|
||||
if act, _ := reqArgs["action"].(string); strings.TrimSpace(act) == "" {
|
||||
return "", fmt.Errorf("invalid_args: canvas_action requires args.action")
|
||||
}
|
||||
}
|
||||
resp, err := t.router.Dispatch(ctx, nodes.Request{Action: action, Node: nodeID, Args: reqArgs}, mode)
|
||||
resp, err := t.router.Dispatch(ctx, nodes.Request{Action: action, Node: nodeID, Task: strings.TrimSpace(task), Model: strings.TrimSpace(model), Args: reqArgs}, mode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user