ollama-dsl 0.1.1 → 0.1.2
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/CHANGELOG.md +16 -0
- data/README.md +159 -13
- data/examples/agent.rb +42 -0
- data/examples/use_case.rb +4 -4
- data/lib/ollama/dsl/agent.rb +82 -0
- data/lib/ollama/dsl/client.rb +44 -6
- data/lib/ollama/dsl/tool.rb +31 -0
- data/lib/ollama/dsl/version.rb +1 -1
- data/lib/ollama/dsl.rb +2 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc69d14a5c3e7e436b59da35482e5fc77d49689fefceb1fdb4bfab84a3ed7c76
|
|
4
|
+
data.tar.gz: dee5bee8fbc4401ce92644e18a7e92980ae8db1942d8f1877b2836d9ac999054
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5409975d8ebd80860eecaa4a6fe9dd7a106701c108f85a2cdfd7bafe41280472059a419c06b5da6f9f51cbaf22b9fe784d6a82cde9f7e6c1f13f748124ad341c
|
|
7
|
+
data.tar.gz: '084f29acb614e165d63dc3bad4dd1cd5ead88a45f5642d60b8670114612ff810d790d8a4a666c167696fc16489f566d924c3d6f6eeff9b3d084cff530b1250f4'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
+
```markdown
|
|
1
2
|
## [Unreleased]
|
|
2
3
|
|
|
4
|
+
## [0.1.2] - 2026-10-04
|
|
5
|
+
|
|
6
|
+
- Added `Agent` with Ollama tool calling support
|
|
7
|
+
- Added `Tool` abstraction for executable tools
|
|
8
|
+
- Added tool execution loop
|
|
9
|
+
- Added tool definitions for Ollama
|
|
10
|
+
- Added RSpec tests for tools
|
|
11
|
+
|
|
12
|
+
## [0.1.1] - 2025-11-04
|
|
13
|
+
|
|
14
|
+
- Updated client to support agent tool calling
|
|
15
|
+
- Updated DSL entry point
|
|
16
|
+
- Updated examples
|
|
17
|
+
|
|
3
18
|
## [0.1.0] - 2025-08-22
|
|
4
19
|
|
|
5
20
|
- Initial release
|
|
21
|
+
```
|
data/README.md
CHANGED
|
@@ -1,37 +1,183 @@
|
|
|
1
1
|
# Ollama::Dsl
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/ollama-dsl)
|
|
4
|
+
[](https://rubydoc.info/gems/ollama-dsl)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A Ruby DSL for interacting with Ollama LLMs, with support for streaming, structured prompts, agents, and tool calling.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
Ollama DSL provides an easy-to-use Ruby interface for communicating with local or remote Ollama language models. It supports system and user prompts, streaming responses, prompt chaining, and building agents that can execute custom Ruby tools.
|
|
8
9
|
|
|
10
|
+
## Installation
|
|
9
11
|
|
|
10
|
-
Install the gem
|
|
12
|
+
Install the gem:
|
|
11
13
|
|
|
12
14
|
```bash
|
|
13
|
-
|
|
15
|
+
gem install ollama-dsl
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
Or add it to your `Gemfile`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "ollama-dsl"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then run:
|
|
17
25
|
|
|
18
26
|
```bash
|
|
19
|
-
|
|
27
|
+
bundle install
|
|
20
28
|
```
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
If you want to use it directly from GitHub:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
gem "ollama-dsl", git: "https://github.com/rafaelmerlotto/ollama-dsl.git"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Basic Example
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require "ollama/dsl"
|
|
40
|
+
|
|
41
|
+
Ollama::Dsl.run do
|
|
42
|
+
model "llama3"
|
|
43
|
+
|
|
44
|
+
system "You are a marketing expert assistant."
|
|
45
|
+
user "Write a catchy Instagram post title about artisanal coffee."
|
|
46
|
+
|
|
47
|
+
on_chunk { |token| print token }
|
|
48
|
+
|
|
49
|
+
on_done do |text|
|
|
50
|
+
puts "\n\nFinal response:\n#{text}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Agents
|
|
58
|
+
|
|
59
|
+
Ollama DSL also provides an `Agent` abstraction for building tool-enabled automations.
|
|
60
|
+
|
|
61
|
+
An agent can:
|
|
62
|
+
|
|
63
|
+
* Receive a user instruction
|
|
64
|
+
* Decide which registered tool to use
|
|
65
|
+
* Execute the tool in Ruby
|
|
66
|
+
* Send the tool result back to Ollama
|
|
67
|
+
* Continue the execution loop until the task is completed
|
|
68
|
+
|
|
69
|
+
### Example
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
require "ollama/dsl"
|
|
73
|
+
|
|
74
|
+
session = Ollama::Dsl::Session.new
|
|
75
|
+
|
|
76
|
+
agent = Ollama::Dsl::Agent.new(
|
|
77
|
+
session,
|
|
78
|
+
model: "gpt-oss:20b"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
agent.tool(
|
|
82
|
+
"create_file",
|
|
83
|
+
"Create a text file with the given filename and content.",
|
|
84
|
+
parameters: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
filename: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "The name of the file to create."
|
|
90
|
+
},
|
|
91
|
+
content: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "The content to write into the file."
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
required: ["filename", "content"]
|
|
97
|
+
}
|
|
98
|
+
) do |filename:, content:|
|
|
99
|
+
File.write(filename, content)
|
|
100
|
+
|
|
101
|
+
"File #{filename} was created successfully."
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
puts agent.run(
|
|
105
|
+
"Create a file called hello.txt containing Hello from my AI agent."
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The agent uses Ollama's native tool calling capabilities to decide when a tool should be executed.
|
|
110
|
+
|
|
111
|
+
Tools are not limited to file operations. You can define tools for any Ruby automation your application needs.
|
|
112
|
+
|
|
113
|
+
For example:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
agent.tool("get_time", "Get the current time.", parameters: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {}
|
|
119
|
+
}) do
|
|
120
|
+
Time.now.to_s
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Tool Calling
|
|
125
|
+
|
|
126
|
+
Tools consist of three main parts:
|
|
127
|
+
|
|
128
|
+
* A name
|
|
129
|
+
* A description
|
|
130
|
+
* A parameter schema and Ruby block
|
|
23
131
|
|
|
24
|
-
|
|
132
|
+
The description and parameter schema are sent to Ollama, allowing the model to decide when and how to use the tool.
|
|
133
|
+
|
|
134
|
+
The Ruby block contains the actual implementation that is executed by the agent.
|
|
25
135
|
|
|
26
136
|
## Development
|
|
27
137
|
|
|
28
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
|
138
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
|
139
|
+
|
|
140
|
+
Run the test suite with:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bundle exec rspec
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can also run:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
bundle exec rake
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
for the project's Rake tasks.
|
|
153
|
+
|
|
154
|
+
To open an interactive console:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bin/console
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
To install this gem onto your local machine:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
bundle exec rake install
|
|
164
|
+
```
|
|
29
165
|
|
|
30
|
-
To
|
|
166
|
+
To release a new version, update the version number in `version.rb`, then run:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
bundle exec rake release
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
This will create a git tag for the version, push the git commits and tag, and publish the gem to RubyGems.
|
|
31
173
|
|
|
32
174
|
## Contributing
|
|
33
175
|
|
|
34
|
-
Bug reports and pull requests are welcome on GitHub at
|
|
176
|
+
Bug reports and pull requests are welcome on GitHub at:
|
|
177
|
+
|
|
178
|
+
https://github.com/rafaelmerlotto/ollama-dsl
|
|
179
|
+
|
|
180
|
+
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Code of Conduct](https://github.com/rafaelmerlotto/ollama-dsl/blob/main/CODE_OF_CONDUCT.md).
|
|
35
181
|
|
|
36
182
|
## License
|
|
37
183
|
|
|
@@ -39,4 +185,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
39
185
|
|
|
40
186
|
## Code of Conduct
|
|
41
187
|
|
|
42
|
-
Everyone interacting
|
|
188
|
+
Everyone interacting with the Ollama::Dsl project's codebases, issue trackers and mailing lists is expected to adhere to the [Code of Conduct](https://github.com/rafaelmerlotto/ollama-dsl/blob/main/CODE_OF_CONDUCT.md).
|
data/examples/agent.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
|
4
|
+
|
|
5
|
+
require "ollama/dsl/session"
|
|
6
|
+
require "ollama/dsl/agent"
|
|
7
|
+
|
|
8
|
+
session = Ollama::Dsl::Session.new
|
|
9
|
+
|
|
10
|
+
agent = Ollama::Dsl::Agent.new(
|
|
11
|
+
session,
|
|
12
|
+
model: "gpt-oss:20b"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
agent.tool(
|
|
16
|
+
"create_file",
|
|
17
|
+
"Create a text file with the given filename and content.",
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
filename: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "The name of the file to create."
|
|
24
|
+
},
|
|
25
|
+
content: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "The content to write into the file."
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ["filename", "content"]
|
|
31
|
+
}
|
|
32
|
+
) do |filename:, content:|
|
|
33
|
+
File.write(filename, content)
|
|
34
|
+
|
|
35
|
+
"File #{filename} was created successfully."
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
result = agent.run(
|
|
39
|
+
"Create a file called hello.txt containing exactly: Hello from my AI agent."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
puts result
|
data/examples/use_case.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
$LOAD_PATH.unshift(File.expand_path("
|
|
2
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
require "ollama/dsl/session"
|
|
5
|
+
require "ollama/dsl/dsl"
|
|
6
|
+
require "ollama/dsl/client"
|
|
7
7
|
|
|
8
8
|
session = Ollama::Dsl::Session.new
|
|
9
9
|
dsl = Ollama::Dsl::DSL.new(session)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "tool"
|
|
4
|
+
require_relative "client"
|
|
5
|
+
|
|
6
|
+
module Ollama
|
|
7
|
+
module Dsl
|
|
8
|
+
class Agent
|
|
9
|
+
attr_reader :tools
|
|
10
|
+
|
|
11
|
+
def initialize(session, model: "llama3")
|
|
12
|
+
@session = session
|
|
13
|
+
@model = model
|
|
14
|
+
@tools = {}
|
|
15
|
+
@client = Client.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def tool(name, description, parameters:, &block)
|
|
19
|
+
@tools[name.to_s] = Tool.new(
|
|
20
|
+
name,
|
|
21
|
+
description,
|
|
22
|
+
parameters: parameters,
|
|
23
|
+
&block
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run(instruction)
|
|
28
|
+
@session.add("user", instruction)
|
|
29
|
+
|
|
30
|
+
loop do
|
|
31
|
+
response = @client.chat(
|
|
32
|
+
model: @model,
|
|
33
|
+
messages: @session.messages,
|
|
34
|
+
tools: tool_definitions
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
message = response.fetch("message")
|
|
38
|
+
|
|
39
|
+
tool_calls = message["tool_calls"]
|
|
40
|
+
|
|
41
|
+
if tool_calls.nil? || tool_calls.empty?
|
|
42
|
+
final_answer = message["content"].to_s
|
|
43
|
+
|
|
44
|
+
@session.add("assistant", final_answer)
|
|
45
|
+
|
|
46
|
+
return final_answer
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@session.messages << message
|
|
50
|
+
|
|
51
|
+
tool_calls.each do |tool_call|
|
|
52
|
+
execute_tool(tool_call)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def tool_definitions
|
|
60
|
+
@tools.values.map(&:definition)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def execute_tool(tool_call)
|
|
64
|
+
function = tool_call.fetch("function")
|
|
65
|
+
|
|
66
|
+
name = function.fetch("name")
|
|
67
|
+
arguments = function["arguments"] || {}
|
|
68
|
+
|
|
69
|
+
tool = @tools[name]
|
|
70
|
+
|
|
71
|
+
raise "Unknown tool: #{name}" unless tool
|
|
72
|
+
|
|
73
|
+
result = tool.call(arguments)
|
|
74
|
+
|
|
75
|
+
@session.messages << {
|
|
76
|
+
role: "tool",
|
|
77
|
+
content: result.to_s
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/ollama/dsl/client.rb
CHANGED
|
@@ -11,17 +11,19 @@ module Ollama
|
|
|
11
11
|
@ollama_host = URI(host)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# Used by DSL for streaming responses
|
|
14
15
|
def stream(path, payload)
|
|
15
|
-
|
|
16
16
|
uri = URI("#{@ollama_host}#{path}")
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
request = Net::HTTP::Post.new(
|
|
19
|
+
uri,
|
|
20
|
+
{ "Content-Type" => "application/json" }
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
request.body = payload.merge(stream: true).to_json
|
|
21
24
|
|
|
22
|
-
# Response streaming
|
|
23
25
|
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
|
24
|
-
http.request(
|
|
26
|
+
http.request(request) do |response|
|
|
25
27
|
response.read_body do |chunk|
|
|
26
28
|
begin
|
|
27
29
|
json = JSON.parse(chunk)
|
|
@@ -32,6 +34,42 @@ module Ollama
|
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
37
|
+
|
|
38
|
+
# Used by Agent for tool calling
|
|
39
|
+
def chat(model:, messages:, tools: nil)
|
|
40
|
+
payload = {
|
|
41
|
+
model: model,
|
|
42
|
+
messages: messages,
|
|
43
|
+
stream: false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
payload[:tools] = tools if tools && !tools.empty?
|
|
47
|
+
|
|
48
|
+
post("/api/chat", payload)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def post(path, payload)
|
|
54
|
+
uri = URI("#{@ollama_host}#{path}")
|
|
55
|
+
|
|
56
|
+
request = Net::HTTP::Post.new(
|
|
57
|
+
uri,
|
|
58
|
+
{ "Content-Type" => "application/json" }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
request.body = payload.to_json
|
|
62
|
+
|
|
63
|
+
response = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
|
64
|
+
http.request(request)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
68
|
+
raise "Ollama request failed: #{response.code} #{response.body}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
JSON.parse(response.body)
|
|
72
|
+
end
|
|
35
73
|
end
|
|
36
74
|
end
|
|
37
75
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ollama
|
|
4
|
+
module Dsl
|
|
5
|
+
class Tool
|
|
6
|
+
attr_reader :name, :description, :parameters
|
|
7
|
+
|
|
8
|
+
def initialize(name, description, parameters:, &block)
|
|
9
|
+
@name = name.to_s
|
|
10
|
+
@description = description
|
|
11
|
+
@parameters = parameters
|
|
12
|
+
@block = block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def definition
|
|
16
|
+
{
|
|
17
|
+
type: "function",
|
|
18
|
+
function: {
|
|
19
|
+
name: @name,
|
|
20
|
+
description: @description,
|
|
21
|
+
parameters: @parameters
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call(arguments = {})
|
|
27
|
+
@block.call(**arguments.transform_keys(&:to_sym))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/ollama/dsl/version.rb
CHANGED
data/lib/ollama/dsl.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ollama-dsl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rafael
|
|
@@ -23,11 +23,14 @@ files:
|
|
|
23
23
|
- LICENSE.txt
|
|
24
24
|
- README.md
|
|
25
25
|
- Rakefile
|
|
26
|
+
- examples/agent.rb
|
|
26
27
|
- examples/use_case.rb
|
|
27
28
|
- lib/ollama/dsl.rb
|
|
29
|
+
- lib/ollama/dsl/agent.rb
|
|
28
30
|
- lib/ollama/dsl/client.rb
|
|
29
31
|
- lib/ollama/dsl/dsl.rb
|
|
30
32
|
- lib/ollama/dsl/session.rb
|
|
33
|
+
- lib/ollama/dsl/tool.rb
|
|
31
34
|
- lib/ollama/dsl/version.rb
|
|
32
35
|
- sig/ollama/dsl.rbs
|
|
33
36
|
homepage: https://github.com/rafaelmerlotto/ollama-dsl
|