elelem 0.11.0 → 0.13.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Elelem
4
4
  class Toolbox
5
- attr_reader :tools, :hooks, :aliases
5
+ attr_reader :tools
6
6
 
7
7
  def initialize
8
8
  @tools = {}
@@ -11,16 +11,12 @@ module Elelem
11
11
  end
12
12
 
13
13
  def clear!
14
- @tools.clear
15
- @aliases.clear
16
- @hooks[:before].clear
17
- @hooks[:after].clear
14
+ [@tools, @aliases, @hooks[:before], @hooks[:after]].each(&:clear)
18
15
  end
19
16
 
20
- def add(name, description:, params: {}, required: [], aliases: [], &fn)
21
- tool = Tool.new(name, description: description, params: params, required: required, aliases: aliases, &fn)
22
- @tools[name] = tool
23
- tool.aliases.each { |a| @aliases[a] = name }
17
+ def add(name, description:, params: {}, required: [], aliases: [], &handler)
18
+ aliases.each { |a| @aliases[a] = name }
19
+ @tools[name] = Tool.new(name, description:, params:, required:, aliases:, &handler)
24
20
  end
25
21
 
26
22
  def before(tool_name = :*, &block)
@@ -31,19 +27,16 @@ module Elelem
31
27
  @hooks[:after][tool_name] << block
32
28
  end
33
29
 
30
+ # The tool by name, else by alias, so a real tool named like an alias wins.
34
31
  def tool_for(name)
35
- tools.fetch(@aliases.fetch(name, name)) { NullTool.new(name, available: tools.keys) }
32
+ tools[name] || tools[@aliases[name]]
36
33
  end
37
34
 
38
35
  def run(name, args)
39
36
  tool = tool_for(name)
40
- errors = tool.validate(args)
41
- return Result.failure(error: errors.join(", ")) if errors.any?
42
-
43
- result = dispatch(tool, args)
44
- result[:error] ? Result.failure(result) : Result.success(result)
45
- rescue => e
46
- Elelem.logger.warn("toolbox: #{e.message}\n#{e.backtrace.join("\n")}")
37
+ invalid(name, tool, args) || to_result(dispatch(tool, args))
38
+ rescue StandardError, ScriptError => e
39
+ Elelem.logger.debug("toolbox: #{e.message}\n#{e.backtrace.join("\n")}")
47
40
  Result.failure(error: e.message, name: name, args: args)
48
41
  end
49
42
 
@@ -53,13 +46,25 @@ module Elelem
53
46
 
54
47
  private
55
48
 
49
+ def invalid(name, tool, args)
50
+ errors = tool ? tool.validate(args) : ["unknown tool: #{name.inspect}. Available: #{tools.keys.join(", ")}."]
51
+ Result.failure(error: errors.join(", ")) if errors.any?
52
+ end
53
+
54
+ # A tool fails by returning error:, or ok: false (as execute does for a
55
+ # non-zero exit).
56
+ def to_result(result)
57
+ result[:error] || result[:ok] == false ? Result.failure(result) : Result.success(result)
58
+ end
59
+
56
60
  def dispatch(tool, args)
57
- @hooks[:before][:*].each { |h| h.call(args, tool_name: tool.name) }
58
- @hooks[:before][tool.name].each { |h| h.call(args) }
59
- result = tool.call(args)
60
- @hooks[:after][:*].each { |h| h.call(args, result, tool_name: tool.name) }
61
- @hooks[:after][tool.name].each { |h| h.call(args, result) }
62
- result
61
+ run_hooks(:before, tool, args)
62
+ tool.call(args).tap { |result| run_hooks(:after, tool, args, result) }
63
+ end
64
+
65
+ def run_hooks(type, tool, *)
66
+ @hooks[type][:*].each { |h| h.call(*, tool_name: tool.name) }
67
+ @hooks[type][tool.name].each { |h| h.call(*) }
63
68
  end
64
69
  end
65
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Elelem
4
- VERSION = "0.11.0"
4
+ VERSION = "0.13.0"
5
5
  end
