1use std::fmt;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct Example {
32 pub input: String,
34 pub output: String,
36}
37
38impl Example {
39 pub fn new(input: impl Into<String>, output: impl Into<String>) -> Self {
41 Self {
42 input: input.into(),
43 output: output.into(),
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
53pub struct FewShotPrompt {
54 instruction: String,
55 examples: Vec<Example>,
56 query: Option<String>,
57 input_prefix: String,
58 output_prefix: String,
59 separator: String,
60}
61
62impl FewShotPrompt {
63 pub fn new(instruction: impl Into<String>) -> Self {
65 Self {
66 instruction: instruction.into(),
67 examples: Vec::new(),
68 query: None,
69 input_prefix: "Input: ".to_string(),
70 output_prefix: "Output: ".to_string(),
71 separator: "\n\n".to_string(),
72 }
73 }
74
75 pub fn add_example(
77 &mut self,
78 input: impl Into<String>,
79 output: impl Into<String>,
80 ) -> &mut Self {
81 self.examples.push(Example::new(input, output));
82 self
83 }
84
85 pub fn add_examples(&mut self, examples: Vec<Example>) -> &mut Self {
87 self.examples.extend(examples);
88 self
89 }
90
91 pub fn set_query(&mut self, query: impl Into<String>) -> &mut Self {
93 self.query = Some(query.into());
94 self
95 }
96
97 pub fn with_prefixes(
99 &mut self,
100 input_prefix: impl Into<String>,
101 output_prefix: impl Into<String>,
102 ) -> &mut Self {
103 self.input_prefix = input_prefix.into();
104 self.output_prefix = output_prefix.into();
105 self
106 }
107
108 pub fn with_separator(&mut self, separator: impl Into<String>) -> &mut Self {
110 self.separator = separator.into();
111 self
112 }
113
114 pub fn build(&self) -> String {
116 let mut prompt = self.instruction.clone();
117 prompt.push_str(&self.separator);
118
119 for example in &self.examples {
121 prompt.push_str(&self.input_prefix);
122 prompt.push_str(&example.input);
123 prompt.push('\n');
124 prompt.push_str(&self.output_prefix);
125 prompt.push_str(&example.output);
126 prompt.push_str(&self.separator);
127 }
128
129 if let Some(query) = &self.query {
131 prompt.push_str(&self.input_prefix);
132 prompt.push_str(query);
133 prompt.push('\n');
134 prompt.push_str(&self.output_prefix);
135 }
136
137 prompt
138 }
139}
140
141#[derive(Debug, Clone)]
146pub struct ChainOfThought {
147 question: String,
148 instruction: String,
149 examples: Vec<(String, String)>, }
151
152impl ChainOfThought {
153 pub fn new(question: impl Into<String>) -> Self {
155 Self {
156 question: question.into(),
157 instruction: "Let's approach this step-by-step:".to_string(),
158 examples: Vec::new(),
159 }
160 }
161
162 pub fn with_instruction(mut self, instruction: impl Into<String>) -> Self {
164 self.instruction = instruction.into();
165 self
166 }
167
168 pub fn add_example(
170 mut self,
171 question: impl Into<String>,
172 reasoning: impl Into<String>,
173 ) -> Self {
174 self.examples.push((question.into(), reasoning.into()));
175 self
176 }
177
178 pub fn build(&self) -> String {
180 let mut prompt = String::new();
181
182 for (q, reasoning) in &self.examples {
184 prompt.push_str("Question: ");
185 prompt.push_str(q);
186 prompt.push_str("\n\n");
187 prompt.push_str(reasoning);
188 prompt.push_str("\n\n---\n\n");
189 }
190
191 prompt.push_str("Question: ");
193 prompt.push_str(&self.question);
194 prompt.push_str("\n\n");
195 prompt.push_str(&self.instruction);
196
197 prompt
198 }
199}
200
201#[derive(Debug, Clone)]
206pub struct RolePrompt {
207 system_message: Option<String>,
208 messages: Vec<(Role, String)>,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub enum Role {
214 System,
216 User,
218 Assistant,
220}
221
222impl fmt::Display for Role {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 match self {
225 Role::System => write!(f, "System"),
226 Role::User => write!(f, "User"),
227 Role::Assistant => write!(f, "Assistant"),
228 }
229 }
230}
231
232impl RolePrompt {
233 pub fn new() -> Self {
235 Self {
236 system_message: None,
237 messages: Vec::new(),
238 }
239 }
240
241 pub fn system(mut self, message: impl Into<String>) -> Self {
243 self.system_message = Some(message.into());
244 self
245 }
246
247 pub fn user(mut self, message: impl Into<String>) -> Self {
249 self.messages.push((Role::User, message.into()));
250 self
251 }
252
253 pub fn assistant(mut self, message: impl Into<String>) -> Self {
255 self.messages.push((Role::Assistant, message.into()));
256 self
257 }
258
259 pub fn add_message(mut self, role: Role, message: impl Into<String>) -> Self {
261 self.messages.push((role, message.into()));
262 self
263 }
264
265 pub fn build(&self) -> String {
267 let mut prompt = String::new();
268
269 if let Some(system) = &self.system_message {
271 prompt.push_str("System: ");
272 prompt.push_str(system);
273 prompt.push_str("\n\n");
274 }
275
276 for (role, message) in &self.messages {
278 prompt.push_str(&format!("{}: ", role));
279 prompt.push_str(message);
280 prompt.push_str("\n\n");
281 }
282
283 prompt.trim_end().to_string()
284 }
285
286 pub fn split(&self) -> (Option<String>, Vec<(Role, String)>) {
288 (self.system_message.clone(), self.messages.clone())
289 }
290}
291
292impl Default for RolePrompt {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298#[derive(Debug, Clone)]
303pub struct InstructionPrompt {
304 task: String,
305 context: Vec<String>,
306 constraints: Vec<String>,
307 examples: Vec<String>,
308 format: Option<String>,
309}
310
311impl InstructionPrompt {
312 pub fn new(task: impl Into<String>) -> Self {
314 Self {
315 task: task.into(),
316 context: Vec::new(),
317 constraints: Vec::new(),
318 examples: Vec::new(),
319 format: None,
320 }
321 }
322
323 pub fn add_context(mut self, context: impl Into<String>) -> Self {
325 self.context.push(context.into());
326 self
327 }
328
329 pub fn add_constraint(mut self, constraint: impl Into<String>) -> Self {
331 self.constraints.push(constraint.into());
332 self
333 }
334
335 pub fn add_example(mut self, example: impl Into<String>) -> Self {
337 self.examples.push(example.into());
338 self
339 }
340
341 pub fn with_format(mut self, format: impl Into<String>) -> Self {
343 self.format = Some(format.into());
344 self
345 }
346
347 pub fn build(&self) -> String {
349 let mut prompt = String::new();
350
351 prompt.push_str("Task: ");
353 prompt.push_str(&self.task);
354 prompt.push_str("\n\n");
355
356 if !self.context.is_empty() {
358 prompt.push_str("Context:\n");
359 for (i, ctx) in self.context.iter().enumerate() {
360 prompt.push_str(&format!("{}. {}\n", i + 1, ctx));
361 }
362 prompt.push('\n');
363 }
364
365 if !self.constraints.is_empty() {
367 prompt.push_str("Constraints:\n");
368 for constraint in &self.constraints {
369 prompt.push_str("- ");
370 prompt.push_str(constraint);
371 prompt.push('\n');
372 }
373 prompt.push('\n');
374 }
375
376 if !self.examples.is_empty() {
378 prompt.push_str("Examples:\n");
379 for (i, example) in self.examples.iter().enumerate() {
380 prompt.push_str(&format!("{}. {}\n", i + 1, example));
381 }
382 prompt.push('\n');
383 }
384
385 if let Some(format) = &self.format {
387 prompt.push_str("Expected Format: ");
388 prompt.push_str(format);
389 prompt.push_str("\n\n");
390 }
391
392 prompt.trim_end().to_string()
393 }
394}
395
396pub struct SystemPrompts;
398
399impl SystemPrompts {
400 pub fn expert_assistant() -> String {
402 "You are an expert assistant with deep knowledge across multiple domains. \
403 Provide accurate, detailed, and well-reasoned responses. When uncertain, \
404 acknowledge limitations and suggest where to find more information."
405 .to_string()
406 }
407
408 pub fn code_assistant() -> String {
410 "You are an expert programming assistant. Provide clean, efficient, and \
411 well-documented code. Explain your reasoning and suggest best practices. \
412 Always consider edge cases and error handling."
413 .to_string()
414 }
415
416 pub fn teacher() -> String {
418 "You are a patient and knowledgeable teacher. Break down complex topics \
419 into simple, understandable explanations. Use analogies and examples. \
420 Encourage learning by asking thought-provoking questions."
421 .to_string()
422 }
423
424 pub fn analyst() -> String {
426 "You are a thorough analyst. Examine information critically, consider \
427 multiple perspectives, identify patterns and trends. Support your \
428 conclusions with evidence and clear reasoning."
429 .to_string()
430 }
431
432 pub fn creative_writer() -> String {
434 "You are a creative writer with a vivid imagination. Craft engaging \
435 narratives with rich descriptions and compelling characters. Pay attention \
436 to tone, pacing, and emotional resonance."
437 .to_string()
438 }
439
440 pub fn concise() -> String {
442 "You are a concise assistant. Provide brief, to-the-point responses. \
443 Focus on key information without unnecessary elaboration. Use clear, \
444 simple language."
445 .to_string()
446 }
447
448 pub fn socratic() -> String {
450 "You are a Socratic teacher. Guide users to discover answers through \
451 thoughtful questions. Encourage critical thinking and self-reflection. \
452 Help users develop their own understanding."
453 .to_string()
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460
461 #[test]
462 fn test_few_shot_basic() {
463 let mut few_shot = FewShotPrompt::new("Classify sentiment:");
464 few_shot.add_example("Great!", "positive");
465 few_shot.add_example("Awful.", "negative");
466 few_shot.set_query("Amazing!");
467
468 let prompt = few_shot.build();
469 assert!(prompt.contains("Classify sentiment:"));
470 assert!(prompt.contains("Input: Great!"));
471 assert!(prompt.contains("Output: positive"));
472 assert!(prompt.contains("Input: Amazing!"));
473 }
474
475 #[test]
476 fn test_few_shot_custom_prefixes() {
477 let mut few_shot = FewShotPrompt::new("Test:");
478 few_shot
479 .with_prefixes("Q: ", "A: ")
480 .add_example("1+1", "2")
481 .set_query("2+2");
482
483 let prompt = few_shot.build();
484 assert!(prompt.contains("Q: 1+1"));
485 assert!(prompt.contains("A: 2"));
486 }
487
488 #[test]
489 fn test_chain_of_thought() {
490 let cot = ChainOfThought::new("What is 25% of 80?")
491 .with_instruction("Let's solve this step by step:");
492
493 let prompt = cot.build();
494 assert!(prompt.contains("Question: What is 25% of 80?"));
495 assert!(prompt.contains("Let's solve this step by step:"));
496 }
497
498 #[test]
499 fn test_chain_of_thought_with_examples() {
500 let cot = ChainOfThought::new("What is 15% of 60?").add_example(
501 "What is 10% of 50?",
502 "Step 1: Convert 10% to decimal: 0.10\nStep 2: Multiply: 50 × 0.10 = 5",
503 );
504
505 let prompt = cot.build();
506 assert!(prompt.contains("What is 10% of 50?"));
507 assert!(prompt.contains("Step 1"));
508 assert!(prompt.contains("What is 15% of 60?"));
509 }
510
511 #[test]
512 fn test_role_prompt() {
513 let prompt = RolePrompt::new()
514 .system("You are a helpful assistant.")
515 .user("Hello!")
516 .assistant("Hi! How can I help you?")
517 .user("Tell me a joke.");
518
519 let text = prompt.build();
520 assert!(text.contains("System: You are a helpful assistant."));
521 assert!(text.contains("User: Hello!"));
522 assert!(text.contains("Assistant: Hi! How can I help you?"));
523 assert!(text.contains("User: Tell me a joke."));
524 }
525
526 #[test]
527 fn test_role_prompt_split() {
528 let prompt = RolePrompt::new().system("Be helpful.").user("Hi");
529
530 let (system, messages) = prompt.split();
531 assert_eq!(system, Some("Be helpful.".to_string()));
532 assert_eq!(messages.len(), 1);
533 assert_eq!(messages[0].0, Role::User);
534 }
535
536 #[test]
537 fn test_instruction_prompt() {
538 let prompt = InstructionPrompt::new("Summarize this text")
539 .add_context("The text is a news article")
540 .add_constraint("Keep it under 100 words")
541 .add_constraint("Focus on key facts")
542 .with_format("Bullet points");
543
544 let text = prompt.build();
545 assert!(text.contains("Task: Summarize this text"));
546 assert!(text.contains("Context:"));
547 assert!(text.contains("news article"));
548 assert!(text.contains("Constraints:"));
549 assert!(text.contains("under 100 words"));
550 assert!(text.contains("Expected Format: Bullet points"));
551 }
552
553 #[test]
554 fn test_system_prompts() {
555 assert!(!SystemPrompts::expert_assistant().is_empty());
556 assert!(!SystemPrompts::code_assistant().is_empty());
557 assert!(!SystemPrompts::teacher().is_empty());
558 assert!(!SystemPrompts::analyst().is_empty());
559 assert!(!SystemPrompts::creative_writer().is_empty());
560 assert!(!SystemPrompts::concise().is_empty());
561 assert!(!SystemPrompts::socratic().is_empty());
562 }
563
564 #[test]
565 fn test_example_creation() {
566 let example = Example::new("input", "output");
567 assert_eq!(example.input, "input");
568 assert_eq!(example.output, "output");
569 }
570
571 #[test]
572 fn test_few_shot_add_multiple() {
573 let examples = vec![Example::new("a", "1"), Example::new("b", "2")];
574
575 let mut few_shot = FewShotPrompt::new("Test");
576 few_shot.add_examples(examples);
577
578 let prompt = few_shot.build();
579 assert!(prompt.contains("Input: a"));
580 assert!(prompt.contains("Input: b"));
581 }
582}