// ignore_for_file: unused_import, unnecessary_import import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'gateway_runtime.dart'; import 'runtime_models.dart'; import 'secure_config_store.dart'; import 'runtime_controllers_settings.dart'; import 'runtime_controllers_entities.dart'; import 'runtime_controllers_derived_tasks.dart'; class AiGatewayResponseExceptionInternal implements Exception { const AiGatewayResponseExceptionInternal({ required this.statusCode, required this.message, }); final int statusCode; final String message; } class GatewayAgentsController extends ChangeNotifier { GatewayAgentsController(this.runtimeInternal); final GatewayRuntime runtimeInternal; List agentsInternal = const []; String selectedAgentIdInternal = ''; bool loadingInternal = false; String? errorInternal; List get agents => agentsInternal; String get selectedAgentId => selectedAgentIdInternal; bool get loading => loadingInternal; String? get error => errorInternal; GatewayAgentSummary? get selectedAgent { final selected = selectedAgentIdInternal.trim(); if (selected.isEmpty) { return null; } for (final agent in agentsInternal) { if (agent.id == selected) { return agent; } } return null; } String get activeAgentName => selectedAgent?.name ?? 'Main'; void restoreSelection(String agentId) { selectedAgentIdInternal = agentId.trim(); notifyListeners(); } void selectAgent(String? agentId) { selectedAgentIdInternal = agentId?.trim() ?? ''; notifyListeners(); } Future refresh() async { if (!runtimeInternal.isConnected) { agentsInternal = const []; errorInternal = null; notifyListeners(); return; } loadingInternal = true; errorInternal = null; notifyListeners(); try { agentsInternal = await runtimeInternal.listAgents(); if (selectedAgentIdInternal.isNotEmpty && !agentsInternal.any((item) => item.id == selectedAgentIdInternal)) { selectedAgentIdInternal = ''; } } catch (error) { errorInternal = error.toString(); } finally { loadingInternal = false; notifyListeners(); } } } class GatewaySessionsController extends ChangeNotifier { GatewaySessionsController(this.runtimeInternal); final GatewayRuntime runtimeInternal; List sessionsInternal = const []; String currentSessionKeyInternal = ''; String selectedAgentIdInternal = ''; String defaultAgentIdInternal = ''; bool loadingInternal = false; String? errorInternal; List get sessions => sessionsInternal; String get currentSessionKey => currentSessionKeyInternal; bool get loading => loadingInternal; String? get error => errorInternal; void configure({ required String selectedAgentId, required String defaultAgentId, }) { selectedAgentIdInternal = selectedAgentId.trim(); defaultAgentIdInternal = defaultAgentId.trim(); notifyListeners(); } Future refresh() async { if (!runtimeInternal.isConnected) { sessionsInternal = const []; errorInternal = null; notifyListeners(); return; } loadingInternal = true; errorInternal = null; notifyListeners(); try { sessionsInternal = await runtimeInternal.listSessions(limit: 50); } catch (error) { errorInternal = error.toString(); } finally { loadingInternal = false; notifyListeners(); } } Future switchSession(String sessionKey) async { final trimmed = sessionKey.trim(); if (trimmed.isEmpty || trimmed == currentSessionKeyInternal) { return; } currentSessionKeyInternal = trimmed; notifyListeners(); } } class GatewayChatController extends ChangeNotifier { GatewayChatController(this.runtimeInternal); final GatewayRuntime runtimeInternal; List messagesInternal = const []; String sessionKeyInternal = 'main'; bool loadingInternal = false; String? errorInternal; String? streamingAssistantTextInternal; final Set pendingRunsInternal = {}; List get messages => messagesInternal; String get sessionKey => sessionKeyInternal; bool get loading => loadingInternal; String? get error => errorInternal; String? get streamingAssistantText => streamingAssistantTextInternal; bool get hasPendingRun => pendingRunsInternal.isNotEmpty; String? get activeRunId => pendingRunsInternal.isEmpty ? null : pendingRunsInternal.first; Future loadSession(String sessionKey) async { final next = sessionKey.trim().isEmpty ? 'main' : sessionKey.trim(); sessionKeyInternal = next; messagesInternal = const []; streamingAssistantTextInternal = null; if (!runtimeInternal.isConnected) { errorInternal = null; notifyListeners(); return; } loadingInternal = true; errorInternal = null; notifyListeners(); try { messagesInternal = await runtimeInternal.loadHistory(next); } catch (error) { messagesInternal = const []; errorInternal = error.toString(); } finally { loadingInternal = false; notifyListeners(); } } void handleEvent(GatewayPushEvent event) { if (event.event == 'chat.run') { handleChatRunEventInternal(asMap(event.payload)); return; } if (event.event == 'chat') { handleChatEventInternal(asMap(event.payload)); return; } if (event.event == 'agent') { handleAgentEventInternal(asMap(event.payload)); } } void clear() { messagesInternal = const []; pendingRunsInternal.clear(); streamingAssistantTextInternal = null; errorInternal = null; notifyListeners(); } void resetSession(String sessionKey) { sessionKeyInternal = sessionKey.trim().isEmpty ? 'main' : sessionKey.trim(); messagesInternal = const []; pendingRunsInternal.clear(); streamingAssistantTextInternal = null; errorInternal = null; notifyListeners(); } void handleChatRunEventInternal(Map payload) { final runId = stringValue(payload['runId']); final state = stringValue(payload['state']) ?? ''; final incomingSessionKey = stringValue(payload['sessionKey']) ?? sessionKeyInternal; final isOurRun = runId != null && pendingRunsInternal.contains(runId); if (!matchesSessionKey(incomingSessionKey, sessionKeyInternal) && !isOurRun) { return; } final assistantText = stringValue(payload['assistantText']) ?? ''; if (assistantText.isNotEmpty && (state == 'delta' || state == 'final')) { streamingAssistantTextInternal = assistantText; } if (state == 'error') { errorInternal = stringValue(payload['errorMessage']) ?? 'Chat failed'; } final terminal = boolValue(payload['terminal']) ?? false || state == 'final' || state == 'aborted' || state == 'error'; if (terminal) { if (runId != null) { pendingRunsInternal.remove(runId); } else { pendingRunsInternal.clear(); } unawaited(loadSession(sessionKeyInternal)); notifyListeners(); return; } notifyListeners(); } void handleChatEventInternal(Map payload) { final message = asMap(payload['message']); final role = (stringValue(message['role']) ?? '').toLowerCase(); handleChatRunEventInternal({ 'runId': payload['runId'], 'sessionKey': payload['sessionKey'], 'state': payload['state'], if (role == 'assistant') 'assistantText': extractMessageText(message), 'errorMessage': payload['errorMessage'], 'terminal': false, }); } void handleAgentEventInternal(Map payload) { final runId = stringValue(payload['runId']); if (runId == null || !pendingRunsInternal.contains(runId)) { return; } final stream = stringValue(payload['stream']); final data = asMap(payload['data']); if (stream == 'assistant') { final nextText = stringValue(data['text']) ?? extractMessageText(data); if (nextText.isNotEmpty) { handleChatRunEventInternal({ 'runId': runId, 'sessionKey': payload['sessionKey'] ?? data['sessionKey'], 'state': 'delta', 'assistantText': nextText, 'source': 'agent', 'terminal': false, }); } } } }