mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-15 08:46:58 +08:00
feat(dashboard): 在交易者详情页显示系统提示词模板名称 (#775)
* feat(dashboard): display system prompt template and extract color constant * style(api): format server.go with go fmt
This commit is contained in:
committed by
tangmengqiu
parent
ced6c3d9de
commit
217ccb08dd
@@ -1277,12 +1277,13 @@ func (s *Server) handleTraderList(c *gin.Context) {
|
|||||||
// 返回完整的 AIModelID(如 "admin_deepseek"),不要截断
|
// 返回完整的 AIModelID(如 "admin_deepseek"),不要截断
|
||||||
// 前端需要完整 ID 来验证模型是否存在(与 handleGetTraderConfig 保持一致)
|
// 前端需要完整 ID 来验证模型是否存在(与 handleGetTraderConfig 保持一致)
|
||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"trader_id": trader.ID,
|
"trader_id": trader.ID,
|
||||||
"trader_name": trader.Name,
|
"trader_name": trader.Name,
|
||||||
"ai_model": trader.AIModelID, // 使用完整 ID
|
"ai_model": trader.AIModelID, // 使用完整 ID
|
||||||
"exchange_id": trader.ExchangeID,
|
"exchange_id": trader.ExchangeID,
|
||||||
"is_running": isRunning,
|
"is_running": isRunning,
|
||||||
"initial_balance": trader.InitialBalance,
|
"initial_balance": trader.InitialBalance,
|
||||||
|
"system_prompt_template": trader.SystemPromptTemplate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2170,16 +2171,17 @@ func (s *Server) handlePublicTraderList(c *gin.Context) {
|
|||||||
result := make([]map[string]interface{}, 0, len(traders))
|
result := make([]map[string]interface{}, 0, len(traders))
|
||||||
for _, trader := range traders {
|
for _, trader := range traders {
|
||||||
result = append(result, map[string]interface{}{
|
result = append(result, map[string]interface{}{
|
||||||
"trader_id": trader["trader_id"],
|
"trader_id": trader["trader_id"],
|
||||||
"trader_name": trader["trader_name"],
|
"trader_name": trader["trader_name"],
|
||||||
"ai_model": trader["ai_model"],
|
"ai_model": trader["ai_model"],
|
||||||
"exchange": trader["exchange"],
|
"exchange": trader["exchange"],
|
||||||
"is_running": trader["is_running"],
|
"is_running": trader["is_running"],
|
||||||
"total_equity": trader["total_equity"],
|
"total_equity": trader["total_equity"],
|
||||||
"total_pnl": trader["total_pnl"],
|
"total_pnl": trader["total_pnl"],
|
||||||
"total_pnl_pct": trader["total_pnl_pct"],
|
"total_pnl_pct": trader["total_pnl_pct"],
|
||||||
"position_count": trader["position_count"],
|
"position_count": trader["position_count"],
|
||||||
"margin_used_pct": trader["margin_used_pct"],
|
"margin_used_pct": trader["margin_used_pct"],
|
||||||
|
"system_prompt_template": trader["system_prompt_template"],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -225,3 +225,81 @@ func TestUpdateTraderRequest_CompleteFields(t *testing.T) {
|
|||||||
t.Errorf("SystemPromptTemplate mismatch: expected %q, got %q", "nof1", req.SystemPromptTemplate)
|
t.Errorf("SystemPromptTemplate mismatch: expected %q, got %q", "nof1", req.SystemPromptTemplate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTraderListResponse_SystemPromptTemplate 测试 handleTraderList API 返回的 trader 对象是否包含 system_prompt_template 字段
|
||||||
|
func TestTraderListResponse_SystemPromptTemplate(t *testing.T) {
|
||||||
|
// 模拟 handleTraderList 中的 trader 对象构造
|
||||||
|
trader := &config.TraderRecord{
|
||||||
|
ID: "trader-001",
|
||||||
|
UserID: "user-1",
|
||||||
|
Name: "My Trader",
|
||||||
|
AIModelID: "gpt-4",
|
||||||
|
ExchangeID: "binance",
|
||||||
|
InitialBalance: 5000,
|
||||||
|
SystemPromptTemplate: "nof1",
|
||||||
|
IsRunning: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造 API 响应对象(与 api/server.go 中的逻辑一致)
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"trader_id": trader.ID,
|
||||||
|
"trader_name": trader.Name,
|
||||||
|
"ai_model": trader.AIModelID,
|
||||||
|
"exchange_id": trader.ExchangeID,
|
||||||
|
"is_running": trader.IsRunning,
|
||||||
|
"initial_balance": trader.InitialBalance,
|
||||||
|
"system_prompt_template": trader.SystemPromptTemplate,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 验证 system_prompt_template 字段存在
|
||||||
|
if _, exists := response["system_prompt_template"]; !exists {
|
||||||
|
t.Errorf("Trader list response is missing 'system_prompt_template' field")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 验证 system_prompt_template 值正确
|
||||||
|
if response["system_prompt_template"] != "nof1" {
|
||||||
|
t.Errorf("Expected system_prompt_template='nof1', got %v", response["system_prompt_template"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPublicTraderListResponse_SystemPromptTemplate 测试 handlePublicTraderList API 返回的 trader 对象是否包含 system_prompt_template 字段
|
||||||
|
func TestPublicTraderListResponse_SystemPromptTemplate(t *testing.T) {
|
||||||
|
// 模拟 getConcurrentTraderData 返回的 trader 数据
|
||||||
|
traderData := map[string]interface{}{
|
||||||
|
"trader_id": "trader-002",
|
||||||
|
"trader_name": "Public Trader",
|
||||||
|
"ai_model": "claude",
|
||||||
|
"exchange": "binance",
|
||||||
|
"total_equity": 10000.0,
|
||||||
|
"total_pnl": 500.0,
|
||||||
|
"total_pnl_pct": 5.0,
|
||||||
|
"position_count": 3,
|
||||||
|
"margin_used_pct": 25.0,
|
||||||
|
"is_running": true,
|
||||||
|
"system_prompt_template": "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造 API 响应对象(与 api/server.go handlePublicTraderList 中的逻辑一致)
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"trader_id": traderData["trader_id"],
|
||||||
|
"trader_name": traderData["trader_name"],
|
||||||
|
"ai_model": traderData["ai_model"],
|
||||||
|
"exchange": traderData["exchange"],
|
||||||
|
"total_equity": traderData["total_equity"],
|
||||||
|
"total_pnl": traderData["total_pnl"],
|
||||||
|
"total_pnl_pct": traderData["total_pnl_pct"],
|
||||||
|
"position_count": traderData["position_count"],
|
||||||
|
"margin_used_pct": traderData["margin_used_pct"],
|
||||||
|
"system_prompt_template": traderData["system_prompt_template"],
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 验证 system_prompt_template 字段存在
|
||||||
|
if _, exists := response["system_prompt_template"]; !exists {
|
||||||
|
t.Errorf("Public trader list response is missing 'system_prompt_template' field")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 验证 system_prompt_template 值正确
|
||||||
|
if response["system_prompt_template"] != "default" {
|
||||||
|
t.Errorf("Expected system_prompt_template='default', got %v", response["system_prompt_template"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -590,48 +590,51 @@ func (tm *TraderManager) getConcurrentTraderData(traders []*trader.AutoTrader) [
|
|||||||
case account := <-accountChan:
|
case account := <-accountChan:
|
||||||
// 成功获取账户信息
|
// 成功获取账户信息
|
||||||
traderData = map[string]interface{}{
|
traderData = map[string]interface{}{
|
||||||
"trader_id": trader.GetID(),
|
"trader_id": trader.GetID(),
|
||||||
"trader_name": trader.GetName(),
|
"trader_name": trader.GetName(),
|
||||||
"ai_model": trader.GetAIModel(),
|
"ai_model": trader.GetAIModel(),
|
||||||
"exchange": trader.GetExchange(),
|
"exchange": trader.GetExchange(),
|
||||||
"total_equity": account["total_equity"],
|
"total_equity": account["total_equity"],
|
||||||
"total_pnl": account["total_pnl"],
|
"total_pnl": account["total_pnl"],
|
||||||
"total_pnl_pct": account["total_pnl_pct"],
|
"total_pnl_pct": account["total_pnl_pct"],
|
||||||
"position_count": account["position_count"],
|
"position_count": account["position_count"],
|
||||||
"margin_used_pct": account["margin_used_pct"],
|
"margin_used_pct": account["margin_used_pct"],
|
||||||
"is_running": status["is_running"],
|
"is_running": status["is_running"],
|
||||||
|
"system_prompt_template": trader.GetSystemPromptTemplate(),
|
||||||
}
|
}
|
||||||
case err := <-errorChan:
|
case err := <-errorChan:
|
||||||
// 获取账户信息失败
|
// 获取账户信息失败
|
||||||
log.Printf("⚠️ 获取交易员 %s 账户信息失败: %v", trader.GetID(), err)
|
log.Printf("⚠️ 获取交易员 %s 账户信息失败: %v", trader.GetID(), err)
|
||||||
traderData = map[string]interface{}{
|
traderData = map[string]interface{}{
|
||||||
"trader_id": trader.GetID(),
|
"trader_id": trader.GetID(),
|
||||||
"trader_name": trader.GetName(),
|
"trader_name": trader.GetName(),
|
||||||
"ai_model": trader.GetAIModel(),
|
"ai_model": trader.GetAIModel(),
|
||||||
"exchange": trader.GetExchange(),
|
"exchange": trader.GetExchange(),
|
||||||
"total_equity": 0.0,
|
"total_equity": 0.0,
|
||||||
"total_pnl": 0.0,
|
"total_pnl": 0.0,
|
||||||
"total_pnl_pct": 0.0,
|
"total_pnl_pct": 0.0,
|
||||||
"position_count": 0,
|
"position_count": 0,
|
||||||
"margin_used_pct": 0.0,
|
"margin_used_pct": 0.0,
|
||||||
"is_running": status["is_running"],
|
"is_running": status["is_running"],
|
||||||
"error": "账户数据获取失败",
|
"system_prompt_template": trader.GetSystemPromptTemplate(),
|
||||||
|
"error": "账户数据获取失败",
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// 超时
|
// 超时
|
||||||
log.Printf("⏰ 获取交易员 %s 账户信息超时", trader.GetID())
|
log.Printf("⏰ 获取交易员 %s 账户信息超时", trader.GetID())
|
||||||
traderData = map[string]interface{}{
|
traderData = map[string]interface{}{
|
||||||
"trader_id": trader.GetID(),
|
"trader_id": trader.GetID(),
|
||||||
"trader_name": trader.GetName(),
|
"trader_name": trader.GetName(),
|
||||||
"ai_model": trader.GetAIModel(),
|
"ai_model": trader.GetAIModel(),
|
||||||
"exchange": trader.GetExchange(),
|
"exchange": trader.GetExchange(),
|
||||||
"total_equity": 0.0,
|
"total_equity": 0.0,
|
||||||
"total_pnl": 0.0,
|
"total_pnl": 0.0,
|
||||||
"total_pnl_pct": 0.0,
|
"total_pnl_pct": 0.0,
|
||||||
"position_count": 0,
|
"position_count": 0,
|
||||||
"margin_used_pct": 0.0,
|
"margin_used_pct": 0.0,
|
||||||
"is_running": status["is_running"],
|
"is_running": status["is_running"],
|
||||||
"error": "获取超时",
|
"system_prompt_template": trader.GetSystemPromptTemplate(),
|
||||||
|
"error": "获取超时",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,6 +282,8 @@ export default function TraderDashboard() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const highlightColor = '#60a5fa'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{/* Trader Header */}
|
{/* Trader Header */}
|
||||||
@@ -346,7 +348,7 @@ export default function TraderDashboard() {
|
|||||||
style={{
|
style={{
|
||||||
color: selectedTrader.ai_model.includes('qwen')
|
color: selectedTrader.ai_model.includes('qwen')
|
||||||
? '#c084fc'
|
? '#c084fc'
|
||||||
: '#60a5fa',
|
: highlightColor,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{getModelDisplayName(
|
{getModelDisplayName(
|
||||||
@@ -355,6 +357,10 @@ export default function TraderDashboard() {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>
|
||||||
|
Prompt: <span className="font-semibold" style={{ color: highlightColor }}>{selectedTrader.system_prompt_template || '-'}</span>
|
||||||
|
</span>
|
||||||
{status && (
|
{status && (
|
||||||
<>
|
<>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export interface TraderInfo {
|
|||||||
custom_prompt?: string
|
custom_prompt?: string
|
||||||
use_coin_pool?: boolean
|
use_coin_pool?: boolean
|
||||||
use_oi_top?: boolean
|
use_oi_top?: boolean
|
||||||
|
system_prompt_template?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AIModel {
|
export interface AIModel {
|
||||||
|
|||||||
Reference in New Issue
Block a user