clag 0.0.5 → 0.0.6

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/clag.gemspec +1 -0
  3. data/exe/clag +1 -3
  4. data/lib/clag/commands/generate.rb +1 -1
  5. data/lib/clag/generators/command_line_command_generator.rb +29 -0
  6. data/lib/clag/version.rb +1 -1
  7. data/lib/clag.rb +19 -1
  8. metadata +17 -23
  9. data/vendor/gems/sublayer/.gitignore +0 -16
  10. data/vendor/gems/sublayer/Gemfile +0 -6
  11. data/vendor/gems/sublayer/LICENSE +0 -21
  12. data/vendor/gems/sublayer/README.md +0 -52
  13. data/vendor/gems/sublayer/Rakefile +0 -8
  14. data/vendor/gems/sublayer/lib/sublayer/agents/generate_code_given_description_agent.rb +0 -35
  15. data/vendor/gems/sublayer/lib/sublayer/agents/generate_command_agent.rb +0 -37
  16. data/vendor/gems/sublayer/lib/sublayer/agents/json_fixing_agent.rb +0 -32
  17. data/vendor/gems/sublayer/lib/sublayer/agents/modify_file_contents_agent.rb +0 -38
  18. data/vendor/gems/sublayer/lib/sublayer/agents/save_file_contents_agent.rb +0 -18
  19. data/vendor/gems/sublayer/lib/sublayer/capabilities/human_assistance.rb +0 -23
  20. data/vendor/gems/sublayer/lib/sublayer/capabilities/llm_assistance.rb +0 -253
  21. data/vendor/gems/sublayer/lib/sublayer/components/output_function.rb +0 -22
  22. data/vendor/gems/sublayer/lib/sublayer/components/output_function_formats/list_of_objects.rb +0 -30
  23. data/vendor/gems/sublayer/lib/sublayer/components/output_function_formats/single_string.rb +0 -40
  24. data/vendor/gems/sublayer/lib/sublayer/enhancements/json.rb +0 -9
  25. data/vendor/gems/sublayer/lib/sublayer/version.rb +0 -5
  26. data/vendor/gems/sublayer/lib/sublayer.rb +0 -47
  27. data/vendor/gems/sublayer/spec/components/output_function_spec.rb +0 -73
  28. data/vendor/gems/sublayer/spec/spec_helper.rb +0 -15
  29. data/vendor/gems/sublayer/sublayer.gemspec +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7567d9817e4d7abc1c6da5ab57e36b99945fe64d3cb9d361d86c2c712e5e41af
4
- data.tar.gz: 5425fc8a75d9267aaf341e4b13d82f76dc7dea19549fd2686d84bc667653f5f8
3
+ metadata.gz: f27cc2c8372c73257a2455613a3f5db566301ce249dc5e0b8d6812a16795f1ea
4
+ data.tar.gz: 2886e0b99f0d5443c66bc19087a96584674927e320de67eea79115a9f734015d
5
5
  SHA512:
6
- metadata.gz: 8b629c28ad455cc20af1fe172c9dd774f88a980335c5f127fe7ebe6a624688f1ff004103261baedd252a7832beea01be378a2f1995e8c3c55cae7c1522355eb6
7
- data.tar.gz: e2327b5d9f0625b0ddf0d084f40372e677d2952becd2ba7f0271ccbc8e234a0dd1e35a0231d2223c31ebdca881409c4d46cad8d72cc05b7dd23e1b0aea76e202
6
+ metadata.gz: 1efa37ed3818cb1b5778e76c5eaccebbb732529cafe66da308964abf3be10047fec073b232e0134b41188b748090752b95d7d542b6d82bb6cd3bc2c841be29fe
7
+ data.tar.gz: 40619d05e7bd9fc2c3f17aa42f615914d2e2f8fb569304ebaf04f04bdfaa056a844a3dceb489d39769faa14c104de927319b0ecb3b28ae938f6bbfabea40374d
data/clag.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency 'activesupport'
26
26
  spec.add_dependency "pry"
27
27
  spec.add_dependency "nokogiri"
28
+ spec.add_dependency "sublayer"
28
29
 
29
30
  spec.add_development_dependency 'rake', '~> 10.0'
30
31
  end
