use crate::error::OpenAIResponseError;
use crate::nlp::get_openai_response;
use polars::prelude::*;
use serde_json::Value;
use std::error::Error;
fn extract_code_blocks(text: &str) -> String {
let mut code_block = String::new();
let mut in_code_block = false;
for line in text.lines() {
if line.starts_with("```") {
if in_code_block {
in_code_block = false;
} else {
in_code_block = true;
}
} else if in_code_block {
code_block.push_str(line);
code_block.push('\n');
}
}
code_block
}
pub struct AIDataFrame {
pub inner_df: DataFrame,
pub last_command: Option<String>,
}
impl AIDataFrame {
pub fn new(inner_df: DataFrame) -> Self {
AIDataFrame {
inner_df,
last_command: None,
}
}
pub async fn ask(&mut self, query: &str) -> Result<String, Box<dyn Error>> {
let ai_response = get_openai_response(query, &self.inner_df).await.unwrap();
let ai_result: Value = serde_json::from_str((*ai_response).into()).unwrap();
if let Some(content) = ai_result["choices"][0]["message"]["content"].as_str() {
let code_block = extract_code_blocks(content);
self.last_command = Some(code_block.clone());
Ok(code_block)
} else {
Err(Box::new(OpenAIResponseError::new(
"AI response content is empty or not a string.",
)) as Box<dyn Error>)
}
}
pub fn shape(&self) -> (usize, usize) {
self.inner_df.shape()
}
pub fn width(&self) -> usize {
self.inner_df.width()
}
pub fn height(&self) -> usize {
self.inner_df.height()
}
pub fn select(&self, columns: &[String]) -> Result<DataFrame, PolarsError> {
self.inner_df.select(columns)
}
pub fn cross_join(
&self,
other: &DataFrame,
suffix: Option<&str>,
slice: Option<(i64, usize)>,
) -> Result<DataFrame, PolarsError> {
self.inner_df.cross_join(other, suffix, slice)
}
}