data/lib/elelem.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
+ require "fileutils"
4
5
  require "forwardable"
5
6
  require "json"
6
7
  require "json_schemer"
@@ -8,38 +9,47 @@ require "logger"
8
9
  require "open3"
9
10
  require "pathname"
10
11
  require "shellwords"
12
+ require "stringio"
13
+ require "tempfile"
11
14
 
12
- require_relative "elelem/agent"
13
- require_relative "elelem/slash_command"
14
- require_relative "elelem/commands"
15
- require_relative "elelem/conversation"
16
- require_relative "elelem/input"
17
- require_relative "elelem/null_input"
15
+ require_relative "elelem/version"
18
16
  require_relative "elelem/output"
19
17
  require_relative "elelem/null_output"
18
+ require_relative "elelem/input"
19
+ require_relative "elelem/null_input"
20
+ require_relative "elelem/builtins"
21
+ require_relative "elelem/builtins/terminal"
22
+ require_relative "elelem/system_prompt"
23
+ require_relative "elelem/conversation"
24
+ require_relative "elelem/result"
25
+ require_relative "elelem/tool"
26
+ require_relative "elelem/toolbox"
27
+ require_relative "elelem/commands"
28
+ require_relative "elelem/agent"
29
+ require_relative "elelem/session"
30
+ require_relative "elelem/repl"
31
+ require_relative "elelem/stub_provider"
20
32
  require_relative "elelem/plugins"
21
33
  require_relative "elelem/registration"
22
34
  require_relative "elelem/registry"
23
35
  require_relative "elelem/config"
24
- require_relative "elelem/result"
25
- require_relative "elelem/stub_provider"
26
- require_relative "elelem/system_prompt"
27
- require_relative "elelem/tool"
28
- require_relative "elelem/null_tool"
29
- require_relative "elelem/toolbox"
30
- require_relative "elelem/version"
31
36
 
32
37
  module Elelem
33
- def self.start(provider: "stub", toolbox: Toolbox.new)
34
- self.build(provider: provider, toolbox: toolbox).repl
38
+ def self.start(provider: "stub", toolbox: Toolbox.new, provider_options: {}, template: nil, system_prompt: nil)
39
+ Repl.new(build(provider: provider, toolbox: toolbox, provider_options: provider_options, template: template,
40
+ system_prompt: system_prompt)).run
35
41
  end
36
42
 
37
- def self.ask(prompt, provider: "stub", toolbox: Toolbox.new, output: NullOutput.new)
38
- self.build(provider: provider, toolbox: toolbox, input: NullInput.new, output: output).turn(prompt)
43
+ def self.ask(prompt, provider: "stub", toolbox: Toolbox.new, output: NullOutput.new, provider_options: {},
44
+ template: nil, system_prompt: nil)
45
+ build(provider: provider, toolbox: toolbox, input: NullInput.new, output: output,
46
+ provider_options: provider_options, template: template, system_prompt: system_prompt).turn(prompt)
39
47
  end
40
48
 
41
- def self.build(provider: "stub", toolbox: Toolbox.new, input: nil, output: nil)
42
- Config.build_agent(provider, toolbox: toolbox, input: input, output: output)
49
+ def self.build(provider: "stub", toolbox: Toolbox.new, input: nil, output: nil, provider_options: {},
50
+ template: nil, system_prompt: nil)
51
+ Config.build_session(provider, toolbox: toolbox, input: input, output: output,
52
+ provider_options: provider_options, template: template, system_prompt: system_prompt)
43
53
  end
44
54
 
45
55
  def self.logger
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elelem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '6.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: fileutils
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.8'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: forwardable
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -164,19 +178,33 @@ dependencies:
164
178
  - !ruby/object:Gem::Version
165
179
  version: '0.2'
166
180
  - !ruby/object:Gem::Dependency
167
- name: uri
181
+ name: stringio
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.1'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.1'
194
+ - !ruby/object:Gem::Dependency
195
+ name: tempfile
168
196
  requirement: !ruby/object:Gem::Requirement
169
197
  requirements:
