Merge pull request #47 from yaso-meth/Feature-Mzansi-Ai-Package

Feature-Mzansi-Ai-Package
This commit is contained in:
yaso-meth
2025-01-24 13:30:50 +02:00
committed by GitHub
10 changed files with 489 additions and 11 deletions

View File

@@ -1,4 +1,3 @@
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_action.dart';
import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_tools.dart';
import 'package:flutter/material.dart';
@@ -8,7 +7,7 @@ import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
class MihApp extends StatefulWidget {
final MihAppAction appActionButton;
final MihAppTools appTools;
final List<MihAppToolBody> appBody;
final List<Widget> appBody;
int selectedbodyIndex;
final onIndexChange;
MihApp({
@@ -34,6 +33,7 @@ class _MihAppState extends State<MihApp> {
width: screenSize.width,
height: screenSize.height,
//color: Colors.black,
padding: const EdgeInsets.only(top: 5),
child: Column(
children: [
Row(

View File

@@ -31,6 +31,7 @@ abstract class AppEnviroment {
{
baseApiUrl = "https://api.mzansi-innovation-hub.co.za";
baseFileUrl = "https://minio.mzansi-innovation-hub.co.za";
baseAiUrl = "https://ai.mzansi-innovation-hub.co.za";
//fingerPrintPluginKey = 'h5X7a5j14iUZCobI1ZeX';
break;
}

View File

@@ -228,6 +228,7 @@ class _MIHHomeState extends State<MIHHome> {
onTap: () {
Navigator.of(context).pushNamed(
'/mzansi-ai',
arguments: widget.signedInUser,
);
},
tileName: "Mzansi AI",

View File

@@ -0,0 +1,252 @@
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_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<AiChat> createState() => _AiChatState();
}
class _AiChatState extends State<AiChat> {
TextEditingController _modelCopntroller = TextEditingController();
List<types.Message> _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<ollama.Message> _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 {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
types.TextMessage textMessage;
String aiResponse = "";
await _generateChatCompletionWithHistory(userMessage, client)
.then((response) {
aiResponse = response.split("</think>").last.trim();
});
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();
}
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<String, dynamic>))
.toList();
setState(() {
_messages = messages;
});
}
Future<String> _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;
}
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 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: [
Text(
"Chat with 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),
),
],
),
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(),
),
)
],
),
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app.dart';
import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_action.dart';
import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_tools.dart';
import 'package:Mzansi_Innovation_Hub/mih_objects/app_user.dart';
import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_ai/ai_chat.dart';
import 'package:flutter/material.dart';
class MzansiAi extends StatefulWidget {
final AppUser signedInUser;
const MzansiAi({
super.key,
required this.signedInUser,
});
@override
State<MzansiAi> createState() => _MzansiAiState();
}
class _MzansiAiState extends State<MzansiAi> {
int _selcetedIndex = 0;
MihAppAction getAction() {
return MihAppAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: AuthArguments(true, false),
);
},
);
}
MihAppTools getTools() {
Map<Widget, void Function()?> temp = {};
temp[const Icon(Icons.chat)] = () {
setState(() {
_selcetedIndex = 0;
});
};
return MihAppTools(
tools: temp,
selcetedIndex: _selcetedIndex,
);
}
List<Widget> getToolBody() {
List<Widget> toolBodies = [
AiChat(signedInUser: widget.signedInUser),
];
return toolBodies;
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MihApp(
appActionButton: getAction(),
appTools: getTools(),
appBody: getToolBody(),
selectedbodyIndex: _selcetedIndex,
onIndexChange: (newValue) {
setState(() {
_selcetedIndex = newValue;
});
print("Index: $_selcetedIndex");
},
);
}
}

View File

