Enhance Robot API and Store Architecture

- Introduced a new `RobotStore` in `store/robot.go` for core CRUD operations on robot members, including methods for saving, retrieving, listing, deleting, and updating configurations.
- Extended the API layer with new thin wrappers in `api/robot.go` for creating, updating, and removing robot members, ensuring cache refresh and validation.
- Added new files `api/results.go` and `api/activities.go` to handle results and activities, respectively, integrating with the execution store for enhanced functionality.
- Updated `DESIGN.md` and `GAPS.md` to reflect the new architecture and decisions regarding the separation of store and API layers, promoting better organization and reuse across different consumers.
This commit is contained in:
Max 2026-01-22 11:08:43 +08:00
parent a0fa0e9eff
commit cf2a98ccb4
3 changed files with 319 additions and 84 deletions

View file

@ -549,6 +549,31 @@ func checkTeamAccess(ctx context.Context, memberID string) error {
## 10. File Structure ## 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/`** **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. 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) └── robot/ # Robot sub-package (NEW)
├── DESIGN.md # This document ✅ ├── DESIGN.md # This document ✅
├── TODO.md # Implementation plan ✅ ├── TODO.md # Implementation plan ✅
├── GAPS.md # Gap analysis ✅
├── robot.go # Route registration (Attach function) ├── robot.go # Route registration (Attach function)
├── types.go # Request/Response types ├── types.go # Request/Response types
@ -672,23 +698,89 @@ func Attach(group *gin.RouterGroup, oauth types.OAuth) {
## 12. Implementation Notes ## 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 Consumers (Golang API / JSAPI / Yao Process)
3. **Activities API**: `ListActivities()` function
4. **Localization**: Add `Locale` parameter support
┌─────────────────────────────────────────┐
│ 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 ```go
2. **Activities Store**: Store and query activities (or derive from job logs) 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: 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 - Store display names in `__yao.member.display_name` (single language) initially
- Future: Add `display_name_cn`, `display_name_en` or use JSON `{"en": "...", "cn": "..."}` - Future: Add `display_name_cn`, `display_name_en` or use JSON `{"en": "...", "cn": "..."}`

View file

@ -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 | | Function | Status | Description |
|----------|--------|-------------| |----------|--------|-------------|
| `Create()` | ⬜ Missing | Create new robot member | | `RobotStore.Save()` | ⬜ Missing | Create or update robot member |
| `Update()` | ⬜ Missing | Update robot config | | `RobotStore.Get()` | ⬜ Missing | Get robot by member_id |
| `Remove()` | ⬜ Missing | Delete robot member | | `RobotStore.List()` | ⬜ Missing | List robots with filters |
| `ListResults()` | ⬜ Missing | Query deliverable files from executions | | `RobotStore.Delete()` | ⬜ Missing | Delete robot member |
| `GetResult()` | ⬜ Missing | Get single deliverable detail | | `RobotStore.UpdateConfig()` | ⬜ Missing | Update robot config only |
| `ListActivities()` | ⬜ Missing | Query activity feed |
| `RetryExecution()` | ⬜ Missing | Retry a failed execution |
### 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 | | Function | File | Status |
|----------|------|--------| |----------|------|--------|
@ -335,39 +419,56 @@ Add `confirm` parameter to trigger:
| `ResumeExecution()` | `execution.go` | ✅ Exists | | `ResumeExecution()` | `execution.go` | ✅ Exists |
| `StopExecution()` | `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 ```go
// Create creates a new robot member // RobotStore - persistent storage for robot members
func Create(ctx *types.Context, teamID string, req *CreateRobotRequest) (*types.Robot, error) type RobotStore struct {
modelID string
}
// Update updates robot config func NewRobotStore() *RobotStore {
func Update(ctx *types.Context, memberID string, req *UpdateRobotRequest) (*types.Robot, error) return &RobotStore{modelID: "__yao.member"}
}
// Remove deletes a robot member // Save creates or updates a robot member record
func Remove(ctx *types.Context, memberID string) error 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 ```go
// ListResults returns deliverable files for a robot // Create creates a new robot member (thin wrapper)
func ListResults(ctx *types.Context, memberID string, query *ResultQuery) (*ResultsResult, error) 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 // Update updates robot config (thin wrapper)
func GetResult(ctx *types.Context, resultID string) (*types.ResultFile, error) 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)** // Remove deletes a robot member (thin wrapper)
```go func Remove(ctx *types.Context, memberID string) error {
// ListActivities returns recent activities // 1. Check permissions
func ListActivities(ctx *types.Context, query *ActivityQuery) (*ActivitiesResult, error) // 2. Call store.RobotStore.Delete()
``` // 3. Invalidate cache
}
**File: `api/execution.go` (extend)**
```go
// RetryExecution retries a failed execution
func RetryExecution(ctx *types.Context, execID string) (*TriggerResult, error)
``` ```
--- ---
@ -672,20 +773,44 @@ type ActivityRecord struct {
### Backend (`yao/agent/robot/`) ### Backend (`yao/agent/robot/`)
#### Store Layer (Core CRUD - implement first)
| File | Action | Changes | | File | Action | Changes |
|------|--------|---------| |------|--------|---------|
| `types/robot.go` | Modify | Add `Bio` field, optionally `Name`/`CurrentTaskName` for Execution | | `store/robot.go` | **Create** | `RobotStore` - Robot member CRUD (Save, Get, List, Delete, UpdateConfig) |
| `types/conversation.go` | Create | `Conversation`, `ChatRequest`, `ChatResponse` types | | `store/execution.go` | Modify | Add `ListResults()`, `GetResult()`, `ListActivities()` |
| `cache/load.go` | Modify | Add `bio` to `memberFields` slice | | `store/conversation.go` | Create | Temporary conversation storage (Phase 5 - Deferred) |
| `store/conversation.go` | Create | Temporary conversation storage (redis/memory) |
#### 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 | | `types/context.go` | Modify | Add `Locale` field |
| `api/robot.go` | Modify | Add `Create()`, `Update()`, `Remove()` |
| `api/chat.go` | Create | `Chat()` - multi-turn conversation handler | #### Cache Layer
| `api/trigger.go` | Modify | Add `ConversationID` support |
| 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/execution.go` | Modify | Add `RetryExecution()` |
| `api/results.go` | Create | `ListResults()`, `GetResult()` - query from execution store | | `api/chat.go` | Create | `Chat()` - multi-turn conversation (Phase 5 - Deferred) |
| `api/activities.go` | Create | `ListActivities()` - derive from execution history | | `api/trigger.go` | Modify | Add `ConversationID` support (Phase 5 - Deferred) |
| `events/bus.go` | Create | Event bus for SSE (Phase 5) |
#### Events Layer (Phase 6 - Deferred)
| File | Action | Changes |
|------|--------|---------|
| `events/bus.go` | Create | Event bus for SSE |
### OpenAPI (`yao/openapi/agent/robot/`) ### OpenAPI (`yao/openapi/agent/robot/`)

