Skip to content

Commit 7183fca

Browse files
feat(search_processing_service): chats kafka (#5304)
1 parent e7a9e3a commit 7183fca

30 files changed

Lines changed: 839 additions & 237 deletions

File tree

.github/workspace-dep-closures.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@
13041304
"crates/authentication_service_client",
13051305
"crates/bot_id",
13061306
"crates/channel_sender",
1307+
"crates/chat",
13071308
"crates/cowlike",
13081309
"crates/crm",
13091310
"crates/document_sub_type",
@@ -1359,6 +1360,7 @@
13591360
"crates/properties",
13601361
"crates/rate_limit",
13611362
"crates/remote_env_var",
1363+
"crates/roles_and_permissions",
13621364
"crates/s3_key",
13631365
"crates/sqs_client",
13641366
"crates/system_properties",
@@ -4924,6 +4926,7 @@
49244926
"crates/call",
49254927
"crates/channel_sender",
49264928
"crates/channels",
4929+
"crates/chat",
49274930
"crates/comms_db_client",
49284931
"crates/connection",
49294932
"crates/connection_gateway_client",
@@ -4996,6 +4999,7 @@
49964999
"crates/properties",
49975000
"crates/rate_limit",
49985001
"crates/remote_env_var",
5002+
"crates/roles_and_permissions",
49995003
"crates/s3_client",
50005004
"crates/s3_key",
50015005
"crates/share_permission_db_utils",

.sqlx/query-92e2916a16d165600f457c778ac3db9ae11c6625c948798e19ca19da4a30f3c1.json renamed to .sqlx/query-eb360ba6aad69fe2c501baa8b61b4243051bd3bde6e517014cd5768334fd7757.json

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

Cargo.lock

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

crates/macro_db_client/fixtures/chat_message_info.sql

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ VALUES ('a1111111-1111-1111-1111-111111111111', 'user', 'user@user.com', 'stripe
44
INSERT INTO public."User" ("id","email","stripeCustomerId","macro_user_id")
55
VALUES ('macro|user@user.com', 'user@user.com','stripe_id','a1111111-1111-1111-1111-111111111111');
66

7-
INSERT INTO public."Chat" ("id","name","userId","model","createdAt","updatedAt","isPersistent")
7+
INSERT INTO public."Chat" ("id","name","userId","model","createdAt","updatedAt","deletedAt","isPersistent")
88
VALUES
9-
('chat-persistent', 'persistent chat', 'macro|user@user.com', 'gpt-4o', '2024-01-01 00:00:00', '2024-01-01 00:00:00', true),
10-
('chat-ephemeral', 'ephemeral chat', 'macro|user@user.com', 'gpt-4o', '2024-01-01 00:00:00', '2024-01-01 00:00:00', false);
9+
('chat-persistent', 'persistent chat', 'macro|user@user.com', 'gpt-4o', '2024-01-01 00:00:00', '2024-01-01 00:00:00', NULL, true),
10+
('chat-ephemeral', 'ephemeral chat', 'macro|user@user.com', 'gpt-4o', '2024-02-01 00:00:00', '2024-02-01 00:00:00', NULL, false),
11+
('chat-deleted', 'deleted chat', 'macro|user@user.com', 'gpt-4o', '2024-03-01 00:00:00', '2024-03-01 00:00:00', '2024-03-04 05:06:07.890', true);
1112

12-
INSERT INTO public."ChatMessage" ("id","content","role","chatId")
13+
INSERT INTO public."ChatMessage" ("id","content","role","chatId","createdAt","updatedAt")
1314
VALUES
14-
('msg-persistent', '"codebase brighter"', 'user', 'chat-persistent'),
15-
('msg-ephemeral', '"another message"', 'user', 'chat-ephemeral');
15+
('msg-persistent', '"codebase brighter"', 'user', 'chat-persistent', '2024-01-02 03:04:05.123', '2024-01-03 04:05:06.789'),
16+
('msg-ephemeral', '"another message"', 'assistant', 'chat-ephemeral', '2024-02-02 03:04:05.123', '2024-02-03 04:05:06.789'),
17+
('msg-deleted', '"remove from search"', 'assistant', 'chat-deleted', '2024-03-02 03:04:05.123', '2024-03-03 04:05:06.789');

crates/macro_db_client/src/chat/get.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ pub struct ChatMessageInfo {
258258
pub name: String,
259259
pub content: String,
260260
pub role: String,
261+
pub owner_user_id: String,
262+
pub created_at: DateTime<Utc>,
263+
pub updated_at: DateTime<Utc>,
261264
pub deleted_at: Option<DateTime<Utc>>,
262265
}
263266

@@ -277,7 +280,10 @@ pub async fn get_chat_message_info(
277280
m.content as "content",
278281
c.name as "name",
279282
m.role as "role",
280-
c."deletedAt"::timestamptz as "deleted_at"
283+
c."deletedAt"::timestamptz as "deleted_at",
284+
c."userId" as "owner_user_id",
285+
m."createdAt" as "created_at",
286+
m."updatedAt" as "updated_at"
281287
FROM
282288
"ChatMessage" m
283289
JOIN
@@ -300,6 +306,9 @@ pub async fn get_chat_message_info(
300306
name: row.name,
301307
content,
302308
role: row.role,
309+
owner_user_id: row.owner_user_id,
310+
created_at: DateTime::<Utc>::from_naive_utc_and_offset(row.created_at, Utc),
311+
updated_at: DateTime::<Utc>::from_naive_utc_and_offset(row.updated_at, Utc),
303312
deleted_at: row.deleted_at,
304313
}))
305314
}

