Clean up auto routing gateway terminology

This commit is contained in:
Haitao Pan 2026-04-03 19:45:37 +08:00
parent e018fc15dd
commit 85eab1806c
11 changed files with 99 additions and 22 deletions

View File

@ -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)
## 相关设计文档

View File

@ -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",
},

View File

@ -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)
}
}

View File

@ -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" {

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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') ...<String, dynamic>{
if (_usesGatewaySessionMode(mode)) ...<String, dynamic>{
'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,

View File

@ -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';
}
}

View File

@ -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 <String, dynamic>{};
}
bool _isGatewayExecutionTarget(String value) {
final normalized = value.trim();
return normalized == 'gateway' || normalized == 'gateway-chat';
}
List<String> _castStringList(Object? raw) {
if (raw is! List) {
return const <String>[];

View File

@ -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,

View File

@ -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: <String>[],
inlineAttachments: <GatewayChatAttachmentPayload>[],
localAttachments: <CollaborationAttachment>[],
aiGatewayBaseUrl: '',
aiGatewayApiKey: '',
agentId: 'agent-1',
metadata: <String, dynamic>{'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',
() {