1use proc_macro::TokenStream;
9use quote::quote;
10use syn::{parse_macro_input, LitStr};
11
12#[proc_macro]
25pub fn ai_slot(input: TokenStream) -> TokenStream {
26 let input_str = parse_macro_input!(input as LitStr);
27 let prompt = input_str.value();
28
29 let output = quote! {
30 aether_core::Slot::new("generated", #prompt)
31 };
32
33 output.into()
34}
35
36#[proc_macro]
46pub fn ai_template(input: TokenStream) -> TokenStream {
47 let input_str = parse_macro_input!(input as LitStr);
48 let content = input_str.value();
49
50 let output = quote! {
51 aether_core::Template::new(#content)
52 };
53
54 output.into()
55}
56
57#[proc_macro]
75pub fn ai(input: TokenStream) -> TokenStream {
76 let input_tokens: proc_macro2::TokenStream = input.into();
77
78 let output = quote! {
80 {
81 async {
82 use aether_core::{InjectionEngine, Template};
83
84 let (prompt, provider) = (#input_tokens);
85 let template = Template::new("{{AI:generated}}")
86 .with_slot("generated", prompt);
87
88 let engine = InjectionEngine::new(provider);
89 engine.render(&template).await
90 }
91 }
92 };
93
94 output.into()
95}
96
97#[proc_macro_attribute]
112pub fn ai_generate(attr: TokenStream, item: TokenStream) -> TokenStream {
113 let _prompt = parse_macro_input!(attr as LitStr);
114 let item_tokens: proc_macro2::TokenStream = item.into();
115
116 let output = quote! {
117 #item_tokens
119 };
120
121 output.into()
122}
123
124#[proc_macro_attribute]
137pub fn aether_secure(attr: TokenStream, item: TokenStream) -> TokenStream {
138 let input = parse_macro_input!(item as syn::ItemFn);
139 let fn_name = &input.sig.ident;
140 let fn_vis = &input.vis;
141 let fn_args = &input.sig.inputs;
142 let fn_output = &input.sig.output;
143
144 let attr_str = attr.to_string();
146 let prompt = if let Some(p) = attr_str.split("prompt =").nth(1).and_then(|s| s.split('"').nth(1)) {
147 p.to_string()
148 } else {
149 "Generate logic for this function".to_string()
150 };
151
152 let arg_names: Vec<_> = fn_args.iter().filter_map(|arg| {
153 if let syn::FnArg::Typed(pat_type) = arg {
154 if let syn::Pat::Ident(pat_id) = &*pat_type.pat {
155 return Some(&pat_id.ident);
156 }
157 }
158 None
159 }).collect();
160
161 let output = quote! {
162 #fn_vis async fn #fn_name(#fn_args) #fn_output {
163 use aether_core::prelude::*;
164 use aether_core::AetherRuntime;
165 use std::collections::HashMap;
166
167 let provider_type = std::env::var("AETHER_PROVIDER").unwrap_or_else(|_| "openai".to_string());
171
172 let script_prompt = format!(
174 "Implement this logic in Rhai script: {}. Output ONLY the raw Rhai script code. The inputs available are: {:?}. Return the result directly. Do not wrap in markdown.",
175 #prompt,
176 vec![#(stringify!(#arg_names)),*]
177 );
178
179 let template = Template::new("{{AI:script}}")
180 .configure_slot(Slot::new("script", script_prompt).with_temperature(0.0));
181
182 let script = match provider_type.to_lowercase().as_str() {
183 "anthropic" | "claude" => {
184 let p = aether_ai::AnthropicProvider::from_env().expect("Anthropic Provider not configured");
185 let engine = InjectionEngine::new(p);
186 engine.render(&template).await.expect("AI script generation failed")
187 },
188 "gemini" => {
189 let p = aether_ai::GeminiProvider::from_env().expect("Gemini Provider not configured");
190 let engine = InjectionEngine::new(p);
191 engine.render(&template).await.expect("AI script generation failed")
192 },
193 "ollama" => {
194 let model = std::env::var("AETHER_MODEL").unwrap_or_else(|_| "llama3".to_string());
195 let p = aether_ai::OllamaProvider::new(&model);
196 let engine = InjectionEngine::new(p);
197 engine.render(&template).await.expect("AI script generation failed")
198 },
199 _ => {
200 let p = aether_ai::OpenAiProvider::from_env().expect("OpenAI Provider not configured");
201 let engine = InjectionEngine::new(p);
202 engine.render(&template).await.expect("AI script generation failed")
203 }
204 };
205
206 let runtime = AetherRuntime::new();
208 let mut inputs = HashMap::new();
209 #(
210 inputs.insert(stringify!(#arg_names).to_string(), rhai::Dynamic::from(#arg_names));
211 )*
212
213 let result = runtime.execute(&script, inputs).expect("Runtime execution failed");
214
215 result.cast()
217 }
218 };
219
220 output.into()
221}