igniter 0.4.5 → 0.5.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 (154) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +217 -0
  3. data/docs/APPLICATION_V1.md +253 -0
  4. data/docs/CAPABILITIES_V1.md +207 -0
  5. data/docs/CONSENSUS_V1.md +477 -0
  6. data/docs/CONTENT_ADDRESSING_V1.md +221 -0
  7. data/docs/DATAFLOW_V1.md +274 -0
  8. data/docs/MESH_V1.md +732 -0
  9. data/docs/NODE_CACHE_V1.md +324 -0
  10. data/docs/PROACTIVE_AGENTS_V1.md +293 -0
  11. data/docs/SERVER_V1.md +200 -1
  12. data/docs/SKILLS_V1.md +213 -0
  13. data/docs/STORE_ADAPTERS.md +41 -13
  14. data/docs/TEMPORAL_V1.md +174 -0
  15. data/docs/TOOLS_V1.md +347 -0
  16. data/docs/TRANSCRIPTION_V1.md +403 -0
  17. data/examples/README.md +37 -0
  18. data/examples/consensus.rb +239 -0
  19. data/examples/dataflow.rb +308 -0
  20. data/examples/elocal_webhook.rb +1 -0
  21. data/examples/llm_tools.rb +237 -0
  22. data/examples/mesh.rb +239 -0
  23. data/examples/mesh_discovery.rb +267 -0
  24. data/examples/mesh_gossip.rb +162 -0
  25. data/examples/ringcentral_routing.rb +1 -1
  26. data/lib/igniter/agents/ai/alert_agent.rb +111 -0
  27. data/lib/igniter/agents/ai/chain_agent.rb +127 -0
  28. data/lib/igniter/agents/ai/critic_agent.rb +163 -0
  29. data/lib/igniter/agents/ai/evaluator_agent.rb +193 -0
  30. data/lib/igniter/agents/ai/evolution_agent.rb +286 -0
  31. data/lib/igniter/agents/ai/health_check_agent.rb +122 -0
  32. data/lib/igniter/agents/ai/observer_agent.rb +184 -0
  33. data/lib/igniter/agents/ai/planner_agent.rb +210 -0
  34. data/lib/igniter/agents/ai/router_agent.rb +131 -0
  35. data/lib/igniter/agents/ai/self_reflection_agent.rb +175 -0
  36. data/lib/igniter/agents/observability/metrics_agent.rb +130 -0
  37. data/lib/igniter/agents/pipeline/batch_processor_agent.rb +131 -0
  38. data/lib/igniter/agents/proactive_agent.rb +208 -0
  39. data/lib/igniter/agents/reliability/retry_agent.rb +99 -0
  40. data/lib/igniter/agents/scheduling/cron_agent.rb +110 -0
  41. data/lib/igniter/agents.rb +56 -0
  42. data/lib/igniter/application/app_config.rb +32 -0
  43. data/lib/igniter/application/autoloader.rb +18 -0
  44. data/lib/igniter/application/generator.rb +157 -0
  45. data/lib/igniter/application/scheduler.rb +109 -0
  46. data/lib/igniter/application/yml_loader.rb +39 -0
  47. data/lib/igniter/application.rb +174 -0
  48. data/lib/igniter/capabilities.rb +68 -0
  49. data/lib/igniter/compiler/validators/dependencies_validator.rb +50 -2
  50. data/lib/igniter/compiler/validators/remote_validator.rb +2 -0
  51. data/lib/igniter/consensus/cluster.rb +183 -0
  52. data/lib/igniter/consensus/errors.rb +14 -0
  53. data/lib/igniter/consensus/executors.rb +43 -0
  54. data/lib/igniter/consensus/node.rb +320 -0
  55. data/lib/igniter/consensus/read_query.rb +30 -0
  56. data/lib/igniter/consensus/state_machine.rb +58 -0
  57. data/lib/igniter/consensus.rb +58 -0
  58. data/lib/igniter/content_addressing.rb +133 -0
  59. data/lib/igniter/contract.rb +12 -0
  60. data/lib/igniter/dataflow/aggregate_operators.rb +147 -0
  61. data/lib/igniter/dataflow/aggregate_state.rb +77 -0
  62. data/lib/igniter/dataflow/diff.rb +37 -0
  63. data/lib/igniter/dataflow/diff_state.rb +81 -0
  64. data/lib/igniter/dataflow/incremental_collection_result.rb +39 -0
  65. data/lib/igniter/dataflow/window_filter.rb +48 -0
  66. data/lib/igniter/dataflow.rb +65 -0
  67. data/lib/igniter/dsl/contract_builder.rb +71 -7
  68. data/lib/igniter/executor.rb +60 -0
  69. data/lib/igniter/extensions/capabilities.rb +39 -0
  70. data/lib/igniter/extensions/content_addressing.rb +5 -0
  71. data/lib/igniter/extensions/dataflow.rb +117 -0
  72. data/lib/igniter/extensions/mesh.rb +31 -0
  73. data/lib/igniter/fingerprint.rb +43 -0
  74. data/lib/igniter/integrations/llm/config.rb +48 -4
  75. data/lib/igniter/integrations/llm/executor.rb +221 -28
  76. data/lib/igniter/integrations/llm/providers/anthropic.rb +37 -4
  77. data/lib/igniter/integrations/llm/providers/openai.rb +34 -5
  78. data/lib/igniter/integrations/llm/transcription/providers/assemblyai.rb +200 -0
  79. data/lib/igniter/integrations/llm/transcription/providers/base.rb +122 -0
  80. data/lib/igniter/integrations/llm/transcription/providers/deepgram.rb +162 -0
  81. data/lib/igniter/integrations/llm/transcription/providers/openai.rb +102 -0
  82. data/lib/igniter/integrations/llm/transcription/transcriber.rb +145 -0
  83. data/lib/igniter/integrations/llm/transcription/transcript_result.rb +29 -0
  84. data/lib/igniter/integrations/llm.rb +37 -1
  85. data/lib/igniter/memory/agent_memory.rb +104 -0
  86. data/lib/igniter/memory/episode.rb +29 -0
  87. data/lib/igniter/memory/fact.rb +27 -0
  88. data/lib/igniter/memory/memorable.rb +90 -0
  89. data/lib/igniter/memory/reflection_cycle.rb +96 -0
  90. data/lib/igniter/memory/reflection_record.rb +28 -0
  91. data/lib/igniter/memory/store.rb +115 -0
  92. data/lib/igniter/memory/stores/in_memory.rb +136 -0
  93. data/lib/igniter/memory/stores/sqlite.rb +284 -0
  94. data/lib/igniter/memory.rb +80 -0
  95. data/lib/igniter/mesh/announcer.rb +55 -0
  96. data/lib/igniter/mesh/config.rb +45 -0
  97. data/lib/igniter/mesh/discovery.rb +39 -0
  98. data/lib/igniter/mesh/errors.rb +31 -0
  99. data/lib/igniter/mesh/gossip.rb +47 -0
  100. data/lib/igniter/mesh/peer.rb +21 -0
  101. data/lib/igniter/mesh/peer_registry.rb +51 -0
  102. data/lib/igniter/mesh/poller.rb +77 -0
  103. data/lib/igniter/mesh/router.rb +109 -0
  104. data/lib/igniter/mesh.rb +85 -0
  105. data/lib/igniter/metrics/collector.rb +131 -0
  106. data/lib/igniter/metrics/prometheus_exporter.rb +104 -0
  107. data/lib/igniter/metrics/snapshot.rb +8 -0
  108. data/lib/igniter/metrics.rb +37 -0
  109. data/lib/igniter/model/aggregate_node.rb +34 -0
  110. data/lib/igniter/model/collection_node.rb +3 -2
  111. data/lib/igniter/model/compute_node.rb +13 -0
  112. data/lib/igniter/model/remote_node.rb +18 -2
  113. data/lib/igniter/node_cache.rb +231 -0
  114. data/lib/igniter/replication/bootstrapper.rb +61 -0
  115. data/lib/igniter/replication/bootstrappers/gem.rb +32 -0
  116. data/lib/igniter/replication/bootstrappers/git.rb +39 -0
  117. data/lib/igniter/replication/bootstrappers/tarball.rb +56 -0
  118. data/lib/igniter/replication/expansion_plan.rb +38 -0
  119. data/lib/igniter/replication/expansion_planner.rb +142 -0
  120. data/lib/igniter/replication/manifest.rb +45 -0
  121. data/lib/igniter/replication/network_topology.rb +123 -0
  122. data/lib/igniter/replication/node_role.rb +42 -0
  123. data/lib/igniter/replication/reflective_replication_agent.rb +238 -0
  124. data/lib/igniter/replication/replication_agent.rb +87 -0
  125. data/lib/igniter/replication/role_registry.rb +73 -0
  126. data/lib/igniter/replication/ssh_session.rb +77 -0
  127. data/lib/igniter/replication.rb +54 -0
  128. data/lib/igniter/runtime/execution.rb +18 -0
  129. data/lib/igniter/runtime/input_validator.rb +6 -2
  130. data/lib/igniter/runtime/resolver.rb +254 -16
  131. data/lib/igniter/runtime/stores/redis_store.rb +41 -4
  132. data/lib/igniter/server/client.rb +44 -1
  133. data/lib/igniter/server/config.rb +13 -6
  134. data/lib/igniter/server/handlers/event_handler.rb +4 -0
  135. data/lib/igniter/server/handlers/execute_handler.rb +6 -0
  136. data/lib/igniter/server/handlers/liveness_handler.rb +20 -0
  137. data/lib/igniter/server/handlers/manifest_handler.rb +34 -0
  138. data/lib/igniter/server/handlers/metrics_handler.rb +51 -0
  139. data/lib/igniter/server/handlers/peers_handler.rb +115 -0
  140. data/lib/igniter/server/handlers/readiness_handler.rb +47 -0
  141. data/lib/igniter/server/http_server.rb +54 -17
  142. data/lib/igniter/server/router.rb +54 -21
  143. data/lib/igniter/server/server_logger.rb +52 -0
  144. data/lib/igniter/server.rb +6 -0
  145. data/lib/igniter/skill/feedback.rb +116 -0
  146. data/lib/igniter/skill/output_schema.rb +110 -0
  147. data/lib/igniter/skill.rb +218 -0
  148. data/lib/igniter/temporal.rb +84 -0
  149. data/lib/igniter/tool/discoverable.rb +151 -0
  150. data/lib/igniter/tool.rb +52 -0
  151. data/lib/igniter/tool_registry.rb +144 -0
  152. data/lib/igniter/version.rb +1 -1
  153. data/lib/igniter.rb +17 -0
  154. metadata +122 -1
