plugin-agent-orchestrator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -0
- package/client.d.ts +2 -0
- package/client.js +1 -0
- package/dist/client/AIEmployeeSelect.js +29 -0
- package/dist/client/AIEmployeesContext.js +64 -0
- package/dist/client/OrchestratorSettings.js +32 -0
- package/dist/client/RulesTab.js +144 -0
- package/dist/client/TracingTab.js +88 -0
- package/dist/client/index.js +8 -0
- package/dist/client/locale/en-US.json +20 -0
- package/dist/client/locale/vi-VN.json +20 -0
- package/dist/client/plugin.js +17 -0
- package/dist/server/index.js +8 -0
- package/dist/server/plugin.js +44 -0
- package/dist/server/resources/tracing.js +85 -0
- package/dist/server/tools/delegate-task.js +317 -0
- package/package.json +43 -0
- package/server.d.ts +2 -0
- package/server.js +1 -0
- package/src/client/AIEmployeeSelect.tsx +49 -0
- package/src/client/AIEmployeesContext.tsx +58 -0
- package/src/client/OrchestratorSettings.tsx +46 -0
- package/src/client/RulesTab.tsx +272 -0
- package/src/client/TracingTab.tsx +227 -0
- package/src/client/index.tsx +1 -0
- package/src/client/plugin.tsx +15 -0
- package/src/index.ts +2 -0
- package/src/locale/en-US.json +20 -0
- package/src/locale/vi-VN.json +20 -0
- package/src/server/collections/orchestrator-config.ts +49 -0
- package/src/server/collections/orchestrator-logs.ts +99 -0
- package/src/server/index.ts +1 -0
- package/src/server/plugin.ts +46 -0
- package/src/server/resources/tracing.ts +91 -0
- package/src/server/tools/delegate-task.ts +388 -0
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Plugin Agent Orchestrator
|
|
2
|
+
|
|
3
|
+
**Hierarchical Multi-Agent Orchestration for NocoBase AI Employees**
|
|
4
|
+
|
|
5
|
+
Plugin này cho phép các AI Employee trong NocoBase hoạt động theo mô hình **Leader ↔ Sub-Agent**: một AI Employee (Leader) có thể tự động ủy thác (delegate) nhiệm vụ cho các AI Employee khác (Sub-Agent) để xử lý các tác vụ chuyên biệt — tất cả mà không cần sửa đổi core `plugin-ai`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Tính năng chính
|
|
10
|
+
|
|
11
|
+
### 🏗️ Hierarchical Delegation (Ủy thác phân cấp)
|
|
12
|
+
- **Leader** nhận yêu cầu từ user → phân tích → gọi Sub-Agent phù hợp qua tool call
|
|
13
|
+
- Mỗi Sub-Agent được đăng ký như một tool riêng (ví dụ: `delegate_to_sql_expert`)
|
|
14
|
+
- LLM tự chọn Sub-Agent phù hợp nhất dựa trên mô tả của từng agent
|
|
15
|
+
|
|
16
|
+
### 🔄 Depth Control (Kiểm soát độ sâu)
|
|
17
|
+
- Giới hạn số lớp delegation (ví dụ: `maxDepth: 1` = Leader gọi Sub-Agent, Sub-Agent không được gọi tiếp)
|
|
18
|
+
- Ngăn chặn vòng lặp vô hạn (circular delegation) — các tool `delegate_to_*` tự động bị loại khỏi Sub-Agent
|
|
19
|
+
|
|
20
|
+
### ⏱️ Timeout & Abort
|
|
21
|
+
- Mỗi rule cấu hình thời gian chờ tối đa (mặc định 120s)
|
|
22
|
+
- Khi timeout, stream LLM bị hủy ngay lập tức qua `AbortController` — không tốn thêm token
|
|
23
|
+
|
|
24
|
+
### 🛡️ Per-Leader Scoping
|
|
25
|
+
- Chỉ Leader được cấu hình mới có thể gọi Sub-Agent tương ứng
|
|
26
|
+
- Kiểm tra tại thời điểm invoke — không phụ thuộc vào core API
|
|
27
|
+
|
|
28
|
+
### 📊 Swarm Tracing (Giám sát)
|
|
29
|
+
- Ghi log mỗi lần delegation: ai gọi ai, task gì, kết quả ra sao, mất bao lâu
|
|
30
|
+
- Trang admin trực quan để theo dõi và debug multi-agent flows
|
|
31
|
+
|
|
32
|
+
### 🔌 Zero Core Modification
|
|
33
|
+
- Chỉ sử dụng **public APIs** từ `@nocobase/ai` và `@nocobase/plugin-ai`
|
|
34
|
+
- Không import private classes, không patch core code
|
|
35
|
+
- Tương thích NocoBase 2.x — upgrade-safe
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 📐 Kiến trúc
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌──────────────────────────────────────────────────────────┐
|
|
43
|
+
│ User Chat Session │
|
|
44
|
+
│ "Hãy phân tích doanh thu Q4 và tạo báo cáo" │
|
|
45
|
+
└─────────────────────┬────────────────────────────────────┘
|
|
46
|
+
│
|
|
47
|
+
▼
|
|
48
|
+
┌──────────────────────────────────────────────────────────┐
|
|
49
|
+
│ Leader AI Employee (ví dụ: "Project Manager") │
|
|
50
|
+
│ │
|
|
51
|
+
│ Tools available: │
|
|
52
|
+
│ ├── delegate_to_sql_expert ← plugin-agent-orch │
|
|
53
|
+
│ ├── delegate_to_report_writer ← plugin-agent-orch │
|
|
54
|
+
│ └── other_skills ← plugin-ai built-in │
|
|
55
|
+
└────┬─────────────────────┬───────────────────────────────┘
|
|
56
|
+
│ tool_call │ tool_call
|
|
57
|
+
▼ ▼
|
|
58
|
+
┌────────────────┐ ┌─────────────────┐
|
|
59
|
+
│ SQL Expert │ │ Report Writer │
|
|
60
|
+
│ (Sub-Agent) │ │ (Sub-Agent) │
|
|
61
|
+
│ │ │ │
|
|
62
|
+
│ createReact │ │ createReact │
|
|
63
|
+
│ Agent + tools │ │ Agent + tools │
|
|
64
|
+
└───────┬────────┘ └────────┬────────┘
|
|
65
|
+
│ result │ result
|
|
66
|
+
└─────────┬───────────┘
|
|
67
|
+
▼
|
|
68
|
+
┌──────────────────────────────────────────────────────────┐
|
|
69
|
+
│ Leader tổng hợp kết quả → trả lời user │
|
|
70
|
+
└──────────────────────────────────────────────────────────┘
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🚀 Cài đặt & Kích hoạt
|
|
76
|
+
|
|
77
|
+
### Yêu cầu
|
|
78
|
+
- NocoBase `>= 2.0`
|
|
79
|
+
- Plugin `@nocobase/plugin-ai` đã kích hoạt
|
|
80
|
+
- Ít nhất 2 AI Employees đã được tạo (1 Leader, 1+ Sub-Agent)
|
|
81
|
+
|
|
82
|
+
### Bước 1: Cài đặt plugin
|
|
83
|
+
```bash
|
|
84
|
+
# Trong monorepo NocoBase
|
|
85
|
+
yarn build plugin-agent-orchestrator
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Bước 2: Kích hoạt
|
|
89
|
+
Vào **Settings → Plugin Manager** → tìm "Agent Orchestrator" → **Enable**
|
|
90
|
+
|
|
91
|
+
### Bước 3: Cấu hình
|
|
92
|
+
Sau khi kích hoạt, vào **Settings → AI → Agent Orchestrator**
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 📖 Hướng dẫn sử dụng
|
|
97
|
+
|
|
98
|
+
### Tab 1: Orchestration Rules
|
|
99
|
+
|
|
100
|
+
Đây là nơi cấu hình quan hệ Leader ↔ Sub-Agent.
|
|
101
|
+
|
|
102
|
+
#### Tạo rule mới
|
|
103
|
+
1. Nhấn **New Rule**
|
|
104
|
+
2. Chọn **Leader (Orchestrator)**: AI Employee đóng vai trò điều phối
|
|
105
|
+
3. Chọn **Sub-Agent**: AI Employee sẽ nhận nhiệm vụ được ủy thác
|
|
106
|
+
4. Cấu hình:
|
|
107
|
+
- **Max Delegation Depth**: Độ sâu tối đa (1-3)
|
|
108
|
+
- `1` = Leader gọi Sub-Agent, Sub-Agent chỉ dùng tool của mình
|
|
109
|
+
- `2` = Sub-Agent có thể gọi tiếp Sub-Agent khác (nếu được cấu hình)
|
|
110
|
+
- **Timeout (ms)**: Thời gian chờ tối đa
|
|
111
|
+
- `120000` (2 phút) — khuyến nghị cho tác vụ đơn giản
|
|
112
|
+
- `300000` (5 phút) — cho tác vụ phức tạp
|
|
113
|
+
- **Enabled**: Bật/tắt rule
|
|
114
|
+
5. Nhấn **Save**
|
|
115
|
+
|
|
116
|
+
#### Ví dụ cấu hình
|
|
117
|
+
|
|
118
|
+
| Leader | Sub-Agent | Max Depth | Timeout | Mô tả |
|
|
119
|
+
|--------|-----------|-----------|---------|-------|
|
|
120
|
+
| PM Bot | SQL Expert | 1 | 120s | PM giao truy vấn DB cho SQL Expert |
|
|
121
|
+
| PM Bot | Report Writer | 1 | 300s | PM giao viết báo cáo |
|
|
122
|
+
| PM Bot | Code Reviewer | 1 | 180s | PM giao review code |
|
|
123
|
+
|
|
124
|
+
> **Lưu ý**: Leader và Sub-Agent không được trùng nhau. Một Sub-Agent có thể phục vụ nhiều Leader.
|
|
125
|
+
|
|
126
|
+
### Tab 2: Swarm Tracing
|
|
127
|
+
|
|
128
|
+
Trang giám sát hiển thị log tất cả các lần delegation:
|
|
129
|
+
|
|
130
|
+
| Cột | Mô tả |
|
|
131
|
+
|-----|-------|
|
|
132
|
+
| **Time** | Thời điểm thực thi |
|
|
133
|
+
| **Sub-Agent** | Agent được gọi |
|
|
134
|
+
| **Task** | Mô tả nhiệm vụ |
|
|
135
|
+
| **Status** | ✅ success hoặc ❌ error |
|
|
136
|
+
| **Duration** | Thời gian thực thi |
|
|
137
|
+
| **Depth** | Độ sâu delegation (0 = cấp đầu tiên) |
|
|
138
|
+
|
|
139
|
+
Nhấn **Detail** để xem chi tiết: task gốc, kết quả đầy đủ, lỗi (nếu có).
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 💡 Best Practices
|
|
144
|
+
|
|
145
|
+
### 1. Thiết kế Agent hiệu quả
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
✅ NÊN: Mỗi Sub-Agent chuyên một lĩnh vực rõ ràng
|
|
149
|
+
- "SQL Query Expert" — chỉ chạy truy vấn
|
|
150
|
+
- "Document Writer" — chỉ viết tài liệu
|
|
151
|
+
|
|
152
|
+
❌ KHÔNG NÊN: Sub-Agent làm quá nhiều việc
|
|
153
|
+
- "General Assistant" — quá rộng, Leader không biết khi nào nên gọi
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2. System Prompt cho Leader
|
|
157
|
+
|
|
158
|
+
Thêm hướng dẫn trong system prompt của Leader để nó biết khi nào nên delegate:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
Bạn là Project Manager. Khi nhận yêu cầu:
|
|
162
|
+
- Cần truy vấn database → delegate cho SQL Expert
|
|
163
|
+
- Cần viết báo cáo → delegate cho Report Writer
|
|
164
|
+
- Có thể tự trả lời → trả lời trực tiếp
|
|
165
|
+
|
|
166
|
+
KHÔNG BAO GIỜ tự viết SQL. Luôn delegate cho SQL Expert.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 3. Bio/About cho Sub-Agent
|
|
170
|
+
|
|
171
|
+
Viết mô tả rõ ràng trong trường `About` của Sub-Agent — nội dung này được LLM đọc để quyết định có gọi agent đó không:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Chuyên gia truy vấn PostgreSQL. Có khả năng:
|
|
175
|
+
- Viết và tối ưu SQL queries
|
|
176
|
+
- Phân tích schema và index
|
|
177
|
+
- Giải thích execution plan
|
|
178
|
+
Không xử lý: tạo báo cáo, viết code ứng dụng.
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 4. Timeout hợp lý
|
|
182
|
+
|
|
183
|
+
| Loại tác vụ | Timeout khuyến nghị |
|
|
184
|
+
|-------------|---------------------|
|
|
185
|
+
| Truy vấn DB đơn giản | 30s - 60s |
|
|
186
|
+
| Phân tích dữ liệu | 120s |
|
|
187
|
+
| Viết tài liệu dài | 300s |
|
|
188
|
+
| Tác vụ phức tạp + tool calls | 300s - 600s |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 🔧 Chi tiết kỹ thuật
|
|
193
|
+
|
|
194
|
+
### Collections
|
|
195
|
+
|
|
196
|
+
| Collection | Mục đích |
|
|
197
|
+
|------------|----------|
|
|
198
|
+
| `orchestratorConfig` | Lưu rules Leader ↔ Sub-Agent |
|
|
199
|
+
| `orchestratorLogs` | Lưu log delegation (cho Tracing) |
|
|
200
|
+
|
|
201
|
+
### APIs
|
|
202
|
+
|
|
203
|
+
| Endpoint | Mô tả |
|
|
204
|
+
|----------|-------|
|
|
205
|
+
| `orchestratorConfig:list` | Danh sách rules |
|
|
206
|
+
| `orchestratorConfig:create` | Tạo rule mới |
|
|
207
|
+
| `orchestratorConfig:update` | Cập nhật rule |
|
|
208
|
+
| `orchestratorConfig:destroy` | Xóa rule |
|
|
209
|
+
| `orchestratorTracing:list` | Danh sách log delegation |
|
|
210
|
+
| `orchestratorTracing:get` | Chi tiết một log |
|
|
211
|
+
|
|
212
|
+
### Execution Flow
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
1. Plugin load
|
|
216
|
+
└─ registerDynamicTools() → đăng ký delegate_to_* tools vào core toolsManager
|
|
217
|
+
|
|
218
|
+
2. User chat với Leader
|
|
219
|
+
└─ Leader LLM nhận tool list (bao gồm delegate_to_*)
|
|
220
|
+
└─ LLM quyết định gọi delegate_to_sql_expert({ task: "..." })
|
|
221
|
+
|
|
222
|
+
3. Tool invoke
|
|
223
|
+
├─ Check per-leader scoping (allowedLeaders)
|
|
224
|
+
├─ Check depth limit (ORCHESTRATOR_DEPTH_KEY)
|
|
225
|
+
├─ Resolve LLM model từ Sub-Agent's modelSettings
|
|
226
|
+
├─ Resolve tools từ Sub-Agent's skillSettings
|
|
227
|
+
├─ createReactAgent({ llm, tools })
|
|
228
|
+
├─ executor.stream({ messages: [system, human] }, { signal })
|
|
229
|
+
├─ Collect AI response chunks
|
|
230
|
+
├─ Log to orchestratorLogs
|
|
231
|
+
└─ Return result to Leader
|
|
232
|
+
|
|
233
|
+
4. Leader tổng hợp và trả lời user
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Dependencies
|
|
237
|
+
|
|
238
|
+
| Package | Version | Mục đích |
|
|
239
|
+
|---------|---------|----------|
|
|
240
|
+
| `@langchain/core` | ^0.3.0 | DynamicStructuredTool, Messages |
|
|
241
|
+
| `@langchain/langgraph` | ^0.2.0 | createReactAgent |
|
|
242
|
+
| `zod` | ^3.23.0 | Tool schema validation |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 📁 Cấu trúc project
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
plugin-agent-orchestrator/
|
|
250
|
+
├── package.json
|
|
251
|
+
├── README.md
|
|
252
|
+
├── src/
|
|
253
|
+
│ ├── index.ts
|
|
254
|
+
│ ├── client/
|
|
255
|
+
│ │ ├── index.tsx
|
|
256
|
+
│ │ ├── plugin.tsx # Đăng ký settings page
|
|
257
|
+
│ │ ├── AIEmployeesContext.tsx # Shared context (P3 optimization)
|
|
258
|
+
│ │ ├── AIEmployeeSelect.tsx # Reusable select component
|
|
259
|
+
│ │ ├── OrchestratorSettings.tsx # Main settings page
|
|
260
|
+
│ │ ├── RulesTab.tsx # CRUD rules
|
|
261
|
+
│ │ └── TracingTab.tsx # Tracing dashboard
|
|
262
|
+
│ ├── server/
|
|
263
|
+
│ │ ├── index.ts
|
|
264
|
+
│ │ ├── plugin.ts # Main server plugin
|
|
265
|
+
│ │ ├── collections/
|
|
266
|
+
│ │ │ ├── orchestrator-config.ts
|
|
267
|
+
│ │ │ └── orchestrator-logs.ts
|
|
268
|
+
│ │ ├── resources/
|
|
269
|
+
│ │ │ └── tracing.ts # Custom read-only resource
|
|
270
|
+
│ │ └── tools/
|
|
271
|
+
│ │ └── delegate-task.ts # Core delegation logic
|
|
272
|
+
│ └── locale/
|
|
273
|
+
│ ├── en-US.json
|
|
274
|
+
│ └── vi-VN.json
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## ⚠️ Lưu ý quan trọng
|
|
280
|
+
|
|
281
|
+
1. **Không sửa core plugin-ai**: Plugin này hoàn toàn standalone, chỉ dùng public APIs
|
|
282
|
+
2. **Sub-Agent cần có LLM model**: Mỗi Sub-Agent phải được cấu hình `modelSettings` (llmService + model) trong AI Employee settings
|
|
283
|
+
3. **Sub-Agent cần có skills**: Tools/skills được gán cho Sub-Agent trong `skillSettings` sẽ được cấp cho nó khi thực thi delegation
|
|
284
|
+
4. **Circular prevention**: Các tool `delegate_to_*` tự động bị loại khỏi Sub-Agent → Sub-Agent không thể gọi lại Leader
|
|
285
|
+
5. **Log retention**: `orchestratorLogs` tích lũy theo thời gian — cân nhắc cleanup định kỳ cho production
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 📜 License
|
|
290
|
+
|
|
291
|
+
Apache-2.0
|
package/client.d.ts
ADDED
package/client.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/client');
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AIEmployeeSelect = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
9
|
+
const antd_1 = require("antd");
|
|
10
|
+
const AIEmployeesContext_1 = require("./AIEmployeesContext");
|
|
11
|
+
/**
|
|
12
|
+
* Reusable Select component for AI Employees.
|
|
13
|
+
* P3 FIX: Uses shared AIEmployeesContext instead of making its own API call.
|
|
14
|
+
*/
|
|
15
|
+
const AIEmployeeSelect = ({ value, onChange, exclude, placeholder = 'Select AI Employee...' }) => {
|
|
16
|
+
const { employees, loading } = (0, AIEmployeesContext_1.useAIEmployees)();
|
|
17
|
+
const options = react_1.default.useMemo(() => {
|
|
18
|
+
return employees
|
|
19
|
+
.filter((emp) => !exclude || emp.username !== exclude)
|
|
20
|
+
.map((emp) => ({
|
|
21
|
+
label: emp.nickname,
|
|
22
|
+
value: emp.username,
|
|
23
|
+
description: emp.about,
|
|
24
|
+
}));
|
|
25
|
+
}, [employees, exclude]);
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)(antd_1.Select, { loading: loading, options: options, value: value, onChange: onChange, placeholder: placeholder, showSearch: true, filterOption: (input, option) => (option?.label ?? '').toString().toLowerCase().includes(input.toLowerCase()) ||
|
|
27
|
+
(option?.value ?? '').toString().toLowerCase().includes(input.toLowerCase()), optionRender: (option) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: 500 }, children: option.label }), option.data.description && ((0, jsx_runtime_1.jsx)("div", { style: { fontSize: 12, color: '#888' }, children: option.data.description }))] })) }));
|
|
28
|
+
};
|
|
29
|
+
exports.AIEmployeeSelect = AIEmployeeSelect;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.useAIEmployees = exports.AIEmployeesProvider = void 0;
|
|
27
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
28
|
+
const react_1 = __importStar(require("react"));
|
|
29
|
+
const client_1 = require("@nocobase/client");
|
|
30
|
+
const AIEmployeesContext = (0, react_1.createContext)({
|
|
31
|
+
employees: [],
|
|
32
|
+
employeeMap: new Map(),
|
|
33
|
+
loading: false,
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* P3 FIX: Shared context provider that fetches aiEmployees once
|
|
37
|
+
* and shares the data across RulesTab, TracingTab, and AIEmployeeSelect.
|
|
38
|
+
*/
|
|
39
|
+
const AIEmployeesProvider = ({ children }) => {
|
|
40
|
+
const { data, loading } = (0, client_1.useRequest)({
|
|
41
|
+
url: 'aiEmployees:list',
|
|
42
|
+
params: { pageSize: 200 },
|
|
43
|
+
});
|
|
44
|
+
const value = react_1.default.useMemo(() => {
|
|
45
|
+
const rawEmployees = data?.data || [];
|
|
46
|
+
const employees = rawEmployees.map((emp) => ({
|
|
47
|
+
username: emp.username,
|
|
48
|
+
nickname: emp.nickname || emp.username,
|
|
49
|
+
about: emp.about?.substring(0, 80),
|
|
50
|
+
}));
|
|
51
|
+
const employeeMap = new Map();
|
|
52
|
+
for (const emp of employees) {
|
|
53
|
+
employeeMap.set(emp.username, emp.nickname);
|
|
54
|
+
}
|
|
55
|
+
return { employees, employeeMap, loading };
|
|
56
|
+
}, [data, loading]);
|
|
57
|
+
return ((0, jsx_runtime_1.jsx)(AIEmployeesContext.Provider, { value: value, children: children }));
|
|
58
|
+
};
|
|
59
|
+
exports.AIEmployeesProvider = AIEmployeesProvider;
|
|
60
|
+
/**
|
|
61
|
+
* Hook to access shared AI employees data.
|
|
62
|
+
*/
|
|
63
|
+
const useAIEmployees = () => (0, react_1.useContext)(AIEmployeesContext);
|
|
64
|
+
exports.useAIEmployees = useAIEmployees;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrchestratorSettings = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const antd_1 = require("antd");
|
|
6
|
+
const icons_1 = require("@ant-design/icons");
|
|
7
|
+
const RulesTab_1 = require("./RulesTab");
|
|
8
|
+
const TracingTab_1 = require("./TracingTab");
|
|
9
|
+
const AIEmployeesContext_1 = require("./AIEmployeesContext");
|
|
10
|
+
/**
|
|
11
|
+
* Main settings page for the Agent Orchestrator plugin.
|
|
12
|
+
* Contains two tabs:
|
|
13
|
+
* - Rules: Configure which AI Employees can delegate tasks to others
|
|
14
|
+
* - Tracing: View and debug delegation execution logs
|
|
15
|
+
*
|
|
16
|
+
* P3 FIX: Wraps with AIEmployeesProvider so both tabs share one API call.
|
|
17
|
+
*/
|
|
18
|
+
const OrchestratorSettings = () => {
|
|
19
|
+
return ((0, jsx_runtime_1.jsx)(AIEmployeesContext_1.AIEmployeesProvider, { children: (0, jsx_runtime_1.jsx)("div", { style: { padding: '0 24px 24px' }, children: (0, jsx_runtime_1.jsx)(antd_1.Tabs, { defaultActiveKey: "rules", items: [
|
|
20
|
+
{
|
|
21
|
+
key: 'rules',
|
|
22
|
+
label: ((0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)(icons_1.ApartmentOutlined, {}), " Orchestration Rules"] })),
|
|
23
|
+
children: (0, jsx_runtime_1.jsx)(RulesTab_1.RulesTab, {}),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: 'tracing',
|
|
27
|
+
label: ((0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)(icons_1.MonitorOutlined, {}), " Swarm Tracing"] })),
|
|
28
|
+
children: (0, jsx_runtime_1.jsx)(TracingTab_1.TracingTab, {}),
|
|
29
|
+
},
|
|
30
|
+
] }) }) }));
|
|
31
|
+
};
|
|
32
|
+
exports.OrchestratorSettings = OrchestratorSettings;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RulesTab = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const antd_1 = require("antd");
|
|
7
|
+
const icons_1 = require("@ant-design/icons");
|
|
8
|
+
const client_1 = require("@nocobase/client");
|
|
9
|
+
const AIEmployeeSelect_1 = require("./AIEmployeeSelect");
|
|
10
|
+
const AIEmployeesContext_1 = require("./AIEmployeesContext");
|
|
11
|
+
const { Text } = antd_1.Typography;
|
|
12
|
+
const RulesTab = () => {
|
|
13
|
+
const api = (0, client_1.useAPIClient)();
|
|
14
|
+
const [visible, setVisible] = (0, react_1.useState)(false);
|
|
15
|
+
const [editingRecord, setEditingRecord] = (0, react_1.useState)(null);
|
|
16
|
+
const [form] = antd_1.Form.useForm();
|
|
17
|
+
const { data, loading, refresh } = (0, client_1.useRequest)({
|
|
18
|
+
url: 'orchestratorConfig:list',
|
|
19
|
+
params: {
|
|
20
|
+
sort: ['-createdAt'],
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
// P3 FIX: Use shared context instead of duplicate API call
|
|
24
|
+
const { employeeMap } = (0, AIEmployeesContext_1.useAIEmployees)();
|
|
25
|
+
const handleOpen = (record) => {
|
|
26
|
+
setEditingRecord(record);
|
|
27
|
+
if (record) {
|
|
28
|
+
form.setFieldsValue(record);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
form.resetFields();
|
|
32
|
+
form.setFieldsValue({ enabled: true, maxDepth: 1, timeout: 120000 });
|
|
33
|
+
}
|
|
34
|
+
setVisible(true);
|
|
35
|
+
};
|
|
36
|
+
const handleClose = () => {
|
|
37
|
+
setVisible(false);
|
|
38
|
+
setEditingRecord(null);
|
|
39
|
+
};
|
|
40
|
+
const handleSave = async (values) => {
|
|
41
|
+
// Validate: leader !== subAgent
|
|
42
|
+
if (values.leaderUsername === values.subAgentUsername) {
|
|
43
|
+
antd_1.message.error('Leader and Sub-Agent cannot be the same employee.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
if (editingRecord) {
|
|
48
|
+
await api.request({
|
|
49
|
+
url: 'orchestratorConfig:update',
|
|
50
|
+
method: 'put',
|
|
51
|
+
params: { filterByTk: editingRecord.id },
|
|
52
|
+
data: values,
|
|
53
|
+
});
|
|
54
|
+
antd_1.message.success('Rule updated');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
await api.request({
|
|
58
|
+
url: 'orchestratorConfig:create',
|
|
59
|
+
method: 'post',
|
|
60
|
+
data: values,
|
|
61
|
+
});
|
|
62
|
+
antd_1.message.success('Rule created');
|
|
63
|
+
}
|
|
64
|
+
handleClose();
|
|
65
|
+
refresh();
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
const msg = e?.response?.data?.errors?.[0]?.message || e.message;
|
|
69
|
+
antd_1.message.error(`Save failed: ${msg}`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const handleDelete = async (id) => {
|
|
73
|
+
try {
|
|
74
|
+
await api.request({
|
|
75
|
+
url: 'orchestratorConfig:destroy',
|
|
76
|
+
method: 'delete',
|
|
77
|
+
params: { filterByTk: id },
|
|
78
|
+
});
|
|
79
|
+
antd_1.message.success('Rule deleted');
|
|
80
|
+
refresh();
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
antd_1.message.error(`Delete failed: ${e.message}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const columns = [
|
|
87
|
+
{
|
|
88
|
+
title: 'Leader (Orchestrator)',
|
|
89
|
+
dataIndex: 'leaderUsername',
|
|
90
|
+
key: 'leaderUsername',
|
|
91
|
+
render: (username) => ((0, jsx_runtime_1.jsx)(antd_1.Tag, { color: "blue", children: employeeMap.get(username) || username })),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
title: '',
|
|
95
|
+
key: 'arrow',
|
|
96
|
+
width: 50,
|
|
97
|
+
render: () => (0, jsx_runtime_1.jsx)(icons_1.SwapRightOutlined, { style: { color: '#999', fontSize: 18 } }),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
title: 'Sub-Agent',
|
|
101
|
+
dataIndex: 'subAgentUsername',
|
|
102
|
+
key: 'subAgentUsername',
|
|
103
|
+
render: (username) => ((0, jsx_runtime_1.jsx)(antd_1.Tag, { color: "green", children: employeeMap.get(username) || username })),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
title: 'Max Depth',
|
|
107
|
+
dataIndex: 'maxDepth',
|
|
108
|
+
key: 'maxDepth',
|
|
109
|
+
width: 100,
|
|
110
|
+
render: (v) => v ?? 1,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
title: 'Timeout',
|
|
114
|
+
dataIndex: 'timeout',
|
|
115
|
+
key: 'timeout',
|
|
116
|
+
width: 100,
|
|
117
|
+
render: (v) => `${((v ?? 120000) / 1000).toFixed(0)}s`,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
title: 'Enabled',
|
|
121
|
+
dataIndex: 'enabled',
|
|
122
|
+
key: 'enabled',
|
|
123
|
+
width: 80,
|
|
124
|
+
render: (enabled, record) => ((0, jsx_runtime_1.jsx)(antd_1.Switch, { checked: enabled, size: "small", onChange: async (checked) => {
|
|
125
|
+
await api.request({
|
|
126
|
+
url: 'orchestratorConfig:update',
|
|
127
|
+
method: 'put',
|
|
128
|
+
params: { filterByTk: record.id },
|
|
129
|
+
data: { enabled: checked },
|
|
130
|
+
});
|
|
131
|
+
refresh();
|
|
132
|
+
} })),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
title: 'Actions',
|
|
136
|
+
key: 'actions',
|
|
137
|
+
width: 160,
|
|
138
|
+
render: (_, record) => ((0, jsx_runtime_1.jsxs)(antd_1.Space, { children: [(0, jsx_runtime_1.jsx)(antd_1.Button, { type: "link", size: "small", icon: (0, jsx_runtime_1.jsx)(icons_1.EditOutlined, {}), onClick: () => handleOpen(record), children: "Edit" }), (0, jsx_runtime_1.jsx)(antd_1.Popconfirm, { title: "Delete this rule?", onConfirm: () => handleDelete(record.id), children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "link", size: "small", danger: true, icon: (0, jsx_runtime_1.jsx)(icons_1.DeleteOutlined, {}), children: "Delete" }) })] })),
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
const leaderUsername = antd_1.Form.useWatch('leaderUsername', form);
|
|
142
|
+
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "info", showIcon: true, style: { marginBottom: 16 }, message: "Orchestration Rules", description: (0, jsx_runtime_1.jsx)(Text, { type: "secondary", children: "Configure which AI Employees can act as Leaders (Orchestrators) and which ones they can delegate tasks to. Each rule creates a callable tool for the Leader to invoke the Sub-Agent." }) }), (0, jsx_runtime_1.jsxs)(antd_1.Card, { bordered: false, children: [(0, jsx_runtime_1.jsx)("div", { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }, children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", icon: (0, jsx_runtime_1.jsx)(icons_1.PlusOutlined, {}), onClick: () => handleOpen(), children: "New Rule" }) }), (0, jsx_runtime_1.jsx)(antd_1.Table, { rowKey: "id", loading: loading, dataSource: data?.data || [], columns: columns, pagination: { hideOnSinglePage: true, pageSize: 20 }, size: "middle" })] }), (0, jsx_runtime_1.jsx)(antd_1.Drawer, { title: editingRecord ? 'Edit Orchestration Rule' : 'New Orchestration Rule', width: 480, onClose: handleClose, open: visible, styles: { body: { paddingBottom: 80 } }, extra: (0, jsx_runtime_1.jsxs)(antd_1.Space, { children: [(0, jsx_runtime_1.jsx)(antd_1.Button, { onClick: handleClose, children: "Cancel" }), (0, jsx_runtime_1.jsx)(antd_1.Button, { onClick: () => form.submit(), type: "primary", children: "Save" })] }), children: (0, jsx_runtime_1.jsxs)(antd_1.Form, { form: form, layout: "vertical", onFinish: handleSave, children: [(0, jsx_runtime_1.jsx)(antd_1.Form.Item, { name: "leaderUsername", label: "Leader (Orchestrator)", rules: [{ required: true, message: 'Please select a Leader' }], tooltip: "The AI Employee that will be able to delegate tasks to the Sub-Agent", children: (0, jsx_runtime_1.jsx)(AIEmployeeSelect_1.AIEmployeeSelect, { placeholder: "Select Leader AI Employee..." }) }), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { name: "subAgentUsername", label: "Sub-Agent", rules: [{ required: true, message: 'Please select a Sub-Agent' }], tooltip: "The AI Employee that will receive delegated tasks", children: (0, jsx_runtime_1.jsx)(AIEmployeeSelect_1.AIEmployeeSelect, { placeholder: "Select Sub-Agent AI Employee...", exclude: leaderUsername }) }), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { name: "maxDepth", label: "Max Delegation Depth", tooltip: "How many layers of delegation are allowed (1 = leader calls sub-agent, sub-agent cannot delegate further)", children: (0, jsx_runtime_1.jsx)(antd_1.InputNumber, { min: 1, max: 3, style: { width: '100%' } }) }), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { name: "timeout", label: "Timeout (ms)", tooltip: "Maximum time in milliseconds for the sub-agent to complete its task", children: (0, jsx_runtime_1.jsx)(antd_1.InputNumber, { min: 10000, max: 600000, step: 10000, style: { width: '100%' } }) }), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { name: "enabled", label: "Enabled", valuePropName: "checked", children: (0, jsx_runtime_1.jsx)(antd_1.Switch, {}) })] }) })] }));
|
|
143
|
+
};
|
|
144
|
+
exports.RulesTab = RulesTab;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TracingTab = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const antd_1 = require("antd");
|
|
7
|
+
const icons_1 = require("@ant-design/icons");
|
|
8
|
+
const client_1 = require("@nocobase/client");
|
|
9
|
+
const AIEmployeesContext_1 = require("./AIEmployeesContext");
|
|
10
|
+
const { Text, Paragraph } = antd_1.Typography;
|
|
11
|
+
/**
|
|
12
|
+
* Phase 5: Swarm Tracing page.
|
|
13
|
+
* Displays delegation execution logs from the orchestratorLogs collection.
|
|
14
|
+
*/
|
|
15
|
+
const TracingTab = () => {
|
|
16
|
+
const [selectedLog, setSelectedLog] = (0, react_1.useState)(null);
|
|
17
|
+
// Fetch delegation logs
|
|
18
|
+
const { data, loading, refresh } = (0, client_1.useRequest)({
|
|
19
|
+
url: 'orchestratorTracing:list',
|
|
20
|
+
params: {
|
|
21
|
+
sort: ['-createdAt'],
|
|
22
|
+
pageSize: 50,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
// P3 FIX: Use shared context instead of duplicate API call
|
|
26
|
+
const { employeeMap } = (0, AIEmployeesContext_1.useAIEmployees)();
|
|
27
|
+
const columns = [
|
|
28
|
+
{
|
|
29
|
+
title: 'Time',
|
|
30
|
+
dataIndex: 'createdAt',
|
|
31
|
+
key: 'createdAt',
|
|
32
|
+
width: 170,
|
|
33
|
+
render: (v) => v ? new Date(v).toLocaleString() : '-',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'Sub-Agent',
|
|
37
|
+
dataIndex: 'subAgentUsername',
|
|
38
|
+
key: 'subAgentUsername',
|
|
39
|
+
render: (username) => ((0, jsx_runtime_1.jsx)(antd_1.Tag, { color: "green", children: employeeMap.get(username) || username })),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
title: 'Task',
|
|
43
|
+
dataIndex: 'task',
|
|
44
|
+
key: 'task',
|
|
45
|
+
render: (task) => ((0, jsx_runtime_1.jsx)(Text, { ellipsis: true, style: { maxWidth: 280 }, children: task?.substring(0, 100) || '-' })),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
title: 'Status',
|
|
49
|
+
dataIndex: 'status',
|
|
50
|
+
key: 'status',
|
|
51
|
+
width: 90,
|
|
52
|
+
render: (status) => ((0, jsx_runtime_1.jsx)(antd_1.Tag, { icon: status === 'success' ? (0, jsx_runtime_1.jsx)(icons_1.CheckCircleOutlined, {}) : (0, jsx_runtime_1.jsx)(icons_1.CloseCircleOutlined, {}), color: status === 'success' ? 'success' : 'error', children: status })),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: 'Duration',
|
|
56
|
+
dataIndex: 'durationMs',
|
|
57
|
+
key: 'durationMs',
|
|
58
|
+
width: 90,
|
|
59
|
+
render: (ms) => {
|
|
60
|
+
if (!ms)
|
|
61
|
+
return '-';
|
|
62
|
+
return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: 'Depth',
|
|
67
|
+
dataIndex: 'depth',
|
|
68
|
+
key: 'depth',
|
|
69
|
+
width: 60,
|
|
70
|
+
render: (v) => v ?? 0,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: '',
|
|
74
|
+
key: 'actions',
|
|
75
|
+
width: 80,
|
|
76
|
+
render: (_, record) => ((0, jsx_runtime_1.jsx)(antd_1.Button, { type: "link", size: "small", icon: (0, jsx_runtime_1.jsx)(icons_1.EyeOutlined, {}), onClick: () => setSelectedLog(record), children: "Detail" })),
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "info", showIcon: true, style: { marginBottom: 16 }, message: "Swarm Tracing", description: (0, jsx_runtime_1.jsx)(Text, { type: "secondary", children: "View delegation execution logs. Each row represents one sub-agent invocation triggered by a Leader's tool call." }) }), (0, jsx_runtime_1.jsxs)(antd_1.Card, { bordered: false, children: [(0, jsx_runtime_1.jsx)("div", { style: { marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }, children: (0, jsx_runtime_1.jsx)(antd_1.Button, { onClick: refresh, children: "Refresh" }) }), (0, jsx_runtime_1.jsx)(antd_1.Table, { rowKey: "id", loading: loading, dataSource: data?.data || [], columns: columns, pagination: { hideOnSinglePage: true, pageSize: 20 }, size: "middle", locale: { emptyText: (0, jsx_runtime_1.jsx)(antd_1.Empty, { description: "No delegation executions yet" }) } })] }), (0, jsx_runtime_1.jsx)(antd_1.Drawer, { title: "Delegation Detail", width: 640, onClose: () => setSelectedLog(null), open: !!selectedLog, children: selectedLog && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(antd_1.Descriptions, { column: 1, bordered: true, size: "small", style: { marginBottom: 16 }, children: [(0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Status", children: (0, jsx_runtime_1.jsx)(antd_1.Tag, { icon: selectedLog.status === 'success' ? (0, jsx_runtime_1.jsx)(icons_1.CheckCircleOutlined, {}) : (0, jsx_runtime_1.jsx)(icons_1.CloseCircleOutlined, {}), color: selectedLog.status === 'success' ? 'success' : 'error', children: selectedLog.status }) }), (0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Sub-Agent", children: (0, jsx_runtime_1.jsx)(antd_1.Tag, { color: "green", children: employeeMap.get(selectedLog.subAgentUsername) || selectedLog.subAgentUsername }) }), (0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Tool", children: (0, jsx_runtime_1.jsx)(Text, { code: true, children: selectedLog.toolName }) }), (0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Depth", children: selectedLog.depth ?? 0 }), (0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Duration", children: selectedLog.durationMs
|
|
80
|
+
? selectedLog.durationMs >= 1000
|
|
81
|
+
? `${(selectedLog.durationMs / 1000).toFixed(1)}s`
|
|
82
|
+
: `${selectedLog.durationMs}ms`
|
|
83
|
+
: '-' }), (0, jsx_runtime_1.jsx)(antd_1.Descriptions.Item, { label: "Time", children: selectedLog.createdAt ? new Date(selectedLog.createdAt).toLocaleString() : '-' })] }), (0, jsx_runtime_1.jsx)(antd_1.Card, { title: "Task", size: "small", style: { marginBottom: 16 }, children: (0, jsx_runtime_1.jsx)(Paragraph, { style: { whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }, children: selectedLog.task || 'No task description' }) }), (0, jsx_runtime_1.jsx)(antd_1.Card, { title: "Result", size: "small", style: {
|
|
84
|
+
marginBottom: 16,
|
|
85
|
+
borderColor: selectedLog.status === 'success' ? '#b7eb8f' : '#ffa39e',
|
|
86
|
+
}, children: (0, jsx_runtime_1.jsx)(Paragraph, { style: { whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }, ellipsis: { rows: 20, expandable: true }, children: selectedLog.result || selectedLog.error || 'No result' }) }), selectedLog.error && ((0, jsx_runtime_1.jsx)(antd_1.Card, { title: "Error", size: "small", style: { borderColor: '#ffa39e' }, children: (0, jsx_runtime_1.jsx)(Paragraph, { type: "danger", style: { whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }, children: selectedLog.error }) }))] })) })] }));
|
|
87
|
+
};
|
|
88
|
+
exports.TracingTab = TracingTab;
|