activeai 0.1.2 → 0.1.4

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: 446cdb7b4d1341e1450486380b0926a68ff594b4361a330f6baa7466ac208c76
4
+ data.tar.gz: 5416c0e23d45fb0c7b7833b3ed8888a7339a5e51fb8bf2f34447283452e3db5b
5
5
  SHA512:
6
- metadata.gz: db6b75600a2cf40c9b872c62a64316ffd36c69bd53856b3b05aed9865332450ed54c4b16690c7225ec313fb86d1634a6baade87cec4ab11464e0f9b4fb48be09
7
- data.tar.gz: 383a17006039e02b64507bba0c95648bef990a17ce7f2ad676ab21fd0505f62a9eada8c5484e7a170215200a770d58e27c924e1a00be397f382d4c4364f39d59
6
+ metadata.gz: 0e5f825a3d3b3c2781cc353462ed74087b5c56bd5492e70dad21f3294328dce63d1e198b28e745f98e503a5483b9eb7f0d41c4a552f680200809753a6527b09e
7
+ data.tar.gz: b7fa0ae8250c6bacd2f5083acfbcbcf2fa58f481721e69b9ce060f5a4fac934b8d6b28b41386ce2bafa6f1cef019543e113b04929fcb7bd2e086858068a38596
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
@@ -102,7 +108,7 @@ controller.call("Pay Mom R127 for groceries")
102
108
  It's possible to instantiate an `ActiveAI::Router`, load up the examples from multiple controllers, and then have it handle many types of requests. It does this in a similar way to how the controller uses an LLM to map to action and params, but it concatenates all controller routing examples and strips out the parameter preparation step for efficiency, since the controller handles this.
103
109
 
104
110
  ```ruby
105
- router = ActiveAI::Router.new
111
+ router = ActiveAI::Router.new # TODO you need to add providers now.. should be optional?
106
112
 
107
113
  # load all auto-detected routes:
108
114
  router.auto_load_routing(Rails.root.join('config','routes')) # loads all .yml files as controller examples
@@ -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,40 @@
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
+ "/* #{example[:description]} */\n#{example[:code]}"
11
+ end + [""]).join("<|endoftext|>")
12
+ end
13
+
14
+ def call(comment)
15
+ prompt = base_prompt
16
+ prompt += "/* #{comment} */\n"
17
+
18
+ complete_result = complete(prompt) # this still breaks sometimes by generating like 50 functions instead of stopping. think i fixed it? forgot an <|endoftext|> at the end
19
+
20
+ # puts prompt
21
+ # puts complete_result
22
+
23
+ completion = complete_result['choices'][0]['text']
24
+ completion = completion.strip.gsub("\n", "\\n") # fixes parsing errors (in JSON??)
25
+
26
+ matcher = /(.*?)\((.*)\)/m # should be made ungreedy, but then i dont see when it over-completes because it fails silently
27
+ matches = matcher.match(completion)
28
+
29
+ if matches.nil?
30
+ # binding.pry
31
+ raise "Unmatched router response in #{self.class}"
32
+ end
33
+
34
+ return {
35
+ text: completion.strip,
36
+ path: matches[1],
37
+ params: matches[2].presence && JSON.parse(matches[2])
38
+ }
39
+ end
40
+ 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,37 @@ 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-cushman-001', temperature: 0)
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
 
20
+ def initialize(provider)
21
+ @provider = provider
22
+ end
23
+
18
24
  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('#')
25
+ # samples to parse:
26
+ # plugins.slack.send_message({ \"channel\": \"#general\", \"text\": \"Hi\" })
27
+ # unmatched()
28
+
29
+ function = self.class.routing_behavior.call(request)
30
+ *controller_path, action_name = function[:path].split(".")
31
+ controller_name = controller_path.join("/").presence
32
+
21
33
  # 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
- }
34
+
35
+ if controller_name.present?
36
+ return {
37
+ action: action_name,
38
+ params: function[:params]
39
+ }
40
+ else
41
+ return nil
42
+ end
26
43
  end
27
44
 
28
45
  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-cushman-001', temperature: 0)
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
@@ -53,11 +69,18 @@ class ActiveAI::Router
53
69
  end
54
70
  end
55
71
 
56
- def call(request)
57
- if controller = find_controller(request)
58
- controller.new.call(request)
59
- else
60
- return nil
61
- end
62
- end
72
+ # This stopped being necessary, because we find controller and call it elsewhere:
73
+ # def call(request, providers=nil)
74
+ # if controller = find_controller(request)
75
+
76
+ # provider = if providers.respond_to?(:find_by)
77
+ # provider_name = controller.to_s.gsub("Plugins::", "").gsub("Controller","")
78
+ # providers.find_by(name: provider_name)
79
+ # end
80
+
81
+ # controller.new(provider).call(request)
82
+ # else
83
+ # return nil
84
+ # end
85
+ # end
63
86
  end
@@ -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.4"
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,14 +1,14 @@
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.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - jeriko
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-06 00:00:00.000000000 Z
11
+ date: 2023-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -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