clag 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -0
  3. data/clag.gemspec +1 -0
  4. data/exe/clag +1 -3
  5. data/lib/clag/commands/generate.rb +1 -1
  6. data/lib/clag/generators/command_line_command_generator.rb +29 -0
  7. data/lib/clag/version.rb +1 -1
  8. data/lib/clag.rb +19 -1
  9. metadata +17 -23
  10. data/vendor/gems/sublayer/.gitignore +0 -16
  11. data/vendor/gems/sublayer/Gemfile +0 -6
  12. data/vendor/gems/sublayer/LICENSE +0 -21
  13. data/vendor/gems/sublayer/README.md +0 -52
  14. data/vendor/gems/sublayer/Rakefile +0 -8
  15. data/vendor/gems/sublayer/lib/sublayer/agents/generate_code_given_description_agent.rb +0 -35
  16. data/vendor/gems/sublayer/lib/sublayer/agents/generate_command_agent.rb +0 -37
  17. data/vendor/gems/sublayer/lib/sublayer/agents/json_fixing_agent.rb +0 -32
  18. data/vendor/gems/sublayer/lib/sublayer/agents/modify_file_contents_agent.rb +0 -38
  19. data/vendor/gems/sublayer/lib/sublayer/agents/save_file_contents_agent.rb +0 -18
  20. data/vendor/gems/sublayer/lib/sublayer/capabilities/human_assistance.rb +0 -23
  21. data/vendor/gems/sublayer/lib/sublayer/capabilities/llm_assistance.rb +0 -203
  22. data/vendor/gems/sublayer/lib/sublayer/components/output_function.rb +0 -22
  23. data/vendor/gems/sublayer/lib/sublayer/components/output_function_formats/list_of_objects.rb +0 -30
  24. data/vendor/gems/sublayer/lib/sublayer/components/output_function_formats/single_string.rb +0 -40
  25. data/vendor/gems/sublayer/lib/sublayer/enhancements/json.rb +0 -9
  26. data/vendor/gems/sublayer/lib/sublayer/version.rb +0 -5
  27. data/vendor/gems/sublayer/lib/sublayer.rb +0 -47
  28. data/vendor/gems/sublayer/spec/components/output_function_spec.rb +0 -73
  29. data/vendor/gems/sublayer/spec/spec_helper.rb +0 -15
  30. data/vendor/gems/sublayer/sublayer.gemspec +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1108e8ad1844399f3606f873dd03b65fb4131a130b829ba1332636ffa7bae74
4
- data.tar.gz: c736a7bf0cd0e6fc3a6000b288c9b411dc53cf30857b61698bc6b28b82bb7a63
3
+ metadata.gz: f27cc2c8372c73257a2455613a3f5db566301ce249dc5e0b8d6812a16795f1ea
4
+ data.tar.gz: 2886e0b99f0d5443c66bc19087a96584674927e320de67eea79115a9f734015d
5
5
  SHA512:
6
- metadata.gz: 36ae1725de2b5d549d2f07f1401724ab82ea1fea9ba8622fb05d154f4008d53638b750a43654c7391c07f953e58d4dd9e6352519fead7b8182e5dbd8694078d8
7
- data.tar.gz: 70d477e186eba030431e2e69d6b9cac4d88741d00a946dee73364e07e2e61f178cf39fd0619da059d3a80ff5b631d83bb2d53674cb8a84922ac163a99b9c784d
6
+ metadata.gz: 1efa37ed3818cb1b5778e76c5eaccebbb732529cafe66da308964abf3be10047fec073b232e0134b41188b748090752b95d7d542b6d82bb6cd3bc2c841be29fe
7
+ data.tar.gz: 40619d05e7bd9fc2c3f17aa42f615914d2e2f8fb569304ebaf04f04bdfaa056a844a3dceb489d39769faa14c104de927319b0ecb3b28ae938f6bbfabea40374d
data/README.md CHANGED
@@ -47,6 +47,15 @@ command with the help of an LLM!
47
47
 
48
48
  * Select Groq as your preferred LLM by setting CLAG\_LLM=groq in your environment
49
49
 
50
+ ### Using a Local Model
51
+
52
+ * Have a model locally from either Ollama or Llamafile with an OpenAI compatible
53
+ API
54
+
55
+ * Have the API server running on port 8080
56
+
57
+ * Select local as your preferred LLM by setting CLAG\_LLM=local in your environment
58
+
50
59
  ## Usage
51
60
 
