fix: keep agent mode selectable and omit OpenClaw model
This commit is contained in:
parent
b9a9999291
commit
bc468d8f2e
@ -73,7 +73,8 @@ extension AppControllerDesktopExternalAcpRouting on AppController {
|
||||
!resolvedProvider.isUnspecified
|
||||
? resolvedProvider.providerId
|
||||
: '';
|
||||
final resolvedExplicitModel = thread?.hasExplicitModelSelection ?? false
|
||||
final resolvedExplicitModel =
|
||||
!currentTarget.isGateway && (thread?.hasExplicitModelSelection ?? false)
|
||||
? assistantModelForSession(normalizedSessionKey)
|
||||
: '';
|
||||
final resolvedExplicitSkills = thread?.hasExplicitSkillSelection ?? false
|
||||
|
||||
@ -336,7 +336,9 @@ extension AppControllerDesktopThreadActions on AppController {
|
||||
}
|
||||
}
|
||||
final provider = assistantProviderForSession(sessionKey);
|
||||
final model = assistantModelForSession(sessionKey);
|
||||
final model = currentTarget.isGateway
|
||||
? ''
|
||||
: assistantModelForSession(sessionKey);
|
||||
final routing = buildExternalAcpRoutingForSessionInternal(sessionKey);
|
||||
final dispatch = await codeAgentNodeOrchestratorInternal
|
||||
.buildGatewayDispatch(
|
||||
|
||||
@ -29,16 +29,9 @@ class AssistantTaskDialogModeControlsInternal extends StatelessWidget {
|
||||
if (supportedExecutionTargets.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final visibleExecutionTargets = controller.visibleAssistantExecutionTargets(
|
||||
supportedExecutionTargets,
|
||||
);
|
||||
final resolutionTargets = visibleExecutionTargets.isNotEmpty
|
||||
? visibleExecutionTargets
|
||||
: supportedExecutionTargets;
|
||||
|
||||
final currentExecutionTarget =
|
||||
resolveAssistantExecutionTargetFromVisibleTargets(
|
||||
resolutionTargets,
|
||||
supportedExecutionTargets,
|
||||
currentTarget: controller.assistantExecutionTarget,
|
||||
);
|
||||
final executionTarget = collapseAssistantExecutionTargetForDisplay(
|
||||
@ -64,7 +57,6 @@ class AssistantTaskDialogModeControlsInternal extends StatelessWidget {
|
||||
controller: controller,
|
||||
executionTarget: executionTarget,
|
||||
supportedExecutionTargets: supportedExecutionTargets,
|
||||
visibleExecutionTargets: visibleExecutionTargets,
|
||||
),
|
||||
_TaskDialogProviderMenuButtonInternal(
|
||||
controller: controller,
|
||||
@ -81,13 +73,11 @@ class _TaskDialogExecutionTargetMenuButtonInternal extends StatelessWidget {
|
||||
required this.controller,
|
||||
required this.executionTarget,
|
||||
required this.supportedExecutionTargets,
|
||||
required this.visibleExecutionTargets,
|
||||
});
|
||||
|
||||
final AppController controller;
|
||||
final AssistantExecutionTarget executionTarget;
|
||||
final List<AssistantExecutionTarget> supportedExecutionTargets;
|
||||
final List<AssistantExecutionTarget> visibleExecutionTargets;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -102,10 +92,8 @@ class _TaskDialogExecutionTargetMenuButtonInternal extends StatelessWidget {
|
||||
},
|
||||
itemBuilder: (context) => supportedExecutionTargets
|
||||
.map((value) {
|
||||
final enabled = visibleExecutionTargets.contains(value);
|
||||
return PopupMenuItem<AssistantExecutionTarget>(
|
||||
value: value,
|
||||
enabled: enabled,
|
||||
key: Key('assistant-execution-target-menu-item-${value.name}'),
|
||||
child: Row(
|
||||
children: [
|
||||
@ -131,7 +119,7 @@ class _TaskDialogExecutionTargetMenuButtonInternal extends StatelessWidget {
|
||||
AssistantExecutionTarget value,
|
||||
) async {
|
||||
final resolvedTarget = resolveAssistantExecutionTargetFromVisibleTargets(
|
||||
visibleExecutionTargets,
|
||||
supportedExecutionTargets,
|
||||
currentTarget: value,
|
||||
);
|
||||
await controller.setAssistantExecutionTarget(resolvedTarget);
|
||||
|
||||
@ -140,6 +140,23 @@ class ExternalCodeAgentAcpRoutingConfig {
|
||||
|
||||
bool get isAuto => mode == ExternalCodeAgentAcpRoutingMode.auto;
|
||||
|
||||
ExternalCodeAgentAcpRoutingConfig withoutExplicitModel() {
|
||||
if (explicitModel.trim().isEmpty) {
|
||||
return this;
|
||||
}
|
||||
return ExternalCodeAgentAcpRoutingConfig(
|
||||
mode: mode,
|
||||
preferredGatewayTarget: preferredGatewayTarget,
|
||||
explicitExecutionTarget: explicitExecutionTarget,
|
||||
explicitProviderId: explicitProviderId,
|
||||
explicitModel: '',
|
||||
explicitSkills: explicitSkills,
|
||||
allowSkillInstall: allowSkillInstall,
|
||||
availableSkills: availableSkills,
|
||||
installApproval: installApproval,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'routingMode': mode.name,
|
||||
@ -254,7 +271,10 @@ class GoTaskServiceRequest {
|
||||
routing ?? _synthesizedRouting();
|
||||
|
||||
Map<String, dynamic> toExternalAcpParams() {
|
||||
final resolvedRouting = effectiveRouting;
|
||||
final resolvedRouting = normalizedTarget.isGateway
|
||||
? effectiveRouting.withoutExplicitModel()
|
||||
: effectiveRouting;
|
||||
final requestModel = normalizedTarget.isGateway ? '' : model.trim();
|
||||
final providerId = provider.isUnspecified ? '' : provider.providerId;
|
||||
final agentProviderId = normalizedTarget.isGateway ? '' : providerId;
|
||||
final params = <String, dynamic>{
|
||||
@ -294,7 +314,7 @@ class GoTaskServiceRequest {
|
||||
if (agentProviderId.isNotEmpty) 'provider': agentProviderId,
|
||||
if (remoteWorkingDirectoryHint.trim().isNotEmpty)
|
||||
'remoteWorkingDirectoryHint': remoteWorkingDirectoryHint.trim(),
|
||||
if (model.trim().isNotEmpty) 'model': model.trim(),
|
||||
if (requestModel.isNotEmpty) 'model': requestModel,
|
||||
if (thinking.trim().isNotEmpty) 'thinking': thinking.trim(),
|
||||
'routing': resolvedRouting.toJson(),
|
||||
if (routingHint.trim().isNotEmpty) 'routingHint': routingHint.trim(),
|
||||
@ -321,7 +341,7 @@ class GoTaskServiceRequest {
|
||||
final explicitProviderId = provider.isUnspecified || gatewayTarget.isGateway
|
||||
? ''
|
||||
: provider.providerId;
|
||||
final explicitModelValue = model.trim();
|
||||
final explicitModelValue = gatewayTarget.isGateway ? '' : model.trim();
|
||||
final explicitSkillsValue = selectedSkills
|
||||
.map((item) => item.trim())
|
||||
.where((item) => item.isNotEmpty)
|
||||
|
||||
@ -317,6 +317,50 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'keeps task dialog modes selectable when only OpenClaw is live',
|
||||
(tester) async {
|
||||
final controller = AppController(
|
||||
environmentOverride: const <String, String>{},
|
||||
initialGatewayProviderCatalog: const <SingleAgentProvider>[
|
||||
SingleAgentProvider.openclaw,
|
||||
],
|
||||
initialAvailableExecutionTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
await controller.sessionsController.switchSession('draft:test-task-a');
|
||||
controller.initializeAssistantThreadContext(
|
||||
'draft:test-task-a',
|
||||
executionTarget: AssistantExecutionTarget.gateway,
|
||||
messageViewMode: controller.assistantMessageViewModeForSession(
|
||||
'draft:test-task-a',
|
||||
),
|
||||
);
|
||||
controller.notifyListeners();
|
||||
|
||||
await tester.pumpWidget(
|
||||
_buildTestApp(child: _buildLowerPane(controller: controller)),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(
|
||||
find.byKey(const Key('assistant-execution-target-button')),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final agentItem = tester
|
||||
.widget<PopupMenuItem<AssistantExecutionTarget>>(
|
||||
find.byKey(
|
||||
const Key('assistant-execution-target-menu-item-agent'),
|
||||
),
|
||||
);
|
||||
expect(agentItem.enabled, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('uses submit button instead of connect action', (tester) async {
|
||||
final controller = AppController(
|
||||
environmentOverride: const <String, String>{},
|
||||
|
||||
@ -2066,6 +2066,50 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('OpenClaw gateway task uses the server default model', () async {
|
||||
final fakeGoTaskService = _BlockingGoTaskServiceClient();
|
||||
final controller = _connectedGatewayController(fakeGoTaskService);
|
||||
addTearDown(() {
|
||||
fakeGoTaskService.completeAll();
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
await _selectGatewaySession(controller, 'openclaw-default-model-task');
|
||||
await controller.selectAssistantModelForSession(
|
||||
'openclaw-default-model-task',
|
||||
'ollama/kimi-k2.5',
|
||||
);
|
||||
|
||||
final taskFuture = controller.sendChatMessage('use OpenClaw default');
|
||||
await fakeGoTaskService.waitForRequestCount(1);
|
||||
|
||||
final request = fakeGoTaskService.requests.single;
|
||||
expect(request.target, AssistantExecutionTarget.gateway);
|
||||
expect(request.provider, SingleAgentProvider.openclaw);
|
||||
expect(request.model, isEmpty);
|
||||
|
||||
final params = request.toExternalAcpParams();
|
||||
expect(params.containsKey('model'), isFalse);
|
||||
expect(
|
||||
params['routing'],
|
||||
isNot(containsPair('explicitModel', 'ollama/kimi-k2.5')),
|
||||
);
|
||||
|
||||
fakeGoTaskService.complete(
|
||||
'openclaw-default-model-task',
|
||||
const GoTaskServiceResult(
|
||||
success: true,
|
||||
message: 'result',
|
||||
turnId: 'turn-openclaw-default-model',
|
||||
raw: <String, dynamic>{},
|
||||
errorMessage: '',
|
||||
resolvedModel: '',
|
||||
route: GoTaskServiceRoute.externalAcpSingle,
|
||||
),
|
||||
);
|
||||
await taskFuture;
|
||||
});
|
||||
|
||||
test(
|
||||
'abortRun removes a queued OpenClaw task without bridge cancel',
|
||||
() async {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user