envoy_ai 0.0.1

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +22 -0
  3. data/README.md +399 -0
  4. data/app/assets/stylesheets/envoy/console.css +371 -0
  5. data/app/controllers/envoy/application_controller.rb +18 -0
  6. data/app/controllers/envoy/conversations_controller.rb +36 -0
  7. data/app/controllers/envoy/messages_controller.rb +14 -0
  8. data/app/controllers/envoy/system_prompts_controller.rb +52 -0
  9. data/app/javascript/envoy/controllers/autoscroll_controller.js +46 -0
  10. data/app/javascript/envoy/controllers/composer_controller.js +30 -0
  11. data/app/jobs/envoy/application_job.rb +4 -0
  12. data/app/jobs/envoy/run_job.rb +65 -0
  13. data/app/models/envoy/application_record.rb +5 -0
  14. data/app/models/envoy/conversation.rb +20 -0
  15. data/app/models/envoy/message.rb +8 -0
  16. data/app/models/envoy/system_prompt.rb +24 -0
  17. data/app/models/envoy/system_prompt_version.rb +14 -0
  18. data/app/models/envoy/tool_call.rb +28 -0
  19. data/app/views/envoy/conversations/_composer_input.html.erb +4 -0
  20. data/app/views/envoy/conversations/index.html.erb +15 -0
  21. data/app/views/envoy/conversations/new.html.erb +19 -0
  22. data/app/views/envoy/conversations/show.html.erb +18 -0
  23. data/app/views/envoy/messages/_message.html.erb +7 -0
  24. data/app/views/envoy/messages/_streaming.html.erb +6 -0
  25. data/app/views/envoy/messages/_tool_call.html.erb +8 -0
  26. data/app/views/envoy/messages/create.turbo_stream.erb +25 -0
  27. data/app/views/envoy/system_prompts/_form.html.erb +14 -0
  28. data/app/views/envoy/system_prompts/edit.html.erb +2 -0
  29. data/app/views/envoy/system_prompts/index.html.erb +14 -0
  30. data/app/views/envoy/system_prompts/new.html.erb +2 -0
  31. data/app/views/envoy/system_prompts/show.html.erb +15 -0
  32. data/app/views/layouts/envoy/application.html.erb +20 -0
  33. data/config/routes.rb +7 -0
  34. data/db/migrate/20260710000001_create_envoy_conversations.rb +14 -0
  35. data/db/migrate/20260710000002_create_envoy_messages.rb +14 -0
  36. data/db/migrate/20260710000003_create_envoy_tool_calls.rb +14 -0
  37. data/db/migrate/20260711000001_create_envoy_system_prompts.rb +10 -0
  38. data/db/migrate/20260711000002_create_envoy_system_prompt_versions.rb +13 -0
  39. data/db/migrate/20260711000003_add_system_prompt_version_to_envoy_conversations.rb +6 -0
  40. data/lib/envoy/badges.rb +22 -0
  41. data/lib/envoy/compiled_tool.rb +43 -0
  42. data/lib/envoy/configuration.rb +21 -0
  43. data/lib/envoy/engine.rb +19 -0
  44. data/lib/envoy/errors.rb +6 -0
  45. data/lib/envoy/guard.rb +28 -0
  46. data/lib/envoy/llm.rb +24 -0
  47. data/lib/envoy/markdown.rb +16 -0
  48. data/lib/envoy/runner.rb +44 -0
  49. data/lib/envoy/testing/fake_llm.rb +41 -0
  50. data/lib/envoy/tool_definition.rb +38 -0
  51. data/lib/envoy/toolset.rb +72 -0
  52. data/lib/envoy/version.rb +3 -0
  53. data/lib/envoy.rb +45 -0
  54. data/lib/envoy_ai.rb +3 -0
  55. metadata +138 -0
