Custom Framework Integration
Integrate reloaded-code into any Rust LLM framework. Wrap tool functions, generate system prompts, and configure path security in three steps. You only need this if you're using a framework other than SerdesAI (an LLM agent framework).
If you're using SerdesAI, see Getting Started instead.
The integration pattern
Every framework adapter does three things:
- Wrap core tool functions into the framework's tool trait
- Generate the system prompt using
SystemPromptBuilder - Resolve paths using a
PathResolverimplementation
Step 1: Wrap a tool function
Here's how you'd wrap the read_file function for a hypothetical framework:
use reloaded_code_core::{
read_file, PathResolver, ToolResult,
tools::read::{ReadInput, ReadOutput},
};
// Your framework's tool trait
trait MyTool {
type Input;
type Output;
fn name(&self) -> &str;
fn execute(&self, input: Self::Input) -> impl std::future::Future<Output = Result<Self::Output, String>>;
}
// Read tool adapter
struct MyReadTool<R: PathResolver> {
resolver: R,
line_numbers: bool,
}
impl<R: PathResolver + Clone + Send + Sync + 'static> MyTool for MyReadTool<R> {
type Input = ReadInput;
type Output = ReadOutput;
fn name(&self) -> &str {
"read"
}
async fn execute(&self, input: Self::Input) -> Result<Self::Output, String> {
let path = self.resolver.resolve(&input.path)
.map_err(|e| e.to_string())?;
read_file::<_, true>(&path, input.offset, input.limit)
.await
.map_err(|e| e.to_string())
}
}
Step 2: Generate the system prompt
Use SystemPromptBuilder to create a system prompt that includes guidance for every tracked tool:
use reloaded_code_core::{
SystemPromptBuilder, ToolContext,
context::{PathMode, ToolPrompt},
tool_metadata,
};
// Implement ToolContext for your tool
impl<R: PathResolver> ToolContext for MyReadTool<R> {
fn name(&self) -> &'static str {
tool_metadata::read::NAME
}
fn context(&self) -> ToolPrompt {
ToolPrompt::Read {
path_mode: PathMode::Absolute,
line_numbers: self.line_numbers,
}
}
}
// Build the prompt
let mut pb = SystemPromptBuilder::new()
.working_directory("/path/to/project".to_string());
let read_tool = MyReadTool {
resolver: AbsolutePathResolver, // simplest; see Step 3 for sandboxed alternatives
line_numbers: true,
};
pb.track(read_tool);
// pb.track(other_tool);
// pb.track(another_tool);
// For custom tools (e.g. tool factories, framework adapters) where you
// have name + prompt but no instance, use track_entry():
pb.track_entry("my_custom_tool", ToolPrompt::Static("Use my_custom_tool to do X."));
let system_prompt = pb.build();
The builder includes guidance only for tracked tools. Cross-tool references (e.g. "prefer grep over read for searching") are included only when both tools are present.
Portable custom tools
Custom tools implement CustomTool in reloaded-code-core, not a framework trait. Your adapter only needs a thin wrapper that:
- Converts
CustomToolDefinitioninto your framework's tool definition type - Forwards JSON arguments to
CustomTool::call(ToolRunContext, args) - Converts the returned
ToolOutputinto your framework's tool return type
That means the same custom tool implementation can be registered once through ToolFactory and reused by SerdesAI or any other Rust LLM framework adapter.
Adapter example
See SerdesAI's CustomToolAdapter for a concrete adapter implementation, plus serdesai-custom-tool for a runnable portable custom tool example using the agent runtime, or serdesai-custom-tool-standalone for a direct AgentBuilder example without the runtime.
Step 3: Choose a path resolver
| Resolver | Use when |
|---|---|
AbsolutePathResolver | Paths are unrestricted; use when you trust the LLM or restrict access via the system prompt |
AllowedPathResolver | You want to restrict file access to specific directories |
AllowedGlobResolver | You want fine-grained glob-based allow/deny rules |
use reloaded_code_core::{
AbsolutePathResolver, AllowedPathResolver,
path::{AllowedGlobResolver, GlobPolicy, RuleAction},
};
// Unrestricted
let any_path = AbsolutePathResolver;
// Directory-restricted
let sandbox = AllowedPathResolver::new(["/workspace/project", "/tmp"])?;
// Glob-filtered (last matching rule takes precedence)
let glob = AllowedGlobResolver::new(["/workspace/project"])?
.with_policy(
GlobPolicy::builder()
.add("src/**", RuleAction::Allow)?
.add("target/**", RuleAction::Deny)?
.build()?
);
Runnable example
See system_prompt_preview for a working example of prompt building with the core library. See Examples for the full list.
What you get from core
| Component | What it provides |
|---|---|
read_file, write_file, edit_file | File operations |
glob_files, grep_search | Search operations |
execute_command, execute_command_with_mode | Shell execution |
fetch_url | URL fetching |
read_todos, write_todos | Shared todo state |
SystemPromptBuilder | Context-aware system prompt generation |
ToolContext trait | Tool metadata interface for prompt building |
CustomTool | Portable custom tool definition and execution |
ToolFactory / CustomToolRegistry | Portable custom tool creation and lookup |
ToolCatalogEntry / ToolCatalogKind | Standard/custom tool catalog for adapters |
PathResolver trait | Path security boundary |
AllowedPathResolver | Directory-based sandbox |
AllowedGlobResolver | Glob-based sandbox (last matching rule takes precedence) |
Ruleset / Rule | Permission evaluation engine |
CredentialResolver | API key lookup with overrides |
ModelCatalog | Compact provider/model hash table |
ToolError | Unified error type for all tools |
For the full API reference, see docs.rs/reloaded-code-core.