|
| 1 | +use crate::models::call_local_model; |
| 2 | +use serde::Deserialize; |
| 3 | + |
| 4 | +const ORCHESTRATOR_PROMPT: &str = r#"You are an expert prompt engineer. Your task is to help a user craft the perfect prompt for a powerful AI model. |
| 5 | +The user has provided the following query: "{query}" |
| 6 | +
|
| 7 | +Analyze the user's query and generate three distinct proposals for a better prompt. |
| 8 | +Each proposal should be a self-contained, ready-to-use prompt. |
| 9 | +Use the 5W method (What, Who, When, Where, How) to explore different angles of the user's request. |
| 10 | +Rank the proposals by your internal confidence, from least confident to most confident. |
| 11 | +
|
| 12 | +Format your response as a JSON object with a single key "proposals" which is an array of three strings. |
| 13 | +Example: |
| 14 | +{ |
| 15 | + "proposals": [ |
| 16 | + "Proposal 1 (least confident)", |
| 17 | + "Proposal 2 (medium confident)", |
| 18 | + "Proposal 3 (most confident)" |
| 19 | + ] |
| 20 | +} |
| 21 | +"#; |
| 22 | + |
| 23 | +const REVISE_PROMPT: &str = r#"You are an expert prompt engineer. A user wants to revise a prompt proposal. |
| 24 | +
|
| 25 | +Original Proposal: "{proposal}" |
| 26 | +User's Revision: "{revision}" |
| 27 | +
|
| 28 | +Your task is to integrate the user's revision into the original proposal to create a new, single, improved prompt. |
| 29 | +The new prompt should be self-contained and ready to use. |
| 30 | +
|
| 31 | +Format your response as a JSON object with a single key "proposal" which is a string. |
| 32 | +Example: |
| 33 | +{ |
| 34 | + "proposal": "This is the new, revised prompt." |
| 35 | +} |
| 36 | +"#; |
| 37 | + |
| 38 | +#[derive(Deserialize, Debug)] |
| 39 | +struct ProposalsResponse { |
| 40 | + proposals: Vec<String>, |
| 41 | +} |
| 42 | + |
| 43 | +#[derive(Deserialize, Debug)] |
| 44 | +struct ReviseResponse { |
| 45 | + proposal: String, |
| 46 | +} |
| 47 | + |
| 48 | +pub async fn generate_proposals( |
| 49 | + query: &str, |
| 50 | + endpoint: &str, |
| 51 | + model: &str, |
| 52 | +) -> Result<Vec<String>, anyhow::Error> { |
| 53 | + let prompt = ORCHESTRATOR_PROMPT.replace("{query}", query); |
| 54 | + let response_str = call_local_model(endpoint, model, &prompt).await?; |
| 55 | + |
| 56 | + // Attempt to find the start of the JSON object |
| 57 | + if let Some(json_start) = response_str.find("{") { |
| 58 | + let json_str = &response_str[json_start..]; |
| 59 | + match serde_json::from_str::<ProposalsResponse>(json_str) { |
| 60 | + Ok(response) => Ok(response.proposals), |
| 61 | + Err(e) => Err(anyhow::anyhow!("Failed to parse proposals JSON: {}", e)), |
| 62 | + } |
| 63 | + } else { |
| 64 | + Err(anyhow::anyhow!("No JSON object found in model response")) |
| 65 | + } |
| 66 | +} |
| 67 | + |
| 68 | +pub async fn revise_proposal( |
| 69 | + proposal: &str, |
| 70 | + revision: &str, |
| 71 | + endpoint: &str, |
| 72 | + model: &str, |
| 73 | +) -> Result<String, anyhow::Error> { |
| 74 | + let prompt = REVISE_PROMPT |
| 75 | + .replace("{proposal}", proposal) |
| 76 | + .replace("{revision}", revision); |
| 77 | + let response_str = call_local_model(endpoint, model, &prompt).await?; |
| 78 | + |
| 79 | + // Attempt to find the start of the JSON object |
| 80 | + if let Some(json_start) = response_str.find("{") { |
| 81 | + let json_str = &response_str[json_start..]; |
| 82 | + match serde_json::from_str::<ReviseResponse>(json_str) { |
| 83 | + Ok(response) => Ok(response.proposal), |
| 84 | + Err(e) => Err(anyhow::anyhow!("Failed to parse revision JSON: {}", e)), |
| 85 | + } |
| 86 | + } else { |
| 87 | + Err(anyhow::anyhow!("No JSON object found in model response")) |
| 88 | + } |
| 89 | +} |
0 commit comments