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.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +138 -50
  3. data/README.md +171 -290
  4. data/Rakefile +17 -1
  5. data/VERSION +1 -1
  6. data/doc/Improvements.md +325 -0
  7. data/doc/StartHere.md +110 -0
  8. data/doc/developer/Architecture.md +126 -0
  9. data/doc/developer/Backends.md +199 -0
  10. data/doc/developer/ChatLifecycle.md +183 -0
  11. data/doc/developer/DelegationInternals.md +295 -0
  12. data/doc/developer/DesignPrinciples.md +245 -0
  13. data/doc/developer/PromptProcessing.md +292 -0
  14. data/doc/developer/Provenance.md +317 -0
  15. data/doc/user/BuildingAgents.md +345 -0
  16. data/doc/user/Cookbook.md +333 -0
  17. data/doc/user/CoreConcepts.md +181 -0
  18. data/doc/user/Delegation.md +191 -0
  19. data/doc/user/GettingStarted.md +159 -0
  20. data/doc/user/ManagingContext.md +163 -0
  21. data/doc/user/MultiAgentWorkflows.md +256 -0
  22. data/doc/user/Python.md +159 -0
  23. data/doc/user/RunningInference.md +200 -0
  24. data/doc/user/ToolCalling.md +193 -0
  25. data/doc/user/WritingChats.md +197 -0
  26. data/lib/scout/llm/agent/chat.rb +61 -11
  27. data/lib/scout/llm/agent/delegate.rb +274 -65
  28. data/lib/scout/llm/agent/iterate.rb +2 -2
  29. data/lib/scout/llm/agent/save.rb +273 -0
  30. data/lib/scout/llm/agent/workflow.rb +164 -0
  31. data/lib/scout/llm/agent.rb +86 -61
  32. data/lib/scout/llm/ask.rb +62 -17
  33. data/lib/scout/llm/backends/anthropic.rb +9 -2
  34. data/lib/scout/llm/backends/bedrock.rb +15 -3
  35. data/lib/scout/llm/backends/default.rb +183 -99
  36. data/lib/scout/llm/backends/glm.rb +58 -0
  37. data/lib/scout/llm/backends/huggingface.rb +196 -26
  38. data/lib/scout/llm/backends/ollama.rb +13 -1
  39. data/lib/scout/llm/backends/openai.rb +0 -2
  40. data/lib/scout/llm/backends/openwebui.rb +20 -13
  41. data/lib/scout/llm/backends/relay.rb +22 -22
  42. data/lib/scout/llm/backends/responses.rb +1 -1
  43. data/lib/scout/llm/chat/agent_meta.rb +264 -0
  44. data/lib/scout/llm/chat/annotation.rb +39 -10
  45. data/lib/scout/llm/chat/parse.rb +28 -6
  46. data/lib/scout/llm/chat/persist.rb +25 -0
  47. data/lib/scout/llm/chat/process/clear.rb +41 -6
  48. data/lib/scout/llm/chat/process/files.rb +21 -6
  49. data/lib/scout/llm/chat/process/meta.rb +421 -34
  50. data/lib/scout/llm/chat/process/options.rb +21 -1
  51. data/lib/scout/llm/chat/process/tools.rb +56 -15
  52. data/lib/scout/llm/chat/process.rb +4 -0
  53. data/lib/scout/llm/chat/prompt/shorten_tools.rb +125 -0
  54. data/lib/scout/llm/chat/prompt/shorten_tools_epoch.rb +365 -0
  55. data/lib/scout/llm/chat/prompt.rb +48 -0
  56. data/lib/scout/llm/chat/provenance.rb +775 -0
  57. data/lib/scout/llm/chat/tool_calls.rb +76 -0
  58. data/lib/scout/llm/chat.rb +18 -2
  59. data/lib/scout/llm/embed.rb +11 -3
  60. data/lib/scout/llm/image.rb +86 -0
  61. data/lib/scout/llm/mcp.rb +10 -2
  62. data/lib/scout/llm/rag.rb +3 -3
  63. data/lib/scout/llm/tools/call.rb +160 -11
  64. data/lib/scout/llm/tools/knowledge_base.rb +1 -1
  65. data/lib/scout/llm/tools/workflow.rb +32 -16
  66. data/lib/scout/model/python/huggingface/causal.rb +23 -5
  67. data/lib/scout/model/python/huggingface.rb +2 -1
  68. data/lib/scout-ai.rb +1 -0
  69. data/python/README.md +197 -14
  70. data/python/scout_ai/huggingface/eval.py +245 -34
  71. data/python/tests/test_huggingface_eval.py +58 -0
  72. data/research/ChatAnalyst-required-changes.md +167 -0
  73. data/research/agent-delegation-analysis.md +810 -0
  74. data/research/agent-meta-provenance-integration-plan.md +622 -0
  75. data/research/agent-workflow-analysis.md +1120 -0
  76. data/research/backends-analysis.md +836 -0
  77. data/research/chat-core-analysis.md +946 -0
  78. data/research/chatanalyst-provenance/00-baseline.md +30 -0
  79. data/research/chatanalyst-provenance/01-repo-map.md +60 -0
  80. data/research/chatanalyst-provenance/02-event-reconstruction.md +55 -0
  81. data/research/chatanalyst-provenance/03-duplication-evidence.md +45 -0
  82. data/research/chatanalyst-provenance/04-tooling-root-cause.md +57 -0
  83. data/research/chatanalyst-provenance/05-fix-plan.md +46 -0
  84. data/research/chatanalyst-provenance/07-critic-review.md +25 -0
  85. data/research/chatanalyst-provenance/final-report.md +45 -0
  86. data/research/chatanalyst-provenance/resumption.md +37 -0
  87. data/research/coding-philosophy-analysis.md +928 -0
  88. data/research/commands-analysis.md +947 -0
  89. data/research/multi-agent-patterns-analysis.md +853 -0
  90. data/research/prompt-strategies-analysis.md +630 -0
  91. data/research/prov-verbosity-fix-notes.md +77 -0
  92. data/research/provenance-analysis.md +469 -0
  93. data/research/provenance-navigation-design.md +640 -0
  94. data/research/synthesis-report.md +487 -0
  95. data/research/tools-system-analysis.md +779 -0
  96. data/scout-ai.gemspec +100 -11
  97. data/scout_commands/agent/ask +13 -3
  98. data/scout_commands/agent/kb +2 -0
  99. data/scout_commands/llm/ask +11 -4
  100. data/scout_commands/llm/md +76 -0
  101. data/scout_commands/llm/process_queries +48 -0
  102. data/scout_commands/llm/prov +602 -0
  103. data/scout_commands/llm/word +71 -0
  104. data/scout_commands/workflow/mcp +43 -0
  105. data/share/word/reference.docx +0 -0
  106. data/test/etc/AI/mock.yaml +11 -0
  107. data/test/fixtures/backends/anthropic.json +19 -0
  108. data/test/fixtures/backends/anthropic_tool_use.json +24 -0
  109. data/test/fixtures/backends/bedrock.json +8 -0
  110. data/test/fixtures/backends/bedrock_embedding.json +3 -0
  111. data/test/fixtures/backends/bedrock_tool_use.json +17 -0
  112. data/test/fixtures/backends/ollama.json +16 -0
  113. data/test/fixtures/backends/ollama_tool_call.json +27 -0
  114. data/test/fixtures/backends/openai_chat.json +21 -0
  115. data/test/fixtures/backends/openai_chat_tool_call.json +31 -0
  116. data/test/fixtures/backends/responses.json +33 -0
  117. data/test/fixtures/backends/responses_tool_call.json +28 -0
  118. data/test/integration/README.md +32 -0
  119. data/test/integration/scout/llm/backends/test_endpoints.rb +34 -0
  120. data/test/integration/scout/llm/backends/test_openwebui.rb +61 -0
  121. data/test/integration/scout/llm/backends/test_relay.rb +52 -0
  122. data/test/integration/scout/llm/test_infrastructure.rb +74 -0
  123. data/test/{scout → integration/scout}/llm/test_mcp.rb +1 -1
  124. data/test/integration/scout/llm/tools/test_mcp.rb +42 -0
  125. data/test/integration/scout/model/test_base.rb +91 -0
  126. data/test/scout/llm/agent/test_chat.rb +8 -2
  127. data/test/scout/llm/agent/test_save.rb +413 -0
  128. data/test/scout/llm/agent/test_workflow.rb +110 -0
  129. data/test/scout/llm/backends/test_anthropic.rb +93 -10
  130. data/test/scout/llm/backends/test_bedrock.rb +118 -2
  131. data/test/scout/llm/backends/test_huggingface.rb +137 -42
  132. data/test/scout/llm/backends/test_ollama.rb +70 -20
  133. data/test/scout/llm/backends/test_openwebui.rb +42 -40
  134. data/test/scout/llm/backends/test_relay.rb +4 -2
  135. data/test/scout/llm/chat/agent_meta_fixtures.rb +131 -0
  136. data/test/scout/llm/chat/process/test_meta.rb +518 -0
  137. data/test/scout/llm/chat/process/test_normalize_usage.rb +183 -0
  138. data/test/scout/llm/chat/test_agent_meta.rb +357 -0
  139. data/test/scout/llm/chat/test_agent_meta_provenance.rb +467 -0
  140. data/test/scout/llm/chat/test_agent_meta_tokens.rb +594 -0
  141. data/test/scout/llm/chat/test_parse.rb +70 -15
  142. data/test/scout/llm/chat/test_prov_cli.rb +274 -0
  143. data/test/scout/llm/chat/test_provenance.rb +240 -0
  144. data/test/scout/llm/chat/test_tool_calls.rb +38 -0
  145. data/test/scout/llm/test_agent.rb +13 -36
  146. data/test/scout/llm/test_ask.rb +75 -52
  147. data/test/scout/llm/test_chat.rb +107 -13
  148. data/test/scout/llm/test_embed.rb +48 -0
  149. data/test/scout/llm/test_rag.rb +23 -16
  150. data/test/scout/llm/test_tools.rb +12 -1
  151. data/test/scout/llm/tools/test_knowledge_base.rb +0 -1
  152. data/test/scout/llm/tools/test_mcp.rb +5 -3
  153. data/test/scout/llm/tools/test_workflow.rb +23 -2
  154. data/test/scout/model/python/huggingface/causal/test_next_token.rb +11 -5
  155. data/test/scout/model/python/huggingface/test_causal.rb +9 -3
  156. data/test/scout/model/python/huggingface/test_classification.rb +11 -2
  157. data/test/scout/model/python/test_torch.rb +2 -0
  158. data/test/scout/model/python/torch/test_helpers.rb +4 -0
  159. data/test/scout/model/test_base.rb +4 -2
  160. data/test/support/availability.rb +231 -0
  161. data/test/support/fake_clients.rb +138 -0
  162. data/test/support/fixtures.rb +21 -0
  163. data/test/support/infrastructure_probes.rb +136 -0
  164. data/test/support/mock_backend.rb +215 -0
  165. data/test/test_helper.rb +32 -2
  166. metadata +99 -10
  167. data/doc/Agent.md +0 -327
  168. data/doc/Chat.md +0 -458
  169. data/doc/LLM.md +0 -340
  170. data/doc/RAG.md +0 -129
  171. data/scout_commands/documenter +0 -148
  172. data/test/scout/llm/backends/test_openai.rb +0 -192
  173. data/test/scout/llm/backends/test_responses.rb +0 -238
  174. 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.
@@ -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
- current_chat.answer
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 = ask(current_chat, ...)
68
+ output = chat(...)
46
69
  current_chat.format nil
47
- obj = JSON.parse output
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 = ask(current_chat, ...)
58
- obj = begin
59
- JSON.parse output
60
- rescue JSON::ParserError
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']