Skip to content

Commit ed896cf

Browse files
committed
Changed: Add framework-agnostic custom tool registry and API
- Introduce `ToolFactory`, `CustomToolRegistry`, and `ToolCatalogEntry` in `reloaded-code-core` so external frameworks can register and enumerate custom tools without coupling to agents/serdesai - Move tool catalog types from `reloaded-code-agents` into `reloaded-code-core` so `AgentRuntimeBuilder` can accept core catalogs directly - Wire custom tools through the serdesai `AgentRuntime` builder and expose a test-stub factory for framework users - Update docs, READMEs, and examples across all three crates
1 parent 516106c commit ed896cf

30 files changed

Lines changed: 1095 additions & 94 deletions

File tree

README.MD

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ A **CI pipeline**? A **custom product**?
2525
**ReloadedCode** ships the same agent tools as a Rust library.
2626
Shell sandboxing. Default-deny permissions. ~10 MiB footprint.
2727

28-
| | OpenCode | ReloadedCode |
29-
| ------------ | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30-
| Language | TypeScript | Rust |
28+
| | OpenCode | ReloadedCode |
29+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30+
| Language | TypeScript | Rust |
3131
| Memory | <abbr title="opencode v1.14.21&#10;serve: 305 MiB RSS&#10;TUI: 525 MiB RSS&#10;&#10;v1.4.2&#10;serve: 392 MiB RSS&#10;TUI: 679 MiB RSS">~305 MiB</abbr> | <abbr title="~13 MiB RSS on release build, all providers enabled.&#10; • Code &amp; read-only data: ~6.5 MiB&#10; • Heap (runtime state): ~2.5 MiB&#10; • Shared libraries (glibc, libm): ~2.3 MiB&#10; • Thread stacks: ~0.1 MiB (34 threads)&#10; Private ~2.5 MiB · PSS ~10 MiB.">~13 MiB</abbr> |
32-
| Interface | TUI / Desktop / IDE | Library (headless) |
33-
| Agent format | Markdown + YAML | Similar format |
34-
| Embeddable | HTTP API | Rust crate |
32+
| Interface | TUI / Desktop / IDE | Library (headless) |
33+
| Agent format | Markdown + YAML | Similar format |
34+
| Embeddable | HTTP API | Rust crate |
3535

3636
## Features
3737

@@ -114,13 +114,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
114114

115115
## Crate Map
116116