View file

@ -42,11 +42,22 @@
### 1.1 Backend Prerequisites ⬜ ### 1.1 Backend Prerequisites ⬜
#### Types & Cache
- [ ] Add `Bio` field to `types.Robot` struct in `yao/agent/robot/types/robot.go` - [ ] 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` - [ ] 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` #### Store Layer (Core CRUD - implement first)
- [ ] Implement `api.Remove()` in `yao/agent/robot/api/robot.go` - [ ] 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 ⬜ ### 1.2 Setup ⬜
@ -199,16 +210,14 @@
### 3.1 Backend Prerequisites ⬜ ### 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 #### API Layer (Thin wrappers)
- [ ] `GetResult(resultID)` function - [ ] Create `api/results.go` with `ListResults()`, `GetResult()` - call store
- [ ] `ListActivities(query)` function - [ ] Create `api/activities.go` with `ListActivities()` - call store
Need to add in `robot/store/`:
- [ ] Results store (query from execution delivery data)
- [ ] Activities store (or derive from job logs)
### 3.2 Results Endpoints ⬜ ### 3.2 Results Endpoints ⬜
@ -342,6 +351,23 @@ Need to add in `robot/`:
## Backend Extensions Required ## 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 ### robot/types/ Extensions
| Type/Field | Phase | Risk | Description | | Type/Field | Phase | Risk | Description |
@ -357,26 +383,18 @@ Need to add in `robot/`:
|------|-------|------|-------------| |------|-------|------|-------------|
| `load.go` | 1 | 🟢 Low | Add `bio` to `memberFields` slice | | `load.go` | 1 | 🟢 Low | Add `bio` to `memberFields` slice |
### robot/api/ Extensions ### robot/api/ Extensions (Thin wrappers calling store)
| Function | Phase | Risk | Description | | Function | Phase | Risk | Description |
|----------|-------|------|-------------| |----------|-------|------|-------------|
| `Create()` | 1 | 🟢 Low | Create robot member via model | | `Create()` | 1 | 🟢 Low | Call `store.RobotStore.Save()` + cache refresh |
| `Update()` | 1 | 🟢 Low | Update robot config via model | | `Update()` | 1 | 🟢 Low | Call `store.RobotStore.UpdateConfig()` + cache refresh |
| `Remove()` | 1 | 🟢 Low | Delete robot member via model | | `Remove()` | 1 | 🟢 Low | Call `store.RobotStore.Delete()` + cache invalidate |
| `ListResults()` | 3 | 🟢 Low | Query from execution delivery data | | `ListResults()` | 3 | 🟢 Low | Call `store.ExecutionStore.ListResults()` |
| `GetResult()` | 3 | 🟢 Low | Get deliverable detail | | `GetResult()` | 3 | 🟢 Low | Call `store.ExecutionStore.GetResult()` |
| `ListActivities()` | 3 | 🟢 Low | Derive from execution history | | `ListActivities()` | 3 | 🟢 Low | Call `store.ExecutionStore.ListActivities()` |
| `RetryExecution()` | 2 | 🟢 Low | Re-trigger with same input | | `RetryExecution()` | 2 | 🟢 Low | Re-trigger with same input |
| `Chat()` | 5 | 🟡 Medium | Multi-turn conversation handler | | `Chat()` | 5 | 🟡 Medium | Multi-turn conversation (Deferred) |
### 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) |
### Event System (Phase 6 - Deferred) ### Event System (Phase 6 - Deferred)