import 'dart:convert'; import 'package:Mzansi_Innovation_Hub/main.dart'; import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart'; import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih-app_tool_body.dart'; import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_window.dart'; import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:Mzansi_Innovation_Hub/mih_env/env.dart'; import 'package:Mzansi_Innovation_Hub/mih_objects/app_user.dart'; import 'package:flutter/material.dart'; import 'package:flutter_chat_ui/flutter_chat_ui.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'package:flutter/services.dart' show rootBundle; import 'package:ollama_dart/ollama_dart.dart' as ollama; import 'package:uuid/uuid.dart'; class AiChat extends StatefulWidget { final AppUser signedInUser; const AiChat({ super.key, required this.signedInUser, }); @override State createState() => _AiChatState(); } class _AiChatState extends State { TextEditingController _modelCopntroller = TextEditingController(); final ValueNotifier _showModelOptions = ValueNotifier(false); List _messages = []; late types.User _user; late types.User _mihAI; String systemPromt = "You are a helpful and friendly AI assistant. You are running on a system called MIH which was created by \"Mzansi Innovation Hub\" a South African based company."; final client = ollama.OllamaClient( baseUrl: "${AppEnviroment.baseAiUrl}/api", ); List _chatHistory = []; void _addMessage(types.Message message) { setState(() { _messages.insert(0, message); }); } void _handleSendPressed(types.PartialText message) { final textMessage = types.TextMessage( author: _user, createdAt: DateTime.now().millisecondsSinceEpoch, id: const Uuid().v4(), text: message.text, ); //Add user prompt to history setState(() { _chatHistory.add( ollama.Message( role: ollama.MessageRole.user, content: message.text, ), ); }); _addMessage(textMessage); _handleMessageBack(message.text); } void _handleMessageBack(String userMessage) async { // types.TextMessage textMessage; // String aiResponse = ""; // final aiResponseStream = showDialog( context: context, builder: (context) { return const Mihloadingcircle(); }, ); _generateChatCompletionWithHistoryStream(userMessage, client) .listen((response) { //aiResponse = response; //.split("").last.trim(); }); Navigator.of(context).pop(); showDialog( context: context, builder: (context) { return responseWindow(); }, ); // setState(() { // _chatHistory.add( // ollama.Message( // role: ollama.MessageRole.assistant, // content: aiResponse, // ), // ); // }); // textMessage = types.TextMessage( // author: _mihAI, // createdAt: DateTime.now().millisecondsSinceEpoch, // id: const Uuid().v4(), // text: aiResponse //message.text, // ); // _addMessage(textMessage); // print(_chatHistory.toString()); // Navigator.of(context).pop(); } Widget responseWindow() { types.TextMessage textMessage; return StreamBuilder( stream: _generateChatCompletionWithHistoryStream("Hello", client), builder: (context, snapshot) { if (snapshot.hasData) { return MihAppWindow( fullscreen: false, windowTitle: 'Mzansi AI Thoughts', windowTools: [ IconButton( onPressed: () { setState(() { _chatHistory.add( ollama.Message( role: ollama.MessageRole.assistant, content: snapshot.requireData, ), ); }); textMessage = types.TextMessage( author: _mihAI, createdAt: DateTime.now().millisecondsSinceEpoch, id: const Uuid().v4(), // metadata: { // "thoughts": snapshot.requireData // }, text: snapshot.requireData .replaceAll("\n\n", "Thinking:\n") .replaceAll( "\n\n", "Answer:\n"), //message.text, ); _addMessage(textMessage); Navigator.of(context).pop(); }, icon: const Icon(Icons.arrow_back), ), ], onWindowTapClose: () { Navigator.of(context).pop(); }, windowBody: [ Text( snapshot.requireData, textAlign: TextAlign.left, style: TextStyle( color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ); } else { return MihAppWindow( fullscreen: false, windowTitle: 'Mzansi AI Thoughts', windowTools: [], onWindowTapClose: () { Navigator.of(context).pop(); }, windowBody: const [ Mihloadingcircle(), ], ); } }, ); } void _loadMessages() async { final response = await rootBundle.loadString('assets/messages.json'); final messages = (jsonDecode(response) as List) .map((e) => types.Message.fromJson(e as Map)) .toList(); setState(() { _messages = messages; }); } // Future _generateChatCompletionWithHistory( // String userMessage, // final ollama.OllamaClient client, // ) async { // final generated = await client.generateChatCompletion( // request: ollama.GenerateChatCompletionRequest( // model: _modelCopntroller.text, // messages: _chatHistory, // ), // ); // return generated.message.content; // } Stream _generateChatCompletionWithHistoryStream( String userMessage, final ollama.OllamaClient client, ) async* { final aiStream = client.generateChatCompletionStream( request: ollama.GenerateChatCompletionRequest( model: _modelCopntroller.text, messages: _chatHistory, ), ); String text = ''; await for (final res in aiStream) { text += (res.message.content); yield text; } // print(text); } void _resetChat() { setState(() { _messages = []; _chatHistory = []; _loadMessages(); }); // Navigator.of(context).popAndPushNamed( // '/mzansi-ai', // arguments: widget.signedInUser, // ); } ChatTheme getChatTheme() { return DarkChatTheme( backgroundColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), inputBackgroundColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), inputTextColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), inputTextCursorColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), primaryColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), secondaryColor: MzanziInnovationHub.of(context)!.theme.successColor(), errorColor: MzanziInnovationHub.of(context)!.theme.errorColor(), sentMessageBodyTextStyle: TextStyle( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), fontSize: 17, fontWeight: FontWeight.w500, fontFamily: 'Segoe UI', ), receivedMessageBodyTextStyle: TextStyle( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), fontSize: 17, fontWeight: FontWeight.w500, fontFamily: 'Segoe UI', ), emptyChatPlaceholderTextStyle: TextStyle( color: MzanziInnovationHub.of(context)!.theme.messageTextColor(), fontSize: 17, fontWeight: FontWeight.w500, fontFamily: 'Segoe UI', ), ); } @override void dispose() { // TODO: implement dispose super.dispose(); _modelCopntroller.dispose(); client.endSession(); } @override void initState() { super.initState(); _user = types.User( firstName: widget.signedInUser.fname, id: widget.signedInUser.app_id, //'82091008-a484-4a89-ae75-a22bf8d6f3ac', ); _mihAI = types.User( firstName: "Mzansi AI", id: const Uuid().v4(), ); _modelCopntroller.text = 'deepseek-r1:1.5b'; // _chatHistory.add( // ollama.Message( // role: ollama.MessageRole.system, // content: systemPromt, // ), // ); _loadMessages(); } @override Widget build(BuildContext context) { return MihAppToolBody( borderOn: false, bodyItem: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: () { if (_showModelOptions.value == true) { setState(() { _showModelOptions.value = false; }); } else { setState(() { _showModelOptions.value = true; }); } }, icon: const Icon(Icons.settings), ), Text( "Mzansi AI", textAlign: TextAlign.center, style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), ), ), IconButton( onPressed: () { _resetChat(); }, icon: const Icon(Icons.refresh), ), ], ), ValueListenableBuilder( valueListenable: _showModelOptions, builder: (BuildContext context, bool value, Widget? child) { return Visibility( visible: value, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 25), child: SizedBox( width: 300, child: MIHDropdownField( controller: _modelCopntroller, hintText: "AI Model", dropdownOptions: const ['deepseek-r1:1.5b'], required: true, editable: true, ), ), ) ], ), ); }, ), Expanded( child: Chat( messages: _messages, // onAttachmentPressed: _handleAttachmentPressed, // onMessageTap: _handleMessageTap, // onPreviewDataFetched: _handlePreviewDataFetched, onSendPressed: _handleSendPressed, showUserAvatars: false, showUserNames: false, user: _user, theme: getChatTheme(), ), ) ], ), ); } }