scout-ai 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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.vimproject +61 -0
  4. data/LICENSE +20 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +18 -0
  7. data/Rakefile +44 -0
  8. data/VERSION +1 -0
  9. data/bin/scout-ai +5 -0
  10. data/lib/scout/llm/agent.rb +78 -0
  11. data/lib/scout/llm/ask.rb +50 -0
  12. data/lib/scout/llm/backends/huggingface.rb +67 -0
  13. data/lib/scout/llm/backends/ollama.rb +103 -0
  14. data/lib/scout/llm/backends/openai.rb +86 -0
  15. data/lib/scout/llm/backends/openwebui.rb +63 -0
  16. data/lib/scout/llm/backends/relay.rb +36 -0
  17. data/lib/scout/llm/embed.rb +31 -0
  18. data/lib/scout/llm/parse.rb +33 -0
  19. data/lib/scout/llm/rag.rb +16 -0
  20. data/lib/scout/llm/tools.rb +104 -0
  21. data/lib/scout/llm/utils.rb +35 -0
  22. data/lib/scout-ai.rb +7 -0
  23. data/questions/coach +2 -0
  24. data/scout_commands/agent/ask +70 -0
  25. data/scout_commands/llm/ask +56 -0
  26. data/scout_commands/llm/process +50 -0
  27. data/scout_commands/llm/template +26 -0
  28. data/test/data/person/brothers +4 -0
  29. data/test/data/person/identifiers +10 -0
  30. data/test/data/person/marriages +3 -0
  31. data/test/data/person/parents +6 -0
  32. data/test/scout/llm/backends/test_huggingface.rb +73 -0
  33. data/test/scout/llm/backends/test_ollama.rb +72 -0
  34. data/test/scout/llm/backends/test_openai.rb +68 -0
  35. data/test/scout/llm/backends/test_openwebui.rb +57 -0
  36. data/test/scout/llm/backends/test_relay.rb +10 -0
  37. data/test/scout/llm/test_agent.rb +114 -0
  38. data/test/scout/llm/test_ask.rb +63 -0
  39. data/test/scout/llm/test_embed.rb +0 -0
  40. data/test/scout/llm/test_parse.rb +19 -0
  41. data/test/scout/llm/test_rag.rb +30 -0
  42. data/test/scout/llm/test_tools.rb +54 -0
  43. data/test/scout/llm/test_utils.rb +10 -0
  44. data/test/test_helper.rb +68 -0
  45. metadata +86 -0
