107 lines
3.4 KiB
Dart
107 lines
3.4 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:manager_api_new/api.dart';
|
|
import 'package:mymuseum_visitapp/Models/visitContext.dart';
|
|
import 'package:mymuseum_visitapp/Models/AssistantResponse.dart';
|
|
|
|
class AssistantService {
|
|
final VisitAppContext visitAppContext;
|
|
|
|
/// Nombre maximum de messages conservés dans l'historique envoyé au backend.
|
|
final int maxHistory;
|
|
|
|
/// Durée d'inactivité après laquelle l'historique est automatiquement vidé.
|
|
/// null = pas de vidage automatique.
|
|
final Duration? inactivityTimeout;
|
|
|
|
final List<AiChatMessage> _history = [];
|
|
Timer? _inactivityTimer;
|
|
|
|
AssistantService({
|
|
required this.visitAppContext,
|
|
this.maxHistory = 10,
|
|
this.inactivityTimeout = const Duration(minutes: 5),
|
|
});
|
|
|
|
Future<AssistantResponse> chat({
|
|
required String message,
|
|
String? configurationId,
|
|
bool isVoice = false,
|
|
}) => chatWithAppType(message: message, configurationId: configurationId, isVoice: isVoice);
|
|
|
|
Future<AssistantResponse> chatWithAppType({
|
|
required String message,
|
|
String? configurationId,
|
|
AppType appType = AppType.Mobile,
|
|
bool isVoice = false,
|
|
}) async {
|
|
_resetInactivityTimer();
|
|
|
|
final request = AiChatRequest(
|
|
message: message,
|
|
instanceId: visitAppContext.instanceId,
|
|
appType: appType,
|
|
configurationId: configurationId,
|
|
language: visitAppContext.language?.toUpperCase() ?? 'FR',
|
|
history: List.from(_history),
|
|
isVoice: isVoice,
|
|
);
|
|
|
|
final response = await visitAppContext.clientAPI.aiApi!.aiChat(request);
|
|
|
|
if (response == null) {
|
|
throw Exception('Empty response from assistant');
|
|
}
|
|
|
|
debugPrint("AI raw response: reply='${response.reply}' navigation.sectionId='${response.navigation?.sectionId}' navigation.sectionTitle='${response.navigation?.sectionTitle}' navigation.sectionType='${response.navigation?.sectionType}' cards=${response.cards?.length}");
|
|
|
|
final result = AssistantResponse(
|
|
reply: response.reply ?? '',
|
|
cards: response.cards
|
|
?.map((c) => AiCard(
|
|
title: c.title ?? '',
|
|
subtitle: c.subtitle ?? '',
|
|
icon: c.icon,
|
|
))
|
|
.toList(),
|
|
navigation: response.navigation != null
|
|
? AssistantNavigationAction(
|
|
sectionId: response.navigation!.sectionId ?? '',
|
|
sectionTitle: response.navigation!.sectionTitle ?? '',
|
|
sectionType: response.navigation!.sectionType ?? '',
|
|
imageUrl: response.navigation!.imageUrl,
|
|
)
|
|
: null,
|
|
expectsReply: response.expectsReply ?? true,
|
|
);
|
|
|
|
_history.add(AiChatMessage(role: 'user', content: message));
|
|
_history.add(AiChatMessage(role: 'assistant', content: result.reply));
|
|
|
|
// Cap local — inutile de garder plus que maxHistory côté client
|
|
if (_history.length > maxHistory) {
|
|
_history.removeRange(0, _history.length - maxHistory);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void _resetInactivityTimer() {
|
|
if (inactivityTimeout == null) return;
|
|
_inactivityTimer?.cancel();
|
|
_inactivityTimer = Timer(inactivityTimeout!, () {
|
|
debugPrint('[AssistantService] Inactivity timeout — clearing history');
|
|
clearHistory();
|
|
});
|
|
}
|
|
|
|
void clearHistory() {
|
|
_history.clear();
|
|
_inactivityTimer?.cancel();
|
|
}
|
|
|
|
void dispose() {
|
|
_inactivityTimer?.cancel();
|
|
}
|
|
}
|