lumos_macro/lib.rs
1extern crate proc_macro;
2use proc_macro::TokenStream;
3use syn::{Expr, Ident, LitStr, Token, parse::{Parse, ParseStream}};
4// use syn::spanned::Spanned; // 暂时未使用
5
6mod parser;
7mod tool_macro;
8mod agent_macro;
9mod workflow;
10mod rag;
11mod eval;
12mod mcp;
13mod agent;
14mod tools;
15mod lumos;
16
17/// Macro for defining a tool in a simplified way
18///
19/// # Example
20/// ```
21/// use lumos_macro::tool;
22///
23/// #[tool(
24/// name = "calculator",
25/// description = "Performs basic math operations"
26/// )]
27/// fn calculator(
28/// #[parameter(
29/// name = "operation",
30/// description = "The operation to perform: add, subtract, multiply, divide",
31/// r#type = "string",
32/// required = true
33/// )]
34/// operation: String,
35///
36/// #[parameter(
37/// name = "a",
38/// description = "First number",
39/// r#type = "number",
40/// required = true
41/// )]
42/// a: f64,
43///
44/// #[parameter(
45/// name = "b",
46/// description = "Second number",
47/// r#type = "number",
48/// required = true
49/// )]
50/// b: f64,
51/// ) -> Result<serde_json::Value, lumosai_core::Error> {
52/// // Function implementation
53/// }
54/// ```
55#[proc_macro_attribute]
56pub fn tool(attr: TokenStream, item: TokenStream) -> TokenStream {
57 tool_macro::tool_macro(attr, item)
58}
59
60struct ToolAttributes {
61 name: LitStr,
62 description: LitStr,
63}
64
65impl Parse for ToolAttributes {
66 fn parse(input: ParseStream) -> syn::Result<Self> {
67 let mut name = None;
68 let mut description = None;
69
70 while !input.is_empty() {
71 let ident: Ident = input.parse()?;
72 input.parse::<Token![=]>()?;
73
74 if ident == "name" {
75 name = Some(input.parse()?);
76 } else if ident == "description" {
77 description = Some(input.parse()?);
78 } else {
79 return Err(syn::Error::new(ident.span(), "Unknown attribute"));
80 }
81
82 // Allow trailing comma
83 if input.peek(Token![,]) {
84 input.parse::<Token![,]>()?;
85 }
86 }
87
88 let name = name.ok_or_else(|| syn::Error::new(input.span(), "Missing name attribute"))?;
89 let description = description.ok_or_else(|| syn::Error::new(input.span(), "Missing description attribute"))?;
90
91 Ok(ToolAttributes { name, description })
92 }
93}
94
95struct ParameterAttributes {
96 name: LitStr,
97 description: LitStr,
98 type_: LitStr,
99 required: bool,
100}
101
102impl Parse for ParameterAttributes {
103 fn parse(input: ParseStream) -> syn::Result<Self> {
104 let mut name = None;
105 let mut description = None;
106 let mut type_ = None;
107 let mut required = None;
108
109 let content;
110 syn::parenthesized!(content in input);
111 let input = &content;
112
113 while !input.is_empty() {
114 let ident: Ident = input.parse()?;
115 input.parse::<Token![=]>()?;
116
117 if ident == "name" {
118 name = Some(input.parse()?);
119 } else if ident == "description" {
120 description = Some(input.parse()?);
121 } else if ident == "r#type" || ident == "type" {
122 type_ = Some(input.parse()?);
123 } else if ident == "required" {
124 let expr: Expr = input.parse()?;
125 if let Expr::Lit(lit) = &expr {
126 if let syn::Lit::Bool(b) = &lit.lit {
127 required = Some(b.value);
128 }
129 }
130 } else {
131 return Err(syn::Error::new(ident.span(), "Unknown parameter attribute"));
132 }
133
134 // Allow trailing comma
135 if input.peek(Token![,]) {
136 input.parse::<Token![,]>()?;
137 }
138 }
139
140 let name = name.ok_or_else(|| syn::Error::new(input.span(), "Missing name attribute"))?;
141 let description = description.ok_or_else(|| syn::Error::new(input.span(), "Missing description attribute"))?;
142 let type_ = type_.ok_or_else(|| syn::Error::new(input.span(), "Missing type attribute"))?;
143 let required = required.unwrap_or(false);
144
145 Ok(ParameterAttributes { name, description, type_, required })
146 }
147}
148
149/// Macro for defining an agent with tools in a simplified way
150///
151/// # Example
152/// ```
153/// use lumos_macro::agent_attr;
154///
155/// #[agent_attr(
156/// name = "math_agent",
157/// instructions = "You are a helpful math assistant that can perform calculations.",
158/// model = "gpt-4"
159/// )]
160/// struct MathAgent {
161/// #[tool]
162/// calculator: CalculatorTool,
163///
164/// #[tool]
165/// unit_converter: UnitConverterTool,
166/// }
167/// ```
168#[proc_macro_attribute]
169pub fn agent_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
170 agent_macro::agent_impl(attr, item)
171}
172
173struct AgentAttributes {
174 name: LitStr,
175 instructions: LitStr,
176 model: LitStr,
177}
178
179impl Parse for AgentAttributes {
180 fn parse(input: ParseStream) -> syn::Result<Self> {
181 let mut name = None;
182 let mut instructions = None;
183 let mut model = None;
184
185 while !input.is_empty() {
186 let ident: Ident = input.parse()?;
187 input.parse::<Token![=]>()?;
188
189 if ident == "name" {
190 name = Some(input.parse()?);
191 } else if ident == "instructions" {
192 instructions = Some(input.parse()?);
193 } else if ident == "model" {
194 model = Some(input.parse()?);
195 } else {
196 return Err(syn::Error::new(ident.span(), "Unknown attribute"));
197 }
198
199 // Allow trailing comma
200 if input.peek(Token![,]) {
201 input.parse::<Token![,]>()?;
202 }
203 }
204
205 let name = name.ok_or_else(|| syn::Error::new(input.span(), "Missing name attribute"))?;
206 let instructions = instructions.ok_or_else(|| syn::Error::new(input.span(), "Missing instructions attribute"))?;
207 let model = model.ok_or_else(|| syn::Error::new(input.span(), "Missing model attribute"))?;
208
209 Ok(AgentAttributes { name, instructions, model })
210 }
211}
212
213/// A convenient derive macro for defining a model adapter
214///
215/// # Example
216/// ```
217/// use lumos_macro::LlmAdapter;
218///
219/// #[derive(LlmAdapter)]
220/// struct OpenAIAdapter {
221/// api_key: String,
222/// model: String,
223/// }
224/// ```
225#[proc_macro_derive(LlmAdapter)]
226pub fn derive_llm_adapter(input: TokenStream) -> TokenStream {
227 agent_macro::derive_llm_adapter(input)
228}
229
230/// A macro for quick tool execution setup
231///
232/// # Example
233/// ```
234/// lumos_execute_tool! {
235/// tool: calculator,
236/// params: {
237/// "operation": "add",
238/// "a": 10.5,
239/// "b": 20.3
240/// }
241/// }
242/// ```
243#[proc_macro]
244pub fn lumos_execute_tool(input: TokenStream) -> TokenStream {
245 tool_macro::lumos_execute_tool(input)
246}
247
248struct ToolExecuteArgs {
249 tool: Expr,
250 params: Expr,
251}
252
253impl Parse for ToolExecuteArgs {
254 fn parse(input: ParseStream) -> syn::Result<Self> {
255 let mut tool = None;
256 let mut params = None;
257
258 while !input.is_empty() {
259 let ident: Ident = input.parse()?;
260 input.parse::<Token![:]>()?;
261
262 if ident == "tool" {
263 tool = Some(input.parse()?);
264 } else if ident == "params" {
265 params = Some(input.parse()?);
266 } else {
267 return Err(syn::Error::new(ident.span(), "Unknown field"));
268 }
269
270 // Allow trailing comma
271 if input.peek(Token![,]) {
272 input.parse::<Token![,]>()?;
273 }
274 }
275
276 let tool = tool.ok_or_else(|| syn::Error::new(input.span(), "Missing tool field"))?;
277 let params = params.ok_or_else(|| syn::Error::new(input.span(), "Missing params field"))?;
278
279 Ok(ToolExecuteArgs { tool, params })
280 }
281}
282
283/// 创建一个工作流定义,参考Mastra的工作流API设计
284///
285/// # 示例
286///
287/// ```rust
288/// workflow! {
289/// name: "content_creation",
290/// description: "创建高质量的内容",
291/// steps: {
292/// {
293/// name: "research",
294/// agent: researcher,
295/// instructions: "进行深入的主题研究",
296/// },
297/// {
298/// name: "writing",
299/// agent: writer,
300/// instructions: "将研究结果整理成文章",
301/// when: { completed("research") },
302/// }
303/// }
304/// }
305/// ```
306#[proc_macro]
307pub fn workflow(input: TokenStream) -> TokenStream {
308 workflow::workflow_impl(input)
309}
310
311/// 创建一个RAG管道,参考Mastra的RAG原语API设计
312///
313/// # 示例
314///
315/// ```rust
316/// rag_pipeline! {
317/// name: "knowledge_base",
318///
319/// source: DocumentSource::from_directory("./docs"),
320///
321/// pipeline: {
322/// chunk: {
323/// chunk_size: 1000,
324/// chunk_overlap: 200
325/// },
326///
327/// embed: {
328/// model: "text-embedding-3-small",
329/// dimensions: 1536
330/// },
331///
332/// store: {
333/// db: "pgvector",
334/// collection: "embeddings"
335/// }
336/// },
337///
338/// query_pipeline: {
339/// rerank: true,
340/// top_k: 5,
341/// filter: r#"{ "type": { "$in": ["article", "faq"] } }"#
342/// }
343/// }
344/// ```
345#[proc_macro]
346pub fn rag_pipeline(input: TokenStream) -> TokenStream {
347 rag::rag_pipeline_impl(input)
348}
349
350/// 创建一个评估套件,参考Mastra的Eval框架
351///
352/// # 示例
353///
354/// ```rust
355/// eval_suite! {
356/// name: "agent_performance",
357///
358/// metrics: {
359/// accuracy: AccuracyMetric::new(0.8),
360/// relevance: RelevanceMetric::new(0.7),
361/// completeness: CompletenessMetric::new(0.6)
362/// },
363///
364/// test_cases: {
365/// basic_queries: "./tests/basic_queries.json",
366/// complex_queries: "./tests/complex_queries.json"
367/// },
368///
369/// reporting: {
370/// format: "html",
371/// output: "./reports/eval_results.html"
372/// }
373/// }
374/// ```
375#[proc_macro]
376pub fn eval_suite(input: TokenStream) -> TokenStream {
377 eval::eval_suite_impl(input)
378}
379
380/// 创建一个MCP客户端配置,参考Mastra的MCP支持
381///
382/// # 示例
383///
384/// ```rust
385/// mcp_client! {
386/// discovery: {
387/// endpoints: ["https://tools.example.com/mcp", "https://api.mcp.run"],
388/// auto_register: true
389/// },
390///
391/// tools: {
392/// data_analysis: {
393/// enabled: true,
394/// auth: {
395/// type: "api_key",
396/// key_env: "DATA_ANALYSIS_API_KEY"
397/// }
398/// },
399/// image_processing: {
400/// enabled: true,
401/// rate_limit: 100
402/// }
403/// }
404/// }
405/// ```
406#[proc_macro]
407pub fn mcp_client(input: TokenStream) -> TokenStream {
408 mcp::mcp_client_impl(input)
409}
410
411/// 创建一个代理定义,参考Mastra的Agent API设计
412///
413/// # 示例
414///
415/// ```rust
416/// agent! {
417/// name: "research_assistant",
418/// instructions: "你是一个专业的研究助手,擅长收集和整理信息。",
419///
420/// llm: {
421/// provider: openai_adapter,
422/// model: "gpt-4"
423/// },
424///
425/// memory: {
426/// store_type: "buffer",
427/// capacity: 10
428/// },
429///
430/// tools: {
431/// search_tool,
432/// calculator_tool: { precision: 2 },
433/// web_browser: { javascript: true, screenshots: true }
434/// }
435/// }
436/// ```
437#[proc_macro]
438pub fn agent(input: TokenStream) -> TokenStream {
439 agent::agent(input)
440}
441
442/// 一次性定义多个工具,参考Mastra的工具API设计
443///
444/// # 示例
445///
446/// ```rust
447/// tools! {
448/// {
449/// name: "calculator",
450/// description: "执行基本的数学运算",
451/// parameters: {
452/// {
453/// name: "operation",
454/// description: "要执行的操作: add, subtract, multiply, divide",
455/// type: "string",
456/// required: true
457/// },
458/// {
459/// name: "a",
460/// description: "第一个数字",
461/// type: "number",
462/// required: true
463/// },
464/// {
465/// name: "b",
466/// description: "第二个数字",
467/// type: "number",
468/// required: true
469/// }
470/// },
471/// handler: |params| async move {
472/// let operation = params.get("operation").unwrap().as_str().unwrap();
473/// let a = params.get("a").unwrap().as_f64().unwrap();
474/// let b = params.get("b").unwrap().as_f64().unwrap();
475///
476/// let result = match operation {
477/// "add" => a + b,
478/// "subtract" => a - b,
479/// "multiply" => a * b,
480/// "divide" => a / b,
481/// _ => return Err(Error::InvalidInput("Unknown operation".into()))
482/// };
483///
484/// Ok(json!({ "result": result }))
485/// }
486/// },
487/// {
488/// name: "weather",
489/// description: "获取指定城市的天气信息",
490/// parameters: {
491/// {
492/// name: "city",
493/// description: "城市名称",
494/// type: "string",
495/// required: true
496/// }
497/// },
498/// handler: get_weather_data
499/// }
500/// }
501/// ```
502#[proc_macro]
503pub fn tools(input: TokenStream) -> TokenStream {
504 tools::tools(input)
505}
506
507/// 基于nom的tool!宏 - 新的语法设计
508///
509/// # 示例
510///
511/// ```rust
512/// tool! {
513/// name: "calculator",
514/// description: "执行基本的数学运算",
515/// parameters: [
516/// {
517/// name: "operation",
518/// description: "要执行的操作: add, subtract, multiply, divide",
519/// type: "string",
520/// required: true
521/// },
522/// {
523/// name: "a",
524/// description: "第一个数字",
525/// type: "number",
526/// required: true
527/// }
528/// ],
529/// handler: handle_calculator
530/// }
531/// ```
532#[proc_macro]
533pub fn tool_nom(input: TokenStream) -> TokenStream {
534 tool_macro::tool_nom_macro(input)
535}
536
537/// 配置整个Lumos应用,参考Mastra的应用级API
538///
539/// # 示例
540///
541/// ```rust
542/// let app = lumos! {
543/// name: "stock_assistant",
544/// description: "一个能够提供股票信息的AI助手",
545///
546/// agents: {
547/// stockAgent
548/// },
549///
550/// tools: {
551/// stockPriceTool,
552/// stockInfoTool
553/// },
554///
555/// rags: {
556/// stockKnowledgeBase
557/// },
558///
559/// workflows: {
560/// stockAnalysisWorkflow
561/// },
562///
563/// mcp_endpoints: vec!["https://api.example.com/mcp"]
564/// };
565/// ```
566#[proc_macro]
567pub fn lumos(input: TokenStream) -> TokenStream {
568 lumos::lumos(input)
569}