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}