implemented basic settings into the backend, renamed files, fixed bugs, and added back button functionality
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:app/util/backend.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'chat.dart';
|
||||
|
||||
@@ -6,19 +6,30 @@ class Conversation with ChangeNotifier {
|
||||
int conversationID;
|
||||
String conversationLabel;
|
||||
String conversationSubLabel;
|
||||
bool history = true;
|
||||
// History is disabled by default. This means chats are auto-deleted once the user exits that conversation.
|
||||
bool conversationContext = true; // The prior prompts and responses are used as additional context for the next prompt and response.
|
||||
late bool stream = false; // Update the UI with a stream of characters as received from the server, or as one text.
|
||||
List<Chat> chats = [];
|
||||
|
||||
Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel);
|
||||
|
||||
Conversation.completeConversation(this.conversationID, this.conversationLabel, this.conversationSubLabel, this.chats);
|
||||
|
||||
void toggleHistory() => history = !history;
|
||||
|
||||
void toggleConversationContext() => conversationContext = !conversationContext;
|
||||
|
||||
void toggleStream() => stream = !stream;
|
||||
|
||||
void add(Chat message) {
|
||||
chats.add(message);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void loadChats() {
|
||||
chats = Backend.getChatsByConversationID(conversationID);
|
||||
Backend.loadConversation;
|
||||
chats = Backend.loadedChats;
|
||||
}
|
||||
|
||||
void setLabel() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:app/components/conversation_thread/chat_bubble_model.dart';
|
||||
import 'package:app/components/conversation_thread/chat.dart';
|
||||
import 'package:app/components/conversation_thread/conversation.dart';
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:app/util/backend.dart';
|
||||
|
||||
import 'package:app/components/prompt_box/prompt_box_widget.dart';
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:app/components/conversation_thread/conversation.dart';
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:app/util/backend.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '/components/conversation_thread/chat_bubble_widget.dart';
|
||||
|
||||
@@ -84,7 +84,7 @@ class _PopupWidgetState extends State<PopupWidget> {
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0),
|
||||
child: Text(
|
||||
'Refine the components modal.',
|
||||
'A popup',
|
||||
style: AppTheme.headlineMedium,
|
||||
),
|
||||
),
|
||||
@@ -106,12 +106,12 @@ class _PopupWidgetState extends State<PopupWidget> {
|
||||
],
|
||||
),
|
||||
const Divider(
|
||||
height: 24,
|
||||
height: 12,
|
||||
thickness: 2,
|
||||
color: Color(0xFFF1F4F8),
|
||||
),
|
||||
const Text(
|
||||
'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.',
|
||||
'Test',
|
||||
style: AppTheme.labelMedium,
|
||||
),
|
||||
Padding(
|
||||
@@ -147,7 +147,7 @@ class _PopupWidgetState extends State<PopupWidget> {
|
||||
onPressed: () {
|
||||
print('Button pressed ...');
|
||||
},
|
||||
text: 'Create Task',
|
||||
text: 'Action',
|
||||
options: FFButtonOptions(
|
||||
width: 130,
|
||||
height: 40,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:app/util/backend.dart';
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -209,7 +209,8 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
|
||||
validator: _model.textControllerValidator.asValidator(context),
|
||||
textInputAction: TextInputAction.send, // "Send" on keyboard
|
||||
onFieldSubmitted: (text) {
|
||||
Backend.respondWhenReady(text); // TODO: Connect with multimedia
|
||||
entry_text = _model.textController.text;
|
||||
Backend.respondWhenReady(entry_text); // TODO: Connect with multimedia
|
||||
_model.textController?.clear(); // Clear the text field
|
||||
},
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import '/components/conversation_thread/conversation_thread_widget.dart';
|
||||
import '/components/model_item/model_item_widget.dart';
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:app/theme.dart';
|
||||
|
||||
@@ -64,17 +65,9 @@ class _ConversationWidgetState extends State<ConversationWidget> {
|
||||
),
|
||||
onPressed: () async {
|
||||
print("something pressed in conversation_widget.dart");
|
||||
// TODO
|
||||
// context.goNamed(
|
||||
// 'ConversationsList',
|
||||
// extra: <String, dynamic>{
|
||||
// kTransitionInfoKey: TransitionInfo(
|
||||
// hasTransition: true,
|
||||
// transitionType: PageTransitionType.leftToRight,
|
||||
// duration: const Duration(milliseconds: 250),
|
||||
// ),
|
||||
// },
|
||||
// );
|
||||
// TODO: Smooth out the animation to prevent the conversations list from loading half-way up the screen.
|
||||
SystemChannels.textInput.invokeMethod('TextInput.hide');
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
title: wrapWithModel(
|
||||
|
||||
@@ -2,11 +2,12 @@ import 'package:app/components/conversation_thread/conversation.dart';
|
||||
import 'package:app/theme.dart';
|
||||
|
||||
import 'package:app/pages/conversation_widget.dart';
|
||||
import 'package:app/util/server.dart';
|
||||
import 'package:app/util/backend.dart';
|
||||
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:app/components/options_dialog/options_dialog_widget.dart';
|
||||
import 'conversations_list_model.dart';
|
||||
export 'conversations_list_model.dart';
|
||||
|
||||
@@ -48,6 +49,20 @@ class _ConversationsListWidgetState extends State<ConversationsListWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Clean conversation list prior to building widgets, to prevent bloating UI with empty conversations.
|
||||
// Additionally, delete conversations that have history disabled.
|
||||
for(int i = 0; i < conversations.length; i++) {
|
||||
if(
|
||||
conversations[i].conversation.getChats().isEmpty ||
|
||||
!conversations[i].conversation.history
|
||||
) {
|
||||
Backend.conversations.removeAt(i);
|
||||
conversations.removeAt(i);
|
||||
Backend.maxConversationID--; // maxConversationID will eventually be deprecated with better ID system.
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _model.unfocusNode.canRequestFocus
|
||||
? FocusScope.of(context).requestFocus(_model.unfocusNode)
|
||||
@@ -181,6 +196,24 @@ class _ConversationsListWidgetState extends State<ConversationsListWidget> {
|
||||
)
|
||||
},
|
||||
onLongPress: () => {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
backgroundColor:
|
||||
Colors.transparent,
|
||||
enableDrag: false,
|
||||
useSafeArea: true,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Padding(
|
||||
padding:
|
||||
MediaQuery.viewInsetsOf(
|
||||
context),
|
||||
child: const PopupWidget(),
|
||||
);
|
||||
},
|
||||
).then((value) =>
|
||||
safeSetState(() {})),
|
||||
|
||||
// TODO: Need to implement.
|
||||
print("opening options")
|
||||
},
|
||||
@@ -197,7 +230,7 @@ class _ConversationsListWidgetState extends State<ConversationsListWidget> {
|
||||
right: 20.0, // Adjust positioning as needed
|
||||
child: FloatingActionButton(
|
||||
onPressed: () => {
|
||||
Backend.loadConversation(Backend.maxConversationID), // TODO: Dynamically get the next ID
|
||||
Backend.loadConversation(Backend.createConversation()), // TODO: Dynamically get the next ID
|
||||
conversations.add(ConversationWidget(conversation: Backend.getLoadedConversation())),
|
||||
Navigator.push(
|
||||
context,
|
||||
|
||||
@@ -1169,6 +1169,7 @@ class _SettingsWidgetState extends State<SettingsWidget>
|
||||
focusColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
// TODO: Should open in a browser rather than in-app?
|
||||
onTap: () async {
|
||||
await showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
|
||||
@@ -11,10 +11,10 @@ import 'model.dart';
|
||||
// Uses Singleton design pattern to ensure that any server references across the app are using the same instance.
|
||||
class Backend {
|
||||
// SERVER VARIABLES
|
||||
static final Backend _single_backend = Backend._internal();
|
||||
static final Backend singleBackend = Backend._internal();
|
||||
Backend._internal();
|
||||
// TODO: One-time process to establish server connection is required for easy setup/maintenance.
|
||||
static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing
|
||||
static const url = "http://10.0.2.2:11434/api/"; // For emulated device in testing
|
||||
//static final url = "http://192.168.1.68:11434/api"; // For local access to home server
|
||||
static String endpoint = "";
|
||||
static final Map<String, String> headers = {'Content-Type': 'application/json'};
|
||||
@@ -27,16 +27,16 @@ class Backend {
|
||||
|
||||
|
||||
|
||||
// PRIVACY VARIABLES
|
||||
static bool _historyEnabled = true;
|
||||
static late int conversationID;
|
||||
static late int maxConversationID;
|
||||
// PRIVACY-FOCUSED VARIABLES
|
||||
// TODO: Reorder based on most recent usage.
|
||||
static List<Conversation> conversations = [];
|
||||
static List<Chat> loadedChats = [];
|
||||
|
||||
static late Conversation loadedConversation;
|
||||
|
||||
|
||||
// RUNTIME & STATUS VARIABLES
|
||||
static late int conversationID;
|
||||
static late int maxConversationID;
|
||||
static late Model model;
|
||||
static String prompt = "";
|
||||
static String response = "";
|
||||
@@ -50,7 +50,7 @@ class Backend {
|
||||
|
||||
// loadedChats = conversations[conversationID].chats;
|
||||
|
||||
return _single_backend;
|
||||
return singleBackend;
|
||||
}
|
||||
|
||||
void init() {
|
||||
@@ -61,31 +61,19 @@ class Backend {
|
||||
model = Model("llama3"); // Primarily used llama3, testing gemma2
|
||||
|
||||
// TODO: Dynamically obtain conversations.
|
||||
conversations.add(
|
||||
Conversation.completeConversation(
|
||||
0,
|
||||
"Test Conversation",
|
||||
"Testing Functionality",
|
||||
[
|
||||
(Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)),
|
||||
(Chat("Understood! How can I help?", false)),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
maxConversationID = conversations.length;
|
||||
|
||||
// conversations.add(
|
||||
// Conversation.completeConversation(
|
||||
// 1,
|
||||
// "Label test",
|
||||
// "Sub-label Test",
|
||||
// 0,
|
||||
// "Test Conversation",
|
||||
// "Testing Functionality",
|
||||
// [
|
||||
// (Chat("A different test. Only reply No.", true)),
|
||||
// (Chat("No.", false)),
|
||||
// (Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)),
|
||||
// (Chat("Understood! How can I help?", false)),
|
||||
// ]
|
||||
// )
|
||||
// );
|
||||
|
||||
maxConversationID = conversations.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,21 +103,32 @@ class Backend {
|
||||
|
||||
|
||||
// CONVERSATION MANAGEMENT
|
||||
@Deprecated("")
|
||||
static List<Conversation> getConversationsList() { return conversations; }
|
||||
|
||||
static Conversation getLoadedConversation() { return conversations[conversationID]; }
|
||||
static Conversation getLoadedConversation() { return loadedConversation; }
|
||||
|
||||
// Load conversation should be called prior to any other server requests.
|
||||
static void loadConversation(id) {
|
||||
conversationID = id;
|
||||
if(maxConversationID == id) {
|
||||
conversations.add(Conversation(conversationID, "Conversation $id", "Another AI conversation"));
|
||||
maxConversationID++;
|
||||
|
||||
loadedConversation = conversations[id];
|
||||
loadedChats = loadedConversation.chats;
|
||||
}
|
||||
|
||||
loadedChats = conversations[conversationID].chats;
|
||||
// TODO: Better conversation ID management is needed. Preferably randomized IDs to not indicate the number of conversations.
|
||||
static int createConversation() {
|
||||
int id = conversations.length;
|
||||
conversations.add(Conversation(id, "Conversation $id", "Another AI conversation"));
|
||||
return id;
|
||||
}
|
||||
|
||||
static void deleteConversation(id) {
|
||||
loadedChats = conversations[conversationID].chats; // TODO: Temporarily saving the chats for "undo" popup.
|
||||
conversations.removeAt(id);
|
||||
}
|
||||
|
||||
@Deprecated('Just directly get loadedChats list. Unless multiple chats can be opened simultaneously?')
|
||||
static List<Chat> getChatsByConversationID(id) { return conversations[id].chats; }
|
||||
|
||||
|
||||
@@ -155,7 +154,7 @@ class Backend {
|
||||
// TODO: Allow for different models to be dynamically selected.
|
||||
Map<String, dynamic> data;
|
||||
|
||||
if(_historyEnabled) {
|
||||
if(conversations[conversationID].conversationContext) {
|
||||
loadedChats = [...loadedChats, Chat(prompt, true)];
|
||||
endpoint = "chat";
|
||||
|
||||
@@ -164,7 +163,7 @@ class Backend {
|
||||
data = {
|
||||
"model": model.name,
|
||||
"messages": messageList,
|
||||
"stream": false,
|
||||
"stream": loadedConversation.stream,
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -172,7 +171,7 @@ class Backend {
|
||||
data = {
|
||||
"model": model.name,
|
||||
"prompt": prompt,
|
||||
"stream": false,
|
||||
"stream": loadedConversation.stream,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,6 +181,8 @@ class Backend {
|
||||
|
||||
// Send the HTTP request to the server.
|
||||
static void httpSendRequest() async {
|
||||
// TODO: Add AES256 encryption to the prompt/messages thread.
|
||||
// TODO: Add SHA256 checksum to be sent and verified to prevent/notify in case of data loss.
|
||||
String json = constructPrompt();
|
||||
|
||||
// Send the HTTP POST request
|
||||
@@ -193,7 +194,7 @@ class Backend {
|
||||
|
||||
if (serverResponse.statusCode == 200) {
|
||||
Map<String, dynamic> re = jsonDecode(serverResponse.body);
|
||||
if(_historyEnabled) {
|
||||
if(conversations[conversationID].conversationContext) {
|
||||
response = re["message"]["content"];
|
||||
loadedChats = [...loadedChats, Chat(response, false)];
|
||||
} else {
|
||||
Reference in New Issue
Block a user