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 +4 -4
- data/README.md +7 -1
- data/lib/activeai/behavior/llm/conversation.rb +1 -1
- data/lib/activeai/behavior/llm/follow_structured_examples.rb +3 -3
- data/lib/activeai/behavior/llm/write_function_call.rb +40 -0
- data/lib/activeai/behavior/llm.rb +3 -2
- data/lib/activeai/controller.rb +25 -8
- data/lib/activeai/router.rb +47 -24
- data/lib/activeai/version.rb +1 -1
- data/lib/activeai.rb +12 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 446cdb7b4d1341e1450486380b0926a68ff594b4361a330f6baa7466ac208c76
|
4
|
+
data.tar.gz: 5416c0e23d45fb0c7b7833b3ed8888a7339a5e51fb8bf2f34447283452e3db5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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(
|
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(
|
19
|
-
].join(
|
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 +
|
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
|
-
|
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"
|
data/lib/activeai/controller.rb
CHANGED
@@ -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: '
|
13
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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)
|
data/lib/activeai/router.rb
CHANGED
@@ -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: '
|
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
|
-
|
28
|
-
'
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
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
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
data/lib/activeai/version.rb
CHANGED
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.
|
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-
|
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
|