52
61
  Currently support one command: "g".
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.4'
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.4
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,203 +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
- else
29
- generate_with_openai
30
- end
31
- end
32
-
33
- private
34
-
35
- def generate_with_groq
36
- system_prompt = <<-PROMPT
37
- In this environment you have access to a set of tools you can use to answer the user's question.
38
-
39
- You may call them like this:
40
- <function_calls>
41
- <invoke>
42
- <tool_name>$TOOL_NAME</tool_name>
43
- <parameters>
44
- <command>value</command>
45
- ...
46
- </parameters>
47
- </invoke>
48
- </function_calls>
49
-
50
- Here are the tools available:
51
- <tools>
52
- #{self.class::OUTPUT_FUNCTION.to_xml}
53
- </tools>
54
-
55
- Respond only with valid xml.
56
- The entire response should be wrapped in a <response> tag.
57
- Any additional information not inside a tool call should go in a <scratch> tag.
58
- PROMPT
59
-
60
- response = HTTParty.post(
61
- "https://api.groq.com/openai/v1/chat/completions",
62
- headers: {
63
- "Authorization": "Bearer #{ENV["GROQ_API_KEY"]}",
64
- "Content-Type": "application/json"
65
- },
66
- body: {
67
- "messages": [{"role": "user", "content": "#{system_prompt}\n#{prompt}"}],
68
- "model": "mixtral-8x7b-32768"
69
- }.to_json
70
- )
71
-
72
- text_containing_xml = JSON.parse(response.body).dig("choices", 0, "message", "content")
73
- xml = text_containing_xml.match(/\<response\>(.*?)\<\/response\>/m).to_s
74
- response_xml = Nokogiri::XML(xml)
75
- function_output = response_xml.at_xpath("//response/function_calls/invoke/parameters/command").children.to_s
76
-
77
- return function_output
78
- end
79
-
80
- def generate_with_claude
81
- system_prompt = <<-PROMPT
82
- In this environment you have access to a set of tools you can use to answer the user's question.
83
-
84
- You may call them like this:
85
- <function_calls>
86
- <invoke>
87
- <tool_name>$TOOL_NAME</tool_name>
88
- <parameters>
89
- <$PARAMETER_NAME>$PARAMETER_VALUE</$PARAMETER_NAME>
90
- ...
91
- </parameters>
92
- </invoke>
93
- </function_calls>
94
-
95
- Here are the tools available:
96
- <tools>
97
- #{self.class::OUTPUT_FUNCTION.to_xml}
98
- </tools>
99
-
100
- 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.
101
- PROMPT
102
-
103
- response = HTTParty.post(
104
- "https://api.anthropic.com/v1/messages",
105
- headers: {
106
- "x-api-key": ENV["ANTHROPIC_API_KEY"],
107
- "anthropic-version": "2023-06-01",
108
- "content-type": "application/json"
109
- },
110
- body: {
111
- model: "claude-3-opus-20240229",
112
- max_tokens: 1337,
113
- system: system_prompt,
114
- messages: [
115
- { "role": "user", "content": prompt }
116
- ]
117
- }.to_json
118
- )
119
-
120
- # raise an error if the response is not a 200
121
- raise "Error generating with Claude, error: #{response.body}" unless response.code == 200
122
-
123
- text_containing_xml = JSON.parse(response.body).dig("content", 0, "text")
124
-
125
- # Extract the xml from the respons contained in <response> tags the content of the string looksl ike this:
126
- xml = text_containing_xml.match(/\<response\>(.*?)\<\/response\>/m).to_s
127
-
128
- # Parse the xml and extract the response
129
- response_xml = Nokogiri::XML(xml)
130
-
131
- # Extract the response from the xml
132
- function_output = response_xml.at_xpath("//response/function_calls/invoke/parameters/command").children.to_s
133
-
134
- return function_output
135
- end
136
-
137
- def generate_with_gemini
138
- gemini_prompt = adapt_prompt_for_gemini
139
-
140
- 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' })
141
-
142
- function_output = extract_function_output_from_gemini_response(response)
143
-
144
- return function_output
145
- end
146
-
147
- def adapt_prompt_for_gemini
148
- return({ tools: { function_declarations: [ self.class::OUTPUT_FUNCTION.to_hash ] }, contents: { role: "user", parts: { text: prompt } } })
149
- end
150
-
151
- def extract_function_output_from_gemini_response(response)
152
- candidates = response.dig('candidates')
153
- if candidates && candidates.size > 0
154
- content = candidates[0].dig('content')
155
- if content && content['parts'] && content['parts'].size > 0
156
- part = content['parts'][0]
157
-
158
- # Check if the part contains a function call
159
- if part.key?('functionCall')
160
- function_name = part['functionCall']['name']
161
- args = part['functionCall']['args']
162
-
163
- # Assuming the agent expects a single string parameter:
164
- if args && args.key?(self.class::OUTPUT_FUNCTION.name)
165
- return args[self.class::OUTPUT_FUNCTION.name]
166
- end
167
- else
168
- # If it's not a function call, check for a direct string response
169
- return part['text'] if part.key?('text')
170
- end
171
- end
172
- end
173
- end
174
-
175
- def generate_with_openai
176
- client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"])
177
-
178
- response = client.chat(
179
- parameters: {
180
- model: "gpt-4-turbo-preview",
181
- messages: [
182
- {
183
- "role": "user",
184
- "content": prompt
185
- }
186
- ],
187
- function_call: { name: self.class::OUTPUT_FUNCTION.name },
188
- functions: [
189
- self.class::OUTPUT_FUNCTION.to_hash
190
- ]
191
- }
192
- )
193
-
194
- message = response.dig("choices", 0, "message")
195
- raise "No function called" unless message["function_call"]
196
-
197
- function_name = message.dig("function_call", self.class::OUTPUT_FUNCTION.name)
198
- args_from_llm = message.dig("function_call", "arguments")
199
- JSON.parse(args_from_llm)[self.class::OUTPUT_FUNCTION.name]
200
- end
201
- end
202
- end
203
- 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