Files

143 lines
4.2 KiB
Dart

import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:social_app/core/chat/chat_api.dart';
import 'package:social_app/data/network/i_api_client.dart';
class ChatApiImpl implements ChatApi {
ChatApiImpl(this._apiClient);
final IApiClient _apiClient;
@override
Future<Map<String, dynamic>> createRun(Map<String, dynamic> runInput) async {
final response = await _apiClient.post<Map<String, dynamic>>(
'/api/v1/agent/runs',
data: runInput,
);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/runs response');
}
return payload;
}
@override
Future<Stream<String>> streamRunEvents(
String threadId, {
required String runId,
String? lastEventId,
}) {
final headers = <String, String>{'Accept': 'text/event-stream'};
if (lastEventId != null && lastEventId.isNotEmpty) {
headers['Last-Event-ID'] = lastEventId;
}
final encodedRunId = Uri.encodeQueryComponent(runId);
final path = '/api/v1/agent/runs/$threadId/events?runId=$encodedRunId';
return _apiClient.getSseLines(path, headers: headers);
}
@override
Future<Map<String, dynamic>> fetchHistory({
String? threadId,
DateTime? beforeDate,
}) async {
final path = _buildHistoryPath(threadId: threadId, beforeDate: beforeDate);
final response = await _apiClient.get<Map<String, dynamic>>(path);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/history response');
}
return payload;
}
@override
Future<Map<String, dynamic>> uploadAttachment({
required String threadId,
required String filename,
required String mimeType,
required Uint8List bytes,
}) async {
final formData = FormData.fromMap({
'threadId': threadId,
'file': MultipartFile.fromBytes(
bytes,
filename: filename,
contentType: DioMediaType.parse(mimeType),
),
});
final response = await _apiClient.post<Map<String, dynamic>>(
'/api/v1/agent/attachments',
data: formData,
);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/attachments response');
}
return payload;
}
@override
Future<Uint8List> fetchAttachmentPreview(String previewPath) async {
final response = await _apiClient.get<List<int>>(
previewPath,
options: Options(responseType: ResponseType.bytes),
);
final payload = response.data;
if (payload is! List<int>) {
throw StateError('Invalid attachment payload');
}
return Uint8List.fromList(payload);
}
@override
Future<String> transcribeAudio(String filePath) async {
final formData = FormData.fromMap({
'audio': await MultipartFile.fromFile(
filePath,
filename: 'recording.wav',
contentType: DioMediaType('audio', 'wav'),
),
});
final response = await _apiClient.post<Map<String, dynamic>>(
'/api/v1/agent/transcribe',
data: formData,
);
final payload = response.data;
if (payload is! Map<String, dynamic>) {
throw StateError('Invalid /agent/transcribe response');
}
final transcript = payload['transcript'];
if (transcript is! String) {
throw StateError('Missing transcript in /agent/transcribe response');
}
return transcript;
}
@override
Future<void> cancelRun({
required String threadId,
required String runId,
}) async {
final encodedRunId = Uri.encodeQueryComponent(runId);
await _apiClient.post<Map<String, dynamic>>(
'/api/v1/agent/runs/$threadId/cancel?runId=$encodedRunId',
);
}
static String _buildHistoryPath({String? threadId, DateTime? beforeDate}) {
final query = <String>[];
if (threadId != null && threadId.isNotEmpty) {
query.add('threadId=${Uri.encodeQueryComponent(threadId)}');
}
if (beforeDate != null) {
final day = DateTime(beforeDate.year, beforeDate.month, beforeDate.day);
query.add('before=${day.toIso8601String().substring(0, 10)}');
}
if (query.isEmpty) {
return '/api/v1/agent/history';
}
return '/api/v1/agent/history?${query.join('&')}';
}
}