Wire Axon /axon/message to NeuronRuntime dispatch

- axon.rs: replace stub AxonHandler with live NeuronRuntime dispatch;
  tool calls now route through Engram VM when modules are loaded
- state.rs: load .el modules from NEURON_LANG_DIR (default ~/.neuron/lang);
  falls back to empty dir gracefully when not present
- Cargo.toml: add engram-core dependency for EngramDb
This commit is contained in:
2026-04-28 13:47:09 -05:00
parent 7a81bb11a7
commit 78fc3a909a
4 changed files with 78 additions and 20 deletions
+2
View File
@@ -11,6 +11,8 @@ path = "src/main.rs"
neuron-domain = { path = "../neuron-domain" }
neuron-store = { path = "../neuron-store" }
neuron-protocol = { path = "../neuron-protocol" }
neuron-runtime = { path = "../neuron-runtime" }
chrono = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
uuid = { workspace = true }
+37 -15
View File
@@ -4,31 +4,53 @@ use axum::{
Json,
};
use futures_util::Stream;
use neuron_protocol::{AxonHandler, AxonMessage, AxonResponse};
use neuron_protocol::{AxonMessage, AxonMethod, AxonResponse};
use serde_json::Value;
use std::sync::Arc;
use std::time::Duration;
use crate::state::AppState;
/// POST /axon/message — receive an Axon protocol message and return a response.
/// POST /axon/message — dispatch to NeuronRuntime.
pub async fn post_message(
State(_state): State<Arc<AppState>>,
State(state): State<Arc<AppState>>,
Json(msg): Json<AxonMessage>,
) -> Json<AxonResponse> {
let mut handler = AxonHandler::new();
match msg.method {
AxonMethod::ToolCall => {
let tool_name = msg
.params
.get("tool")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let params = msg
.params
.get("params")
.cloned()
.unwrap_or(Value::Null);
// Register stub handlers for all known tool names.
for &tool in neuron_protocol::tools::ALL_TOOLS {
let name = tool.to_string();
handler.register(name, |params| {
serde_json::json!({
"status": "ok",
"params": params
})
});
if tool_name.is_empty() {
return Json(AxonResponse::err(msg.id, "missing 'tool' field in params"));
}
let result = state.runtime.dispatch(&tool_name, params).await;
// If the result contains an "error" field, surface it as an error response.
if let Some(err_msg) = result.get("error").and_then(|v| v.as_str()) {
Json(AxonResponse::err(msg.id, err_msg))
} else {
Json(AxonResponse::ok(msg.id, result))
}
}
AxonMethod::Ping => Json(AxonResponse::ok(
msg.id,
serde_json::json!({ "ts": chrono::Utc::now().to_rfc3339() }),
)),
_ => Json(AxonResponse::err(msg.id, "unsupported method")),
}
Json(handler.dispatch(msg))
}
/// GET /axon/sse — Server-Sent Events stream for Axon events.
+36 -5
View File
@@ -1,4 +1,7 @@
use std::path::Path;
use std::sync::Arc;
use neuron_runtime::NeuronRuntime;
use neuron_store::{BacklogStore, ContextStore, IseStore, KnowledgeStore, MemoryStore, open_db};
/// Shared application state — one store per domain.
@@ -12,17 +15,45 @@ pub struct AppState {
pub context: ContextStore,
pub ise: IseStore,
pub api_key: String,
pub runtime: Arc<NeuronRuntime>,
}
impl AppState {
pub fn new(db_path: &Path, api_key: String) -> Result<Self, neuron_domain::NeuronError> {
// Module directory defaults to "~/.neuron/lang" (or wherever .el files live).
// Falls back to a non-existent directory — runtime will have zero modules
// and fall back to native bindings for all tools.
let module_dir_raw =
std::env::var("NEURON_LANG_DIR").unwrap_or_else(|_| "~/.neuron/lang".to_string());
let module_dir = std::path::PathBuf::from(
module_dir_raw
.replace('~', &std::env::var("HOME").unwrap_or_else(|_| ".".to_string())),
);
// If the module directory doesn't exist, create an empty temp dir so
// load_directory doesn't error out.
let effective_dir = if module_dir.exists() {
module_dir
} else {
// Use a tmp dir — runtime will have zero modules (safe fallback).
std::env::temp_dir().join("neuron-lang-empty")
};
let _ = std::fs::create_dir_all(&effective_dir);
// Open the sled DB exactly once — Clone shares the same file lock.
let db = open_db(db_path)?;
let runtime = NeuronRuntime::from_dir(&effective_dir, db.clone())
.map_err(|e| neuron_domain::NeuronError::Storage(e.to_string()))?;
Ok(Self {
memory: MemoryStore::new(open_db(db_path)?),
knowledge: KnowledgeStore::new(open_db(db_path)?),
backlog: BacklogStore::new(open_db(db_path)?),
context: ContextStore::new(open_db(db_path)?),
ise: IseStore::new(open_db(db_path)?),
memory: MemoryStore::new(db.clone()),
knowledge: KnowledgeStore::new(db.clone()),
backlog: BacklogStore::new(db.clone()),
context: ContextStore::new(db.clone()),
ise: IseStore::new(db),
api_key,
runtime: Arc::new(runtime),
})
}
}