use anyhow::Result;
use rig::completion::ToolDefinition;
use rig::tool::Tool;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;
use tokio::sync::mpsc;
use super::common::parameters_schema;
crate::define_tool_error!(ContentUpdateError);
#[derive(Debug, Clone)]
pub enum ContentUpdate {
Commit {
emoji: Option<String>,
title: String,
message: String,
},
PR { content: String },
Review { content: String },
}
pub const CONTENT_UPDATE_CHANNEL_CAPACITY: usize = 100;
pub type ContentUpdateSender = mpsc::Sender<ContentUpdate>;
pub type ContentUpdateReceiver = mpsc::Receiver<ContentUpdate>;
#[must_use]
pub fn create_content_update_channel() -> (ContentUpdateSender, ContentUpdateReceiver) {
mpsc::channel(CONTENT_UPDATE_CHANNEL_CAPACITY)
}
#[derive(Clone)]
pub struct UpdateCommitTool {
sender: Arc<ContentUpdateSender>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct UpdateCommitArgs {
#[serde(default)]
pub emoji: Option<String>,
pub title: String,
#[serde(default)]
pub message: Option<String>,
}
impl UpdateCommitTool {
#[must_use]
pub fn new(sender: ContentUpdateSender) -> Self {
Self {
sender: Arc::new(sender),
}
}
}
impl Tool for UpdateCommitTool {
const NAME: &'static str = "update_commit";
type Error = ContentUpdateError;
type Args = UpdateCommitArgs;
type Output = String;
async fn definition(&self, _: String) -> ToolDefinition {
ToolDefinition {
name: "update_commit".to_string(),
description: "Update the current commit message. Use this when the user asks you to modify, change, or rewrite the commit message. The update will be applied immediately.".to_string(),
parameters: parameters_schema::<UpdateCommitArgs>(),
}
}
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
tracing::info!(
"update_commit tool called! emoji={:?}, title={}, message_len={}",
args.emoji,
args.title,
args.message.as_ref().map_or(0, std::string::String::len)
);
let update = ContentUpdate::Commit {
emoji: args.emoji.clone(),
title: args.title.clone(),
message: args.message.clone().unwrap_or_default(),
};
self.sender.try_send(update).map_err(|e| {
tracing::error!("Failed to send content update: {}", e);
ContentUpdateError(format!("Failed to send update: {}", e))
})?;
tracing::info!("Content update sent successfully via channel");
let result = json!({
"success": true,
"message": "Commit message updated successfully",
"new_title": args.title,
"emoji": args.emoji
});
serde_json::to_string_pretty(&result).map_err(|e| ContentUpdateError(e.to_string()))
}
}
#[derive(Clone)]
pub struct UpdatePRTool {
sender: Arc<ContentUpdateSender>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct UpdatePRArgs {
pub content: String,
}
impl UpdatePRTool {
#[must_use]
pub fn new(sender: ContentUpdateSender) -> Self {
Self {
sender: Arc::new(sender),
}
}
}
impl Tool for UpdatePRTool {
const NAME: &'static str = "update_pr";
type Error = ContentUpdateError;
type Args = UpdatePRArgs;
type Output = String;
async fn definition(&self, _: String) -> ToolDefinition {
ToolDefinition {
name: "update_pr".to_string(),
description: "Update the current PR description. Use this when the user asks you to modify, change, or rewrite the PR content.".to_string(),
parameters: parameters_schema::<UpdatePRArgs>(),
}
}
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
let content_len = args.content.len();
let update = ContentUpdate::PR {
content: args.content,
};
self.sender
.try_send(update)
.map_err(|e| ContentUpdateError(format!("Failed to send update: {}", e)))?;
let result = json!({
"success": true,
"message": "PR description updated successfully",
"content_length": content_len
});
serde_json::to_string_pretty(&result).map_err(|e| ContentUpdateError(e.to_string()))
}
}
#[derive(Clone)]
pub struct UpdateReviewTool {
sender: Arc<ContentUpdateSender>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct UpdateReviewArgs {
pub content: String,
}
impl UpdateReviewTool {
#[must_use]
pub fn new(sender: ContentUpdateSender) -> Self {
Self {
sender: Arc::new(sender),
}
}
}
impl Tool for UpdateReviewTool {
const NAME: &'static str = "update_review";
type Error = ContentUpdateError;
type Args = UpdateReviewArgs;
type Output = String;
async fn definition(&self, _: String) -> ToolDefinition {
ToolDefinition {
name: "update_review".to_string(),
description: "Update the current code review. Use this when the user asks you to modify, change, or rewrite the review content.".to_string(),
parameters: parameters_schema::<UpdateReviewArgs>(),
}
}
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
let content_len = args.content.len();
let update = ContentUpdate::Review {
content: args.content,
};
self.sender
.try_send(update)
.map_err(|e| ContentUpdateError(format!("Failed to send update: {}", e)))?;
let result = json!({
"success": true,
"message": "Review updated successfully",
"content_length": content_len
});
serde_json::to_string_pretty(&result).map_err(|e| ContentUpdateError(e.to_string()))
}
}