Stabilize frontend login. Make chat messages multiline.
This commit is contained in:
1 parent
5bbdfe8747
commit
3f37f727af
6 files changed
+71
-31
No files matched your search
@@ -34,8 +34,6 @@ class Agent:
|
||||
]
|
||||
tools = []
|
||||
|
||||
# while this constructor accepts two parameters,
|
||||
# all subclasses must also accept a fourth 'data' parameter
|
||||
def __init__(self, app, agent_id, data):
|
||||
self.app = app
|
||||
self.agent_id = agent_id
|
||||
|
||||
+2
-4
@@ -31,7 +31,7 @@ class App:
|
||||
if replicate_api_token:
|
||||
self.replicate_client = replicate.Client(api_token=replicate_api_token)
|
||||
else:
|
||||
raise Exception("Horde only works with a replicate client. Please provide an API Token.")
|
||||
raise Exception("Horde only works with a replicate connection. Please provide an API Token.")
|
||||
|
||||
# `Agent` is the kind 'basic', will be class `BasicAgent` in the future
|
||||
self.agent_classes = [Agent]
|
||||
@@ -119,7 +119,7 @@ class App:
|
||||
|
||||
if data:
|
||||
# Assign a copy to trigger a value change
|
||||
agent.data = agent.data.copy()
|
||||
agent.data = agent.data.copy() if agent.data else {}
|
||||
agent.data.update(data)
|
||||
summary.extend([f"data.{name}" for name in data.keys()])
|
||||
|
||||
@@ -335,8 +335,6 @@ if __name__ == "__main__":
|
||||
print("No directory specified. Using current directory as default.")
|
||||
data_directory = "."
|
||||
|
||||
print("api token", os.getenv('REPLICATE_API_TOKEN'))
|
||||
|
||||
app = App(
|
||||
data_directory=data_directory,
|
||||
replicate_api_token=os.getenv('REPLICATE_API_TOKEN'),
|
||||
|
||||
@@ -86,6 +86,10 @@ def start_server(app):
|
||||
await websocket.send(json.dumps({'error': 'invalid credentials'}))
|
||||
return
|
||||
|
||||
# TODO: print username instead, same in the finally block
|
||||
print("User logged in:", user_id)
|
||||
await websocket.send(json.dumps({'message': 'authentication successful'}))
|
||||
|
||||
send_queue = asyncio.Queue()
|
||||
async def sender_task():
|
||||
nonlocal send_queue
|
||||
@@ -190,6 +194,7 @@ def start_server(app):
|
||||
else:
|
||||
await send({'error': 'unknown action: ' + action, 'nonce': nonce})
|
||||
finally:
|
||||
print("User logged out:", user_id)
|
||||
app.remove_user_connection(user_id, send_queue)
|
||||
|
||||
server.run(debug=True)
|
||||
@@ -24,7 +24,7 @@ class Backend {
|
||||
|
||||
// Authentication
|
||||
static ({String username, String password})? _credentials;
|
||||
static Completer? _loginCompleter;
|
||||
static Completer<bool>? _loginCompleter;
|
||||
static Completer? _credentialsCompleter;
|
||||
|
||||
static void setup(String url) {
|
||||
@@ -49,8 +49,11 @@ class Backend {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> auth(String username, String password) {
|
||||
_socket!.send(json.encode({'username': username, 'password': password}));
|
||||
static Future<bool> auth(String username, String password) {
|
||||
if (_credentials != null) {
|
||||
print("Cannot authenticate twice");
|
||||
return Future.value(false);
|
||||
}
|
||||
_credentials = (username: username, password: password);
|
||||
if (_credentialsCompleter != null) {
|
||||
_credentialsCompleter!.complete(_credentials);
|
||||
@@ -62,11 +65,13 @@ class Backend {
|
||||
|
||||
static void connect() {
|
||||
_socket = WebSocket(_url! + "/ws");
|
||||
_socket!.onOpen.listen((event) {
|
||||
_socket!.onOpen.listen((event) async {
|
||||
print('Connected to the WebSocket server');
|
||||
Backend.invalidateCache(chat: true);
|
||||
|
||||
print('Logging in...');
|
||||
// secure the listener for the response before sending credentials
|
||||
var responseFuture = nextIncomingMessage();
|
||||
if (_credentials != null) {
|
||||
_socket!.send(json.encode({'username': _credentials!.username, 'password': _credentials!.password}));
|
||||
} else {
|
||||
@@ -75,7 +80,14 @@ class Backend {
|
||||
_socket!.send(json.encode({'username': credentials.username, 'password': credentials.password}));
|
||||
});
|
||||
}
|
||||
_loginCompleter?.complete();
|
||||
var response = await responseFuture;
|
||||
if (response['error'] == null) {
|
||||
_loginCompleter?.complete(true);
|
||||
} else {
|
||||
_loginCompleter?.complete(false);
|
||||
_socket!.close(); // Retry
|
||||
}
|
||||
_loginCompleter = null;
|
||||
});
|
||||
_socket!.onError.listen((event) {
|
||||
print('Error: $event');
|
||||
@@ -92,11 +104,10 @@ class Backend {
|
||||
|
||||
static void disconnect() {
|
||||
_socket?.close();
|
||||
_loginCompleter = null;
|
||||
}
|
||||
|
||||
static void _reconnect() {
|
||||
Future.delayed(Duration(seconds: 5), () {
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
connect();
|
||||
});
|
||||
}
|
||||
@@ -140,10 +151,17 @@ class Backend {
|
||||
print("No active listeners for chatId $chatId");
|
||||
}
|
||||
} else {
|
||||
if (jsonData['error'] != null) {
|
||||
print("Error: ${jsonData['error']}");
|
||||
}
|
||||
_packetStreamController.add(jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> nextIncomingMessage() {
|
||||
return _packetStreamController.stream.first;
|
||||
}
|
||||
|
||||
static Stream<Map<String, dynamic>> chatMessages(int chatId) {
|
||||
if (!_chatControllers.containsKey(chatId) || _chatControllers[chatId]!.isClosed) {
|
||||
_chatControllers[chatId] = StreamController<Map<String, dynamic>>.broadcast();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'app_menu.dart';
|
||||
import 'styles.dart';
|
||||
import 'backend.dart';
|
||||
@@ -36,6 +37,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
String? _title;
|
||||
List<Message> _messages = [];
|
||||
final TextEditingController _textController = TextEditingController();
|
||||
final FocusNode _inputFocusNode = FocusNode();
|
||||
StreamSubscription? _chatSubscription;
|
||||
|
||||
@override
|
||||
@@ -137,23 +139,36 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textController,
|
||||
decoration: inputDecoration(),
|
||||
onSubmitted: sendMessage, // Send message on Enter key
|
||||
child: KeyboardListener(
|
||||
focusNode: _inputFocusNode,
|
||||
onKeyEvent: (event) {
|
||||
// TODO: Probably doing this in the input focus node would be cleaner
|
||||
if (HardwareKeyboard.instance.isControlPressed && HardwareKeyboard.instance.isLogicalKeyPressed(LogicalKeyboardKey.enter)) {
|
||||
sendMessage(_textController.text);
|
||||
_textController.clear();
|
||||
//return KeyEventResult.handled;
|
||||
}
|
||||
//return KeyEventResult.ignored;
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textController,
|
||||
maxLines: null,
|
||||
decoration: inputDecoration(),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
sendMessage(_textController.text);
|
||||
},
|
||||
child: Text('Send'),
|
||||
),
|
||||
],
|
||||
SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
sendMessage(_textController.text);
|
||||
_textController.clear();
|
||||
},
|
||||
child: Text('Send'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -44,12 +44,18 @@ class _UserCheckerState extends State<UserChecker> {
|
||||
}
|
||||
|
||||
void _handleLogin(String user, String password) async {
|
||||
bool success = await Backend.auth(user, password);
|
||||
if (!success) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
username = user;
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
await Backend.auth(user, password);
|
||||
await _storage.write(key: 'username', value: user);
|
||||
await _storage.write(key: 'password', value: password);
|
||||
|
||||
|
||||
Reference in new issue
Block a user