scout-ai 0.2.0 → 1.0.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/.vimproject +91 -10
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/scout-ai +2 -0
- data/lib/scout/llm/agent/chat.rb +24 -0
- data/lib/scout/llm/agent.rb +13 -13
- data/lib/scout/llm/ask.rb +26 -16
- data/lib/scout/llm/backends/bedrock.rb +129 -0
- data/lib/scout/llm/backends/huggingface.rb +6 -21
- data/lib/scout/llm/backends/ollama.rb +69 -36
- data/lib/scout/llm/backends/openai.rb +85 -35
- data/lib/scout/llm/backends/openwebui.rb +1 -1
- data/lib/scout/llm/backends/relay.rb +3 -2
- data/lib/scout/llm/backends/responses.rb +272 -0
- data/lib/scout/llm/chat.rb +547 -0
- data/lib/scout/llm/parse.rb +70 -13
- data/lib/scout/llm/tools.rb +126 -5
- data/lib/scout/llm/utils.rb +17 -10
- data/lib/scout/model/base.rb +19 -0
- data/lib/scout/model/python/base.rb +25 -0
- data/lib/scout/model/python/huggingface/causal/next_token.rb +23 -0
- data/lib/scout/model/python/huggingface/causal.rb +29 -0
- data/lib/scout/model/python/huggingface/classification +0 -0
- data/lib/scout/model/python/huggingface/classification.rb +50 -0
- data/lib/scout/model/python/huggingface.rb +112 -0
- data/lib/scout/model/python/torch/dataloader.rb +57 -0
- data/lib/scout/model/python/torch/helpers.rb +84 -0
- data/lib/scout/model/python/torch/introspection.rb +34 -0
- data/lib/scout/model/python/torch/load_and_save.rb +47 -0
- data/lib/scout/model/python/torch.rb +94 -0
- data/lib/scout/model/util/run.rb +181 -0
- data/lib/scout/model/util/save.rb +81 -0
- data/lib/scout-ai.rb +3 -1
- data/python/scout_ai/__init__.py +35 -0
- data/python/scout_ai/__pycache__/__init__.cpython-310.pyc +0 -0
- data/python/scout_ai/__pycache__/__init__.cpython-311.pyc +0 -0
- data/python/scout_ai/__pycache__/huggingface.cpython-310.pyc +0 -0
- data/python/scout_ai/__pycache__/huggingface.cpython-311.pyc +0 -0
- data/python/scout_ai/__pycache__/util.cpython-310.pyc +0 -0
- data/python/scout_ai/__pycache__/util.cpython-311.pyc +0 -0
- data/python/scout_ai/atcold/__init__.py +0 -0
- data/python/scout_ai/atcold/plot_lib.py +141 -0
- data/python/scout_ai/atcold/spiral.py +27 -0
- data/python/scout_ai/huggingface/data.py +48 -0
- data/python/scout_ai/huggingface/eval.py +60 -0
- data/python/scout_ai/huggingface/model.py +29 -0
- data/python/scout_ai/huggingface/rlhf.py +83 -0
- data/python/scout_ai/huggingface/train/__init__.py +34 -0
- data/python/scout_ai/huggingface/train/__pycache__/__init__.cpython-310.pyc +0 -0
- data/python/scout_ai/huggingface/train/__pycache__/next_token.cpython-310.pyc +0 -0
- data/python/scout_ai/huggingface/train/next_token.py +315 -0
- data/python/scout_ai/language_model.py +70 -0
- data/python/scout_ai/util.py +32 -0
- data/scout-ai.gemspec +130 -0
- data/scout_commands/agent/ask +133 -15
- data/scout_commands/agent/kb +15 -0
- data/scout_commands/llm/ask +71 -12
- data/scout_commands/llm/process +4 -2
- data/test/data/cat.jpg +0 -0
- data/test/scout/llm/agent/test_chat.rb +14 -0
- data/test/scout/llm/backends/test_bedrock.rb +60 -0
- data/test/scout/llm/backends/test_huggingface.rb +3 -3
- data/test/scout/llm/backends/test_ollama.rb +48 -10
- data/test/scout/llm/backends/test_openai.rb +96 -11
- data/test/scout/llm/backends/test_responses.rb +115 -0
- data/test/scout/llm/test_ask.rb +1 -0
- data/test/scout/llm/test_chat.rb +214 -0
- data/test/scout/llm/test_parse.rb +81 -2
- data/test/scout/model/python/huggingface/causal/test_next_token.rb +59 -0
- data/test/scout/model/python/huggingface/test_causal.rb +33 -0
- data/test/scout/model/python/huggingface/test_classification.rb +30 -0
- data/test/scout/model/python/test_base.rb +44 -0
- data/test/scout/model/python/test_huggingface.rb +9 -0
- data/test/scout/model/python/test_torch.rb +71 -0
- data/test/scout/model/python/torch/test_helpers.rb +14 -0
- data/test/scout/model/test_base.rb +117 -0
- data/test/scout/model/util/test_save.rb +31 -0
- metadata +72 -5
- data/questions/coach +0 -2
@@ -2,7 +2,7 @@ require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
|
2
2
|
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
3
|
|
4
4
|
class TestLLMOpenAI < Test::Unit::TestCase
|
5
|
-
def
|
5
|
+
def _test_ask
|
6
6
|
prompt =<<-EOF
|
7
7
|
system: you are a coding helper that only write code and comments without formatting so that it can work directly, avoid the initial and end commas ```.
|
8
8
|
user: write a script that sorts files in a directory
|
@@ -11,24 +11,95 @@ user: write a script that sorts files in a directory
|
|
11
11
|
ppp LLM::OpenAI.ask prompt
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def __test_embeddings
|
15
|
+
Log.severity = 0
|
16
|
+
text =<<-EOF
|
17
|
+
Some text
|
18
|
+
EOF
|
19
|
+
emb = LLM::OpenAI.embed text, log_errors: true, model: 'embedding-model'
|
20
|
+
|
21
|
+
assert(Float === emb.first)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _test_tool_call_output
|
25
|
+
Log.severity = 0
|
15
26
|
prompt =<<-EOF
|
16
|
-
|
27
|
+
function_call:
|
28
|
+
|
29
|
+
{"type":"function","function":{"name":"Baking-bake_muffin_tray","arguments":"{}"},"id":"Baking_bake_muffin_tray_Default"}
|
30
|
+
|
31
|
+
function_call_output:
|
32
|
+
|
33
|
+
{"id":"Baking_bake_muffin_tray_Default","role":"tool","content":"Baking batter (Mixing base (Whisking eggs from share/pantry/eggs) with mixer (share/pantry/flour))"}
|
34
|
+
|
35
|
+
user:
|
36
|
+
|
37
|
+
How do you bake muffins, according to the tool I provided you. Don't
|
38
|
+
tell me the recipe you already know, use the tool call output. Let me
|
39
|
+
know if you didn't get it.
|
17
40
|
EOF
|
18
|
-
|
41
|
+
ppp LLM::OpenAI.ask prompt, model: 'gpt-4.1-nano'
|
19
42
|
end
|
20
43
|
|
21
|
-
def
|
44
|
+
def _test_tool_call_output_2
|
22
45
|
Log.severity = 0
|
23
|
-
|
24
|
-
|
46
|
+
prompt =<<-EOF
|
47
|
+
function_call:
|
48
|
+
|
49
|
+
{"name":"get_current_temperature", "arguments":{"location":"London","unit":"Celsius"},"id":"tNTnsQq2s6jGh0npOh43AwDD"}
|
50
|
+
|
51
|
+
function_call_output:
|
52
|
+
|
53
|
+
{"id":"tNTnsQq2s6jGh0npOh43AwDD", "content":"It's 15 degrees and raining."}
|
54
|
+
|
55
|
+
user:
|
56
|
+
|
57
|
+
should i take an umbrella?
|
25
58
|
EOF
|
26
|
-
|
27
|
-
assert(Float === emb.first)
|
59
|
+
ppp LLM::OpenAI.ask prompt, model: 'gpt-4.1-nano'
|
28
60
|
end
|
29
61
|
|
30
|
-
def
|
62
|
+
def _test_tool_call_output_features
|
63
|
+
Log.severity = 0
|
31
64
|
prompt =<<-EOF
|
65
|
+
function_call:
|
66
|
+
|
67
|
+
{"name":"Baking-bake_muffin_tray","arguments":{},"id":"Baking_bake_muffin_tray_Default"}
|
68
|
+
|
69
|
+
function_call_output:
|
70
|
+
|
71
|
+
{"id":"Baking_bake_muffin_tray_Default","content":"Baking batter (Mixing base (Whisking eggs from share/pantry/eggs) with mixer (share/pantry/flour))"}
|
72
|
+
|
73
|
+
user:
|
74
|
+
|
75
|
+
How do you bake muffins, according to the tool I provided you. Don't
|
76
|
+
tell me the recipe you already know, use the tool call output. Let me
|
77
|
+
know if you didn't get it.
|
78
|
+
EOF
|
79
|
+
ppp LLM::OpenAI.ask prompt, model: 'gpt-4.1-nano'
|
80
|
+
end
|
81
|
+
|
82
|
+
def _test_tool_call_output_weather
|
83
|
+
Log.severity = 0
|
84
|
+
prompt =<<-EOF
|
85
|
+
function_call:
|
86
|
+
|
87
|
+
{"name":"get_current_temperature", "arguments":{"location":"London","unit":"Celsius"},"id":"tNTnsQq2s6jGh0npOh43AwDD"}
|
88
|
+
|
89
|
+
function_call_output:
|
90
|
+
|
91
|
+
{"id":"tNTnsQq2s6jGh0npOh43AwDD", "content":"It's 15 degrees and raining."}
|
92
|
+
|
93
|
+
user:
|
94
|
+
|
95
|
+
should i take an umbrella?
|
96
|
+
EOF
|
97
|
+
ppp LLM::OpenAI.ask prompt, model: 'gpt-4.1-nano'
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_tool
|
101
|
+
prompt =<<-EOF
|
102
|
+
user:
|
32
103
|
What is the weather in London. Should I take my umbrella?
|
33
104
|
EOF
|
34
105
|
|
@@ -58,11 +129,25 @@ What is the weather in London. Should I take my umbrella?
|
|
58
129
|
]
|
59
130
|
|
60
131
|
sss 0
|
61
|
-
respose = LLM::OpenAI.ask prompt, tool_choice: 'required', tools: tools, model: "gpt-
|
132
|
+
respose = LLM::OpenAI.ask prompt, tool_choice: 'required', tools: tools, model: "gpt-4.1-mini", log_errors: true do |name,arguments|
|
62
133
|
"It's 15 degrees and raining."
|
63
134
|
end
|
64
135
|
|
65
136
|
ppp respose
|
66
137
|
end
|
138
|
+
|
139
|
+
def _test_json_output
|
140
|
+
prompt =<<-EOF
|
141
|
+
system:
|
142
|
+
|
143
|
+
Respond in json format with a hash of strings as keys and string arrays as values, at most three in length
|
144
|
+
|
145
|
+
user:
|
146
|
+
|
147
|
+
What other movies have the protagonists of the original gost busters played on, just the top.
|
148
|
+
EOF
|
149
|
+
sss 0
|
150
|
+
ppp LLM::OpenAI.ask prompt, format: :json
|
151
|
+
end
|
67
152
|
end
|
68
153
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
2
|
+
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
|
+
|
4
|
+
class TestLLMResponses < Test::Unit::TestCase
|
5
|
+
def test_ask
|
6
|
+
prompt =<<-EOF
|
7
|
+
system: you are a coding helper that only write code and comments without formatting so that it can work directly, avoid the initial and end commas ```.
|
8
|
+
user: write a script that sorts files in a directory
|
9
|
+
EOF
|
10
|
+
sss 0
|
11
|
+
ppp LLM::Responses.ask prompt, model: 'gpt-4.1-nano'
|
12
|
+
end
|
13
|
+
|
14
|
+
def __test_embeddings
|
15
|
+
Log.severity = 0
|
16
|
+
text =<<-EOF
|
17
|
+
Some text
|
18
|
+
EOF
|
19
|
+
emb = LLM::Responses.embed text, log_errors: true
|
20
|
+
assert(Float === emb.first)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_tool_call_output_weather
|
24
|
+
Log.severity = 0
|
25
|
+
prompt =<<-EOF
|
26
|
+
function_call:
|
27
|
+
|
28
|
+
{"name":"get_current_temperature", "arguments":{"location":"London","unit":"Celsius"},"id":"tNTnsQq2s6jGh0npOh43AwDD"}
|
29
|
+
|
30
|
+
function_call_output:
|
31
|
+
|
32
|
+
{"id":"tNTnsQq2s6jGh0npOh43AwDD", "content":"It's 15 degrees and raining."}
|
33
|
+
|
34
|
+
user:
|
35
|
+
|
36
|
+
should i take an umbrella?
|
37
|
+
EOF
|
38
|
+
ppp LLM::Responses.ask prompt, model: 'gpt-4.1-nano'
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_tool
|
42
|
+
prompt =<<-EOF
|
43
|
+
user:
|
44
|
+
What is the weather in London. Should I take my umbrella?
|
45
|
+
EOF
|
46
|
+
|
47
|
+
tools = [
|
48
|
+
{
|
49
|
+
"type": "function",
|
50
|
+
"name": "get_current_temperature",
|
51
|
+
"description": "Get the current temperature and raining conditions for a specific location",
|
52
|
+
"parameters": {
|
53
|
+
"type": "object",
|
54
|
+
"properties": {
|
55
|
+
"location": {
|
56
|
+
"type": "string",
|
57
|
+
"description": "The city and state, e.g., San Francisco, CA"
|
58
|
+
},
|
59
|
+
"unit": {
|
60
|
+
"type": "string",
|
61
|
+
"enum": ["Celsius", "Fahrenheit"],
|
62
|
+
"description": "The temperature unit to use. Infer this from the user's location."
|
63
|
+
}
|
64
|
+
},
|
65
|
+
"required": ["location", "unit"]
|
66
|
+
}
|
67
|
+
},
|
68
|
+
]
|
69
|
+
|
70
|
+
sss 1
|
71
|
+
respose = LLM::Responses.ask prompt, tool_choice: 'required', tools: tools, model: "gpt-4.1-nano", log_errors: true do |name,arguments|
|
72
|
+
"It's 15 degrees and raining."
|
73
|
+
end
|
74
|
+
|
75
|
+
ppp respose
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_news
|
79
|
+
prompt =<<-EOF
|
80
|
+
websearch: true
|
81
|
+
|
82
|
+
user:
|
83
|
+
|
84
|
+
What was the top new in the US today?
|
85
|
+
EOF
|
86
|
+
ppp LLM::Responses.ask prompt
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_image
|
90
|
+
prompt =<<-EOF
|
91
|
+
image: #{datafile_test 'cat.jpg'}
|
92
|
+
|
93
|
+
user:
|
94
|
+
|
95
|
+
What animal is represented in the image?
|
96
|
+
EOF
|
97
|
+
sss 0
|
98
|
+
ppp LLM::Responses.ask prompt
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_json_output
|
102
|
+
prompt =<<-EOF
|
103
|
+
system:
|
104
|
+
|
105
|
+
Respond in json format with a hash of strings as keys and string arrays as values, at most three in length
|
106
|
+
|
107
|
+
user:
|
108
|
+
|
109
|
+
What other movies have the protagonists of the original gost busters played on, just the top.
|
110
|
+
EOF
|
111
|
+
sss 0
|
112
|
+
ppp LLM::Responses.ask prompt, format: :json
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
data/test/scout/llm/test_ask.rb
CHANGED
@@ -0,0 +1,214 @@
|
|
1
|
+
require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
2
|
+
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
|
+
|
4
|
+
class TestMessages < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_short
|
7
|
+
|
8
|
+
question =<<-EOF
|
9
|
+
Hi
|
10
|
+
EOF
|
11
|
+
|
12
|
+
iii LLM.chat(question)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_inline
|
16
|
+
question =<<-EOF
|
17
|
+
system:
|
18
|
+
|
19
|
+
you are a terse assistant that only write in short sentences
|
20
|
+
|
21
|
+
assistant:
|
22
|
+
|
23
|
+
Here is some stuff
|
24
|
+
|
25
|
+
user: feedback
|
26
|
+
|
27
|
+
that continues here
|
28
|
+
EOF
|
29
|
+
|
30
|
+
iii LLM.chat(question)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_messages
|
34
|
+
question =<<-EOF
|
35
|
+
system:
|
36
|
+
|
37
|
+
you are a terse assistant that only write in short sentences
|
38
|
+
|
39
|
+
user:
|
40
|
+
|
41
|
+
What is the capital of France
|
42
|
+
|
43
|
+
assistant:
|
44
|
+
|
45
|
+
Paris
|
46
|
+
|
47
|
+
user:
|
48
|
+
|
49
|
+
is this the national anthem
|
50
|
+
|
51
|
+
[[
|
52
|
+
corous: Viva Espagna
|
53
|
+
]]
|
54
|
+
|
55
|
+
assistant:
|
56
|
+
|
57
|
+
no
|
58
|
+
|
59
|
+
user:
|
60
|
+
|
61
|
+
import: math.system
|
62
|
+
|
63
|
+
consider this file
|
64
|
+
|
65
|
+
<file name=foo_bar>
|
66
|
+
foo: bar
|
67
|
+
</file>
|
68
|
+
|
69
|
+
how many characters does it hold
|
70
|
+
|
71
|
+
assistant:
|
72
|
+
|
73
|
+
8
|
74
|
+
EOF
|
75
|
+
|
76
|
+
messages = LLM.messages question
|
77
|
+
refute messages.collect{|i| i[:role] }.include?("corous")
|
78
|
+
assert messages.collect{|i| i[:role] }.include?("import")
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_chat_import
|
82
|
+
file1 =<<-EOF
|
83
|
+
system: You are an assistant
|
84
|
+
EOF
|
85
|
+
|
86
|
+
file2 =<<-EOF
|
87
|
+
import: header
|
88
|
+
user: say something
|
89
|
+
EOF
|
90
|
+
|
91
|
+
TmpFile.with_path do |tmpdir|
|
92
|
+
tmpdir.header.write file1
|
93
|
+
tmpdir.chat.write file2
|
94
|
+
|
95
|
+
chat = LLM.chat tmpdir.chat
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_clear
|
100
|
+
question =<<-EOF
|
101
|
+
system:
|
102
|
+
|
103
|
+
you are a terse assistant that only write in short sentences
|
104
|
+
|
105
|
+
clear:
|
106
|
+
|
107
|
+
user:
|
108
|
+
|
109
|
+
What is the capital of France
|
110
|
+
EOF
|
111
|
+
|
112
|
+
TmpFile.with_file question do |file|
|
113
|
+
messages = LLM.chat file
|
114
|
+
refute messages.collect{|m| m[:role] }.include?('system')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def __test_job
|
119
|
+
question =<<-EOF
|
120
|
+
system:
|
121
|
+
|
122
|
+
you are a terse assistant that only write in short sentences
|
123
|
+
|
124
|
+
job: Baking/bake_muffin_tray/Default_08a1812eca3a18dce2232509dabc9b41
|
125
|
+
|
126
|
+
How are muffins made
|
127
|
+
|
128
|
+
EOF
|
129
|
+
|
130
|
+
TmpFile.with_file question do |file|
|
131
|
+
messages = LLM.chat file
|
132
|
+
ppp LLM.print messages
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def test_task
|
138
|
+
question =<<-EOF
|
139
|
+
system:
|
140
|
+
|
141
|
+
you are a terse assistant that only write in short sentences
|
142
|
+
|
143
|
+
user:
|
144
|
+
|
145
|
+
task: Baking bake_muffin_tray blueberries=true title="This is a title" list=one,two,"and three"
|
146
|
+
|
147
|
+
How are muffins made?
|
148
|
+
|
149
|
+
EOF
|
150
|
+
|
151
|
+
TmpFile.with_file question do |file|
|
152
|
+
messages = LLM.chat file
|
153
|
+
ppp LLM.print messages
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_structure
|
158
|
+
require 'scout/llm/ask'
|
159
|
+
sss 0
|
160
|
+
question =<<-EOF
|
161
|
+
system:
|
162
|
+
|
163
|
+
Respond in json format with a hash of strings as keys and string arrays as values, at most three in length
|
164
|
+
|
165
|
+
endpoint: sambanova
|
166
|
+
|
167
|
+
What other movies have the protagonists of the original gost busters played on, just the top.
|
168
|
+
|
169
|
+
EOF
|
170
|
+
|
171
|
+
TmpFile.with_file question do |file|
|
172
|
+
ppp LLM.ask file
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_tools
|
177
|
+
require 'scout/llm/ask'
|
178
|
+
|
179
|
+
question =<<-EOF
|
180
|
+
user:
|
181
|
+
|
182
|
+
Use the provided tool to learn the instructions of baking a tray of muffins. Don't
|
183
|
+
give me your own recipe, return the one provided by the tool
|
184
|
+
|
185
|
+
tool: Baking bake_muffin_tray
|
186
|
+
EOF
|
187
|
+
|
188
|
+
TmpFile.with_file question do |file|
|
189
|
+
ppp LLM.ask file
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_knowledge_base
|
194
|
+
require 'scout/llm/ask'
|
195
|
+
sss 0
|
196
|
+
question =<<-EOF
|
197
|
+
system:
|
198
|
+
|
199
|
+
Query the knowledge base of familiar relationships to answer the question
|
200
|
+
|
201
|
+
user:
|
202
|
+
|
203
|
+
Who is Miki's brother in law?
|
204
|
+
|
205
|
+
association: brothers #{datafile_test(:person).brothers} undirected=true
|
206
|
+
association: marriages #{datafile_test(:person).marriages} undirected=true source="=>Alias" target="=>Alias"
|
207
|
+
EOF
|
208
|
+
|
209
|
+
TmpFile.with_file question do |file|
|
210
|
+
ppp LLM.ask file
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
@@ -4,8 +4,9 @@ require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1
|
|
4
4
|
class TestLLMParse < Test::Unit::TestCase
|
5
5
|
def test_parse
|
6
6
|
text=<<-EOF
|
7
|
+
hi
|
7
8
|
system: you are an asistant
|
8
|
-
user: Given the contents of this file:
|
9
|
+
user: Given the contents of this file:[[
|
9
10
|
line 1: 1
|
10
11
|
line 2: 2
|
11
12
|
line 3: 3
|
@@ -13,7 +14,85 @@ line 3: 3
|
|
13
14
|
Show me the lines in reverse order
|
14
15
|
EOF
|
15
16
|
|
16
|
-
|
17
|
+
assert_include LLM.parse(text).first[:content], 'hi'
|
18
|
+
assert_include LLM.parse(text).last[:content], 'reverse'
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_code
|
22
|
+
text=<<-EOF
|
23
|
+
hi
|
24
|
+
system: you are an asistant
|
25
|
+
user: Given the contents of this file:
|
26
|
+
```yaml
|
27
|
+
key: value
|
28
|
+
key2: value2
|
29
|
+
```
|
30
|
+
Show me the lines in reverse order
|
31
|
+
EOF
|
32
|
+
|
33
|
+
assert_include LLM.parse(text).last[:content], 'key2'
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_lines
|
37
|
+
text=<<-EOF
|
38
|
+
system: you are an asistant
|
39
|
+
user: I have a question
|
40
|
+
EOF
|
41
|
+
|
42
|
+
assert_include LLM.parse(text).last[:content], 'question'
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_blocks
|
46
|
+
text=<<-EOF
|
47
|
+
system:
|
48
|
+
|
49
|
+
you are an asistant
|
50
|
+
|
51
|
+
user:
|
52
|
+
|
53
|
+
I have a question
|
54
|
+
|
55
|
+
EOF
|
56
|
+
|
57
|
+
assert_include LLM.parse(text).last[:content], 'question'
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_no_role
|
61
|
+
text=<<-EOF
|
62
|
+
I have a question
|
63
|
+
EOF
|
64
|
+
|
65
|
+
assert_include LLM.parse(text).last[:content], 'question'
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def test_cmd
|
70
|
+
text=<<-EOF
|
71
|
+
How many files are there:
|
72
|
+
|
73
|
+
[[cmd list of files
|
74
|
+
echo "file1 file2"
|
75
|
+
]]
|
76
|
+
EOF
|
77
|
+
|
78
|
+
assert_equal :user, LLM.parse(text).last[:role]
|
79
|
+
assert_include LLM.parse(text).first[:content], 'file1'
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_directory
|
83
|
+
TmpFile.with_path do |tmpdir|
|
84
|
+
tmpdir.file1.write "foo"
|
85
|
+
tmpdir.file2.write "bar"
|
86
|
+
text=<<-EOF
|
87
|
+
How many files are there:
|
88
|
+
|
89
|
+
[[directory DIR
|
90
|
+
#{tmpdir}
|
91
|
+
]]
|
92
|
+
EOF
|
93
|
+
|
94
|
+
assert_include LLM.parse(text).first[:content], 'file1'
|
95
|
+
end
|
17
96
|
end
|
18
97
|
end
|
19
98
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
2
|
+
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
|
+
|
4
|
+
require 'scout-ai'
|
5
|
+
class TestClass < Test::Unit::TestCase
|
6
|
+
def test_main
|
7
|
+
model = NextTokenModel.new
|
8
|
+
train_texts = [
|
9
|
+
"say hi, no!",
|
10
|
+
"say hi, no no no",
|
11
|
+
"say hi, hi ",
|
12
|
+
"say hi, hi how are you ",
|
13
|
+
"say hi, hi are you good",
|
14
|
+
]
|
15
|
+
|
16
|
+
model_name = "distilgpt2" # Replace with your local/other HF Llama checkpoint as needed
|
17
|
+
|
18
|
+
TmpFile.with_path do |tmp_dir|
|
19
|
+
iii tmp_dir
|
20
|
+
|
21
|
+
sss 0
|
22
|
+
model = NextTokenModel.new model_name, tmp_dir, training_num_train_epochs: 1000, training_learning_rate: 0.1
|
23
|
+
|
24
|
+
iii :new
|
25
|
+
chat = Chat.setup []
|
26
|
+
chat.user "say hi"
|
27
|
+
ppp model.eval chat
|
28
|
+
|
29
|
+
model.save
|
30
|
+
model = PythonModel.new tmp_dir
|
31
|
+
|
32
|
+
iii :load
|
33
|
+
chat = Chat.setup []
|
34
|
+
chat.user "say hi"
|
35
|
+
ppp model.eval chat
|
36
|
+
|
37
|
+
iii :training
|
38
|
+
state, tokenizer = model.init
|
39
|
+
tokenizer.pad_token = tokenizer.eos_token
|
40
|
+
model.add_list train_texts.shuffle
|
41
|
+
model.train
|
42
|
+
|
43
|
+
iii :trained
|
44
|
+
chat = Chat.setup []
|
45
|
+
chat.user "say hi"
|
46
|
+
ppp model.eval chat
|
47
|
+
|
48
|
+
model.save
|
49
|
+
model = PythonModel.new tmp_dir
|
50
|
+
|
51
|
+
iii :load_again
|
52
|
+
chat = Chat.setup []
|
53
|
+
chat.user "say hi"
|
54
|
+
ppp model.eval chat
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
2
|
+
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
|
+
|
4
|
+
class TestClass < Test::Unit::TestCase
|
5
|
+
def test_eval_chat
|
6
|
+
#model = CausalModel.new 'BSC-LT/salamandra-2b-instruct'
|
7
|
+
model = CausalModel.new 'mistralai/Mistral-7B-Instruct-v0.3'
|
8
|
+
|
9
|
+
model.init
|
10
|
+
|
11
|
+
net, tok = model.state
|
12
|
+
|
13
|
+
iii model.eval([
|
14
|
+
{role: :system, content: "You are a calculator, just reply with the answer"},
|
15
|
+
{role: :user, content: " 1 + 2 ="}
|
16
|
+
])
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_eval_train
|
20
|
+
#model = CausalModel.new 'BSC-LT/salamandra-2b-instruct'
|
21
|
+
model = CausalModel.new 'mistralai/Mistral-7B-Instruct-v0.3'
|
22
|
+
|
23
|
+
model.init
|
24
|
+
|
25
|
+
net, tok = model.state
|
26
|
+
|
27
|
+
iii model.eval([
|
28
|
+
{role: :system, content: "You are a calculator, just reply with the answer"},
|
29
|
+
{role: :user, content: " 1 + 2 ="}
|
30
|
+
])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path(__FILE__).sub(%r(/test/.*), '/test/test_helper.rb')
|
2
|
+
require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1')
|
3
|
+
|
4
|
+
class TestSequenceClassification < Test::Unit::TestCase
|
5
|
+
def _test_eval_sequence_classification
|
6
|
+
model = SequenceClassificationModel.new 'bert-base-uncased', nil,
|
7
|
+
class_labels: %w(Bad Good)
|
8
|
+
|
9
|
+
assert_include ["Bad", "Good"], model.eval("This is dog")
|
10
|
+
assert_include ["Bad", "Good"], model.eval_list(["This is dog", "This is cat"]).first
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_train_sequence_classification
|
14
|
+
model = SequenceClassificationModel.new 'bert-base-uncased', nil,
|
15
|
+
class_labels: %w(Bad Good)
|
16
|
+
|
17
|
+
model.init
|
18
|
+
|
19
|
+
10.times do
|
20
|
+
model.add "The dog", 'Bad'
|
21
|
+
model.add "The cat", 'Good'
|
22
|
+
end
|
23
|
+
|
24
|
+
model.train
|
25
|
+
|
26
|
+
assert_equal "Bad", model.eval("This is dog")
|
27
|
+
assert_equal "Good", model.eval("This is cat")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|