data/exe/clag CHANGED
@@ -8,10 +8,8 @@ unshift_path = ->(path) {
8
8
  $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p)
9
9
  }
10
10
  unshift_path.call('lib')
11
- require 'bundler/setup'
12
-
13
- unshift_path.call('vendor/gems/sublayer/lib')
14
11
 
12
+ require 'bundler/setup'
15
13
  require 'clag'
16
14
 
17
15
  exit(Clag::ErrorHandler.call do
@@ -17,7 +17,7 @@ module Clag
17
17
  return
18
18
  end
19
19
 
20
- results = Sublayer::Agents::GenerateCommandLineCommandAgent.new(description: input).execute
20
+ results = Clag::CommandLineCommandGenerator.new(description: input).generate
21
21
 
22
22
  if results == 'unknown'
23
23
  puts CLI::UI.fmt("{{yellow:Unable to generate command. Please try again or provide more information.}}")
@@ -0,0 +1,29 @@
1
+ module Clag
2
+ class CommandLineCommandGenerator < Sublayer::Generators::Base
3
+ llm_output_adapter type: :single_string,
4
+ name: "command",
5
+ description: "The command line command for the user to run or 'unknown'"
6
+
7
+ def initialize(description:)
8
+ @description = description
9
+ end
10
+
11
+ def generate
12
+ super
13
+ end
14
+
15
+ def prompt
16
+ <<-PROMPT
17
+ You are an expert in command line operations.
18
+
19
+ You are tasked with finding or crafting a command line command to achieve the following:
20
+
21
+ #{@description}
22
+
23
+ Considering best practices, what should be run on the command line to achieve this.
24
+
25
+ If no command is possible, respond with 'unknown'
26
+ PROMPT
27
+ end
28
+ end
29
+ end
data/lib/clag/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Clag
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.6'
3
3
  end
data/lib/clag.rb CHANGED
@@ -11,7 +11,7 @@ module Clag
11
11
 
12
12
  autoload(:EntryPoint, 'clag/entry_point')
13
13
  autoload(:Commands, 'clag/commands')
14
-
14
+ load 'clag/generators/command_line_command_generator.rb'
15
15
 
16
16
  Config = CLI::Kit::Config.new(tool_name: TOOL_NAME)
17
17
  Command = CLI::Kit::BaseCommand
@@ -23,4 +23,22 @@ module Clag
23
23
  )
24
24
 
25
25
  ErrorHandler = CLI::Kit::ErrorHandler.new(log_file: LOG_FILE)
26
+
27
+ case ENV["CLAG_LLM"]
28
+ when "gemini"
29
+ Sublayer.configuration.ai_provider = Sublayer::Providers::Gemini
30
+ Sublayer.configuration.ai_model = "gemini-pro"
31
+ when "claude"
32
+ Sublayer.configuration.ai_provider = Sublayer::Providers::Claude
33
+ Sublayer.configuration.ai_model ="claude-3-opus-20240229"
34
+ when "groq"
35
+ Sublayer.configuration.ai_provider = Sublayer::Providers::Groq
36
+ Sublayer.configuration.ai_model = "mixtral-8x7b-32768"
37
+ when "local"
38
+ Sublayer.configuration.ai_provider = Sublayer::Providers::Local
39
+ Sublayer.configuration.ai_model = "LLaMA_CPP"
40
+ else
41
+ Sublayer.configuration.ai_provider = Sublayer::Providers::OpenAI
42
+ Sublayer.configuration.ai_model = "gpt-4-turbo-preview"
43
+ end
26
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clag
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Werner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-05 00:00:00.000000000 Z
11
+ date: 2024-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cli-kit
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sublayer
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rake
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -158,30 +172,10 @@ files:
158
172
  - lib/clag/commands/generate.rb
159
173
  - lib/clag/commands/help.rb
160
174
  - lib/clag/entry_point.rb
175
+ - lib/clag/generators/command_line_command_generator.rb
161
176
  - lib/clag/version.rb
162
177
  - test/example_test.rb
163
178
  - test/test_helper.rb
