diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart index 4d2fbf4..ade2c2b 100644 --- a/mobile/app/lib/components/conversation_thread/chat.dart +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -1,7 +1,3 @@ -import 'dart:convert'; -import 'package:json_annotation/json_annotation.dart'; - -@JsonSerializable() class Chat { final String content; final bool role; @@ -9,14 +5,5 @@ class Chat { // Most basic chat, text from server or from user. Chat(this.content, this.role); - String toJson() { - Map c = { - "content": content, - "role": role, - }; - - return json.encode(c); - } - // TODO: Overload constructors to allow for multimedia. } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart index 5023210..ef0667c 100644 --- a/mobile/app/lib/components/conversation_thread/conversation.dart +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -1,15 +1,40 @@ import 'package:app/util/server.dart'; +import 'package:flutter/material.dart'; import 'chat.dart'; -class Conversation { +class Conversation with ChangeNotifier { int conversationID; + String conversationLabel; + String conversationSubLabel; List chats = []; - Conversation(this.conversationID) { - chats = Server().getConversationByID(conversationID); + Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel); + + Conversation.completeConversation(this.conversationID, this.conversationLabel, this.conversationSubLabel, this.chats); + + void add(Chat message) { + chats.add(message); + notifyListeners(); + } + + void loadChats() { + chats = Server.getChatsByConversationID(conversationID); + } + + void setLabel() { + // TODO: Need to implement + } + + void setSubLabel() { + // TODO: Need to implement } List getChats() { return chats; } + + void setLabels(String cLabel, String cSubLabel) { + conversationLabel = cLabel; + conversationSubLabel = cSubLabel; + } } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 08eacc1..6b3197d 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -14,16 +14,15 @@ import 'package:cached_network_image/cached_network_image.dart'; class ConversationThreadModel extends FlutterFlowModel { final formKey = GlobalKey(); late PromptBoxModel promptBoxModel; - late int ?conversationID; late Conversation conversation; - // If there's a conversation ID, retrieve the conversation history. - // Otherwise, start a new conversation with user defaults. - ConversationThreadModel(this.conversationID); + void updateConversation() { + conversation = Server.getLoadedConversation(); + } @override void initState(BuildContext context) { - conversation = Conversation(conversationID!); + conversation = Server.getLoadedConversation(); promptBoxModel = createModel(context, () => PromptBoxModel()); } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 6668fd6..f096d00 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,4 +1,6 @@ import 'package:app/components/conversation_thread/conversation.dart'; +import 'package:app/util/server.dart'; +import 'package:provider/provider.dart'; import '/components/conversation_thread/chat_bubble_widget.dart'; import 'package:app/components/conversation_thread/conversation_thread_model.dart'; @@ -32,7 +34,7 @@ class _ConversationThreadWidgetState extends State { @override void initState() { super.initState(); - _model = createModel(context, () => ConversationThreadModel(0)); // TODO + _model = createModel(context, () => ConversationThreadModel()); } @override @@ -44,132 +46,140 @@ class _ConversationThreadWidgetState extends State { @override Widget build(BuildContext context) { - Conversation c = _model.conversation; - return Container( - width: double.infinity, - height: double.infinity, - decoration: const BoxDecoration( - color: AppTheme.primaryBackground, - ), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: ListView.builder( - itemCount: c.chats.length, - padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), - reverse: false, - scrollDirection: Axis.vertical, - itemBuilder: (BuildContext context, int index) { - return ChatBubbleWidget(c: c.chats[index]); - }, + return Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + color: AppTheme.primaryBackground, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListenableBuilder( + listenable: _model.conversation, + builder: (BuildContext context, Widget? child) { + return ListView.builder( + itemCount: Server.getLoadedConversation().chats.length, + padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), + reverse: false, + scrollDirection: Axis.vertical, + itemBuilder: (BuildContext context, int index) { + if(Server.getLoadedConversation().chats.last.role) { + return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]); + } else { + + } + }, + ); + }, + ), ), - ), - Container( - width: double.infinity, - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - boxShadow: [ - BoxShadow( - blurRadius: 3, - color: Color(0x33000000), - offset: Offset( - 0, - -2, - ), - ) - ], - ), - // TODO - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // FlutterFlowMediaDisplay( - // path: '', - // imageBuilder: (path) => ClipRRect( - // borderRadius: BorderRadius.circular(8), - // child: CachedNetworkImage( - // fadeInDuration: const Duration(milliseconds: 500), - // fadeOutDuration: - // const Duration(milliseconds: 500), - // imageUrl: path, - // width: 120, - // height: 100, - // fit: BoxFit.cover, - // ), - // ), - // TODO - // videoPlayerBuilder: (path) => - // FlutterFlowVideoPlayer( - // path: path, - // width: 300, - // autoPlay: false, - // looping: true, - // showControls: true, - // allowFullScreen: true, - // allowPlaybackSpeedMenu: false, - // ), - // ), - Align( - alignment: const AlignmentDirectional(-1, -1), - // TODO: Make the delete button conditional upon media upload. - child: FlutterFlowIconButton( - borderColor: - AppTheme.error, - borderRadius: 20, - borderWidth: 2, - buttonSize: 40, - fillColor: AppTheme.primaryBackground, - icon: const Icon( - Icons.delete_outline_rounded, - color: AppTheme.error, - size: 24, + Container( + width: double.infinity, + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 3, + color: Color(0x33000000), + offset: Offset( + 0, + -2, + ), + ) + ], + ), + // TODO + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // FlutterFlowMediaDisplay( + // path: '', + // imageBuilder: (path) => ClipRRect( + // borderRadius: BorderRadius.circular(8), + // child: CachedNetworkImage( + // fadeInDuration: const Duration(milliseconds: 500), + // fadeOutDuration: + // const Duration(milliseconds: 500), + // imageUrl: path, + // width: 120, + // height: 100, + // fit: BoxFit.cover, + // ), + // ), + // TODO + // videoPlayerBuilder: (path) => + // FlutterFlowVideoPlayer( + // path: path, + // width: 300, + // autoPlay: false, + // looping: true, + // showControls: true, + // allowFullScreen: true, + // allowPlaybackSpeedMenu: false, + // ), + // ), + Align( + alignment: const AlignmentDirectional(-1, -1), + // TODO: Make the delete button conditional upon media upload. + child: FlutterFlowIconButton( + borderColor: + AppTheme.error, + borderRadius: 20, + borderWidth: 2, + buttonSize: 40, + fillColor: AppTheme.primaryBackground, + icon: const Icon( + Icons.delete_outline_rounded, + color: AppTheme.error, + size: 24, + ), + onPressed: () { + print('IconButton pressed ...'); + }, ), - onPressed: () { - print('IconButton pressed ...'); - }, ), - ), - ] - .divide(const SizedBox(width: 8)) - .addToStart(const SizedBox(width: 16)) - .addToEnd(const SizedBox(width: 16)), + ] + .divide(const SizedBox(width: 8)) + .addToStart(const SizedBox(width: 16)) + .addToEnd(const SizedBox(width: 16)), + ), ), ), ), - ), - ], - ), - Form( - key: _model.formKey, - autovalidateMode: AutovalidateMode.disabled, - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), - child: wrapWithModel( - model: _model.promptBoxModel, - updateCallback: () => setState(() {}), - child: const PromptBoxWidget(), + ], + ), + Form( + key: _model.formKey, + autovalidateMode: AutovalidateMode.disabled, + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + child: wrapWithModel( + model: _model.promptBoxModel, + updateCallback: () => setState(() {}), + child: const PromptBoxWidget(), + ), ), ), - ), - ], + ], + ), ), - ), - ], - ), - ); + ], + ), + ); } } diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index d149eea..3ab8ee1 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -2,8 +2,6 @@ import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; import 'package:app/theme.dart'; @@ -221,7 +219,8 @@ class _PromptBoxWidgetState extends State { validator: _model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard onFieldSubmitted: (text) { - updateFromLatestInput(text); // TODO: Connect with multimedia + // TODO: Clear text box + Server.respondWhenReady(text); // TODO: Connect with multimedia }, ), ), @@ -233,9 +232,4 @@ class _PromptBoxWidgetState extends State { ), ); } - - void updateFromLatestInput(String input) { - Server().getResponseWhenReady(input); - // TODO: Refresh the page with the prompt & response. - } } diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 30672db..ebc8243 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -53,13 +53,12 @@ void main() { class MyApp extends ConsumerWidget { MyApp({super.key}); - final selectedIndexProvider = StateProvider((_) => 2); // Initial state + final selectedIndexProvider = StateProvider((_) => 1); // Initial state // This list holds the pages to navigate between final List _pages = [ const Center(child: SettingsWidget()), const Center(child: ConversationsListWidget()), - const Center(child: ConversationWidget()), ]; // This widget is the root of your application. diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/pages/conversation_model.dart index 4566fad..115bbf0 100644 --- a/mobile/app/lib/pages/conversation_model.dart +++ b/mobile/app/lib/pages/conversation_model.dart @@ -15,8 +15,7 @@ class ConversationModel extends FlutterFlowModel { @override void initState(BuildContext context) { - conversationThreadModel = - createModel(context, () => ConversationThreadModel(0)); // TODO + conversationThreadModel = createModel(context, () => ConversationThreadModel()); // TODO modelItemModel = createModel(context, () => ModelItemModel()); } diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index a22bbba..556478b 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -19,8 +19,9 @@ class ConversationWidget extends StatefulWidget { class _ConversationWidgetState extends State { late ConversationModel _model; - final scaffoldKey = GlobalKey(); + _ConversationWidgetState(); + final scaffoldKey = GlobalKey(); @override void initState() { super.initState(); diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/pages/conversations_list_model.dart index 23245b2..c4ad379 100644 --- a/mobile/app/lib/pages/conversations_list_model.dart +++ b/mobile/app/lib/pages/conversations_list_model.dart @@ -1,8 +1,6 @@ import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversations_list_widget.dart' show ConversationsListWidget; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; class ConversationsListModel extends FlutterFlowModel { diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 4f48bce..7df996a 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,7 +1,9 @@ +import 'package:app/components/conversation_thread/conversation.dart'; import 'package:app/theme.dart'; import 'package:app/pages/conversation_widget.dart'; import 'package:app/components/conversation_options/conversation_options_widget.dart'; +import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; @@ -10,19 +12,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; -final conversationIndex = ValueNotifier(0); - -// State controller using flutter_riverpod API. -// This handles changing between different conversations. -class MyState with ChangeNotifier { - int get selectedIndex => conversationIndex.value; - - set selectedIndex(int value) { - conversationIndex.value = value; - notifyListeners(); - } -} - class ConversationsListWidget extends StatefulWidget { const ConversationsListWidget({super.key}); @@ -35,17 +24,24 @@ class _ConversationsListWidgetState extends State { late ConversationsListModel _model; final selectedIndexProvider = StateProvider((_) => 0); // Initial state - final List conversations = [ - // TODO: Dynamically generate list of pages based upon existing conversations. - // This requires retrievable conversations from the server side. - const Center(child: ConversationWidget()), - ]; + late List conversations = []; + // = [ + // // TODO: Dynamically generate list of pages based upon existing conversations. + // // This requires retrievable conversations from the server side. + // const Center(child: ConversationWidget()), + // ]; final scaffoldKey = GlobalKey(); @override void initState() { super.initState(); + Server().init(); + List convos = Server.conversations; + for(Conversation c in convos) { + conversations.add(const ConversationWidget()); + } + _model = createModel(context, () => ConversationsListModel()); } @@ -57,6 +53,7 @@ class _ConversationsListWidgetState extends State { @override Widget build(BuildContext context) { + List conversations = Server.getConversationsList(); return GestureDetector( onTap: () => _model.unfocusNode.canRequestFocus ? FocusScope.of(context).requestFocus(_model.unfocusNode) @@ -77,12 +74,13 @@ class _ConversationsListWidgetState extends State { Expanded( child: Align( alignment: const AlignmentDirectional(0, 0), - child: ListView( + child: ListView.builder( + itemCount: conversations.length, padding: EdgeInsets.zero, reverse: true, scrollDirection: Axis.vertical, - children: [ - Align( + itemBuilder: (context, index) { + return Align( alignment: const AlignmentDirectional(0, 0), child: InkWell( splashColor: Colors.transparent, @@ -101,9 +99,7 @@ class _ConversationsListWidgetState extends State { Container( width: 100, height: 100, - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - ), + decoration: const BoxDecoration(color: AppTheme.secondaryBackground,), child: const Align( alignment: AlignmentDirectional(0, 0), child: Icon( @@ -120,28 +116,24 @@ class _ConversationsListWidgetState extends State { width: 100, height: 100, constraints: BoxConstraints( - minWidth: - MediaQuery.sizeOf(context).width, + minWidth: MediaQuery.sizeOf(context).width, ), decoration: const BoxDecoration( color: AppTheme.secondaryBackground, shape: BoxShape.rectangle, ), - child: const Column( + child: Column( mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - crossAxisAlignment: - CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Align( - alignment: - AlignmentDirectional(-1, 0), + alignment: const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Text( - 'Hello World', // TODO: Dynamically obtain conversation title from server. + conversations[index].conversationLabel, style: AppTheme.headlineLarge, ), ], @@ -149,13 +141,13 @@ class _ConversationsListWidgetState extends State { ), Align( alignment: - AlignmentDirectional(-1, 0), + const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Text( - 'Hello World', // TODO: Summary or list of models used + conversations[index].conversationSubLabel, style: AppTheme.bodyMedium, ), ), @@ -183,15 +175,23 @@ class _ConversationsListWidgetState extends State { ), onTap: () => { // TODO: Need to implement. - print("opening existing conversation") + // Get the conversation object based on the index + // Use Navigator to push a new ConversationWidget with conversation data + Server.loadConversation(index), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ConversationWidget(), + ), + ) }, onLongPress: () => { // TODO: Need to implement. print("opening options") }, ), - ), - ], + ); + } ), ), ), diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index 8a82d7b..00fcf6e 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:app/components/conversation_thread/conversation.dart'; import 'package:http/http.dart' as http; import 'package:app/components/conversation_thread/chat.dart'; @@ -7,12 +8,34 @@ import 'package:app/util/speech_to_text.dart'; // Uses Singleton design pattern to ensure that any server references across the app are using the same instance. class Server { + // SERVER VARIABLES + static final Server _single_server = Server._internal(); + Server._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/"; + static String endpoint = ""; + static final Map headers = {'Content-Type': 'application/json'}; - // STT variables + + + // AI VARIABLES static final SpeechToText stt = SpeechToText(); static late bool _speechEnabled; - static final Server _single_server = Server._internal(); + + + // PRIVACY VARIABLES + static bool _historyEnabled = true; + static late int conversationID; + static List conversations = []; + static List loadedChats = []; + + + + // RUNTIME & STATUS VARIABLES + static String prompt = ""; + static String response = ""; + static bool initiated = false; // TODO: Verify all different components of STT, TTS, LLM, etc have been initialized. // TODO: Handshake w/ server to validate identity @@ -20,41 +43,83 @@ class Server { // Speech-To-Text engine activation. _speechEnabled = stt.isSpeechEnabled(); + // loadedChats = conversations[conversationID].chats; + return _single_server; } - Server._internal(); + void init() { + initiated = true; - // TODO: One-time process to establish server connection is required for easy setup/maintenance. - // Server variables - final url = "http://10.0.2.2:11434/api/"; - String endpoint = ""; - Map headers = {'Content-Type': 'application/json'}; - - // Status variables - bool _historyEnabled = true; + // TODO: Dynamically obtain conversations. + conversations.add( + Conversation.completeConversation( + 0, + "Test label", + "Test sub-label", + [ + (Chat("This is just a test. Only reply Yes.", true)), + (Chat("Yes.", false)), + ] + ) + ); - // Context variables - List chats = [ - (Chat("This is just a test. Only reply Yes.", true)), - (Chat("Yes.", false)), - ]; - - // Runtime variables - String prompt = ""; - String response = ""; - int conversationID = 0; - - List getConversationByID(conversationID) { - // TODO: Implement conversation IDs & retrieval from server - this.conversationID = conversationID; - return chats; + conversations.add( + Conversation.completeConversation( + 1, + "Label test", + "Sub-label Test", + [ + (Chat("A different test. Only reply No.", true)), + (Chat("No.", false)), + ] + ) + ); } - // TODO: Integrate with getConversationByID() - List> convertMessages() { + + + // PRIMARY INTERFACE + static Future sttGetResponseWhenReady() async { + // TODO: In case on-device STT is unavailable, use server-side STT service. + if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; + + prompt = stt.getTextWhenReady() as String; + conversations[conversationID].add(Chat(prompt, true)); + httpSendRequest(); + conversations[conversationID].add(Chat(response, false)); + + return response; + } + + static void respondWhenReady(String p) async { + prompt = p; + conversations[conversationID].add(Chat(prompt, true)); + httpSendRequest(); + } + + + + // CONVERSATION MANAGEMENT + static List getConversationsList() { return conversations; } + + static Conversation getLoadedConversation() { return conversations[conversationID]; } + + // Load conversation should be called prior to any other server requests. + static void loadConversation(id) { + conversationID = id; + loadedChats = conversations[conversationID].chats; + } + + static List getChatsByConversationID(id) { return conversations[id].chats; } + + + + // SERVER UTILS + static List> convertMessages() { + // TODO: Integrate with getConversationByID() List> messageList = []; - for (Chat c in chats) { + for (Chat c in loadedChats) { messageList.add({ "role": c.role ? "user" : "assistant", "content": c.content, @@ -64,41 +129,15 @@ class Server { return messageList; } - // Using native on-device Speech-To-Text capability, get the server response when ready. - Future sttGetResponseWhenReady() async { - // TODO: In case on-device STT is unavailable, use server-side STT service. - if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; - - prompt = stt.getTextWhenReady() as String; - httpSendRequest(); - - return response; - } - - Future getResponseWhenReady(String p) async { - prompt = p; - httpSendRequest(); - return response; - } - - // Get the last response or request a response from the server. - String getResponse() { - httpSendRequest(); - return response; - } - - // Get the latest prompt. - String getPrompt() => prompt; - // Formats the prompt in JSON. - String constructPrompt() { + static String constructPrompt() { // TODO: Allow for additional flags, ie. continuous conversation, images, etc. // TODO: Allow for toggleable states between stream // TODO: Allow for different models to be dynamically selected. Map data; if(_historyEnabled) { - chats = [...chats, Chat(prompt, true)]; + loadedChats = [...loadedChats, Chat(prompt, true)]; endpoint = "chat"; List> messageList = convertMessages(); @@ -123,9 +162,8 @@ class Server { } // Send the HTTP request to the server. - void httpSendRequest() async { + static void httpSendRequest() async { String json = constructPrompt(); - print("\n\nJSON:\n$json\n\n"); // Send the HTTP POST request final serverResponse = await http.post( @@ -138,15 +176,14 @@ class Server { Map re = jsonDecode(serverResponse.body); if(_historyEnabled) { response = re["message"]["content"]; - chats = [...chats, Chat(response, false)]; + loadedChats = [...loadedChats, Chat(response, false)]; } else { response = re["response"]; } } - print("\n\nresponse:\n$response"); - print("\nRaw chats variable:\n"); - for(Chat c in chats) { - print(c.content); - } + conversations[conversationID].add(Chat(response, false)); + + prompt = ""; + response = ""; } } \ No newline at end of file