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.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": "..."}`

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 |
|----------|--------|-------------|
| `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/`)

View file

@ -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)