refactor: remove legacy single-agent provider selection flow
This commit is contained in:
parent
d4e09d7113
commit
51c90e9613
@ -572,16 +572,19 @@ class AppController extends ChangeNotifier {
|
||||
List<AssistantExecutionTarget> get bridgeAvailableExecutionTargets =>
|
||||
compactAssistantExecutionTargets(bridgeAvailableExecutionTargetsInternal);
|
||||
|
||||
List<SingleAgentProvider> get assistantProviderCatalogForDisplay {
|
||||
return assistantProviderCatalog;
|
||||
}
|
||||
|
||||
List<SingleAgentProvider> providerCatalogForExecutionTarget(
|
||||
AssistantExecutionTarget executionTarget,
|
||||
) {
|
||||
return executionTarget.isGateway
|
||||
final source = executionTarget.isGateway
|
||||
? gatewayProviderCatalog
|
||||
: assistantProviderCatalogForDisplay;
|
||||
: assistantProviderCatalog;
|
||||
return source
|
||||
.where(
|
||||
(provider) =>
|
||||
provider.supportedTargets.isEmpty ||
|
||||
provider.supportedTargets.contains(executionTarget),
|
||||
)
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
SingleAgentProvider? bridgeProviderForId(String providerId) {
|
||||
@ -625,13 +628,6 @@ class AppController extends ChangeNotifier {
|
||||
return SingleAgentProvider.unspecified;
|
||||
}
|
||||
|
||||
SingleAgentProvider resolveAssistantProvider(String? providerId) {
|
||||
return resolveProviderForExecutionTarget(
|
||||
providerId,
|
||||
executionTarget: AssistantExecutionTarget.agent,
|
||||
);
|
||||
}
|
||||
|
||||
SingleAgentProvider assistantProviderForSession(String sessionKey) {
|
||||
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionKey,
|
||||
|
||||
@ -227,9 +227,9 @@ extension AppControllerDesktopSkillPermissions on AppController {
|
||||
List<AssistantThreadSkillEntry>? importedSkills,
|
||||
List<String>? selectedSkillKeys,
|
||||
String? assistantModelId,
|
||||
SingleAgentProvider? singleAgentProvider,
|
||||
SingleAgentProvider? selectedProvider,
|
||||
ThreadSelectionSource? executionTargetSource,
|
||||
ThreadSelectionSource? singleAgentProviderSource,
|
||||
ThreadSelectionSource? selectedProviderSource,
|
||||
ThreadSelectionSource? assistantModelSource,
|
||||
ThreadSelectionSource? selectedSkillsSource,
|
||||
String? gatewayEntryState,
|
||||
@ -291,8 +291,8 @@ extension AppControllerDesktopSkillPermissions on AppController {
|
||||
'TaskThread $normalizedSessionKey is missing a complete workspaceBinding.',
|
||||
);
|
||||
}
|
||||
final requestedProvider = singleAgentProvider?.isUnspecified == false
|
||||
? singleAgentProvider
|
||||
final requestedProvider = selectedProvider?.isUnspecified == false
|
||||
? selectedProvider
|
||||
: null;
|
||||
final nextProviderId = normalizeSingleAgentProviderId(
|
||||
requestedProvider?.providerId ??
|
||||
@ -305,7 +305,7 @@ extension AppControllerDesktopSkillPermissions on AppController {
|
||||
executionTarget: nextExecutionTarget,
|
||||
);
|
||||
final nextProviderSource =
|
||||
singleAgentProviderSource ??
|
||||
selectedProviderSource ??
|
||||
existing?.executionBinding.providerSource ??
|
||||
ThreadSelectionSource.inherited;
|
||||
final nextExecutionBinding =
|
||||
|
||||
@ -99,13 +99,13 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
executionTarget: resolvedTarget,
|
||||
executionTargetSource: ThreadSelectionSource.explicit,
|
||||
singleAgentProvider: resolveProviderForExecutionTarget(
|
||||
selectedProvider: resolveProviderForExecutionTarget(
|
||||
taskThreadForSessionInternal(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
)?.executionBinding.providerId,
|
||||
executionTarget: resolvedTarget,
|
||||
),
|
||||
singleAgentProviderSource: ThreadSelectionSource.explicit,
|
||||
selectedProviderSource: ThreadSelectionSource.explicit,
|
||||
gatewayEntryState: gatewayEntryStateForTargetInternal(resolvedTarget),
|
||||
latestResolvedRuntimeModel: '',
|
||||
latestResolvedProviderId: '',
|
||||
@ -125,10 +125,16 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
|
||||
notifyIfActiveInternal();
|
||||
}
|
||||
|
||||
Future<void> setAssistantSingleAgentProvider(
|
||||
Future<void> setAssistantProvider(
|
||||
SingleAgentProvider provider,
|
||||
) async {
|
||||
final resolvedProvider = resolveAssistantProvider(provider.providerId);
|
||||
final executionTarget = assistantExecutionTargetForSession(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
);
|
||||
final resolvedProvider = resolveProviderForExecutionTarget(
|
||||
provider.providerId,
|
||||
executionTarget: executionTarget,
|
||||
);
|
||||
final sessionKey = normalizedAssistantSessionKeyInternal(
|
||||
sessionsControllerInternal.currentSessionKey,
|
||||
);
|
||||
@ -146,27 +152,22 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
|
||||
if (!assistantThreadRecordsInternal.containsKey(sessionKey)) {
|
||||
initializeAssistantThreadContext(
|
||||
sessionKey,
|
||||
executionTarget: AssistantExecutionTarget.agent,
|
||||
executionTarget: executionTarget,
|
||||
messageViewMode: assistantMessageViewModeForSession(sessionKey),
|
||||
);
|
||||
}
|
||||
upsertTaskThreadInternal(
|
||||
sessionKey,
|
||||
executionTarget: AssistantExecutionTarget.agent,
|
||||
executionTarget: executionTarget,
|
||||
executionTargetSource: ThreadSelectionSource.explicit,
|
||||
singleAgentProvider: resolvedProvider,
|
||||
singleAgentProviderSource: ThreadSelectionSource.explicit,
|
||||
selectedProvider: resolvedProvider,
|
||||
selectedProviderSource: ThreadSelectionSource.explicit,
|
||||
gatewayEntryState: gatewayEntryStateForTargetInternal(
|
||||
AssistantExecutionTarget.agent,
|
||||
executionTarget,
|
||||
),
|
||||
latestResolvedProviderId: '',
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
);
|
||||
await applyAssistantExecutionTargetInternal(
|
||||
AssistantExecutionTarget.agent,
|
||||
sessionKey: sessionKey,
|
||||
persistDefaultSelection: true,
|
||||
);
|
||||
await flushAssistantThreadPersistenceInternal();
|
||||
recomputeTasksInternal();
|
||||
notifyIfActiveInternal();
|
||||
@ -222,13 +223,13 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
|
||||
);
|
||||
upsertTaskThreadInternal(
|
||||
normalizedSessionKey,
|
||||
singleAgentProvider: resolveProviderForExecutionTarget(
|
||||
selectedProvider: resolveProviderForExecutionTarget(
|
||||
taskThreadForSessionInternal(normalizedSessionKey)
|
||||
?.executionBinding
|
||||
.providerId,
|
||||
executionTarget: resolvedTarget,
|
||||
),
|
||||
singleAgentProviderSource: ThreadSelectionSource.explicit,
|
||||
selectedProviderSource: ThreadSelectionSource.explicit,
|
||||
latestResolvedRuntimeModel: '',
|
||||
latestResolvedProviderId: '',
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
|
||||
@ -44,9 +44,8 @@ class AssistantTaskDialogModeControlsInternal extends StatelessWidget {
|
||||
final executionTarget = collapseAssistantExecutionTargetForDisplay(
|
||||
currentExecutionTarget,
|
||||
);
|
||||
final providerMenuProviders = _taskDialogProviderCatalogForTarget(
|
||||
controller: controller,
|
||||
executionTarget: executionTarget,
|
||||
final providerMenuProviders = controller.providerCatalogForExecutionTarget(
|
||||
executionTarget,
|
||||
);
|
||||
|
||||
return Wrap(
|
||||
@ -73,13 +72,6 @@ class AssistantTaskDialogModeControlsInternal extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
List<SingleAgentProvider> _taskDialogProviderCatalogForTarget({
|
||||
required AppController controller,
|
||||
required AssistantExecutionTarget executionTarget,
|
||||
}) {
|
||||
return controller.providerCatalogForExecutionTarget(executionTarget);
|
||||
}
|
||||
|
||||
class _TaskDialogExecutionTargetMenuButtonInternal extends StatelessWidget {
|
||||
const _TaskDialogExecutionTargetMenuButtonInternal({
|
||||
required this.controller,
|
||||
@ -160,7 +152,7 @@ class _TaskDialogProviderMenuButtonInternal extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final displayProvider = selectedProvider.isUnspecified
|
||||
? _fallbackDisplayProvider(context)
|
||||
? _fallbackDisplayProvider()
|
||||
: selectedProvider;
|
||||
final isEnabled = providers.isNotEmpty;
|
||||
|
||||
@ -204,7 +196,7 @@ class _TaskDialogProviderMenuButtonInternal extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
SingleAgentProvider _fallbackDisplayProvider(BuildContext context) {
|
||||
SingleAgentProvider _fallbackDisplayProvider() {
|
||||
if (providers.isNotEmpty) {
|
||||
return providers.first;
|
||||
}
|
||||
@ -218,10 +210,10 @@ class _TaskDialogProviderMenuButtonInternal extends StatelessWidget {
|
||||
}
|
||||
|
||||
Future<void> _handleProviderSelected(SingleAgentProvider provider) async {
|
||||
if (executionTarget.isGateway || providers.isEmpty) {
|
||||
if (providers.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await controller.setAssistantSingleAgentProvider(provider);
|
||||
await controller.setAssistantProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ String normalizeSingleAgentProviderId(String value) {
|
||||
return buffer.toString().replaceAll(RegExp(r'^[-_.]+|[-_.]+$'), '');
|
||||
}
|
||||
|
||||
String singleAgentProviderFallbackLabelInternal(String providerId) {
|
||||
String providerFallbackLabelInternal(String providerId) {
|
||||
final normalized = normalizeSingleAgentProviderId(providerId);
|
||||
if (normalized.isEmpty) {
|
||||
return appText('Bridge Provider', 'Bridge Provider');
|
||||
@ -140,7 +140,7 @@ String singleAgentProviderFallbackLabelInternal(String providerId) {
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
String singleAgentProviderFallbackBadgeInternal({
|
||||
String providerFallbackBadgeInternal({
|
||||
required String providerId,
|
||||
required String label,
|
||||
}) {
|
||||
@ -259,10 +259,10 @@ class SingleAgentProvider {
|
||||
return SingleAgentProvider(
|
||||
providerId: resolvedProviderId,
|
||||
label: resolvedLabel.isEmpty
|
||||
? singleAgentProviderFallbackLabelInternal(resolvedProviderId)
|
||||
? providerFallbackLabelInternal(resolvedProviderId)
|
||||
: resolvedLabel,
|
||||
badge: resolvedBadge.isEmpty
|
||||
? singleAgentProviderFallbackBadgeInternal(
|
||||
? providerFallbackBadgeInternal(
|
||||
providerId: resolvedProviderId,
|
||||
label: resolvedLabel,
|
||||
)
|
||||
@ -297,10 +297,10 @@ class SingleAgentProvider {
|
||||
'auto' || '' => unspecified,
|
||||
_ => SingleAgentProvider(
|
||||
providerId: normalized,
|
||||
label: singleAgentProviderFallbackLabelInternal(normalized),
|
||||
badge: singleAgentProviderFallbackBadgeInternal(
|
||||
label: providerFallbackLabelInternal(normalized),
|
||||
badge: providerFallbackBadgeInternal(
|
||||
providerId: normalized,
|
||||
label: singleAgentProviderFallbackLabelInternal(normalized),
|
||||
label: providerFallbackLabelInternal(normalized),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
@ -253,6 +253,101 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('allows switching gateway providers from the dynamic catalog', (
|
||||
tester,
|
||||
) async {
|
||||
final controller = AppController(
|
||||
initialBridgeProviderCatalog: const <SingleAgentProvider>[
|
||||
SingleAgentProvider.codex,
|
||||
SingleAgentProvider.opencode,
|
||||
SingleAgentProvider.gemini,
|
||||
],
|
||||
initialGatewayProviderCatalog: <SingleAgentProvider>[
|
||||
SingleAgentProvider.openclaw.copyWith(
|
||||
logoEmoji: '🦞',
|
||||
supportedTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
),
|
||||
SingleAgentProvider.fromJsonValue(
|
||||
'hermes',
|
||||
label: 'Hermes',
|
||||
badge: 'H',
|
||||
supportedTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
),
|
||||
],
|
||||
initialAvailableExecutionTargets: const <AssistantExecutionTarget>[
|
||||
AssistantExecutionTarget.agent,
|
||||
AssistantExecutionTarget.gateway,
|
||||
],
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
await controller.sessionsController.switchSession('session-1');
|
||||
controller.initializeAssistantThreadContext(
|
||||
'session-1',
|
||||
executionTarget: AssistantExecutionTarget.gateway,
|
||||
messageViewMode: controller.assistantMessageViewModeForSession(
|
||||
'session-1',
|
||||
),
|
||||
);
|
||||
final gatewayThread = controller
|
||||
.requireTaskThreadForSessionInternal('session-1')
|
||||
.copyWith(
|
||||
executionBinding: ExecutionBinding(
|
||||
executionMode: threadExecutionModeFromAssistantExecutionTarget(
|
||||
AssistantExecutionTarget.gateway,
|
||||
),
|
||||
executorId: SingleAgentProvider.openclaw.providerId,
|
||||
providerId: SingleAgentProvider.openclaw.providerId,
|
||||
endpointId: '',
|
||||
executionModeSource: ThreadSelectionSource.explicit,
|
||||
providerSource: ThreadSelectionSource.explicit,
|
||||
),
|
||||
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
|
||||
);
|
||||
controller.taskThreadRepositoryInternal.replace(
|
||||
gatewayThread,
|
||||
persist: false,
|
||||
);
|
||||
controller.notifyListeners();
|
||||
|
||||
await tester.pumpWidget(
|
||||
_buildTestApp(child: _buildLowerPane(controller: controller)),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('assistant-provider-button')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.byKey(const Key('assistant-provider-menu-item-openclaw')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.byKey(const Key('assistant-provider-menu-item-hermes')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.byKey(const Key('assistant-provider-menu-item-codex')),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
await tester.tap(
|
||||
find.byKey(const Key('assistant-provider-menu-item-hermes')),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
controller
|
||||
.assistantProviderForSession(controller.currentSessionKey)
|
||||
.providerId,
|
||||
'hermes',
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('uses submit button instead of connect action', (tester) async {
|
||||
final controller = AppController();
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user