@@ -0,0 +1,57 @@
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 TestOpenWebUI < Test::Unit::TestCase
5
+ def test_gepeto
6
+ Log.severity = 0
7
+ prompt =<<-EOF
8
+ 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 ```.
9
+ user: write a script that sorts files in a directory
10
+ EOF
11
+
12
+ prompt =<<-EOF
13
+ user: write a script that sorts files in a directory
14
+ EOF
15
+
16
+ ppp LLM::OpenWebUI.ask prompt, model: 'mistral:latest', url: "http://gepeto.bsc.es/api"
17
+ end
18
+
19
+ def _test_tool
20
+ prompt =<<-EOF
21
+ What is the weather in London. Should I take an umbrella?
22
+ EOF
23
+
24
+ tools = [
25
+ {
26
+ "type": "function",
27
+ "function": {
28
+ "name": "get_current_temperature",
29
+ "description": "Get the current temperature for a specific location",
30
+ "parameters": {
31
+ "type": "object",
32
+ "properties": {
33
+ "location": {
34
+ "type": "string",
35
+ "description": "The city and state, e.g., San Francisco, CA"
36
+ },
37
+ "unit": {
38
+ "type": "string",
39
+ "enum": ["Celsius", "Fahrenheit"],
40
+ "description": "The temperature unit to use. Infer this from the user's location."
41
+ }
42
+ },
43
+ "required": ["location", "unit"]
44
+ }
45
+ }
46
+ },
47
+ ]
48
+
49
+ sss 0
50
+ respose = LLM::OpenWebUI.ask prompt, model: 'gemma2:latest', tool_choice: 'required', tools: tools do |name,arguments|
51
+ "It's raining cats and dogs"
52
+ end
53
+
54
+ ppp respose
55
+ end
56
+ end
57
+
@@ -0,0 +1,10 @@
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 TestRelay < Test::Unit::TestCase
5
+ def test_ask
6
+ Scout::Config.set(:server, 'localhost', :relay)
7
+ ppp LLM::Relay.ask 'Say hi', model: 'gemma2'
8
+ end
9
+ end
10
+
@@ -0,0 +1,114 @@
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 'rbbt-util'
5
+ class TestLLMAgent < Test::Unit::TestCase
6
+ def _test_system
7
+ TmpFile.with_dir do |dir|
8
+ kb = KnowledgeBase.new dir
9
+ kb.format = {"Person" => "Alias"}
10
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
11
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
12
+ kb.register :parents, datafile_test(:person).parents
13
+
14
+ agent = LLM::Agent.new knowledge_base: kb
15
+
16
+ agent.system = ""
17
+
18
+ sss 0
19
+ ppp agent.ask "Who is Miguel's brother-in-law. Brother in law is your spouses sibling or your sibling's spouse"
20
+ #ppp agent.ask "Who is Guille's brother-in-law. Brother in law is your spouses sibling or your sibling's spouse"
21
+ end
22
+ end
23
+
24
+ def _test_system_gepeto
25
+ TmpFile.with_dir do |dir|
26
+ Scout::Config.set(:backend, :ollama, :ask)
27
+ kb = KnowledgeBase.new dir
28
+ kb.format = {"Person" => "Alias"}
29
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
30
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
31
+ kb.register :parents, datafile_test(:person).parents
32
+
33
+ agent = LLM::Agent.new knowledge_base: kb, model: 'mistral', url: "https://gepeto.bsc.es/"
34
+
35
+ agent.system = ""
36
+
37
+ sss 0
38
+ ppp agent.ask "Who is Guille's brother-in-law. Brother in law is your spouses sibling or your sibling's spouse"
39
+ end
40
+ end
41
+
42
+ def test_workflow
43
+ m = Module.new do
44
+ extend Workflow
45
+ self.name = "Registration"
46
+
47
+ desc "Register a person"
48
+ input :name, :string, "Last, first name"
49
+ input :age, :integer, "Age"
50
+ input :gender, :select, "Gender", nil, :select_options => %w(male female)
51
+ task :person => :yaml do
52
+ iii inputs.to_hash
53
+ inputs.to_hash
54
+ end
55
+ end
56
+
57
+ sss 0
58
+ #ppp LLM.workflow_ask(m, "Register Eduard Smith, a 25 yo male", model: "Meta-Llama-3.3-70B-Instruct")
59
+ ppp LLM.workflow_ask(m, "Register Eduard Smith, a 25 yo male, using a tool call to the tool provided", backend: 'ollama', model: "llama3")
60
+ end
61
+
62
+ def _test_openai
63
+ TmpFile.with_dir do |dir|
64
+ kb = KnowledgeBase.new dir
65
+ kb.format = {"Person" => "Alias"}
66
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
67
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
68
+ kb.register :parents, datafile_test(:person).parents
69
+
70
+ sss 3
71
+ agent = LLM::Agent.new knowledge_base: kb, model: 'gpt-4o'
72
+
73
+ agent.system = ""
74
+
75
+ ppp agent.ask "Who is Miguel's brother-in-law"
76
+ end
77
+ end
78
+
79
+ def _test_argonne
80
+ TmpFile.with_dir do |dir|
81
+ kb = KnowledgeBase.new dir
82
+ kb.format = {"Person" => "Alias"}
83
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
84
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
85
+ kb.register :parents, datafile_test(:person).parents
86
+
87
+ agent.system = ""
88
+
89
+ ppp agent.ask "Who is Miguel's brother-in-law"
90
+ end
91
+ end
92
+
93
+ def _test_nvidia
94
+ TmpFile.with_dir do |dir|
95
+ kb = KnowledgeBase.new dir
96
+ kb.format = {"Person" => "Alias"}
97
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
98
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
99
+ kb.register :parents, datafile_test(:person).parents
100
+
101
+ sss 0
102
+
103
+ ppp LLM::OpenAI.ask "Say Hi", url: "https://integrate.api.nvidia.com/v1", model: "deepseek-ai/deepseek-r1"
104
+ exit
105
+
106
+
107
+ agent.system = ""
108
+
109
+ ppp agent.ask "Who is Miguel's brother-in-law. Make use of the tools using tool_calls"
110
+ end
111
+ end
112
+
113
+ end
114
+
@@ -0,0 +1,63 @@
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/workflow'
5
+ require 'scout/knowledge_base'
6
+
7
+ class TestLLM < Test::Unit::TestCase
8
+ def _test_ask
9
+ Log.severity = 0
10
+ prompt =<<-EOF
11
+ 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 ```.
12
+ user: write a script that sorts files in a directory
13
+ EOF
14
+ ppp LLM.ask prompt
15
+ end
16
+
17
+ def _test_workflow_ask
18
+ m = Module.new do
19
+ extend Workflow
20
+ self.name = "RecipeWorkflow"
21
+
22
+ desc "List the steps to cook a recipe"
23
+ input :recipe, :string, "Recipe for which to extract steps"
24
+ task :recipe_steps => :array do |recipe|
25
+ ["prepare batter", "bake"]
26
+ end
27
+
28
+ desc "Calculate time spent in each step of the recipe"
29
+ input :step, :string, "Cooking step"
30
+ task :step_time => :string do |step|
31
+ case step
32
+ when "prepare batter"
33
+ "2 hours"
34
+ when "bake"
35
+ "30 minutes"
36
+ else
37
+ "1 minute"
38
+ end
39
+ end
40
+ end
41
+
42
+ ppp LLM.workflow_ask(m, "How much time does it take to prepare a cake recipe")
43
+ end
44
+
45
+ def test_knowledbase
46
+ TmpFile.with_dir do |dir|
47
+ kb = KnowledgeBase.new dir
48
+ kb.format = {"Person" => "Alias"}
49
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
50
+ kb.register :marriages, datafile_test(:person).marriages, undirected: true, source: "=>Alias", target: "=>Alias"
51
+ kb.register :parents, datafile_test(:person).parents
52
+
53
+ Scout::Config.set(:backend, :openai, :llm)
54
+ ppp LLM.knowledge_base_ask(kb, "Who is Miki's brother in law?", log_errors: true, model: 'gpt-4o')
55
+ ppp LLM.knowledge_base_ask(kb, "Who is Miki's father in law?", log_errors: true, model: 'gpt-4o')
56
+ Scout::Config.set(:backend, :ollama, :llm)
57
+ ppp LLM.knowledge_base_ask(kb, "Who is Miki's brother in law?")
58
+ ppp LLM.knowledge_base_ask(kb, "Who is Miki's father in law?")
59
+ end
60
+ end
61
+
62
+ end
63
+
File without changes
@@ -0,0 +1,19 @@
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 TestLLMParse < Test::Unit::TestCase
5
+ def test_parse
6
+ text=<<-EOF
7
+ system: you are an asistant
8
+ user: Given the contents of this file: [[
9
+ line 1: 1
10
+ line 2: 2
11
+ line 3: 3
12
+ ]]
13
+ Show me the lines in reverse order
14
+ EOF
15
+
16
+ iii LLM.parse(text)
17
+ end
18
+ end
19
+
@@ -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
+ require 'scout/llm/embed'
5
+
6
+ class TestLLMRAG < Test::Unit::TestCase
7
+ def test_rag
8
+ text1 =<<-EOF
9
+ Crime, Killing and Theft.
10
+ EOF
11
+ text2 =<<-EOF
12
+ Murder, felony and violence
13
+ EOF
14
+ text3 =<<-EOF
15
+ Puppies, cats and flowers
16
+ EOF
17
+
18
+ data = [ LLM.embed(text1),
19
+ LLM.embed(text2),
20
+ LLM.embed(text3)]
21
+
22
+ i = LLM::RAG.index(data)
23
+ nodes, scores = i.search_knn LLM.embed('I love the zoo'), 1
24
+ assert_equal 2, nodes.first
25
+
26
+ nodes, scores = i.search_knn LLM.embed('The victim got stabbed'), 2
27
+ assert_equal [0, 1], nodes.sort
28
+ end
29
+ end
30
+
@@ -0,0 +1,54 @@
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 'rbbt/workflow'
5
+ require 'scout/knowledge_base'
6
+ class TestLLMTools < Test::Unit::TestCase
7
+ def test_workflow_definition
8
+ m = Module.new do
9
+ extend Workflow
10
+ self.name = "RecipeWorkflow"
11
+
12
+ desc "List the steps to cook a recipe"
13
+ input :recipe, :string, "Recipe for which to extract steps"
14
+ task :recipe_steps => :array do |recipe|
15
+ ["prepare batter", "bake"]
16
+ end
17
+
18
+ desc "Calculate time spent in each step of the recipe"
19
+ input :step, :string, "Cooking step"
20
+ task :step_time => :string do |step|
21
+ case step
22
+ when "prepare batter"
23
+ "2 hours"
24
+ when "bake"
25
+ "30 minutes"
26
+ else
27
+ "1 minute"
28
+ end
29
+ end
30
+ end
31
+
32
+ LLM.task_tool_definition(m, :recipe_steps)
33
+ LLM.task_tool_definition(m, :step_time)
34
+
35
+ tool_definitions = LLM.workflow_tools(m)
36
+ ppp JSON.pretty_generate tool_definitions
37
+ end
38
+
39
+ def test_knowledbase_definition
40
+ TmpFile.with_dir do |dir|
41
+ kb = KnowledgeBase.new dir
42
+ kb.register :brothers, datafile_test(:person).brothers, undirected: true
43
+ kb.register :parents, datafile_test(:person).parents
44
+
45
+ assert_include kb.all_databases, :brothers
46
+
47
+ assert_equal Person, kb.target_type(:parents)
48
+
49
+ knowledge_base_definition = LLM.knowledge_base_tool_definition(kb)
50
+ ppp JSON.pretty_generate knowledge_base_definition
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,10 @@
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 TestLLMUtils < Test::Unit::TestCase
5
+ def test_server_tokens
6
+ assert_equal %w(some random server some.random random.server some.random.server some.random.server.com), LLM.get_url_server_tokens('http://some.random.server.com/api')
7
+ assert_equal %w(ollama.some ollama.server ollama.some.server ollama.some.server.com), LLM.get_url_server_tokens('http://some.server.com/api', :ollama)
8
+ end
9
+ end
10
+
@@ -0,0 +1,68 @@
1
+ require 'test/unit'
2
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
4
+ require 'scout'
5
+
6
+ class Test::Unit::TestCase
7
+
8
+ def assert_equal_path(path1, path2)
9
+ assert_equal File.expand_path(path1), File.expand_path(path2)
10
+ end
11
+
12
+ def self.tmpdir
13
+ @@tmpdir ||= Path.setup('tmp/test_tmpdir').find
14
+ end
15
+
16
+ def tmpdir
17
+ @tmpdir ||= Test::Unit::TestCase.tmpdir
18
+ end
19
+
20
+ setup do
21
+ Open.rm_rf tmpdir
22
+ TmpFile.tmpdir = tmpdir.tmpfiles
23
+ Log::ProgressBar.default_severity = 0
24
+ Persist.cache_dir = tmpdir.var.cache
25
+ Persist::MEMORY_CACHE.clear
26
+ Open.remote_cache_dir = tmpdir.var.cache
27
+ Workflow.directory = tmpdir.var.jobs
28
+ Workflow.workflows.each{|wf| wf.directory = Workflow.directory[wf.name] }
29
+ Entity.entity_property_cache = tmpdir.entity_properties if defined?(Entity)
30
+ end
31
+
32
+ teardown do
33
+ Open.rm_rf tmpdir
34
+ end
35
+
36
+ def self.datadir_test
37
+ Path.setup(File.join(File.dirname(__FILE__), 'data'))
38
+ end
39
+
40
+ def self.datafile_test(file)
41
+ datadir_test[file.to_s]
42
+ end
43
+
44
+ def datadir_test
45
+ Test::Unit::TestCase.datadir_test
46
+ end
47
+
48
+ def datafile_test(file)
49
+ Test::Unit::TestCase.datafile_test(file)
50
+ end
51
+ end
52
+
53
+ module Object::Person
54
+ extend Entity
55
+
56
+ annotation :language
57
+
58
+ property :salutation do
59
+ case language
60
+ when 'es'
61
+ "Hola #{self}"
62
+ else
63
+ "Hi #{self}"
64
+ end
65
+ end
66
+ end
67
+
68
+ Object::Person.add_identifiers Test::Unit::TestCase.datafile_test(:person).identifiers
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scout-ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Miguel Vazquez
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-03-19 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: assorted functionalities to help scouts use AI
13
+ email: mikisvaz@gmail.com
14
+ executables:
15
+ - scout-ai
16
+ extensions: []
17
+ extra_rdoc_files:
18
+ - LICENSE
19
+ - LICENSE.txt
20
+ - README.rdoc
21
+ files:
22
+ - ".document"
23
+ - ".vimproject"
24
+ - LICENSE
25
+ - LICENSE.txt
26
+ - README.rdoc
27
+ - Rakefile
28
+ - VERSION
29
+ - bin/scout-ai
30
+ - lib/scout-ai.rb
31
+ - lib/scout/llm/agent.rb
32
+ - lib/scout/llm/ask.rb
33
+ - lib/scout/llm/backends/huggingface.rb
34
+ - lib/scout/llm/backends/ollama.rb
35
+ - lib/scout/llm/backends/openai.rb
36
+ - lib/scout/llm/backends/openwebui.rb
37
+ - lib/scout/llm/backends/relay.rb
38
+ - lib/scout/llm/embed.rb
39
+ - lib/scout/llm/parse.rb
40
+ - lib/scout/llm/rag.rb
41
+ - lib/scout/llm/tools.rb
42
+ - lib/scout/llm/utils.rb
43
+ - questions/coach
44
+ - scout_commands/agent/ask
45
+ - scout_commands/llm/ask
46
+ - scout_commands/llm/process
47
+ - scout_commands/llm/template
48
+ - test/data/person/brothers
49
+ - test/data/person/identifiers
50
+ - test/data/person/marriages
51
+ - test/data/person/parents
52
+ - test/scout/llm/backends/test_huggingface.rb
53
+ - test/scout/llm/backends/test_ollama.rb
54
+ - test/scout/llm/backends/test_openai.rb
55
+ - test/scout/llm/backends/test_openwebui.rb
56
+ - test/scout/llm/backends/test_relay.rb
57
+ - test/scout/llm/test_agent.rb
58
+ - test/scout/llm/test_ask.rb
59
+ - test/scout/llm/test_embed.rb
60
+ - test/scout/llm/test_parse.rb
61
+ - test/scout/llm/test_rag.rb
62
+ - test/scout/llm/test_tools.rb
63
+ - test/scout/llm/test_utils.rb
64
+ - test/test_helper.rb
65
+ homepage: http://github.com/mikisvaz/scout-ai
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.6.5
84
+ specification_version: 4
85
+ summary: AI gear for scouts
86
+ test_files: []