dify_client/
api.rs

1//! This module provides a client for interacting with the Dify API.
2//!
3//! The `Api` struct in this module allows you to send requests to the Dify API and handle the responses.
4//! It provides methods for various API endpoints such as sending chat messages, uploading files, and executing workflows.
5//! The module also defines several request and response structs that are used for interacting with the API.
6//!
7//! # Example
8//!
9//! ```rust
10//! use dify_client::api::Api;
11//! use dify_client::request::ChatMessagesRequest;
12//!
13//! #[tokio::main]
14//! async fn main() {
15//!     // Create a client for interacting with the Dify API
16//!     let client = dify_client::Client::new("https://api.dify.ai", "API_KEY");
17//!
18//!     // Create an API instance using the client
19//!     let api = Api::new(&client);
20//!
21//!     // Create a chat message request
22//!     let request = ChatMessagesRequest {
23//!         query: "What is the weather today?".to_string(),
24//!         user: "user123".to_string(),
25//!         ..Default::default()  
26//!     };
27//!
28//!     // Send the chat message request and get the response
29//!     let response = api.chat_messages(request).await;
30//!
31//!     // Handle the response
32//!     match response {
33//!         Ok(response) => {
34//!             // Do something with the response
35//!             println!("Chat message sent successfully: {:?}", response);
36//!         }
37//!         Err(error) => {
38//!             // Handle the error
39//!             eprintln!("Failed to send chat message: {}", error);
40//!         }
41//!     }
42//! }
43//! ```
44//! This module provides a client for interacting with the Dify API.
45//!
46use super::{
47    client::Client,
48    http::{multipart, Method, Request},
49    request::{
50        AudioToTextRequest, Bytes, ChatMessagesRequest, CompletionMessagesRequest,
51        ConversationsDeleteRequest, ConversationsRenameRequest, ConversationsRequest,
52        FilesUploadRequest, MessagesFeedbacksRequest, MessagesRequest, MessagesSuggestedRequest,
53        MetaRequest, ParametersRequest, ResponseMode, StreamTaskStopRequest, TextToAudioRequest,
54        WorkflowsRunRequest,
55    },
56    response::{
57        parse_error_response, parse_response, AudioToTextResponse, ChatMessagesResponse,
58        CompletionMessagesResponse, ConversationsResponse, FilesUploadResponse, MessagesResponse,
59        MessagesSuggestedResponse, MetaResponse, ParametersResponse, ResultResponse,
60        SseMessageEventStream, WorkflowsRunResponse,
61    },
62};
63use anyhow::{bail, Result as AnyResult};
64use eventsource_stream::Eventsource;
65use futures::stream::Stream;
66use std::fmt::{Display, Formatter, Result as FmtResult};
67
68/// API 路径
69#[derive(Debug)]
70pub enum ApiPath {
71    /// 发送对话消息, 创建会话消息。
72    ChatMessages,
73    /// 上传文件
74    /// 上传文件(目前仅支持图片)并在发送消息时使用,可实现图文多模态理解。  
75    /// 支持 png, jpg, jpeg, webp, gif 格式。  
76    /// 上传的文件仅供当前终端用户使用。
77    FilesUpload,
78    /// 停止响应, 仅支持流式模式。
79    ChatMessagesStop,
80    /// 消息反馈(点赞, 消息终端用户反馈、点赞,方便应用开发者优化输出预期。
81    MessagesFeedbacks,
82    /// 获取下一轮建议问题列表
83    MessagesSuggested,
84    /// 获取会话历史消息, 滚动加载形式返回历史聊天记录,第一页返回最新 limit 条,即:倒序返回。
85    Messages,
86    /// 获取会话列表, 获取当前用户的会话列表,默认返回最近的 20 条。
87    Conversations,
88    /// 删除会话
89    ConversationsDelete,
90    /// 会话重命名, 对会话进行重命名,会话名称用于显示在支持多会话的客户端上。
91    ConversationsRename,
92    /// 语音转文字
93    AudioToText,
94    /// 文字转语音
95    TextToAudio,
96    /// 获取应用配置信息, 用于进入页面一开始,获取功能开关、输入参数名称、类型及默认值等使用。
97    Parameters,
98    /// 获取应用Meta信息, 用于获取工具icon
99    Meta,
100
101    /// workflow
102    /// 执行 workflow
103    WorkflowsRun,
104    /// 停止响应, 仅支持流式模式。
105    WorkflowsStop,
106
107    /// completion 文本生成
108    /// 发送请求给文本生成型应用
109    CompletionMessages,
110    /// 文本生成停止响应
111    CompletionMessagesStop,
112}
113
114/// API 路径
115impl ApiPath {
116    /// 获取 API 路径
117    /// # Example
118    /// ```no_run
119    /// use dify_client::api::ApiPath;
120    /// let path = ApiPath::ChatMessages;
121    /// assert_eq!(path.as_str(), "/v1/chat-messages");
122    /// ```
123    pub fn as_str(&self) -> &'static str {
124        match self {
125            ApiPath::ChatMessages => "/v1/chat-messages",
126            ApiPath::FilesUpload => "/v1/files/upload",
127            ApiPath::ChatMessagesStop => "/v1/chat-messages/{task_id}/stop",
128            ApiPath::MessagesFeedbacks => "/v1/messages/{message_id}/feedbacks",
129            ApiPath::MessagesSuggested => "/v1/messages/{message_id}/suggested",
130            ApiPath::Messages => "/v1/messages",
131            ApiPath::Conversations => "/v1/conversations",
132            ApiPath::ConversationsDelete => "/v1/conversations/{conversation_id}",
133            ApiPath::ConversationsRename => "/v1/conversations/{conversation_id}/name",
134            ApiPath::AudioToText => "/v1/audio-to-text",
135            ApiPath::TextToAudio => "/v1/text-to-audio",
136            ApiPath::Parameters => "/v1/parameters",
137            ApiPath::Meta => "/v1/meta",
138            ApiPath::WorkflowsRun => "/v1/workflows/run",
139            ApiPath::WorkflowsStop => "/v1/workflows/{task_id}/stop",
140            ApiPath::CompletionMessages => "/v1/completion-messages",
141            ApiPath::CompletionMessagesStop => "/v1/completion-messages/{task_id}/stop",
142        }
143    }
144}
145
146impl Display for ApiPath {
147    fn fmt(&self, f: &mut Formatter) -> FmtResult {
148        write!(f, "{}", self.as_str())
149    }
150}
151
152/// 发送请求前的钩子函数
153type BeforeSend = Option<Box<dyn Fn(Request) -> Request + Send + Sync>>;
154
155/// Dify API
156pub struct Api<'a> {
157    before_send_hook: BeforeSend,
158    pub(crate) client: &'a Client,
159}
160
161/// Dify API
162impl<'a> Api<'a> {
163    /// Creates a new `Api` instance with the specified client.
164    ///
165    /// # Arguments
166    /// * `client` - The client for interacting with the Dify API.
167    pub fn new(client: &'a Client) -> Self {
168        Self {
169            before_send_hook: None,
170            client,
171        }
172    }
173
174    /// Sets a hook function to be called before sending a request.
175    /// The hook function is called with the request before it is sent.
176    /// The hook function can be used to modify the request before it is sent.
177    /// The hook function should return the modified request.
178    /// The hook function can be used to add headers, query parameters, etc.
179    ///
180    /// # Arguments
181    /// * `hook` - The hook function to be called before sending a request.
182    pub fn before_send<F>(&mut self, hook: F)
183    where
184        F: Fn(Request) -> Request + Send + Sync + 'static,
185    {
186        self.before_send_hook = Some(Box::new(hook));
187    }
188
189    /// Sends a request to the Dify API and returns the response.
190    ///
191    /// # Arguments
192    /// * `req` - The request to send.
193    ///
194    /// # Returns
195    /// A `Result` containing the response or an error.
196    async fn send(&self, mut req: Request) -> AnyResult<reqwest::Response> {
197        if let Some(hook) = self.before_send_hook.as_ref() {
198            req = hook(req);
199        }
200        self.client.execute(req).await
201    }
202
203    /// Builds the API request URL.
204    ///
205    /// # Arguments
206    /// * `api_path` - The API path.
207    ///
208    /// # Returns
209    /// The request URL.
210    fn build_request_api(&self, api_path: ApiPath) -> String {
211        self.client.config.base_url.clone() + api_path.as_str()
212    }
213
214    /// Creates a chat message request.
215    ///
216    /// # Arguments
217    /// * `req` - The chat message request data.
218    ///
219    /// # Returns
220    /// A `Result` containing the request or an error.
221    ///
222    /// # Errors
223    /// Returns an error if the request cannot be created.
224    fn create_chat_messages_request(&self, req: ChatMessagesRequest) -> AnyResult<Request> {
225        let url = self.build_request_api(ApiPath::ChatMessages);
226        self.client.create_request(url, Method::POST, req)
227    }
228
229    /// Sends a chat message request to the Dify API and returns the response.
230    ///
231    /// # Arguments
232    /// * `req_data` - The chat message request data.
233    ///
234    /// # Returns
235    /// A `Result` containing the chat message response or an error.
236    pub async fn chat_messages(
237        &self,
238        mut req_data: ChatMessagesRequest,
239    ) -> AnyResult<ChatMessagesResponse> {
240        req_data.response_mode = ResponseMode::Blocking;
241
242        let req = self.create_chat_messages_request(req_data)?;
243        let resp = self.send(req).await?;
244        let text = resp.text().await?;
245        parse_response::<ChatMessagesResponse>(&text)
246    }
247
248    /// Sends a chat message request to the Dify API and returns the response as a stream.
249    ///
250    /// # Arguments
251    /// * `req_data` - The chat message request data.
252    ///
253    /// # Returns
254    /// A `Result` containing SSE message event stream or an error.
255    ///
256    /// # Errors
257    /// Returns an error if the request cannot be created or the stream fails.
258    ///
259    /// # Stream response usage
260    /// ```ignore
261    /// while let Some(event) = stream.next().await {
262    ///     match event {
263    ///         Ok(event) => {
264    ///             println!("event: {:?}", event);
265    ///         }
266    ///         Err(error) => {
267    ///             eprintln!("Failed to receive event: {}", error);
268    ///         }
269    ///     }
270    /// }
271    /// ```
272    pub async fn chat_messages_stream(
273        &self,
274        mut req_data: ChatMessagesRequest,
275    ) -> AnyResult<SseMessageEventStream<impl Stream<Item = Result<Bytes, reqwest::Error>>>> {
276        req_data.response_mode = ResponseMode::Streaming;
277
278        let req = self.create_chat_messages_request(req_data)?;
279        let resp = self.send(req).await?;
280        let stream = resp.bytes_stream().eventsource();
281        let s = SseMessageEventStream::new(stream);
282
283        Ok(s)
284    }
285
286    /// Sends a request to upload files to the Dify API and returns the response.  
287    /// 上传文件(目前仅支持图片)并在发送消息时使用,可实现图文多模态理解。  
288    /// 支持 png, jpg, jpeg, webp, gif 格式。  
289    /// 上传的文件仅供当前终端用户使用。  
290    ///
291    /// # Arguments
292    /// * `req_data` - The files upload request data.
293    ///
294    /// # Returns
295    /// A `Result` containing the files upload response or an error.
296    pub async fn files_upload(
297        &self,
298        req_data: FilesUploadRequest,
299    ) -> AnyResult<FilesUploadResponse> {
300        if !infer::is_image(&req_data.file) {
301            bail!("FilesUploadRequest.File Illegal");
302        }
303        let kind = infer::get(&req_data.file).expect("Failed to get file type");
304        let file_part = multipart::Part::stream(req_data.file)
305            .file_name(format!("image_file.{}", kind.extension()))
306            .mime_str(kind.mime_type())?;
307        let form = multipart::Form::new()
308            .text("user", req_data.user)
309            .part("file", file_part);
310
311        let url = self.build_request_api(ApiPath::FilesUpload);
312        let req = self.client.create_multipart_request(url, form)?;
313        let resp = self.send(req).await?;
314        let text = resp.text().await?;
315        parse_response::<FilesUploadResponse>(&text)
316    }
317
318    /// Sends a request to stop stream task from the Dify API and returns the response.
319    /// 仅支持流式模式。
320    ///
321    /// # Arguments
322    /// * `req_data` - The stream task stop request data.
323    /// * `api_path` - The API path.
324    ///
325    /// # Returns
326    /// A `Result` containing the stream task stop response or an error.
327    async fn stream_task_stop(
328        &self,
329        mut req_data: StreamTaskStopRequest,
330        api_path: ApiPath,
331    ) -> AnyResult<ResultResponse> {
332        if req_data.task_id.is_empty() {
333            bail!("StreamTaskStopRequest.TaskId Illegal");
334        }
335
336        let url = self.build_request_api(api_path);
337        let url = url.replace("{task_id}", &req_data.task_id);
338
339        req_data.task_id = String::new();
340        let req = self.client.create_request(url, Method::POST, req_data)?;
341        let resp = self.send(req).await?;
342        let text = resp.text().await?;
343        parse_response::<ResultResponse>(&text)
344    }
345
346    /// Sends a request to stop stream chat messages to the Dify API and returns the response.
347    ///
348    /// # Arguments
349    /// * `req_data` - The chat message stop request data.
350    ///
351    /// # Returns
352    /// A `Result` containing the chat message stop response or an error.
353    pub async fn chat_messages_stop(
354        &self,
355        req_data: StreamTaskStopRequest,
356    ) -> AnyResult<ResultResponse> {
357        self.stream_task_stop(req_data, ApiPath::ChatMessagesStop)
358            .await
359    }
360
361    /// Sends a request to retrieve suggested messages from the Dify API and returns the response.
362    ///
363    /// # Arguments
364    /// * `req_data` - The messages suggested request data.
365    ///
366    /// # Returns
367    /// A `Result` containing the messages suggested response or an error.
368    pub async fn messages_suggested(
369        &self,
370        mut req_data: MessagesSuggestedRequest,
371    ) -> AnyResult<MessagesSuggestedResponse> {
372        if req_data.message_id.is_empty() {
373            bail!("MessagesSuggestedRequest.MessageID Illegal");
374        }
375
376        let url = self.build_request_api(ApiPath::MessagesSuggested);
377        let url = url.replace("{message_id}", &req_data.message_id);
378
379        req_data.message_id = String::new();
380        let req = self.client.create_request(url, Method::GET, req_data)?;
381        let resp = self.send(req).await?;
382        let text = resp.text().await?;
383        parse_response::<MessagesSuggestedResponse>(&text)
384    }
385
386    /// Sends a request to retrieve messages feedbacks from the Dify API and returns the response.
387    ///
388    /// # Arguments
389    /// * `req_data` - The messages feedbacks request data.
390    ///
391    /// # Returns
392    /// A `Result` containing the messages feedbacks response or an error.
393    pub async fn messages_feedbacks(
394        &self,
395        mut req_data: MessagesFeedbacksRequest,
396    ) -> AnyResult<ResultResponse> {
397        if req_data.message_id.is_empty() {
398            bail!("MessagesFeedbacksRequest.MessageID Illegal");
399        }
400
401        let url = self.build_request_api(ApiPath::MessagesFeedbacks);
402        let url = url.replace("{message_id}", &req_data.message_id);
403
404        req_data.message_id = String::new();
405        let req = self.client.create_request(url, Method::POST, req_data)?;
406        let resp = self.send(req).await?;
407        let text = resp.text().await?;
408        parse_response::<ResultResponse>(&text)
409    }
410
411    /// Sends a request to retrieve conversations from the Dify API and returns the response.
412    ///
413    /// # Arguments
414    /// * `req_data` - The conversations request data.
415    ///
416    /// # Returns
417    /// A `Result` containing the conversations response or an error.
418    pub async fn conversations(
419        &self,
420        req_data: ConversationsRequest,
421    ) -> AnyResult<ConversationsResponse> {
422        if req_data.user.is_empty() {
423            bail!("ConversationsRequest.User Illegal");
424        }
425
426        let url = self.build_request_api(ApiPath::Conversations);
427        let req = self.client.create_request(url, Method::GET, req_data)?;
428        let resp = self.send(req).await?;
429        let text = resp.text().await?;
430        parse_response::<ConversationsResponse>(&text)
431    }
432
433    /// Sends a request to retrieve history messages from the Dify API and returns the response.
434    ///
435    /// # Arguments
436    /// * `req_data` - The messages request data.
437    ///
438    /// # Returns
439    /// A `Result` containing the messages response or an error.
440    pub async fn messages(&self, req_data: MessagesRequest) -> AnyResult<MessagesResponse> {
441        if req_data.conversation_id.is_empty() {
442            bail!("MessagesRequest.ConversationID Illegal");
443        }
444
445        let url = self.build_request_api(ApiPath::Messages);
446        let req = self.client.create_request(url, Method::GET, req_data)?;
447        let resp = self.send(req).await?;
448        let text = resp.text().await?;
449        parse_response::<MessagesResponse>(&text)
450    }
451
452    /// Sends a request to rename a conversation in the Dify API and returns the response.
453    ///
454    /// # Arguments
455    /// * `req_data` - The conversations rename request data.
456    ///
457    /// # Returns
458    /// A `Result` containing the conversations rename response or an error.
459    pub async fn conversations_renaming(
460        &self,
461        mut req_data: ConversationsRenameRequest,
462    ) -> AnyResult<ResultResponse> {
463        if req_data.conversation_id.is_empty() {
464            bail!("ConversationsRenameRequest.ConversationID Illegal");
465        }
466        if req_data.auto_generate && req_data.name.is_none() {
467            bail!("ConversationsRenameRequest.Name Illegal");
468        }
469
470        let url = self.build_request_api(ApiPath::ConversationsRename);
471        let url = url.replace("{conversation_id}", &req_data.conversation_id);
472
473        req_data.conversation_id = String::new();
474        let req = self.client.create_request(url, Method::POST, req_data)?;
475        let resp = self.send(req).await?;
476        let text = resp.text().await?;
477        parse_response::<ResultResponse>(&text)
478    }
479
480    /// Sends a request to delete a conversation in the Dify API and returns the response.
481    ///
482    /// # Arguments
483    /// * `req_data` - The conversations delete request data.
484    ///
485    /// # Returns
486    /// A `Result` containing the conversations delete response or an error.
487    pub async fn conversations_delete(
488        &self,
489        mut req_data: ConversationsDeleteRequest,
490    ) -> AnyResult<()> {
491        if req_data.conversation_id.is_empty() {
492            bail!("ConversationsDeleteRequest.ConversationID Illegal");
493        }
494
495        let url = self.build_request_api(ApiPath::ConversationsDelete);
496        let url = url.replace("{conversation_id}", &req_data.conversation_id);
497
498        req_data.conversation_id = String::new();
499        let req = self.client.create_request(url, Method::DELETE, req_data)?;
500        let resp = self.send(req).await?;
501        // http 204 means success ?
502        if resp.status().as_u16() == 204 {
503            Ok(())
504        } else {
505            // parse message type
506            let text = resp.text().await?;
507            parse_error_response(&text)
508        }
509    }
510
511    /// Sends a request to convert audio to text in the Dify API and returns the response.
512    ///
513    /// # Arguments
514    /// * `req_data` - The audio to text request data.
515    ///
516    /// # Returns
517    /// A `Result` containing the audio to text response or an error.
518    pub async fn text_to_audio(&self, req_data: TextToAudioRequest) -> AnyResult<Bytes> {
519        if req_data.text.is_empty() {
520            bail!("TextToAudioRequest.Text Illegal");
521        }
522
523        let url = self.build_request_api(ApiPath::TextToAudio);
524        let req = self.client.create_request(url, Method::POST, req_data)?;
525        let resp = self.send(req).await?;
526        // check if header is audio
527        let content_type = resp.headers().get(reqwest::header::CONTENT_TYPE);
528        let content_type = content_type
529            .ok_or(anyhow::anyhow!("Content-Type is missing"))?
530            .to_str()?;
531        // check if content_type is audio
532        if content_type.starts_with("audio/") {
533            let bytes = resp.bytes().await?;
534            return Ok(bytes);
535        }
536        let text = resp.text().await?;
537        parse_error_response(&text)
538    }
539
540    /// Sends a request to convert audio to text in the Dify API and returns the response.
541    ///
542    /// # Arguments
543    /// * `req_data` - The audio to text request data.
544    ///
545    /// # Returns
546    /// A `Result` containing the audio to text response or an error.
547    pub async fn audio_to_text(
548        &self,
549        req_data: AudioToTextRequest,
550    ) -> AnyResult<AudioToTextResponse> {
551        if !infer::is_audio(&req_data.file) {
552            bail!("AudioToTextRequest.File Illegal");
553        }
554        let kind = infer::get(&req_data.file).expect("Failed to get file type");
555        let file_part = multipart::Part::stream(req_data.file)
556            .file_name(format!("audio_file.{}", kind.extension()))
557            .mime_str(kind.mime_type())?;
558        let form = multipart::Form::new()
559            .text("user", req_data.user)
560            .part("file", file_part);
561
562        let url = self.build_request_api(ApiPath::AudioToText);
563        let req = self.client.create_multipart_request(url, form)?;
564        let resp = self.send(req).await?;
565        let text = resp.text().await?;
566        parse_response::<AudioToTextResponse>(&text)
567    }
568
569    /// Sends a request to retrieve parameters from the Dify API and returns the response.
570    ///
571    /// # Arguments
572    /// * `req_data` - The parameters request data.
573    ///
574    /// # Returns
575    /// A `Result` containing the parameters response or an error.
576    pub async fn parameters(&self, req_data: ParametersRequest) -> AnyResult<ParametersResponse> {
577        if req_data.user.is_empty() {
578            bail!("ParametersRequest.User Illegal");
579        }
580
581        let url = self.build_request_api(ApiPath::Parameters);
582        let req = self.client.create_request(url, Method::GET, req_data)?;
583        let resp = self.send(req).await?;
584        let text = resp.text().await?;
585        parse_response::<ParametersResponse>(&text)
586    }
587
588    /// Sends a request to retrieve meta information from the Dify API and returns the response.
589    ///
590    /// # Arguments
591    /// * `req_data` - The meta request data.
592    ///
593    /// # Returns
594    /// A `Result` containing the meta response or an error.
595    pub async fn meta(&self, req_data: MetaRequest) -> AnyResult<MetaResponse> {
596        if req_data.user.is_empty() {
597            bail!("MetaRequest.User Illegal");
598        }
599
600        let url = self.build_request_api(ApiPath::Meta);
601        let req = self.client.create_request(url, Method::GET, req_data)?;
602        let resp = self.send(req).await?;
603        let text = resp.text().await?;
604        parse_response::<MetaResponse>(&text)
605    }
606
607    /// Creates a request to run workflows from the Dify API.
608    ///
609    /// # Arguments
610    /// * `req` - The workflows run request data.
611    ///     
612    /// # Returns
613    /// A `Result` containing the request or an error.
614    fn create_workflows_run_request(&self, req: WorkflowsRunRequest) -> AnyResult<Request> {
615        let url = self.build_request_api(ApiPath::WorkflowsRun);
616        self.client.create_request(url, Method::POST, req)
617    }
618
619    /// Sends a request to run workflows from the Dify API and returns the response.
620    ///
621    /// # Arguments
622    /// * `req_data` - The workflows run request data.
623    ///
624    /// # Returns
625    /// A `Result` containing the workflows run response or an error.
626    pub async fn workflows_run(
627        &self,
628        mut req_data: WorkflowsRunRequest,
629    ) -> AnyResult<WorkflowsRunResponse> {
630        req_data.response_mode = ResponseMode::Blocking;
631
632        let req = self.create_workflows_run_request(req_data)?;
633        let resp = self.send(req).await?;
634        let text = resp.text().await?;
635        parse_response::<WorkflowsRunResponse>(&text)
636    }
637
638    /// Sends a request to run workflows from the Dify API and returns the response as a stream.
639    ///
640    /// # Arguments
641    /// * `req_data` - The workflows run request data.
642    ///
643    /// # Returns
644    /// A `Result` containing SSE message event stream or an error.
645    ///
646    /// # Errors
647    /// Returns an error if the request cannot be created or the stream fails.
648    pub async fn workflows_run_stream(
649        &self,
650        mut req_data: WorkflowsRunRequest,
651    ) -> AnyResult<SseMessageEventStream<impl Stream<Item = Result<Bytes, reqwest::Error>>>> {
652        req_data.response_mode = ResponseMode::Streaming;
653
654        let req = self.create_workflows_run_request(req_data)?;
655        let resp = self.send(req).await?;
656        let stream = resp.bytes_stream().eventsource();
657        let s = SseMessageEventStream::new(stream);
658        Ok(s)
659    }
660
661    /// Sends a request to stop stream workflows from the Dify API and returns the response.
662    ///
663    /// # Arguments
664    /// * `req_data` - The stream task stop request data.
665    ///
666    /// # Returns
667    /// A `Result` containing the stream task stop response or an error.
668    pub async fn workflows_stop(
669        &self,
670        req_data: StreamTaskStopRequest,
671    ) -> AnyResult<ResultResponse> {
672        self.stream_task_stop(req_data, ApiPath::WorkflowsStop)
673            .await
674    }
675
676    /// Creates a request to create completion messages from the Dify API.
677    ///
678    /// # Arguments
679    /// * `req` - The completion messages request data.
680    ///
681    /// # Returns
682    /// A `Result` containing the request or an error.
683    fn create_completion_messages_request(
684        &self,
685        req: CompletionMessagesRequest,
686    ) -> AnyResult<Request> {
687        let url = self.build_request_api(ApiPath::CompletionMessages);
688        self.client.create_request(url, Method::POST, req)
689    }
690
691    /// Sends a request to create completion messages from the Dify API and returns the response.
692    /// 发送请求给文本生成型应用
693    ///
694    /// # Arguments
695    /// * `req_data` - The completion messages request data.
696    ///
697    /// # Returns
698    /// A `Result` containing the completion messages response or an error.
699    pub async fn completion_messages(
700        &self,
701        mut req_data: CompletionMessagesRequest,
702    ) -> AnyResult<CompletionMessagesResponse> {
703        req_data.response_mode = ResponseMode::Blocking;
704
705        let req = self.create_completion_messages_request(req_data)?;
706        let resp = self.send(req).await?;
707        let text = resp.text().await?;
708        parse_response::<CompletionMessagesResponse>(&text)
709    }
710
711    /// Sends a request to create completion messages from the Dify API and returns the response as a stream.
712    ///
713    /// # Arguments
714    /// * `req_data` - The completion messages request data.
715    ///
716    /// # Returns
717    /// A `Result` containing SSE message event stream or an error.
718    ///
719    /// # Errors
720    /// Returns an error if the request cannot be created or the stream fails.
721    pub async fn completion_messages_stream(
722        &self,
723        mut req_data: CompletionMessagesRequest,
724    ) -> AnyResult<SseMessageEventStream<impl Stream<Item = Result<Bytes, reqwest::Error>>>> {
725        req_data.response_mode = ResponseMode::Streaming;
726
727        let req = self.create_completion_messages_request(req_data)?;
728        let resp = self.send(req).await?;
729        let stream = resp.bytes_stream().eventsource();
730        let s = SseMessageEventStream::new(stream);
731        Ok(s)
732    }
733
734    /// Sends a request to stop stream completion messages from the Dify API and returns the response.
735    /// 文本生成停止响应
736    ///
737    /// # Arguments
738    /// * `req_data` - The stream task stop request data.
739    ///
740    /// # Returns
741    /// A `Result` containing the stream task stop response or an error.
742    pub async fn completion_messages_stop(
743        &self,
744        req_data: StreamTaskStopRequest,
745    ) -> AnyResult<ResultResponse> {
746        self.stream_task_stop(req_data, ApiPath::CompletionMessagesStop)
747            .await
748    }
749}