crates/macro_db_client/src/chat/get/test.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,86 @@ use super::*;
22
use sqlx::{Pool, Postgres};
33

44
#[sqlx::test(fixtures(path = "../../../fixtures", scripts("chat_message_info")))]
5-
async fn persistent_chat_messages_are_indexed_for_search(
5+
async fn returns_persistent_and_ephemeral_chat_message_metadata(
66
pool: Pool<Postgres>,
77
) -> anyhow::Result<()> {
88
let persistent = get_chat_message_info(&pool, "chat-persistent", "msg-persistent")
99
.await?
1010
.expect("persistent chat message should be returned for indexing");
11+
assert_eq!(persistent.name, "persistent chat");
1112
assert_eq!(persistent.content, "codebase brighter");
13+
assert_eq!(persistent.role, "user");
14+
assert_eq!(persistent.owner_user_id, "macro|user@user.com");
15+
assert_eq!(
16+
persistent.created_at,
17+
"2024-01-02T03:04:05.123Z".parse::<DateTime<Utc>>()?
18+
);
19+
assert_eq!(
20+
persistent.updated_at,
21+
"2024-01-03T04:05:06.789Z".parse::<DateTime<Utc>>()?
22+
);
1223
assert!(persistent.deleted_at.is_none());
1324

1425
let ephemeral = get_chat_message_info(&pool, "chat-ephemeral", "msg-ephemeral")
1526
.await?
1627
.expect("ephemeral chat message should be returned for indexing");
28+
assert_eq!(ephemeral.name, "ephemeral chat");
1729
assert_eq!(ephemeral.content, "another message");
30+
assert_eq!(ephemeral.role, "assistant");
31+
assert_eq!(ephemeral.owner_user_id, "macro|user@user.com");
32+
assert_eq!(
33+
ephemeral.created_at,
34+
"2024-02-02T03:04:05.123Z".parse::<DateTime<Utc>>()?
35+
);
36+
assert_eq!(
37+
ephemeral.updated_at,
38+
"2024-02-03T04:05:06.789Z".parse::<DateTime<Utc>>()?
39+
);
1840
assert!(ephemeral.deleted_at.is_none());
1941

2042
Ok(())
2143
}
44+
45+
#[sqlx::test(fixtures(path = "../../../fixtures", scripts("chat_message_info")))]
46+
async fn returns_soft_deleted_chat_message_metadata(pool: Pool<Postgres>) -> anyhow::Result<()> {
47+
let message = get_chat_message_info(&pool, "chat-deleted", "msg-deleted")
48+
.await?
49+
.expect("soft-deleted chat message should be returned for removal");
50+
51+
assert_eq!(message.name, "deleted chat");
52+
assert_eq!(message.content, "remove from search");
53+
assert_eq!(message.role, "assistant");
54+
assert_eq!(message.owner_user_id, "macro|user@user.com");
55+
assert_eq!(
56+
message.created_at,
57+
"2024-03-02T03:04:05.123Z".parse::<DateTime<Utc>>()?
58+
);
59+
assert_eq!(
60+
message.updated_at,
61+
"2024-03-03T04:05:06.789Z".parse::<DateTime<Utc>>()?
62+
);
63+
assert_eq!(
64+
message.deleted_at,
65+
Some("2024-03-04T05:06:07.890Z".parse::<DateTime<Utc>>()?)
66+
);
67+
68+
Ok(())
69+
}
70+
71+
#[sqlx::test(fixtures(path = "../../../fixtures", scripts("chat_message_info")))]
72+
async fn returns_none_for_missing_or_mismatched_message_id(
73+
pool: Pool<Postgres>,
74+
) -> anyhow::Result<()> {
75+
assert!(
76+
get_chat_message_info(&pool, "chat-persistent", "msg-missing")
77+
.await?
78+
.is_none()
79+
);
80+
assert!(
81+
get_chat_message_info(&pool, "chat-ephemeral", "msg-persistent")
82+
.await?
83+
.is_none()
84+
);
85+
86+
Ok(())
87+
}

crates/projects/src/domain/ports.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,9 @@ pub trait ShaCounterPort: Send + Sync + 'static {
206206
) -> impl Future<Output = anyhow::Result<()>> + Send;
207207
}
208208