164
- - vendor/gems/sublayer/.gitignore
165
- - vendor/gems/sublayer/Gemfile
166
- - vendor/gems/sublayer/LICENSE
167
- - vendor/gems/sublayer/README.md
168
- - vendor/gems/sublayer/Rakefile
169
- - vendor/gems/sublayer/lib/sublayer.rb
170
- - vendor/gems/sublayer/lib/sublayer/agents/generate_code_given_description_agent.rb
171
- - vendor/gems/sublayer/lib/sublayer/agents/generate_command_agent.rb
172
- - vendor/gems/sublayer/lib/sublayer/agents/json_fixing_agent.rb
173
- - vendor/gems/sublayer/lib/sublayer/agents/modify_file_contents_agent.rb
174
- - vendor/gems/sublayer/lib/sublayer/agents/save_file_contents_agent.rb
175
- - vendor/gems/sublayer/lib/sublayer/capabilities/human_assistance.rb
176
- - vendor/gems/sublayer/lib/sublayer/capabilities/llm_assistance.rb
177
- - vendor/gems/sublayer/lib/sublayer/components/output_function.rb
178
- - vendor/gems/sublayer/lib/sublayer/components/output_function_formats/list_of_objects.rb
179
- - vendor/gems/sublayer/lib/sublayer/components/output_function_formats/single_string.rb
180
- - vendor/gems/sublayer/lib/sublayer/enhancements/json.rb
181
- - vendor/gems/sublayer/lib/sublayer/version.rb
182
- - vendor/gems/sublayer/spec/components/output_function_spec.rb
183
- - vendor/gems/sublayer/spec/spec_helper.rb
184
- - vendor/gems/sublayer/sublayer.gemspec
185
179
  homepage: https://github.com/sublayerapp/clag
186
180
  licenses:
187
181
  - MIT
