Files
English/kouyu_english/test/deepseek_thinking_test.dart

137 lines
4.5 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(3));
expect(messages![0]['role'], 'system');
expect(jsonDecode(messages![1]['content'] as String), {
'reply': "Hi! What's your name?",
});
expect(messages![2]['role'], 'user');
final last = messages![2]['content'] as String;
expect(last, startsWith('My name is Alex.'));
expect(
last,
endsWith('After your line the learner has to: say where they are from.'),
);
expect(messages![0]['content'], isNot(contains('Ask where the learner')));
});
test('dialogue system prompt stays identical across turns', () {
final words = ['Excuse me.', 'Can you help me?'];
final a = AiService.dialogueSystemPrompt(
level: 'A1',
allowedLanguage: words,
);
final b = AiService.dialogueSystemPrompt(
level: 'A1',
allowedLanguage: words,
);
expect(a, b);
expect(a, contains('CEFR A1'));
expect(a, endsWith('Taught language: Excuse me.; Can you help me?'));
expect(
AiService.dialogueSystemPrompt(level: 'A0', allowedLanguage: const []),
contains('A0 English conversation partner'),
);
});
}