igniter 0.3.1 → 0.4.3
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/CHANGELOG.md +25 -0
- data/README.md +238 -218
- data/docs/DISTRIBUTED_CONTRACTS_V1.md +493 -0
- data/docs/LLM_V1.md +335 -0
- data/docs/PATTERNS.md +189 -0
- data/docs/SERVER_V1.md +313 -0
- data/examples/README.md +129 -0
- data/examples/agents.rb +150 -0
- data/examples/differential.rb +161 -0
- data/examples/distributed_server.rb +94 -0
- data/examples/distributed_workflow.rb +52 -0
- data/examples/effects.rb +184 -0
- data/examples/invariants.rb +179 -0
- data/examples/order_pipeline.rb +163 -0
- data/examples/provenance.rb +122 -0
- data/examples/saga.rb +110 -0
- data/lib/igniter/agent/mailbox.rb +96 -0
- data/lib/igniter/agent/message.rb +21 -0
- data/lib/igniter/agent/ref.rb +86 -0
- data/lib/igniter/agent/runner.rb +129 -0
- data/lib/igniter/agent/state_holder.rb +23 -0
- data/lib/igniter/agent.rb +155 -0
- data/lib/igniter/compiler/compiled_graph.rb +12 -0
- data/lib/igniter/compiler/validation_pipeline.rb +3 -1
- data/lib/igniter/compiler/validators/await_validator.rb +53 -0
- data/lib/igniter/compiler/validators/callable_validator.rb +21 -3
- data/lib/igniter/compiler/validators/dependencies_validator.rb +41 -1
- data/lib/igniter/compiler/validators/remote_validator.rb +58 -0
- data/lib/igniter/compiler.rb +2 -0
- data/lib/igniter/contract.rb +59 -8
- data/lib/igniter/differential/divergence.rb +29 -0
- data/lib/igniter/differential/formatter.rb +96 -0
- data/lib/igniter/differential/report.rb +86 -0
- data/lib/igniter/differential/runner.rb +130 -0
- data/lib/igniter/differential.rb +51 -0
- data/lib/igniter/dsl/contract_builder.rb +74 -4
- data/lib/igniter/effect.rb +91 -0
- data/lib/igniter/effect_registry.rb +78 -0
- data/lib/igniter/errors.rb +17 -2
- data/lib/igniter/execution_report/builder.rb +54 -0
- data/lib/igniter/execution_report/formatter.rb +50 -0
- data/lib/igniter/execution_report/node_entry.rb +24 -0
- data/lib/igniter/execution_report/report.rb +65 -0
- data/lib/igniter/execution_report.rb +32 -0
- data/lib/igniter/extensions/differential.rb +114 -0
- data/lib/igniter/extensions/execution_report.rb +27 -0
- data/lib/igniter/extensions/invariants.rb +116 -0
- data/lib/igniter/extensions/provenance.rb +45 -0
- data/lib/igniter/extensions/saga.rb +74 -0
- data/lib/igniter/integrations/agents.rb +18 -0
- data/lib/igniter/integrations/llm/config.rb +69 -0
- data/lib/igniter/integrations/llm/context.rb +74 -0
- data/lib/igniter/integrations/llm/executor.rb +159 -0
- data/lib/igniter/integrations/llm/providers/anthropic.rb +148 -0
- data/lib/igniter/integrations/llm/providers/base.rb +33 -0
- data/lib/igniter/integrations/llm/providers/ollama.rb +137 -0
- data/lib/igniter/integrations/llm/providers/openai.rb +153 -0
- data/lib/igniter/integrations/llm.rb +59 -0
- data/lib/igniter/integrations/rails/cable_adapter.rb +49 -0
- data/lib/igniter/integrations/rails/contract_job.rb +76 -0
- data/lib/igniter/integrations/rails/generators/contract/contract_generator.rb +22 -0
- data/lib/igniter/integrations/rails/generators/install/install_generator.rb +33 -0
- data/lib/igniter/integrations/rails/railtie.rb +25 -0
- data/lib/igniter/integrations/rails/webhook_concern.rb +49 -0
- data/lib/igniter/integrations/rails.rb +12 -0
- data/lib/igniter/invariant.rb +50 -0
- data/lib/igniter/model/await_node.rb +21 -0
- data/lib/igniter/model/effect_node.rb +37 -0
- data/lib/igniter/model/remote_node.rb +26 -0
- data/lib/igniter/model.rb +3 -0
- data/lib/igniter/property_testing/formatter.rb +66 -0
- data/lib/igniter/property_testing/generators.rb +115 -0
- data/lib/igniter/property_testing/result.rb +45 -0
- data/lib/igniter/property_testing/run.rb +43 -0
- data/lib/igniter/property_testing/runner.rb +47 -0
- data/lib/igniter/property_testing.rb +64 -0
- data/lib/igniter/provenance/builder.rb +97 -0
- data/lib/igniter/provenance/lineage.rb +82 -0
- data/lib/igniter/provenance/node_trace.rb +65 -0
- data/lib/igniter/provenance/text_formatter.rb +70 -0
- data/lib/igniter/provenance.rb +29 -0
- data/lib/igniter/registry.rb +67 -0
- data/lib/igniter/runtime/execution.rb +2 -2
- data/lib/igniter/runtime/input_validator.rb +5 -3
- data/lib/igniter/runtime/resolver.rb +58 -1
- data/lib/igniter/runtime/stores/active_record_store.rb +13 -1
- data/lib/igniter/runtime/stores/file_store.rb +50 -2
- data/lib/igniter/runtime/stores/memory_store.rb +55 -2
- data/lib/igniter/runtime/stores/redis_store.rb +13 -1
- data/lib/igniter/saga/compensation.rb +31 -0
- data/lib/igniter/saga/compensation_record.rb +20 -0
- data/lib/igniter/saga/executor.rb +85 -0
- data/lib/igniter/saga/formatter.rb +49 -0
- data/lib/igniter/saga/result.rb +47 -0
- data/lib/igniter/saga.rb +56 -0
- data/lib/igniter/server/client.rb +123 -0
- data/lib/igniter/server/config.rb +27 -0
- data/lib/igniter/server/handlers/base.rb +105 -0
- data/lib/igniter/server/handlers/contracts_handler.rb +15 -0
- data/lib/igniter/server/handlers/event_handler.rb +28 -0
- data/lib/igniter/server/handlers/execute_handler.rb +37 -0
- data/lib/igniter/server/handlers/health_handler.rb +32 -0
- data/lib/igniter/server/handlers/status_handler.rb +27 -0
- data/lib/igniter/server/http_server.rb +109 -0
- data/lib/igniter/server/rack_app.rb +35 -0
- data/lib/igniter/server/registry.rb +56 -0
- data/lib/igniter/server/router.rb +75 -0
- data/lib/igniter/server.rb +67 -0
- data/lib/igniter/stream_loop.rb +80 -0
- data/lib/igniter/supervisor.rb +167 -0
- data/lib/igniter/version.rb +1 -1
- data/lib/igniter.rb +14 -0
- metadata +92 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Igniter
|
|
6
|
+
module Server
|
|
7
|
+
# Transport-agnostic HTTP router.
|
|
8
|
+
# Receives (method, path, body_string) and returns { status:, body:, headers: }.
|
|
9
|
+
# Used by both HttpServer (TCPServer) and RackApp.
|
|
10
|
+
class Router
|
|
11
|
+
ROUTES = [
|
|
12
|
+
{ method: "GET", pattern: %r{\A/v1/health\z}, handler: :health },
|
|
13
|
+
{ method: "GET", pattern: %r{\A/v1/contracts\z}, handler: :contracts },
|
|
14
|
+
{ method: "POST", pattern: %r{\A/v1/contracts/(?<name>[^/]+)/execute\z}, handler: :execute },
|
|
15
|
+
{ method: "POST", pattern: %r{\A/v1/contracts/(?<name>[^/]+)/events\z}, handler: :event },
|
|
16
|
+
{ method: "GET", pattern: %r{\A/v1/executions/(?<id>[^/]+)\z}, handler: :status }
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
def initialize(config)
|
|
20
|
+
@config = config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Main dispatch entry point — called by both WEBrick and Rack adapters.
|
|
24
|
+
def call(http_method, path, body_str) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
|
25
|
+
method_uc = http_method.to_s.upcase
|
|
26
|
+
ROUTES.each do |route|
|
|
27
|
+
next unless route[:method] == method_uc
|
|
28
|
+
|
|
29
|
+
match = route[:pattern].match(path)
|
|
30
|
+
next unless match
|
|
31
|
+
|
|
32
|
+
params = match.named_captures.transform_keys(&:to_sym)
|
|
33
|
+
body = parse_body(body_str)
|
|
34
|
+
|
|
35
|
+
handler = build_handler(route[:handler])
|
|
36
|
+
return handler.call(params: params, body: body)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
not_found_response(path)
|
|
40
|
+
rescue JSON::ParserError => e
|
|
41
|
+
{ status: 400, body: JSON.generate({ error: "Invalid JSON: #{e.message}" }), headers: json_ct }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def build_handler(key)
|
|
47
|
+
registry = @config.registry
|
|
48
|
+
store = @config.store
|
|
49
|
+
node_url = "http://#{@config.host}:#{@config.port}"
|
|
50
|
+
|
|
51
|
+
case key
|
|
52
|
+
when :health then Handlers::HealthHandler.new(registry, store, node_url: node_url)
|
|
53
|
+
when :contracts then Handlers::ContractsHandler.new(registry, store)
|
|
54
|
+
when :execute then Handlers::ExecuteHandler.new(registry, store)
|
|
55
|
+
when :event then Handlers::EventHandler.new(registry, store)
|
|
56
|
+
when :status then Handlers::StatusHandler.new(registry, store)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_body(str)
|
|
61
|
+
return {} if str.nil? || str.strip.empty?
|
|
62
|
+
|
|
63
|
+
JSON.parse(str)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def not_found_response(path)
|
|
67
|
+
{ status: 404, body: JSON.generate({ error: "Not found: #{path}" }), headers: json_ct }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def json_ct
|
|
71
|
+
{ "Content-Type" => "application/json" }
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "igniter"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
# Define the top-level Server module and Error class first,
|
|
7
|
+
# so subfiles can inherit from Igniter::Server::Error.
|
|
8
|
+
module Igniter
|
|
9
|
+
module Server
|
|
10
|
+
class Error < Igniter::Error; end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require_relative "server/registry"
|
|
15
|
+
require_relative "server/config"
|
|
16
|
+
require_relative "server/router"
|
|
17
|
+
require_relative "server/http_server"
|
|
18
|
+
require_relative "server/rack_app"
|
|
19
|
+
require_relative "server/client"
|
|
20
|
+
require_relative "server/handlers/base"
|
|
21
|
+
require_relative "server/handlers/health_handler"
|
|
22
|
+
require_relative "server/handlers/contracts_handler"
|
|
23
|
+
require_relative "server/handlers/execute_handler"
|
|
24
|
+
require_relative "server/handlers/event_handler"
|
|
25
|
+
require_relative "server/handlers/status_handler"
|
|
26
|
+
|
|
27
|
+
module Igniter
|
|
28
|
+
module Server
|
|
29
|
+
class << self
|
|
30
|
+
def config
|
|
31
|
+
@config ||= Config.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def configure
|
|
35
|
+
yield config
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Start the built-in HTTP server (blocking).
|
|
40
|
+
def start(**options)
|
|
41
|
+
apply_options!(options)
|
|
42
|
+
HttpServer.new(config).start
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Return a Rack-compatible application for use with Puma/Unicorn/etc.
|
|
46
|
+
def rack_app
|
|
47
|
+
RackApp.new(config)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Reset configuration (useful in tests).
|
|
51
|
+
def reset!
|
|
52
|
+
@config = nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def apply_options!(options) # rubocop:disable Metrics/AbcSize
|
|
58
|
+
config.port = options[:port] if options[:port]
|
|
59
|
+
config.host = options[:host] if options[:host]
|
|
60
|
+
config.store = options[:store] if options[:store]
|
|
61
|
+
return unless options[:contracts].is_a?(Hash)
|
|
62
|
+
|
|
63
|
+
options[:contracts].each { |name, klass| config.register(name.to_s, klass) }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
# Runs an Igniter contract in a continuous tick-loop.
|
|
5
|
+
#
|
|
6
|
+
# Each tick resolves the contract with the current inputs and delivers the
|
|
7
|
+
# result to the on_result callback. Useful for sensor polling, feed
|
|
8
|
+
# processing, or any recurring computation.
|
|
9
|
+
#
|
|
10
|
+
# stream = Igniter::StreamLoop.new(
|
|
11
|
+
# contract: SensorContract,
|
|
12
|
+
# tick_interval: 0.1,
|
|
13
|
+
# inputs: { sensor_id: "temp-1", threshold: 25.0 },
|
|
14
|
+
# on_result: ->(result) { puts result.status },
|
|
15
|
+
# on_error: ->(err) { warn err.message }
|
|
16
|
+
# )
|
|
17
|
+
#
|
|
18
|
+
# stream.start
|
|
19
|
+
# stream.update_inputs(threshold: 30.0) # hot-swap inputs between ticks
|
|
20
|
+
# stream.stop
|
|
21
|
+
#
|
|
22
|
+
class StreamLoop
|
|
23
|
+
def initialize(contract:, tick_interval: 1.0, inputs: {}, on_result: nil, on_error: nil)
|
|
24
|
+
@contract_class = contract
|
|
25
|
+
@tick_interval = tick_interval.to_f
|
|
26
|
+
@on_result = on_result
|
|
27
|
+
@on_error = on_error
|
|
28
|
+
@mutex = Mutex.new
|
|
29
|
+
@current_inputs = inputs.dup
|
|
30
|
+
@running = false
|
|
31
|
+
@thread = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Start the loop in a background thread. Returns self.
|
|
35
|
+
def start
|
|
36
|
+
@running = true
|
|
37
|
+
@thread = Thread.new { loop_body }
|
|
38
|
+
@thread.abort_on_exception = false
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Stop the loop and wait for the current tick to finish.
|
|
43
|
+
def stop(timeout: 5)
|
|
44
|
+
@running = false
|
|
45
|
+
@thread&.join(timeout)
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Merge +new_inputs+ into the current input set. Takes effect on the next tick.
|
|
50
|
+
def update_inputs(new_inputs)
|
|
51
|
+
@mutex.synchronize { @current_inputs.merge!(new_inputs) }
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def alive?
|
|
56
|
+
@thread&.alive? || false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def loop_body
|
|
62
|
+
while @running
|
|
63
|
+
tick_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
64
|
+
run_tick
|
|
65
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - tick_start
|
|
66
|
+
sleep_for = [@tick_interval - elapsed, 0].max
|
|
67
|
+
sleep(sleep_for) if sleep_for.positive? && @running
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def run_tick
|
|
72
|
+
inputs = @mutex.synchronize { @current_inputs.dup }
|
|
73
|
+
contract = @contract_class.new(**inputs)
|
|
74
|
+
contract.resolve_all
|
|
75
|
+
@on_result&.call(contract.result)
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
@on_error&.call(e)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
# Supervises a group of agents and restarts them when they crash.
|
|
5
|
+
#
|
|
6
|
+
# Subclass Supervisor and declare children with the class-level DSL:
|
|
7
|
+
#
|
|
8
|
+
# class AppSupervisor < Igniter::Supervisor
|
|
9
|
+
# strategy :one_for_one # default
|
|
10
|
+
# max_restarts 5, within: 60 # default
|
|
11
|
+
#
|
|
12
|
+
# children do |c|
|
|
13
|
+
# c.worker :counter, CounterAgent
|
|
14
|
+
# c.worker :logger, LoggerAgent, initial_state: { level: :info }
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# sup = AppSupervisor.start
|
|
19
|
+
# sup.child(:counter).send(:increment, by: 1)
|
|
20
|
+
# sup.stop
|
|
21
|
+
#
|
|
22
|
+
# Restart strategies:
|
|
23
|
+
# :one_for_one — restart only the crashed agent (default)
|
|
24
|
+
# :one_for_all — stop all agents and restart them all when any one crashes
|
|
25
|
+
#
|
|
26
|
+
# Restart budget: if more than +max_restarts+ crashes happen within +within+
|
|
27
|
+
# seconds, the supervisor logs the failure and stops trying to restart.
|
|
28
|
+
#
|
|
29
|
+
class Supervisor
|
|
30
|
+
class RestartBudgetExceeded < Igniter::Error; end
|
|
31
|
+
|
|
32
|
+
# ── ChildSpec ────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
ChildSpec = Struct.new(:name, :agent_class, :init_opts, keyword_init: true)
|
|
35
|
+
|
|
36
|
+
class ChildSpecBuilder
|
|
37
|
+
attr_reader :specs
|
|
38
|
+
|
|
39
|
+
def initialize
|
|
40
|
+
@specs = []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def worker(name, agent_class, **opts)
|
|
44
|
+
@specs << ChildSpec.new(name: name.to_sym, agent_class: agent_class, init_opts: opts)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# ── Class-level defaults ─────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
@strategy = :one_for_one
|
|
51
|
+
@max_restarts = 5
|
|
52
|
+
@restart_window = 60
|
|
53
|
+
@spec_builder = ChildSpecBuilder.new
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
def inherited(subclass)
|
|
57
|
+
super
|
|
58
|
+
subclass.instance_variable_set(:@strategy, :one_for_one)
|
|
59
|
+
subclass.instance_variable_set(:@max_restarts, 5)
|
|
60
|
+
subclass.instance_variable_set(:@restart_window, 60)
|
|
61
|
+
subclass.instance_variable_set(:@spec_builder, ChildSpecBuilder.new)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def strategy(sym)
|
|
65
|
+
@strategy = sym
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def max_restarts(count, within:)
|
|
69
|
+
@max_restarts = count
|
|
70
|
+
@restart_window = within
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def children(&block)
|
|
74
|
+
block.call(@spec_builder)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def child_specs
|
|
78
|
+
@spec_builder.specs
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def start
|
|
82
|
+
new.tap(&:start_all)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# ── Instance ─────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
def initialize
|
|
89
|
+
@refs = {}
|
|
90
|
+
@specs_by_name = {}
|
|
91
|
+
@restart_log = []
|
|
92
|
+
@mutex = Mutex.new
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def start_all
|
|
96
|
+
self.class.child_specs.each do |spec|
|
|
97
|
+
@specs_by_name[spec.name] = spec
|
|
98
|
+
start_child(spec)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Return the Ref for a named child. Returns nil if not found.
|
|
103
|
+
def child(name)
|
|
104
|
+
@mutex.synchronize { @refs[name.to_sym] }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Stop all children gracefully.
|
|
108
|
+
def stop
|
|
109
|
+
refs = @mutex.synchronize { @refs.values.dup }
|
|
110
|
+
refs.each do |ref|
|
|
111
|
+
ref.stop
|
|
112
|
+
rescue StandardError
|
|
113
|
+
nil
|
|
114
|
+
end
|
|
115
|
+
self
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def start_child(spec)
|
|
121
|
+
opts = spec.init_opts.dup
|
|
122
|
+
opts[:on_crash] = ->(error) { handle_crash(spec, error) }
|
|
123
|
+
ref = spec.agent_class.start(**opts)
|
|
124
|
+
@mutex.synchronize { @refs[spec.name] = ref }
|
|
125
|
+
ref
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def handle_crash(spec, _error)
|
|
129
|
+
check_restart_budget!
|
|
130
|
+
|
|
131
|
+
case self.class.instance_variable_get(:@strategy)
|
|
132
|
+
when :one_for_one
|
|
133
|
+
start_child(spec)
|
|
134
|
+
when :one_for_all
|
|
135
|
+
stop_all_children
|
|
136
|
+
self.class.child_specs.each { |s| start_child(s) }
|
|
137
|
+
end
|
|
138
|
+
rescue RestartBudgetExceeded => e
|
|
139
|
+
warn "Igniter::Supervisor #{self.class.name}: #{e.message}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def check_restart_budget! # rubocop:disable Metrics/MethodLength
|
|
143
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
144
|
+
window = self.class.instance_variable_get(:@restart_window).to_f
|
|
145
|
+
max = self.class.instance_variable_get(:@max_restarts)
|
|
146
|
+
|
|
147
|
+
@mutex.synchronize do
|
|
148
|
+
@restart_log.reject! { |t| now - t > window }
|
|
149
|
+
@restart_log << now
|
|
150
|
+
|
|
151
|
+
if @restart_log.size > max
|
|
152
|
+
raise RestartBudgetExceeded,
|
|
153
|
+
"#{@restart_log.size} crashes in #{window}s (max=#{max})"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def stop_all_children
|
|
159
|
+
refs = @mutex.synchronize { @refs.values.dup }
|
|
160
|
+
refs.each do |ref|
|
|
161
|
+
ref.stop(timeout: 2)
|
|
162
|
+
rescue StandardError
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
data/lib/igniter/version.rb
CHANGED
data/lib/igniter.rb
CHANGED
|
@@ -5,6 +5,8 @@ require_relative "igniter/errors"
|
|
|
5
5
|
require_relative "igniter/type_system"
|
|
6
6
|
require_relative "igniter/executor"
|
|
7
7
|
require_relative "igniter/executor_registry"
|
|
8
|
+
require_relative "igniter/effect"
|
|
9
|
+
require_relative "igniter/effect_registry"
|
|
8
10
|
require_relative "igniter/model"
|
|
9
11
|
require_relative "igniter/compiler"
|
|
10
12
|
require_relative "igniter/events"
|
|
@@ -32,6 +34,14 @@ module Igniter
|
|
|
32
34
|
executor_registry.register(key, executor_class, **metadata)
|
|
33
35
|
end
|
|
34
36
|
|
|
37
|
+
def effect_registry
|
|
38
|
+
@effect_registry ||= EffectRegistry.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def register_effect(key, adapter_class, **metadata)
|
|
42
|
+
effect_registry.register(key, adapter_class, **metadata)
|
|
43
|
+
end
|
|
44
|
+
|
|
35
45
|
def compile(&block)
|
|
36
46
|
DSL::ContractBuilder.compile(&block)
|
|
37
47
|
end
|
|
@@ -39,5 +49,9 @@ module Igniter
|
|
|
39
49
|
def compile_schema(schema, name: nil)
|
|
40
50
|
DSL::SchemaBuilder.compile(schema, name: name)
|
|
41
51
|
end
|
|
52
|
+
|
|
53
|
+
def configure
|
|
54
|
+
yield self
|
|
55
|
+
end
|
|
42
56
|
end
|
|
43
57
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: igniter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander
|
|
@@ -54,20 +54,38 @@ files:
|
|
|
54
54
|
- docs/BACKLOG.md
|
|
55
55
|
- docs/BRANCHES_V1.md
|
|
56
56
|
- docs/COLLECTIONS_V1.md
|
|
57
|
+
- docs/DISTRIBUTED_CONTRACTS_V1.md
|
|
57
58
|
- docs/EXECUTION_MODEL_V2.md
|
|
58
59
|
- docs/IGNITER_CONCEPTS.md
|
|
60
|
+
- docs/LLM_V1.md
|
|
59
61
|
- docs/PATTERNS.md
|
|
62
|
+
- docs/SERVER_V1.md
|
|
60
63
|
- docs/STORE_ADAPTERS.md
|
|
61
64
|
- examples/README.md
|
|
65
|
+
- examples/agents.rb
|
|
62
66
|
- examples/async_store.rb
|
|
63
67
|
- examples/basic_pricing.rb
|
|
64
68
|
- examples/collection.rb
|
|
65
69
|
- examples/collection_partial_failure.rb
|
|
66
70
|
- examples/composition.rb
|
|
67
71
|
- examples/diagnostics.rb
|
|
72
|
+
- examples/differential.rb
|
|
73
|
+
- examples/distributed_server.rb
|
|
74
|
+
- examples/distributed_workflow.rb
|
|
75
|
+
- examples/effects.rb
|
|
76
|
+
- examples/invariants.rb
|
|
68
77
|
- examples/marketing_ergonomics.rb
|
|
78
|
+
- examples/order_pipeline.rb
|
|
79
|
+
- examples/provenance.rb
|
|
69
80
|
- examples/ringcentral_routing.rb
|
|
81
|
+
- examples/saga.rb
|
|
70
82
|
- lib/igniter.rb
|
|
83
|
+
- lib/igniter/agent.rb
|
|
84
|
+
- lib/igniter/agent/mailbox.rb
|
|
85
|
+
- lib/igniter/agent/message.rb
|
|
86
|
+
- lib/igniter/agent/ref.rb
|
|
87
|
+
- lib/igniter/agent/runner.rb
|
|
88
|
+
- lib/igniter/agent/state_holder.rb
|
|
71
89
|
- lib/igniter/compiler.rb
|
|
72
90
|
- lib/igniter/compiler/compiled_graph.rb
|
|
73
91
|
- lib/igniter/compiler/graph_compiler.rb
|
|
@@ -75,9 +93,11 @@ files:
|
|
|
75
93
|
- lib/igniter/compiler/validation_context.rb
|
|
76
94
|
- lib/igniter/compiler/validation_pipeline.rb
|
|
77
95
|
- lib/igniter/compiler/validator.rb
|
|
96
|
+
- lib/igniter/compiler/validators/await_validator.rb
|
|
78
97
|
- lib/igniter/compiler/validators/callable_validator.rb
|
|
79
98
|
- lib/igniter/compiler/validators/dependencies_validator.rb
|
|
80
99
|
- lib/igniter/compiler/validators/outputs_validator.rb
|
|
100
|
+
- lib/igniter/compiler/validators/remote_validator.rb
|
|
81
101
|
- lib/igniter/compiler/validators/type_compatibility_validator.rb
|
|
82
102
|
- lib/igniter/compiler/validators/uniqueness_validator.rb
|
|
83
103
|
- lib/igniter/contract.rb
|
|
@@ -87,35 +107,84 @@ files:
|
|
|
87
107
|
- lib/igniter/diagnostics/introspection/formatters/mermaid_formatter.rb
|
|
88
108
|
- lib/igniter/diagnostics/introspection/formatters/text_tree_formatter.rb
|
|
89
109
|
- lib/igniter/diagnostics/report.rb
|
|
110
|
+
- lib/igniter/differential.rb
|
|
111
|
+
- lib/igniter/differential/divergence.rb
|
|
112
|
+
- lib/igniter/differential/formatter.rb
|
|
113
|
+
- lib/igniter/differential/report.rb
|
|
114
|
+
- lib/igniter/differential/runner.rb
|
|
90
115
|
- lib/igniter/dsl.rb
|
|
91
116
|
- lib/igniter/dsl/contract_builder.rb
|
|
92
117
|
- lib/igniter/dsl/schema_builder.rb
|
|
118
|
+
- lib/igniter/effect.rb
|
|
119
|
+
- lib/igniter/effect_registry.rb
|
|
93
120
|
- lib/igniter/errors.rb
|
|
94
121
|
- lib/igniter/events.rb
|
|
95
122
|
- lib/igniter/events/bus.rb
|
|
96
123
|
- lib/igniter/events/event.rb
|
|
124
|
+
- lib/igniter/execution_report.rb
|
|
125
|
+
- lib/igniter/execution_report/builder.rb
|
|
126
|
+
- lib/igniter/execution_report/formatter.rb
|
|
127
|
+
- lib/igniter/execution_report/node_entry.rb
|
|
128
|
+
- lib/igniter/execution_report/report.rb
|
|
97
129
|
- lib/igniter/executor.rb
|
|
98
130
|
- lib/igniter/executor_registry.rb
|
|
99
131
|
- lib/igniter/extensions.rb
|
|
100
132
|
- lib/igniter/extensions/auditing.rb
|
|
101
133
|
- lib/igniter/extensions/auditing/timeline.rb
|
|
134
|
+
- lib/igniter/extensions/differential.rb
|
|
135
|
+
- lib/igniter/extensions/execution_report.rb
|
|
102
136
|
- lib/igniter/extensions/introspection.rb
|
|
103
137
|
- lib/igniter/extensions/introspection/graph_formatter.rb
|
|
104
138
|
- lib/igniter/extensions/introspection/plan_formatter.rb
|
|
105
139
|
- lib/igniter/extensions/introspection/runtime_formatter.rb
|
|
140
|
+
- lib/igniter/extensions/invariants.rb
|
|
141
|
+
- lib/igniter/extensions/provenance.rb
|
|
106
142
|
- lib/igniter/extensions/reactive.rb
|
|
107
143
|
- lib/igniter/extensions/reactive/engine.rb
|
|
108
144
|
- lib/igniter/extensions/reactive/matcher.rb
|
|
109
145
|
- lib/igniter/extensions/reactive/reaction.rb
|
|
146
|
+
- lib/igniter/extensions/saga.rb
|
|
147
|
+
- lib/igniter/integrations/agents.rb
|
|
148
|
+
- lib/igniter/integrations/llm.rb
|
|
149
|
+
- lib/igniter/integrations/llm/config.rb
|
|
150
|
+
- lib/igniter/integrations/llm/context.rb
|
|
151
|
+
- lib/igniter/integrations/llm/executor.rb
|
|
152
|
+
- lib/igniter/integrations/llm/providers/anthropic.rb
|
|
153
|
+
- lib/igniter/integrations/llm/providers/base.rb
|
|
154
|
+
- lib/igniter/integrations/llm/providers/ollama.rb
|
|
155
|
+
- lib/igniter/integrations/llm/providers/openai.rb
|
|
156
|
+
- lib/igniter/integrations/rails.rb
|
|
157
|
+
- lib/igniter/integrations/rails/cable_adapter.rb
|
|
158
|
+
- lib/igniter/integrations/rails/contract_job.rb
|
|
159
|
+
- lib/igniter/integrations/rails/generators/contract/contract_generator.rb
|
|
160
|
+
- lib/igniter/integrations/rails/generators/install/install_generator.rb
|
|
161
|
+
- lib/igniter/integrations/rails/railtie.rb
|
|
162
|
+
- lib/igniter/integrations/rails/webhook_concern.rb
|
|
163
|
+
- lib/igniter/invariant.rb
|
|
110
164
|
- lib/igniter/model.rb
|
|
165
|
+
- lib/igniter/model/await_node.rb
|
|
111
166
|
- lib/igniter/model/branch_node.rb
|
|
112
167
|
- lib/igniter/model/collection_node.rb
|
|
113
168
|
- lib/igniter/model/composition_node.rb
|
|
114
169
|
- lib/igniter/model/compute_node.rb
|
|
170
|
+
- lib/igniter/model/effect_node.rb
|
|
115
171
|
- lib/igniter/model/graph.rb
|
|
116
172
|
- lib/igniter/model/input_node.rb
|
|
117
173
|
- lib/igniter/model/node.rb
|
|
118
174
|
- lib/igniter/model/output_node.rb
|
|
175
|
+
- lib/igniter/model/remote_node.rb
|
|
176
|
+
- lib/igniter/property_testing.rb
|
|
177
|
+
- lib/igniter/property_testing/formatter.rb
|
|
178
|
+
- lib/igniter/property_testing/generators.rb
|
|
179
|
+
- lib/igniter/property_testing/result.rb
|
|
180
|
+
- lib/igniter/property_testing/run.rb
|
|
181
|
+
- lib/igniter/property_testing/runner.rb
|
|
182
|
+
- lib/igniter/provenance.rb
|
|
183
|
+
- lib/igniter/provenance/builder.rb
|
|
184
|
+
- lib/igniter/provenance/lineage.rb
|
|
185
|
+
- lib/igniter/provenance/node_trace.rb
|
|
186
|
+
- lib/igniter/provenance/text_formatter.rb
|
|
187
|
+
- lib/igniter/registry.rb
|
|
119
188
|
- lib/igniter/runtime.rb
|
|
120
189
|
- lib/igniter/runtime/cache.rb
|
|
121
190
|
- lib/igniter/runtime/collection_result.rb
|
|
@@ -136,6 +205,27 @@ files:
|
|
|
136
205
|
- lib/igniter/runtime/stores/file_store.rb
|
|
137
206
|
- lib/igniter/runtime/stores/memory_store.rb
|
|
138
207
|
- lib/igniter/runtime/stores/redis_store.rb
|
|
208
|
+
- lib/igniter/saga.rb
|
|
209
|
+
- lib/igniter/saga/compensation.rb
|
|
210
|
+
- lib/igniter/saga/compensation_record.rb
|
|
211
|
+
- lib/igniter/saga/executor.rb
|
|
212
|
+
- lib/igniter/saga/formatter.rb
|
|
213
|
+
- lib/igniter/saga/result.rb
|
|
214
|
+
- lib/igniter/server.rb
|
|
215
|
+
- lib/igniter/server/client.rb
|
|
216
|
+
- lib/igniter/server/config.rb
|
|
217
|
+
- lib/igniter/server/handlers/base.rb
|
|
218
|
+
- lib/igniter/server/handlers/contracts_handler.rb
|
|
219
|
+
- lib/igniter/server/handlers/event_handler.rb
|
|
220
|
+
- lib/igniter/server/handlers/execute_handler.rb
|
|
221
|
+
- lib/igniter/server/handlers/health_handler.rb
|
|
222
|
+
- lib/igniter/server/handlers/status_handler.rb
|
|
223
|
+
- lib/igniter/server/http_server.rb
|
|
224
|
+
- lib/igniter/server/rack_app.rb
|
|
225
|
+
- lib/igniter/server/registry.rb
|
|
226
|
+
- lib/igniter/server/router.rb
|
|
227
|
+
- lib/igniter/stream_loop.rb
|
|
228
|
+
- lib/igniter/supervisor.rb
|
|
139
229
|
- lib/igniter/type_system.rb
|
|
140
230
|
- lib/igniter/version.rb
|
|
141
231
|
- sig/igniter.rbs
|
|
@@ -161,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
161
251
|
- !ruby/object:Gem::Version
|
|
162
252
|
version: '0'
|
|
163
253
|
requirements: []
|
|
164
|
-
rubygems_version:
|
|
254
|
+
rubygems_version: 4.0.9
|
|
165
255
|
specification_version: 4
|
|
166
256
|
summary: Declarative dependency-graph runtime for business logic
|
|
167
257
|
test_files: []
|