@@ -1,16 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- /.sublayer/
11
-
12
- .rspec_status
13
- .idea/
14
-
15
- *.log
16
- *.gem
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in sublayer_ruby.gemspec
6
- gemspec
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Sublayer
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,52 +0,0 @@
1
- # Sublayer
2
-
3
- An AI agent framework
4
-
5
- ## Installation
6
-
7
- Install the gem by running the following commands:
8
-
9
- $ bundle
10
- $ gem build sublayer.gemspec
11
- $ gem install sublayer-0.0.1.gem
12
-
13
- Your OpenAI API key needs to be accessible in your environment at OPENAI\_API\_KEY
14
-
15
- Your default editor for your environment is also used.
16
-
17
- ## Usage
18
-
19
- ### * Interactive CLI
20
-
21
- You can use the gem by running the command `sublayer` in any project directory.
22
- This will open an interactive shell where all file operations are run relative
23
- to that root project directory.
24
-
25
- In the interactive shell, you're able to create new agents specific to your
26
- project, generate code, modify existing files, and save the resulting code from
27
- those agent commands.
28
-
29
- ### * Simple TDD Pair (experimental / dangerous)
30
-
31
- **Warning:** This will generate code from GPT4 and run it as called from your
32
- tests. Use at your own risk.
33
-
34
- Usage: `sublayer_simple_tdd_pair "TEST_RUN_COMMAND" "FILE_UNDER_TEST"`
35
-
36
- This command will run the TEST_RUN_COMMAND, send the test output, the tests, and
37
- the FILE\_UNDER\_TEST to GPT4 and will attempt to edit FILE\_UNDER\_TEST and
38
- rerun the tests until they pass.
39
-
40
- To do use it like in [this
41
- loom](https://www.loom.com/share/6970b51856b04a91b792f14e848e9b6d) you'll need
42
- to install entr: `brew install entr`
43
-
44
- The command I'm running there is: `ls ./day3/*.rb | entr sublayer_simple_tdd_pair "rspec ./day3/santa_spec.rb" "./day3/santa.rb"`
45
-
46
- ## Development
47
-
48
- TBD
49
-
50
- ## Contributing
51
-
52
- TBD
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec
@@ -1,35 +0,0 @@
1
- module Sublayer
2
- module Agents
3
- class GenerateCodeGivenDescriptionAgent
4
- include Sublayer::Capabilities::LLMAssistance
5
- include Sublayer::Capabilities::HumanAssistance
6
-
7
- attr_reader :description, :technologies, :results
8
-
9
- llm_result_format type: :single_string,
10
- name: "generated_code",
11
- description: "The generated code in the requested language"
12
-
13
- def initialize(description:, technologies:)
14
- @description = description
15
- @technologies = technologies
16
- end
17
-
18
- def execute
19
- @results = human_assistance_with(llm_generate)
20
- end
21
-
22
- def prompt
23
- <<-PROMPT
24
- You are an expert programmer in #{technologies.join(", ")}.
25
-
26
- You are tasked with writing code using the following technologies: #{technologies.join(", ")}.
27
-
28
- The description of the task is #{description}
29
-
30
- Take a deep breath and think step by step before you start coding.
31
- PROMPT
32
- end
33
- end
34
- end
35
- end
@@ -1,37 +0,0 @@
1
- module Sublayer
2
- module Agents
3
- class GenerateCommandLineCommandAgent
4
- include Sublayer::Capabilities::LLMAssistance
5
- include Sublayer::Capabilities::HumanAssistance
6
-
7
- attr_reader :description, :results
8
-
9
- llm_result_format type: :single_string,
10
- name: "command",
11
- description: "The command line command for the user to run or 'unknown'"
12
-
13
- def initialize(description:)
14
- @description = description
15
- end
16
-
17
- def execute
18
- @results = llm_generate
19
- end
20
-
21
- def prompt
22
- <<-PROMPT
23
- You are an expert in command line operations.
24
-
25
- You are tasked with finding or crafting a command line command to achieve the following:
26
-
27
- #{description}
28
-
29
- Considering best practices, what should be run on the command line to achieve this.
30
-
31
- If no command is possible, respond with 'unknown'
32
-
33
- PROMPT
34
- end
35
- end
36
- end
37
- end
@@ -1,32 +0,0 @@
1
- module Sublayer
2
- module Agents
3
- class JSONFixingAgent
4
- include Sublayer::Capabilities::LLMAssistance
5
- include Sublayer::Capabilities::HumanAssistance
6
-
7
- attr_reader :invalid_json, :results
8
-
9
- llm_result_format type: :single_string,
10
- name: "valid_json",
11
- description: "The valid JSON string"
12
-
13
- def initialize(invalid_json:)
14
- @invalid_json = invalid_json
15
- end
16
-
17
- def execute
18
- @results = llm_generate
19
- end
20
-
21
- def prompt
22
- <<-PROMPT
23
- You are an expert in JSON parsing.
24
-
25
- The given string is not a valid JSON: #{invalid_json}
26
-
27
- Please fix this and produce a valid JSON.
28
- PROMPT
29
- end
30
- end
31
- end
32
- end
@@ -1,38 +0,0 @@
1
- module Sublayer
2
- module Agents
3
- class ModifyFileContentsAgent
4
- include Sublayer::Capabilities::LLMAssistance
5
- include Sublayer::Capabilities::HumanAssistance
6
-
7
- attr_reader :file_path, :description, :technologies, :results
8
-
9
- llm_result_format type: :single_string,
10
- name: "modified_file_contents",
11
- description: "The modified file contents"
12
-
13
- def initialize(file_path:, description:, technologies:)
14
- @file_path = file_path
15
- @description = description
16
- @technologies = technologies
17
- end
18
-
19
- def execute
20
- @results = human_assistance_with(llm_generate)
21
- end
22
-
23
- def prompt
24
- <<-PROMPT
25
- You are an expert programmer in #{technologies.join(", ")}.
26
-
27
- Here are the original file contents:
28
-
29
- #{File.read(@file_path)}
30
-
31
- The description of the changes to make is: #{description}
32
-
33
- Please make the necessary changes to this file.
34
- PROMPT
35
- end
36
- end
37
- end
38
- end
@@ -1,18 +0,0 @@
1
- module Sublayer
2
- module Agents
3
- class SaveFileContentsAgent
4
- attr_reader :file_contents, :file_path
5
-
6
- def initialize(file_contents:, file_path:)
7
- @file_contents = file_contents
8
- @file_path = file_path
9
- end
10
-
11
- def execute
12
- File.open(file_path, "w") do |file|
13
- file.write(file_contents)
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,23 +0,0 @@
1
- require "tempfile"
2
-
3
- module Sublayer
4
- module Capabilities
5
- module HumanAssistance
6
- def human_assistance_with(string_to_assist_with)
7
- tempfile = Tempfile.new("some_tempfile_to_assist_with")
8
- tempfile.write(string_to_assist_with)
9
- tempfile.close
10
-
11
- system("$EDITOR #{tempfile.path}")
12
-
13
- tempfile.open
14
- results = tempfile.read
15
-
16
- tempfile.close
17
- tempfile.unlink
18
-
19
- results.chomp
20
- end
21
- end
22
- end
23
- end
@@ -1,253 +0,0 @@
1
- require "openai"
2
- require "pry"
3
- require "httparty"
4
- require "nokogiri"
5
-
6
- module Sublayer
7
- module Capabilities
8
- module LLMAssistance
9
- def self.included(base)
10
- base.extend(ClassMethods)
11
- end
12
-
13
- module ClassMethods
14
- def llm_result_format(type:, name:, description:)
15
- output_function = Sublayer::Components::OutputFunction.create(type: type, name: name, description: description)
16
- const_set(:OUTPUT_FUNCTION, output_function)
17
- end
18
- end
19
-
20
- def llm_generate
21
- case ENV["CLAG_LLM"]
22
- when "gemini"
23
- generate_with_gemini
24
- when "claude"
25
- generate_with_claude
26
- when "groq"
27
- generate_with_groq
28
- when "local"
29
- generate_with_local_model
30
- else
31
- generate_with_openai
32
- end
33
- end
34
-
35
- private
36
-
37
- def generate_with_local_model
38
- system_prompt = <<-PROMPT
39
- In this environment you have access to a set of tools you can use to answer the user's question.
40
-
41
- You may call them like this:
42
- <function_calls>
43
- <invoke>
44
- <tool_name>$TOOL_NAME</tool_name>
45
- <parameters>
46
- <command>value</command>
47
- ...
48
- </parameters>
49
- </invoke>
50
- </function_calls>
51
-
52
- Here are the tools available:
53
- <tools>
54
- #{self.class::OUTPUT_FUNCTION.to_xml}
55
- </tools>
56
-
57
- Respond only with valid xml.
58
- The entire response should be wrapped in a <response> tag.
59
- Any additional information not inside a tool call should go in a <scratch> tag.
60
- PROMPT
61
-
62
- response = HTTParty.post(
63
- "http://localhost:8080/v1/chat/completions",
64
- headers: {
65
- "Authorization": "Bearer no-key",
66
- "Content-Type": "application/json"
67
- },
68
- body: {
69
- "model": "LLaMA_CPP",
70
- "messages": [
71
- { "role": "system", "content": system_prompt },
72
- { "role": "user", "content": prompt }
73
- ]
74
- }.to_json
75
- )
76
-
77
- text_containing_xml = JSON.parse(response.body).dig("choices", 0, "message", "content")
78
- xml = text_containing_xml.match(/\<response\>(.*?)\<\/response\>/m).to_s
79
- response_xml = Nokogiri::XML(xml)
80
- function_output = response_xml.at_xpath("//parameters/command").children.to_s
81
-
82
- return function_output
83
- end
84
-
85
- def generate_with_groq
86
- system_prompt = <<-PROMPT
87
- In this environment you have access to a set of tools you can use to answer the user's question.
88
-
89
- You may call them like this:
90
- <function_calls>
91
- <invoke>
92
- <tool_name>$TOOL_NAME</tool_name>
93
- <parameters>
94
- <command>value</command>
95
- ...
96
- </parameters>
97
- </invoke>
98
- </function_calls>
99
-
100
- Here are the tools available:
101
- <tools>
102
- #{self.class::OUTPUT_FUNCTION.to_xml}
103
- </tools>
104
-
105
- Respond only with valid xml.
106
- The entire response should be wrapped in a <response> tag.
107
- Any additional information not inside a tool call should go in a <scratch> tag.
108
- PROMPT
109
-
110
- response = HTTParty.post(
111
- "https://api.groq.com/openai/v1/chat/completions",
112
- headers: {
113
- "Authorization": "Bearer #{ENV["GROQ_API_KEY"]}",
114
- "Content-Type": "application/json"
115
- },
116
- body: {
117
- "messages": [{"role": "user", "content": "#{system_prompt}\n#{prompt}"}],
118
- "model": "mixtral-8x7b-32768"
119
- }.to_json
120
- )
121
-
122
- text_containing_xml = JSON.parse(response.body).dig("choices", 0, "message", "content")
123
- xml = text_containing_xml.match(/\<response\>(.*?)\<\/response\>/m).to_s
124
- response_xml = Nokogiri::XML(xml)
125
- function_output = response_xml.at_xpath("//response/function_calls/invoke/parameters/command").children.to_s
126
-
127
- return function_output
128
- end
129
-
130
- def generate_with_claude
131
- system_prompt = <<-PROMPT
132
- In this environment you have access to a set of tools you can use to answer the user's question.
133
-
134
- You may call them like this:
135
- <function_calls>
136
- <invoke>
137
- <tool_name>$TOOL_NAME</tool_name>
138
- <parameters>
139
- <$PARAMETER_NAME>$PARAMETER_VALUE</$PARAMETER_NAME>
140
- ...
141
- </parameters>
142
- </invoke>
143
- </function_calls>
144
-
145
- Here are the tools available:
146
- <tools>
147
- #{self.class::OUTPUT_FUNCTION.to_xml}
148
- </tools>
149
-
150
- Respond only with valid xml. The entire response should be wrapped in a <response> tag. Any additional information not inside a tool call should go in a <scratch> tag.
151
- PROMPT
152
-
153
- response = HTTParty.post(
154
- "https://api.anthropic.com/v1/messages",
155
- headers: {
156
- "x-api-key": ENV["ANTHROPIC_API_KEY"],
157
- "anthropic-version": "2023-06-01",
158
- "content-type": "application/json"
159
- },
160
- body: {
161
- model: "claude-3-opus-20240229",
162
- max_tokens: 1337,
163
- system: system_prompt,
164
- messages: [
165
- { "role": "user", "content": prompt }
166
- ]
167
- }.to_json
168
- )
169
-
170
- # raise an error if the response is not a 200
171
- raise "Error generating with Claude, error: #{response.body}" unless response.code == 200
172
-
173
- text_containing_xml = JSON.parse(response.body).dig("content", 0, "text")
174
-
175
- # Extract the xml from the respons contained in <response> tags the content of the string looksl ike this:
176
- xml = text_containing_xml.match(/\<response\>(.*?)\<\/response\>/m).to_s
177
-
178
- # Parse the xml and extract the response
179
- response_xml = Nokogiri::XML(xml)
180
-
181
- # Extract the response from the xml
182
- function_output = response_xml.at_xpath("//response/function_calls/invoke/parameters/command").children.to_s
183
-
184
- return function_output
185
- end
186
-
187
- def generate_with_gemini
188
- gemini_prompt = adapt_prompt_for_gemini
189
-
190
- response = HTTParty.post("https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=#{ENV['GEMINI_API_KEY']}", body: gemini_prompt.to_json, headers: { 'Content-Type' => 'application/json' })
191
-
192
- function_output = extract_function_output_from_gemini_response(response)
193
-
194
- return function_output
195
- end
196
-
197
- def adapt_prompt_for_gemini
198
- return({ tools: { function_declarations: [ self.class::OUTPUT_FUNCTION.to_hash ] }, contents: { role: "user", parts: { text: prompt } } })
199
- end
200
-
201
- def extract_function_output_from_gemini_response(response)
202
- candidates = response.dig('candidates')
203
- if candidates && candidates.size > 0
204
- content = candidates[0].dig('content')
205
- if content && content['parts'] && content['parts'].size > 0
206
- part = content['parts'][0]
207
-
208
- # Check if the part contains a function call
209
- if part.key?('functionCall')
210
- function_name = part['functionCall']['name']
211
- args = part['functionCall']['args']
212
-
213
- # Assuming the agent expects a single string parameter:
214
- if args && args.key?(self.class::OUTPUT_FUNCTION.name)
215
- return args[self.class::OUTPUT_FUNCTION.name]
216
- end
217
- else
218
- # If it's not a function call, check for a direct string response
219
- return part['text'] if part.key?('text')
220
- end
221
- end
222
- end
223
- end
224
-
225
- def generate_with_openai
226
- client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
227
-
228
- response = client.chat(
229
- parameters: {
230
- model: "gpt-4-turbo-preview",
231
- messages: [
232
- {
233
- "role": "user",
234
- "content": prompt
235
- }
236
- ],
237
- function_call: { name: self.class::OUTPUT_FUNCTION.name },
238
- functions: [
239
- self.class::OUTPUT_FUNCTION.to_hash
240
- ]
241
- }
242
- )
243
-
244
- message = response.dig("choices", 0, "message")
245
- raise "No function called" unless message["function_call"]
246
-
247
- function_name = message.dig("function_call", self.class::OUTPUT_FUNCTION.name)
248
- args_from_llm = message.dig("function_call", "arguments")
249
- JSON.parse(args_from_llm)[self.class::OUTPUT_FUNCTION.name]
250
- end
251
- end
252
- end
253
- end
@@ -1,22 +0,0 @@
1
- module Sublayer
2
- module Components
3
- class OutputFunction
4
- include Sublayer::Components
5
-
6
- attr_reader :name
7
-
8
- def self.create(options)
9
- ("Sublayer::Components::"+options[:type].to_s.camelize).constantize.new(options)
10
- end
11
-
12
- def to_hash
13
- # Raise not implemented error
14
- raise NotImplementedError
15
- end
16
-
17
- def to_xml
18
- raise NotImplementedError
19
- end
20
- end
21
- end
22
- end
@@ -1,30 +0,0 @@
1
- module Sublayer
2
- module Components
3
- class ListOfObjects < OutputFunction
4
- def initialize(options)
5
- @name = options[:name]
6
- @description = options[:description]
7
- @structure = options[:structure]
8
- end
9
-
10
- def to_hash
11
- {
12
- name: @name,
13
- description: @description,
14
- parameters: {
15
- type: "object",
16
- properties: {
17
- @name.to_sym => {
18
- type: "array",
19
- items: {
20
- type: "object",
21
- properties: @structure.transform_values { |desc| { type: "string", description: desc.capitalize } }
22
- }
23
- }
24
- }
25
- }
26
- }
27
- end
28
- end
29
- end
30
- end
@@ -1,40 +0,0 @@
1
- module Sublayer
2
- module Components
3
- class SingleString < OutputFunction
4
- def initialize(options)
5
- @name = options[:name]
6
- @description = options[:description]
7
- end
8
-
9
- def to_hash
10
- {
11
- name: @name,
12
- description: @description,
13
- parameters: {
14
- type: "object",
15
- properties: {
16
- @name => {
17
- type: "string",
18
- description: @description
19
- }
20
- }
21
- }
22
- }
23
- end
24
-
25
- def to_xml
26
- <<-XML
27
- <tool_description>
28
- <tool_name>#{@name}</tool_name>
29
- <tool_description>#{@description}</tool_description>
30
- <parameters>
31
- <name>#{@name}</name>
32
- <type>string</type>
33
- <description>#{@description}</description>
34
- </parameters>
35
- </tool_description>
36
- XML
37
- end
38
- end
39
- end
40
- end
@@ -1,9 +0,0 @@
1
- module JSON
2
- class << self
3
- alias_method :original_parse, :parse
4
-
5
- def parse(json, options = {})
6
- original_parse(json, options).with_indifferent_access
7
- end
8
- end
9
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sublayer
4
- VERSION = "0.0.1"
5
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support'
4
- require 'active_support/core_ext/hash/indifferent_access'
5
- require 'active_support/inflector'
6
- require_relative "sublayer/version"
7
-
8
- if !File.directory?(File.join(Dir.pwd, ".sublayer", "agents"))
9
- Dir.mkdir(File.join(Dir.pwd, ".sublayer"))
10
- Dir.mkdir(File.join(Dir.pwd, ".sublayer", "agents"))
11
- end
12
-
13
- # List of directories to load files from
14
- LOAD_PATHS = [
15
- File.join(__dir__, 'sublayer', 'components'),
16
- File.join(__dir__, 'sublayer', 'components', 'output_function_formats'),
17
- File.join(__dir__, 'sublayer', 'capabilities'),
18
- File.join(__dir__, 'sublayer', 'agents'),
19
- File.join(Dir.pwd, '.sublayer', 'agents')
20
- ]
21
-
22
-
23
- # Load files from each directory in the list
24
- LOAD_PATHS.each do |load_path|
25
- Dir.glob(File.join(load_path, '*.rb')).each do |file|
26
- require_relative file
27
- end
28
- end
29
-
30
- require_relative "sublayer/enhancements/json.rb"
31
-
32
- module Sublayer
33
- class Error < StandardError; end
34
-
35
- # Defines a method to reload all the agents
36
- def self.reload_agents
37
- LOAD_PATHS.each do |load_path|
38
- Dir.glob(File.join(load_path, '*.rb')) do |file|
39
- load file
40
- end
41
- end
42
- end
43
-
44
- def self.cwd
45
- Dir.pwd
46
- end
47
- end
@@ -1,73 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe Sublayer::Components::OutputFunction do
4
- describe "#to_hash" do
5
- context "type: :single_string" do
6
- it "formats the hash output correctly" do
7
- output_function = Sublayer::Components::OutputFunction.create(type: :single_string, name: "modified_file_contents", description: "The modified file contents")
8
- expect(output_function.to_hash).to eq(
9
- {
10
- name: "modified_file_contents",
11
- description: "The modified file contents",
12
- parameters: {
13
- type: "object",
14
- properties: {
15
- "modified_file_contents" => {
16
- type: "string",
17
- description: "The modified file contents"
18
- }
19
- }
20
- }
21
- }
22
- )
23
- end
24
- end
25
-
26
- context "type: :list_of_objects" do
27
- it "formats the hash output correctly" do
28
- output_function = Sublayer::Components::OutputFunction.create(
29
- type: :list_of_objects,
30
- name: "retrieved_steps",
31
- description: "The retrieved steps for performing the given coding task",
32
- structure: {
33
- category: "The category of the step",
34
- command: "The command to run on the command line",
35
- description: "The description of what the command is for"
36
- }
37
- )
38
-
39
- expect(output_function.to_hash).to eq(
40
- {
41
- name: "retrieved_steps",
42
- description: "The retrieved steps for performing the given coding task",
43
- parameters: {
44
- type: "object",
45
- properties: {
46
- retrieved_steps: {
47
- type: "array",
48
- items: {
49
- type: "object",
50
- properties: {
51
- category: {
52
- type: "string",
53
- description: "The category of the step"
54
- },
55
- command: {
56
- type: "string",
57
- description: "The command to run on the command line"
58
- },
59
- description: {
60
- type: "string",
61
- description: "The description of what the command is for"
62
- }
63
- }
64
- }
65
- }
66
- }
67
- }
68
- }
69
- )
70
- end
71
- end
72
- end
73
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sublayer"
4
-
5
- RSpec.configure do |config|
6
- # Enable flags like --only-failures and --next-failure
7
- config.example_status_persistence_file_path = ".rspec_status"
8
-
9
- # Disable RSpec exposing methods globally on `Module` and `main`
10
- config.disable_monkey_patching!
11
-
12
- config.expect_with :rspec do |c|
13
- c.syntax = :expect
14
- end
15
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/sublayer/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "sublayer"
7
- spec.version = Sublayer::VERSION
8
- spec.authors = ["Scott Werner"]
9
- spec.email = ["scott@sublayer.com"]
10
- spec.license = "MIT"
11
-
12
- spec.summary = "A ruby library for interacting with and orchestrating AI agents"
13
- spec.description = "A command line framework for building, generating, and orchestrating AI agents."
14
- spec.homepage = "https://www.sublayer.com"
15
- spec.required_ruby_version = ">= 2.6.0"
16
-
17
- spec.metadata["homepage_uri"] = spec.homepage
18
-
19
- # Specify which files should be added to the gem when it is released.
20
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(__dir__) do
22
- `git ls-files -z`.split("\x0").reject do |f|
23
- (File.expand_path(f) == __FILE__) ||
24
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
25
- end
26
- end
27
-
28
- spec.executables << "sublayer"
29
- spec.executables << "sublayer_simple_tdd_pair"
30
- spec.require_paths = ["lib"]
31
-
32
- spec.add_dependency "ruby-openai"
33
- spec.add_dependency "colorize"
34
- spec.add_dependency "activesupport"
35
-
36
- spec.add_development_dependency "rspec", "~> 3.12"
37
- spec.add_development_dependency "pry", " ~> 0.14"
38
- end