Skip to content

Commit 12b6f68

Browse files
authored
Dependabot fixes + mongodb vector search update + improvement on websocket connection on UI (#43)
* working version pre dependency bot break * update websocket connection feedback and pom updates * version bumps in search function and mongodb vector search update * bump memory allocation for lambdas
1 parent 37db87a commit 12b6f68

File tree

7 files changed

+134
-29
lines changed

7 files changed

+134
-29
lines changed

frontend/package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/components/Chat.tsx

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { WS_URL } from "astro:env/client";
22
import useWebSocket, { ReadyState } from "react-use-websocket";
3-
import React, { useState, useCallback } from "react";
3+
import React, { useState, useCallback, useEffect } from "react";
44
import ChatInput from "./ChatInput.tsx";
55
import ChatMessages from "./ChatMessages.tsx";
66
import type { ChatMessage } from "./ChatMessage.tsx";
@@ -21,7 +21,9 @@ export default function Chat({ username }: { username: string }) {
2121

2222
const [input, setInput] = useState("");
2323
const [messageHistory, setMessageHistory] = useState<ChatMessage[]>([]);
24-
const [isLoading, setIsLoading] = useState(false); // Add this line
24+
const [isLoading, setIsLoading] = useState(false);
25+
const [connectionStatus, setConnectionStatus] = useState<string>("Connecting...");
26+
const [isReconnecting, setIsReconnecting] = useState(false);
2527

2628
const handleMessage = useCallback((event: WebSocketEventMap["message"]) => {
2729
console.log(event);
@@ -37,25 +39,92 @@ export default function Chat({ username }: { username: string }) {
3739
} catch (e) {
3840
console.debug("Invalid JSON message received:", event.data);
3941
}
40-
setIsLoading(false); // Add this line
42+
setIsLoading(false);
4143
}, []);
4244

4345
const { sendJsonMessage, readyState } = useWebSocket<ChatMessage>(WS_URL, {
44-
onOpen: () => console.log("WebSocket connection opened!"),
45-
onClose: (event) => console.log("WebSocket connection closed!: ", event),
46-
onError: (event) => console.error("WebSocket error:", event),
46+
onOpen: () => {
47+
console.log("WebSocket connection opened!");
48+
setConnectionStatus("Connected");
49+
setIsReconnecting(false);
50+
},
51+
onClose: (event) => {
52+
console.log("WebSocket connection closed!: ", event);
53+
// Only show disconnected if we're not actively reconnecting
54+
if (!isReconnecting) {
55+
setConnectionStatus("Disconnected");
56+
}
57+
},
58+
onError: (event) => {
59+
console.error("WebSocket error:", event);
60+
setConnectionStatus("Error - Retrying...");
61+
setIsReconnecting(true);
62+
},
4763
onMessage: handleMessage,
64+
onReconnectStop: () => {
65+
console.log("Reconnection attempts stopped");
66+
setConnectionStatus("Disconnected");
67+
setIsReconnecting(false);
68+
},
69+
// Add retry configuration
70+
shouldReconnect: (closeEvent) => {
71+
console.log("Should reconnect:", closeEvent);
72+
setIsReconnecting(true);
73+
setConnectionStatus("Reconnecting...");
74+
return true; // Always try to reconnect
75+
},
76+
reconnectInterval: (attemptNumber) => {
77+
// Exponential backoff: 1s, 2s, 4s, 8s, max 30s
78+
return Math.min(1000 * Math.pow(2, attemptNumber), 30000);
79+
},
80+
reconnectAttempts: 10,
81+
// Connection timeout
82+
heartbeat: {
83+
message: JSON.stringify({ type: "ping" }),
84+
returnMessage: JSON.stringify({ type: "pong" }),
85+
timeout: 60000, // 60 seconds
86+
interval: 60000, // 60 seconds
87+
},
4888
});
4989

90+
// Update connection status based on readyState
91+
useEffect(() => {
92+
switch (readyState) {
93+
case ReadyState.CONNECTING:
94+
if (!isReconnecting) {
95+
setConnectionStatus("Connecting...");
96+
}
97+
break;
98+
case ReadyState.OPEN:
99+
setConnectionStatus("Connected");
100+
setIsReconnecting(false);
101+
break;
102+
case ReadyState.CLOSING:
103+
setConnectionStatus("Disconnecting...");
104+
break;
105+
case ReadyState.CLOSED:
106+
if (!isReconnecting) {
107+
setConnectionStatus("Disconnected");
108+
}
109+
break;
110+
case ReadyState.UNINSTANTIATED:
111+
setConnectionStatus("Not Connected");
112+
break;
113+
}
114+
}, [readyState, isReconnecting]);
115+
50116
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
51117
setInput(event.target.value);
52118
};
53119

54120
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
55121
event.preventDefault();
56122
if (input.trim() === "") return;
123+
124+
// Only prevent submission if truly disconnected (not reconnecting)
125+
if (readyState !== ReadyState.OPEN && !isReconnecting) return;
57126

58-
setIsLoading(true); // Add this line
127+
setIsLoading(true);
59128

60129
const chatMessage: ChatMessage = {
61130
role: "client",
@@ -79,8 +148,29 @@ export default function Chat({ username }: { username: string }) {
79148

80149
console.log(clientMessage);
81150

82-
sendJsonMessage(clientMessage);
83-
setInput("");
151+
try {
152+
sendJsonMessage(clientMessage);
153+
setInput("");
154+
} catch (error) {
155+
console.error("Failed to send message:", error);
156+
setIsLoading(false);
157+
// Optionally show error message to user
158+
}
159+
};
160+
161+
const getConnectionStatusColor = () => {
162+
if (isReconnecting) {
163+
return "text-yellow-500"; // Show yellow during reconnection attempts
164+
}
165+
166+
switch (readyState) {
167+
case ReadyState.OPEN:
168+
return "text-green-500";
169+
case ReadyState.CONNECTING:
170+
return "text-yellow-500";
171+
default:
172+
return "text-red-500";
173+
}
84174
};
85175

86176
return (
@@ -95,17 +185,22 @@ export default function Chat({ username }: { username: string }) {
95185
<div className="fixed bottom-32 right-4 bg-gray-800 text-white rounded-lg shadow-lg flex flex-col w-11/12 md:w-1/3">
96186
<div className="flex justify-between items-center p-4 bg-blue-500 rounded-t-lg">
97187
<h1 className="text-xl font-bold">Big Friendly Bank</h1>
98-
<button onClick={toggleChat} className="text-white">
99-
X
100-
</button>
188+
<div className="flex items-center gap-2">
189+
<span className={`text-sm ${getConnectionStatusColor()}`}>
190+
{connectionStatus}
191+
</span>
192+
<button onClick={toggleChat} className="text-white">
193+
X
194+
</button>
195+
</div>
101196
</div>
102197
<div className="flex flex-col flex-grow w-full max-w-screen-lg rounded-lg h-full overflow-y-auto">
103198
<ChatMessages messages={messageHistory} isLoading={isLoading} />
104199
<ChatInput
105200
input={input}
106201
handleSubmit={handleSubmit}
107202
handleInputChange={handleInputChange}
108-
isDisabled={readyState !== ReadyState.OPEN}
203+
isDisabled={readyState !== ReadyState.OPEN && !isReconnecting}
109204
/>
110205
</div>
111206
</div>
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
boto3==1.38.18
22
confluent-kafka==2.10.0
33
jsonschema==4.23.0
4-
requests==2.32.4
4+
requests==2.32.4
5+
httpx>=0.28.0
6+
cachetools>=6.1.0
7+
authlib==1.6.0

infrastructure/modules/backend/main.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ module "websocket_chat_api_lambda" {
117117
handler = "websocket_lambda.lambda_handler"
118118
runtime = "python3.12"
119119
architectures = [var.system_architecture]
120+
memory_size = 512
120121

121122
environment_variables = {
122123
SCHEMA_REGISTRY_URL = var.schema_registry_url
@@ -281,6 +282,7 @@ module "lambda_trigger_vector_search" {
281282
handler = "io.confluent.pie.search.SearchHandler::handleRequest"
282283
runtime = "java17"
283284
architectures = [var.system_architecture]
285+
memory_size = 1024
284286
timeout = 600
285287
snap_start = false
286288
provisioned_concurrent_executions = 1

infrastructure/modules/backend/search/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@
3030
<dependency>
3131
<groupId>io.confluent</groupId>
3232
<artifactId>kafka-json-schema-serializer</artifactId>
33-
<version>7.9.1-180</version>
33+
<version>7.7.1</version>
3434
</dependency>
3535

3636
<dependency>
3737
<groupId>org.apache.kafka</groupId>
3838
<artifactId>kafka-clients</artifactId>
39-
<version>3.9.1</version>
39+
<version>3.7.1</version>
4040
</dependency>
4141
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-secretsmanager -->
4242
<dependency>

infrastructure/modules/backend/search/src/main/java/io/confluent/pie/search/services/KafkaProducerSupplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public Producer<SearchResultsKey, SearchResults> get() {
4646
configuration.getCcCredentials(),
4747
configuration.getSrURL(),
4848
configuration.getSrCredentials(),
49-
false);
49+
true); // true to enable schema registry
5050

5151
return new KafkaProducer<>(properties);
5252
} catch (Throwable e) {

infrastructure/modules/backend/search/src/main/java/io/confluent/pie/search/services/impl/MongoService.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.mongodb.client.MongoCollection;
66
import com.mongodb.client.MongoDatabase;
77
import com.mongodb.client.model.search.FieldSearchPath;
8+
import com.mongodb.client.model.search.VectorSearchOptions;
89
import io.confluent.pie.search.models.Credentials;
910
import io.confluent.pie.search.models.SearchRequest;
1011
import io.confluent.pie.search.services.DBService;
@@ -19,7 +20,9 @@
1920
import static com.google.common.primitives.Doubles.asList;
2021
import static com.mongodb.client.model.Aggregates.project;
2122
import static com.mongodb.client.model.Aggregates.vectorSearch;
22-
import static com.mongodb.client.model.Projections.*;
23+
import static com.mongodb.client.model.Projections.exclude;
24+
import static com.mongodb.client.model.Projections.fields;
25+
import static com.mongodb.client.model.Projections.metaVectorSearchScore;
2326
import static com.mongodb.client.model.search.SearchPath.fieldPath;
2427

2528
/**
@@ -81,12 +84,14 @@ public List<Document> find(SearchRequest request) {
8184
final List<Document> products = new ArrayList<>();
8285
final boolean hasScoreFilter = request.minScore() > 0;
8386

87+
final VectorSearchOptions options = VectorSearchOptions.approximateVectorSearchOptions((long) request.numberOfCandidate());
88+
8489
final List<Bson> pipeline = List.of(vectorSearch(
8590
fieldSearchPath,
8691
asList(request.embeddings()),
8792
indexName,
88-
request.numberOfCandidate(),
89-
request.limit()),
93+
(long) request.limit(),
94+
options),
9095
project(fields(
9196
exclude(embeddingsFieldName),
9297
metaVectorSearchScore("score"))));

0 commit comments

Comments
 (0)