QOL: Mzansi AI Enhancement pt1

This commit is contained in:
2025-11-27 09:48:42 +02:00
parent 08bfe1d417
commit cc3f18f7e2
10 changed files with 231 additions and 176 deletions

View File

@@ -19,7 +19,10 @@ class MzansiAiProvider extends ChangeNotifier {
}) {
ollamaProvider = OllamaProvider(
baseUrl: "${AppEnviroment.baseAiUrl}/api",
model: AppEnviroment.getEnv() == "Prod" ? 'gemma3n:e4b' : "gemma3:1b",
model: AppEnviroment.getEnv() == "Prod"
? 'gemma3n:e4b'
: "qwen3-vl:2b-instruct",
think: false,
systemPrompt: "You are Mzansi AI, a helpful and friendly AI assistant running on the 'MIH App'.\n" +
"The MIH App was created by 'Mzansi Innovation Hub', a South African-based startup company." +
"Your primary purpose is to assist users by answering general questions and helping with creative writing tasks or any other task a user might have for you.\n" +
@@ -147,9 +150,6 @@ class MzansiAiProvider extends ChangeNotifier {
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
progressIndicatorColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
menuColor: Colors.black,
// MihColors.getGreenColor(
// MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
disabledButtonStyle: ActionButtonStyle(
icon: MihIcons.mzansiAi,
iconColor: MihColors.getSecondaryColor(
@@ -323,6 +323,21 @@ class MzansiAiProvider extends ChangeNotifier {
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
addButtonStyle: ActionButtonStyle(
iconDecoration: BoxDecoration(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
borderRadius: BorderRadius.circular(25),
),
iconColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
textStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
menuColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
}
}

View File

@@ -4,14 +4,16 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_ai_toolkit/flutter_ai_toolkit.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:ollama_dart/ollama_dart.dart';
import 'package:cross_file/cross_file.dart';
class OllamaProvider extends LlmProvider with ChangeNotifier {
OllamaProvider({
String? baseUrl,
Map<String, String>? headers,
Map<String, String>? queryParams,
Map<String, dynamic>? queryParams,
required String model,
String? systemPrompt,
bool? think,
}) : _client = OllamaClient(
baseUrl: baseUrl,
headers: headers,
@@ -19,11 +21,13 @@ class OllamaProvider extends LlmProvider with ChangeNotifier {
),
_model = model,
_systemPrompt = systemPrompt,
_think = think,
_history = [];
final OllamaClient _client;
final String _model;
final List<ChatMessage> _history;
final String? _systemPrompt;
final bool? _think;
@override
Stream<String> generateStream(
@@ -36,6 +40,29 @@ class OllamaProvider extends LlmProvider with ChangeNotifier {
yield* _generateStream(messages);
}
Stream<String> speechToText(XFile audioFile) async* {
KenLogger.success("Inside Custom speechToText funtion");
// 1. Convert the XFile to the attachment format needed for the LLM.
final attachments = [await FileAttachment.fromFile(audioFile)];
KenLogger.success("added attachment for audio file");
// 2. Define the transcription prompt, mirroring the logic from LlmChatView.
const prompt =
'translate the attached audio to text; provide the result of that '
'translation as just the text of the translation itself. be careful to '
'separate the background audio from the foreground audio and only '
'provide the result of translating the foreground audio.';
KenLogger.success("Created Prompt");
// 3. Use your existing Ollama API call to process the prompt and attachment.
// We are essentially running a new, one-off chat session for transcription.
yield* generateStream(
prompt,
attachments: attachments,
);
KenLogger.success("done");
}
@override
Stream<String> sendMessageStream(
String prompt, {
@@ -76,6 +103,7 @@ class OllamaProvider extends LlmProvider with ChangeNotifier {
Stream<String> _generateStream(List<Message> messages) async* {
final allMessages = <Message>[];
if (_systemPrompt != null && _systemPrompt.isNotEmpty) {
KenLogger.success("Adding system prompt to the conversation");
allMessages.add(Message(
role: MessageRole.system,
content: _systemPrompt,
@@ -87,6 +115,7 @@ class OllamaProvider extends LlmProvider with ChangeNotifier {
request: GenerateChatCompletionRequest(
model: _model,
messages: allMessages,
think: _think,
),
);
// final stream = _client.generateChatCompletionStream(
@@ -109,19 +138,32 @@ class OllamaProvider extends LlmProvider with ChangeNotifier {
content: message.text ?? '',
);
}
final imageAttachments = <String>[];
final docAttachments = <String>[];
if (message.text != null && message.text!.isNotEmpty) {
docAttachments.add(message.text!);
}
for (final attachment in message.attachments) {
if (attachment is FileAttachment) {
final mimeType = attachment.mimeType.toLowerCase();
if (mimeType.startsWith('image/')) {
imageAttachments.add(base64Encode(attachment.bytes));
} else if (mimeType == 'application/pdf' ||
mimeType.startsWith('text/')) {
throw LlmFailureException(
"\n\nAww, that file is a little too advanced for us right now ($mimeType)! We're still learning, but we'll get there! Please try sending us a different file type.\n\nHint: We can handle images quite well!",
);
}
} else {
throw LlmFailureException(
'Unsupported attachment type: $attachment',
);
}
}
return Message(
role: MessageRole.user,
content: message.text ?? '',
images: [
for (final attachment in message.attachments)
if (attachment is ImageFileAttachment)
base64Encode(attachment.bytes)
else
throw LlmFailureException(
'Unsupported attachment type: $attachment',
),
],
content: docAttachments.join(' '),
images: imageAttachments,
);
case MessageOrigin.llm: