Retry agent skill loading after transient errors

This commit is contained in:
Hamza Ayed
2026-10-07 19:00:18 +03:00
parent d36957a1ca
commit ad74c342b8
3 changed files with 97 additions and 32 deletions
@@ -21,6 +21,8 @@ class _VersionTestApi extends ApiRepository {
List<String> deletedFiles = const [];
String? registeredProject;
String? unregisteredProject;
int skillFailuresRemaining = 0;
int skillLoadCount = 0;
@override
Future<String> getModelName() async => 'gemma4:e2b';
@@ -40,23 +42,30 @@ class _VersionTestApi extends ApiRepository {
];
@override
Future<List<AgentSkillDescriptor>> getAgentSkills() async => const [
AgentSkillDescriptor(
id: 'code_explain',
name: 'شرح الكود',
description: 'شرح المصدر.',
),
AgentSkillDescriptor(
id: 'code_review',
name: 'مراجعة الكود',
description: 'مراجعة المصدر.',
),
AgentSkillDescriptor(
id: 'test_plan',
name: 'خطة اختبارات',
description: 'اقتراح الاختبارات.',
),
];
Future<List<AgentSkillDescriptor>> getAgentSkills() async {
skillLoadCount++;
if (skillFailuresRemaining > 0) {
skillFailuresRemaining--;
throw StateError('temporary API failure');
}
return const [
AgentSkillDescriptor(
id: 'code_explain',
name: 'شرح الكود',
description: 'شرح المصدر.',
),
AgentSkillDescriptor(
id: 'code_review',
name: 'مراجعة الكود',
description: 'مراجعة المصدر.',
),
AgentSkillDescriptor(
id: 'test_plan',
name: 'خطة اختبارات',
description: 'اقتراح الاختبارات.',
),
];
}
@override
Future<List<SavedConversation>> listConversations() async => [];
@@ -164,6 +173,36 @@ class _MemorySettingsStore extends LocalSettingsStore {
}
void main() {
test('agent skills retry a transient API failure during startup', () async {
final api = _VersionTestApi()..skillFailuresRemaining = 1;
final cubit = ChatCubit(api, settingsStore: LocalSettingsStore.inMemory());
await Future<void>.delayed(const Duration(milliseconds: 450));
expect(api.skillLoadCount, 2);
expect(cubit.state.availableSkills, hasLength(3));
expect(cubit.state.error, isNull);
await cubit.close();
});
test(
'agent skills preserve an error after all startup retries fail',
() async {
final api = _VersionTestApi()..skillFailuresRemaining = 3;
final cubit = ChatCubit(
api,
settingsStore: LocalSettingsStore.inMemory(),
);
await Future<void>.delayed(const Duration(milliseconds: 1200));
expect(api.skillLoadCount, 3);
expect(cubit.state.availableSkills, isEmpty);
expect(cubit.state.error, contains('تعذر تحميل مهارات الوكيل'));
await cubit.close();
},
);
test('embedding-only models are not selectable for chat', () async {
final api = _VersionTestApi();
final cubit = ChatCubit(api, settingsStore: LocalSettingsStore.inMemory());
@@ -197,14 +236,8 @@ void main() {
]);
expect(message, contains('فُهرست 2 ملفًا'));
expect(message, contains('4 مقطعًا'));
expect(
progressUpdates,
contains('يفهرس الملف 1/2: README.md…'),
);
expect(
progressUpdates,
contains('يفهرس الملف 2/2: main.dart…'),
);
expect(progressUpdates, contains('يفهرس الملف 1/2: README.md…'));
expect(progressUpdates, contains('يفهرس الملف 2/2: main.dart…'));
expect(cubit.state.agentProgress, isNull);
await subscription.cancel();