feat(portal): add logical group guidance config
This commit is contained in:
@@ -531,6 +531,20 @@
|
||||
description
|
||||
<textarea id="group-description" placeholder="例如:对用户只暴露一个 GPT Shared 分组,内部按 route 转到不同 shadow group"></textarea>
|
||||
</label>
|
||||
<label style="margin-top:12px;">
|
||||
usage_scenario
|
||||
<textarea id="group-usage-scenario" placeholder="例如:适合高质量推理、复杂编排、统一 GPT 产品入口。"></textarea>
|
||||
</label>
|
||||
<div class="field-grid two" style="margin-top:12px;">
|
||||
<label>
|
||||
recommendation
|
||||
<textarea id="group-recommendation" placeholder="例如:优先使用 gpt-5.4 做主模型。"></textarea>
|
||||
</label>
|
||||
<label>
|
||||
next_step_hint
|
||||
<textarea id="group-next-step-hint" placeholder="例如:先创建测试 Key,再按推荐模型发起第一次请求。"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="primary" id="create-group-btn" type="button">创建分组</button>
|
||||
<button class="secondary" id="update-group-btn" type="button">更新分组</button>
|
||||
@@ -691,6 +705,9 @@
|
||||
const groupDisplayNameInput = document.getElementById("group-display-name");
|
||||
const groupStatusInput = document.getElementById("group-status-input");
|
||||
const groupDescriptionInput = document.getElementById("group-description");
|
||||
const groupUsageScenarioInput = document.getElementById("group-usage-scenario");
|
||||
const groupRecommendationInput = document.getElementById("group-recommendation");
|
||||
const groupNextStepHintInput = document.getElementById("group-next-step-hint");
|
||||
const groupRoutePolicyInput = document.getElementById("group-route-policy");
|
||||
const groupStickyModeInput = document.getElementById("group-sticky-mode");
|
||||
const groupConversationTTLInput = document.getElementById("group-conversation-ttl");
|
||||
@@ -868,6 +885,9 @@
|
||||
display_name: groupDisplayNameInput.value.trim(),
|
||||
status: groupStatusInput.value,
|
||||
description: groupDescriptionInput.value.trim(),
|
||||
usage_scenario: groupUsageScenarioInput.value.trim(),
|
||||
recommendation: groupRecommendationInput.value.trim(),
|
||||
next_step_hint: groupNextStepHintInput.value.trim(),
|
||||
route_policy: groupRoutePolicyInput.value,
|
||||
sticky_mode: groupStickyModeInput.value,
|
||||
conversation_ttl_seconds: Number(groupConversationTTLInput.value || "0"),
|
||||
@@ -896,6 +916,9 @@
|
||||
groupDisplayNameInput.value = group?.display_name || "";
|
||||
groupStatusInput.value = group?.status || "active";
|
||||
groupDescriptionInput.value = group?.description || "";
|
||||
groupUsageScenarioInput.value = group?.usage_scenario || "";
|
||||
groupRecommendationInput.value = group?.recommendation || "";
|
||||
groupNextStepHintInput.value = group?.next_step_hint || "";
|
||||
groupRoutePolicyInput.value = group?.route_policy || "priority";
|
||||
groupStickyModeInput.value = group?.sticky_mode || "conversation_preferred";
|
||||
groupConversationTTLInput.value = String(group?.conversation_ttl_seconds || 7200);
|
||||
|
||||
@@ -823,7 +823,7 @@
|
||||
description: "适合 DeepSeek 官方 chat 路线需求。当前用户侧建议直接使用 deepseek-chat。"
|
||||
}
|
||||
};
|
||||
const MODEL_GUIDANCE = {
|
||||
const LEGACY_MODEL_GUIDANCE = {
|
||||
"kimi-k2.6": {
|
||||
scenario: "适合日常聊天、长上下文问答和轻量智能体使用。",
|
||||
recommendation: "默认先从这条模型线开始验证接入是否通畅。"
|
||||
@@ -1254,11 +1254,14 @@
|
||||
const group = row.logicalGroup;
|
||||
const models = portalLogicalGroupModels(group);
|
||||
const primaryModel = models[0] || "";
|
||||
const guidance = MODEL_GUIDANCE[primaryModel] || {
|
||||
const configuredScenario = String(group.usage_scenario || "").trim();
|
||||
const configuredRecommendation = String(group.recommendation || "").trim();
|
||||
const configuredNextStep = String(group.next_step_hint || "").trim();
|
||||
const guidance = LEGACY_MODEL_GUIDANCE[primaryModel] || {
|
||||
scenario: "适合按该逻辑分组下的公开模型集合统一接入。",
|
||||
recommendation: "建议先用列表中的第一个公开模型做连通性验证。"
|
||||
};
|
||||
const nextStep = row.stateKey === "active"
|
||||
const defaultNextStep = row.stateKey === "active"
|
||||
? "当前已具备订阅与权限,建议直接创建测试 Key 并使用推荐模型发起第一次请求。"
|
||||
: row.stateKey === "granted"
|
||||
? "当前已具备线路权限,但未发现活跃订阅;建议先确认订阅状态后再发起调用。"
|
||||
@@ -1274,9 +1277,9 @@
|
||||
models,
|
||||
stateText: row.stateText,
|
||||
stateKey: row.stateKey,
|
||||
scenario: guidance.scenario,
|
||||
recommendation: guidance.recommendation,
|
||||
nextStep,
|
||||
scenario: configuredScenario || guidance.scenario,
|
||||
recommendation: configuredRecommendation || guidance.recommendation,
|
||||
nextStep: configuredNextStep || defaultNextStep,
|
||||
compatibility,
|
||||
stickyMode: group.sticky_mode || "conversation_preferred",
|
||||
routePolicy: group.route_policy || "priority"
|
||||
|
||||
@@ -16,6 +16,9 @@ type CreateLogicalGroupRequest struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description,omitempty"`
|
||||
UsageScenario string `json:"usage_scenario,omitempty"`
|
||||
Recommendation string `json:"recommendation,omitempty"`
|
||||
NextStepHint string `json:"next_step_hint,omitempty"`
|
||||
RoutePolicy string `json:"route_policy,omitempty"`
|
||||
StickyMode string `json:"sticky_mode,omitempty"`
|
||||
ConversationTTLSeconds int `json:"conversation_ttl_seconds,omitempty"`
|
||||
@@ -29,6 +32,9 @@ type UpdateLogicalGroupRequest struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description,omitempty"`
|
||||
UsageScenario string `json:"usage_scenario,omitempty"`
|
||||
Recommendation string `json:"recommendation,omitempty"`
|
||||
NextStepHint string `json:"next_step_hint,omitempty"`
|
||||
RoutePolicy string `json:"route_policy,omitempty"`
|
||||
StickyMode string `json:"sticky_mode,omitempty"`
|
||||
ConversationTTLSeconds int `json:"conversation_ttl_seconds,omitempty"`
|
||||
@@ -42,6 +48,9 @@ type LogicalGroupInfo struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
Status string `json:"status"`
|
||||
Description string `json:"description,omitempty"`
|
||||
UsageScenario string `json:"usage_scenario,omitempty"`
|
||||
Recommendation string `json:"recommendation,omitempty"`
|
||||
NextStepHint string `json:"next_step_hint,omitempty"`
|
||||
RoutePolicy string `json:"route_policy,omitempty"`
|
||||
StickyMode string `json:"sticky_mode,omitempty"`
|
||||
ConversationTTLSeconds int `json:"conversation_ttl_seconds,omitempty"`
|
||||
@@ -433,6 +442,9 @@ func buildUpdateLogicalGroupAction(sqliteDSN string) func(context.Context, Updat
|
||||
DisplayName: req.DisplayName,
|
||||
Status: req.Status,
|
||||
Description: req.Description,
|
||||
UsageScenario: req.UsageScenario,
|
||||
Recommendation: req.Recommendation,
|
||||
NextStepHint: req.NextStepHint,
|
||||
RoutePolicy: req.RoutePolicy,
|
||||
StickyMode: req.StickyMode,
|
||||
ConversationTTLSeconds: req.ConversationTTLSeconds,
|
||||
@@ -683,6 +695,9 @@ func logicalGroupRequestToRow(req CreateLogicalGroupRequest) sqlite.LogicalGroup
|
||||
DisplayName: strings.TrimSpace(req.DisplayName),
|
||||
Status: strings.TrimSpace(req.Status),
|
||||
Description: strings.TrimSpace(req.Description),
|
||||
UsageScenario: strings.TrimSpace(req.UsageScenario),
|
||||
Recommendation: strings.TrimSpace(req.Recommendation),
|
||||
NextStepHint: strings.TrimSpace(req.NextStepHint),
|
||||
RoutePolicy: strings.TrimSpace(req.RoutePolicy),
|
||||
StickyMode: strings.TrimSpace(req.StickyMode),
|
||||
ConversationTTLSeconds: req.ConversationTTLSeconds,
|
||||
@@ -745,6 +760,9 @@ func logicalGroupRowToInfo(group sqlite.LogicalGroup, models []LogicalGroupModel
|
||||
DisplayName: group.DisplayName,
|
||||
Status: group.Status,
|
||||
Description: group.Description,
|
||||
UsageScenario: group.UsageScenario,
|
||||
Recommendation: group.Recommendation,
|
||||
NextStepHint: group.NextStepHint,
|
||||
RoutePolicy: group.RoutePolicy,
|
||||
StickyMode: group.StickyMode,
|
||||
ConversationTTLSeconds: group.ConversationTTLSeconds,
|
||||
|
||||
@@ -16,10 +16,14 @@ func TestAPICreateLogicalGroupReturnsCreated(t *testing.T) {
|
||||
if req.LogicalGroupID != "gpt-shared" {
|
||||
t.Fatalf("LogicalGroupID = %q, want gpt-shared", req.LogicalGroupID)
|
||||
}
|
||||
if req.UsageScenario != "适合统一 GPT 产品入口" {
|
||||
t.Fatalf("UsageScenario = %q, want configured guidance", req.UsageScenario)
|
||||
}
|
||||
return LogicalGroupInfo{
|
||||
LogicalGroupID: req.LogicalGroupID,
|
||||
DisplayName: req.DisplayName,
|
||||
Status: req.Status,
|
||||
UsageScenario: req.UsageScenario,
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
@@ -28,6 +32,7 @@ func TestAPICreateLogicalGroupReturnsCreated(t *testing.T) {
|
||||
"logical_group_id": "gpt-shared",
|
||||
"display_name": "GPT Shared",
|
||||
"status": "active",
|
||||
"usage_scenario": "适合统一 GPT 产品入口",
|
||||
}, "secret-token")
|
||||
response := httptestRecorder(handler, request)
|
||||
assertStatusCode(t, response, http.StatusCreated)
|
||||
@@ -44,6 +49,9 @@ func TestAPIGetLogicalGroupReturnsAggregatedItem(t *testing.T) {
|
||||
LogicalGroupID: groupID,
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
UsageScenario: "适合统一 GPT 产品入口",
|
||||
Recommendation: "优先使用 gpt-5.4",
|
||||
NextStepHint: "先创建测试 Key",
|
||||
Models: []LogicalGroupModelInfo{{PublicModel: "gpt-5.4", Status: "active"}},
|
||||
Routes: []LogicalGroupRouteInfo{{
|
||||
RouteID: "asxs",
|
||||
@@ -83,6 +91,9 @@ func TestAPIGetLogicalGroupReturnsAggregatedItem(t *testing.T) {
|
||||
if !ok || firstRoute["route_id"] != "asxs" {
|
||||
t.Fatalf("first route = %#v, want route_id asxs", routes[0])
|
||||
}
|
||||
if group["usage_scenario"] != "适合统一 GPT 产品入口" || group["recommendation"] != "优先使用 gpt-5.4" || group["next_step_hint"] != "先创建测试 Key" {
|
||||
t.Fatalf("group guidance = %#v, want configured guidance fields", group)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPICreateLogicalGroupRouteUsesPathGroupID(t *testing.T) {
|
||||
@@ -151,6 +162,9 @@ func TestNewActionSetLogicalGroupCRUDFlow(t *testing.T) {
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
UsageScenario: "适合统一 GPT 产品入口",
|
||||
Recommendation: "优先使用 gpt-5.4",
|
||||
NextStepHint: "先创建测试 Key",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateLogicalGroup() error = %v", err)
|
||||
@@ -158,6 +172,9 @@ func TestNewActionSetLogicalGroupCRUDFlow(t *testing.T) {
|
||||
if createdGroup.LogicalGroupID != "gpt-shared" {
|
||||
t.Fatalf("CreateLogicalGroup() = %+v, want logical_group_id gpt-shared", createdGroup)
|
||||
}
|
||||
if createdGroup.UsageScenario != "适合统一 GPT 产品入口" || createdGroup.Recommendation != "优先使用 gpt-5.4" || createdGroup.NextStepHint != "先创建测试 Key" {
|
||||
t.Fatalf("CreateLogicalGroup() guidance = %+v, want configured guidance fields", createdGroup)
|
||||
}
|
||||
|
||||
if _, err := actions.CreateLogicalGroupModel(ctx, CreateLogicalGroupModelRequest{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
@@ -199,11 +216,17 @@ func TestNewActionSetLogicalGroupCRUDFlow(t *testing.T) {
|
||||
if len(group.Routes[0].Models) != 1 || group.Routes[0].Models[0].ShadowModel != "gpt-5.4" {
|
||||
t.Fatalf("GetLogicalGroup().Routes[0].Models = %+v, want shadow gpt-5.4", group.Routes[0].Models)
|
||||
}
|
||||
if group.UsageScenario != "适合统一 GPT 产品入口" || group.Recommendation != "优先使用 gpt-5.4" || group.NextStepHint != "先创建测试 Key" {
|
||||
t.Fatalf("GetLogicalGroup() guidance = %+v, want configured guidance fields", group)
|
||||
}
|
||||
|
||||
if _, err := actions.UpdateLogicalGroup(ctx, UpdateLogicalGroupRequest{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared Updated",
|
||||
Status: "paused",
|
||||
UsageScenario: "适合升级后的 GPT 产品入口",
|
||||
Recommendation: "先验证高质量推理链路",
|
||||
NextStepHint: "升级后重新申请测试 Key",
|
||||
}); err != nil {
|
||||
t.Fatalf("UpdateLogicalGroup() error = %v", err)
|
||||
}
|
||||
@@ -228,6 +251,9 @@ func TestNewActionSetLogicalGroupCRUDFlow(t *testing.T) {
|
||||
if len(groups) != 1 || groups[0].DisplayName != "GPT Shared Updated" {
|
||||
t.Fatalf("ListLogicalGroups() = %+v, want updated group", groups)
|
||||
}
|
||||
if groups[0].UsageScenario != "适合升级后的 GPT 产品入口" || groups[0].Recommendation != "先验证高质量推理链路" || groups[0].NextStepHint != "升级后重新申请测试 Key" {
|
||||
t.Fatalf("ListLogicalGroups() guidance = %+v, want updated guidance fields", groups[0])
|
||||
}
|
||||
|
||||
routeModels, err := actions.ListLogicalGroupRouteModels(ctx, ListLogicalGroupRouteModelsRequest{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
|
||||
@@ -13,6 +13,9 @@ type PortalLogicalGroupInfo struct {
|
||||
LogicalGroupID string `json:"logical_group_id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
UsageScenario string `json:"usage_scenario,omitempty"`
|
||||
Recommendation string `json:"recommendation,omitempty"`
|
||||
NextStepHint string `json:"next_step_hint,omitempty"`
|
||||
Status string `json:"status"`
|
||||
StickyMode string `json:"sticky_mode,omitempty"`
|
||||
RoutePolicy string `json:"route_policy,omitempty"`
|
||||
@@ -159,6 +162,9 @@ func buildPortalLogicalGroupInfo(ctx context.Context, store *sqlite.DB, group sq
|
||||
LogicalGroupID: group.LogicalGroupID,
|
||||
DisplayName: group.DisplayName,
|
||||
Description: group.Description,
|
||||
UsageScenario: group.UsageScenario,
|
||||
Recommendation: group.Recommendation,
|
||||
NextStepHint: group.NextStepHint,
|
||||
Status: group.Status,
|
||||
StickyMode: group.StickyMode,
|
||||
RoutePolicy: group.RoutePolicy,
|
||||
|
||||
@@ -15,6 +15,9 @@ func TestAPIListPortalLogicalGroups(t *testing.T) {
|
||||
return []PortalLogicalGroupInfo{{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared",
|
||||
UsageScenario: "适合统一 GPT 产品入口",
|
||||
Recommendation: "优先使用 gpt-5.4",
|
||||
NextStepHint: "先创建测试 Key",
|
||||
Status: "active",
|
||||
RouteCount: 2,
|
||||
ActiveRouteCount: 1,
|
||||
@@ -37,6 +40,9 @@ func TestAPIListPortalLogicalGroups(t *testing.T) {
|
||||
if len(listPayload.LogicalGroups) != 1 || listPayload.LogicalGroups[0].LogicalGroupID != "gpt-shared" || listPayload.LogicalGroups[0].RouteCount != 2 {
|
||||
t.Fatalf("portal logical groups payload = %+v", listPayload)
|
||||
}
|
||||
if listPayload.LogicalGroups[0].UsageScenario != "适合统一 GPT 产品入口" || listPayload.LogicalGroups[0].Recommendation != "优先使用 gpt-5.4" || listPayload.LogicalGroups[0].NextStepHint != "先创建测试 Key" {
|
||||
t.Fatalf("portal logical groups guidance = %+v", listPayload.LogicalGroups[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGetPortalLogicalGroupModels(t *testing.T) {
|
||||
@@ -80,6 +86,9 @@ func TestNewActionSetPortalLogicalGroups(t *testing.T) {
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
Description: "Public GPT product",
|
||||
UsageScenario: "适合统一 GPT 产品入口",
|
||||
Recommendation: "优先使用 gpt-5.4",
|
||||
NextStepHint: "先创建测试 Key",
|
||||
RoutePolicy: "priority",
|
||||
StickyMode: "conversation_preferred",
|
||||
ConversationTTLSeconds: 7200,
|
||||
@@ -158,6 +167,9 @@ func TestNewActionSetPortalLogicalGroups(t *testing.T) {
|
||||
if group.DisplayName != "GPT Shared" || group.RoutePolicy != "priority" {
|
||||
t.Fatalf("GetPortalLogicalGroup() = %+v", group)
|
||||
}
|
||||
if group.UsageScenario != "适合统一 GPT 产品入口" || group.Recommendation != "优先使用 gpt-5.4" || group.NextStepHint != "先创建测试 Key" {
|
||||
t.Fatalf("GetPortalLogicalGroup() guidance = %+v", group)
|
||||
}
|
||||
|
||||
models, err := actions.ListPortalLogicalGroupModels(ctx, "gpt-shared")
|
||||
if err != nil {
|
||||
@@ -177,4 +189,7 @@ func TestNewActionSetPortalLogicalGroups(t *testing.T) {
|
||||
if payload["logical_group"].LogicalGroupID != "gpt-shared" {
|
||||
t.Fatalf("portal logical group payload = %+v", payload)
|
||||
}
|
||||
if payload["logical_group"].Recommendation != "优先使用 gpt-5.4" {
|
||||
t.Fatalf("portal logical group recommendation = %+v, want configured recommendation", payload["logical_group"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE logical_groups ADD COLUMN usage_scenario TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE logical_groups ADD COLUMN recommendation TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE logical_groups ADD COLUMN next_step_hint TEXT NOT NULL DEFAULT '';
|
||||
@@ -21,6 +21,9 @@ type LogicalGroup struct {
|
||||
DisplayName string
|
||||
Status string
|
||||
Description string
|
||||
UsageScenario string
|
||||
Recommendation string
|
||||
NextStepHint string
|
||||
RoutePolicy string
|
||||
StickyMode string
|
||||
ConversationTTLSeconds int
|
||||
@@ -52,17 +55,23 @@ func (r *LogicalGroupsRepo) Create(ctx context.Context, group LogicalGroup) (int
|
||||
display_name,
|
||||
status,
|
||||
description,
|
||||
usage_scenario,
|
||||
recommendation,
|
||||
next_step_hint,
|
||||
route_policy,
|
||||
sticky_mode,
|
||||
conversation_ttl_seconds,
|
||||
user_model_ttl_seconds,
|
||||
failover_threshold,
|
||||
cooldown_seconds
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
group.LogicalGroupID,
|
||||
group.DisplayName,
|
||||
group.Status,
|
||||
group.Description,
|
||||
group.UsageScenario,
|
||||
group.Recommendation,
|
||||
group.NextStepHint,
|
||||
group.RoutePolicy,
|
||||
group.StickyMode,
|
||||
group.ConversationTTLSeconds,
|
||||
@@ -90,7 +99,7 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
var group LogicalGroup
|
||||
if err := r.db.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT id, logical_group_id, display_name, status, description, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
FROM logical_groups
|
||||
WHERE logical_group_id = ?`,
|
||||
logicalGroupID,
|
||||
@@ -100,6 +109,9 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
&group.DisplayName,
|
||||
&group.Status,
|
||||
&group.Description,
|
||||
&group.UsageScenario,
|
||||
&group.Recommendation,
|
||||
&group.NextStepHint,
|
||||
&group.RoutePolicy,
|
||||
&group.StickyMode,
|
||||
&group.ConversationTTLSeconds,
|
||||
@@ -117,7 +129,7 @@ func (r *LogicalGroupsRepo) GetByLogicalGroupID(ctx context.Context, logicalGrou
|
||||
func (r *LogicalGroupsRepo) List(ctx context.Context) ([]LogicalGroup, error) {
|
||||
rows, err := r.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT id, logical_group_id, display_name, status, description, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
`SELECT id, logical_group_id, display_name, status, description, usage_scenario, recommendation, next_step_hint, route_policy, sticky_mode, conversation_ttl_seconds, user_model_ttl_seconds, failover_threshold, cooldown_seconds, created_at, updated_at
|
||||
FROM logical_groups
|
||||
ORDER BY id ASC`,
|
||||
)
|
||||
@@ -135,6 +147,9 @@ func (r *LogicalGroupsRepo) List(ctx context.Context) ([]LogicalGroup, error) {
|
||||
&group.DisplayName,
|
||||
&group.Status,
|
||||
&group.Description,
|
||||
&group.UsageScenario,
|
||||
&group.Recommendation,
|
||||
&group.NextStepHint,
|
||||
&group.RoutePolicy,
|
||||
&group.StickyMode,
|
||||
&group.ConversationTTLSeconds,
|
||||
@@ -163,11 +178,14 @@ func (r *LogicalGroupsRepo) UpdateByLogicalGroupID(ctx context.Context, group Lo
|
||||
result, err := r.db.ExecContext(
|
||||
ctx,
|
||||
`UPDATE logical_groups
|
||||
SET display_name = ?, status = ?, description = ?, route_policy = ?, sticky_mode = ?, conversation_ttl_seconds = ?, user_model_ttl_seconds = ?, failover_threshold = ?, cooldown_seconds = ?, updated_at = CURRENT_TIMESTAMP
|
||||
SET display_name = ?, status = ?, description = ?, usage_scenario = ?, recommendation = ?, next_step_hint = ?, route_policy = ?, sticky_mode = ?, conversation_ttl_seconds = ?, user_model_ttl_seconds = ?, failover_threshold = ?, cooldown_seconds = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE logical_group_id = ?`,
|
||||
group.DisplayName,
|
||||
group.Status,
|
||||
group.Description,
|
||||
group.UsageScenario,
|
||||
group.Recommendation,
|
||||
group.NextStepHint,
|
||||
group.RoutePolicy,
|
||||
group.StickyMode,
|
||||
group.ConversationTTLSeconds,
|
||||
@@ -214,6 +232,9 @@ func normalizeLogicalGroup(group LogicalGroup) (LogicalGroup, error) {
|
||||
group.DisplayName = strings.TrimSpace(group.DisplayName)
|
||||
group.Status = strings.TrimSpace(group.Status)
|
||||
group.Description = strings.TrimSpace(group.Description)
|
||||
group.UsageScenario = strings.TrimSpace(group.UsageScenario)
|
||||
group.Recommendation = strings.TrimSpace(group.Recommendation)
|
||||
group.NextStepHint = strings.TrimSpace(group.NextStepHint)
|
||||
group.RoutePolicy = strings.TrimSpace(group.RoutePolicy)
|
||||
group.StickyMode = strings.TrimSpace(group.StickyMode)
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
DisplayName: "GPT Shared",
|
||||
Status: "active",
|
||||
Description: "shared group",
|
||||
UsageScenario: "适合统一 GPT 产品入口。",
|
||||
Recommendation: "优先使用 gpt-5.4。",
|
||||
NextStepHint: "先创建测试 Key。",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
@@ -34,12 +37,18 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
if group.StickyMode != defaultLogicalGroupStickyMode {
|
||||
t.Fatalf("StickyMode = %q, want %q", group.StickyMode, defaultLogicalGroupStickyMode)
|
||||
}
|
||||
if group.UsageScenario != "适合统一 GPT 产品入口。" || group.Recommendation != "优先使用 gpt-5.4。" || group.NextStepHint != "先创建测试 Key。" {
|
||||
t.Fatalf("guidance fields = %+v, want persisted guidance", group)
|
||||
}
|
||||
|
||||
if err := store.LogicalGroups().UpdateByLogicalGroupID(ctx, LogicalGroup{
|
||||
LogicalGroupID: "gpt-shared",
|
||||
DisplayName: "GPT Shared Updated",
|
||||
Status: "paused",
|
||||
Description: "updated",
|
||||
UsageScenario: "适合更新后的产品入口。",
|
||||
Recommendation: "优先做连通性验证。",
|
||||
NextStepHint: "先确认订阅再调用。",
|
||||
RoutePolicy: "priority",
|
||||
StickyMode: "user_preferred",
|
||||
ConversationTTLSeconds: 3600,
|
||||
@@ -57,6 +66,9 @@ func TestLogicalGroupsRepoCreateGetUpdateDelete(t *testing.T) {
|
||||
if updated.DisplayName != "GPT Shared Updated" || updated.Status != "paused" {
|
||||
t.Fatalf("updated group = %+v, want updated fields", updated)
|
||||
}
|
||||
if updated.UsageScenario != "适合更新后的产品入口。" || updated.Recommendation != "优先做连通性验证。" || updated.NextStepHint != "先确认订阅再调用。" {
|
||||
t.Fatalf("updated guidance = %+v, want updated guidance fields", updated)
|
||||
}
|
||||
|
||||
if err := store.LogicalGroups().DeleteByLogicalGroupID(ctx, "gpt-shared"); err != nil {
|
||||
t.Fatalf("DeleteByLogicalGroupID() error = %v", err)
|
||||
|
||||
@@ -63,13 +63,16 @@ assert_contains_file "$HTML_FILE" "LEGACY_GROUP_CATALOG"
|
||||
assert_contains_file "$HTML_FILE" "逻辑分组权限"
|
||||
assert_contains_file "$HTML_FILE" "renderEntitlementView"
|
||||
assert_contains_file "$HTML_FILE" "使用建议与可用模型说明"
|
||||
assert_contains_file "$HTML_FILE" "MODEL_GUIDANCE"
|
||||
assert_contains_file "$HTML_FILE" "LEGACY_MODEL_GUIDANCE"
|
||||
assert_contains_file "$HTML_FILE" "buildGuideEntries"
|
||||
assert_contains_file "$HTML_FILE" "renderUsageGuides"
|
||||
assert_contains_file "$HTML_FILE" "推荐模型"
|
||||
assert_contains_file "$HTML_FILE" "接入建议"
|
||||
assert_contains_file "$HTML_FILE" "下一步"
|
||||
assert_contains_file "$HTML_FILE" "路由策略"
|
||||
assert_contains_file "$HTML_FILE" "usage_scenario"
|
||||
assert_contains_file "$HTML_FILE" "recommendation"
|
||||
assert_contains_file "$HTML_FILE" "next_step_hint"
|
||||
assert_contains_file "$HTML_FILE" "已开通订阅"
|
||||
assert_contains_file "$HTML_FILE" "已授予权限"
|
||||
assert_contains_file "$HTML_FILE" "归属待整理"
|
||||
@@ -122,6 +125,9 @@ assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "/api/logical-groups"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "logical_group"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "shadow_group_id"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "shadow_host_id"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "usage_scenario"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "recommendation"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "next_step_hint"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "首版页面只覆盖新增与查看"
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" 'credentials: "include"'
|
||||
assert_contains_file "$ADMIN_LOGICAL_GROUPS_FILE" "/portal-admin-api"
|
||||
|
||||
Reference in New Issue
Block a user