llm.rb 12.5.1 → 13.0.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/CHANGELOG.md +482 -0
- data/LICENSE +21 -93
- data/README.md +49 -159
- data/data/deepinfra.json +3 -0
- data/data/xai.json +1 -1
- data/lib/llm/a2a.rb +1 -1
- data/lib/llm/active_record/acts_as_agent.rb +32 -0
- data/lib/llm/active_record/acts_as_llm.rb +6 -6
- data/lib/llm/agent.rb +101 -26
- data/lib/llm/buffer.rb +85 -3
- data/lib/llm/compactor/null.rb +19 -0
- data/lib/llm/compactor/truncate.rb +80 -0
- data/lib/llm/compactor.rb +42 -124
- data/lib/llm/context.rb +33 -37
- data/lib/llm/contract.rb +4 -25
- data/lib/llm/function/array.rb +15 -14
- data/lib/llm/function/async/group.rb +54 -0
- data/lib/llm/function/async/reactor.rb +48 -0
- data/lib/llm/function/async/task.rb +83 -0
- data/lib/llm/function/fiber/group.rb +46 -0
- data/lib/llm/function/fiber/task.rb +62 -0
- data/lib/llm/function/{fork_group.rb → fork/group.rb} +14 -5
- data/lib/llm/function/fork/job.rb +4 -3
- data/lib/llm/function/fork/task.rb +20 -10
- data/lib/llm/function/group.rb +40 -0
- data/lib/llm/function/{ractor_group.rb → ractor/group.rb} +13 -5
- data/lib/llm/function/ractor/job.rb +19 -3
- data/lib/llm/function/ractor/mailbox.rb +9 -0
- data/lib/llm/function/ractor/task.rb +24 -15
- data/lib/llm/function/{call_group.rb → sequential/group.rb} +17 -8
- data/lib/llm/function/sequential/task.rb +49 -0
- data/lib/llm/function/task.rb +25 -37
- data/lib/llm/function/thread/group.rb +46 -0
- data/lib/llm/function/thread/task.rb +60 -0
- data/lib/llm/function/tracing.rb +2 -0
- data/lib/llm/function.rb +56 -64
- data/lib/llm/loop_guard.rb +1 -2
- data/lib/llm/mcp.rb +22 -0
- data/lib/llm/object.rb +2 -1
- data/lib/llm/provider.rb +6 -3
- data/lib/llm/providers/google.rb +2 -2
- data/lib/llm/repl/command.rb +35 -8
- data/lib/llm/repl/commands/compact.rb +33 -0
- data/lib/llm/repl/input.rb +80 -15
- data/lib/llm/repl/markdown/table.rb +76 -0
- data/lib/llm/repl/markdown.rb +31 -1
- data/lib/llm/repl/status.rb +1 -1
- data/lib/llm/repl/stream.rb +10 -3
- data/lib/llm/repl/transcript.rb +1 -1
- data/lib/llm/repl/walker.rb +46 -0
- data/lib/llm/repl.rb +18 -12
- data/lib/llm/response.rb +10 -0
- data/lib/llm/schema/leaf.rb +5 -0
- data/lib/llm/schema/object.rb +11 -5
- data/lib/llm/sequel/agent.rb +32 -0
- data/lib/llm/sequel/plugin.rb +6 -6
- data/lib/llm/stream.rb +24 -17
- data/lib/llm/tool/param.rb +12 -0
- data/lib/llm/tool.rb +20 -4
- data/lib/llm/tools/chdir.rb +0 -2
- data/lib/llm/tools/git.rb +8 -4
- data/lib/llm/tools/mkdir.rb +1 -1
- data/lib/llm/tools/pwd.rb +0 -2
- data/lib/llm/tools/read_file.rb +0 -2
- data/lib/llm/tools/rg.rb +8 -4
- data/lib/llm/tools/shell.rb +8 -4
- data/lib/llm/tools/utils.rb +31 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +25 -5
- data/llm.gemspec +3 -3
- data/resources/deepdive.md +693 -57
- metadata +24 -13
- data/lib/llm/function/call_task.rb +0 -46
- data/lib/llm/function/fiber_group.rb +0 -105
- data/lib/llm/function/task_group.rb +0 -97
- data/lib/llm/function/thread_group.rb +0 -102
data/lib/llm/stream.rb
CHANGED
|
@@ -11,6 +11,21 @@ module LLM
|
|
|
11
11
|
# a small helper for collecting asynchronous tool work started from a
|
|
12
12
|
# callback.
|
|
13
13
|
#
|
|
14
|
+
# @example Subclass with callbacks
|
|
15
|
+
# class MyStream < LLM::Stream
|
|
16
|
+
# def on_content(content)
|
|
17
|
+
# print content
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# def on_reasoning_content(content)
|
|
21
|
+
# warn content
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# llm = LLM.deepseek(key: ENV["KEY"])
|
|
26
|
+
# agent = LLM::Agent.new(llm, stream: MyStream.new)
|
|
27
|
+
# agent.talk "Explain Ruby fibers."
|
|
28
|
+
#
|
|
14
29
|
# @note The `on_*` callbacks run inline with the streaming parser. They
|
|
15
30
|
# therefore block streaming progress and should generally return as
|
|
16
31
|
# quickly as possible.
|
|
@@ -19,6 +34,9 @@ module LLM
|
|
|
19
34
|
# Providers may also call {#on_reasoning_content} and {#on_tool_call} when
|
|
20
35
|
# that data is available. Runtime features such as context compaction may
|
|
21
36
|
# also emit lifecycle callbacks like {#on_transform} or {#on_compaction}.
|
|
37
|
+
#
|
|
38
|
+
# @see LLM::Agent Where streams are typically attached
|
|
39
|
+
# @see LLM::Context Where streams are bound per-turn
|
|
22
40
|
class Stream
|
|
23
41
|
require_relative "stream/queue"
|
|
24
42
|
require_relative "stream/io"
|
|
@@ -109,16 +127,8 @@ module LLM
|
|
|
109
127
|
|
|
110
128
|
##
|
|
111
129
|
# Called when a streamed tool call has been fully constructed.
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
# `ctx.spawn(tool, :fiber)`, or `ctx.spawn(tool, :task)` onto {#queue}.
|
|
115
|
-
# Mixed strategies can also be selected per tool, such as
|
|
116
|
-
# `tool.mcp? ? ctx.spawn(tool, :task) : ctx.spawn(tool, :ractor)`.
|
|
117
|
-
# Streamed tool resolution now prefers the current request tools, so
|
|
118
|
-
# {LLM.function}, MCP tools, bound tool instances, and normal
|
|
119
|
-
# {LLM::Tool LLM::Tool} classes can all resolve through the same
|
|
120
|
-
# request-local path. The current `:ractor` mode is for class-based
|
|
121
|
-
# tools and does not support MCP tools.
|
|
130
|
+
# A stream implementation may start tool execution here by pushing
|
|
131
|
+
# `@queue << tool.task(:thread)` onto {#queue}.
|
|
122
132
|
# @param [LLM::Function] tool
|
|
123
133
|
# The parsed tool call.
|
|
124
134
|
# @return [nil]
|
|
@@ -128,9 +138,8 @@ module LLM
|
|
|
128
138
|
|
|
129
139
|
##
|
|
130
140
|
# Called when queued streamed tool work returns.
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
# `ctx.spawn(tool, :fiber)`, or `ctx.spawn(tool, :task)`.
|
|
141
|
+
# This callback runs when {#wait} resolves work queued from
|
|
142
|
+
# {#on_tool_call}.
|
|
134
143
|
# @param [LLM::Function] tool
|
|
135
144
|
# The tool that returned.
|
|
136
145
|
# @param [LLM::Function::Return] result
|
|
@@ -160,19 +169,17 @@ module LLM
|
|
|
160
169
|
|
|
161
170
|
##
|
|
162
171
|
# Called before a context compaction starts.
|
|
163
|
-
# @param [LLM::Context] ctx
|
|
164
172
|
# @param [LLM::Compactor] compactor
|
|
165
173
|
# @return [nil]
|
|
166
|
-
def on_compaction(
|
|
174
|
+
def on_compaction(compactor)
|
|
167
175
|
nil
|
|
168
176
|
end
|
|
169
177
|
|
|
170
178
|
##
|
|
171
179
|
# Called after a context compaction finishes.
|
|
172
|
-
# @param [LLM::Context] ctx
|
|
173
180
|
# @param [LLM::Compactor] compactor
|
|
174
181
|
# @return [nil]
|
|
175
|
-
def on_compaction_finish(
|
|
182
|
+
def on_compaction_finish(compactor)
|
|
176
183
|
nil
|
|
177
184
|
end
|
|
178
185
|
|
data/lib/llm/tool/param.rb
CHANGED
|
@@ -56,6 +56,18 @@ class LLM::Tool
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
##
|
|
60
|
+
# Set default values for parameters.
|
|
61
|
+
# @param [Hash] defaults
|
|
62
|
+
# @return [LLM::Schema::Object]
|
|
63
|
+
def defaults(defaults)
|
|
64
|
+
lock do
|
|
65
|
+
function.params.tap do |schema|
|
|
66
|
+
defaults.each { Utils.fetch(schema.properties, _1).default(_2) }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
59
71
|
##
|
|
60
72
|
# @api private
|
|
61
73
|
module Utils
|
data/lib/llm/tool.rb
CHANGED
|
@@ -5,10 +5,23 @@
|
|
|
5
5
|
# that can be called by an LLM. Under the hood, it is a wrapper
|
|
6
6
|
# around {LLM::Function LLM::Function} but allows the definition
|
|
7
7
|
# of a function (also known as a tool) as a class.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
8
|
+
#
|
|
9
|
+
# @example Declarative form (preferred)
|
|
10
|
+
# class ReadFile < LLM::Tool
|
|
11
|
+
# name "read-file"
|
|
12
|
+
# description "Read a file from disk"
|
|
13
|
+
# parameter :path, String, "The filename or path"
|
|
14
|
+
# required %i[path]
|
|
15
|
+
#
|
|
16
|
+
# def call(path:)
|
|
17
|
+
# {contents: File.read(path)}
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Block-form DSL (also supported)
|
|
22
|
+
# class RunCommand < LLM::Tool
|
|
23
|
+
# name "run-command"
|
|
24
|
+
# description "Runs a shell command"
|
|
12
25
|
# params do |schema|
|
|
13
26
|
# schema.object(command: schema.string.required)
|
|
14
27
|
# end
|
|
@@ -17,6 +30,9 @@
|
|
|
17
30
|
# {success: Kernel.system(command)}
|
|
18
31
|
# end
|
|
19
32
|
# end
|
|
33
|
+
#
|
|
34
|
+
# @see LLM::Agent Tools are attached to agents
|
|
35
|
+
# @see LLM::Function The function object that Tool wraps
|
|
20
36
|
class LLM::Tool
|
|
21
37
|
require_relative "tool/param"
|
|
22
38
|
extend LLM::Tool::Param
|
data/lib/llm/tools/chdir.rb
CHANGED
data/lib/llm/tools/git.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# frozen_string_literal
|
|
4
|
-
|
|
5
3
|
class LLM::Tool
|
|
6
4
|
##
|
|
7
5
|
# The {LLM::Tool::Git LLM::Tool::Git} class implements
|
|
@@ -9,18 +7,24 @@ class LLM::Tool
|
|
|
9
7
|
# The actions it can perform are read-only - at least for
|
|
10
8
|
# the time being.
|
|
11
9
|
class Git < self
|
|
10
|
+
require_relative "utils"
|
|
11
|
+
include Utils
|
|
12
|
+
|
|
12
13
|
name "git"
|
|
13
14
|
description "perform an action with git"
|
|
14
15
|
parameter :action, Enum["log", "diff", "commit", "checkout", "branch", "show"], "the git operation to perform"
|
|
15
16
|
parameter :arguments, Array[String], "one or more arguments for the git action"
|
|
17
|
+
parameter :timeout, Integer, "the maximum time to allow the command to run"
|
|
16
18
|
required %i[action]
|
|
19
|
+
defaults arguments: [], timeout: 5
|
|
17
20
|
|
|
18
21
|
##
|
|
19
22
|
# @param [String] action
|
|
20
23
|
# @param [Array<String>, nil] arguments
|
|
21
24
|
# @return [Hash]
|
|
22
|
-
def call(action:, arguments:
|
|
25
|
+
def call(action:, arguments: [], timeout: 5)
|
|
23
26
|
command = spawn(action:, arguments:)
|
|
27
|
+
wait(command:, timeout:)
|
|
24
28
|
{ok: command.success?, stdout: command.stdout, stderr: command.stderr}
|
|
25
29
|
end
|
|
26
30
|
|
|
@@ -34,7 +38,7 @@ class LLM::Tool
|
|
|
34
38
|
.spawn
|
|
35
39
|
end
|
|
36
40
|
|
|
37
|
-
LLM.require "test-cmd.rb"
|
|
41
|
+
LLM.require "test-cmd.rb", "~> 1.1"
|
|
38
42
|
Command = Test::Cmd
|
|
39
43
|
end
|
|
40
44
|
end
|
data/lib/llm/tools/mkdir.rb
CHANGED
data/lib/llm/tools/pwd.rb
CHANGED
data/lib/llm/tools/read_file.rb
CHANGED
data/lib/llm/tools/rg.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# frozen_string_literal
|
|
4
|
-
|
|
5
3
|
class LLM::Tool
|
|
6
4
|
##
|
|
7
5
|
# The {LLM::Tool::Rg LLM::Tool::Rg} class implements
|
|
@@ -9,19 +7,25 @@ class LLM::Tool
|
|
|
9
7
|
# recursively search the current working directory
|
|
10
8
|
# for one or more patterns.
|
|
11
9
|
class Rg < self
|
|
10
|
+
require_relative "utils"
|
|
11
|
+
include Utils
|
|
12
|
+
|
|
12
13
|
name "rg"
|
|
13
14
|
description "recursively search the current directory for lines matching a pattern"
|
|
14
15
|
parameter :patterns, Array[String], "one or more search patterns"
|
|
15
16
|
parameter :path, String, "the path where the search is performed (default is cwd)"
|
|
17
|
+
parameter :timeout, Integer, "the number of seconds to wait before cancelling the action"
|
|
16
18
|
required %i[patterns]
|
|
19
|
+
defaults path: Dir.getwd, timeout: 5
|
|
17
20
|
|
|
18
21
|
##
|
|
19
22
|
# @param [Array<String>] patterns
|
|
20
23
|
# @param [String] path
|
|
21
24
|
# @return [Hash]
|
|
22
|
-
def call(patterns:, path: Dir.getwd)
|
|
25
|
+
def call(patterns:, path: Dir.getwd, timeout: 5)
|
|
23
26
|
validate!(patterns:, path:)
|
|
24
27
|
command = spawn(patterns:, path:)
|
|
28
|
+
wait(command:, timeout:)
|
|
25
29
|
{ok: command.success?, stdout: command.stdout, stderr: command.stderr}
|
|
26
30
|
end
|
|
27
31
|
|
|
@@ -41,7 +45,7 @@ class LLM::Tool
|
|
|
41
45
|
.spawn
|
|
42
46
|
end
|
|
43
47
|
|
|
44
|
-
LLM.require "test-cmd.rb"
|
|
48
|
+
LLM.require "test-cmd.rb", "~> 1.1"
|
|
45
49
|
Command = Test::Cmd
|
|
46
50
|
end
|
|
47
51
|
end
|
data/lib/llm/tools/shell.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# frozen_string_literal
|
|
4
|
-
|
|
5
3
|
class LLM::Tool
|
|
6
4
|
##
|
|
7
5
|
# The {LLM::Tool::Shell} class implements a tool that can
|
|
@@ -12,11 +10,16 @@ class LLM::Tool
|
|
|
12
10
|
# by managing the tool loop manually through
|
|
13
11
|
# {LLM::Context LLM::Context}.
|
|
14
12
|
class Shell < self
|
|
13
|
+
require_relative "utils"
|
|
14
|
+
include Utils
|
|
15
|
+
|
|
15
16
|
name "shell"
|
|
16
17
|
description "run a shell command"
|
|
17
18
|
parameter :name, String, "the command name"
|
|
18
19
|
parameter :arguments, Array[String], "one or more command arguments"
|
|
20
|
+
parameter :timeout, Integer, "the maximum allowed time for the command to run (in seconds)"
|
|
19
21
|
required %i[name]
|
|
22
|
+
defaults arguments: [], timeout: 60
|
|
20
23
|
|
|
21
24
|
##
|
|
22
25
|
# @param [String] name
|
|
@@ -24,8 +27,9 @@ class LLM::Tool
|
|
|
24
27
|
# @param [Array<String>] arguments
|
|
25
28
|
# One or more command-line arguments
|
|
26
29
|
# @return [Hash]
|
|
27
|
-
def call(name:, arguments:
|
|
30
|
+
def call(name:, arguments: [], timeout: 60)
|
|
28
31
|
command = spawn(name:, arguments:)
|
|
32
|
+
wait(command:, timeout:)
|
|
29
33
|
{ok: command.success?, stdout: command.stdout, stderr: command.stderr}
|
|
30
34
|
end
|
|
31
35
|
|
|
@@ -42,7 +46,7 @@ class LLM::Tool
|
|
|
42
46
|
.spawn
|
|
43
47
|
end
|
|
44
48
|
|
|
45
|
-
LLM.require "test-cmd.rb"
|
|
49
|
+
LLM.require "test-cmd.rb", "~> 1.1"
|
|
46
50
|
Command = Test::Cmd
|
|
47
51
|
end
|
|
48
52
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LLM::Tool
|
|
4
|
+
##
|
|
5
|
+
# Shared utilities for tool implementations.
|
|
6
|
+
module Utils
|
|
7
|
+
##
|
|
8
|
+
# Wait for a command to finish, or abort
|
|
9
|
+
# with an error when it exceeds the
|
|
10
|
+
# specified timeout.
|
|
11
|
+
# @param [Test::Cmd] command
|
|
12
|
+
# @param [Integer] timeout
|
|
13
|
+
# @return [void]
|
|
14
|
+
def wait(command:, timeout:)
|
|
15
|
+
start = now
|
|
16
|
+
while command.running?
|
|
17
|
+
if now - start > timeout
|
|
18
|
+
command.kill!
|
|
19
|
+
raise "command timed out after #{timeout}s"
|
|
20
|
+
end
|
|
21
|
+
sleep 0.01
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# @return [Numeric]
|
|
27
|
+
def now
|
|
28
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/llm/version.rb
CHANGED
data/lib/llm.rb
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
##
|
|
4
|
+
# llm.rb is a zero-dependency AI runtime for Ruby. Twelve providers, six
|
|
5
|
+
# concurrency strategies, MCP and A2A, streaming tool calls with
|
|
6
|
+
# cancellation, context compaction, and ORM persistence.
|
|
7
|
+
#
|
|
8
|
+
# @example The three-step workflow
|
|
9
|
+
# require "llm"
|
|
10
|
+
# llm = LLM.deepseek(key: ENV["KEY"]) # 1. pick a provider
|
|
11
|
+
# agent = LLM::Agent.new(llm, stream: $stdout) # 2. create an agent
|
|
12
|
+
# agent.talk "Hello world" # 3. talk to it
|
|
13
|
+
#
|
|
14
|
+
# @see LLM::Agent The recommended high-level interface
|
|
15
|
+
# @see LLM::Context The low-level stateful runtime (advanced)
|
|
3
16
|
module LLM
|
|
4
17
|
require "stringio"
|
|
5
18
|
require "securerandom"
|
|
19
|
+
require_relative "llm/compactor"
|
|
6
20
|
require_relative "llm/json_adapter"
|
|
7
21
|
require_relative "llm/tracer"
|
|
8
22
|
require_relative "llm/error"
|
|
@@ -49,15 +63,21 @@ module LLM
|
|
|
49
63
|
|
|
50
64
|
##
|
|
51
65
|
# Requires an optional runtime dependency
|
|
52
|
-
# @
|
|
66
|
+
# @param [String] name
|
|
67
|
+
# The name of a gem
|
|
68
|
+
# @param [String, nil] version
|
|
69
|
+
# Optional gem version
|
|
70
|
+
# @raise [LLM::LoadError]
|
|
53
71
|
# When the dependency cannot be loaded
|
|
54
|
-
def self.require(name)
|
|
55
|
-
super
|
|
56
|
-
rescue ::LoadError
|
|
72
|
+
def self.require(name, version = nil)
|
|
57
73
|
names = {"xchan" => "xchan.rb", "net/http/persistent" => "net-http-persistent"}
|
|
74
|
+
gem(names[name] || name, version) if version
|
|
75
|
+
super(name)
|
|
76
|
+
rescue ::LoadError
|
|
58
77
|
name = names[name] || name
|
|
59
78
|
raise LLM::LoadError,
|
|
60
|
-
"#{name}
|
|
79
|
+
"#{name}#{version ? " #{version}" : ""} is an optional " \
|
|
80
|
+
"runtime dependency but it does not appear to be installed. " \
|
|
61
81
|
"Consider 'gem install #{name}', adding '#{name}' to your Gemfile or " \
|
|
62
82
|
"opting out of the functionality provided by '#{name}'"
|
|
63
83
|
end
|
data/llm.gemspec
CHANGED
|
@@ -16,7 +16,7 @@ functionality – such as ActiveRecord support – require
|
|
|
16
16
|
optional dependencies that are opt-in.
|
|
17
17
|
DESCRIPTION
|
|
18
18
|
|
|
19
|
-
spec.license = "
|
|
19
|
+
spec.license = "MIT"
|
|
20
20
|
spec.required_ruby_version = ">= 3.3.0"
|
|
21
21
|
|
|
22
22
|
spec.homepage = "https://r.uby.dev/llm/"
|
|
@@ -38,7 +38,7 @@ DESCRIPTION
|
|
|
38
38
|
spec.add_development_dependency "yard", "~> 0.9.37"
|
|
39
39
|
spec.add_development_dependency "redcarpet", "~> 3.6"
|
|
40
40
|
spec.add_development_dependency "webrick", "~> 1.8"
|
|
41
|
-
spec.add_development_dependency "test-cmd.rb", "~>
|
|
41
|
+
spec.add_development_dependency "test-cmd.rb", "~> 1.1.0"
|
|
42
42
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
43
43
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
44
44
|
spec.add_development_dependency "standard", "~> 1.50"
|
|
@@ -50,7 +50,7 @@ DESCRIPTION
|
|
|
50
50
|
spec.add_development_dependency "activerecord", "~> 8.0"
|
|
51
51
|
spec.add_development_dependency "sequel", "~> 5.0"
|
|
52
52
|
spec.add_development_dependency "sqlite3", "~> 2.0"
|
|
53
|
-
spec.add_development_dependency "xchan.rb", "~> 0.
|
|
53
|
+
spec.add_development_dependency "xchan.rb", "~> 0.22"
|
|
54
54
|
spec.add_development_dependency "pg", "~> 1.5"
|
|
55
55
|
spec.add_development_dependency "irb", "~> 1.18"
|
|
56
56
|
end
|