omakase-agents 0.0.1.alpha → 0.0.2.alpha
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 +51 -10
- data/lib/omakase/agent.rb +10 -0
- data/lib/omakase/mcp.rb +43 -0
- data/lib/omakase/skills.rb +30 -0
- data/lib/omakase/version.rb +1 -1
- data/lib/omakase.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 96b04f8669c6e6bf4f176d19b779ca495fb1b2fde034952c48175eb2999647b3
|
|
4
|
+
data.tar.gz: 32d2d58018dd35cf63590c889f6c9e5a8bca214e1ccfcd9989c0f90c157bec70
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e596c18452218540f3a92c7f2542cbf1ea7e5b4fe0314c359e784d14fe331defdaf40f642cd4679667ed0e6ca5c8e42bd171cb940b2b2bd6b3fd47faba41bf97
|
|
7
|
+
data.tar.gz: 18e626683d93029d7d29da409b9f456800cf4b0579e9e478636d4b8ff2902f628ef34460cf3dd935b164e76d057bc3b01bb22c3a4b58fe9c15c354befbdc9cf9
|
data/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Omakase
|
|
2
2
|
|
|
3
|
-
A light agent framework — about
|
|
3
|
+
A light agent framework — about 700 lines of library. *Omakase* (お任せ): you name what you want,
|
|
4
4
|
the rest is left to the chef.
|
|
5
5
|
|
|
6
|
+
**[esshka.github.io/omakase](https://esshka.github.io/omakase/)** · [rubygems](https://rubygems.org/gems/omakase-agents) 
|
|
7
|
+
|
|
6
8
|
The whole philosophy: **an agent is an object**. Its fields are state, its methods are what the
|
|
7
9
|
model can call, and the methods it *declares without a body* are written by the model at runtime —
|
|
8
10
|
the method name and prompt are the specification, the schema is the contract.
|
|
@@ -40,13 +42,11 @@ Ruby 3.2+.
|
|
|
40
42
|
gem "omakase-agents" # the library is `Omakase`
|
|
41
43
|
```
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
From the command line the flag is needed — `0.0.2.alpha` is a prerelease, and RubyGems skips those
|
|
46
|
+
unless asked. Bundler resolves it without one, since no stable version exists yet.
|
|
44
47
|
|
|
45
48
|
```bash
|
|
46
|
-
|
|
47
|
-
cd omakase
|
|
48
|
-
gem build omakase-agents.gemspec
|
|
49
|
-
gem install ./omakase-agents-*.gem
|
|
49
|
+
gem install omakase-agents --pre
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
## Usage
|
|
@@ -131,8 +131,45 @@ Omakase.configure do |config|
|
|
|
131
131
|
end
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### MCP tools
|
|
135
|
+
|
|
136
|
+
An MCP server's tools become methods on the agent, listed among its capabilities like any other —
|
|
137
|
+
so generated code calls a remote tool and the agent's own methods in the same expression. Add the
|
|
138
|
+
`ruby_llm-mcp` gem; options are passed to it verbatim.
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
class DocsAgent < ApplicationAgent
|
|
142
|
+
mcp :files,
|
|
143
|
+
transport_type: :stdio,
|
|
144
|
+
config: {command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", Rails.root.to_s]}
|
|
145
|
+
|
|
146
|
+
generates :changelog, "Summarise what changed in the last release."
|
|
147
|
+
end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The connection opens when the class is defined and the tools are read from the server then, so a
|
|
151
|
+
tool's arguments reach the model as documentation. A failed call raises, which the model sees and
|
|
152
|
+
can correct. Only text comes back: an image or audio result is dropped.
|
|
134
153
|
### Testing
|
|
135
154
|
|
|
155
|
+
### Skills
|
|
156
|
+
|
|
157
|
+
A skill is a directory with a `SKILL.md` — the same YAML front matter Claude Code and friends use.
|
|
158
|
+
`skill` reads it and defines one method: the front matter's `description` joins the agent's
|
|
159
|
+
capabilities, and the body is what the method returns.
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
class CommitAgent < ApplicationAgent
|
|
163
|
+
skill "skills/commit-style" # description: "How this project writes commit subjects…"
|
|
164
|
+
|
|
165
|
+
generates :subject_for, "Write the commit subject for this change.", returns: :string
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
That is the whole of “loaded on demand”: the one-line description is in the prompt, the body only
|
|
170
|
+
reaches the model if the generated code calls `commit_style`. Anything else the skill ships —
|
|
171
|
+
scripts, templates — sits in the same directory, and the body ends with its path, so generated Ruby
|
|
172
|
+
can read or run it.
|
|
136
173
|
`Omakase::Agent.new(chat:)` takes any object that quacks like a `RubyLLM::Chat`, and one ships with
|
|
137
174
|
the library, so agents are tested without a network:
|
|
138
175
|
|
|
@@ -278,6 +315,8 @@ generates :plan, strategy: CriticStrategy
|
|
|
278
315
|
lib/omakase/doc.rb what an unfamiliar object offers, for generated code
|
|
279
316
|
lib/omakase/executor.rb runs generated Ruby against the agent
|
|
280
317
|
lib/omakase/tools/ruby.rb that executor, as a RubyLLM tool, with a call budget
|
|
318
|
+
lib/omakase/mcp.rb an MCP server’s tools, as methods on the agent
|
|
319
|
+
lib/omakase/skills.rb a SKILL.md directory, as one described method
|
|
281
320
|
lib/omakase/fake_chat.rb the stand-in chat for tests
|
|
282
321
|
lib/omakase/strategies/ code_act, predict
|
|
283
322
|
|
|
@@ -293,6 +332,8 @@ Copy `.env.example` to `.env` and fill in a key; `MODEL` and `PROVIDER` there pi
|
|
|
293
332
|
| [`support_agent.rb`](examples/support_agent.rb) | plain Ruby orchestrating generated methods |
|
|
294
333
|
| [`support_job.rb`](examples/support_job.rb) | generation off the request thread, via ActiveJob |
|
|
295
334
|
| [`rails_app.rb`](examples/rails_app.rb) | a whole Rails app in one file: initializer, agent, controller |
|
|
335
|
+
| [`mcp_agent.rb`](examples/mcp_agent.rb) | an MCP server's tools as methods on the agent |
|
|
336
|
+
| [`skill_agent.rb`](examples/skill_agent.rb) | a SKILL.md directory the model loads when it needs it |
|
|
296
337
|
|
|
297
338
|
```bash
|
|
298
339
|
bundle exec rake # tests, no network
|
|
@@ -327,10 +368,10 @@ What is not here yet, roughly in the order it would earn its place:
|
|
|
327
368
|
- [ ] **Conversation history** — the chat is fresh per call. Keeping one per agent would let a
|
|
328
369
|
method continue where the last one left off, at the cost of deciding what to keep.
|
|
329
370
|
- [ ] **Session storage** — persist that history and the agent state so a run can be resumed.
|
|
330
|
-
- [
|
|
331
|
-
|
|
332
|
-
- [
|
|
333
|
-
all sitting in the system prompt.
|
|
371
|
+
- [x] **MCP tools** — external tools over the Model Context Protocol, via `ruby_llm-mcp`. Done:
|
|
372
|
+
`mcp :files, …` puts the server's tools on the agent, and generated code calls them.
|
|
373
|
+
- [x] **Skills** — capabilities as markdown files with front matter, loaded on demand rather than
|
|
374
|
+
all sitting in the system prompt. Done: `skill "path/to/dir"`, one described method.
|
|
334
375
|
- [ ] **Memory** — recall that survives across sessions, backed by vector search.
|
|
335
376
|
- [x] **Concurrency** — parallel generation calls. Done: output is buffered per thread instead of
|
|
336
377
|
through `$stdout`, so threads no longer collide.
|
data/lib/omakase/agent.rb
CHANGED
|
@@ -27,6 +27,16 @@ module Omakase
|
|
|
27
27
|
@strategy = name
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# An MCP server's tools, as methods on the agent. Options are passed to
|
|
31
|
+
# `ruby_llm-mcp` verbatim: `mcp :files, transport_type: :stdio, config: {command: "npx", …}`.
|
|
32
|
+
def mcp(name, **options)
|
|
33
|
+
require "ruby_llm/mcp"
|
|
34
|
+
MCP.attach(self, RubyLLM::MCP.add_client(name: name.to_s, **options))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# A skill directory — a SKILL.md with YAML front matter. Its description
|
|
38
|
+
# joins the agent's capabilities; its body arrives when the model asks.
|
|
39
|
+
def skill(path) = Skills.attach(self, path)
|
|
30
40
|
# Documents the method defined next — the docstring Ruby does not have.
|
|
31
41
|
def describe(text)
|
|
32
42
|
@pending_description = text
|
data/lib/omakase/mcp.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omakase
|
|
4
|
+
# An MCP server's tools, as methods on the agent — so generated code calls a
|
|
5
|
+
# remote tool the same way it calls anything else the agent exposes.
|
|
6
|
+
module MCP
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def attach(agent_class, client)
|
|
10
|
+
client.tools.each do |tool|
|
|
11
|
+
name = method_name(tool)
|
|
12
|
+
# A remote tool list must not quietly shadow a capability the agent already has.
|
|
13
|
+
raise Error, "#{agent_class} already has ##{name}" if Capabilities.names(agent_class).include?(name)
|
|
14
|
+
|
|
15
|
+
agent_class.describe(description(tool))
|
|
16
|
+
# nil is how a model leaves an argument out; MCP servers reject it.
|
|
17
|
+
agent_class.define_method(name) { |**arguments| MCP.result(tool.execute(**arguments.compact)) }
|
|
18
|
+
end
|
|
19
|
+
client
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Tool names may hold characters a Ruby method name cannot.
|
|
23
|
+
def method_name(tool) = tool.name.tr("-", "_").to_sym
|
|
24
|
+
|
|
25
|
+
# ponytail: text only — an image or audio result is dropped.
|
|
26
|
+
def result(value)
|
|
27
|
+
raise Error, value[:error] if value.is_a?(Hash) && value[:error]
|
|
28
|
+
|
|
29
|
+
value.to_s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The signature is `**arguments`, so what those arguments are goes here.
|
|
33
|
+
def description(tool)
|
|
34
|
+
schema = tool.params_schema || {}
|
|
35
|
+
required = schema["required"] || []
|
|
36
|
+
arguments = (schema["properties"] || {}).map do |name, property|
|
|
37
|
+
"#{name}: #{property["type"]}#{" (required)" if required.include?(name)}"
|
|
38
|
+
end
|
|
39
|
+
text = tool.description.to_s.gsub(/\s+/, " ").strip
|
|
40
|
+
[text, ("Arguments — #{arguments.join(", ")}" if arguments.any?)].compact.join(" ")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omakase
|
|
4
|
+
# A skill is a directory with a SKILL.md: YAML front matter says what it is,
|
|
5
|
+
# the body is the guidance. The description is listed with the agent's other
|
|
6
|
+
# capabilities; the body only arrives when the model calls the method — which
|
|
7
|
+
# is all "loaded on demand" has to mean.
|
|
8
|
+
module Skills
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def attach(agent_class, path)
|
|
12
|
+
directory = File.expand_path(path)
|
|
13
|
+
front_matter, body = parse(File.read(File.join(directory, "SKILL.md")))
|
|
14
|
+
name = (front_matter["name"] || File.basename(directory)).tr("-", "_").to_sym
|
|
15
|
+
raise Error, "#{agent_class} already has ##{name}" if Capabilities.names(agent_class).include?(name)
|
|
16
|
+
|
|
17
|
+
agent_class.describe(front_matter["description"].to_s)
|
|
18
|
+
agent_class.define_method(name) { "#{body}\n\nFiles for this skill are in #{directory}." }
|
|
19
|
+
name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The front matter every SKILL.md in the wild is written with.
|
|
23
|
+
def parse(text)
|
|
24
|
+
match = text.match(/\A---\n(.*?)\n---\n(.*)\z/m)
|
|
25
|
+
return [{}, text.strip] unless match
|
|
26
|
+
|
|
27
|
+
[YAML.safe_load(match[1]), match[2].strip]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/omakase/version.rb
CHANGED
data/lib/omakase.rb
CHANGED
|
@@ -8,9 +8,11 @@ require "schematist"
|
|
|
8
8
|
require "stringio"
|
|
9
9
|
require "timeout"
|
|
10
10
|
require "zeitwerk"
|
|
11
|
+
require "yaml"
|
|
11
12
|
|
|
12
13
|
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
|
13
14
|
loader.ignore("#{__dir__}/omakase-agents.rb")
|
|
15
|
+
loader.inflector.inflect("mcp" => "MCP")
|
|
14
16
|
loader.setup
|
|
15
17
|
|
|
16
18
|
module Omakase
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omakase-agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2.alpha
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- eugeny
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -73,8 +73,10 @@ files:
|
|
|
73
73
|
- lib/omakase/executor.rb
|
|
74
74
|
- lib/omakase/fake_chat.rb
|
|
75
75
|
- lib/omakase/generation.rb
|
|
76
|
+
- lib/omakase/mcp.rb
|
|
76
77
|
- lib/omakase/request.rb
|
|
77
78
|
- lib/omakase/schema.rb
|
|
79
|
+
- lib/omakase/skills.rb
|
|
78
80
|
- lib/omakase/strategies.rb
|
|
79
81
|
- lib/omakase/strategies/code_act.rb
|
|
80
82
|
- lib/omakase/strategies/predict.rb
|