scout-ai 1.2.3 → 2.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 +138 -50
- data/README.md +171 -290
- data/Rakefile +17 -1
- data/VERSION +1 -1
- data/doc/Improvements.md +325 -0
- data/doc/StartHere.md +110 -0
- data/doc/developer/Architecture.md +126 -0
- data/doc/developer/Backends.md +199 -0
- data/doc/developer/ChatLifecycle.md +183 -0
- data/doc/developer/DelegationInternals.md +295 -0
- data/doc/developer/DesignPrinciples.md +245 -0
- data/doc/developer/PromptProcessing.md +292 -0
- data/doc/developer/Provenance.md +317 -0
- data/doc/user/BuildingAgents.md +345 -0
- data/doc/user/Cookbook.md +333 -0
- data/doc/user/CoreConcepts.md +181 -0
- data/doc/user/Delegation.md +191 -0
- data/doc/user/GettingStarted.md +159 -0
- data/doc/user/ManagingContext.md +163 -0
- data/doc/user/MultiAgentWorkflows.md +256 -0
- data/doc/user/Python.md +159 -0
- data/doc/user/RunningInference.md +200 -0
- data/doc/user/ToolCalling.md +193 -0
- data/doc/user/WritingChats.md +197 -0
- data/lib/scout/llm/agent/chat.rb +61 -11
- data/lib/scout/llm/agent/delegate.rb +274 -65
- data/lib/scout/llm/agent/iterate.rb +2 -2
- data/lib/scout/llm/agent/save.rb +273 -0
- data/lib/scout/llm/agent/workflow.rb +164 -0
- data/lib/scout/llm/agent.rb +86 -61
- data/lib/scout/llm/ask.rb +62 -17
- data/lib/scout/llm/backends/anthropic.rb +9 -2
- data/lib/scout/llm/backends/bedrock.rb +15 -3
- data/lib/scout/llm/backends/default.rb +183 -99
- data/lib/scout/llm/backends/glm.rb +58 -0
- data/lib/scout/llm/backends/huggingface.rb +196 -26
- data/lib/scout/llm/backends/ollama.rb +13 -1
- data/lib/scout/llm/backends/openai.rb +0 -2
- data/lib/scout/llm/backends/openwebui.rb +20 -13
- data/lib/scout/llm/backends/relay.rb +22 -22
- data/lib/scout/llm/backends/responses.rb +1 -1
- data/lib/scout/llm/chat/agent_meta.rb +264 -0
- data/lib/scout/llm/chat/annotation.rb +39 -10
- data/lib/scout/llm/chat/parse.rb +28 -6
- data/lib/scout/llm/chat/persist.rb +25 -0
- data/lib/scout/llm/chat/process/clear.rb +41 -6
- data/lib/scout/llm/chat/process/files.rb +21 -6
- data/lib/scout/llm/chat/process/meta.rb +421 -34
- data/lib/scout/llm/chat/process/options.rb +21 -1
- data/lib/scout/llm/chat/process/tools.rb +56 -15
- data/lib/scout/llm/chat/process.rb +4 -0
- data/lib/scout/llm/chat/prompt/shorten_tools.rb +125 -0
- data/lib/scout/llm/chat/prompt/shorten_tools_epoch.rb +365 -0
- data/lib/scout/llm/chat/prompt.rb +48 -0
- data/lib/scout/llm/chat/provenance.rb +775 -0
- data/lib/scout/llm/chat/tool_calls.rb +76 -0
- data/lib/scout/llm/chat.rb +18 -2
- data/lib/scout/llm/embed.rb +11 -3
- data/lib/scout/llm/image.rb +86 -0
- data/lib/scout/llm/mcp.rb +10 -2
- data/lib/scout/llm/rag.rb +3 -3
- data/lib/scout/llm/tools/call.rb +160 -11
- data/lib/scout/llm/tools/knowledge_base.rb +1 -1
- data/lib/scout/llm/tools/workflow.rb +32 -16
- data/lib/scout/model/python/huggingface/causal.rb +23 -5
- data/lib/scout/model/python/huggingface.rb +2 -1
- data/lib/scout-ai.rb +1 -0
- data/python/README.md +197 -14
- data/python/scout_ai/huggingface/eval.py +245 -34
- data/python/tests/test_huggingface_eval.py +58 -0
- data/research/ChatAnalyst-required-changes.md +167 -0
- data/research/agent-delegation-analysis.md +810 -0
- data/research/agent-meta-provenance-integration-plan.md +622 -0
- data/research/agent-workflow-analysis.md +1120 -0
- data/research/backends-analysis.md +836 -0
- data/research/chat-core-analysis.md +946 -0
- data/research/chatanalyst-provenance/00-baseline.md +30 -0
- data/research/chatanalyst-provenance/01-repo-map.md +60 -0
- data/research/chatanalyst-provenance/02-event-reconstruction.md +55 -0
- data/research/chatanalyst-provenance/03-duplication-evidence.md +45 -0
- data/research/chatanalyst-provenance/04-tooling-root-cause.md +57 -0
- data/research/chatanalyst-provenance/05-fix-plan.md +46 -0
- data/research/chatanalyst-provenance/07-critic-review.md +25 -0
- data/research/chatanalyst-provenance/final-report.md +45 -0
- data/research/chatanalyst-provenance/resumption.md +37 -0
- data/research/coding-philosophy-analysis.md +928 -0
- data/research/commands-analysis.md +947 -0
- data/research/multi-agent-patterns-analysis.md +853 -0
- data/research/prompt-strategies-analysis.md +630 -0
- data/research/prov-verbosity-fix-notes.md +77 -0
- data/research/provenance-analysis.md +469 -0
- data/research/provenance-navigation-design.md +640 -0
- data/research/synthesis-report.md +487 -0
- data/research/tools-system-analysis.md +779 -0
- data/scout-ai.gemspec +100 -11
- data/scout_commands/agent/ask +13 -3
- data/scout_commands/agent/kb +2 -0
- data/scout_commands/llm/ask +11 -4
- data/scout_commands/llm/md +76 -0
- data/scout_commands/llm/process_queries +48 -0
- data/scout_commands/llm/prov +602 -0
- data/scout_commands/llm/word +71 -0
- data/scout_commands/workflow/mcp +43 -0
- data/share/word/reference.docx +0 -0
- data/test/etc/AI/mock.yaml +11 -0
- data/test/fixtures/backends/anthropic.json +19 -0
- data/test/fixtures/backends/anthropic_tool_use.json +24 -0
- data/test/fixtures/backends/bedrock.json +8 -0
- data/test/fixtures/backends/bedrock_embedding.json +3 -0
- data/test/fixtures/backends/bedrock_tool_use.json +17 -0
- data/test/fixtures/backends/ollama.json +16 -0
- data/test/fixtures/backends/ollama_tool_call.json +27 -0
- data/test/fixtures/backends/openai_chat.json +21 -0
- data/test/fixtures/backends/openai_chat_tool_call.json +31 -0
- data/test/fixtures/backends/responses.json +33 -0
- data/test/fixtures/backends/responses_tool_call.json +28 -0
- data/test/integration/README.md +32 -0
- data/test/integration/scout/llm/backends/test_endpoints.rb +34 -0
- data/test/integration/scout/llm/backends/test_openwebui.rb +61 -0
- data/test/integration/scout/llm/backends/test_relay.rb +52 -0
- data/test/integration/scout/llm/test_infrastructure.rb +74 -0
- data/test/{scout → integration/scout}/llm/test_mcp.rb +1 -1
- data/test/integration/scout/llm/tools/test_mcp.rb +42 -0
- data/test/integration/scout/model/test_base.rb +91 -0
- data/test/scout/llm/agent/test_chat.rb +8 -2
- data/test/scout/llm/agent/test_save.rb +413 -0
- data/test/scout/llm/agent/test_workflow.rb +110 -0
- data/test/scout/llm/backends/test_anthropic.rb +93 -10
- data/test/scout/llm/backends/test_bedrock.rb +118 -2
- data/test/scout/llm/backends/test_huggingface.rb +137 -42
- data/test/scout/llm/backends/test_ollama.rb +70 -20
- data/test/scout/llm/backends/test_openwebui.rb +42 -40
- data/test/scout/llm/backends/test_relay.rb +4 -2
- data/test/scout/llm/chat/agent_meta_fixtures.rb +131 -0
- data/test/scout/llm/chat/process/test_meta.rb +518 -0
- data/test/scout/llm/chat/process/test_normalize_usage.rb +183 -0
- data/test/scout/llm/chat/test_agent_meta.rb +357 -0
- data/test/scout/llm/chat/test_agent_meta_provenance.rb +467 -0
- data/test/scout/llm/chat/test_agent_meta_tokens.rb +594 -0
- data/test/scout/llm/chat/test_parse.rb +70 -15
- data/test/scout/llm/chat/test_prov_cli.rb +274 -0
- data/test/scout/llm/chat/test_provenance.rb +240 -0
- data/test/scout/llm/chat/test_tool_calls.rb +38 -0
- data/test/scout/llm/test_agent.rb +13 -36
- data/test/scout/llm/test_ask.rb +75 -52
- data/test/scout/llm/test_chat.rb +107 -13
- data/test/scout/llm/test_embed.rb +48 -0
- data/test/scout/llm/test_rag.rb +23 -16
- data/test/scout/llm/test_tools.rb +12 -1
- data/test/scout/llm/tools/test_knowledge_base.rb +0 -1
- data/test/scout/llm/tools/test_mcp.rb +5 -3
- data/test/scout/llm/tools/test_workflow.rb +23 -2
- data/test/scout/model/python/huggingface/causal/test_next_token.rb +11 -5
- data/test/scout/model/python/huggingface/test_causal.rb +9 -3
- data/test/scout/model/python/huggingface/test_classification.rb +11 -2
- data/test/scout/model/python/test_torch.rb +2 -0
- data/test/scout/model/python/torch/test_helpers.rb +4 -0
- data/test/scout/model/test_base.rb +4 -2
- data/test/support/availability.rb +231 -0
- data/test/support/fake_clients.rb +138 -0
- data/test/support/fixtures.rb +21 -0
- data/test/support/infrastructure_probes.rb +136 -0
- data/test/support/mock_backend.rb +215 -0
- data/test/test_helper.rb +32 -2
- metadata +99 -10
- data/doc/Agent.md +0 -327
- data/doc/Chat.md +0 -458
- data/doc/LLM.md +0 -340
- data/doc/RAG.md +0 -129
- data/scout_commands/documenter +0 -148
- data/test/scout/llm/backends/test_openai.rb +0 -192
- data/test/scout/llm/backends/test_responses.rb +0 -238
- data/test/scout/llm/test_parse.rb +0 -98
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Writing Chats
|
|
2
|
+
|
|
3
|
+
This page explains the Scout-AI chat-file format. It is intended for workflow
|
|
4
|
+
authors who want to write conversations by hand, inspect saved agent sessions,
|
|
5
|
+
or construct chat inputs for agents and workflows.
|
|
6
|
+
|
|
7
|
+
**You should read this if:** you want to write or read `.chat` files.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The basic format
|
|
12
|
+
|
|
13
|
+
A chat file is plain text. Each message is a **role name** followed by a colon,
|
|
14
|
+
a blank line, then the content:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
system:
|
|
18
|
+
|
|
19
|
+
You are a helpful assistant.
|
|
20
|
+
|
|
21
|
+
user:
|
|
22
|
+
|
|
23
|
+
What is 2 + 2?
|
|
24
|
+
|
|
25
|
+
assistant:
|
|
26
|
+
|
|
27
|
+
4.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Rules:
|
|
31
|
+
- The role name is the first non-blank token on the line, followed by `:`.
|
|
32
|
+
- A blank line separates the role header from the content.
|
|
33
|
+
- Content continues until the next role header or end of file.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Standard roles
|
|
38
|
+
|
|
39
|
+
| Role | Purpose | Content |
|
|
40
|
+
|------|---------|---------|
|
|
41
|
+
| `system` | System instructions | Text |
|
|
42
|
+
| `user` | User input | Text |
|
|
43
|
+
| `assistant` | Model response | Text |
|
|
44
|
+
|
|
45
|
+
These three are the core conversational roles. All others are processed and
|
|
46
|
+
consumed before inference — they configure the conversation but never appear
|
|
47
|
+
in what the model sees directly.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Configuration roles
|
|
52
|
+
|
|
53
|
+
These roles set options, declare tools, and import content. They are processed
|
|
54
|
+
during chat compilation and removed from the final message list.
|
|
55
|
+
|
|
56
|
+
### Setting options
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
option: model gpt-4o
|
|
60
|
+
option: temperature 0.7
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Options apply to the next inference call. Some options are **sticky** — they
|
|
64
|
+
persist across turns:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
endpoint: anthropic
|
|
68
|
+
model: claude-sonnet-4-20250514
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Declaring tools
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
tool: MyWorkflow task_name input1=value1 input2=value2
|
|
75
|
+
introduce: MyWorkflow
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `tool:` exposes a specific workflow task.
|
|
79
|
+
- `introduce:` exposes an entire workflow (all its tasks).
|
|
80
|
+
|
|
81
|
+
### Importing files
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
file: path/to/document.txt
|
|
85
|
+
directory: path/to/folder
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
File contents are wrapped in `<file name="...">` tags and inserted as user
|
|
89
|
+
messages.
|
|
90
|
+
|
|
91
|
+
### Importing other chats
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
import: other_chat.chat
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This inlines the full content of another chat file.
|
|
98
|
+
|
|
99
|
+
### MCP tools
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
mcp: https://api.example.com/mcp/
|
|
103
|
+
mcp: stdio my-mcp-command
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Tool call and result messages
|
|
109
|
+
|
|
110
|
+
When the model calls a tool, two messages are appended to the chat:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
function_call: {"name":"search","arguments":{"query":"ruby"},"id":"call_1"}
|
|
114
|
+
function_call_output: {"id":"call_1","content":"Search results..."}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
These are normally auto-generated. You rarely write them by hand, but you will
|
|
118
|
+
see them in saved agent sessions.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Comments
|
|
123
|
+
|
|
124
|
+
Lines starting with `#` are comments and are ignored:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
# This is a comment
|
|
128
|
+
system:
|
|
129
|
+
|
|
130
|
+
# So is this
|
|
131
|
+
You are a helpful assistant.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Metadata and provenance
|
|
137
|
+
|
|
138
|
+
When Scout-AI saves a conversation (e.g., as a workflow job output), it
|
|
139
|
+
annotates it with metadata:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
meta: job=/path/to/job pt_c=1000 ct_c=500
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This metadata records provenance — which job produced this chat, token counts,
|
|
146
|
+
and other bookkeeping. It is used by the provenance system to trace inference
|
|
147
|
+
trees.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Complete example
|
|
152
|
+
|
|
153
|
+
Here is a realistic chat file that configures an endpoint, declares tools,
|
|
154
|
+
imports a file, and asks a question:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
# Configuration
|
|
158
|
+
endpoint: anthropic
|
|
159
|
+
model: claude-sonnet-4-20250514
|
|
160
|
+
|
|
161
|
+
# System prompt
|
|
162
|
+
system:
|
|
163
|
+
|
|
164
|
+
You are a code analyst. Use the provided tools to answer questions about
|
|
165
|
+
the codebase.
|
|
166
|
+
|
|
167
|
+
# Give the model a workflow as tools
|
|
168
|
+
introduce: CodeAnalyzer
|
|
169
|
+
|
|
170
|
+
# Import context
|
|
171
|
+
file: src/main.rb
|
|
172
|
+
|
|
173
|
+
# The question
|
|
174
|
+
user:
|
|
175
|
+
|
|
176
|
+
What design patterns are used in main.rb?
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Common mistakes
|
|
182
|
+
|
|
183
|
+
- **Forgetting the blank line** between the role header and content. Without
|
|
184
|
+
it, the role header and content may merge.
|
|
185
|
+
- **Using unknown role names.** Only recognized roles are processed; unknown
|
|
186
|
+
ones are treated as literal user messages.
|
|
187
|
+
- **Expecting configuration roles to appear in the model's prompt.** Roles
|
|
188
|
+
like `tool:`, `option:`, `file:` are compiled away — they configure the
|
|
189
|
+
conversation but do not become visible messages.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Next steps
|
|
194
|
+
|
|
195
|
+
- [BuildingAgents.md](BuildingAgents.md) — create agents that use chats.
|
|
196
|
+
- [ToolCalling.md](ToolCalling.md) — detailed tool declaration syntax.
|
|
197
|
+
- [RunningInference.md](RunningInference.md) — endpoint and model configuration.
|
data/lib/scout/llm/agent/chat.rb
CHANGED
|
@@ -5,6 +5,15 @@ module LLM
|
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def start(chat=nil)
|
|
8
|
+
# Restart hook: keep the pre-restart conversation recoverable before it
|
|
9
|
+
# is replaced. Lazy (only with a prior non-empty chat + configured
|
|
10
|
+
# save_file) and never fatal.
|
|
11
|
+
begin
|
|
12
|
+
save_restart_snapshot
|
|
13
|
+
rescue
|
|
14
|
+
Log.warn "Agent restart snapshot failed: #{$!.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
8
17
|
if chat
|
|
9
18
|
(@current_chat || start_chat).annotate chat unless Chat === chat
|
|
10
19
|
@current_chat = chat
|
|
@@ -27,24 +36,38 @@ module LLM
|
|
|
27
36
|
self.ask(current_chat, ...)
|
|
28
37
|
end
|
|
29
38
|
|
|
30
|
-
|
|
31
39
|
def chat(options = {})
|
|
32
40
|
response = ask(current_chat, options.merge(return_messages: true))
|
|
33
41
|
if Array === response
|
|
34
42
|
current_chat.concat(response)
|
|
35
|
-
|
|
43
|
+
if options[:return_messages]
|
|
44
|
+
response
|
|
45
|
+
else
|
|
46
|
+
current_chat.answer
|
|
47
|
+
end
|
|
36
48
|
else
|
|
37
49
|
current_chat.push({role: :assistant, content: response})
|
|
38
50
|
response
|
|
39
51
|
end
|
|
52
|
+
ensure
|
|
53
|
+
# Auto-save once the conversation has been updated by this chat round.
|
|
54
|
+
# Non-fatal by design: a save problem must never break the agent run.
|
|
55
|
+
begin
|
|
56
|
+
save_if_configured
|
|
57
|
+
rescue
|
|
58
|
+
Log.warn "Agent auto-save after chat failed: #{$!.message}"
|
|
59
|
+
end
|
|
40
60
|
end
|
|
41
61
|
|
|
62
|
+
def create_image(file, options = {})
|
|
63
|
+
current_chat.create_image(file, @other_options.merge(options))
|
|
64
|
+
end
|
|
42
65
|
|
|
43
66
|
def json(...)
|
|
44
67
|
current_chat.format :json
|
|
45
|
-
output =
|
|
68
|
+
output = chat(...)
|
|
46
69
|
current_chat.format nil
|
|
47
|
-
obj =
|
|
70
|
+
obj = Chat.parse_json output
|
|
48
71
|
if (Hash === obj) and obj.keys == ['content']
|
|
49
72
|
obj['content']
|
|
50
73
|
else
|
|
@@ -53,14 +76,12 @@ module LLM
|
|
|
53
76
|
end
|
|
54
77
|
|
|
55
78
|
def json_format(format, ...)
|
|
79
|
+
old_format = current_chat.remove_role :format
|
|
56
80
|
current_chat.format format
|
|
57
|
-
output =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Log.warn "Not valid JSON:" + output
|
|
62
|
-
raise $!
|
|
63
|
-
end
|
|
81
|
+
output = chat(...)
|
|
82
|
+
current_chat.remove_role :format
|
|
83
|
+
current_chat.concat old_format
|
|
84
|
+
obj = Chat.parse_json output
|
|
64
85
|
if (Hash === obj) and obj.keys == ['content']
|
|
65
86
|
obj['content']
|
|
66
87
|
else
|
|
@@ -68,6 +89,35 @@ module LLM
|
|
|
68
89
|
end
|
|
69
90
|
end
|
|
70
91
|
|
|
92
|
+
#def json(...)
|
|
93
|
+
# current_chat.format :json
|
|
94
|
+
# output = ask(current_chat, ...)
|
|
95
|
+
# current_chat.format nil
|
|
96
|
+
# obj = Chat.parse_json output
|
|
97
|
+
# if (Hash === obj) and obj.keys == ['content']
|
|
98
|
+
# obj['content']
|
|
99
|
+
# else
|
|
100
|
+
# obj
|
|
101
|
+
# end
|
|
102
|
+
#end
|
|
103
|
+
|
|
104
|
+
#def json_format(format, options = {})
|
|
105
|
+
# current_chat.format format
|
|
106
|
+
# output = ask(current_chat, options.merge({return_messages: false}))
|
|
107
|
+
# current_chat.format nil
|
|
108
|
+
# obj = begin
|
|
109
|
+
# obj = Chat.parse_json output
|
|
110
|
+
# rescue JSON::ParserError
|
|
111
|
+
# Log.warn "Not valid JSON:" + output
|
|
112
|
+
# raise $!
|
|
113
|
+
# end
|
|
114
|
+
# if (Hash === obj) and obj.keys == ['content']
|
|
115
|
+
# obj['content']
|
|
116
|
+
# else
|
|
117
|
+
# obj
|
|
118
|
+
# end
|
|
119
|
+
#end
|
|
120
|
+
|
|
71
121
|
def get_previous_response_id
|
|
72
122
|
msg = current_chat.reverse.find{|msg| msg[:role].to_sym == :previous_response_id }
|
|
73
123
|
msg.nil? ? nil : msg['content']
|