149 lines
4.8 KiB
Dart
149 lines
4.8 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:http/testing.dart';
|
|
import 'package:kouyu_english/core/ai_service.dart';
|
|
import 'package:kouyu_english/core/models.dart';
|
|
|
|
void main() {
|
|
const definition = '{"definition":"苹果"}';
|
|
|
|
Future<Map<String, dynamic>> captureBody(String endpoint) async {
|
|
AiService.instance.setFallbackApiKey('test-key');
|
|
Map<String, dynamic>? body;
|
|
final result = await http.runWithClient(
|
|
() => AiService.instance.temporaryDefinition(
|
|
provider: AiProviderType.compatible,
|
|
endpoint: endpoint,
|
|
model: 'deepseek-flash',
|
|
text: 'apple',
|
|
),
|
|
() => MockClient((request) async {
|
|
body = jsonDecode(request.body) as Map<String, dynamic>;
|
|
return http.Response(
|
|
jsonEncode({
|
|
'choices': [
|
|
{
|
|
'message': {'content': definition},
|
|
},
|
|
],
|
|
'output_text': definition,
|
|
}),
|
|
200,
|
|
headers: {'content-type': 'application/json; charset=utf-8'},
|
|
);
|
|
}),
|
|
);
|
|
expect(result, '苹果');
|
|
return body!;
|
|
}
|
|
|
|
test('DeepSeek chat completions request disables thinking', () async {
|
|
final body = await captureBody('https://api.deepseek.com');
|
|
|
|
expect(body['thinking'], {'type': 'disabled'});
|
|
expect(body['response_format'], {'type': 'json_object'});
|
|
expect(body.containsKey('reasoning_effort'), isFalse);
|
|
expect(body['max_tokens'], 200);
|
|
});
|
|
|
|
test('DeepSeek responses request sets reasoning effort to none', () async {
|
|
final body = await captureBody('https://api.deepseek.com/v1/responses');
|
|
|
|
expect(body['reasoning'], {'effort': 'none'});
|
|
expect(body['text'], {
|
|
'format': {'type': 'json_object'},
|
|
});
|
|
expect(body.containsKey('reasoning_effort'), isFalse);
|
|
expect(body.containsKey('thinking'), isFalse);
|
|
});
|
|
|
|
test('other compatible endpoints keep their original payload', () async {
|
|
final body = await captureBody('https://example.test/v1');
|
|
|
|
expect(body.containsKey('thinking'), isFalse);
|
|
expect(body.containsKey('response_format'), isFalse);
|
|
expect(body['reasoning_effort'], 'low');
|
|
});
|
|
|
|
test('dialogue history replays earlier AI lines as JSON', () async {
|
|
AiService.instance.setFallbackApiKey('test-key');
|
|
List<dynamic>? messages;
|
|
final reply = await http.runWithClient(
|
|
() => AiService.instance.dialogueReply(
|
|
provider: AiProviderType.compatible,
|
|
endpoint: 'https://api.deepseek.com',
|
|
model: 'deepseek-flash',
|
|
aiGoal: 'Ask where the learner is from.',
|
|
learnerTask: 'say where they are from',
|
|
history: const [
|
|
{'role': 'assistant', 'content': "Hi! What's your name?"},
|
|
{'role': 'user', 'content': 'My name is Alex.'},
|
|
],
|
|
),
|
|
() => MockClient((request) async {
|
|
final body = jsonDecode(request.body) as Map<String, dynamic>;
|
|
messages = body['messages'] as List<dynamic>;
|
|
return http.Response(
|
|
jsonEncode({
|
|
'choices': [
|
|
{
|
|
'message': {'content': '{"reply":"Where are you from?"}'},
|
|
},
|
|
],
|
|
}),
|
|
200,
|
|
headers: {'content-type': 'application/json; charset=utf-8'},
|
|
);
|
|
}),
|
|
);
|
|
|
|
expect(reply?.reply, 'Where are you from?');
|
|
expect(messages, hasLength(4));
|
|
expect(messages![0]['role'], 'system');
|
|
expect(messages![1]['role'], 'user');
|
|
expect(messages![1]['content'], startsWith('[ConversationConfig]'));
|
|
expect(jsonDecode(messages![2]['content'] as String), {
|
|
'reply': "Hi! What's your name?",
|
|
});
|
|
expect(messages![3]['role'], 'user');
|
|
final last = messages![3]['content'] as String;
|
|
expect(last, startsWith('My name is Alex.'));
|
|
expect(
|
|
last,
|
|
contains('After your line the learner has to: say where they are from.'),
|
|
);
|
|
expect(
|
|
last,
|
|
endsWith(
|
|
'Do not repeat any questions or greetings already asked or answered.',
|
|
),
|
|
);
|
|
expect(messages![0]['content'], isNot(contains('Ask where the learner')));
|
|
});
|
|
|
|
test('dialogue system prompt stays identical across levels and lessons', () {
|
|
final words = ['Excuse me.', 'Can you help me?'];
|
|
final a = AiService.dialogueSystemPrompt();
|
|
final b = AiService.dialogueSystemPrompt();
|
|
expect(a, b);
|
|
expect(a, contains('Strictly do not repeat any question'));
|
|
expect(a, isNot(contains('Excuse me.')));
|
|
|
|
final config = AiService.dialogueConversationConfig(
|
|
level: 'A1',
|
|
allowedLanguage: words,
|
|
);
|
|
expect(config, contains('A1'));
|
|
expect(config, contains('Excuse me.'));
|
|
expect(
|
|
AiService.dialogueConversationConfig(
|
|
level: 'A0',
|
|
allowedLanguage: const [],
|
|
),
|
|
contains('simple, common A0 English'),
|
|
);
|
|
});
|
|
}
|