data/docs/TOOLS_V1.md ADDED
@@ -0,0 +1,347 @@
1
+ # Igniter::Tool — AI-Callable Tools — v1
2
+
3
+ `Igniter::Tool` bridges declarative Igniter primitives with LLM function-calling APIs.
4
+ Each tool is an `Igniter::Executor` subclass enriched with metadata — so it can be
5
+ used both as a standard compute node in any `Igniter::Contract` graph AND as an
6
+ AI-callable function with auto-generated JSON schemas for Anthropic / OpenAI.
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ ```
13
+ Igniter::Tool < Igniter::Executor
14
+
15
+ ├── description "..." ← for LLM context
16
+ ├── param :name, type:, ... ← auto-generates JSON Schema
17
+ ├── requires_capability :x ← capability guard (enforced before call)
18
+
19
+ ├── to_schema(:anthropic) ← Anthropic tool definition Hash
20
+ ├── to_schema(:openai) ← OpenAI tool definition Hash
21
+
22
+ └── def call(**kwargs) ← implementation
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Defining a tool
28
+
29
+ ```ruby
30
+ require "igniter/tool"
31
+
32
+ class DatabaseLookup < Igniter::Tool
33
+ description "Look up a product by SKU in the catalog"
34
+
35
+ param :sku, type: :string, required: true, desc: "Product SKU identifier"
36
+ param :fields, type: :array, default: nil, desc: "Fields to return (nil = all)"
37
+
38
+ requires_capability :database_read # LLM executor must declare this capability
39
+
40
+ def call(sku:, fields: nil)
41
+ product = ProductCatalog.find(sku)
42
+ fields ? product.slice(*fields) : product
43
+ end
44
+ end
45
+ ```
46
+
47
+ ### Supported param types
48
+
49
+ | Symbol | JSON type |
50
+ |--------|-----------|
51
+ | `:string` | `"string"` |
52
+ | `:integer` | `"integer"` |
53
+ | `:float` | `"number"` |
54
+ | `:boolean` | `"boolean"` |
55
+ | `:array` | `"array"` |
56
+ | `:object` | `"object"` |
57
+
58
+ ---
59
+
60
+ ## Schema generation
61
+
62
+ ```ruby
63
+ DatabaseLookup.tool_name # => "database_lookup" (ClassName → snake_case)
64
+
65
+ # Intermediate format (used internally by Igniter, processed by normalize_tools)
66
+ DatabaseLookup.to_schema
67
+ # => { name: "database_lookup", description: "...", parameters: { ... } }
68
+
69
+ # Provider-specific final formats
70
+ DatabaseLookup.to_schema(:anthropic)
71
+ # => { name: "database_lookup", description: "...", input_schema: { ... } }
72
+
73
+ DatabaseLookup.to_schema(:openai)
74
+ # => { type: "function", function: { name: "database_lookup", ... } }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## `Igniter::ToolRegistry` — discovery and schema export
80
+
81
+ ```ruby
82
+ require "igniter/tool_registry"
83
+
84
+ # Global registration (typically in an initializer)
85
+ Igniter::ToolRegistry.register(Calculator, DatabaseLookup, SendEmail)
86
+
87
+ # Discovery
88
+ Igniter::ToolRegistry.all # => [Calculator, DatabaseLookup, SendEmail]
89
+ Igniter::ToolRegistry.find("calculator") # => Calculator
90
+
91
+ # Capability filtering — only tools the agent is authorized to call
92
+ Igniter::ToolRegistry.tools_for(capabilities: [:database_read])
93
+ # => [Calculator, DatabaseLookup] (SendEmail needs :email_send)
94
+
95
+ # Schema export
96
+ Igniter::ToolRegistry.schemas # intermediate, all tools
97
+ Igniter::ToolRegistry.schemas(:anthropic) # Anthropic format, all
98
+ Igniter::ToolRegistry.schemas(:openai, capabilities: [:database_read]) # filtered
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Capability guard
104
+
105
+ `requires_capability` declares what the calling agent must be allowed to do.
106
+ The guard runs **before `call`** — if the agent lacks a required capability,
107
+ `Igniter::Tool::CapabilityError` is raised immediately.
108
+
109
+ ```ruby
110
+ class SendEmail < Igniter::Tool
111
+ description "Send an email to a recipient"
112
+ param :to, type: :string, required: true
113
+ param :subject, type: :string, required: true
114
+ param :body, type: :string, required: true
115
+ requires_capability :email_send
116
+
117
+ def call(to:, subject:, body:)
118
+ EmailService.deliver(to: to, subject: subject, body: body)
119
+ { sent: true }
120
+ end
121
+ end
122
+
123
+ # Direct guard check
124
+ SendEmail.new.call_with_capability_check!(
125
+ allowed_capabilities: [:email_send],
126
+ to: "user@example.com", subject: "Hello", body: "..."
127
+ )
128
+ # => { sent: true }
129
+
130
+ SendEmail.new.call_with_capability_check!(
131
+ allowed_capabilities: [], # missing :email_send
132
+ to: "user@example.com", ...
133
+ )
134
+ # => Igniter::Tool::CapabilityError:
135
+ # Tool "send_email" requires capabilities [:email_send] but agent only has []
136
+ ```
137
+
138
+ The agent's capabilities come from the `Igniter::Executor.capabilities` DSL
139
+ inherited by `LLM::Executor`:
140
+
141
+ ```ruby
142
+ class SupportAgent < Igniter::LLM::Executor
143
+ capabilities :database_read, :email_send # what this agent may do
144
+ tools DatabaseLookup, SendEmail
145
+ ...
146
+ end
147
+ ```
148
+
149
+ ---
150
+
151
+ ## LLM::Executor — automatic tool-use loop
152
+
153
+ When `tools` DSL contains `Igniter::Tool` subclasses, `#complete` runs an
154
+ automatic loop:
155
+
156
+ ```
157
+ LLM request + tool schemas
158
+
159
+
160
+ ┌─────────────────┐
161
+ │ LLM response │── text only ──► return text
162
+ └─────────────────┘
163
+ │ tool_use blocks
164
+
165
+ CapabilityGuard.check! ← raises CapabilityError if cap missing
166
+
167
+
168
+ Tool#call(**arguments) ← error text returned if StandardError
169
+
170
+
171
+ Append :tool_results message
172
+
173
+ └──► repeat (up to max_tool_iterations)
174
+ ```
175
+
176
+ ```ruby
177
+ class ProductAssistant < Igniter::LLM::Executor
178
+ provider :anthropic
179
+ model "claude-haiku-4-5-20251001"
180
+ system_prompt "You are a product assistant. Use tools when needed."
181
+
182
+ tools Calculator, DatabaseLookup, SendEmail
183
+ capabilities :database_read, :email_send # authorizes these tools
184
+ max_tool_iterations 8 # default: 10
185
+
186
+ def call(question:)
187
+ complete(question)
188
+ # complete() automatically:
189
+ # 1. Sends tool schemas to Anthropic API
190
+ # 2. Handles tool_use responses in a loop
191
+ # 3. Returns final text when LLM stops calling tools
192
+ end
193
+ end
194
+
195
+ assistant = ProductAssistant.new
196
+ answer = assistant.call(question: "What's the price of SKU-001 and apply 15% discount?")
197
+ puts answer
198
+ # "Widget Pro (SKU-001) costs $29.99. With a 15% discount, the final price is $25.49."
199
+ ```
200
+
201
+ ### Error handling in the loop
202
+
203
+ | Situation | Behaviour |
204
+ |-----------|-----------|
205
+ | Tool raises `CapabilityError` | Re-raised immediately — loop stops |
206
+ | Tool raises any other error | Error message string returned as tool result; LLM can recover |
207
+ | Unknown tool name in response | `"Unknown tool: ..."` returned as tool result |
208
+ | Loop exceeds `max_tool_iterations` | `Igniter::LLM::ToolLoopError` raised |
209
+
210
+ ---
211
+
212
+ ## Tool as a Contract compute node
213
+
214
+ `Tool < Executor` — full backward compatibility. Use a tool anywhere an executor is used:
215
+
216
+ ```ruby
217
+ class PriceReport < Igniter::Contract
218
+ define do
219
+ input :sku
220
+
221
+ compute :product, with: :sku, call: DatabaseLookup
222
+ compute :discount, with: :product, call: DiscountCalculator
223
+ compute :report, with: [:product, :discount], call: ReportFormatter
224
+
225
+ output :report
226
+ end
227
+ end
228
+
229
+ report = PriceReport.new(sku: "SKU-002")
230
+ report.resolve_all
231
+ puts report.result.report
232
+ ```
233
+
234
+ The tool's `requires_capability` is not enforced in Contract graphs — it only
235
+ applies when the tool is invoked through the LLM tool-use path (via
236
+ `call_with_capability_check!`). Regular `call` (Executor protocol) is unrestricted.
237
+
238
+ ---
239
+
240
+ ## Provider message format
241
+
242
+ The tool-use loop produces provider-agnostic messages that each provider's
243
+ `normalize_messages` converts:
244
+
245
+ ```ruby
246
+ # Executor sends (provider-agnostic)
247
+ { role: "assistant", content: "", tool_calls: [{ id: "id1", name: "calculator", arguments: { expression: "2+2" } }] }
248
+ { role: :tool_results, results: [{ id: "id1", name: "calculator", content: "4" }] }
249
+
250
+ # Anthropic receives
251
+ { "role" => "assistant", "content" => [{ "type" => "tool_use", "id" => "id1", ... }] }
252
+ { "role" => "user", "content" => [{ "type" => "tool_result", "tool_use_id" => "id1", "content" => "4" }] }
253
+
254
+ # OpenAI receives
255
+ { "role" => "assistant", "tool_calls" => [{ "id" => "id1", "type" => "function", ... }] }
256
+ { "role" => "tool", "tool_call_id" => "id1", "content" => "4" }
257
+ ```
258
+
259
+ Each provider handles its own format in `normalize_messages`.
260
+
261
+ ---
262
+
263
+ ## Full example
264
+
265
+ ```ruby
266
+ require "igniter/tool"
267
+ require "igniter/tool_registry"
268
+ require "igniter/integrations/llm"
269
+
270
+ class SearchWeb < Igniter::Tool
271
+ description "Search the internet for current information"
272
+ param :query, type: :string, required: true
273
+ param :max_results, type: :integer, default: 5
274
+ requires_capability :web_access
275
+
276
+ def call(query:, max_results: 5)
277
+ WebSearchClient.search(query, limit: max_results)
278
+ end
279
+ end
280
+
281
+ class WriteReport < Igniter::Tool
282
+ description "Write a markdown report to a file"
283
+ param :filename, type: :string, required: true
284
+ param :content, type: :string, required: true
285
+ requires_capability :filesystem_write
286
+
287
+ def call(filename:, content:)
288
+ File.write(filename, content)
289
+ { written: true, path: filename }
290
+ end
291
+ end
292
+
293
+ # Register globally
294
+ Igniter::ToolRegistry.register(SearchWeb, WriteReport)
295
+
296
+ # LLM executor with auto-loop
297
+ class ResearchAgent < Igniter::LLM::Executor
298
+ provider :anthropic
299
+ model "claude-sonnet-4-6"
300
+ system_prompt "Research assistant. Search, synthesize, write reports."
301
+
302
+ tools SearchWeb, WriteReport
303
+ capabilities :web_access, :filesystem_write
304
+ max_tool_iterations 10
305
+
306
+ def call(topic:, output_file:)
307
+ complete("Research '#{topic}' and write a report to #{output_file}")
308
+ end
309
+ end
310
+
311
+ # In a Contract pipeline
312
+ class ResearchPipeline < Igniter::Contract
313
+ runner :thread_pool, pool_size: 3
314
+
315
+ define do
316
+ input :topics # Array<String>
317
+ input :output_dir
318
+
319
+ compose :report1, with: [:topics, :output_dir], contract: Class.new(Igniter::Contract) {
320
+ define do
321
+ input :topics
322
+ input :output_dir
323
+ compute :result, with: [:topics, :output_dir], call: ResearchAgent
324
+ output :result
325
+ end
326
+ }
327
+
328
+ output :report1
329
+ end
330
+ end
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Files
336
+
337
+ | File | Purpose |
338
+ |------|---------|
339
+ | `lib/igniter/tool.rb` | `Igniter::Tool` base class — DSL, schema, capability guard |
340
+ | `lib/igniter/tool_registry.rb` | Global registry + capability-filtered discovery |
341
+ | `lib/igniter/integrations/llm/executor.rb` | Auto tool-use loop in `#complete`, `max_tool_iterations` |
342
+ | `lib/igniter/integrations/llm/providers/anthropic.rb` | Tool message normalization (Anthropic format) |
343
+ | `lib/igniter/integrations/llm/providers/openai.rb` | Tool message normalization (OpenAI format) |
344
+ | `spec/igniter/tool_spec.rb` | Tool unit tests (40 examples) |
345
+ | `spec/igniter/tool_registry_spec.rb` | Registry tests (20 examples) |
346
+ | `spec/igniter/integrations/llm_tool_loop_spec.rb` | Loop + guards tests (mock provider, 27 examples) |
347
+ | `examples/llm_tools.rb` | Full demo |