scout-ai 0.2.0 → 1.0.1
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 +155 -9
- data/README.md +296 -0
- data/Rakefile +3 -0
- data/VERSION +1 -1
- data/bin/scout-ai +2 -0
- data/doc/Agent.md +279 -0
- data/doc/Chat.md +258 -0
- data/doc/LLM.md +446 -0
- data/doc/Model.md +513 -0
- data/doc/RAG.md +129 -0
- data/lib/scout/llm/agent/chat.rb +74 -0
- data/lib/scout/llm/agent/delegate.rb +39 -0
- data/lib/scout/llm/agent/iterate.rb +44 -0
- data/lib/scout/llm/agent.rb +51 -30
- data/lib/scout/llm/ask.rb +63 -21
- data/lib/scout/llm/backends/anthropic.rb +147 -0
- 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 +62 -35
- data/lib/scout/llm/backends/openai.rb +77 -33
- 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 +320 -0
- data/lib/scout/llm/chat.rb +703 -0
- data/lib/scout/llm/embed.rb +4 -4
- data/lib/scout/llm/mcp.rb +28 -0
- data/lib/scout/llm/parse.rb +71 -13
- data/lib/scout/llm/rag.rb +9 -0
- data/lib/scout/llm/tools/call.rb +66 -0
- data/lib/scout/llm/tools/knowledge_base.rb +158 -0
- data/lib/scout/llm/tools/mcp.rb +59 -0
- data/lib/scout/llm/tools/workflow.rb +69 -0
- data/lib/scout/llm/tools.rb +112 -76
- 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 +4 -1
- data/python/scout_ai/__init__.py +35 -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/next_token.py +315 -0
- data/python/scout_ai/util.py +32 -0
- data/scout-ai.gemspec +143 -0
- data/scout_commands/agent/ask +89 -14
- data/scout_commands/agent/kb +15 -0
- data/scout_commands/documenter +148 -0
- data/scout_commands/llm/ask +71 -12
- data/scout_commands/llm/process +4 -2
- data/scout_commands/llm/server +319 -0
- data/share/server/chat.html +138 -0
- data/share/server/chat.js +468 -0
- data/test/data/cat.jpg +0 -0
- data/test/scout/llm/agent/test_chat.rb +14 -0
- data/test/scout/llm/backends/test_anthropic.rb +134 -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 +134 -10
- data/test/scout/llm/backends/test_responses.rb +239 -0
- data/test/scout/llm/test_agent.rb +0 -70
- data/test/scout/llm/test_ask.rb +4 -1
- data/test/scout/llm/test_chat.rb +256 -0
- data/test/scout/llm/test_mcp.rb +29 -0
- data/test/scout/llm/test_parse.rb +81 -2
- data/test/scout/llm/tools/test_call.rb +0 -0
- data/test/scout/llm/tools/test_knowledge_base.rb +22 -0
- data/test/scout/llm/tools/test_mcp.rb +11 -0
- data/test/scout/llm/tools/test_workflow.rb +39 -0
- 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 +113 -7
- data/README.rdoc +0 -18
- data/questions/coach +0 -2
data/scout_commands/agent/ask
CHANGED
|
@@ -19,8 +19,12 @@ Use STDIN to add context to the question
|
|
|
19
19
|
-h--help Print this help
|
|
20
20
|
-l--log* Log level
|
|
21
21
|
-t--template* Use a template
|
|
22
|
+
-c--chat* Follow a conversation
|
|
22
23
|
-m--model* Model to use
|
|
23
|
-
-
|
|
24
|
+
-e--endpoint* Endpoint to use
|
|
25
|
+
-f--file* Incorporate file
|
|
26
|
+
-wt--workflow_tasks* Export these tasks to the agent
|
|
27
|
+
-i--imports* Chat files to import, separated by comma
|
|
24
28
|
EOF
|
|
25
29
|
if options[:help]
|
|
26
30
|
if defined? scout_usage
|
|
@@ -33,38 +37,109 @@ end
|
|
|
33
37
|
|
|
34
38
|
Log.severity = options.delete(:log).to_i if options.include? :log
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
agent_name, *question_parts = ARGV
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
question = question_parts * " "
|
|
39
43
|
|
|
44
|
+
file, chat, inline, template, dry_run, imports = IndiferentHash.process_options options, :file, :chat, :inline, :template, :dry_run, :imports
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
Workflow.require_workflow agent
|
|
43
|
-
rescue
|
|
44
|
-
end
|
|
46
|
+
file = Path.setup(file) if file
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
imports = imports.split(/,\s*/) if imports
|
|
47
49
|
|
|
48
|
-
agent = LLM::Agent.new workflow: workflow, knowledge_base: knowledge_base
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
agent_name ||= 'default'
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
agent_file = Scout.workflows[agent_name]
|
|
54
|
+
|
|
55
|
+
agent_file = Scout.chats[agent_name] unless agent_file.exists?
|
|
56
|
+
|
|
57
|
+
agent_file = agent_file.find_with_extension('rb') unless agent_file.exists?
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if agent_file.exists?
|
|
61
|
+
if agent_file.directory?
|
|
62
|
+
if agent_file.agent.find_with_extension('rb').exists?
|
|
63
|
+
agent = load agent_file.agent.find_with_extension('rb')
|
|
64
|
+
else
|
|
65
|
+
agent = LLM::Agent.load_from_path agent_file
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
agent = load agent_file
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
#raise ParameterException agent_file
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if template
|
|
53
75
|
if Open.exists?(template)
|
|
54
76
|
template_question = Open.read(template)
|
|
55
|
-
|
|
77
|
+
elsif Scout.questions[template].exists?
|
|
56
78
|
template_question = Scout.questions[template].read
|
|
79
|
+
elsif Scout.chats.system[template].exists?
|
|
80
|
+
template_question = Scout.chats.system[template].read
|
|
81
|
+
elsif Scout.chats[template].exists?
|
|
82
|
+
template_question = Scout.chats[template].read
|
|
57
83
|
end
|
|
58
84
|
if template_question.include?('???')
|
|
59
85
|
question = template_question.sub('???', question)
|
|
86
|
+
elsif not question.empty?
|
|
87
|
+
question = template_question + "\nuser: #{question}"
|
|
60
88
|
else
|
|
61
89
|
question = template_question
|
|
62
90
|
end
|
|
63
91
|
end
|
|
64
92
|
|
|
65
93
|
if question.include?('...')
|
|
66
|
-
context = file ? Open.read(file) : STDIN.read
|
|
94
|
+
context = file ? Open.read(file) : STDIN.read
|
|
67
95
|
question = question.sub('...', context)
|
|
96
|
+
elsif file
|
|
97
|
+
question = "<file basename=#{File.basename file}>\n" + Open.read(file) + "\n</file>\n\n" + question
|
|
68
98
|
end
|
|
69
99
|
|
|
70
|
-
|
|
100
|
+
if chat
|
|
101
|
+
conversation = Open.exist?(chat)? LLM.chat(chat) : []
|
|
102
|
+
convo_options = LLM.options conversation
|
|
103
|
+
conversation = question.empty? ? conversation : conversation + LLM.chat(question)
|
|
104
|
+
|
|
105
|
+
if dry_run
|
|
106
|
+
ppp LLM.print conversation
|
|
107
|
+
exit 0
|
|
108
|
+
end
|
|
109
|
+
new = agent.ask(conversation, convo_options.merge(options.merge(return_messages: true)))
|
|
110
|
+
conversation = Open.read(chat) + LLM.print(new)
|
|
111
|
+
Open.write(chat, conversation)
|
|
112
|
+
elsif inline
|
|
113
|
+
|
|
114
|
+
file = Open.read inline
|
|
115
|
+
|
|
116
|
+
new_file = ""
|
|
117
|
+
while true
|
|
118
|
+
pre, question, post =
|
|
119
|
+
file.partition(/^\s*#\s*ask:(?:.*?)(?=^\s*[^\s#])/smu)
|
|
120
|
+
|
|
121
|
+
break if post.empty?
|
|
122
|
+
|
|
123
|
+
new_file << pre
|
|
124
|
+
new_file << question
|
|
125
|
+
clean_question = question.gsub('#', '').gsub(/\s+/,' ').sub(/.*ask:\s*/,'').strip
|
|
126
|
+
chat = [
|
|
127
|
+
{role: :system, content: "Write a succint reply with no commentary and no formatting."},
|
|
128
|
+
{role: :user, content: "Find the following question as a comment in the file give a response to be placed inline: #{question}"},
|
|
129
|
+
LLM.tag('file', file, inline)
|
|
130
|
+
]
|
|
131
|
+
response = LLM.ask(LLM.chat(chat))
|
|
132
|
+
new_file << <<-EOF
|
|
133
|
+
# Response start
|
|
134
|
+
#{response}
|
|
135
|
+
# Response end
|
|
136
|
+
EOF
|
|
137
|
+
file = post
|
|
138
|
+
end
|
|
139
|
+
new_file << file
|
|
140
|
+
Open.write(inline, new_file)
|
|
141
|
+
else
|
|
142
|
+
conversation = Chat.setup(LLM.chat question)
|
|
143
|
+
imports.each{|import| conversation.import import } if imports
|
|
144
|
+
puts agent.ask(conversation, nil, options)
|
|
145
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
agent = ARGV.shift
|
|
4
|
+
|
|
5
|
+
agent_dir = Scout.var.Agent[agent]
|
|
6
|
+
|
|
7
|
+
if ARGV.any?
|
|
8
|
+
ARGV.push "--knowledge_base"
|
|
9
|
+
ARGV.push agent_dir.knowledge_base
|
|
10
|
+
ARGV.push "--log"
|
|
11
|
+
ARGV.push Log.severity.to_s
|
|
12
|
+
end
|
|
13
|
+
ARGV.unshift 'kb'
|
|
14
|
+
|
|
15
|
+
load Scout.bin.scout.find
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'scout'
|
|
4
|
+
|
|
5
|
+
$0 = "scout-ai #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
|
6
|
+
|
|
7
|
+
options = SOPT.setup <<EOF
|
|
8
|
+
Scout documenter tool
|
|
9
|
+
|
|
10
|
+
$ #{$0} [<options>] <topic>
|
|
11
|
+
|
|
12
|
+
Generates technical, example-driven documentation for a given Scout topic by
|
|
13
|
+
analyzing Ruby source and corresponding test files. For a specified topic, it
|
|
14
|
+
locates main and subtopic files, invokes an LLM agent to synthesize markdown
|
|
15
|
+
documentation using real test behavior and examples, then outputs comprehensive
|
|
16
|
+
topic and subtopic documentation files.
|
|
17
|
+
|
|
18
|
+
-h--help Print this help
|
|
19
|
+
EOF
|
|
20
|
+
if options[:help]
|
|
21
|
+
if defined? scout_usage
|
|
22
|
+
scout_usage
|
|
23
|
+
else
|
|
24
|
+
puts SOPT.doc
|
|
25
|
+
end
|
|
26
|
+
exit 0
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require 'scout-ai'
|
|
30
|
+
|
|
31
|
+
topic = ARGV.first
|
|
32
|
+
|
|
33
|
+
raise MissingParameterException if topic.nil?
|
|
34
|
+
|
|
35
|
+
src_files = Scout.lib.scout.glob("#{topic}*/**/*") + Scout.lib.scout.glob("#{topic}.rb")
|
|
36
|
+
src_files.collect! do |file|
|
|
37
|
+
file.sub(Dir.pwd, '.')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
bin_files = Scout.scout_commands.glob("**/*").select{|file| file.include?(topic) && ! file.directory? }
|
|
41
|
+
|
|
42
|
+
main = src_files.select{|f| f.split("/").length == 4}.first
|
|
43
|
+
subtopics = src_files.select{|f| f.split("/").length == 5}.collect{|f| File.basename(f).sub(".rb",'') }.uniq
|
|
44
|
+
|
|
45
|
+
def source_to_test(file)
|
|
46
|
+
file.sub(%r{\A./lib/}, './test/').sub(%r{([^/]+)\.rb\z}, 'test_\1.rb')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
documenter = LLM::Agent.new
|
|
50
|
+
|
|
51
|
+
documenter.start_chat.system <<-EOF
|
|
52
|
+
You are a world-class Ruby documentation author. For each (source, test) file
|
|
53
|
+
pair given, produce technically precise module- and file-level documentation,
|
|
54
|
+
incorporating specific code usage and behavior from the test file as worked
|
|
55
|
+
examples, code idioms, and edge-case handling.
|
|
56
|
+
|
|
57
|
+
Never insert your own example code: always use live content from the tests as examples.
|
|
58
|
+
|
|
59
|
+
Integrate documentation and test-derived examples smoothly.
|
|
60
|
+
|
|
61
|
+
You will be given first the main topic documentation for the main file and
|
|
62
|
+
test_file, then you will be asked to produce documentation for a subtopic.
|
|
63
|
+
|
|
64
|
+
Finally you will be ask to aggregate all the documentation portions into
|
|
65
|
+
a final topic documentation file
|
|
66
|
+
|
|
67
|
+
User markdown
|
|
68
|
+
|
|
69
|
+
Avoid initial and final comments like: Certainly! I'll do this and that
|
|
70
|
+
EOF
|
|
71
|
+
|
|
72
|
+
documenter.start_chat.file main
|
|
73
|
+
documenter.start_chat.file source_to_test(main)
|
|
74
|
+
|
|
75
|
+
documenter.start_chat.user <<-EOF
|
|
76
|
+
This is the basic topic file. Write the markdown documentation for it.
|
|
77
|
+
EOF
|
|
78
|
+
|
|
79
|
+
docs = {}
|
|
80
|
+
subtopics.each do |subtopic|
|
|
81
|
+
src = src_files.select{|f| f.include? subtopic}.select{|f| f.end_with?(".rb") }
|
|
82
|
+
test = src.collect{|f| source_to_test(f) }.select{|f| Open.exists? f }.select{|f| f.end_with?(".rb")}
|
|
83
|
+
|
|
84
|
+
documenter.start
|
|
85
|
+
(src + test + bin_files).each do |file|
|
|
86
|
+
documenter.file file
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
documenter.start_chat.user <<-EOF
|
|
90
|
+
Write documentation for topic #{topic} subtopic #{subtopic}
|
|
91
|
+
EOF
|
|
92
|
+
docs[subtopic] = documenter.respond
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
documenter.start
|
|
96
|
+
|
|
97
|
+
docs.each do |subtopic, documentation|
|
|
98
|
+
documenter.user <<-EOF
|
|
99
|
+
Please construct a comprehensive documentation on topic #{topic}. For each
|
|
100
|
+
subtopic reproduce all the most important from the original documentation
|
|
101
|
+
files. The subtopic documentation files will not be available anymore, so
|
|
102
|
+
don't leave anything imporant out
|
|
103
|
+
|
|
104
|
+
<file subtopic=#{subtopic}>
|
|
105
|
+
#{documentation}
|
|
106
|
+
<file>
|
|
107
|
+
EOF
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
main_documentation = documenter.chat
|
|
111
|
+
|
|
112
|
+
documenter.start_chat.user <<-EOF
|
|
113
|
+
This is the revise documentation for the topic:
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
#{main_documentation}
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
EOF
|
|
121
|
+
|
|
122
|
+
revised_subtopics = {}
|
|
123
|
+
docs.each do |subtopic, documentation|
|
|
124
|
+
documenter.start
|
|
125
|
+
|
|
126
|
+
src = src_files.select{|f| f.include? subtopic}.select{|f| f.end_with?(".rb") }
|
|
127
|
+
test = src.collect{|f| source_to_test(f) }.select{|f| Open.exists? f }.select{|f| f.end_with?(".rb")}
|
|
128
|
+
|
|
129
|
+
documenter.start
|
|
130
|
+
(src + test + bin_files).each do |file|
|
|
131
|
+
documenter.file file
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
documenter.user <<-EOF
|
|
135
|
+
Please revise the subtopic documentation in light of the revised main_documentation
|
|
136
|
+
|
|
137
|
+
<file subtopic=#{subtopic}>
|
|
138
|
+
#{documentation}
|
|
139
|
+
<file>
|
|
140
|
+
EOF
|
|
141
|
+
revised_subtopics[subtopic] = documenter.respond
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
Open.write Scout.doc.lib.scout[topic + '.md'].find(:current), main_documentation
|
|
146
|
+
revised_subtopics.each do |subtopic,documentation|
|
|
147
|
+
Open.write Scout.doc.lib.scout[topic][subtopic + '.md'].find(:current), documentation
|
|
148
|
+
end
|
data/scout_commands/llm/ask
CHANGED
|
@@ -3,22 +3,30 @@
|
|
|
3
3
|
require 'scout'
|
|
4
4
|
require 'scout-ai'
|
|
5
5
|
|
|
6
|
-
$0 = "scout #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
|
6
|
+
$0 = "scout-ai #{$previous_commands.any? ? $previous_commands*" " + " " : "" }#{ File.basename(__FILE__) }" if $previous_commands
|
|
7
7
|
|
|
8
8
|
options = SOPT.setup <<EOF
|
|
9
|
+
Ask an LLM model
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
$ #{$0} [<options>] [<question>]
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Use STDIN to add context to the question. The context can be referenced using
|
|
14
|
+
three dots '...'. The model will be prompted with the question, unless the
|
|
15
|
+
inline option is used. If the chat option is used, the response will be added
|
|
16
|
+
to the end of the file. If the file option is used the file contents will be
|
|
17
|
+
prepended before the question. With the template option, the file will be read
|
|
18
|
+
as if it were the question, and the actual question will be placed under the
|
|
19
|
+
characters '???', if they are present.
|
|
15
20
|
|
|
16
21
|
-h--help Print this help
|
|
17
|
-
-l--log* Log level
|
|
18
22
|
-t--template* Use a template
|
|
23
|
+
-c--chat* Follow a conversation
|
|
24
|
+
-i--inline* Ask inline questions about a file
|
|
25
|
+
-f--file* Incorporate file at the start
|
|
19
26
|
-m--model* Model to use
|
|
20
27
|
-e--endpoint* Endpoint to use
|
|
21
|
-
-
|
|
28
|
+
-b--backend* Backend to use
|
|
29
|
+
-d--dry_run Dry run, don't ask
|
|
22
30
|
EOF
|
|
23
31
|
if options[:help]
|
|
24
32
|
if defined? scout_usage
|
|
@@ -31,26 +39,77 @@ end
|
|
|
31
39
|
|
|
32
40
|
Log.severity = options.delete(:log).to_i if options.include? :log
|
|
33
41
|
|
|
34
|
-
file = options
|
|
42
|
+
file, chat, inline, template, dry_run = IndiferentHash.process_options options, :file, :chat, :inline, :template, :dry_run
|
|
35
43
|
|
|
36
44
|
question = ARGV * " "
|
|
37
45
|
|
|
38
|
-
if template
|
|
46
|
+
if template
|
|
39
47
|
if Open.exists?(template)
|
|
40
48
|
template_question = Open.read(template)
|
|
41
|
-
|
|
49
|
+
elsif Scout.questions[template].exists?
|
|
42
50
|
template_question = Scout.questions[template].read
|
|
51
|
+
elsif Scout.chats.system[template].exists?
|
|
52
|
+
template_question = Scout.chats.system[template].read
|
|
53
|
+
elsif Scout.chats[template].exists?
|
|
54
|
+
template_question = Scout.chats[template].read
|
|
43
55
|
end
|
|
44
56
|
if template_question.include?('???')
|
|
45
57
|
question = template_question.sub('???', question)
|
|
58
|
+
elsif not question.empty?
|
|
59
|
+
question = template_question + "\nuser: #{question}"
|
|
46
60
|
else
|
|
47
61
|
question = template_question
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
|
|
51
65
|
if question.include?('...')
|
|
52
|
-
context = file ? Open.read(file) : STDIN.read
|
|
66
|
+
context = file ? Open.read(file) : STDIN.read
|
|
53
67
|
question = question.sub('...', context)
|
|
68
|
+
elsif file
|
|
69
|
+
question = "<file basename=#{File.basename file}>[[[\n" + Open.read(file) + "\n]]]</file>"
|
|
54
70
|
end
|
|
55
71
|
|
|
56
|
-
|
|
72
|
+
if chat
|
|
73
|
+
conversation = Open.exist?(chat)? LLM.chat(chat) : []
|
|
74
|
+
convo_options = LLM.options conversation
|
|
75
|
+
conversation = question.empty? ? conversation : conversation + LLM.chat(question)
|
|
76
|
+
|
|
77
|
+
if dry_run
|
|
78
|
+
ppp LLM.print conversation
|
|
79
|
+
exit 0
|
|
80
|
+
end
|
|
81
|
+
new = LLM.ask(conversation, convo_options.merge(options.merge(return_messages: true)))
|
|
82
|
+
conversation = Open.read(chat) + LLM.print(new)
|
|
83
|
+
Open.write(chat, conversation)
|
|
84
|
+
elsif inline
|
|
85
|
+
|
|
86
|
+
file = Open.read inline
|
|
87
|
+
|
|
88
|
+
new_file = ""
|
|
89
|
+
while true
|
|
90
|
+
pre, question, post =
|
|
91
|
+
file.partition(/^\s*#\s*ask:(?:.*?)(?=^\s*[^\s#]|\z)/smu)
|
|
92
|
+
|
|
93
|
+
break if question.empty?
|
|
94
|
+
|
|
95
|
+
new_file << pre
|
|
96
|
+
new_file << question
|
|
97
|
+
clean_question = question.gsub('#', '').gsub(/\s+/,' ').sub(/.*ask:\s*/,'').strip
|
|
98
|
+
chat = [
|
|
99
|
+
{role: :system, content: "Write a succint reply with no commentary and no formatting."},
|
|
100
|
+
{role: :user, content: "Find the following question as a comment in the file give a response to be placed inline: #{question}"},
|
|
101
|
+
LLM.tag('file', file, inline)
|
|
102
|
+
]
|
|
103
|
+
response = LLM.ask(LLM.chat(chat))
|
|
104
|
+
new_file << <<-EOF
|
|
105
|
+
# Response start
|
|
106
|
+
#{response}
|
|
107
|
+
# Response end
|
|
108
|
+
EOF
|
|
109
|
+
file = post
|
|
110
|
+
end
|
|
111
|
+
new_file << file
|
|
112
|
+
Open.write(inline, new_file)
|
|
113
|
+
else
|
|
114
|
+
puts LLM.ask(question, options)
|
|
115
|
+
end
|
data/scout_commands/llm/process
CHANGED
|
@@ -32,7 +32,9 @@ directory = ARGV.first || Scout.var.ask.find
|
|
|
32
32
|
directory = Path.setup directory
|
|
33
33
|
|
|
34
34
|
while true
|
|
35
|
-
directory.glob('*.json')
|
|
35
|
+
files = directory.glob('*.json')
|
|
36
|
+
|
|
37
|
+
files.each do |file|
|
|
36
38
|
target = directory.reply[id + '.json']
|
|
37
39
|
|
|
38
40
|
if ! File.exist?(target)
|
|
@@ -46,5 +48,5 @@ while true
|
|
|
46
48
|
Open.rm(file)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
sleep 1
|
|
51
|
+
sleep 1 if files.empty?
|
|
50
52
|
end
|