Summary
Implement compile-time type checking for LLM prompts in rsai, similar to how sqlx provides compile-time SQL query validation. This feature will transform prompts from untyped strings into type-safe templates with compile-time validation of parameters and structure.
Problem Statement
- Prompts are constructed as raw
String values with no type safety
- Template variables are embedded without validation
- Type mismatches between prompt expectations and provided data are only caught at runtime
- No IDE support for prompt authoring (autocomplete, validation)
- Refactoring prompts is error-prone
Proposed Solution
Introduce a prompt! macro that provides:
- Compile-time validation of prompt templates
- Type-safe parameter binding
- Automatic formatting and escaping
- Integration with rsai's existing structured output system
Example Usage
use rsai::prelude::*;
#[derive(PromptInput)]
struct AnalysisInput {
text: String,
max_length: usize,
include_sentiment: bool,
}
// Compile-time validated prompt
let messages = prompt!(
AnalysisInput,
r#"
Analyze the following text:
{{text}}
Requirements:
- Maximum response length: {{max_length}} words
{{#if include_sentiment}}
- Include sentiment analysis
{{/if}}
"#,
AnalysisInput {
text: "This library is amazing!".to_string(),
max_length: 100,
include_sentiment: true,
}
);
// Or with inline syntax for simple cases
let messages = prompt!(
"Summarize this article: {{article}} in {{word_count}} words",
article = content,
word_count = 50
);
// Integration with builder pattern
let result = llm::with(Provider::OpenAI)
.api_key(ApiKey::Default)?
.model("gpt-4o-mini")
.prompt!(
"Analyze sentiment: {{text}}",
text = "I love Rust!"
)
.complete::<SentimentAnalysis>()
.await?;
Technical Design
1. Macro Implementation
The prompt! macro will:
- Parse the template at compile time
- Extract variable placeholders (e.g.,
{{variable_name}})
- Validate that all required variables are provided
- Generate type-safe binding code
- Support conditional sections (
{{#if condition}})
- Support loops (
{{#each items}})
2. PromptInput Derive Macro
#[derive(PromptInput)]
struct MyPromptData {
#[prompt(description = "The main text to analyze")]
text: String,
#[prompt(optional)]
context: Option<String>,
#[prompt(format = "list")]
items: Vec<String>,
}
3. Template Syntax
Support Handlebars-like syntax:
- Variables:
{{variable_name}}
- Conditionals:
{{#if condition}}...{{/if}}
- Loops:
{{#each collection}}{{this}}{{/each}}
- Escaping:
\{{literal}}
- Comments:
{{! This is a comment }}
4. Compile-Time Validation
Similar to sqlx's approach:
- Offline mode: Generate a
.rsai/prompts.json file with prompt metadata
- Online mode: Validate prompt structure at compile time
- Type checking: Ensure all template variables match struct fields
- Unused variable detection: Warn about defined but unused fields
Instead of #[derive(PromptInput)] you can consider to combine this with #16
The idea is that prompts should be grouped with their generation params, models, providers etc.
There is definitely a trade-off to explore.
Relatively narrow possibilities to use a prompt
vs.
Unconstrained use of the "prompt text".
Being opinionated here can be valuable.
Summary
Implement compile-time type checking for LLM prompts in rsai, similar to how sqlx provides compile-time SQL query validation. This feature will transform prompts from untyped strings into type-safe templates with compile-time validation of parameters and structure.
Problem Statement
Stringvalues with no type safetyProposed Solution
Introduce a
prompt!macro that provides:Example Usage
Technical Design
1. Macro Implementation
The
prompt!macro will:{{variable_name}}){{#if condition}}){{#each items}})2. PromptInput Derive Macro
3. Template Syntax
Support Handlebars-like syntax:
{{variable_name}}{{#if condition}}...{{/if}}{{#each collection}}{{this}}{{/each}}\{{literal}}{{! This is a comment }}4. Compile-Time Validation
Similar to sqlx's approach:
.rsai/prompts.jsonfile with prompt metadataInstead of
#[derive(PromptInput)]you can consider to combine this with #16The idea is that prompts should be grouped with their generation params, models, providers etc.
There is definitely a trade-off to explore.
Relatively narrow possibilities to use a prompt
vs.
Unconstrained use of the "prompt text".
Being opinionated here can be valuable.