You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
4.5 KiB
154 lines
4.5 KiB
import 'dart:convert';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
|
|
import 'ai_service.dart';
|
|
|
|
class VolcanoAIService implements AiService {
|
|
final String apiKey;
|
|
static const String _baseUrl = 'https://ark.cn-beijing.volces.com/api/v3';
|
|
static const String _chatEndpoint = '/chat/completions';
|
|
|
|
VolcanoAIService() : apiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? '' {
|
|
if (apiKey.isEmpty) {
|
|
throw VolcanoAIException('火山AI配置信息不完整,请检查环境变量');
|
|
}
|
|
}
|
|
|
|
// 生成个性化问候语
|
|
Stream<String> generateGreeting({
|
|
required String agentName,
|
|
required String systemPrompt,
|
|
}) {
|
|
final messages = [
|
|
{'role': 'system', 'content': systemPrompt},
|
|
{'role': 'user', 'content': '请用一句简短的话向我打个招呼,要符合你的身份和性格特点,不要超过18个字。'},
|
|
];
|
|
|
|
return sendMessageStream(
|
|
messages: messages,
|
|
systemPrompt: systemPrompt,
|
|
);
|
|
}
|
|
|
|
// 非流式输出方法
|
|
Future<String> sendMessage({
|
|
required List<Map<String, String>> messages,
|
|
required String systemPrompt,
|
|
}) async {
|
|
final url = Uri.parse('$_baseUrl$_chatEndpoint');
|
|
|
|
final fullMessages = [
|
|
{'role': 'system', 'content': systemPrompt},
|
|
...messages,
|
|
];
|
|
|
|
try {
|
|
final response = await http.post(
|
|
url,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer $apiKey',
|
|
},
|
|
body: json.encode({
|
|
'model': 'doubao-1-5-lite-32k-250115',
|
|
'messages': fullMessages,
|
|
'temperature': 0.7,
|
|
'max_tokens': 2000,
|
|
'stream': false,
|
|
}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final data = json.decode(response.body);
|
|
if (data['choices'] != null &&
|
|
data['choices'].isNotEmpty &&
|
|
data['choices'][0]['message'] != null) {
|
|
return data['choices'][0]['message']['content'];
|
|
}
|
|
throw VolcanoAIException('Invalid response format');
|
|
} else {
|
|
final error = json.decode(response.body);
|
|
throw VolcanoAIException(
|
|
error['error']?['message'] ?? 'Unknown error occurred',
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (e is VolcanoAIException) rethrow;
|
|
throw VolcanoAIException('Failed to communicate with AI service: $e');
|
|
}
|
|
}
|
|
|
|
// 流式输出方法
|
|
@override
|
|
Stream<String> sendMessageStream({
|
|
required List<Map<String, String>> messages,
|
|
required String systemPrompt,
|
|
}) async* {
|
|
final url = Uri.parse('$_baseUrl$_chatEndpoint');
|
|
|
|
final fullMessages = [
|
|
{'role': 'system', 'content': systemPrompt},
|
|
...messages,
|
|
];
|
|
|
|
try {
|
|
final request = http.Request('POST', url);
|
|
request.headers.addAll({
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer $apiKey',
|
|
'Accept': 'text/event-stream',
|
|
});
|
|
|
|
request.body = json.encode({
|
|
'model': 'doubao-1-5-lite-32k-250115',
|
|
'messages': fullMessages,
|
|
'temperature': 0.7,
|
|
'max_tokens': 2000,
|
|
'stream': true,
|
|
});
|
|
|
|
final response = await http.Client().send(request);
|
|
|
|
if (response.statusCode != 200) {
|
|
final error = await response.stream.bytesToString();
|
|
throw VolcanoAIException(json.decode(error)['error']?['message'] ??
|
|
'Unknown error occurred');
|
|
}
|
|
|
|
await for (final chunk in response.stream
|
|
.transform(utf8.decoder)
|
|
.transform(const LineSplitter())) {
|
|
if (chunk.isEmpty) continue;
|
|
if (chunk.startsWith('data: ')) {
|
|
final data = chunk.substring(6);
|
|
if (data == '[DONE]') break;
|
|
|
|
try {
|
|
final jsonData = json.decode(data);
|
|
if (jsonData['choices'] != null &&
|
|
jsonData['choices'].isNotEmpty &&
|
|
jsonData['choices'][0]['delta'] != null &&
|
|
jsonData['choices'][0]['delta']['content'] != null) {
|
|
yield jsonData['choices'][0]['delta']['content'] as String;
|
|
}
|
|
} catch (e) {
|
|
// 忽略无效的JSON数据
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (e is VolcanoAIException) rethrow;
|
|
throw VolcanoAIException('Failed to communicate with AI service: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
class VolcanoAIException implements Exception {
|
|
final String message;
|
|
VolcanoAIException(this.message);
|
|
|
|
@override
|
|
String toString() => message;
|
|
}
|
|
|