clag 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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