aether_core/
script.rs

1//! # Aether Script
2//! 
3//! Aether Script (.ae) is a specialized DSL built on top of Rhai,
4//! optimized for AI-agentic workflows. It introduces first-class 
5//! AI directives and data-flow operators.
6
7use crate::{Result, AetherError, AiProvider, InjectionEngine};
8use rhai::{Engine, Dynamic, Scope};
9use std::sync::Arc;
10use tracing::debug;
11
12/// Pre-processor for Aether Script syntax.
13pub struct AetherScript;
14
15impl AetherScript {
16    /// Compiles Aether Script syntax into valid Rhai code.
17    /// 
18    /// Supported Directives:
19    /// - `@ai("prompt")` -> Shortcut for AI generation.
20    /// - `@json { ... }` -> Auto-decoding JSON responses.
21    pub fn preprocess(script: &str) -> String {
22        let mut processed = script.to_string();
23
24        // Placeholder for regex-based transformation
25        // In a production system, this would be a proper lexer/parser
26        
27        // Example: Transform @ai("prompt") -> __aether_ask("prompt")
28        // We use a simple replacement for now to demonstrate the concept
29        processed = processed.replace("@ai", "__aether_ask");
30        
31        processed
32    }
33}
34
35/// Aether-enhanced runtime that supports agentic functions.
36pub struct AetherAgenticRuntime<P: AiProvider> {
37    engine: Engine,
38    _provider: Arc<P>,
39}
40
41impl<P: AiProvider + 'static> AetherAgenticRuntime<P> {
42    /// Create a new agentic runtime with the given AI provider.
43    pub fn new(provider: P) -> Self {
44        let mut engine = Engine::new();
45        let provider = Arc::new(provider);
46        let p_clone = Arc::clone(&provider);
47
48        // Register __aether_ask function
49        // This allows scripts to call AI directly!
50        engine.register_fn("__aether_ask", move |prompt: String| -> Dynamic {
51            let p = Arc::clone(&p_clone);
52            
53            // Note: Since Rhai functions are typically synchronous, 
54            // and AiProvider::generate is async, we need a bridge.
55            // In a real implementation, we'd use a dedicated async engine 
56            // or a local runtime block.
57            
58            let handle = tokio::runtime::Handle::current();
59            let result = handle.block_on(async move {
60                let engine = InjectionEngine::new_raw(p);
61                engine.inject_raw(&prompt).await
62            });
63
64            match result {
65                Ok(code) => Dynamic::from(code),
66                Err(e) => Dynamic::from(format!("Error: {}", e)),
67            }
68        });
69
70        Self { engine, _provider: provider }
71    }
72
73    /// Execute an Aether Script.
74    pub fn execute(&self, script: &str, scope: &mut Scope) -> Result<Dynamic> {
75        let processed = AetherScript::preprocess(script);
76        debug!("Executing preprocessed script: {}", processed);
77        
78        self.engine.eval_with_scope(scope, &processed)
79            .map_err(|e| AetherError::ConfigError(format!("Script execution failed: {}", e)))
80    }
81}