ruby_llm-code_mode 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +21 -17
- data/lib/ruby_llm/code_mode.rb +47 -36
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 65329e122014fe96a542cab62f9eb0bb002f3c1ac9f53fae0f5d7b384183433e
|
|
4
|
+
data.tar.gz: c9f4272b1b65da90128fb4a62d211a47606814091ee07095fe55535126aea154
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f31a40ab23606bfd6f2c5799ff90c780c514eac95bd3f63d4e4ef933c97adf38ae7d61ca65fe4dc53ba472f8ed38a8a64ae3fcf286656c7694ac2c8c9842817
|
|
7
|
+
data.tar.gz: 1316c738fba0e23e1dc0db07ea63d926cb535bd082cf649a0511c68639e86e777e9077cbe7f1a9973b05e110f023ba9d4d21df3638602956525ca33a2d8e3602
|
data/README.md
CHANGED
|
@@ -50,7 +50,7 @@ a fixed description built from the declared mounts:
|
|
|
50
50
|
- a **Read-only folders** section listing each `mount` as `` `/guest/path` — description``;
|
|
51
51
|
- a **Read-write folders** section listing each `mount_rw`;
|
|
52
52
|
- a **Host tools** section listing every bound `RubyLLM::Tool` with its
|
|
53
|
-
parameters, when any are bound with `
|
|
53
|
+
parameters, when any are bound with `tools` or `mcps`.
|
|
54
54
|
|
|
55
55
|
The single parameter is `code` — a complete, self-contained Ruby script.
|
|
56
56
|
|
|
@@ -70,9 +70,10 @@ A JSON object sent back to the model:
|
|
|
70
70
|
```ruby
|
|
71
71
|
mount source: host_path, dest: guest_path, description: "..." # read-only
|
|
72
72
|
mount_rw source: host_path, dest: guest_path, description: "..." # read-write
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
tools SomeRubyLLMTool # host tool
|
|
74
|
+
tools SomeTool, OtherTool # several (or arrays)
|
|
75
|
+
tools "name" => SomeRubyLLMTool # explicit name(s)
|
|
76
|
+
mcps SomeMcpServer # MCP server(s) (instance or class)
|
|
76
77
|
```
|
|
77
78
|
|
|
78
79
|
- `source:` — folder on the host machine, relative to the process working
|
|
@@ -80,12 +81,15 @@ mcp SomeMcpServer # MCP server (
|
|
|
80
81
|
- `dest:` — absolute, normalized path inside the sandbox (may not overlap
|
|
81
82
|
the reserved `/work`, `/usr` or `/src` trees, and must be unique).
|
|
82
83
|
- `description:` — shown to the model in the tool description.
|
|
83
|
-
- `
|
|
84
|
-
is derived from the class-name leaf
|
|
85
|
-
or the explicit one from the
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
- `tools` — one or more `RubyLLM::Tool` classes/instances (or arrays of
|
|
85
|
+
them); the name inside the sandbox is derived from the class-name leaf
|
|
86
|
+
(`MyApp::Tools::Weather` → `weather`), or the explicit one from the
|
|
87
|
+
`"name" => tool` pairs. Called without arguments, it returns the bound
|
|
88
|
+
tools.
|
|
89
|
+
- `mcps` — one or more `RubyLLM::MCP` instances or classes
|
|
90
|
+
(auto-instantiated, with the server's declared inputs as keywords); all of
|
|
91
|
+
each server's tools are bound under their own names. Called without
|
|
92
|
+
arguments, it returns the connected servers.
|
|
89
93
|
|
|
90
94
|
Invalid declarations raise at class-definition time, so a misconfigured tool
|
|
91
95
|
never reaches a live chat. Subclasses inherit their parent's mounts and tools.
|
|
@@ -107,8 +111,8 @@ end
|
|
|
107
111
|
|
|
108
112
|
class Assistant < RubyLLM::CodeMode
|
|
109
113
|
mount source: "data", dest: "/data", description: "Project data"
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
tools Weather # binds as `weather`
|
|
115
|
+
tools "forecast" => Weather # or bind it under an explicit name
|
|
112
116
|
end
|
|
113
117
|
```
|
|
114
118
|
|
|
@@ -136,7 +140,7 @@ report = SB.call("weather", city: "Porto Alegre")
|
|
|
136
140
|
|
|
137
141
|
### MCP servers
|
|
138
142
|
|
|
139
|
-
The `
|
|
143
|
+
The `mcps` DSL connects [RubyLLM::MCP](https://rubyllm.com/next/mcp/) servers
|
|
140
144
|
and binds all their tools in one call, letting the sandboxed code reach the
|
|
141
145
|
server through the host RPC channel:
|
|
142
146
|
|
|
@@ -144,9 +148,9 @@ server through the host RPC channel:
|
|
|
144
148
|
docs = RubyLLM.mcp(url: "https://learn.microsoft.com/api/mcp")
|
|
145
149
|
|
|
146
150
|
class DocsResearch < RubyLLM::CodeMode
|
|
147
|
-
|
|
148
|
-
#
|
|
149
|
-
#
|
|
151
|
+
mcps docs # instance
|
|
152
|
+
# mcps MicrosoftDocs # or the class (instantiated with .new)
|
|
153
|
+
# mcps Linear, user: current_user # class + the server's declared inputs
|
|
150
154
|
end
|
|
151
155
|
```
|
|
152
156
|
|
|
@@ -170,7 +174,7 @@ result[:error] # => true when the tool reported a failure
|
|
|
170
174
|
and raise at definition time.
|
|
171
175
|
- The class form binds the tools of one instance built at definition time —
|
|
172
176
|
for servers that act per-user, pass a ready instance instead
|
|
173
|
-
(`
|
|
177
|
+
(`mcps Linear.new(user: current_user)`) where the user is available.
|
|
174
178
|
|
|
175
179
|
The sandbox itself never sees the network — every `SB.call` performs the MCP
|
|
176
180
|
request on the host. A runnable example lives in `sample/ruby_llm_mcp/`.
|
data/lib/ruby_llm/code_mode.rb
CHANGED
|
@@ -5,14 +5,14 @@ require "security_box"
|
|
|
5
5
|
|
|
6
6
|
module RubyLLM
|
|
7
7
|
class CodeMode < Tool
|
|
8
|
-
VERSION = "0.
|
|
8
|
+
VERSION = "0.3.0"
|
|
9
9
|
|
|
10
10
|
DEFAULT_TIMEOUT_MS = 30_000
|
|
11
11
|
DEFAULT_FUEL_MS = 10_000
|
|
12
12
|
MAX_TOOLS = SecurityBox::Rpcs::MAX_RPCS
|
|
13
13
|
|
|
14
|
-
TOOL_USAGE = "
|
|
15
|
-
'or
|
|
14
|
+
TOOL_USAGE = "tools expects RubyLLM::Tool classes or instances, " \
|
|
15
|
+
'or "name" => tool pairs'
|
|
16
16
|
|
|
17
17
|
FIXED_DESCRIPTION = <<~DESC.freeze
|
|
18
18
|
Executes Ruby code inside a secure sandbox and returns what it produced.
|
|
@@ -69,22 +69,38 @@ module RubyLLM
|
|
|
69
69
|
add_mount(source, dest, :read_write, description)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
# Binds RubyLLM::Tool classes/instances as host tools callable from
|
|
73
|
+
# inside the sandbox via SB.call:
|
|
74
|
+
#
|
|
75
|
+
# tools Weather # class (instantiated with .new)
|
|
76
|
+
# tools Weather, Echo # several at once
|
|
77
|
+
# tools [Weather, Echo] # or arrays
|
|
78
|
+
# tools "forecast" => Weather # explicit sandbox name
|
|
79
|
+
# tools forecast: Weather # same, as kwargs
|
|
80
|
+
#
|
|
81
|
+
# Without arguments it returns the hash of bound tools. The name is
|
|
82
|
+
# derived from the class-name leaf (`MyApp::Tools::Weather` binds as
|
|
83
|
+
# `weather`). Fails fast: every item must be a RubyLLM::Tool (class or
|
|
84
|
+
# instance), names must be unique and non-empty, and if anything goes
|
|
85
|
+
# wrong nothing is bound.
|
|
86
|
+
def tools(*args, **kwargs)
|
|
87
|
+
return @tools ||= {} if args.empty? && kwargs.empty?
|
|
88
|
+
|
|
89
|
+
snapshot = @tools.dup
|
|
90
|
+
begin
|
|
91
|
+
args.flatten(1).each do |item|
|
|
92
|
+
if item.is_a?(Hash)
|
|
93
|
+
item.each { |name, bound| add_tool(bound, name.to_s) }
|
|
94
|
+
else
|
|
95
|
+
add_tool(item, nil)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
kwargs.each { |name, bound| add_tool(bound, name.to_s) }
|
|
99
|
+
rescue StandardError
|
|
100
|
+
instance_variable_set(:@tools, snapshot)
|
|
101
|
+
raise
|
|
87
102
|
end
|
|
103
|
+
tools
|
|
88
104
|
end
|
|
89
105
|
|
|
90
106
|
# Connects MCP server(s) and binds all their tools into the sandbox:
|
|
@@ -92,21 +108,24 @@ module RubyLLM
|
|
|
92
108
|
# docs = RubyLLM.mcp(url: "https://learn.microsoft.com/api/mcp")
|
|
93
109
|
#
|
|
94
110
|
# class DocsResearch < RubyLLM::CodeMode
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
111
|
+
# mcps docs # instance
|
|
112
|
+
# mcps MicrosoftDocs # or the class (instantiated with .new)
|
|
113
|
+
# mcps Linear, user: current_user # class + declared inputs
|
|
114
|
+
# mcps Docs, Github # several servers / arrays at once
|
|
99
115
|
# end
|
|
100
116
|
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
117
|
+
# Without arguments it returns the array of connected servers. Server
|
|
118
|
+
# tools keep their names (`microsoft_docs_search`, ...); a tool whose
|
|
119
|
+
# name is already bound is re-bound as "<server>_tool". The servers'
|
|
120
|
+
# instructions (when the server sends them) are snapshotted at
|
|
121
|
+
# definition and listed in the description under "## Server notes".
|
|
105
122
|
# Fails fast: every argument must be a RubyLLM::MCP (class or
|
|
106
123
|
# instance), and if anything goes wrong — a server that cannot be
|
|
107
124
|
# reached, a name that stays colliding after prefixing — nothing is
|
|
108
125
|
# bound.
|
|
109
|
-
def
|
|
126
|
+
def mcps(*servers, **inputs)
|
|
127
|
+
return @mcps ||= [] if servers.empty? && inputs.empty?
|
|
128
|
+
|
|
110
129
|
flat = servers.flatten(1)
|
|
111
130
|
raise ArgumentError, "expected at least one RubyLLM::MCP server" if flat.empty?
|
|
112
131
|
unless inputs.empty? || flat.all?(Class)
|
|
@@ -127,14 +146,6 @@ module RubyLLM
|
|
|
127
146
|
@mounts ||= []
|
|
128
147
|
end
|
|
129
148
|
|
|
130
|
-
def tools
|
|
131
|
-
@tools ||= {}
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def mcps
|
|
135
|
-
@mcps ||= []
|
|
136
|
-
end
|
|
137
|
-
|
|
138
149
|
def configuration
|
|
139
150
|
@configuration ||= SecurityBox::Configuration.build(
|
|
140
151
|
timeout_ms: DEFAULT_TIMEOUT_MS,
|
|
@@ -263,7 +274,7 @@ module RubyLLM
|
|
|
263
274
|
def normalize_mcp_servers(servers, inputs)
|
|
264
275
|
unless defined?(RubyLLM::MCP)
|
|
265
276
|
raise ArgumentError,
|
|
266
|
-
"RubyLLM::MCP is not available;
|
|
277
|
+
"RubyLLM::MCP is not available; mcps needs a ruby_llm version with MCP support"
|
|
267
278
|
end
|
|
268
279
|
|
|
269
280
|
servers.map do |server|
|