117-
| Crate | Version | Description |
118-
| --------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
119-
| [**reloaded-code-core**](./src/reloaded-code-core/) | 0.2 | Framework-agnostic tool implementations, path resolvers, permissions, system prompt builder |
120-
| [**reloaded-code-agents**](./src/reloaded-code-agents/) | 0.1 | agent markdown loader similar to [OpenCode](https://opencode.ai), typed catalog, runtime builder |
121-
| [**reloaded-code-serdesai**](./src/reloaded-code-serdesai/) | 0.2 | SerdesAI framework integration, tool adapters, 15 provider bridges, task delegation |
122-
| [**reloaded-code-bubblewrap**](./src/reloaded-code-bubblewrap/) | 0.1 | Linux bubblewrap sandbox profiles (Public Bot + Trusted Maintenance) |
123-
| [**reloaded-code-models-dev**](./src/reloaded-code-models-dev/) | 0.1 | models.dev catalog sync with ETag caching and offline fallback |
117+
| Crate | Version | Description |
118+
| ------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
119+
| [**reloaded-code-core**](./src/reloaded-code-core/) | 0.2 | Framework-agnostic tool implementations, path resolvers, permissions, custom tool registry |
120+
| [**reloaded-code-agents**](./src/reloaded-code-agents/) | 0.1 | agent markdown loader similar to [OpenCode](https://opencode.ai), typed catalog, runtime builder |
121+
| [**reloaded-code-serdesai**](./src/reloaded-code-serdesai/) | 0.2 | SerdesAI framework integration, tool adapters, 15 provider bridges, task delegation |
122+
| [**reloaded-code-bubblewrap**](./src/reloaded-code-bubblewrap/) | 0.1 | Linux bubblewrap sandbox profiles (Public Bot + Trusted Maintenance) |
123+
| [**reloaded-code-models-dev**](./src/reloaded-code-models-dev/) | 0.1 | models.dev catalog sync with ETag caching and offline fallback |
124+
| [**reloaded-code-provider-config**](./src/reloaded-code-provider-config/) | 0.1 | Provider configuration loading and provider catalog overrides |
124125

125126
## Examples
126127

docs/src/agents.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ Evaluation uses **last-match-wins**: the final matching rule takes effect.
9292
For the full rule table and examples, see
9393
[Tools > Permission rules](tools.md#permission-rules).
9494

95+
#### Custom tool permissions
96+
97+
Custom tools are referenced in the `permission` map by name, same as built-in tools.
98+
See [Custom tools](tools.md#custom-tools) for registration.
99+
100+
```yaml
101+
permission:
102+
web_search: allow # custom tool
103+
database: deny # custom tool
104+
```
105+
95106
### Model specification
96107

97108
Format: `provider/model-id` or `synthetic/hf:huggingface-model-id`.

docs/src/architecture.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ graph TD
1212
serdesai["reloaded-code-serdesai<br/><i>SerdesAI framework integration</i>"]
1313
bubblewrap["reloaded-code-bubblewrap<br/><i>Linux sandbox profiles</i>"]
1414
modelsdev["reloaded-code-models-dev<br/><i>models.dev catalog sync</i>"]
15+
providerconfig["reloaded-code-provider-config<br/><i>Provider config loading</i>"]
1516
1617
agents --> core
1718
serdesai --> core
1819
serdesai --> agents
1920
serdesai -.->|optional| bubblewrap
2021
modelsdev --> core
22+
providerconfig --> core
2123
2224
classDef default fill:#1C1C1C,stroke:#fa7774,strokeWidth:2px,color:#fff
2325
```
@@ -32,6 +34,8 @@ The foundation. Contains every tool implementation as a plain function
3234
- **Path resolvers** - control which files tools can access
3335
- **System prompt builder** - generates context-aware tool guidance
3436
- **Permission engine** - last-match-wins rules with wildcard patterns
37+
- **Custom tool registry + catalog** - framework-agnostic `ToolFactory`,
38+
`CustomToolRegistry`, and `ToolCatalogEntry` types
3539
- **Credential resolver** - API key lookup with override support ([details](getting-started.md#credential-management))
3640
- **Model catalog** - compact hash-table-based provider/model lookup
3741

@@ -46,6 +50,7 @@ Loads agent definitions from markdown files with YAML frontmatter. Provides:
4650
- **AgentLoader** - scans directories for `.md` agent files
4751
- **AgentCatalog** - name-to-config lookup table
4852
- **AgentRuntime** - bundles catalog + defaults + permissions + task settings
53+
- **AgentRuntimeBuilder** - accepts core tool catalogs and custom tool factories
4954

5055
The agent file format mirrors [OpenCode]'s schema - similar enough that many
5156
files are drop-in compatible, but [not identical](migration.md). The most
@@ -82,6 +87,11 @@ Syncs the online [models.dev](https://models.dev) catalog into a compact
8287
- Offline fallback when network is unavailable
8388
- Cache load in ~0.3 ms
8489

90+
### reloaded-code-provider-config
91+
92+
Loads provider override configuration and turns it into provider catalog entries
93+
that can be merged with or replace the defaults from models.dev.
94+
8595
## Where your code plugs in
8696

8797
There are two integration paths:

docs/src/getting-started.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,38 @@ a Rust project and an LLM API key (e.g. `OPENAI_API_KEY`).
155155
[serdesai-agents](https://github.com/Reloaded-Project/ReloadedCode/blob/main/src/reloaded-code-serdesai/examples/serdesai-agents.rs)
156156
(with agent files). See [Examples](examples.md) for the full list.
157157

158+
## Custom tools
159+
160+
Implement [`ToolContext`] and [`ToolFactory`], then register with the builder:
161+
162+
```rust
163+
struct MyFactory;
164+
impl ToolContext for MyFactory {
165+
fn name(&self) -> &'static str { "my_tool" }
166+
fn context(&self) -> ToolPrompt {
167+
ToolPrompt::Static("Guidance for using my_tool.")
168+
}
169+
}
170+
impl ToolFactory for MyFactory {
171+
fn create(&self, _ctx: &ToolBuildContext) -> Box<dyn Any + Send + Sync> {
172+
todo!("return your tool")
173+
}
174+
}
175+
176+
let runtime = AgentRuntimeBuilder::new()
177+
.custom_tool(MyFactory)
178+
.tools(vec![
179+
ToolCatalogEntry::new("my_tool", ToolCatalogKind::Custom),
180+
])
181+
.build()?;
182+
```
183+
184+
See [Tools > Custom tools](tools.md#custom-tools) for annotated details
185+
and error handling.
186+
187+
[`ToolContext`]: https://docs.rs/reloaded-code-core/latest/reloaded_code_core/trait.ToolContext.html
188+
[`ToolFactory`]: https://docs.rs/reloaded-code-core/latest/reloaded_code_core/trait.ToolFactory.html
189+
158190
## Credential management
159191

160192
`CredentialResolver` resolves API keys by name (e.g. `"OPENAI_API_KEY"`) -

docs/src/guides/custom-framework.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ pb.track(read_tool);
9797
// pb.track(other_tool);
9898
// pb.track(another_tool);
9999

100+
// For custom tools (e.g. tool factories, framework adapters) where you
101+
// have name + prompt but no instance, use track_entry():
102+
pb.track_entry("my_custom_tool", ToolPrompt::Static("Use my_custom_tool to do X."));
103+
100104
let system_prompt = pb.build();
101105
```
102106

@@ -143,22 +147,24 @@ let glob = AllowedGlobResolver::new(["/workspace/project"])?
143147

144148
## What you get from core
145149

146-
| Component | What it provides |
147-
| ---------------------------------------------- | ------------------------------------------- |
148-
| `read_file`, `write_file`, `edit_file` | File operations |
149-
| `glob_files`, `grep_search` | Search operations |
150-
| `execute_command`, `execute_command_with_mode` | Shell execution |
151-
| `fetch_url` | URL fetching |
152-
| `read_todos`, `write_todos` | Shared todo state |
153-
| `SystemPromptBuilder` | Context-aware system prompt generation |
154-
| `ToolContext` trait | Tool metadata interface for prompt building |
155-
| `PathResolver` trait | Path security boundary |
156-
| `AllowedPathResolver` | Directory-based sandbox |
150+
| Component | What it provides |
151+
| ---------------------------------------------- | -------------------------------------------------------- |
152+
| `read_file`, `write_file`, `edit_file` | File operations |
153+
| `glob_files`, `grep_search` | Search operations |
154+
| `execute_command`, `execute_command_with_mode` | Shell execution |
155+
| `fetch_url` | URL fetching |
156+
| `read_todos`, `write_todos` | Shared todo state |
157+
| `SystemPromptBuilder` | Context-aware system prompt generation |
158+
| `ToolContext` trait | Tool metadata interface for prompt building |
159+
| `ToolFactory` / `CustomToolRegistry` | Framework-agnostic custom tool creation and lookup |
160+
| `ToolCatalogEntry` / `ToolCatalogKind` | Standard/custom tool catalog for adapters |
161+
| `PathResolver` trait | Path security boundary |
162+
| `AllowedPathResolver` | Directory-based sandbox |
157163
| `AllowedGlobResolver` | Glob-based sandbox (last matching rule takes precedence) |
158-
| `Ruleset` / `Rule` | Permission evaluation engine |
159-
| `CredentialResolver` | API key lookup with overrides |
160-
| `ModelCatalog` | Compact provider/model hash table |
161-
| `ToolError` | Unified error type for all tools |
164+
| `Ruleset` / `Rule` | Permission evaluation engine |
165+
| `CredentialResolver` | API key lookup with overrides |
166+
| `ModelCatalog` | Compact provider/model hash table |
167+
| `ToolError` | Unified error type for all tools |
162168

163169
For the full API reference, see [docs.rs/reloaded-code-core](https://docs.rs/reloaded-code-core).
164170

docs/src/tools.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,58 @@ permission:
118118
| [**todoread**](#todoread-todowrite) | `read_todos` | Read shared todo list state |
119119
| [**todowrite**](#todoread-todowrite) | `write_todos` | Update shared todo list state |
120120
| [**task**](#task) | `TaskInput`/`TaskOutput` | Delegate work to a named sub-agent |
121+
| [**custom**](#custom-tools) | `ToolFactory` | User-defined tool registered by the embedder |
122+
123+
### Custom tools
124+
125+
Custom tools let embedders add non-built-in tools to an agent runtime.
126+
127+
```rust
128+
use reloaded_code_agents::AgentRuntimeBuilder;
129+
use reloaded_code_core::{
130+
ToolBuildContext, ToolCatalogEntry, ToolCatalogKind, ToolContext, ToolFactory,
131+
};
132+
use reloaded_code_core::context::ToolPrompt;
133+
use std::any::Any;
134+
135+
struct WebSearchFactory;
136+
137+
// Name + prompt guidance.
138+
impl ToolContext for WebSearchFactory {
139+
fn name(&self) -> &'static str { "web_search" }
140+
fn context(&self) -> ToolPrompt {
141+
ToolPrompt::Static("Use web_search to find information online.")
142+
}
143+
}
144+
145+
// Build framework-specific tool instance.
146+
impl ToolFactory for WebSearchFactory {
147+
fn create(&self, _ctx: &ToolBuildContext) -> Box<dyn Any + Send + Sync> {
148+
// SerdesAI: return Box::new(Box<dyn serdes_ai::Tool<()>>).
149+
todo!("return your tool")
150+
}
151+
}
152+
153+
let runtime = AgentRuntimeBuilder::new()
154+
// Register factory.
155+
.custom_tool(WebSearchFactory)
156+
// Enable tool in catalog.
157+
.tools(vec![
158+
ToolCatalogEntry::new("web_search", ToolCatalogKind::Custom),
159+
])
160+
.build()?;
161+
```
162+
163+
Rules:
164+
165+
- Factory name must match catalog entry name.
166+
- `ToolContext::context()` adds system-prompt guidance.
167+
- Custom tool names work in agent `permission` maps.
168+
- Missing factory: `AgentBuildError::UnknownCustomTool`.
169+
- Wrong return type: `AgentBuildError::CustomToolDowncastFailed`.
170+
171+
See [reloaded-code-core API docs](https://docs.rs/reloaded-code-core/latest)
172+
for full API details.
121173

122174
### read
123175

@@ -425,3 +477,5 @@ For a deeper dive into path security, see [Sandboxing](sandboxing.md).
425477
[reloaded-code-serdesai]: https://docs.rs/reloaded-code-serdesai
426478
[Agents]: agents.md
427479
[agent files]: agents.md
480+
[`ToolFactory`]: https://docs.rs/reloaded-code-core/latest/reloaded_code_core/trait.ToolFactory.html
481+
[`ToolContext`]: https://docs.rs/reloaded-code-core/latest/reloaded_code_core/trait.ToolContext.html

0 commit comments

Comments
 (0)