diff --git a/backend/.sqlx/query-318ed7a45d8326e3ecbc9727ee3812adaeeead8b39b007a32badadf67ec5ca17.json b/backend/.sqlx/query-2802cb8dcabbd601c9de44c85cf512b1d4961b4bb3b8d598ee69f1c1a154bac9.json similarity index 81% rename from backend/.sqlx/query-318ed7a45d8326e3ecbc9727ee3812adaeeead8b39b007a32badadf67ec5ca17.json rename to backend/.sqlx/query-2802cb8dcabbd601c9de44c85cf512b1d4961b4bb3b8d598ee69f1c1a154bac9.json index ae6ffd4e3c..82c375e241 100644 --- a/backend/.sqlx/query-318ed7a45d8326e3ecbc9727ee3812adaeeead8b39b007a32badadf67ec5ca17.json +++ b/backend/.sqlx/query-2802cb8dcabbd601c9de44c85cf512b1d4961b4bb3b8d598ee69f1c1a154bac9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, conversation_id, message_type as \"message_type: MessageType\", content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM flow_conversation_message\n WHERE conversation_id = $1\n AND created_seq > $2\n ORDER BY created_seq ASC\n LIMIT $3\n ", + "query": "SELECT id, conversation_id, message_type as \"message_type: MessageType\", content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM flow_conversation_message\n WHERE conversation_id = $1\n AND created_seq > $2\n AND ($4::message_type IS NULL OR message_type = $4)\n ORDER BY created_seq ASC\n LIMIT $3\n ", "describe": { "columns": [ { @@ -79,7 +79,19 @@ "Left": [ "Uuid", "Int8", - "Int8" + "Int8", + { + "Custom": { + "name": "message_type", + "kind": { + "Enum": [ + "user", + "assistant", + "tool" + ] + } + } + } ] }, "nullable": [ @@ -97,5 +109,5 @@ true ] }, - "hash": "318ed7a45d8326e3ecbc9727ee3812adaeeead8b39b007a32badadf67ec5ca17" + "hash": "2802cb8dcabbd601c9de44c85cf512b1d4961b4bb3b8d598ee69f1c1a154bac9" } diff --git a/backend/.sqlx/query-d6c65ce1d443d1e1783818d36c7eabd9633bbe5219bfd9683f336de19763cb58.json b/backend/.sqlx/query-456678fe3907343f7e991d4469ec1152788a32124ca6e9a101eca8ccea6fc840.json similarity index 78% rename from backend/.sqlx/query-d6c65ce1d443d1e1783818d36c7eabd9633bbe5219bfd9683f336de19763cb58.json rename to backend/.sqlx/query-456678fe3907343f7e991d4469ec1152788a32124ca6e9a101eca8ccea6fc840.json index d3d76191f8..20cfd4f979 100644 --- a/backend/.sqlx/query-d6c65ce1d443d1e1783818d36c7eabd9633bbe5219bfd9683f336de19763cb58.json +++ b/backend/.sqlx/query-456678fe3907343f7e991d4469ec1152788a32124ca6e9a101eca8ccea6fc840.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, conversation_id, message_type as \"message_type: MessageType\", content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM (\n SELECT id, conversation_id, message_type, content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM flow_conversation_message\n WHERE conversation_id = $1\n ORDER BY created_seq DESC\n LIMIT $2 OFFSET $3\n ) AS messages\n ORDER BY created_seq ASC\n ", + "query": "SELECT id, conversation_id, message_type as \"message_type: MessageType\", content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM (\n SELECT id, conversation_id, message_type, content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning\n FROM flow_conversation_message\n WHERE conversation_id = $1\n AND ($4::message_type IS NULL OR message_type = $4)\n ORDER BY created_seq DESC\n LIMIT $2 OFFSET $3\n ) AS messages\n ORDER BY created_seq ASC\n ", "describe": { "columns": [ { @@ -79,7 +79,19 @@ "Left": [ "Uuid", "Int8", - "Int8" + "Int8", + { + "Custom": { + "name": "message_type", + "kind": { + "Enum": [ + "user", + "assistant", + "tool" + ] + } + } + } ] }, "nullable": [ @@ -97,5 +109,5 @@ true ] }, - "hash": "d6c65ce1d443d1e1783818d36c7eabd9633bbe5219bfd9683f336de19763cb58" + "hash": "456678fe3907343f7e991d4469ec1152788a32124ca6e9a101eca8ccea6fc840" } diff --git a/backend/windmill-api-flow-conversations/src/lib.rs b/backend/windmill-api-flow-conversations/src/lib.rs index e380906298..dd8aaa4d5e 100644 --- a/backend/windmill-api-flow-conversations/src/lib.rs +++ b/backend/windmill-api-flow-conversations/src/lib.rs @@ -67,6 +67,10 @@ pub struct ListConversationsQuery { #[derive(Deserialize)] pub struct ListMessagesQuery { pub after_seq: Option, + /// Keep only the rows of one kind. A chat reopened while a run is going asks for the + /// newest `user` row this way: it carries the flow job of the turn that is running, and + /// an agent writes enough rows per round to push it many pages back in the transcript. + pub message_type: Option, } async fn list_conversations( @@ -226,6 +230,16 @@ async fn list_messages( Query(query): Query, ) -> JsonResult> { let (per_page, offset) = paginate(pagination); + + // `MessageType` carries a fourth kind the column has no label for and nothing writes. + // Refused here rather than bound: Postgres rejects the label, and what reaches the + // caller then is a database error about an enum rather than a word about their request. + if matches!(query.message_type, Some(MessageType::System)) { + return Err(windmill_common::error::Error::BadRequest( + "message_type must be one of user, assistant, tool".to_string(), + )); + } + let mut tx = user_db.clone().begin(&authed).await?; // Verify the conversation exists and belongs to the user @@ -252,12 +266,14 @@ async fn list_messages( FROM flow_conversation_message WHERE conversation_id = $1 AND created_seq > $2 + AND ($4::message_type IS NULL OR message_type = $4) ORDER BY created_seq ASC LIMIT $3 "#, conversation_id, after_seq, - per_page as i64 + per_page as i64, + query.message_type as Option ) .fetch_all(&mut *tx) .await? @@ -270,6 +286,7 @@ async fn list_messages( SELECT id, conversation_id, message_type, content, job_id, created_at, created_seq, step_name, success, tool_arguments, tool_result, reasoning FROM flow_conversation_message WHERE conversation_id = $1 + AND ($4::message_type IS NULL OR message_type = $4) ORDER BY created_seq DESC LIMIT $2 OFFSET $3 ) AS messages @@ -277,7 +294,8 @@ async fn list_messages( "#, conversation_id, per_page as i64, - offset as i64 + offset as i64, + query.message_type as Option ) .fetch_all(&mut *tx) .await? diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 0a936796d6..f55f54d6bc 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -12507,6 +12507,16 @@ paths: schema: type: integer format: int64 + - name: message_type + description: Keep only the messages of that kind + in: query + required: false + schema: + type: string + # The labels the MESSAGE_TYPE column actually has. The response schema below + # carries a fourth, `system`, which nothing writes and the column cannot hold; + # the handler refuses a request to filter on it. + enum: [user, assistant, tool] responses: "200": description: conversation messages