enabled togglable chat history (during runtime) that uses different api endpoints.

This commit is contained in:
Joshua
2024-07-08 13:03:15 -06:00
parent 8956ed7885
commit ff4a54fbbc
4 changed files with 99 additions and 84 deletions
@@ -1,9 +1,22 @@
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';
@JsonSerializable()
class Chat { class Chat {
late String message; final String content;
late bool isMe; final bool role;
// Most basic chat, text from server or from user. // Most basic chat, text from server or from user.
Chat(this.message, this.isMe); Chat(this.content, this.role);
String toJson() {
Map<String, dynamic> c = {
"content": content,
"role": role,
};
return json.encode(c);
}
// TODO: Overload constructors to allow for multimedia. // TODO: Overload constructors to allow for multimedia.
} }
@@ -9,7 +9,7 @@ class ChatBubbleWidget extends StatelessWidget{
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisAlignment: c.isMe ? MainAxisAlignment.end : MainAxisAlignment.start, mainAxisAlignment: c.role ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@@ -18,14 +18,14 @@ class ChatBubbleWidget extends StatelessWidget{
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: const Radius.circular(12.0), topLeft: const Radius.circular(12.0),
topRight: const Radius.circular(12.0), topRight: const Radius.circular(12.0),
bottomLeft: c.isMe ? const Radius.circular(12.0) : const Radius bottomLeft: c.role ? const Radius.circular(12.0) : const Radius
.circular(0.0), .circular(0.0),
bottomRight: c.isMe ? const Radius.circular(12.0) : const Radius bottomRight: c.role ? const Radius.circular(12.0) : const Radius
.circular(0.0), .circular(0.0),
), ),
color: c.isMe ? Colors.blue[200] : Colors.grey[200], color: c.role ? Colors.blue[200] : Colors.grey[200],
), ),
child: Text(c.message), child: Text(c.content),
), ),
], ],
); );
@@ -1,3 +1,4 @@
import 'package:app/util/server.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:easy_debounce/easy_debounce.dart'; import 'package:easy_debounce/easy_debounce.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -58,8 +59,9 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
// TODO: Implement the context upload buttons
children: [ children: [
Expanded( Expanded( // TODO: File context upload
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: FlutterFlowIconButton( child: FlutterFlowIconButton(
@@ -78,7 +80,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
), ),
), ),
Expanded( Expanded( // TODO: Photo context upload
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: FlutterFlowIconButton( child: FlutterFlowIconButton(
@@ -97,7 +99,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
), ),
), ),
Expanded( Expanded( // TODO: Microphone context upload -> or STT??
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: FlutterFlowIconButton( child: FlutterFlowIconButton(
@@ -116,7 +118,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
), ),
), ),
Expanded( Expanded( // TODO: Video context upload
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: FlutterFlowIconButton( child: FlutterFlowIconButton(
@@ -135,7 +137,7 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
), ),
), ),
Expanded( Expanded( // TODO: Camera context upload
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: FlutterFlowIconButton( child: FlutterFlowIconButton(
@@ -173,8 +175,6 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
autofocus: true, autofocus: true,
textCapitalization: TextCapitalization.none, textCapitalization: TextCapitalization.none,
textInputAction: TextInputAction.go,
obscureText: false,
decoration: InputDecoration( decoration: InputDecoration(
isDense: false, isDense: false,
counterStyle: AppTheme.bodyMedium, counterStyle: AppTheme.bodyMedium,
@@ -218,8 +218,11 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
textAlign: TextAlign.start, textAlign: TextAlign.start,
maxLines: 5, maxLines: 5,
minLines: 1, minLines: 1,
validator: validator: _model.textControllerValidator.asValidator(context),
_model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard
onFieldSubmitted: (text) {
updateFromLatestInput(text); // TODO: Connect with multimedia
},
), ),
), ),
), ),
@@ -230,4 +233,10 @@ class _PromptBoxWidgetState extends State<PromptBoxWidget> {
), ),
); );
} }
void updateFromLatestInput(String input) {
Server s = Server();
s.setPrompt(input);
print(s.getResponse());
}
} }
+60 -67
View File
@@ -16,6 +16,13 @@ class Server {
// Status variables // Status variables
late bool _speechEnabled; late bool _speechEnabled;
bool _historyEnabled = true;
// Context variables
List<Chat> chats = [
(Chat("This is just a test. Only reply Yes.", true)),
(Chat("Yes.", false)),
];
// Runtime variables // Runtime variables
String prompt = ""; String prompt = "";
@@ -31,40 +38,48 @@ class Server {
} }
List<Chat> getConversationByID(conversationID) { List<Chat> getConversationByID(conversationID) {
this.conversationID = conversationID;
// TODO: Implement conversation IDs & retrieval from server // TODO: Implement conversation IDs & retrieval from server
List<Chat> chats = [ this.conversationID = conversationID;
(Chat("hello world!", true)),
(Chat("hello to you too!", false)),
];
return chats; return chats;
} }
// TODO: Integrate with getConversationByID()
List<Map<String, String>> convertMessages() {
List<Map<String, String>> messageList = [];
for (Chat c in chats) {
messageList.add({
"role": c.role ? "user" : "assistant",
"content": c.content,
});
}
return messageList;
}
// Using native on-device Speech-To-Text capability, get the server response when ready. // Using native on-device Speech-To-Text capability, get the server response when ready.
Future<String> sttGetResponseWhenReady() async { Future<String> sttGetResponseWhenReady() async {
// TODO: In case on-device STT is unavailable, use server-side STT service. // TODO: In case on-device STT is unavailable, use server-side STT service.
if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; if (!_speechEnabled) "Unable to process Speech-To-Text on-device.";
setPrompt(stt.getTextWhenReady() as String); setPrompt(stt.getTextWhenReady() as String);
constructPrompt();
httpSendRequest(); httpSendRequest();
return response; return response;
} }
Future<String> getResponseWhenReady(String p) async {
prompt = p;
httpSendRequest();
return response;
}
// Arbitrary prompt, used for standard text interaction rather than STT. // Arbitrary prompt, used for standard text interaction rather than STT.
void setPrompt(String p) => prompt = p; void setPrompt(String p) => prompt = p;
// Get the last response or request a response from the server. // Get the last response or request a response from the server.
String getResponse() { String getResponse() {
if(response.isEmpty) {
constructPrompt();
httpSendRequest(); httpSendRequest();
} return response;
return response;
} }
// Get the latest prompt. // Get the latest prompt.
@@ -72,28 +87,46 @@ class Server {
// Formats the prompt in JSON. // Formats the prompt in JSON.
String constructPrompt() { String constructPrompt() {
// Construct the JSON payload // TODO: Allow for additional flags, ie. continuous conversation, images, etc.
final Map<String, dynamic> data = { // TODO: Allow for toggleable states between stream
"model": // TODO: Allow for different models to be dynamically selected.
"llama3", // TODO: Allow for different models to be dynamically selected. Map<String, dynamic> data;
"prompt": prompt,
"stream": false // TODO: Allow for toggleable states between stream if(_historyEnabled) {
// TODO: Allow for additional flags, ie. continuous conversation, images, etc. chats = [...chats, Chat(prompt, true)];
};
List<Map<String, String>> messageList = convertMessages();
data = {
"model": "llama3",
"messages": messageList,
"stream": false,
};
}
else {
data = {
"model": "llama3",
"prompt": prompt,
"stream": false,
};
}
// Encode the JSON payload // Encode the JSON payload
final String body = json.encode(data); return json.encode(data);
return body;
} }
// Send the HTTP request to the server. // Send the HTTP request to the server.
void httpSendRequest() async { void httpSendRequest() async {
String json = constructPrompt(); String json = constructPrompt();
print("\n\n$json\n\n");
String endpoint = "/api/";
if(_historyEnabled) endpoint += "chat";
else endpoint += "generate";
// Send the HTTP POST request // Send the HTTP POST request
final serverResponse = await http.post( final serverResponse = await http.post(
Uri.parse('$url/api/generate'), Uri.parse('$url$endpoint'),
headers: headers, headers: headers,
body: json, body: json,
); );
@@ -104,46 +137,6 @@ class Server {
} else { } else {
response = ('Error: ${serverResponse.statusCode}'); response = ('Error: ${serverResponse.statusCode}');
} }
chats.add(Chat(response, false));
} }
} }
//
//
// // GUI components
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: <Widget>[
// Container(
// padding: const EdgeInsets.all(16),
// child: const Text(
// 'Recognized words:',
// style: TextStyle(fontSize: 20.0),
// ),
// ),
// Expanded(
// child: Container(
// padding: const EdgeInsets.all(16),
// child: Text(
// // If listening is active show the recognized words
// _speechToText.isListening
// ? prompt
// // If listening isn't active but could be tell the user
// // how to start it, otherwise indicate that speech
// // recognition is not yet ready or not supported on
// // the target device
// : _speechEnabled
// ? responseFromAI
// : 'Speech disabled',
// ),
// ),
// ),
// ],
// ),
// ),
// floatingActionButton: FloatingActionButton(
// onPressed:
// // If not yet listening for speech start, otherwise stop
// //sendPrompt,
// _speechToText.isNotListening ? _startListening : sendPrompt,
// tooltip: 'Listen',
// child: Icon(_speechToText.isNotListening ? Icons.mic_off : Icons.mic),
// ),