diff --git a/deploy/tksea-portal/admin/logical-groups.html b/deploy/tksea-portal/admin/logical-groups.html
index d12b52ff..878c395a 100644
--- a/deploy/tksea-portal/admin/logical-groups.html
+++ b/deploy/tksea-portal/admin/logical-groups.html
@@ -531,6 +531,20 @@
description
+
+
+
+
+
@@ -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);
diff --git a/deploy/tksea-portal/index.html b/deploy/tksea-portal/index.html
index f17d80aa..d91783ea 100644
--- a/deploy/tksea-portal/index.html
+++ b/deploy/tksea-portal/index.html
@@ -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"
diff --git a/internal/app/logical_groups_api.go b/internal/app/logical_groups_api.go
index 457f777c..a7d22571 100644
--- a/internal/app/logical_groups_api.go
+++ b/internal/app/logical_groups_api.go
@@ -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,
diff --git a/internal/app/logical_groups_api_test.go b/internal/app/logical_groups_api_test.go
index a50ca63b..13063cff 100644
--- a/internal/app/logical_groups_api_test.go
+++ b/internal/app/logical_groups_api_test.go
@@ -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",
diff --git a/internal/app/portal_api.go b/internal/app/portal_api.go
index 08062f79..886eb69a 100644
--- a/internal/app/portal_api.go
+++ b/internal/app/portal_api.go
@@ -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,
diff --git a/internal/app/portal_api_test.go b/internal/app/portal_api_test.go
index 625ea3fc..a5886d40 100644
--- a/internal/app/portal_api_test.go
+++ b/internal/app/portal_api_test.go
@@ -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"])
+ }
}
diff --git a/internal/store/migrations/0013_logical_group_guidance.sql b/internal/store/migrations/0013_logical_group_guidance.sql
new file mode 100644
index 00000000..fe1abbeb
--- /dev/null
+++ b/internal/store/migrations/0013_logical_group_guidance.sql
@@ -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 '';
diff --git a/internal/store/sqlite/logical_groups_repo.go b/internal/store/sqlite/logical_groups_repo.go
index 15c85cb8..f30f950f 100644
--- a/internal/store/sqlite/logical_groups_repo.go
+++ b/internal/store/sqlite/logical_groups_repo.go
@@ -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)
diff --git a/internal/store/sqlite/logical_groups_repo_test.go b/internal/store/sqlite/logical_groups_repo_test.go
index be4cd5bb..8ca8a848 100644
--- a/internal/store/sqlite/logical_groups_repo_test.go
+++ b/internal/store/sqlite/logical_groups_repo_test.go
@@ -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)
diff --git a/scripts/test/test_tksea_portal_assets.sh b/scripts/test/test_tksea_portal_assets.sh
index cff6df84..3e70f8d4 100755
--- a/scripts/test/test_tksea_portal_assets.sh
+++ b/scripts/test/test_tksea_portal_assets.sh
@@ -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"