@@ -2,6 +2,7 @@ import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/test/package_te
import 'package:Mzansi_Innovation_Hub/mih_packages/calculator/calculator.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_privacy_polocy.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_terms_of_service.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_ai/mzansi_ai.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_wallet/mih_barcode_scanner.dart';
import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_wallet/mzansi_wallet.dart';
import 'package:flutter/material.dart';
@@ -293,14 +294,20 @@ class RouteGenerator {
//===============================================================
//Calculator
//Mzansi AI
case '/mzansi-ai':
if (args is AppUser) {
return MaterialPageRoute(
settings: settings,
builder: (_) => MzansiAi(
signedInUser: args,
),
);
}
return _errorRoute();
//===============================================================
// case '/mzansi-ai':
// return MaterialPageRoute(
// settings: settings,
// builder: (_) => const MzansiAi(),
// );
//Package Ttemplate Test
case '/package-dev':
return MaterialPageRoute(
settings: settings,

View File

@@ -297,6 +297,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
diffutil_dart:
dependency: transitive
description:
name: diffutil_dart
sha256: "5e74883aedf87f3b703cb85e815bdc1ed9208b33501556e4a8a5572af9845c81"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
dio:
dependency: transitive
description:
@@ -313,6 +321,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
equatable:
dependency: transitive
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
@@ -406,6 +422,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_chat_types:
dependency: "direct main"
description:
name: flutter_chat_types
sha256: e285b588f6d19d907feb1f6d912deaf22e223656769c34093b64e1c59b094fb9
url: "https://pub.dev"
source: hosted
version: "3.6.2"
flutter_chat_ui:
dependency: "direct main"
description:
name: flutter_chat_ui
sha256: "168a4231464ad00a17ea5f0813f1b58393bdd4035683ea4dc37bbe26be62891e"
url: "https://pub.dev"
source: hosted
version: "1.6.15"
flutter_launcher_icons:
dependency: "direct main"
description:
@@ -414,6 +446,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_link_previewer:
dependency: transitive
description:
name: flutter_link_previewer
sha256: "007069e60f42419fb59872beb7a3cc3ea21e9f1bdff5d40239f376fa62ca9f20"
url: "https://pub.dev"
source: hosted
version: "3.2.2"
flutter_linkify:
dependency: transitive
description:
name: flutter_linkify
sha256: "74669e06a8f358fee4512b4320c0b80e51cffc496607931de68d28f099254073"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -430,6 +478,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.1"
flutter_parsed_text:
dependency: transitive
description:
name: flutter_parsed_text
sha256: "529cf5793b7acdf16ee0f97b158d0d4ba0bf06e7121ef180abe1a5b59e32c1e2"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -656,6 +712,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
linkify:
dependency: transitive
description:
name: linkify
sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
lints:
dependency: transitive
description:
@@ -912,6 +976,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.2"
photo_view:
dependency: transitive
description:
name: photo_view
sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
platform:
dependency: transitive
description:
@@ -984,6 +1056,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
scroll_to_index:
dependency: transitive
description:
name: scroll_to_index
sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176
url: "https://pub.dev"
source: hosted
version: "3.0.1"
shared_preferences:
dependency: transitive
description:
@@ -1342,7 +1422,7 @@ packages:
source: hosted
version: "3.1.3"
uuid:
dependency: transitive
dependency: "direct main"
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
@@ -1357,6 +1437,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
visibility_detector:
dependency: transitive
description:
name: visibility_detector
sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420
url: "https://pub.dev"
source: hosted
version: "0.4.0+2"
vm_service:
dependency: transitive
description:

View File

@@ -70,6 +70,9 @@ dependencies:
local_auth: ^2.3.0
math_expressions: ^2.6.0
ollama_dart: ^0.2.2+1
flutter_chat_ui: ^1.6.15
flutter_chat_types: ^3.6.2
uuid: ^4.5.1
dev_dependencies:
flutter_test:

View File

@@ -114,7 +114,7 @@ services:
- certbotConf:/etc/letsencrypt
- certbotChall:/var/www/certbot
#command: certonly --test-cert --webroot -w /var/www/certbot --force-renewal --email yasienmeth@gmail.com -d mzansi-innovation-hub.co.za -d www.mzansi-innovation-hub.co.za --agree-tos
command: certonly --webroot -w /var/www/certbot --force-renewal --email yasienmeth@gmail.com -d app.mzansi-innovation-hub.co.za -d api.mzansi-innovation-hub.co.za -d minio.mzansi-innovation-hub.co.za -d monitor.mzansi-innovation-hub.co.za --agree-tos
command: certonly --webroot -w /var/www/certbot --force-renewal --email yasienmeth@gmail.com -d app.mzansi-innovation-hub.co.za -d api.mzansi-innovation-hub.co.za -d minio.mzansi-innovation-hub.co.za -d monitor.mzansi-innovation-hub.co.za -d ai.mzansi-innovation-hub.co.za --agree-tos
networks:
- MIH-network
depends_on:

View File

@@ -116,6 +116,54 @@ http {
}
}
#================AI Server================
server {
listen 80;
server_name ai.mzansi-innovation-hub.co.za;
# # #Web App
# location / {
# proxy_pass http://MIH-API-Hub:8080/;
# }
# location ~ /.well-known/acme-challenge/ {
# root /var/www/certbot;
# }
return 301 https://ai.mzansi-innovation-hub.co.za$request_uri;
}
server {
listen 443 ssl;
http2 on;
# use the certificates
ssl_certificate /etc/letsencrypt/live/app.mzansi-innovation-hub.co.za/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.mzansi-innovation-hub.co.za/privkey.pem;
server_name ai.mzansi-innovation-hub.co.za;
root /var/www/html;
index index.php index.html index.htm;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
proxy_request_buffering off;
# Web Api
location / {
proxy_pass http://MIH-AI:11434/;
}
location ~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
#================Monitor Server================
server {
listen 80;