170
198
  - - "~>"
171
199
  - !ruby/object:Gem::Version
172
- version: '1.0'
200
+ version: '0.3'
173
201
  type: :runtime
174
202
  prerelease: false
175
203
  version_requirements: !ruby/object:Gem::Requirement
176
204
  requirements:
177
205
  - - "~>"
178
206
  - !ruby/object:Gem::Version
179
- version: '1.0'
207
+ version: '0.3'
180
208
  description: A minimal coding harness.
181
209
  email:
182
210
  - mo@mokhan.ca
@@ -191,6 +219,8 @@ files:
191
219
  - exe/elelem
192
220
  - lib/elelem.rb
193
221
  - lib/elelem/agent.rb
222
+ - lib/elelem/builtins.rb
223
+ - lib/elelem/builtins/terminal.rb
194
224
  - lib/elelem/cli.rb
195
225
  - lib/elelem/commands.rb
196
226
  - lib/elelem/config.rb
@@ -198,26 +228,36 @@ files:
198
228
  - lib/elelem/input.rb
199
229
  - lib/elelem/null_input.rb
200
230
  - lib/elelem/null_output.rb
201
- - lib/elelem/null_tool.rb
202
231
  - lib/elelem/output.rb
203
232
  - lib/elelem/plugins.rb
233
+ - lib/elelem/plugins/builtins.rb
234
+ - lib/elelem/plugins/context.rb
235
+ - lib/elelem/plugins/execute.rb
236
+ - lib/elelem/plugins/provider.rb
237
+ - lib/elelem/plugins/read.rb
238
+ - lib/elelem/plugins/reload.rb
239
+ - lib/elelem/plugins/terminal.rb
240
+ - lib/elelem/plugins/tools.rb
241
+ - lib/elelem/plugins/write.rb
204
242
  - lib/elelem/prompts/default.erb
205
243
  - lib/elelem/registration.rb
206
244
  - lib/elelem/registry.rb
245
+ - lib/elelem/repl.rb
207
246
  - lib/elelem/result.rb
208
- - lib/elelem/slash_command.rb
247
+ - lib/elelem/session.rb
209
248
  - lib/elelem/stub_provider.rb
210
249
  - lib/elelem/system_prompt.rb
211
250
  - lib/elelem/tool.rb
212
251
  - lib/elelem/toolbox.rb
213
252
  - lib/elelem/version.rb
214
- homepage: https://src.mokhan.ca/elelem/elelem
253
+ homepage: https://elelem.sh
215
254
  licenses:
216
255
  - MIT
217
256
  metadata:
218
257
  allowed_push_host: https://rubygems.org
219
- homepage_uri: https://src.mokhan.ca/elelem/elelem
220
- source_code_uri: https://src.mokhan.ca/elelem/elelem
258
+ homepage_uri: https://elelem.sh
259
+ source_code_uri: https://github.com/elelem-sh/elelem
260
+ rubygems_mfa_required: 'true'
221
261
  rdoc_options: []
222
262
  require_paths:
223
263
  - lib
@@ -232,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
272
  - !ruby/object:Gem::Version
233
273
  version: 4.0.0
234
274
  requirements: []
235
- rubygems_version: 4.0.20
275
+ rubygems_version: 4.0.6
236
276
  specification_version: 4
237
277
  summary: A minimal coding harness for LLMs.
238
278
  test_files: []
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Elelem
4
- class NullTool
5
- def initialize(name, available: [])
6
- @name = name
7
- @available = available
8
- end
9
-
10
- def name
11
- "#{@name}?"
12
- end
13
-
14
- def validate(_args)
15
- ["unknown tool: #{@name.inspect}. Available: #{@available.join(", ")}."]
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Elelem
4
- class SlashCommand
5
- attr_reader :name, :description, :completions
6
-
7
- def initialize(name, description = "", completions = nil, &handler)
8
- @name = name
9
- @description = description
10
- @completions = completions
11
- @handler = handler
12
- end
13
-
14
- def call(args)
15
- @handler.call(args)
16
- end
17
- end
18
- end