Clean up LLM digest provider errors
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:relationship_saver/app/data/relationship_repository.dart';
|
import 'package:relationship_saver/app/data/relationship_repository.dart';
|
||||||
import 'package:relationship_saver/app/state/local_data_state.dart';
|
import 'package:relationship_saver/app/state/local_data_state.dart';
|
||||||
import 'package:relationship_saver/core/llm/llm_config.dart';
|
import 'package:relationship_saver/core/llm/llm_config.dart';
|
||||||
|
import 'package:relationship_saver/core/llm/llm_service.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/ai_digest_notifier.dart';
|
import 'package:relationship_saver/features/ai_digest/application/ai_digest_notifier.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/ai_digest_response_parser.dart';
|
import 'package:relationship_saver/features/ai_digest/application/ai_digest_response_parser.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/anonymized_llm_context_builder.dart';
|
import 'package:relationship_saver/features/ai_digest/application/anonymized_llm_context_builder.dart';
|
||||||
@@ -185,11 +186,12 @@ class LlmDigestOrchestrator {
|
|||||||
createdDraftCount: parsed.drafts.length,
|
createdDraftCount: parsed.drafts.length,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await _markFailure('$error');
|
final String reason = _digestFailureReason(error);
|
||||||
|
await _markFailure(reason);
|
||||||
return LlmDigestRunResult(
|
return LlmDigestRunResult(
|
||||||
started: true,
|
started: true,
|
||||||
completed: false,
|
completed: false,
|
||||||
reason: '$error',
|
reason: reason,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,6 +234,13 @@ class LlmDigestOrchestrator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _digestFailureReason(Object error) {
|
||||||
|
if (error is LlmProviderException) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
return '$error';
|
||||||
|
}
|
||||||
|
|
||||||
const String _systemPrompt = '''
|
const String _systemPrompt = '''
|
||||||
You create private relationship-maintenance digest suggestions.
|
You create private relationship-maintenance digest suggestions.
|
||||||
The input contains pseudonymous people only. Never ask for or invent names.
|
The input contains pseudonymous people only. Never ask for or invent names.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import 'package:relationship_saver/app/data/relationship_repository.dart';
|
|||||||
import 'package:relationship_saver/app/data/storage/local_data_store_in_memory.dart';
|
import 'package:relationship_saver/app/data/storage/local_data_store_in_memory.dart';
|
||||||
import 'package:relationship_saver/app/data/storage/local_data_store_provider.dart';
|
import 'package:relationship_saver/app/data/storage/local_data_store_provider.dart';
|
||||||
import 'package:relationship_saver/app/state/local_data_state.dart';
|
import 'package:relationship_saver/app/state/local_data_state.dart';
|
||||||
|
import 'package:relationship_saver/core/llm/llm_config.dart';
|
||||||
|
import 'package:relationship_saver/core/llm/llm_service.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/ai_digest_notifier.dart';
|
import 'package:relationship_saver/features/ai_digest/application/ai_digest_notifier.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/llm_digest_environment.dart';
|
import 'package:relationship_saver/features/ai_digest/application/llm_digest_environment.dart';
|
||||||
import 'package:relationship_saver/features/ai_digest/application/llm_digest_orchestrator.dart';
|
import 'package:relationship_saver/features/ai_digest/application/llm_digest_orchestrator.dart';
|
||||||
@@ -117,10 +119,45 @@ void main() {
|
|||||||
expect(result.started, isFalse);
|
expect(result.started, isFalse);
|
||||||
expect(textClient.calls, 0);
|
expect(textClient.calls, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('manual digest reports provider rate limits without Dio noise', () async {
|
||||||
|
final ThrowingTextClient textClient = ThrowingTextClient(
|
||||||
|
error: const LlmProviderException(
|
||||||
|
provider: LlmProvider.openai,
|
||||||
|
statusCode: 429,
|
||||||
|
message:
|
||||||
|
'OpenAI rejected the digest request with 429 rate limiting or quota pressure.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final ProviderContainer container = _createContainer(
|
||||||
|
textClient: textClient,
|
||||||
|
notifier: FakeNotifier(),
|
||||||
|
);
|
||||||
|
addTearDown(container.dispose);
|
||||||
|
|
||||||
|
await container.read(localRepositoryProvider.future);
|
||||||
|
await container
|
||||||
|
.read(localRepositoryProvider.notifier)
|
||||||
|
.addPerson(
|
||||||
|
name: 'Taylor Quinn',
|
||||||
|
relationship: 'Friend',
|
||||||
|
notes: '',
|
||||||
|
tags: const <String>['tea'],
|
||||||
|
);
|
||||||
|
|
||||||
|
final LlmDigestRunResult result = await container
|
||||||
|
.read(llmDigestOrchestratorProvider)
|
||||||
|
.runManualDigest();
|
||||||
|
|
||||||
|
expect(result.started, isTrue);
|
||||||
|
expect(result.completed, isFalse);
|
||||||
|
expect(result.reason, contains('429 rate limiting'));
|
||||||
|
expect(result.reason, isNot(contains('DioException')));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ProviderContainer _createContainer({
|
ProviderContainer _createContainer({
|
||||||
required FakeTextClient textClient,
|
required LlmDigestTextClient textClient,
|
||||||
required FakeNotifier notifier,
|
required FakeNotifier notifier,
|
||||||
LlmDigestEnvironment environment = const FakeEnvironment(),
|
LlmDigestEnvironment environment = const FakeEnvironment(),
|
||||||
}) {
|
}) {
|
||||||
@@ -158,6 +195,20 @@ class FakeTextClient implements LlmDigestTextClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ThrowingTextClient implements LlmDigestTextClient {
|
||||||
|
const ThrowingTextClient({required this.error});
|
||||||
|
|
||||||
|
final Object error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> complete({
|
||||||
|
required String systemPrompt,
|
||||||
|
required String userPrompt,
|
||||||
|
}) async {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FakeNotifier implements AiDigestNotifier {
|
class FakeNotifier implements AiDigestNotifier {
|
||||||
int? lastCount;
|
int? lastCount;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user