sublayer 0.1.0.pre.alpha.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +43 -38
- data/lib/sublayer/agents/base.rb +23 -19
- data/lib/sublayer/components/output_adapters/formattable.rb +29 -0
- data/lib/sublayer/components/output_adapters/list_of_strings.rb +28 -0
- data/lib/sublayer/components/output_adapters.rb +3 -0
- data/lib/sublayer/generators/base.rb +1 -1
- data/lib/sublayer/providers/claude.rb +3 -17
- data/lib/sublayer/providers/gemini.rb +30 -48
- data/lib/sublayer/providers/open_ai.rb +3 -17
- data/lib/sublayer/triggers/base.rb +13 -0
- data/lib/sublayer/triggers/file_change.rb +20 -0
- data/lib/sublayer/version.rb +1 -1
- data/lib/sublayer.rb +1 -1
- data/sublayer.gemspec +1 -1
- metadata +11 -13
- data/lib/sublayer/generators/examples/code_from_blueprint_generator.rb +0 -30
- data/lib/sublayer/generators/examples/code_from_description_generator.rb +0 -26
- data/lib/sublayer/generators/examples/description_from_code_generator.rb +0 -23
- data/lib/sublayer/generators/examples/invalid_to_valid_json_generator.rb +0 -23
- data/lib/sublayer/generators/examples/route_selection_from_user_intent_generator.rb +0 -29
- data/lib/sublayer/generators/examples/sentiment_from_text_generator.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0266ca3b856c768f521cf65a907ebe9d411f5420f83bfb62455912075253820
|
4
|
+
data.tar.gz: 1b32e201baa8f26e8caa3a124b4ebe9aeeb652b31745ec0fe6b06ab0cee57da2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 417a853211fb739887936804948c5758d3880431b2b8252df5e3fc58beb6464f44d6f3e1efb0945b0e72a4f80b7135eec8b9813fbffbe9a6cfbe0c4a1d4bf63a
|
7
|
+
data.tar.gz: 4ee873c4e5edbe289211320573847cd58273b959e001ad3db8fadf68fdedc0bdc9e957b103573c3d31080acc69886dd58d0146906122192bd75fce1e24724d49
|
data/README.md
CHANGED
@@ -14,12 +14,18 @@ for new features and bug fixes.
|
|
14
14
|
|
15
15
|
To maintain stability in your application, we recommend pinning the version of
|
16
16
|
Sublayer in your Gemfile to a specific minor version. For example, to pin to
|
17
|
-
version 0.
|
17
|
+
version 0.2.x, you would add the following line to your Gemfile:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
gem 'sublayer', '~> 0.
|
20
|
+
gem 'sublayer', '~> 0.2'
|
21
21
|
```
|
22
22
|
|
23
|
+
## Notes on 0.2
|
24
|
+
|
25
|
+
New default model update: gpt 4 turbo -> gpt 4o
|
26
|
+
|
27
|
+
Gemini: Updates include the use of beta API function calling features. Experimental and unstable.
|
28
|
+
|
23
29
|
## Installation
|
24
30
|
|
25
31
|
Install the gem by running the following commands:
|
@@ -29,12 +35,12 @@ Install the gem by running the following commands:
|
|
29
35
|
Or add this line to your application's Gemfile:
|
30
36
|
|
31
37
|
```ruby
|
32
|
-
gem 'sublayer', '~> 0.
|
38
|
+
gem 'sublayer', '~> 0.2'
|
33
39
|
```
|
34
40
|
|
35
41
|
## Choose your AI Model
|
36
42
|
|
37
|
-
Sublayer is model-agnostic and can be used with any AI model. Below are the
|
43
|
+
Sublayer is model-agnostic and can be used with any AI model. Below are the supported LLM Providers. Check out our [docs](https://docs.sublayer.com) to add your own custom Provider.
|
38
44
|
|
39
45
|
### OpenAI (Default)
|
40
46
|
|
@@ -45,10 +51,12 @@ Visit [OpenAI](https://openai.com/product) to get an API key.
|
|
45
51
|
Usage:
|
46
52
|
```ruby
|
47
53
|
Sublayer.configuration.ai_provider = Sublayer::Providers::OpenAI
|
48
|
-
Sublayer.configuration.ai_model = "gpt-
|
54
|
+
Sublayer.configuration.ai_model = "gpt-4o"
|
49
55
|
```
|
50
56
|
|
51
|
-
### Gemini
|
57
|
+
### Gemini [UNSTABLE]
|
58
|
+
|
59
|
+
(Gemini's function calling API is in beta. Not recommended for production use.)
|
52
60
|
|
53
61
|
Expects you to have a Gemini API key set in the `GEMINI_API_KEY` environment variable.
|
54
62
|
|
@@ -57,7 +65,7 @@ Visit [Google AI Studio](https://ai.google.dev/) to get an API key.
|
|
57
65
|
Usage:
|
58
66
|
```ruby
|
59
67
|
Sublayer.configuration.ai_provider = Sublayer::Providers::Gemini
|
60
|
-
Sublayer.configuration.ai_model = "gemini-pro"
|
68
|
+
Sublayer.configuration.ai_model = "gemini-1.5-pro"
|
61
69
|
```
|
62
70
|
|
63
71
|
### Claude
|
@@ -70,7 +78,7 @@ Visit [Anthropic](https://anthropic.com/) to get an API key.
|
|
70
78
|
Usage:
|
71
79
|
```ruby
|
72
80
|
Sublayer.configuration.ai_provider = Sublayer::Providers::Claude
|
73
|
-
Sublayer.configuration.ai_model ="claude-3-
|
81
|
+
Sublayer.configuration.ai_model ="claude-3-5-sonnet-20240620"
|
74
82
|
```
|
75
83
|
|
76
84
|
## Concepts
|
@@ -81,49 +89,46 @@ Generators are responsible for generating specific outputs based on input data.
|
|
81
89
|
They focus on a single generation task and do not perform any actions or complex
|
82
90
|
decision-making. Generators are the building blocks of the Sublayer framework.
|
83
91
|
|
84
|
-
Examples (in the `/
|
85
|
-
-
|
86
|
-
technologies used.
|
87
|
-
-
|
88
|
-
it.
|
89
|
-
-
|
90
|
-
description, and a description of the desired code.
|
92
|
+
Examples (in the `/spec/generators/examples` directory):
|
93
|
+
- [CodeFromDescriptionGenerator](https://github.com/sublayerapp/sublayer/blob/main/spec/generators/examples/code_from_description_generator.rb):
|
94
|
+
Generates code based on a description and the technologies used.
|
95
|
+
- [DescriptionFromCodeGenerator](https://github.com/sublayerapp/sublayer/blob/main/spec/generators/description_from_code_generator_spec.rb):
|
96
|
+
Generates a description of the code passed in to it.
|
97
|
+
- [CodeFromBlueprintGenerator](https://github.com/sublayerapp/sublayer/blob/main/spec/generators/examples/code_from_blueprint_generator.rb):
|
98
|
+
Generates code based on a blueprint, a blueprint description, and a description of the desired code.
|
91
99
|
|
92
100
|
|
93
|
-
### Actions
|
101
|
+
### Actions
|
94
102
|
|
95
|
-
Actions
|
96
|
-
|
97
|
-
single action and do not involve complex decision-making. Actions are the
|
98
|
-
executable units that bring the generated inputs to life.
|
103
|
+
Actions perform specific operations to either get inputs for a Generator or use
|
104
|
+
the generated output from a Generator. Actions do not involve complex decision making.
|
99
105
|
|
100
106
|
Examples:
|
101
|
-
-
|
102
|
-
|
107
|
+
- [WriteFileAction](https://github.com/sublayerapp/tddbot/blob/43297c5da9445bd6c8882d5e3876cff5fc6b2650/lib/tddbot/sublayer/actions/write_file_action.rb):
|
108
|
+
Saves generated output to a file.
|
109
|
+
- [RunTestCommandAction](https://github.com/sublayerapp/tddbot/blob/43297c5da9445bd6c8882d5e3876cff5fc6b2650/lib/tddbot/sublayer/actions/run_test_command_action.rb):
|
110
|
+
Runs a generated command line command.
|
103
111
|
|
104
|
-
###
|
112
|
+
### Agents
|
105
113
|
|
106
|
-
|
107
|
-
|
108
|
-
and flow control. Tasks are the high-level building blocks that define the
|
109
|
-
desired outcome.
|
114
|
+
Sublayer Agents are autonomous entities designed to perform specific
|
115
|
+
tasks or monitor systems.
|
110
116
|
|
111
117
|
Examples:
|
112
|
-
-
|
113
|
-
|
118
|
+
- [RSpecAgent](https://github.com/sublayerapp/sublayer/blob/main/spec/agents/examples/rspec_agent.rb):
|
119
|
+
Runs tests whenever a file is changed. If the tests fail the code is changed
|
120
|
+
by the agent to pass the tests.
|
114
121
|
|
115
|
-
###
|
122
|
+
### Triggers
|
116
123
|
|
117
|
-
|
118
|
-
|
119
|
-
adaptation based on the outcomes of the Tasks. Agents are the intelligent
|
120
|
-
supervisors that manage the overall workflow.
|
124
|
+
Sublayer Triggers are used in agents. Triggers decide when an agent is activated
|
125
|
+
and performs its task
|
121
126
|
|
122
127
|
Examples:
|
123
|
-
-
|
124
|
-
|
125
|
-
|
126
|
-
|
128
|
+
- [FileChange](https://github.com/sublayerapp/sublayer/blob/main/lib/sublayer/triggers/file_change.rb):
|
129
|
+
This built in sublayer trigger, listens for file changes
|
130
|
+
- [TimeInterval](https://docs.sublayer.com/docs/guides/build-a-custom-trigger)
|
131
|
+
This custom trigger tutorial shows how to create your own trigger, this one activates on a time interval
|
127
132
|
|
128
133
|
## Usage Examples
|
129
134
|
|
data/lib/sublayer/agents/base.rb
CHANGED
@@ -2,48 +2,52 @@ module Sublayer
|
|
2
2
|
module Agents
|
3
3
|
class Base
|
4
4
|
class << self
|
5
|
-
|
5
|
+
attr_reader :triggers, :goal_condition_block, :check_status_block, :step_block, :listeners
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def trigger(trigger_instance = nil)
|
8
|
+
@triggers ||= []
|
9
|
+
|
10
|
+
if trigger_instance
|
11
|
+
@triggers << trigger_instance
|
12
|
+
else
|
13
|
+
raise ArgumentError, "Either a trigger instance or a block must be provided"
|
14
|
+
end
|
9
15
|
end
|
10
16
|
|
11
17
|
def trigger_on_files_changed(&block)
|
12
|
-
|
18
|
+
trigger(Triggers::FileChange.new(&block))
|
13
19
|
end
|
14
20
|
|
15
21
|
def goal_condition(&block)
|
16
|
-
|
22
|
+
@goal_condition_block = block
|
17
23
|
end
|
18
24
|
|
19
25
|
def check_status(&block)
|
20
|
-
|
26
|
+
@check_status_block = block
|
21
27
|
end
|
22
28
|
|
23
29
|
def step(&block)
|
24
|
-
|
30
|
+
@step_block = block
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
28
34
|
def run
|
29
|
-
|
30
|
-
folders = files_to_listen_to.map { |file| File.dirname(file) }.uniq
|
31
|
-
|
32
|
-
listener = Listen.to(*folders) do |modified, added, removed|
|
33
|
-
if files_to_listen_to.any? { |file| modified.include?(file) }
|
34
|
-
take_step
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
listener.start
|
39
|
-
|
35
|
+
setup_triggers
|
40
36
|
take_step
|
41
|
-
|
42
37
|
sleep
|
43
38
|
end
|
44
39
|
|
45
40
|
private
|
46
41
|
|
42
|
+
def setup_triggers
|
43
|
+
@listeners = []
|
44
|
+
|
45
|
+
self.class.triggers.each do |trigger|
|
46
|
+
listener = trigger.setup(self)
|
47
|
+
@listeners << listener if listener
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
47
51
|
def take_step
|
48
52
|
instance_eval(&self.class.check_status_block)
|
49
53
|
instance_eval(&self.class.step_block) unless instance_eval(&self.class.goal_condition_block)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sublayer
|
2
|
+
module Components
|
3
|
+
module OutputAdapters
|
4
|
+
module Formattable
|
5
|
+
def format_properties
|
6
|
+
formatted_properties = {}
|
7
|
+
self.properties.each do |prop|
|
8
|
+
property = {
|
9
|
+
type: prop.type,
|
10
|
+
description: prop.description
|
11
|
+
}
|
12
|
+
|
13
|
+
property[:enum] = prop.enum if prop.respond_to?(:enum) && prop.enum
|
14
|
+
property[:default] = prop.default if prop.respond_to?(:default) && !prop.default.nil?
|
15
|
+
property[:minimum] = prop.minimum if prop.respond_to?(:minimum) && !prop.minimum.nil?
|
16
|
+
property[:maximum] = prop.maximum if prop.respond_to?(:maximum) && !prop.maximum.nil?
|
17
|
+
property[:items] = prop.items if prop.respond_to?(:items) && prop.items
|
18
|
+
formatted_properties[prop.name.to_sym] = property
|
19
|
+
end
|
20
|
+
formatted_properties
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_required
|
24
|
+
self.properties.select(&:required).map(&:name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sublayer
|
2
|
+
module Components
|
3
|
+
module OutputAdapters
|
4
|
+
class ListOfStrings
|
5
|
+
attr_reader :name, :description
|
6
|
+
|
7
|
+
def initialize(options)
|
8
|
+
@name = options[:name]
|
9
|
+
@description = options[:description]
|
10
|
+
end
|
11
|
+
|
12
|
+
def properties
|
13
|
+
[
|
14
|
+
OpenStruct.new(
|
15
|
+
name: @name,
|
16
|
+
type: 'array',
|
17
|
+
description: @description,
|
18
|
+
required: true,
|
19
|
+
items: {
|
20
|
+
type: 'string'
|
21
|
+
}
|
22
|
+
)
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -4,7 +4,7 @@ module Sublayer
|
|
4
4
|
attr_reader :results
|
5
5
|
|
6
6
|
def self.llm_output_adapter(options)
|
7
|
-
output_adapter = Sublayer::Components::OutputAdapters.create(options)
|
7
|
+
output_adapter = Sublayer::Components::OutputAdapters.create(options).extend(Sublayer::Components::OutputAdapters::Formattable)
|
8
8
|
const_set(:OUTPUT_ADAPTER, output_adapter)
|
9
9
|
end
|
10
10
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Sublayer.configuration.ai_provider = Sublayer::Providers::Claude
|
2
|
-
# Sublayer.configuration.ai_model ="claude-3-
|
2
|
+
# Sublayer.configuration.ai_model ="claude-3-5-sonnet-20240620"
|
3
3
|
|
4
4
|
module Sublayer
|
5
5
|
module Providers
|
@@ -22,8 +22,8 @@ module Sublayer
|
|
22
22
|
description: output_adapter.description,
|
23
23
|
input_schema: {
|
24
24
|
type: "object",
|
25
|
-
properties: format_properties
|
26
|
-
required: output_adapter.
|
25
|
+
properties: output_adapter.format_properties,
|
26
|
+
required: output_adapter.format_required
|
27
27
|
}
|
28
28
|
}
|
29
29
|
],
|
@@ -35,20 +35,6 @@ module Sublayer
|
|
35
35
|
function_input = JSON.parse(response.body).dig("content").find {|content| content['type'] == 'tool_use'}.dig("input")
|
36
36
|
function_input[output_adapter.name]
|
37
37
|
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def self.format_properties(output_adapter)
|
41
|
-
output_adapter.properties.each_with_object({}) do |property, hash|
|
42
|
-
hash[property.name] = {
|
43
|
-
type: property.type,
|
44
|
-
description: property.description
|
45
|
-
}
|
46
|
-
|
47
|
-
if property.enum
|
48
|
-
hash[property.name][:enum] = property.enum
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
38
|
end
|
53
39
|
end
|
54
40
|
end
|
@@ -1,44 +1,41 @@
|
|
1
|
+
# *UNSTABLE* Gemini function calling API is in beta.
|
2
|
+
# Provider is not recommended until API update.
|
3
|
+
|
1
4
|
# Sublayer.configuration.ai_provider = Sublayer::Providers::Gemini
|
2
|
-
# Sublayer.configuration.ai_model = "gemini-pro"
|
5
|
+
# Sublayer.configuration.ai_model = "gemini-1.5-pro"
|
3
6
|
|
4
7
|
module Sublayer
|
5
8
|
module Providers
|
6
9
|
class Gemini
|
7
10
|
def self.call(prompt:, output_adapter:)
|
8
|
-
system_prompt = <<-PROMPT
|
9
|
-
You have access to a set of tools to answer the prompt.
|
10
|
-
|
11
|
-
You may call tools like this:
|
12
|
-
<tool_calls>
|
13
|
-
<tool_call>
|
14
|
-
<tool_name>$TOOL_NAME</tool_name>
|
15
|
-
<parameters>
|
16
|
-
<$PARAMETER_NAME>$VALUE</$PARAMETER_NAME>
|
17
|
-
...
|
18
|
-
</parameters>
|
19
|
-
</tool_call>
|
20
|
-
</tool_calls>
|
21
|
-
|
22
|
-
Here are the tools available:
|
23
|
-
<tools>
|
24
|
-
<tool_description>
|
25
|
-
<tool_name>#{output_adapter.name}</tool_name>
|
26
|
-
<tool_description>#{output_adapter.description}</tool_description>
|
27
|
-
<parameters>
|
28
|
-
#{format_properties(output_adapter)}
|
29
|
-
</parameters>
|
30
|
-
</tool_description>
|
31
|
-
</tools>
|
32
|
-
|
33
|
-
Respond only with valid xml.
|
34
|
-
The entire response should be wrapped in a <response> tag.
|
35
|
-
Your response should call a tool inside a <tool_calls> tag.
|
36
|
-
PROMPT
|
37
|
-
|
38
11
|
response = HTTParty.post(
|
39
12
|
"https://generativelanguage.googleapis.com/v1beta/models/#{Sublayer.configuration.ai_model}:generateContent?key=#{ENV['GEMINI_API_KEY']}",
|
40
13
|
body: {
|
41
|
-
contents: {
|
14
|
+
contents: {
|
15
|
+
role: "user",
|
16
|
+
parts: {
|
17
|
+
text: "#{prompt}"
|
18
|
+
},
|
19
|
+
},
|
20
|
+
tools: {
|
21
|
+
functionDeclarations: [
|
22
|
+
{
|
23
|
+
name: output_adapter.name,
|
24
|
+
description: output_adapter.description,
|
25
|
+
parameters: {
|
26
|
+
type: "OBJECT",
|
27
|
+
properties: output_adapter.format_properties,
|
28
|
+
required: output_adapter.format_required
|
29
|
+
}
|
30
|
+
}
|
31
|
+
]
|
32
|
+
},
|
33
|
+
tool_config: {
|
34
|
+
function_calling_config: {
|
35
|
+
mode: "ANY",
|
36
|
+
allowed_function_names: [output_adapter.name]
|
37
|
+
}
|
38
|
+
}
|
42
39
|
}.to_json,
|
43
40
|
headers: {
|
44
41
|
"Content-Type" => "application/json"
|
@@ -47,22 +44,7 @@ module Sublayer
|
|
47
44
|
|
48
45
|
raise "Error generating with Gemini, error: #{response.body}" unless response.success?
|
49
46
|
|
50
|
-
|
51
|
-
tool_output = Nokogiri::HTML.parse(text_containing_xml.match(/\<#{output_adapter.name}\>(.*?)\<\/#{output_adapter.name}\>/m)[1]).text
|
52
|
-
|
53
|
-
raise "Gemini did not format response, error: #{response.body}" unless tool_output
|
54
|
-
return tool_output
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
def self.format_properties(output_adapter)
|
59
|
-
output_adapter.properties.each_with_object("") do |property, xml|
|
60
|
-
xml << "<name>#{property.name}</name>"
|
61
|
-
xml << "<type>#{property.type}</type>"
|
62
|
-
xml << "<description>#{property.description}</description>"
|
63
|
-
xml << "<required>#{property.required}</required>"
|
64
|
-
xml << "<enum>#{property.enum}</enum>" if property.enum
|
65
|
-
end
|
47
|
+
argument = response.dig("candidates", 0, "content", "parts", 0, "functionCall", "args", output_adapter.name)
|
66
48
|
end
|
67
49
|
end
|
68
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Sublayer.configuration.ai_provider = Sublayer::Providers::OpenAI
|
2
|
-
# Sublayer.configuration.ai_model = "gpt-
|
2
|
+
# Sublayer.configuration.ai_model = "gpt-4o"
|
3
3
|
|
4
4
|
module Sublayer
|
5
5
|
module Providers
|
@@ -25,9 +25,9 @@ module Sublayer
|
|
25
25
|
description: output_adapter.description,
|
26
26
|
parameters: {
|
27
27
|
type: "object",
|
28
|
-
properties:
|
28
|
+
properties: output_adapter.format_properties
|
29
29
|
},
|
30
|
-
required:
|
30
|
+
required: output_adapter.format_required
|
31
31
|
}
|
32
32
|
}
|
33
33
|
]
|
@@ -41,20 +41,6 @@ module Sublayer
|
|
41
41
|
function_body = message.dig("tool_calls", 0, "function", "arguments")
|
42
42
|
JSON.parse(function_body)[output_adapter.name]
|
43
43
|
end
|
44
|
-
|
45
|
-
private
|
46
|
-
def self.format_properties(output_adapter)
|
47
|
-
output_adapter.properties.each_with_object({}) do |property, hash|
|
48
|
-
hash[property.name] = {
|
49
|
-
type: property.type,
|
50
|
-
description: property.description
|
51
|
-
}
|
52
|
-
|
53
|
-
if property.enum
|
54
|
-
hash[property.name][:enum] = property.enum
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
44
|
end
|
59
45
|
end
|
60
46
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sublayer
|
2
|
+
module Triggers
|
3
|
+
class FileChange < Base
|
4
|
+
def initialize(&block)
|
5
|
+
@block = block
|
6
|
+
end
|
7
|
+
|
8
|
+
def setup(agent)
|
9
|
+
files_to_watch = agent.instance_eval(&@block)
|
10
|
+
folders = files_to_watch.map { |file| File.dirname(File.expand_path(file)) }.uniq
|
11
|
+
|
12
|
+
Listen.to(*folders) do |modified, added, removed|
|
13
|
+
if files_to_watch.any? { |file| modified.include?(File.expand_path(file)) }
|
14
|
+
activate(agent)
|
15
|
+
end
|
16
|
+
end.start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/sublayer/version.rb
CHANGED
data/lib/sublayer.rb
CHANGED
data/sublayer.gemspec
CHANGED
@@ -41,6 +41,6 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_development_dependency "rspec", "~> 3.12"
|
42
42
|
spec.add_development_dependency "pry", "~> 0.14"
|
43
43
|
spec.add_development_dependency "vcr", "~> 6.0"
|
44
|
-
spec.add_development_dependency "webmock", "~> 3
|
44
|
+
spec.add_development_dependency "webmock", "~> 3"
|
45
45
|
spec.add_development_dependency "clag"
|
46
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sublayer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Werner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-openai
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: '3
|
159
|
+
version: '3'
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: '3
|
166
|
+
version: '3'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: clag
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,19 +193,17 @@ files:
|
|
193
193
|
- lib/sublayer/actions/base.rb
|
194
194
|
- lib/sublayer/agents/base.rb
|
195
195
|
- lib/sublayer/components/output_adapters.rb
|
196
|
+
- lib/sublayer/components/output_adapters/formattable.rb
|
197
|
+
- lib/sublayer/components/output_adapters/list_of_strings.rb
|
196
198
|
- lib/sublayer/components/output_adapters/single_string.rb
|
197
199
|
- lib/sublayer/components/output_adapters/string_selection_from_list.rb
|
198
200
|
- lib/sublayer/generators/base.rb
|
199
|
-
- lib/sublayer/generators/examples/code_from_blueprint_generator.rb
|
200
|
-
- lib/sublayer/generators/examples/code_from_description_generator.rb
|
201
|
-
- lib/sublayer/generators/examples/description_from_code_generator.rb
|
202
|
-
- lib/sublayer/generators/examples/invalid_to_valid_json_generator.rb
|
203
|
-
- lib/sublayer/generators/examples/route_selection_from_user_intent_generator.rb
|
204
|
-
- lib/sublayer/generators/examples/sentiment_from_text_generator.rb
|
205
201
|
- lib/sublayer/providers/claude.rb
|
206
202
|
- lib/sublayer/providers/gemini.rb
|
207
203
|
- lib/sublayer/providers/open_ai.rb
|
208
204
|
- lib/sublayer/tasks/base.rb
|
205
|
+
- lib/sublayer/triggers/base.rb
|
206
|
+
- lib/sublayer/triggers/file_change.rb
|
209
207
|
- lib/sublayer/version.rb
|
210
208
|
- sublayer.gemspec
|
211
209
|
homepage: https://docs.sublayer.com
|
@@ -227,11 +225,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
227
225
|
version: 2.6.0
|
228
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
227
|
requirements:
|
230
|
-
- - "
|
228
|
+
- - ">="
|
231
229
|
- !ruby/object:Gem::Version
|
232
|
-
version:
|
230
|
+
version: '0'
|
233
231
|
requirements: []
|
234
|
-
rubygems_version: 3.3
|
232
|
+
rubygems_version: 3.5.3
|
235
233
|
signing_key:
|
236
234
|
specification_version: 4
|
237
235
|
summary: A model-agnostic Ruby GenerativeAI DSL and Framework
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class CodeFromBlueprintGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :single_string,
|
3
|
-
name: "generated_code",
|
4
|
-
description: "The generated code for the description"
|
5
|
-
|
6
|
-
def initialize(blueprint_description:, blueprint_code:, description:)
|
7
|
-
@blueprint_description = blueprint_description
|
8
|
-
@blueprint_code = blueprint_code
|
9
|
-
@description = description
|
10
|
-
end
|
11
|
-
|
12
|
-
def generate
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def prompt
|
17
|
-
<<-PROMPT
|
18
|
-
You are an expert programmer and are great at looking at and understanding existing patterns and applying them to new situations.
|
19
|
-
|
20
|
-
The blueprint we're working with is: #{@blueprint_description}.
|
21
|
-
The code for that blueprint is:
|
22
|
-
#{@blueprint_code}
|
23
|
-
|
24
|
-
You need to use the blueprint above and modify it so that it satisfied the following description:
|
25
|
-
#{@description}
|
26
|
-
|
27
|
-
Take a deep breath and think step by step before you start coding.
|
28
|
-
PROMPT
|
29
|
-
end
|
30
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
class CodeFromDescriptionGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :single_string,
|
3
|
-
name: "generated_code",
|
4
|
-
description: "The generated code in the requested language"
|
5
|
-
|
6
|
-
def initialize(description:, technologies:)
|
7
|
-
@description = description
|
8
|
-
@technologies = technologies
|
9
|
-
end
|
10
|
-
|
11
|
-
def generate
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def prompt
|
16
|
-
<<-PROMPT
|
17
|
-
You are an expert programmer in #{@technologies.join(", ")}.
|
18
|
-
|
19
|
-
You are tasked with writing code using the following technologies: #{@technologies.join(", ")}.
|
20
|
-
|
21
|
-
The description of the task is #{@description}
|
22
|
-
|
23
|
-
Take a deep breath and think step by step before you start coding.
|
24
|
-
PROMPT
|
25
|
-
end
|
26
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class DescriptionFromCodeGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :single_string,
|
3
|
-
name: "code_description",
|
4
|
-
description: "A description of what the code in the file does"
|
5
|
-
|
6
|
-
def initialize(code:)
|
7
|
-
@code = code
|
8
|
-
end
|
9
|
-
|
10
|
-
def generate
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def prompt
|
15
|
-
<<-PROMPT
|
16
|
-
You are an experienced software engineer. Below is a chunk of code:
|
17
|
-
|
18
|
-
#{@code}
|
19
|
-
|
20
|
-
Please read the code carefully and provide a high-level description of what this code does, including its purpose, functionalities, and any noteworthy details.
|
21
|
-
PROMPT
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class InvalidToValidJsonGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :single_string,
|
3
|
-
name: "valid_json",
|
4
|
-
description: "The valid JSON string"
|
5
|
-
|
6
|
-
def initialize(invalid_json:)
|
7
|
-
@invalid_json = invalid_json
|
8
|
-
end
|
9
|
-
|
10
|
-
def generate
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def prompt
|
15
|
-
<<-PROMPT
|
16
|
-
You are an expert in JSON parsing.
|
17
|
-
|
18
|
-
The given string is not a valid JSON: #{@invalid_json}
|
19
|
-
|
20
|
-
Please fix this and produce a valid JSON.
|
21
|
-
PROMPT
|
22
|
-
end
|
23
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class RouteSelectionFromUserIntentGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :string_selection_from_list,
|
3
|
-
name: "route",
|
4
|
-
description: "A route selected from the list",
|
5
|
-
options: :available_routes
|
6
|
-
|
7
|
-
def initialize(user_intent:)
|
8
|
-
@user_intent = user_intent
|
9
|
-
end
|
10
|
-
|
11
|
-
def generate
|
12
|
-
super
|
13
|
-
end
|
14
|
-
|
15
|
-
def available_routes
|
16
|
-
["GET /", "GET /users", "GET /users/:id", "POST /users", "PUT /users/:id", "DELETE /users/:id"]
|
17
|
-
end
|
18
|
-
|
19
|
-
def prompt
|
20
|
-
<<-PROMPT
|
21
|
-
You are skilled at selecting routes based on user intent.
|
22
|
-
|
23
|
-
Your task is to choose a route based on the following intent:
|
24
|
-
|
25
|
-
The user's intent is:
|
26
|
-
#{@user_intent}
|
27
|
-
PROMPT
|
28
|
-
end
|
29
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
class SentimentFromTextGenerator < Sublayer::Generators::Base
|
2
|
-
llm_output_adapter type: :string_selection_from_list,
|
3
|
-
name: "sentiment_value",
|
4
|
-
description: "A sentiment value from the list",
|
5
|
-
options: -> { @sentiment_options }
|
6
|
-
|
7
|
-
def initialize(text:, sentiment_options:)
|
8
|
-
@text = text
|
9
|
-
@sentiment_options = sentiment_options
|
10
|
-
end
|
11
|
-
|
12
|
-
def generate
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
16
|
-
def prompt
|
17
|
-
<<-PROMPT
|
18
|
-
You are an expert at determining sentiment from text.
|
19
|
-
|
20
|
-
You are tasked with analyzing the following text and determining its sentiment value.
|
21
|
-
|
22
|
-
The text is:
|
23
|
-
#{@text}
|
24
|
-
PROMPT
|
25
|
-
end
|
26
|
-
end
|