209-
/// Port for publishing cross-family search and document-deletion work.
210-
// TODO: Remove this port and its SQS adapter after the chats and documents migrations.
209+
/// Port for publishing document search-removal and deletion work.
210+
// TODO: Remove this port and its SQS adapter after the documents migration.
211211
pub trait ProjectSearchIndexer: Send + Sync + 'static {
212-
/// Remove chats from the search index.
213-
fn remove_chats(
214-
&self,
215-
chat_ids: Vec<String>,
216-
) -> impl Future<Output = anyhow::Result<()>> + Send;
217-
218212
/// Remove documents from the search index.
219213
fn remove_documents(
220214
&self,

crates/projects/src/domain/service.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ where
7777
pub sha_counter: Sha,
7878
/// Entity-access inheritance manager.
7979
pub entity_access_management_service: Eam,
80-
/// Search and deletion queue publisher.
80+
/// Document search and deletion queue publisher.
8181
pub search_indexer: Idx,
8282
/// Project lifecycle event broker.
8383
pub macro_event_broker: B,
@@ -534,13 +534,6 @@ where
534534
.map(|(document_id, _)| document_id.clone())
535535
.collect::<Vec<_>>();
536536

537-
if !purged.chat_ids.is_empty() {
538-
let _ = self
539-
.search_indexer
540-
.remove_chats(purged.chat_ids)
541-
.await
542-
.inspect_err(|error| tracing::error!(error = ?error, "unable to enqueue purged chats for search"));
543-
}
544537
if !purged.documents.is_empty() {
545538
let _ = self
546539
.search_indexer

crates/projects/src/domain/service/tests.rs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,6 @@ impl EntityAccessManagementService for NullPort {
153153
}
154154

155155
impl ProjectSearchIndexer for NullPort {
156-
async fn remove_chats(&self, _chat_ids: Vec<String>) -> anyhow::Result<()> {
157-
unreachable!()
158-
}
159-
160156
async fn remove_documents(&self, _document_ids: Vec<String>) -> anyhow::Result<()> {
161157
unreachable!()
162158
}
@@ -224,10 +220,6 @@ struct RecordingIndexer {
224220
}
225221

226222
impl ProjectSearchIndexer for RecordingIndexer {
227-
async fn remove_chats(&self, _chat_ids: Vec<String>) -> anyhow::Result<()> {
228-
unreachable!()
229-
}
230-
231223
async fn remove_documents(&self, _document_ids: Vec<String>) -> anyhow::Result<()> {
232224
unreachable!()
233225
}
@@ -1574,10 +1566,6 @@ impl OrderedIndexer {
15741566
}
15751567

15761568
impl ProjectSearchIndexer for OrderedIndexer {
1577-
async fn remove_chats(&self, _chat_ids: Vec<String>) -> anyhow::Result<()> {
1578-
self.record("chats")
1579-
}
1580-
15811569
async fn remove_documents(&self, _document_ids: Vec<String>) -> anyhow::Result<()> {
15821570
self.record("documents")
15831571
}
@@ -1740,7 +1728,7 @@ async fn permanent_delete_runs_external_work_after_committed_purge() {
17401728

17411729
assert_eq!(
17421730
*events.lock().unwrap(),
1743-
vec!["repo", "sha", "chats", "documents", "document_deletes"]
1731+
vec!["repo", "sha", "documents", "document_deletes"]
17441732
);
17451733
let published = published.lock().unwrap();
17461734
assert_eq!(published.len(), 1);

crates/projects/src/outbound/sqs_search_indexer.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
//! SQS adapter for cross-family search cleanup and document deletion.
1+
//! SQS adapter for document search cleanup and deletion.
22
33
use std::sync::Arc;
44

5-
use sqs_client::search::{SearchQueueMessage, chat::RemoveChatMessage, document::DocumentId};
5+
use sqs_client::search::{SearchQueueMessage, document::DocumentId};
66

77
use crate::domain::ports::ProjectSearchIndexer;
88

9-
/// SQS-backed cross-family search cleanup and document-deletion adapter.
9+
/// SQS-backed document search cleanup and deletion adapter.
1010
#[derive(Clone)]
1111
pub struct SqsProjectSearchIndexer {
1212
sqs: Arc<sqs_client::SQS>,
@@ -20,24 +20,6 @@ impl SqsProjectSearchIndexer {
2020
}
2121

2222
impl ProjectSearchIndexer for SqsProjectSearchIndexer {
23-
#[tracing::instrument(skip(self), err)]
24-
async fn remove_chats(&self, chat_ids: Vec<String>) -> anyhow::Result<()> {
25-
let messages = chat_ids
26-
.into_iter()
27-
.map(|chat_id| {
28-
SearchQueueMessage::RemoveChatMessage(RemoveChatMessage {
29-
chat_id,
30-
message_id: None,
31-
index_override: None,
32-
})
33-
})
34-
.collect();
35-
36-
self.sqs
37-
.bulk_send_message_to_search_event_queue(messages)
38-
.await
39-
}
40-
4123
#[tracing::instrument(skip(self), err)]
4224
async fn remove_documents(&self, document_ids: Vec<String>) -> anyhow::Result<()> {
4325
let messages = document_ids

0 commit comments

Comments
 (0)