activeai 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5a985f1783335da05fa3165fbe352823a431d206c2586a334eff561874e88d2
4
- data.tar.gz: bce08aa0e01e59e780ad8a47fd632e10567ae7c25dd8d88b295f61344d4a044c
3
+ metadata.gz: a327faafb1ed168dfbbfce2c3f5beed3b6090c8ed5c95d24913d988e39227143
4
+ data.tar.gz: f3bb5b04eabc7838193f099e649f5dbe48ae3f151537b28469d1b6738848fb00
5
5
  SHA512:
6
- metadata.gz: db6b75600a2cf40c9b872c62a64316ffd36c69bd53856b3b05aed9865332450ed54c4b16690c7225ec313fb86d1634a6baade87cec4ab11464e0f9b4fb48be09
7
- data.tar.gz: 383a17006039e02b64507bba0c95648bef990a17ce7f2ad676ab21fd0505f62a9eada8c5484e7a170215200a770d58e27c924e1a00be397f382d4c4364f39d59
6
+ metadata.gz: c3530fdce13cab64414b9b921a49672cd2dae8c170e6f50af5486fe30b33055403a1ef37b6a8c2e3172178ff61ac8c4d26292a3708877111d329cecb0dc59a72
7
+ data.tar.gz: 9dc7550488f4dab988b11719a065f0e7fd0c17afa2712c6670b97e8300969fb9d218ac47e2939d295134e5313594f00f45e8838c34c5d9fb178237e3292adcce
data/README.md CHANGED
@@ -39,6 +39,12 @@ puts result
39
39
  #=> 'tomatoes, jam'
