use crate::client::IFlowClient;
use crate::error::Result;
use crate::types::{IFlowOptions, Message};
use futures::stream::StreamExt;
use std::time::Duration;
use tokio::time::timeout;
pub async fn query(prompt: &str) -> Result<String> {
let default_timeout = IFlowOptions::default().timeout;
query_with_timeout(prompt, default_timeout).await
}
pub async fn query_with_config(prompt: &str, options: IFlowOptions) -> Result<String> {
let timeout_secs = options.timeout;
let message_timeout_secs = (timeout_secs / 10.0).min(1.0).max(0.1);
match timeout(Duration::from_secs_f64(timeout_secs), async {
let local = tokio::task::LocalSet::new();
local
.run_until(async {
tracing::debug!("Creating IFlowClient with custom options");
let mut client = IFlowClient::new(Some(options));
tracing::debug!("Connecting to iFlow...");
client.connect().await?;
tracing::debug!("Connected to iFlow");
tracing::debug!("Sending message: {}", prompt);
client.send_message(prompt, None).await?;
tracing::debug!("Message sent");
let mut response = String::new();
let mut message_stream = client.messages();
let mut prompt_finished = false;
while !prompt_finished {
match timeout(
Duration::from_secs_f64(message_timeout_secs),
message_stream.next(),
)
.await
{
Ok(Some(message)) => {
tracing::debug!("Received message: {:?}", message);
match message {
Message::Assistant { content } => {
response.push_str(&content);
}
Message::TaskFinish { .. } => {
prompt_finished = true;
}
_ => {}
}
}
Ok(None) => {
tracing::debug!("Message stream ended");
prompt_finished = true;
}
Err(_) => {
}
}
}
tracing::debug!("Query completed, response length: {}", response.len());
client.disconnect().await?;
Ok(response.trim().to_string())
})
.await
})
.await
{
Ok(result) => result,
Err(_) => Err(crate::error::IFlowError::Timeout(
"Operation timed out".to_string(),
)),
}
}
pub async fn query_with_timeout(prompt: &str, timeout_secs: f64) -> Result<String> {
let message_timeout_secs = (timeout_secs / 10.0).min(1.0).max(0.1);
match timeout(Duration::from_secs_f64(timeout_secs), async {
let local = tokio::task::LocalSet::new();
local
.run_until(async {
let options = IFlowOptions::new()
.with_timeout(timeout_secs)
.with_process_config(
crate::types::ProcessConfig::new()
.enable_auto_start()
.stdio_mode(),
);
tracing::debug!(
"Creating IFlowClient with options: auto_start={}, start_port={:?}",
options.process.auto_start,
options.process.start_port
);
let mut client = IFlowClient::new(Some(options));
tracing::debug!("Connecting to iFlow...");
client.connect().await?;
tracing::debug!("Connected to iFlow");
tracing::debug!("Sending message: {}", prompt);
client.send_message(prompt, None).await?;
tracing::debug!("Message sent");
let mut response = String::new();
let mut message_stream = client.messages();
let mut prompt_finished = false;
while !prompt_finished {
match timeout(
Duration::from_secs_f64(message_timeout_secs),
message_stream.next(),
)
.await
{
Ok(Some(message)) => {
tracing::debug!("Received message: {:?}", message);
match message {
Message::Assistant { content } => {
response.push_str(&content);
}
Message::TaskFinish { .. } => {
prompt_finished = true;
}
_ => {}
}
}
Ok(None) => {
tracing::debug!("Message stream ended");
prompt_finished = true;
}
Err(_) => {
}
}
}
tracing::debug!("Query completed, response length: {}", response.len());
client.disconnect().await?;
Ok(response.trim().to_string())
})
.await
})
.await
{
Ok(result) => result,
Err(_) => Err(crate::error::IFlowError::Timeout(
"Operation timed out".to_string(),
)),
}
}
pub async fn query_stream(prompt: &str) -> Result<impl futures::Stream<Item = String>> {
query_stream_with_timeout(prompt, 120.0).await
}
pub async fn query_stream_with_config(
prompt: &str,
options: IFlowOptions,
) -> Result<impl futures::Stream<Item = String>> {
let local = tokio::task::LocalSet::new();
local
.run_until(async {
let mut client = IFlowClient::new(Some(options));
client.connect().await?;
client.send_message(prompt, None).await?;
let (tx, rx) = futures::channel::mpsc::unbounded();
let message_stream = client.messages();
tokio::task::spawn_local(async move {
futures::pin_mut!(message_stream);
while let Some(message) = message_stream.next().await {
match message {
Message::Assistant { content } => {
if tx.unbounded_send(content).is_err() {
break;
}
}
Message::TaskFinish { .. } => {
break;
}
_ => {}
}
}
let _ = client.disconnect().await;
});
Ok(rx)
})
.await
}
pub async fn query_stream_with_timeout(
prompt: &str,
timeout_secs: f64,
) -> Result<impl futures::Stream<Item = String>> {
let local = tokio::task::LocalSet::new();
local
.run_until(async {
let options = IFlowOptions::new().with_timeout(timeout_secs);
let mut client = IFlowClient::new(Some(options));
client.connect().await?;
client.send_message(prompt, None).await?;
let (tx, rx) = futures::channel::mpsc::unbounded();
let message_stream = client.messages();
tokio::task::spawn_local(async move {
futures::pin_mut!(message_stream);
while let Some(message) = message_stream.next().await {
match message {
Message::Assistant { content } => {
if tx.unbounded_send(content).is_err() {
break;
}
}
Message::TaskFinish { .. } => {
break;
}
_ => {}
}
}
let _ = client.disconnect().await;
});
Ok(rx)
})
.await
}