mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 00:06:14 +00:00
* refactor: extract windmill-dep-map crate for parallel api/worker compilation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve WebhookShared type mismatch and missing enterprise propagation - Make windmill-api webhook_util re-export from windmill-common instead of duplicating types, fixing Extension<WebhookShared> mismatch between windmill-store and windmill-api - Add windmill-api-jobs/enterprise to windmill-trigger enterprise feature so check_license_key_valid is available when trigger subcrates enable enterprise on windmill-trigger Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: stop trigger features from unconditionally enabling enterprise Move enterprise propagation for all trigger subcrates from individual trigger feature definitions to the enterprise feature itself, so enterprise is only enabled when explicitly requested. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove unused pub use re-exports and disable CI cargo cache - Remove unused re-exports from windmill-worker/src/lib.rs: trigger_dependents_to_recompute_dependencies, handle_job_error, and unused bun/otel items - Fix callers to use direct module paths instead - Add windmill-dep-map as dev-dependency for tests - Disable cargo cache in backend-check CI (faster from-scratch builds) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restore bun re-exports used by tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * all * chore: re-enable cargo cache for check_ee_full CI job Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
149 lines
4.0 KiB
Rust
149 lines
4.0 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::{self, FromRow};
|
|
use uuid::Uuid;
|
|
|
|
use crate::db::DB;
|
|
use crate::error::Result;
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, sqlx::Type)]
|
|
#[sqlx(type_name = "MESSAGE_TYPE", rename_all = "lowercase")]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum MessageType {
|
|
User,
|
|
Assistant,
|
|
System,
|
|
Tool,
|
|
}
|
|
|
|
#[derive(Serialize, FromRow, Debug)]
|
|
pub struct FlowConversation {
|
|
pub id: Uuid,
|
|
pub workspace_id: String,
|
|
pub flow_path: String,
|
|
pub title: Option<String>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
pub created_by: String,
|
|
}
|
|
|
|
pub async fn get_or_create_conversation_with_id(
|
|
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
w_id: &str,
|
|
flow_path: &str,
|
|
username: &str,
|
|
title: &str,
|
|
conversation_id: Uuid,
|
|
) -> Result<FlowConversation> {
|
|
// Check if conversation already exists
|
|
let existing_conversation = sqlx::query_as!(
|
|
FlowConversation,
|
|
"SELECT id, workspace_id, flow_path, title, created_at, updated_at, created_by
|
|
FROM flow_conversation
|
|
WHERE id = $1 AND workspace_id = $2",
|
|
conversation_id,
|
|
w_id
|
|
)
|
|
.fetch_optional(&mut **tx)
|
|
.await?;
|
|
|
|
if let Some(existing) = existing_conversation {
|
|
return Ok(existing);
|
|
}
|
|
|
|
// Truncate title to 25 char characters max
|
|
let title = if title.len() > 25 {
|
|
format!("{}...", &title[..25])
|
|
} else {
|
|
title.to_string()
|
|
};
|
|
|
|
// Create new conversation with provided ID
|
|
let conversation = sqlx::query_as!(
|
|
FlowConversation,
|
|
"INSERT INTO flow_conversation (id, workspace_id, flow_path, created_by, title)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, workspace_id, flow_path, title, created_at, updated_at, created_by",
|
|
conversation_id,
|
|
w_id,
|
|
flow_path,
|
|
username,
|
|
title
|
|
)
|
|
.fetch_one(&mut **tx)
|
|
.await?;
|
|
|
|
Ok(conversation)
|
|
}
|
|
|
|
/// Add a message to a conversation using an existing transaction
|
|
/// If the conversation doesn't exist, logs a warning and returns Ok (no error thrown)
|
|
/// This allows memory_id to be used for agent memory without requiring a conversation
|
|
pub async fn add_message_to_conversation_tx(
|
|
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
conversation_id: Uuid,
|
|
job_id: Option<Uuid>,
|
|
content: &str,
|
|
message_type: MessageType,
|
|
step_name: Option<&str>,
|
|
success: bool,
|
|
) -> Result<()> {
|
|
// Check if conversation exists first
|
|
let conversation_exists = sqlx::query!(
|
|
"SELECT EXISTS(SELECT 1 FROM flow_conversation WHERE id = $1) as \"exists!\"",
|
|
conversation_id
|
|
)
|
|
.fetch_one(&mut **tx)
|
|
.await?
|
|
.exists;
|
|
|
|
if !conversation_exists {
|
|
tracing::warn!(
|
|
"Conversation {} does not exist. Skipping message insertion. This is expected when flows are called from apps (memory_id is used for agent memory only).",
|
|
conversation_id
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Insert the message
|
|
sqlx::query!(
|
|
"INSERT INTO flow_conversation_message (conversation_id, message_type, content, job_id, step_name, success)
|
|
VALUES ($1, $2, $3, $4, $5, $6)",
|
|
conversation_id,
|
|
message_type as MessageType,
|
|
content,
|
|
job_id,
|
|
step_name,
|
|
success
|
|
)
|
|
.execute(&mut **tx)
|
|
.await?;
|
|
|
|
// Update conversation updated_at timestamp
|
|
sqlx::query!(
|
|
"UPDATE flow_conversation SET updated_at = NOW() WHERE id = $1",
|
|
conversation_id
|
|
)
|
|
.execute(&mut **tx)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Delete all memory for a conversation from the database
|
|
pub async fn delete_conversation_memory(
|
|
db: &DB,
|
|
workspace_id: &str,
|
|
conversation_id: Uuid,
|
|
) -> Result<()> {
|
|
sqlx::query!(
|
|
"DELETE FROM ai_agent_memory WHERE workspace_id = $1 AND conversation_id = $2",
|
|
workspace_id,
|
|
conversation_id
|
|
)
|
|
.execute(db)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|