40
40
  ```
41
41
 
42
+ ### Behavior: WriteFunctionCall
43
+
44
+ #### TODO
45
+
46
+ This lets you use `code-davinci-002` or `code-cushman-001` to run logic. The router uses this internally. Supply a list of example pairs that are a "description" and "code", and then you can complete another one.
47
+
42
48
  ### TODO: with other patterns
43
49
 
44
50
  ### TODO: auto-detected behavior pattern from config
@@ -20,7 +20,7 @@ class ActiveAI::Behavior::LLM::Conversation < ActiveAI::Behavior::LLM
20
20
  # TODO use the label key they provide in the yml file
21
21
  end,
22
22
  "Conversation:\n" + @state['conversation']
23
- ].join(SEPARATOR)
23
+ ].join(LINE_SEPARATOR)
24
24
  end
25
25
 
26
26
  def add(speaker, message)
@@ -15,12 +15,12 @@ class ActiveAI::Behavior::LLM::FollowStructuredExamples < ActiveAI::Behavior::LL
15
15
  example.map do |key, value|
16
16
  "#{key}: #{value}"
17
17
  end.join("\n")
18
- end.join(SEPARATOR)
19
- ].join(SEPARATOR)
18
+ end.join(LINE_SEPARATOR)
19
+ ].join(LINE_SEPARATOR)
20
20
  end
21
21
 
22
22
  def call(input={}, extract: []) # TODO cool splat stuff?
23
- prompt = base_prompt + SEPARATOR
23
+ prompt = base_prompt + LINE_SEPARATOR
24
24
 
25
25
  prompt += input.map do |key, value|
26
26
  "#{key}: #{value}"
@@ -0,0 +1,35 @@
1
+ class ActiveAI::Behavior::LLM::WriteFunctionCall < ActiveAI::Behavior::LLM
2
+ def initialize(llm, state)
3
+ super(llm)
4
+ @state = state
5
+ # TODO raise errors if not expected thingies available in the config
6
+ end
7
+
8
+ def base_prompt
9
+ @state[:examples].map do |example|
10
+ [
11
+ "// #{example[:description]}",
12
+ example[:code]
13
+ ].join("\n")
14
+ end.join("\n\n") + "\n\n"
15
+ end
16
+
17
+ def call(comment)
18
+ prompt = base_prompt + "\n\n"
19
+ prompt += "//#{comment}\n"
20
+ complete_result = complete(prompt, stop: "\n")
21
+
22
+ # TODO stop \n works for the router but not for other stuff, later
23
+
24
+ completion = complete_result['choices'][0]['text']
25
+
26
+ matcher = /(.*)\((.*)\)/
27
+ matches = matcher.match(completion)
28
+
29
+ return {
30
+ text: completion.strip,
31
+ path: matches[1],
32
+ params: matches[2].presence && JSON.parse(matches[2])
33
+ }
34
+ end
35
+ end
@@ -7,7 +7,7 @@ class ActiveAI::Behavior::LLM < ActiveAI::Behavior::Base
7
7
  @llm.complete(prompt: prompt, stop: stop)
8
8
  end
9
9
 
10
- SEPARATOR = "\n\n###\n\n"
10
+ LINE_SEPARATOR = "\n\n###\n\n"
11
11
 
12
12
  def extract_keys(completion, extract)
13
13
  matcher_string = extract.map{ |key| "#{key}:(.*)" }.join
@@ -25,5 +25,6 @@ class ActiveAI::Behavior::LLM < ActiveAI::Behavior::Base
25
25
  end
26
26
 
27
27
  require_relative "llm/conversation"
28
- require_relative "llm/unstructured"
29
28
  require_relative "llm/follow_structured_examples"
29
+ require_relative "llm/unstructured"
30
+ require_relative "llm/write_function_call"
@@ -9,20 +9,33 @@ class ActiveAI::Controller
9
9
  end
10
10
 
11
11
  def self.load_routing(routes_config)
12
- @llm = ActiveAI::NeuralNetwork::GPT3.new(ActiveAI.config[:gpt3_token], model: 'text-curie-001', temperature: 0.2)
13
- self.routing_behavior = ActiveAI::Behavior::LLM::FollowStructuredExamples.new(@llm, routes_config)
12
+ @llm = ActiveAI::NeuralNetwork::GPT3.new(ActiveAI.config[:gpt3_token], model: 'code-davinci-002', temperature: 0.2)
13
+
14
+ examples = ActiveAI.route_examples_to_function_call_examples(routes_config['examples'])
15
+ self.routing_behavior = ActiveAI::Behavior::LLM::WriteFunctionCall.new(@llm, { examples: examples })
14
16
  end
15
17
 
16
18
  attr_accessor :params
17
19
 
18
20
  def prepare_action(request)
19
- routing = self.class.routing_behavior.call({ 'Request' => request }, extract: %W[Route Params])
20
- controller_name, action_name = routing['Route'].split('#')
21
+ # samples to parse:
22
+ # plugins.slack.send_message({ \"channel\": \"#general\", \"text\": \"Hi\" })
23
+ # unmatched()
24
+
25
+ function = self.class.routing_behavior.call(request)
26
+ *controller_path, action_name = function[:path].split(".")
27
+ controller_name = controller_path.join("/").presence
28
+
21
29
  # TODO verify it's the right controller and the action name exists and it's not a reserved / internal thing
22
- return {
23
- action: action_name,
24
- params: JSON.parse(routing['Params']) # TODO cast as JSON earlier? e.g. in config of the behavior?
25
- }
30
+
31
+ if controller_name.present?
32
+ return {
33
+ action: action_name,
34
+ params: function[:params]
35
+ }
36
+ else
37
+ return nil
38
+ end
26
39
  end
27
40
 
28
41
  def call(request)
@@ -4,7 +4,7 @@ class ActiveAI::Router
4
4
 
5
5
  def initialize
6
6
  @routings = []
7
- @llm = ActiveAI::NeuralNetwork::GPT3.new(ActiveAI.config[:gpt3_token], model: 'text-curie-001', temperature: 0.2)
7
+ @llm = ActiveAI::NeuralNetwork::GPT3.new(ActiveAI.config[:gpt3_token], model: 'code-davinci-002', temperature: 0.2)
8
8
  end
9
9
 
10
10
  def add_controller_routing(routing)
@@ -24,26 +24,42 @@ class ActiveAI::Router
24
24
  end
25
25
 
26
26
  def behavior
27
- config = {
28
- 'instruction' => INSTRUCTION,
29
- 'examples' => [UNMATCHED] + @routings.map do |routing|
30
- routing['examples'].reject do |example|
31
- example['Route'] == 'None'
32
- end.map do |example|
33
- example.slice('Match', 'Route')
34
- end
35
- end.flatten
36
- }
37
-
38
- ActiveAI::Behavior::LLM::FollowStructuredExamples.new(@llm, config)
27
+ raw_examples = [UNMATCHED] + @routings.map do |routing|
28
+ routing['examples'].reject do |example|
29
+ example['Route'] == 'None'
30
+ end.map do |example|
31
+ example.slice('Match', 'Route')
32
+ end
33
+ end.flatten
34
+ examples = ActiveAI.route_examples_to_function_call_examples(raw_examples)
35
+
36
+ ActiveAI::Behavior::LLM::WriteFunctionCall.new(@llm, { examples: examples })
39
37
  end
40
38
 
39
+ # def behavior_via_structured_examples
40
+ # config = {
41
+ # 'instruction' => INSTRUCTION,
42
+ # 'examples' => [UNMATCHED] + @routings.map do |routing|
43
+ # routing['examples'].reject do |example|
44
+ # example['Route'] == 'None'
45
+ # end.map do |example|
46
+ # example.slice('Match', 'Route')
47
+ # end
48
+ # end.flatten
49
+ # }
50
+
51
+ # ActiveAI::Behavior::LLM::FollowStructuredExamples.new(@llm, config)
52
+ # end
53
+
41
54
  def find_controller(request)
42
- # should return constantized maybe?
43
- routing = behavior.call({ 'Request' => request }, extract: %W[Route])
44
- controller_name, action_name = routing['Route'].split('#')
55
+ function = behavior.call(request) # TODO maybe the behavior should return function and params as well. seems right
45
56
 
46
- if controller_name == "None" || action_name.blank?
57
+ *controller_path, action_name = function[:path].split(".")
58
+ controller_name = controller_path.join("/").presence
59
+
60
+ # TODO verify it's the right controller and the action name exists and it's not a reserved / internal thing
61
+
62
+ if controller_name.blank? || action_name.blank? || action_name == 'unmatched'
47
63
  return nil
48
64
  else
49
65
  return (controller_name + "_controller").classify.constantize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveAI
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/activeai.rb CHANGED
@@ -16,5 +16,17 @@ module ActiveAI
16
16
  }
17
17
  end
18
18
 
19
+ def self.route_examples_to_function_call_examples(examples)
20
+ examples.map do |example|
21
+ function = example['Route'].gsub('/','.').gsub('#','.')
22
+ function = "unmatched" if function == "None"
23
+
24
+ {
25
+ description: example['Match'],
26
+ code: "#{function}(#{example['Params']&.strip})"
27
+ }
28
+ end
29
+ end
30
+
19
31
  end
20
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - jeriko
@@ -64,6 +64,7 @@ files:
64
64
  - lib/activeai/behavior/llm/conversation.rb
65
65
  - lib/activeai/behavior/llm/follow_structured_examples.rb
66
66
  - lib/activeai/behavior/llm/unstructured.rb
67
+ - lib/activeai/behavior/llm/write_function_call.rb
67
68
  - lib/activeai/configuration.rb
68
69
  - lib/activeai/controller.rb
69
70
  - lib/activeai/neural_network.rb