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