@@ -0,0 +1,38 @@
1
+ module Envoy
2
+ class ToolDefinition
3
+ attr_reader :name, :params, :perform_block
4
+ attr_accessor :tool_class
5
+
6
+ def initialize(name)
7
+ @name = name.to_s
8
+ @description = ""
9
+ @access = :read
10
+ @params = []
11
+ @perform_block = ->(**) { }
12
+ end
13
+
14
+ # --- DSL ---
15
+ def description(text = nil)
16
+ return @description if text.nil?
17
+ @description = text
18
+ end
19
+
20
+ def access(level = nil)
21
+ return @access if level.nil?
22
+ raise Envoy::Error, "access must be :read or :write" unless %i[read write].include?(level)
23
+ @access = level
24
+ end
25
+
26
+ def param(name, desc, type: :string, required: true)
27
+ @params << { name: name.to_sym, desc: desc, type: type, required: required }
28
+ end
29
+
30
+ def perform(&block)
31
+ @perform_block = block
32
+ end
33
+
34
+ # --- introspection ---
35
+ def read? = @access == :read
36
+ def write? = @access == :write
37
+ end
38
+ end
@@ -0,0 +1,72 @@
1
+ module Envoy
2
+ class Toolset
3
+ attr_reader :key, :tools
4
+
5
+ def initialize(key)
6
+ @key = key.to_s
7
+ @description = ""
8
+ @tools = {}
9
+ @composed_keys = []
10
+ end
11
+
12
+ # Reader returns this toolset's own description followed by each composed
13
+ # toolset's (so the model understands every tool group); setter records it.
14
+ def description(text = nil)
15
+ return @description = text unless text.nil?
16
+ collect_descriptions.compact_blank.join("\n\n")
17
+ end
18
+
19
+ def tool(name, &block)
20
+ definition = ToolDefinition.new(name)
21
+ definition.instance_eval(&block)
22
+ @tools[definition.name] = definition
23
+ end
24
+
25
+ # Compose other toolsets into this one. Keys are resolved lazily (at
26
+ # tools_for / description time via Envoy.toolset), so the load order of
27
+ # toolset definitions does not matter.
28
+ def use(*keys)
29
+ @composed_keys.concat(keys.map(&:to_s))
30
+ end
31
+
32
+ # This toolset's own tools plus every composed toolset's, de-duplicated by
33
+ # name (own tools win, then composed in `use` order). read_only strips write
34
+ # tools across the merged set (governance lever for embeds).
35
+ def tools_for(read_only: false)
36
+ list = collect_tools.values
37
+ read_only ? list.select(&:read?) : list
38
+ end
39
+
40
+ protected
41
+
42
+ # seen threads the ancestry so a toolset that composes itself (directly or
43
+ # transitively) raises instead of looping forever.
44
+ def collect_tools(seen = [])
45
+ guard_cycle!(seen)
46
+ merged = {}
47
+ @tools.each { |name, definition| merged[name] = definition } # own first, own wins
48
+ @composed_keys.each do |composed_key|
49
+ Envoy.toolset(composed_key).collect_tools(seen + [ key ]).each do |name, definition|
50
+ merged[name] ||= definition # earlier composed wins over later
51
+ end
52
+ end
53
+ merged
54
+ end
55
+
56
+ def collect_descriptions(seen = [])
57
+ guard_cycle!(seen)
58
+ descriptions = [ @description ]
59
+ @composed_keys.each do |composed_key|
60
+ descriptions.concat(Envoy.toolset(composed_key).collect_descriptions(seen + [ key ]))
61
+ end
62
+ descriptions
63
+ end
64
+
65
+ private
66
+
67
+ def guard_cycle!(seen)
68
+ return unless seen.include?(key)
69
+ raise Envoy::ToolsetCycle, "toolset composition cycle: #{(seen + [ key ]).join(' -> ')}"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Envoy
2
+ VERSION = "0.0.1"
3
+ end
data/lib/envoy.rb ADDED
@@ -0,0 +1,45 @@
1
+ require "ruby_llm"
2
+ require "commonmarker"
3
+ require "envoy/version"
4
+ require "envoy/configuration"
5
+ require "envoy/markdown"
6
+ require "envoy/badges"
7
+ require "envoy/engine"
8
+ require "envoy/errors"
9
+ require "envoy/tool_definition"
10
+ require "envoy/toolset"
11
+ require "envoy/guard"
12
+ require "envoy/compiled_tool"
13
+ require "envoy/llm"
14
+ require "envoy/runner"
15
+
16
+ module Envoy
17
+ class << self
18
+ def config
19
+ @config ||= Configuration.new
20
+ end
21
+
22
+ def configure
23
+ yield config
24
+ end
25
+
26
+ # Build an LLM runner for a turn.
27
+ def llm(**opts)
28
+ config.llm.call(**opts)
29
+ end
30
+
31
+ def toolsets
32
+ @toolsets ||= {}
33
+ end
34
+
35
+ def define_toolset(key, &block)
36
+ toolset = Toolset.new(key)
37
+ toolset.instance_eval(&block)
38
+ toolsets[toolset.key] = toolset
39
+ end
40
+
41
+ def toolset(key)
42
+ toolsets.fetch(key.to_s) { raise UnknownToolset, "no toolset #{key.inspect}" }
43
+ end
44
+ end
45
+ end
data/lib/envoy_ai.rb ADDED
@@ -0,0 +1,3 @@
1
+ # The gem is published as `envoy_ai` (the `envoy` name was taken), so
2
+ # `require "envoy_ai"` — Bundler's default for the gem name — must work.
3
+ require "envoy"
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: envoy_ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Travis Petticrew
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '8.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby_llm
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.16'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.16'
40
+ - !ruby/object:Gem::Dependency
41
+ name: commonmarker
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.6'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.6'
54
+ description: 'Envoy is a mountable Rails engine for governed, tool-calling agent chat:
55
+ a DSL for host-owned toolsets, guard hooks for authorizing every tool call, persisted
56
+ conversations, and a debug console.'
57
+ email:
58
+ - travis@launchsupply.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - MIT-LICENSE
64
+ - README.md
65
+ - app/assets/stylesheets/envoy/console.css
66
+ - app/controllers/envoy/application_controller.rb
67
+ - app/controllers/envoy/conversations_controller.rb
68
+ - app/controllers/envoy/messages_controller.rb
69
+ - app/controllers/envoy/system_prompts_controller.rb
70
+ - app/javascript/envoy/controllers/autoscroll_controller.js
71
+ - app/javascript/envoy/controllers/composer_controller.js
72
+ - app/jobs/envoy/application_job.rb
73
+ - app/jobs/envoy/run_job.rb
74
+ - app/models/envoy/application_record.rb
75
+ - app/models/envoy/conversation.rb
76
+ - app/models/envoy/message.rb
77
+ - app/models/envoy/system_prompt.rb
78
+ - app/models/envoy/system_prompt_version.rb
79
+ - app/models/envoy/tool_call.rb
80
+ - app/views/envoy/conversations/_composer_input.html.erb
81
+ - app/views/envoy/conversations/index.html.erb
82
+ - app/views/envoy/conversations/new.html.erb
83
+ - app/views/envoy/conversations/show.html.erb
84
+ - app/views/envoy/messages/_message.html.erb
85
+ - app/views/envoy/messages/_streaming.html.erb
86
+ - app/views/envoy/messages/_tool_call.html.erb
87
+ - app/views/envoy/messages/create.turbo_stream.erb
88
+ - app/views/envoy/system_prompts/_form.html.erb
89
+ - app/views/envoy/system_prompts/edit.html.erb
90
+ - app/views/envoy/system_prompts/index.html.erb
91
+ - app/views/envoy/system_prompts/new.html.erb
92
+ - app/views/envoy/system_prompts/show.html.erb
93
+ - app/views/layouts/envoy/application.html.erb
94
+ - config/routes.rb
95
+ - db/migrate/20260710000001_create_envoy_conversations.rb
96
+ - db/migrate/20260710000002_create_envoy_messages.rb
97
+ - db/migrate/20260710000003_create_envoy_tool_calls.rb
98
+ - db/migrate/20260711000001_create_envoy_system_prompts.rb
99
+ - db/migrate/20260711000002_create_envoy_system_prompt_versions.rb
100
+ - db/migrate/20260711000003_add_system_prompt_version_to_envoy_conversations.rb
101
+ - lib/envoy.rb
102
+ - lib/envoy/badges.rb
103
+ - lib/envoy/compiled_tool.rb
104
+ - lib/envoy/configuration.rb
105
+ - lib/envoy/engine.rb
106
+ - lib/envoy/errors.rb
107
+ - lib/envoy/guard.rb
108
+ - lib/envoy/llm.rb
109
+ - lib/envoy/markdown.rb
110
+ - lib/envoy/runner.rb
111
+ - lib/envoy/testing/fake_llm.rb
112
+ - lib/envoy/tool_definition.rb
113
+ - lib/envoy/toolset.rb
114
+ - lib/envoy/version.rb
115
+ - lib/envoy_ai.rb
116
+ homepage: https://www.launchsupply.com/
117
+ licenses:
118
+ - MIT
119
+ metadata:
120
+ homepage_uri: https://www.launchsupply.com/
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '3.2'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubygems_version: 4.0.6
136
+ specification_version: 4
137
+ summary: Governed, tool-calling agent chat as a mountable Rails engine.
138
+ test_files: []