mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
Load tools at launch
This commit is contained in:
@@ -68,7 +68,7 @@ impl ContextMonitor {
|
||||
|
||||
/// Estimate the token count for a list of messages.
|
||||
pub fn estimate_tokens(&self, messages: &[ChatMessage]) -> usize {
|
||||
messages.iter().map(|m| estimate_message_tokens(m)).sum()
|
||||
messages.iter().map(estimate_message_tokens).sum()
|
||||
}
|
||||
|
||||
/// Check if compaction is needed.
|
||||
|
||||
+59
-18
@@ -267,14 +267,36 @@ fn build_wasm_component(source_dir: &Path, release: bool) -> anyhow::Result<Path
|
||||
}
|
||||
|
||||
// Find the output wasm file
|
||||
// cargo-component may output to wasm32-wasip1 or wasm32-wasip2 depending on version
|
||||
let profile = if release { "release" } else { "debug" };
|
||||
let target_dir = source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip2")
|
||||
.join(profile);
|
||||
let candidates = [
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip1")
|
||||
.join(profile),
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip2")
|
||||
.join(profile),
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-unknown-unknown")
|
||||
.join(profile),
|
||||
];
|
||||
|
||||
let target_dir = candidates.iter().find(|p| p.exists()).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"No WASM target directory found. Expected one of: {}",
|
||||
candidates
|
||||
.iter()
|
||||
.map(|p| p.display().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
})?;
|
||||
|
||||
// Look for .wasm files in target dir
|
||||
let entries: Vec<_> = std::fs::read_dir(&target_dir)?
|
||||
let entries: Vec<_> = std::fs::read_dir(target_dir)?
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| {
|
||||
e.path()
|
||||
@@ -307,26 +329,45 @@ fn build_wasm_component(source_dir: &Path, release: bool) -> anyhow::Result<Path
|
||||
/// Find an existing WASM artifact without building.
|
||||
fn find_wasm_artifact(source_dir: &Path, name: &str, release: bool) -> anyhow::Result<PathBuf> {
|
||||
let profile = if release { "release" } else { "debug" };
|
||||
let target_dir = source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip2")
|
||||
.join(profile);
|
||||
|
||||
// Try exact name match first
|
||||
let snake_name = name.replace('-', "_");
|
||||
let candidates = [
|
||||
target_dir.join(format!("{}.wasm", name)),
|
||||
target_dir.join(format!("{}.wasm", snake_name)),
|
||||
// cargo-component may output to wasm32-wasip1 or wasm32-wasip2 depending on version
|
||||
let target_dirs = [
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip1")
|
||||
.join(profile),
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-wasip2")
|
||||
.join(profile),
|
||||
source_dir
|
||||
.join("target")
|
||||
.join("wasm32-unknown-unknown")
|
||||
.join(profile),
|
||||
];
|
||||
|
||||
for candidate in &candidates {
|
||||
if candidate.exists() {
|
||||
return Ok(candidate.clone());
|
||||
let snake_name = name.replace('-', "_");
|
||||
|
||||
// Try exact name match in any target dir first
|
||||
for target_dir in &target_dirs {
|
||||
let candidates = [
|
||||
target_dir.join(format!("{}.wasm", name)),
|
||||
target_dir.join(format!("{}.wasm", snake_name)),
|
||||
];
|
||||
for candidate in &candidates {
|
||||
if candidate.exists() {
|
||||
return Ok(candidate.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find a target dir that exists
|
||||
let target_dir = target_dirs.iter().find(|p| p.exists()).ok_or_else(|| {
|
||||
anyhow::anyhow!("No target directory found. Run without --skip-build to build first.")
|
||||
})?;
|
||||
|
||||
// Fall back to any .wasm file
|
||||
let entries: Vec<_> = std::fs::read_dir(&target_dir)
|
||||
let entries: Vec<_> = std::fs::read_dir(target_dir)
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!(
|
||||
"Target directory not found: {}. Run without --skip-build.",
|
||||
|
||||
@@ -240,6 +240,8 @@ impl SafetyConfig {
|
||||
pub struct WasmConfig {
|
||||
/// Whether WASM tool execution is enabled.
|
||||
pub enabled: bool,
|
||||
/// Directory containing installed WASM tools (default: ~/.near-agent/tools/).
|
||||
pub tools_dir: PathBuf,
|
||||
/// Default memory limit in bytes (default: 10 MB).
|
||||
pub default_memory_limit: u64,
|
||||
/// Default execution timeout in seconds (default: 60).
|
||||
@@ -302,6 +304,7 @@ impl Default for WasmConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
tools_dir: default_tools_dir(),
|
||||
default_memory_limit: 10 * 1024 * 1024, // 10 MB
|
||||
default_timeout_secs: 60,
|
||||
default_fuel_limit: 10_000_000,
|
||||
@@ -311,6 +314,14 @@ impl Default for WasmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default tools directory (~/.near-agent/tools/).
|
||||
fn default_tools_dir() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
.join(".near-agent")
|
||||
.join("tools")
|
||||
}
|
||||
|
||||
impl WasmConfig {
|
||||
fn from_env() -> Result<Self, ConfigError> {
|
||||
Ok(Self {
|
||||
@@ -322,6 +333,9 @@ impl WasmConfig {
|
||||
message: format!("must be 'true' or 'false': {e}"),
|
||||
})?
|
||||
.unwrap_or(true),
|
||||
tools_dir: optional_env("WASM_TOOLS_DIR")?
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(default_tools_dir),
|
||||
default_memory_limit: parse_optional_env(
|
||||
"WASM_DEFAULT_MEMORY_LIMIT",
|
||||
10 * 1024 * 1024,
|
||||
|
||||
+40
-2
@@ -13,7 +13,10 @@ use near_agent::{
|
||||
history::Store,
|
||||
llm::create_llm_provider,
|
||||
safety::SafetyLayer,
|
||||
tools::ToolRegistry,
|
||||
tools::{
|
||||
ToolRegistry,
|
||||
wasm::{WasmToolLoader, WasmToolRuntime},
|
||||
},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
@@ -86,7 +89,42 @@ async fn main() -> anyhow::Result<()> {
|
||||
// Initialize tool registry
|
||||
let tools = Arc::new(ToolRegistry::new());
|
||||
tools.register_builtin_tools();
|
||||
tracing::info!("Tool registry initialized with {} tools", tools.count());
|
||||
tracing::info!("Registered {} built-in tools", tools.count());
|
||||
|
||||
// Load installed WASM tools
|
||||
if config.wasm.enabled && config.wasm.tools_dir.exists() {
|
||||
match WasmToolRuntime::new(config.wasm.to_runtime_config()) {
|
||||
Ok(runtime) => {
|
||||
let runtime = Arc::new(runtime);
|
||||
let loader = WasmToolLoader::new(Arc::clone(&runtime), Arc::clone(&tools));
|
||||
|
||||
match loader.load_from_dir(&config.wasm.tools_dir).await {
|
||||
Ok(results) => {
|
||||
if !results.loaded.is_empty() {
|
||||
tracing::info!(
|
||||
"Loaded {} WASM tools from {}",
|
||||
results.loaded.len(),
|
||||
config.wasm.tools_dir.display()
|
||||
);
|
||||
}
|
||||
for (path, err) in &results.errors {
|
||||
tracing::warn!("Failed to load WASM tool {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("Failed to scan WASM tools directory: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("Failed to initialize WASM runtime: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
tracing::info!(
|
||||
"Tool registry initialized with {} total tools",
|
||||
tools.count()
|
||||
);
|
||||
|
||||
// Initialize channel manager
|
||||
let mut channels = ChannelManager::new();
|
||||
|
||||
@@ -258,7 +258,7 @@ fn extract_response(response: &Val) -> Result<(Option<String>, Option<String>),
|
||||
|
||||
for (name, val) in fields {
|
||||
match name.as_str() {
|
||||
"result" => {
|
||||
"output" => {
|
||||
if let Val::Option(Some(inner)) = val {
|
||||
if let Val::String(s) = inner.as_ref() {
|
||||
result = Some(s.to_string());
|
||||
|
||||
+2
-2
@@ -112,8 +112,8 @@ interface tool {
|
||||
|
||||
/// Response from tool execution.
|
||||
record response {
|
||||
/// JSON-encoded result on success.
|
||||
result: option<string>,
|
||||
/// JSON-encoded output on success.
|
||||
output: option<string>,
|
||||
/// Error message on failure.
|
||||
error: option<string>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user