| import 'dart:convert'; |
| import 'package:http/http.dart' as http; |
| import '../config/app_config.dart'; |
| import '../models/persona.dart'; |
| import '../models/pictogram.dart'; |
| import '../models/prediction.dart'; |
|
|
| class ApiException implements Exception { |
| final int statusCode; |
| final String message; |
| ApiException(this.statusCode, this.message); |
| @override |
| String toString() => 'ApiException: $statusCode - $message'; |
| } |
|
|
| class ApiTimeoutException implements Exception { |
| final String message; |
| ApiTimeoutException(this.message); |
| @override |
| String toString() => 'ApiTimeoutException: $message'; |
| } |
|
|
| class HealthStatus { |
| final bool isOk; |
| final bool modelLoaded; |
| HealthStatus(this.isOk, this.modelLoaded); |
| } |
|
|
| class AnkahiApi { |
| final String baseUrl = AppConfig.apiBaseUrl; |
|
|
| Future<PredictionResult> predict( |
| List<String> sequence, |
| String personaId, { |
| String? context, |
| String? audioHint |
| }) async { |
| final response = await http.post( |
| Uri.parse('$baseUrl/predict'), |
| headers: {'Content-Type': 'application/json'}, |
| body: jsonEncode({ |
| 'pictogram_sequence': sequence, |
| 'persona_id': personaId, |
| 'context': context ?? 'daily', |
| 'audio_transcript': audioHint, |
| }), |
| ).timeout(AppConfig.apiTimeout, onTimeout: () { |
| throw ApiTimeoutException('Prediction timed out'); |
| }); |
|
|
| if (response.statusCode == 200) { |
| return PredictionResult.fromJson(jsonDecode(response.body)); |
| } else { |
| throw ApiException(response.statusCode, response.body); |
| } |
| } |
|
|
| Future<String> speak(String text, String personaId, String language) async { |
| final response = await http.post( |
| Uri.parse('$baseUrl/speak'), |
| headers: {'Content-Type': 'application/json'}, |
| body: jsonEncode({ |
| 'text': text, |
| 'persona_id': personaId, |
| 'language': language, |
| }), |
| ).timeout(AppConfig.apiTimeout, onTimeout: () { |
| throw ApiTimeoutException('Speak request timed out'); |
| }); |
|
|
| if (response.statusCode == 200) { |
| final data = jsonDecode(response.body); |
| return '$baseUrl${data['audio_url']}'; |
| } else { |
| throw ApiException(response.statusCode, response.body); |
| } |
| } |
|
|
| Future<void> logUtterance( |
| List<String> seq, |
| String confirmed, |
| String personaId, { |
| bool wasFirst = true |
| }) async { |
| try { |
| await http.post( |
| Uri.parse('$baseUrl/utterances/log'), |
| headers: {'Content-Type': 'application/json'}, |
| body: jsonEncode({ |
| 'pictogram_sequence': seq, |
| 'confirmed_sentence': confirmed, |
| 'persona_id': personaId, |
| 'was_first_alternative': wasFirst, |
| }), |
| ).timeout(const Duration(seconds: 10)); |
| } catch (e) { |
| |
| print('Failed to log utterance: $e'); |
| } |
| } |
|
|
| Future<List<PersonaModel>> getPersonas() async { |
| final response = await http.get(Uri.parse('$baseUrl/personas')) |
| .timeout(AppConfig.apiTimeout); |
|
|
| if (response.statusCode == 200) { |
| final List<dynamic> data = jsonDecode(response.body); |
| return data.map((json) => PersonaModel.fromJson(json)).toList(); |
| } else { |
| throw ApiException(response.statusCode, response.body); |
| } |
| } |
|
|
| Future<bool> switchPersona(String personaId) async { |
| final response = await http.post(Uri.parse('$baseUrl/personas/switch/$personaId')) |
| .timeout(AppConfig.apiTimeout); |
|
|
| if (response.statusCode == 200) { |
| return true; |
| } else { |
| throw ApiException(response.statusCode, response.body); |
| } |
| } |
|
|
| Future<List<PictogramModel>> searchSymbols( |
| String query, { |
| String language = 'en', |
| int limit = 20 |
| }) async { |
| final response = await http.get( |
| Uri.parse('$baseUrl/symbols/search?query=$query&language=$language&limit=$limit') |
| ).timeout(AppConfig.apiTimeout); |
|
|
| if (response.statusCode == 200) { |
| final List<dynamic> data = jsonDecode(response.body); |
| return data.map((json) => PictogramModel.fromJson(json)).toList(); |
| } else { |
| throw ApiException(response.statusCode, response.body); |
| } |
| } |
|
|
| Future<HealthStatus> getHealth() async { |
| try { |
| final response = await http.get(Uri.parse('$baseUrl/health')) |
| .timeout(const Duration(seconds: 5)); |
| if (response.statusCode == 200) { |
| final data = jsonDecode(response.body); |
| return HealthStatus(true, data['model_loaded']); |
| } |
| } catch (e) { |
| print('Health check failed: $e'); |
| } |
| return HealthStatus(false, false); |
| } |
| } |
|
|