- Added `Name` and `CurrentTaskName` fields to the `Execution` struct for improved UI display during execution phases. - Implemented localization support for UI messages, allowing dynamic updates based on the execution context and user locale. - Updated the executor to manage these fields at various phases, ensuring accurate representation of execution status. - Enhanced OpenAPI documentation to reflect the new fields and their usage in execution responses. - Added unit tests to validate the functionality of UI fields and localization handling.
33 KiB
Robot OpenAPI - Design Document
Based on:
yao/agent/robot/(Backend),cui/packages/cui/pages/mission-control/(Frontend) Gap Analysis:yao/openapi/agent/robot/GAPS.md
1. Overview
1.1 Purpose
Provide HTTP REST API endpoints for Robot Agent management, designed to support the Mission Control frontend UI.
1.2 Implementation Strategy
Low-risk phases first. Medium-risk features (Chat API, SSE Event Bus) can be deferred.
| Phase | Risk | Features | Frontend Fallback |
|---|---|---|---|
| 1. Core CRUD | 🟢 Low | List, Get, Create, Update, Delete | - |
| 2. Execution Management | 🟢 Low | List, Get, Control executions | - |
| 3. Results & Activities | 🟢 Low | Deliverables, Activity feed | - |
| 4. i18n | 🟢 Low | Locale parameter support | - |
| 5. Chat API | 🟡 Medium (Deferred) | Multi-turn conversation | Single-submit mode |
| 6. SSE Event Bus | 🟡 Medium (Deferred) | Real-time status streams | Polling every 3-5s |
1.3 Route Decision: /v1/agent/robots
Analysis of existing openapi/ route structure:
| Package | Route | Description |
|---|---|---|
agent/ |
/v1/agent/assistants |
Assistant CRUD, info |
chat/ |
/v1/chat/completions |
Chat completions |
kb/ |
/v1/kb/collections |
Knowledge base |
job/ |
/v1/job/jobs |
Job management |
file/ |
/v1/file/* |
File operations |
user/ |
/v1/user/* |
User management |
team/ |
/v1/team/* |
Team management |
Decision: Put Robot routes under /v1/agent/robots because:
- Semantic Alignment: Robot is a type of Agent (Autonomous Robot Agent), just like Assistant is a type of Agent
- Existing Pattern:
openapi/agent/already handles/v1/agent/assistants - Logical Grouping: Agent-related APIs grouped together
- Consistent Hierarchy:
/v1/agent/{type}pattern
Route Comparison:
| Option | Path | Verdict |
|---|---|---|
❌ /v1/robots |
New top-level namespace | Inconsistent with agent grouping |
✅ /v1/agent/robots |
Under agent namespace | Follows existing pattern |
❌ /v1/members?type=robot |
Reuse members | Less intuitive for operations |
1.4 Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ Frontend (Mission Control) │
│ cui/packages/cui/pages/mission-control/ │
└───────────────────────────────┬─────────────────────────────────────────┘
│ HTTP REST / SSE
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ OpenAPI Layer │
│ yao/openapi/agent/ │
│ - Routes: /v1/agent/assistants/* (existing) │
│ - Routes: /v1/agent/robots/* (NEW) │
│ - Auth: OAuth2 via Guard middleware │
│ - SSE: Real-time updates │
└───────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Robot API Layer │
│ yao/agent/robot/api/ │
│ - Go functions: Get(), List(), Trigger(), etc. │
│ - Business logic │
└───────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Robot Core │
│ yao/agent/robot/ │
│ - Manager, Executor, Cache, Pool, Store │
└─────────────────────────────────────────────────────────────────────────┘
1.5 Design Principles
- Layered Architecture: OpenAPI layer only handles HTTP concerns (routing, request parsing, response formatting). Business logic stays in
robot/api/. - Consistent with Existing Patterns: Follow
yao/openapi/agent/conventions, extend existing agent package - Incremental Implementation: Start with core CRUD, then add real-time features
- Frontend-Backend Balance: API design considers both frontend needs and backend capabilities
2. Differences Analysis
2.1 Frontend Expectations vs Backend Reality
| Feature | Frontend (API.md) | Backend (robot/api/) | Gap | Solution |
|---|---|---|---|---|
| Robot List | GET /v1/robots with name, description |
List() returns types.Robot |
Field mapping needed | Map in OpenAPI layer |
| Robot Detail | GET /v1/robots/:id with full config |
Get() returns Robot + Config |
Need format conversion | Map to frontend format |
| Create Robot | POST with work_mode |
Not implemented | New feature | Add Create() |
| Update Robot | PUT with partial update | Not implemented | New feature | Add Update() |
| Delete Robot | DELETE | Not implemented | New feature | Add Remove() |
| Trigger | Immediate execution | Trigger() returns sync result |
Works | Wrap with SSE events |
| Intervene | Immediate intervention | Intervene() returns sync result |
Works | Wrap with SSE events |
| Multi-turn Chat | Chat before execute | Not implemented | Deferred | Frontend uses single-submit |
| Results List | /results endpoint |
No separate results API | New feature | Derive from executions |
| Activities | /activities endpoint |
No activities tracking | New feature | Derive from executions |
| Real-time Stream | SSE /stream endpoints |
No SSE support | Deferred | Frontend uses polling |
| i18n | ?locale= query param |
No i18n support | New feature | Add locale handling |
2.2 Field Mapping (Backend → Frontend API)
The __yao.member model already has the necessary fields, with different names:
| Frontend API | Backend DB (__yao.member) |
Backend Go (types.Robot) |
Mapping |
|---|---|---|---|
member_id |
member_id |
MemberID |
Direct |
name |
member_id |
MemberID |
Reuse (slug-like identifier) |
display_name |
display_name |
DisplayName |
Direct |
description |
bio |
Need to add Bio field |
Map in OpenAPI layer |
email |
robot_email |
RobotEmail |
Direct |
Required Backend Changes:
- Add
Biofield totypes.Robotstruct - Add
biotocache/load.gomemberFields
2.3 Type Differences
| Frontend Type | Backend Type | Solution |
|---|---|---|
RobotState.name |
Robot.MemberID |
Map member_id to name |
RobotState.description |
Robot.Bio (new) |
Add field, map to description |
Execution.name |
Not in types.Execution |
Derive from goals or input in OpenAPI layer |
Execution.current_task_name |
Not in types.Execution |
Derive from current task in OpenAPI layer |
ResultFile |
No equivalent | New type in OpenAPI layer (derive from delivery) |
Activity |
No equivalent | New type in OpenAPI layer (derive from executions) |
3. API Endpoints
Base Path:
/v1/agent/robots
3.1 Robot Management
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | /v1/agent/robots | ListRobots |
List all robots |
| GET | /v1/agent/robots/:id | GetRobot |
Get robot details |
| POST | /v1/agent/robots | CreateRobot |
Create robot |
| PUT | /v1/agent/robots/:id | UpdateRobot |
Update robot |
| DELETE | /v1/agent/robots/:id | DeleteRobot |
Delete robot |
3.2 Execution Management
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | /v1/agent/robots/:id/executions | ListExecutions |
List executions |
| GET | /v1/agent/robots/:id/executions/:exec_id | GetExecution |
Get execution detail |
| POST | /v1/agent/robots/:id/trigger | TriggerRobot |
Trigger execution (SSE) |
| POST | /v1/agent/robots/:id/intervene | InterveneRobot |
Intervene execution (SSE) |
| POST | /v1/agent/robots/:id/executions/:exec_id/pause | PauseExecution |
Pause execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/resume | ResumeExecution |
Resume execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/cancel | CancelExecution |
Cancel execution |
| POST | /v1/agent/robots/:id/executions/:exec_id/retry | RetryExecution |
Retry execution |
3.3 Results Management
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | /v1/agent/robots/:id/results | ListResults |
List deliverables |
| GET | /v1/agent/robots/:id/results/:result_id | GetResult |
Get deliverable detail |
3.4 Activities & Real-time
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | /v1/agent/robots/activities | ListActivities |
List recent activities |
| GET | /v1/agent/robots/stream | StreamRobots |
Robot status SSE |
| GET | /v1/agent/robots/:id/executions/:exec_id/stream | StreamExecution |
Execution progress SSE |
4. Response Types
4.1 RobotResponse (for list and detail)
// RobotResponse - formatted robot for API response
// Maps backend fields to frontend expected format
type RobotResponse struct {
MemberID string `json:"member_id"`
TeamID string `json:"team_id"`
Name string `json:"name"` // From Robot.MemberID (slug-like identifier)
DisplayName string `json:"display_name"` // From Robot.DisplayName
Description string `json:"description,omitempty"` // From Robot.Bio
Status string `json:"status"` // idle | working | paused | error | maintenance
Running int `json:"running"` // Current running count
MaxRunning int `json:"max_running"` // From Config.Quota.Max
LastRun *string `json:"last_run,omitempty"` // ISO timestamp
NextRun *string `json:"next_run,omitempty"` // ISO timestamp
RunningIDs []string `json:"running_ids,omitempty"` // Execution IDs
Config *ConfigResponse `json:"config,omitempty"` // Full config (for detail)
}
// NewRobotResponse converts backend Robot to API response
func NewRobotResponse(robot *types.Robot) *RobotResponse {
return &RobotResponse{
MemberID: robot.MemberID,
TeamID: robot.TeamID,
Name: robot.MemberID, // Use MemberID as unique identifier
DisplayName: robot.DisplayName,
Description: robot.Bio, // Map Bio to Description
Status: string(robot.Status),
// ... other fields
}
}
4.2 ConfigResponse (robot config)
// ConfigResponse - formatted config for API response
type ConfigResponse struct {
Identity *IdentityConfig `json:"identity,omitempty"`
Clock *ClockConfig `json:"clock,omitempty"`
Events []EventConfig `json:"events,omitempty"`
Quota *QuotaConfig `json:"quota,omitempty"`
Resources *ResourcesConfig `json:"resources,omitempty"`
Delivery *DeliveryConfig `json:"delivery,omitempty"`
Triggers *TriggersConfig `json:"triggers,omitempty"`
Learn *LearnConfig `json:"learn,omitempty"`
Executor *ExecutorConfig `json:"executor,omitempty"`
}
4.3 ExecutionResponse
// ExecutionResponse - formatted execution for API response
type ExecutionResponse struct {
ID string `json:"id"`
MemberID string `json:"member_id"`
TeamID string `json:"team_id"`
TriggerType string `json:"trigger_type"`
StartTime string `json:"start_time"`
EndTime *string `json:"end_time,omitempty"`
Status string `json:"status"`
Phase string `json:"phase"`
Error *string `json:"error,omitempty"`
// UI display fields (from backend Execution)
// These are updated by executor at each phase for frontend display
Name string `json:"name,omitempty"` // Execution title
CurrentTaskName string `json:"current_task_name,omitempty"` // Current task description
// Phase outputs (for detail view)
Goals *GoalsResponse `json:"goals,omitempty"`
Tasks []TaskResponse `json:"tasks,omitempty"`
Current *CurrentState `json:"current,omitempty"`
Delivery *DeliveryResult `json:"delivery,omitempty"`
}
UI Display Fields Update Timeline:
| Phase | Name |
CurrentTaskName |
|---|---|---|
| Created | Human: from input.messages[0]Clock/Event: "Preparing..." |
"Starting..." |
inspiration |
- | "Analyzing context..." |
goals complete |
Extracted from first goal in goals.content |
"Planning goals..." |
tasks |
- | "Breaking down tasks..." |
run (each task) |
- | Current task description |
| Completed/Failed | - | "Completed" / "Failed: {error}" |
4.4 ResultResponse
// ResultResponse - deliverable file for Results tab
type ResultResponse struct {
ID string `json:"id"`
MemberID string `json:"member_id"`
ExecutionID string `json:"execution_id"`
Name string `json:"name"`
Type string `json:"type"` // pdf, xlsx, csv, json, md
Size int64 `json:"size"` // bytes
CreatedAt string `json:"created_at"`
TriggerType string `json:"trigger_type,omitempty"`
ExecutionName string `json:"execution_name,omitempty"`
}
4.5 ActivityResponse
// ActivityResponse - activity item
type ActivityResponse struct {
ID string `json:"id"`
Type string `json:"type"` // completed | file | error | started | paused
MemberID string `json:"member_id"`
RobotName string `json:"robot_name"` // Localized
Title string `json:"title"` // Localized
Description string `json:"description,omitempty"` // Localized
FileID string `json:"file_id,omitempty"`
Timestamp string `json:"timestamp"`
}
5. Request Types
5.1 CreateRobotRequest
// CreateRobotRequest - create robot request
type CreateRobotRequest struct {
Locale string `json:"locale,omitempty"` // zh-CN | en-US
Name string `json:"name"` // Unique identifier
DisplayName string `json:"display_name"` // Display name
Email string `json:"email,omitempty"` // Robot email
ManagerID string `json:"manager_id,omitempty"` // Manager user ID
WorkMode string `json:"work_mode"` // autonomous | on-demand
Identity *IdentityConfig `json:"identity"`
Resources *ResourcesConfig `json:"resources,omitempty"`
}
5.2 UpdateRobotRequest
// UpdateRobotRequest - update robot request
type UpdateRobotRequest struct {
Locale string `json:"locale,omitempty"`
DisplayName *string `json:"display_name,omitempty"`
Config *ConfigResponse `json:"config,omitempty"` // Partial update supported
}
5.3 TriggerRequest (SSE)
// TriggerRequest - trigger robot execution
type TriggerRequest struct {
Locale string `json:"locale,omitempty"`
Messages []Message `json:"messages"`
Attachments []Attachment `json:"attachments,omitempty"`
}
// Message - chat message
type Message struct {
Role string `json:"role"` // user | assistant
Content string `json:"content"`
}
// Attachment - file attachment
type Attachment struct {
File string `json:"file"` // __yao.attachment://fileID
Name string `json:"name,omitempty"`
}
5.4 InterveneRequest (SSE)
// InterveneRequest - intervene during execution
type InterveneRequest struct {
Locale string `json:"locale,omitempty"`
ExecutionID string `json:"execution_id"`
Action string `json:"action"` // task.add | goal.adjust | instruct
Messages []Message `json:"messages"`
Priority string `json:"priority,omitempty"` // high | normal | low
Position string `json:"position,omitempty"` // first | last | next | at
}
6. Deferred Features
6.1 Multi-turn Chat API (Phase 5 - Deferred)
Risk Level: 🟡 Medium - Requires new stateful component Frontend Fallback: Single-submit mode (user input → immediate execution)
The frontend ChatDrawer component expects multi-turn conversation before execution:
User: "Help me analyze competitor pricing"
↓
Robot: "Got it. Which competitors?"
↓
User: "Focus on Company A and B"
↓
Robot: "Understood. Ready to start?"
↓
User clicks [Confirm] → Execution starts
Current backend behavior: Trigger() immediately submits to execution pool.
Deferred implementation:
POST /v1/agent/robots/:id/chat
{
"conversation_id": "conv_001", // For continuing conversation
"messages": [{ "role": "user", "content": "..." }]
}
Response (SSE):
event: message
data: {"role": "assistant", "content": "..."}
event: state
data: {"conversation_id": "conv_001", "ready_to_execute": false}
For now: Frontend can skip chat flow, directly call /trigger with user message.
6.2 SSE Event Bus (Phase 6 - Deferred)
Risk Level: 🟡 Medium - Requires modification of executor/manager Frontend Fallback: Polling (GET /executions every 3-5 seconds)
Real-time status updates via SSE require an event bus integrated with:
- Manager (robot status changes)
- Executor (execution progress)
For now: Frontend uses polling to refresh status.
7. SSE Events
7.1 Trigger/Intervene SSE Events
event: received
data: {"message": "Task received, creating execution..."}
event: execution
data: {"execution_id": "exec_002", "status": "pending"}
event: message
data: {"role": "assistant", "content": "好的,我开始处理..."}
event: phase
data: {"phase": "goals", "message": "正在生成目标..."}
event: complete
data: {"execution_id": "exec_002", "status": "running"}
event: error
data: {"error": "Something went wrong"}
7.2 Robot Stream SSE Events (Phase 6 - Deferred)
event: robot_status
data: {"member_id": "robot_001", "status": "working", "running": 1}
event: execution_start
data: {"member_id": "robot_001", "execution_id": "exec_001", "name": "每日报表生成"}
event: execution_complete
data: {"member_id": "robot_001", "execution_id": "exec_001", "status": "completed"}
event: activity
data: {"id": "act_001", "type": "completed", "member_id": "robot_001", ...}
7.3 Execution Stream SSE Events (Phase 6 - Deferred)
event: phase
data: {"phase": "tasks", "progress": "2/5 tasks"}
event: task_start
data: {"task_id": "task_002", "order": 2}
event: task_complete
data: {"task_id": "task_002", "status": "completed"}
event: message
data: {"role": "assistant", "content": "正在分析数据..."}
event: delivery
data: {"summary": "...", "attachments": [...]}
event: complete
data: {"status": "completed"}
event: error
data: {"error": "Something went wrong", "phase": "run"}
8. i18n Support
8.1 Locale Detection
For API requests (Human trigger):
Priority order:
- Request body field:
locale: "zh-CN"(in TriggerRequest) - Query parameter:
?locale=zh-CN - Accept-Language header
- Robot's
default_localeconfig - System default:
en-US
For Clock/Event triggers (no user context):
Priority order:
- Robot's
default_localeconfig (fromrobot_config.default_locale) - System default:
en-US
8.2 Robot Default Locale
Robots can configure a default language for clock/event triggered executions:
// In RobotConfig (robot_config field in __yao.member)
type Config struct {
// ... other fields ...
DefaultLocale string `json:"default_locale,omitempty"` // "en-US", "zh-CN"
}
Language resolution:
func getLocale(robot *Robot, input *TriggerInput) string {
// 1. Human trigger with explicit locale
if input != nil && input.Locale != "" {
return input.Locale
}
// 2. Robot configured default
if robot.Config != nil && robot.Config.DefaultLocale != "" {
return robot.Config.DefaultLocale
}
// 3. System default
return "en-US"
}
8.3 Localized Fields
| Response Type | Localized Fields |
|---|---|
| RobotResponse | display_name, description |
| ExecutionResponse | name, current_task_name |
| TaskResponse | (none - tasks use executor_id) |
| ResultResponse | name, execution_name |
| ActivityResponse | robot_name, title, description |
9. Authentication & Authorization
9.1 Guard Middleware
All endpoints require OAuth2 authentication via oauth.Guard middleware.
// In router registration
router.Use(oauth.Guard())
9.2 Permission Checks
| Endpoint | Required Scope |
|---|---|
| GET /robots | robots:read |
| POST /robots | robots:write |
| PUT/DELETE /robots/:id | robots:write + ownership check |
| Trigger/Intervene | robots:execute |
| Stream endpoints | robots:read |
9.3 Team Isolation
Robots are team-scoped. Users can only access robots in their team.
func checkTeamAccess(ctx context.Context, memberID string) error {
auth := oauth.GetAuthorized(ctx)
robot, _ := robotapi.Get(memberID)
if robot.TeamID != auth.TeamID {
return errors.New("access denied")
}
return nil
}
10. File Structure
10.1 Backend Store + API Layers
yao/agent/robot/
├── store/ # Store Layer (Core CRUD)
│ ├── store.go # Common interfaces
│ ├── execution.go # ExecutionStore (EXISTS)
│ └── robot.go # RobotStore (NEW)
│
├── api/ # API Layer (Thin wrappers)
│ ├── robot.go # Get, List, Create, Update, Remove
│ ├── execution.go # Execution management
│ ├── trigger.go # Trigger, Intervene
│ ├── results.go # ListResults, GetResult (NEW)
│ └── activities.go # ListActivities (NEW)
│
├── types/ # Type definitions
│ └── robot.go # Add Bio field
│
└── cache/ # Cache Layer
└── load.go # Add bio to memberFields
10.2 OpenAPI Layer
Decision: Sub-package under openapi/agent/
Robot logic is complex enough to warrant its own package. This keeps code organized and follows the pattern used by other complex modules.
yao/openapi/agent/
├── agent.go # Main route registration (MODIFY: add robot.Attach)
├── assistant.go # Assistant handlers (existing)
├── filter.go # Query filtering (existing)
├── models.go # LLM models (existing)
├── types.go # Types (existing)
│
└── robot/ # Robot sub-package (NEW)
├── DESIGN.md # This document ✅
├── TODO.md # Implementation plan ✅
├── GAPS.md # Gap analysis ✅
│
├── robot.go # Route registration (Attach function)
├── types.go # Request/Response types
│
├── list.go # GET /v1/agent/robots
├── detail.go # GET/POST/PUT/DELETE /v1/agent/robots/:id
│
├── execution.go # Execution list/detail/control handlers
├── trigger.go # POST /trigger, POST /intervene (SSE)
│
├── results.go # GET /results, GET /results/:id
├── activities.go # GET /activities
│
├── stream.go # GET /stream, GET /executions/:id/stream (SSE)
│
├── filter.go # Query param parsing helpers
└── utils.go # Locale, time formatting utilities
Route Registration (in openapi/agent/agent.go):
import "github.com/yaoapp/yao/openapi/agent/robot"
func Attach(group *gin.RouterGroup, oauth types.OAuth) {
group.Use(oauth.Guard)
// Assistant routes (existing)
group.GET("/assistants", ListAssistants)
group.POST("/assistants", CreateAssistant)
// ...
// Robot routes (NEW)
robot.Attach(group.Group("/robots"), oauth)
}
Robot Route Registration (robot/robot.go):
package robot
func Attach(group *gin.RouterGroup, oauth types.OAuth) {
// Robot CRUD
group.GET("", ListRobots)
group.POST("", CreateRobot)
group.GET("/:id", GetRobot)
group.PUT("/:id", UpdateRobot)
group.DELETE("/:id", DeleteRobot)
// Activities (before :id to avoid conflict)
group.GET("/activities", ListActivities)
group.GET("/stream", StreamRobots)
// Execution management
group.GET("/:id/executions", ListExecutions)
group.GET("/:id/executions/:exec_id", GetExecution)
group.GET("/:id/executions/:exec_id/stream", StreamExecution)
group.POST("/:id/executions/:exec_id/pause", PauseExecution)
group.POST("/:id/executions/:exec_id/resume", ResumeExecution)
group.POST("/:id/executions/:exec_id/cancel", CancelExecution)
group.POST("/:id/executions/:exec_id/retry", RetryExecution)
// Trigger & Intervene (SSE)
group.POST("/:id/trigger", TriggerRobot)
group.POST("/:id/intervene", InterveneRobot)
// Results
group.GET("/:id/results", ListResults)
group.GET("/:id/results/:result_id", GetResult)
}
11. Error Handling
11.1 Error Response Format
{
"error": {
"code": "ROBOT_NOT_FOUND",
"message": "Robot not found",
"details": {
"member_id": "robot_001"
}
}
}
11.2 Error Codes
| Code | HTTP Status | Description |
|---|---|---|
| ROBOT_NOT_FOUND | 404 | Robot does not exist |
| EXECUTION_NOT_FOUND | 404 | Execution does not exist |
| ROBOT_BUSY | 409 | Robot at max capacity |
| TRIGGER_DISABLED | 403 | Trigger type disabled |
| EXECUTION_NOT_RUNNING | 400 | Cannot pause/resume non-running execution |
| INVALID_REQUEST | 400 | Request validation failed |
| UNAUTHORIZED | 401 | Not authenticated |
| FORBIDDEN | 403 | No permission |
12. Implementation Notes
12.1 Backend Architecture: Store + API Layers
Principle: Store layer handles database CRUD, API layer handles business logic. This enables reuse across Golang API, JSAPI, and Yao Process.
Consumers (Golang API / JSAPI / Yao Process)
│
▼
┌─────────────────────────────────────────┐
│ API Layer (robot/api/) │
│ Thin wrappers: validation, cache ops │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Store Layer (robot/store/) │
│ Core CRUD: RobotStore, ExecutionStore │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Model Layer (__yao.member) │
└─────────────────────────────────────────┘
12.2 Store Layer Extensions
File: store/robot.go (NEW) - Core Robot CRUD
type RobotStore struct {
modelID string // "__yao.member"
}
func (s *RobotStore) Save(ctx context.Context, record *RobotRecord) error
func (s *RobotStore) Get(ctx context.Context, memberID string) (*RobotRecord, error)
func (s *RobotStore) List(ctx context.Context, opts *ListOptions) ([]*RobotRecord, error)
func (s *RobotStore) Delete(ctx context.Context, memberID string) error
func (s *RobotStore) UpdateConfig(ctx context.Context, memberID string, config map[string]interface{}) error
File: store/execution.go (extend)
func (s *ExecutionStore) ListResults(ctx context.Context, memberID string, opts *ResultsQuery) ([]*ResultRecord, error)
func (s *ExecutionStore) GetResult(ctx context.Context, resultID string) (*ResultRecord, error)
func (s *ExecutionStore) ListActivities(ctx context.Context, opts *ActivityQuery) ([]*ActivityRecord, error)
12.3 API Layer Extensions
File: api/robot.go (extend) - Thin wrappers
// Create - calls store.RobotStore.Save() + cache refresh
func Create(ctx *types.Context, teamID string, req *CreateRobotRequest) (*types.Robot, error)
// Update - calls store.RobotStore.UpdateConfig() + cache refresh
func Update(ctx *types.Context, memberID string, req *UpdateRobotRequest) (*types.Robot, error)
// Remove - calls store.RobotStore.Delete() + cache invalidate
func Remove(ctx *types.Context, memberID string) error
File: api/results.go (NEW)
func ListResults(ctx *types.Context, memberID string, query *ResultQuery) (*ResultsResult, error)
func GetResult(ctx *types.Context, resultID string) (*ResultFile, error)
File: api/activities.go (NEW)
func ListActivities(ctx *types.Context, query *ActivityQuery) (*ActivitiesResult, error)
12.4 Localization
Add Locale parameter support for localized responses.
12.5 SSE Implementation
Use standard Go SSE pattern:
func streamHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, _ := w.(http.Flusher)
for event := range events {
fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, event.Data)
flusher.Flush()
}
}
12.6 Localization Strategy
- Store display names in
__yao.member.display_name(single language) initially - Future: Add
display_name_cn,display_name_enor use JSON{"en": "...", "cn": "..."} - Execution names derived from goals or input message
- Activities derive titles from execution data
13. API Base Path Decision
Based on analysis of existing openapi/ structure:
| Option | Path | Pros | Cons |
|---|---|---|---|
| ❌ A | /v1/robots |
Shorter path | New namespace, inconsistent |
| ✅ B | /v1/agent/robots |
Groups with agent APIs, consistent | Longer path |
| ❌ C | /v1/members?type=robot |
Uses existing members | Less intuitive |
Decision: Use /v1/agent/robots as base path.
Rationale:
openapi/agent/already exists with/v1/agent/assistants- Robot is conceptually an Agent type (Autonomous Robot Agent)
- Follows the established pattern:
/v1/agent/{agent-type} - Keeps agent-related APIs logically grouped
Frontend Impact:
- Update
cui/packages/cui/pages/mission-control/API.mdbase path from/v1/robotsto/v1/agent/robots - Minimal code change (just update base URL constant)
14. References
- Frontend API Requirements:
cui/packages/cui/pages/mission-control/API.md - Backend Robot Design:
yao/agent/robot/DESIGN.md - Backend Technical Spec:
yao/agent/robot/TECHNICAL.md - Existing OpenAPI Patterns:
yao/openapi/kb/,yao/openapi/chat/