diff --git a/openapi/agent/robot/DESIGN.md b/openapi/agent/robot/DESIGN.md index 74731f0c..63caebdc 100644 --- a/openapi/agent/robot/DESIGN.md +++ b/openapi/agent/robot/DESIGN.md @@ -549,6 +549,31 @@ func checkTeamAccess(ctx context.Context, memberID string) error { ## 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. @@ -564,6 +589,7 @@ yao/openapi/agent/ └── 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 @@ -672,23 +698,89 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) { ## 12. Implementation Notes -### 12.1 Backend API Extension +### 12.1 Backend Architecture: Store + API Layers -The existing `robot/api/` package needs these additions: +> **Principle:** Store layer handles database CRUD, API layer handles business logic. +> This enables reuse across Golang API, JSAPI, and Yao Process. -1. **Robot CRUD**: `Create()`, `Update()`, `Remove()` functions -2. **Results API**: `ListResults()`, `GetResult()` functions -3. **Activities API**: `ListActivities()` function -4. **Localization**: Add `Locale` parameter support +``` +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 Extension +### 12.2 Store Layer Extensions -The `robot/store/` package needs: +**File: `store/robot.go` (NEW)** - Core Robot CRUD -1. **Results Store**: Store and query deliverable files -2. **Activities Store**: Store and query activities (or derive from job logs) +```go +type RobotStore struct { + modelID string // "__yao.member" +} -### 12.3 SSE Implementation +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)** + +```go +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 + +```go +// 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)** + +```go +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)** + +```go +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: @@ -707,7 +799,7 @@ func streamHandler(w http.ResponseWriter, r *http.Request) { } ``` -### 12.4 Localization Strategy +### 12.6 Localization Strategy - Store display names in `__yao.member.display_name` (single language) initially - Future: Add `display_name_cn`, `display_name_en` or use JSON `{"en": "...", "cn": "..."}` diff --git a/openapi/agent/robot/GAPS.md b/openapi/agent/robot/GAPS.md index 83bfbfc9..209dfecd 100644 --- a/openapi/agent/robot/GAPS.md +++ b/openapi/agent/robot/GAPS.md @@ -306,21 +306,105 @@ Add `confirm` parameter to trigger: --- -## 3. Backend API Gaps (`yao/agent/robot/api/`) +## 3. Backend Architecture: Store + API Layers -### 3.1 Missing Functions +### 3.1 Architecture Decision + +> **Principle:** Store layer handles database CRUD, API layer handles business logic. +> This enables reuse across Golang API, JSAPI, and Yao Process. + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ Consumers │ +├──────────────────────────────────────────────────────────────────────┤ +│ Golang API (robot/api) │ JSAPI (JS Runtime) │ Yao Process │ +└──────────────────────────────┴───────────────────────┴───────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ API Layer (robot/api/) │ +│ Business logic, parameter validation, cache invalidation │ +│ - Thin wrappers that call store layer │ +│ - Reusable across all consumers │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ Store Layer (robot/store/) │ +│ Pure database CRUD, no business logic │ +│ - RobotStore: Robot member CRUD (NEW) │ +│ - ExecutionStore: Execution records (EXISTS) │ +└──────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────┐ +│ Model Layer (__yao.member, etc.) │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +### 3.2 Store Layer: Missing Functions + +**File: `store/robot.go` (NEW)** - Core CRUD implementation | Function | Status | Description | |----------|--------|-------------| -| `Create()` | ⬜ Missing | Create new robot member | -| `Update()` | ⬜ Missing | Update robot config | -| `Remove()` | ⬜ Missing | Delete robot member | -| `ListResults()` | ⬜ Missing | Query deliverable files from executions | -| `GetResult()` | ⬜ Missing | Get single deliverable detail | -| `ListActivities()` | ⬜ Missing | Query activity feed | -| `RetryExecution()` | ⬜ Missing | Retry a failed execution | +| `RobotStore.Save()` | ⬜ Missing | Create or update robot member | +| `RobotStore.Get()` | ⬜ Missing | Get robot by member_id | +| `RobotStore.List()` | ⬜ Missing | List robots with filters | +| `RobotStore.Delete()` | ⬜ Missing | Delete robot member | +| `RobotStore.UpdateConfig()` | ⬜ Missing | Update robot config only | -### 3.2 Existing Functions (API layer can call these) +**File: `store/execution.go` (extend)** + +| Function | Status | Description | +|----------|--------|-------------| +| `ExecutionStore.ListResults()` | ⬜ Missing | Query deliverables from executions | +| `ExecutionStore.GetResult()` | ⬜ Missing | Get single deliverable | +| `ExecutionStore.ListActivities()` | ⬜ Missing | Derive activities from history | + +### 3.3 API Layer: Missing Functions + +**File: `api/robot.go` (extend)** - Thin wrappers calling store + +| Function | Status | Description | +|----------|--------|-------------| +| `Create()` | ⬜ Missing | Call `store.RobotStore.Save()` + cache refresh | +| `Update()` | ⬜ Missing | Call `store.RobotStore.UpdateConfig()` + cache refresh | +| `Remove()` | ⬜ Missing | Call `store.RobotStore.Delete()` + cache invalidate | + +**File: `api/results.go` (NEW)** - Thin wrappers + +| Function | Status | Description | +|----------|--------|-------------| +| `ListResults()` | ⬜ Missing | Call `store.ExecutionStore.ListResults()` | +| `GetResult()` | ⬜ Missing | Call `store.ExecutionStore.GetResult()` | + +**File: `api/activities.go` (NEW)** - Thin wrappers + +| Function | Status | Description | +|----------|--------|-------------| +| `ListActivities()` | ⬜ Missing | Call `store.ExecutionStore.ListActivities()` | + +**File: `api/execution.go` (extend)** + +| Function | Status | Description | +|----------|--------|-------------| +| `RetryExecution()` | ⬜ Missing | Re-trigger with same input | + +### 3.4 Existing Functions (Already implemented) + +**Store Layer (`store/`):** + +| Function | File | Status | +|----------|------|--------| +| `ExecutionStore.Save()` | `execution.go` | ✅ Exists | +| `ExecutionStore.Get()` | `execution.go` | ✅ Exists | +| `ExecutionStore.List()` | `execution.go` | ✅ Exists | +| `ExecutionStore.Delete()` | `execution.go` | ✅ Exists | +| `ExecutionStore.UpdatePhase()` | `execution.go` | ✅ Exists | +| `ExecutionStore.UpdateStatus()` | `execution.go` | ✅ Exists | + +**API Layer (`api/`):** | Function | File | Status | |----------|------|--------| @@ -335,39 +419,56 @@ Add `confirm` parameter to trigger: | `ResumeExecution()` | `execution.go` | ✅ Exists | | `StopExecution()` | `execution.go` | ✅ Exists | -### 3.3 Required API Extensions +### 3.5 Code Examples -**File: `api/robot.go`** +**Store Layer (`store/robot.go`):** ```go -// Create creates a new robot member -func Create(ctx *types.Context, teamID string, req *CreateRobotRequest) (*types.Robot, error) +// RobotStore - persistent storage for robot members +type RobotStore struct { + modelID string +} -// Update updates robot config -func Update(ctx *types.Context, memberID string, req *UpdateRobotRequest) (*types.Robot, error) +func NewRobotStore() *RobotStore { + return &RobotStore{modelID: "__yao.member"} +} -// Remove deletes a robot member -func Remove(ctx *types.Context, memberID string) error +// Save creates or updates a robot member record +func (s *RobotStore) Save(ctx context.Context, record *RobotRecord) error + +// Get retrieves a robot by member_id +func (s *RobotStore) Get(ctx context.Context, memberID string) (*RobotRecord, error) + +// List retrieves robots with filters +func (s *RobotStore) List(ctx context.Context, opts *ListOptions) ([]*RobotRecord, error) + +// Delete removes a robot member +func (s *RobotStore) Delete(ctx context.Context, memberID string) error ``` -**File: `api/results.go` (NEW)** +**API Layer (`api/robot.go`):** ```go -// ListResults returns deliverable files for a robot -func ListResults(ctx *types.Context, memberID string, query *ResultQuery) (*ResultsResult, error) +// Create creates a new robot member (thin wrapper) +func Create(ctx *types.Context, teamID string, req *CreateRobotRequest) (*types.Robot, error) { + // 1. Validate request + // 2. Call store.RobotStore.Save() + // 3. Refresh cache + // 4. Return robot +} -// GetResult returns a single deliverable detail -func GetResult(ctx *types.Context, resultID string) (*types.ResultFile, error) -``` +// Update updates robot config (thin wrapper) +func Update(ctx *types.Context, memberID string, req *UpdateRobotRequest) (*types.Robot, error) { + // 1. Validate request + // 2. Call store.RobotStore.UpdateConfig() + // 3. Refresh cache + // 4. Return updated robot +} -**File: `api/activities.go` (NEW)** -```go -// ListActivities returns recent activities -func ListActivities(ctx *types.Context, query *ActivityQuery) (*ActivitiesResult, error) -``` - -**File: `api/execution.go` (extend)** -```go -// RetryExecution retries a failed execution -func RetryExecution(ctx *types.Context, execID string) (*TriggerResult, error) +// Remove deletes a robot member (thin wrapper) +func Remove(ctx *types.Context, memberID string) error { + // 1. Check permissions + // 2. Call store.RobotStore.Delete() + // 3. Invalidate cache +} ``` --- @@ -672,20 +773,44 @@ type ActivityRecord struct { ### Backend (`yao/agent/robot/`) +#### Store Layer (Core CRUD - implement first) + | File | Action | Changes | |------|--------|---------| -| `types/robot.go` | Modify | Add `Bio` field, optionally `Name`/`CurrentTaskName` for Execution | -| `types/conversation.go` | Create | `Conversation`, `ChatRequest`, `ChatResponse` types | -| `cache/load.go` | Modify | Add `bio` to `memberFields` slice | -| `store/conversation.go` | Create | Temporary conversation storage (redis/memory) | +| `store/robot.go` | **Create** | `RobotStore` - Robot member CRUD (Save, Get, List, Delete, UpdateConfig) | +| `store/execution.go` | Modify | Add `ListResults()`, `GetResult()`, `ListActivities()` | +| `store/conversation.go` | Create | Temporary conversation storage (Phase 5 - Deferred) | + +#### Types Layer + +| File | Action | Changes | +|------|--------|---------| +| `types/robot.go` | Modify | Add `Bio` field | +| `types/conversation.go` | Create | `Conversation`, `ChatRequest`, `ChatResponse` types (Phase 5) | | `types/context.go` | Modify | Add `Locale` field | -| `api/robot.go` | Modify | Add `Create()`, `Update()`, `Remove()` | -| `api/chat.go` | Create | `Chat()` - multi-turn conversation handler | -| `api/trigger.go` | Modify | Add `ConversationID` support | + +#### Cache Layer + +| File | Action | Changes | +|------|--------|---------| +| `cache/load.go` | Modify | Add `bio` to `memberFields` slice | + +#### API Layer (Thin wrappers calling store) + +| File | Action | Changes | +|------|--------|---------| +| `api/robot.go` | Modify | Add `Create()`, `Update()`, `Remove()` - call store.RobotStore | +| `api/results.go` | Create | `ListResults()`, `GetResult()` - call store.ExecutionStore | +| `api/activities.go` | Create | `ListActivities()` - call store.ExecutionStore | | `api/execution.go` | Modify | Add `RetryExecution()` | -| `api/results.go` | Create | `ListResults()`, `GetResult()` - query from execution store | -| `api/activities.go` | Create | `ListActivities()` - derive from execution history | -| `events/bus.go` | Create | Event bus for SSE (Phase 5) | +| `api/chat.go` | Create | `Chat()` - multi-turn conversation (Phase 5 - Deferred) | +| `api/trigger.go` | Modify | Add `ConversationID` support (Phase 5 - Deferred) | + +#### Events Layer (Phase 6 - Deferred) + +| File | Action | Changes | +|------|--------|---------| +| `events/bus.go` | Create | Event bus for SSE | ### OpenAPI (`yao/openapi/agent/robot/`) diff --git a/openapi/agent/robot/TODO.md b/openapi/agent/robot/TODO.md index a2845a4c..33fcb334 100644 --- a/openapi/agent/robot/TODO.md +++ b/openapi/agent/robot/TODO.md @@ -42,11 +42,22 @@ ### 1.1 Backend Prerequisites ⬜ +#### Types & Cache - [ ] Add `Bio` field to `types.Robot` struct in `yao/agent/robot/types/robot.go` - [ ] Add `bio` to `memberFields` in `yao/agent/robot/cache/load.go` -- [ ] Implement `api.Create()` in `yao/agent/robot/api/robot.go` -- [ ] Implement `api.Update()` in `yao/agent/robot/api/robot.go` -- [ ] Implement `api.Remove()` in `yao/agent/robot/api/robot.go` + +#### Store Layer (Core CRUD - implement first) +- [ ] Create `store/robot.go` with `RobotStore` struct +- [ ] Implement `RobotStore.Save()` - create/update robot member +- [ ] Implement `RobotStore.Get()` - get by member_id +- [ ] Implement `RobotStore.List()` - list with filters +- [ ] Implement `RobotStore.Delete()` - delete robot member +- [ ] Implement `RobotStore.UpdateConfig()` - update config only + +#### API Layer (Thin wrappers calling store) +- [ ] Implement `api.Create()` - call `store.RobotStore.Save()` + cache refresh +- [ ] Implement `api.Update()` - call `store.RobotStore.UpdateConfig()` + cache refresh +- [ ] Implement `api.Remove()` - call `store.RobotStore.Delete()` + cache invalidate ### 1.2 Setup ⬜ @@ -199,16 +210,14 @@ ### 3.1 Backend Prerequisites ⬜ -Need to add in `robot/api/`: +#### Store Layer (Core implementation) +- [ ] Add `ExecutionStore.ListResults()` - query deliverables from execution delivery data +- [ ] Add `ExecutionStore.GetResult()` - get single deliverable detail +- [ ] Add `ExecutionStore.ListActivities()` - derive activities from execution history -- [ ] `ListResults(memberID, query)` function -- [ ] `GetResult(resultID)` function -- [ ] `ListActivities(query)` function - -Need to add in `robot/store/`: - -- [ ] Results store (query from execution delivery data) -- [ ] Activities store (or derive from job logs) +#### API Layer (Thin wrappers) +- [ ] Create `api/results.go` with `ListResults()`, `GetResult()` - call store +- [ ] Create `api/activities.go` with `ListActivities()` - call store ### 3.2 Results Endpoints ⬜ @@ -342,6 +351,23 @@ Need to add in `robot/`: ## Backend Extensions Required +> **Architecture:** Store layer handles CRUD, API layer handles business logic. +> This enables reuse across Golang API, JSAPI, and Yao Process. + +### robot/store/ Extensions (Core CRUD - implement first) + +| Function | Phase | Risk | Description | +|----------|-------|------|-------------| +| `RobotStore.Save()` | 1 | 🟢 Low | Create/update robot member | +| `RobotStore.Get()` | 1 | 🟢 Low | Get robot by member_id | +| `RobotStore.List()` | 1 | 🟢 Low | List robots with filters | +| `RobotStore.Delete()` | 1 | 🟢 Low | Delete robot member | +| `RobotStore.UpdateConfig()` | 1 | 🟢 Low | Update config only | +| `ExecutionStore.ListResults()` | 3 | 🟢 Low | Query deliverables from executions | +| `ExecutionStore.GetResult()` | 3 | 🟢 Low | Get single deliverable | +| `ExecutionStore.ListActivities()` | 3 | 🟢 Low | Derive activities from history | +| Conversation store | 5 | 🟡 Medium | Temporary chat history (Deferred) | + ### robot/types/ Extensions | Type/Field | Phase | Risk | Description | @@ -357,26 +383,18 @@ Need to add in `robot/`: |------|-------|------|-------------| | `load.go` | 1 | 🟢 Low | Add `bio` to `memberFields` slice | -### robot/api/ Extensions +### robot/api/ Extensions (Thin wrappers calling store) | Function | Phase | Risk | Description | |----------|-------|------|-------------| -| `Create()` | 1 | 🟢 Low | Create robot member via model | -| `Update()` | 1 | 🟢 Low | Update robot config via model | -| `Remove()` | 1 | 🟢 Low | Delete robot member via model | -| `ListResults()` | 3 | 🟢 Low | Query from execution delivery data | -| `GetResult()` | 3 | 🟢 Low | Get deliverable detail | -| `ListActivities()` | 3 | 🟢 Low | Derive from execution history | +| `Create()` | 1 | 🟢 Low | Call `store.RobotStore.Save()` + cache refresh | +| `Update()` | 1 | 🟢 Low | Call `store.RobotStore.UpdateConfig()` + cache refresh | +| `Remove()` | 1 | 🟢 Low | Call `store.RobotStore.Delete()` + cache invalidate | +| `ListResults()` | 3 | 🟢 Low | Call `store.ExecutionStore.ListResults()` | +| `GetResult()` | 3 | 🟢 Low | Call `store.ExecutionStore.GetResult()` | +| `ListActivities()` | 3 | 🟢 Low | Call `store.ExecutionStore.ListActivities()` | | `RetryExecution()` | 2 | 🟢 Low | Re-trigger with same input | -| `Chat()` | 5 | 🟡 Medium | Multi-turn conversation handler | - -### robot/store/ Extensions - -| Store | Phase | Risk | Description | -|-------|-------|------|-------------| -| Results query | 3 | 🟢 Low | Query from execution delivery data | -| Activities query | 3 | 🟢 Low | Derive from execution history | -| Conversation store | 5 | 🟡 Medium | Temporary chat history (redis/memory) | +| `Chat()` | 5 | 🟡 Medium | Multi-turn conversation (Deferred) | ### Event System (Phase 6 - Deferred)