From 85eab1806c355a6de55c2d4f61a3b0259990c485 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 3 Apr 2026 19:45:37 +0800 Subject: [PATCH] Clean up auto routing gateway terminology --- docs/cases/README.md | 1 + go/go_core/internal/acp/routing_test.go | 8 +++--- go/go_core/internal/memory/provider.go | 13 ++++++++- go/go_core/internal/memory/provider_test.go | 2 +- go/go_core/internal/router/router.go | 22 ++++++++++----- go/go_core/internal/router/router_test.go | 4 +-- lib/runtime/go_agent_core_client.dart | 24 ++++++++++++++--- .../go_agent_core_desktop_transport.dart | 9 +++++-- lib/web/go_agent_core_web_transport.dart | 9 +++++-- ...controller_ai_gateway_chat_suite_chat.dart | 2 +- test/runtime/go_agent_core_client_suite.dart | 27 +++++++++++++++++++ 11 files changed, 99 insertions(+), 22 deletions(-) diff --git a/docs/cases/README.md b/docs/cases/README.md index 17ccb576..d85f9d7b 100644 --- a/docs/cases/README.md +++ b/docs/cases/README.md @@ -16,6 +16,7 @@ 2. [ARIS 缺陷修复与审阅循环](/Users/shenlan/workspaces/cloud-neutral-toolkit/XWorkmate.svc.plus/docs/cases/aris_bugfix_review_loop.md) 3. [外部 Agent CLI Bridge 会话](/Users/shenlan/workspaces/cloud-neutral-toolkit/XWorkmate.svc.plus/docs/cases/external_agent_bridge_session.md) 4. [模式切换与线程连续追问](/Users/shenlan/workspaces/cloud-neutral-toolkit/XWorkmate.svc.plus/docs/cases/thread_mode_switch_followup.md) +5. [Intent Router + Skill Resolver + Memory Injector 典型用例](/Users/shenlan/workspaces/cloud-neutral-toolkit/xworkmate/docs/cases/intent_router_skill_memory_typical_cases.md) ## 相关设计文档 diff --git a/go/go_core/internal/acp/routing_test.go b/go/go_core/internal/acp/routing_test.go index 5273b068..c0c74fb3 100644 --- a/go/go_core/internal/acp/routing_test.go +++ b/go/go_core/internal/acp/routing_test.go @@ -65,28 +65,28 @@ func TestHandleRoutingResolveCoversNineScenarioBuckets(t *testing.T) { { name: "image-cog", prompt: "use image-cog to generate consistent characters", - expectedExecutionTarget: "gateway-chat", + expectedExecutionTarget: "gateway", expectedSkillSource: "find_skills", expectedNeedsSkillInstall: true, }, { name: "image-video-generation-editting", prompt: "wan 图生视频并做视频编辑", - expectedExecutionTarget: "gateway-chat", + expectedExecutionTarget: "gateway", expectedSkillSource: "find_skills", expectedNeedsSkillInstall: true, }, { name: "video-translator", prompt: "translate video subtitles and dub the clip", - expectedExecutionTarget: "gateway-chat", + expectedExecutionTarget: "gateway", expectedSkillSource: "find_skills", expectedNeedsSkillInstall: true, }, { name: "browser-search-news", prompt: "跨浏览器执行并搜索最新资讯采集结果", - expectedExecutionTarget: "gateway-chat", + expectedExecutionTarget: "gateway", expectedSkillSource: "local_match", expectedResolvedSkill: "Browser Automation", }, diff --git a/go/go_core/internal/memory/provider.go b/go/go_core/internal/memory/provider.go index e6f61538..252afdfc 100644 --- a/go/go_core/internal/memory/provider.go +++ b/go/go_core/internal/memory/provider.go @@ -149,7 +149,9 @@ func parsePreferences(text string) Preferences { trimmed := strings.TrimSpace(line) switch { case strings.HasPrefix(strings.ToLower(trimmed), "preferred-route:"): - prefs.PreferredRoute = strings.TrimSpace(strings.TrimPrefix(trimmed, "preferred-route:")) + prefs.PreferredRoute = normalizePreferredRoute( + strings.TrimSpace(strings.TrimPrefix(trimmed, "preferred-route:")), + ) case strings.HasPrefix(strings.ToLower(trimmed), "preferred-model:"): prefs.PreferredModel = strings.TrimSpace(strings.TrimPrefix(trimmed, "preferred-model:")) case strings.HasPrefix(strings.ToLower(trimmed), "preferred-skills:"): @@ -205,3 +207,12 @@ func projectNameFromWorkingDirectory(workingDirectory string) string { } return strings.TrimSpace(filepath.Base(cleaned)) } + +func normalizePreferredRoute(value string) string { + switch strings.ToLower(strings.TrimSpace(value)) { + case "gateway-chat": + return "gateway" + default: + return strings.TrimSpace(value) + } +} diff --git a/go/go_core/internal/memory/provider_test.go b/go/go_core/internal/memory/provider_test.go index fef9aa2e..49ae4af1 100644 --- a/go/go_core/internal/memory/provider_test.go +++ b/go/go_core/internal/memory/provider_test.go @@ -35,7 +35,7 @@ func TestLoadMergesGlobalAndProjectMemoryAndSanitizesSecrets(t *testing.T) { if strings.Contains(strings.ToLower(result.MergedText), "api_key") || strings.Contains(strings.ToLower(result.MergedText), "password") { t.Fatalf("expected sanitized merged text, got %q", result.MergedText) } - if result.Preferences.PreferredRoute != "gateway-chat" { + if result.Preferences.PreferredRoute != "gateway" { t.Fatalf("unexpected preferred route: %#v", result.Preferences) } if result.Preferences.PreferredModel != "gpt-5.4" { diff --git a/go/go_core/internal/router/router.go b/go/go_core/internal/router/router.go index cf038c29..53f562d0 100644 --- a/go/go_core/internal/router/router.go +++ b/go/go_core/internal/router/router.go @@ -14,6 +14,7 @@ const ( ExecutionTargetSingleAgent = "single-agent" ExecutionTargetMultiAgent = "multi-agent" + ExecutionTargetGateway = "gateway" ExecutionTargetGatewayChat = "gateway-chat" EndpointTargetSingleAgent = "singleAgent" @@ -114,15 +115,15 @@ func resolveExecution(req Request, prefs memory.Preferences) (string, string) { return ExecutionTargetMultiAgent, EndpointTargetSingleAgent } if looksOnline(prompt) { - return ExecutionTargetGatewayChat, normalizeGatewayTarget(req.PreferredGatewayTarget) + return ExecutionTargetGateway, normalizeGatewayTarget(req.PreferredGatewayTarget) } if looksLocal(prompt) { return ExecutionTargetSingleAgent, EndpointTargetSingleAgent } - switch strings.TrimSpace(prefs.PreferredRoute) { - case ExecutionTargetGatewayChat: - return ExecutionTargetGatewayChat, normalizeGatewayTarget(req.PreferredGatewayTarget) + switch normalizeExecutionTarget(strings.TrimSpace(prefs.PreferredRoute)) { + case ExecutionTargetGateway: + return ExecutionTargetGateway, normalizeGatewayTarget(req.PreferredGatewayTarget) case ExecutionTargetMultiAgent: return ExecutionTargetMultiAgent, EndpointTargetSingleAgent } @@ -132,9 +133,9 @@ func resolveExecution(req Request, prefs memory.Preferences) (string, string) { func mapExplicitTarget(value string) (string, string) { switch strings.TrimSpace(value) { case EndpointTargetLocal: - return ExecutionTargetGatewayChat, EndpointTargetLocal + return ExecutionTargetGateway, EndpointTargetLocal case EndpointTargetRemote: - return ExecutionTargetGatewayChat, EndpointTargetRemote + return ExecutionTargetGateway, EndpointTargetRemote case "multiAgent", ExecutionTargetMultiAgent: return ExecutionTargetMultiAgent, EndpointTargetSingleAgent case EndpointTargetSingleAgent, ExecutionTargetSingleAgent: @@ -187,3 +188,12 @@ func containsAny(haystack string, needles []string) bool { func normalize(value string) string { return strings.ToLower(strings.TrimSpace(value)) } + +func normalizeExecutionTarget(value string) string { + switch normalize(value) { + case ExecutionTargetGatewayChat: + return ExecutionTargetGateway + default: + return normalize(value) + } +} diff --git a/go/go_core/internal/router/router_test.go b/go/go_core/internal/router/router_test.go index ebe9e041..2d120b13 100644 --- a/go/go_core/internal/router/router_test.go +++ b/go/go_core/internal/router/router_test.go @@ -58,8 +58,8 @@ func TestResolveAutoOnlineTaskToGateway(t *testing.T) { PreferredGatewayTarget: EndpointTargetLocal, }) - if result.ResolvedExecutionTarget != ExecutionTargetGatewayChat { - t.Fatalf("expected gateway-chat route, got %#v", result) + if result.ResolvedExecutionTarget != ExecutionTargetGateway { + t.Fatalf("expected gateway route, got %#v", result) } if result.ResolvedEndpointTarget != EndpointTargetLocal { t.Fatalf("expected local gateway target, got %#v", result) diff --git a/lib/runtime/go_agent_core_client.dart b/lib/runtime/go_agent_core_client.dart index 47b59ca0..57154ef2 100644 --- a/lib/runtime/go_agent_core_client.dart +++ b/lib/runtime/go_agent_core_client.dart @@ -148,8 +148,19 @@ class GoAgentCoreSessionRequest { } return switch (target) { AssistantExecutionTarget.singleAgent => 'single-agent', - AssistantExecutionTarget.local => 'gateway-chat', - AssistantExecutionTarget.remote => 'gateway-chat', + AssistantExecutionTarget.local => _gatewaySessionMode, + AssistantExecutionTarget.remote => _gatewaySessionMode, + }; + } + + String get routingExecutionTarget { + if (multiAgent) { + return 'multi-agent'; + } + return switch (target) { + AssistantExecutionTarget.singleAgent => 'single-agent', + AssistantExecutionTarget.local => 'gateway', + AssistantExecutionTarget.remote => 'gateway', }; } @@ -198,7 +209,7 @@ class GoAgentCoreSessionRequest { if (aiGatewayApiKey.trim().isNotEmpty) 'aiGatewayApiKey': aiGatewayApiKey.trim(), if (routing != null) 'routing': routing!.toJson(), - if (mode == 'gateway-chat') ...{ + if (_usesGatewaySessionMode(mode)) ...{ 'executionTarget': target.promptValue, if (agentId.trim().isNotEmpty) 'agentId': agentId.trim(), if (metadata.isNotEmpty) 'metadata': metadata, @@ -208,6 +219,13 @@ class GoAgentCoreSessionRequest { } } +const String _gatewaySessionMode = 'gateway-chat'; + +bool _usesGatewaySessionMode(String mode) { + final normalized = mode.trim(); + return normalized == 'gateway' || normalized == _gatewaySessionMode; +} + class GoAgentCoreSessionUpdate { const GoAgentCoreSessionUpdate({ required this.sessionId, diff --git a/lib/runtime/go_agent_core_desktop_transport.dart b/lib/runtime/go_agent_core_desktop_transport.dart index 59216d67..72e720b9 100644 --- a/lib/runtime/go_agent_core_desktop_transport.dart +++ b/lib/runtime/go_agent_core_desktop_transport.dart @@ -285,7 +285,7 @@ class GoAgentCoreDesktopTransport implements GoAgentCoreClient { } if (resolvedEndpointTarget.isNotEmpty) { params['resolvedEndpointTarget'] = resolvedEndpointTarget; - if (resolvedExecutionTarget == 'gateway-chat') { + if (_isGatewayExecutionTarget(resolvedExecutionTarget)) { params['executionTarget'] = resolvedEndpointTarget; } } @@ -323,7 +323,7 @@ class GoAgentCoreDesktopTransport implements GoAgentCoreClient { } final resolvedExecutionTarget = routingResult['resolvedExecutionTarget']?.toString().trim() ?? ''; - if (resolvedExecutionTarget == 'gateway-chat') { + if (_isGatewayExecutionTarget(resolvedExecutionTarget)) { final endpointTarget = routingResult['resolvedEndpointTarget']?.toString().trim() ?? ''; return switch (endpointTarget) { @@ -354,4 +354,9 @@ class GoAgentCoreDesktopTransport implements GoAgentCoreClient { .where((item) => item.isNotEmpty) .toList(growable: false); } + + bool _isGatewayExecutionTarget(String value) { + final normalized = value.trim(); + return normalized == 'gateway' || normalized == 'gateway-chat'; + } } diff --git a/lib/web/go_agent_core_web_transport.dart b/lib/web/go_agent_core_web_transport.dart index f0e0addb..f54f1f1d 100644 --- a/lib/web/go_agent_core_web_transport.dart +++ b/lib/web/go_agent_core_web_transport.dart @@ -159,7 +159,7 @@ class GoAgentCoreWebTransport implements GoAgentCoreClient { } if (resolvedEndpointTarget.isNotEmpty) { params['resolvedEndpointTarget'] = resolvedEndpointTarget; - if (resolvedExecutionTarget == 'gateway-chat') { + if (_isGatewayExecutionTarget(resolvedExecutionTarget)) { params['executionTarget'] = resolvedEndpointTarget; } } @@ -197,7 +197,7 @@ class GoAgentCoreWebTransport implements GoAgentCoreClient { } final resolvedExecutionTarget = routingResult['resolvedExecutionTarget']?.toString().trim() ?? ''; - if (resolvedExecutionTarget == 'gateway-chat') { + if (_isGatewayExecutionTarget(resolvedExecutionTarget)) { final endpointTarget = routingResult['resolvedEndpointTarget']?.toString().trim() ?? ''; return switch (endpointTarget) { @@ -219,6 +219,11 @@ class GoAgentCoreWebTransport implements GoAgentCoreClient { return const {}; } + bool _isGatewayExecutionTarget(String value) { + final normalized = value.trim(); + return normalized == 'gateway' || normalized == 'gateway-chat'; + } + List _castStringList(Object? raw) { if (raw is! List) { return const []; diff --git a/test/runtime/app_controller_ai_gateway_chat_suite_chat.dart b/test/runtime/app_controller_ai_gateway_chat_suite_chat.dart index 90f5b8da..d258fd43 100644 --- a/test/runtime/app_controller_ai_gateway_chat_suite_chat.dart +++ b/test/runtime/app_controller_ai_gateway_chat_suite_chat.dart @@ -24,7 +24,7 @@ void registerAppControllerAiGatewayChatSuiteChatTestsInternal() { 'AppController streams and restores persistent Single Agent conversation turns', () async { final tempDirectory = await createTempDirectoryInternal( - 'xworkmate-ai-gateway-chat-', + 'xworkmate-ai-gateway-session-', ); final server = await FakeAiGatewayServerInternal.start( responseMode: AiGatewayResponseModeInternal.sse, diff --git a/test/runtime/go_agent_core_client_suite.dart b/test/runtime/go_agent_core_client_suite.dart index 31462549..287bc96d 100644 --- a/test/runtime/go_agent_core_client_suite.dart +++ b/test/runtime/go_agent_core_client_suite.dart @@ -95,6 +95,33 @@ void main() { }); }); + test('routing execution target uses gateway while session mode stays compatible', () { + const request = GoAgentCoreSessionRequest( + sessionId: 'session-2', + threadId: 'thread-2', + target: AssistantExecutionTarget.local, + prompt: 'search latest news', + workingDirectory: '/tmp/workspace', + model: '', + thinking: '', + selectedSkills: [], + inlineAttachments: [], + localAttachments: [], + aiGatewayBaseUrl: '', + aiGatewayApiKey: '', + agentId: 'agent-1', + metadata: {'source': 'test'}, + provider: SingleAgentProvider.auto, + ); + + final params = request.toAcpParams(); + + expect(request.routingExecutionTarget, 'gateway'); + expect(params['mode'], 'gateway-chat'); + expect(params['executionTarget'], 'local'); + expect(params['agentId'], 'agent-1'); + }); + test( 'run result prefers completion text and preserves resolved workspace', () {