smith-agents 0.8.0 → 0.10.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +230 -0
  3. data/docs/CONFIGURATION.md +5 -5
  4. data/lib/smith/agent/completion_usage_recording.rb +21 -3
  5. data/lib/smith/agent/invocation_preparation.rb +24 -0
  6. data/lib/smith/agent/lifecycle.rb +4 -2
  7. data/lib/smith/agent/provider_attempt.rb +10 -4
  8. data/lib/smith/agent/provider_call_timing.rb +74 -0
  9. data/lib/smith/agent/provider_completion.rb +27 -9
  10. data/lib/smith/agent/provider_failure_handling.rb +2 -2
  11. data/lib/smith/agent/usage_entry_recording.rb +27 -4
  12. data/lib/smith/agent/usage_traces.rb +49 -0
  13. data/lib/smith/agent/usage_tracking.rb +23 -20
  14. data/lib/smith/attribution.rb +115 -0
  15. data/lib/smith/event.rb +10 -2
  16. data/lib/smith/events/bus.rb +63 -5
  17. data/lib/smith/events/step_completed.rb +3 -0
  18. data/lib/smith/events/step_failed.rb +25 -0
  19. data/lib/smith/events/subscription.rb +8 -0
  20. data/lib/smith/tool/capture.rb +9 -1
  21. data/lib/smith/tool.rb +10 -1
  22. data/lib/smith/trace/logger.rb +1 -0
  23. data/lib/smith/trace/memory.rb +40 -4
  24. data/lib/smith/trace/open_telemetry.rb +47 -3
  25. data/lib/smith/trace.rb +24 -5
  26. data/lib/smith/version.rb +1 -1
  27. data/lib/smith/workflow/composite/effects.rb +64 -33
  28. data/lib/smith/workflow/deterministic_execution.rb +2 -1
  29. data/lib/smith/workflow/deterministic_step.rb +14 -1
  30. data/lib/smith/workflow/dsl.rb +25 -1
  31. data/lib/smith/workflow/evaluator_optimizer.rb +3 -1
  32. data/lib/smith/workflow/event_integration.rb +48 -1
  33. data/lib/smith/workflow/execution.rb +8 -0
  34. data/lib/smith/workflow/fanout_execution.rb +1 -1
  35. data/lib/smith/workflow/guardrail_integration.rb +25 -0
  36. data/lib/smith/workflow/parallel_execution.rb +6 -0
  37. data/lib/smith/workflow/persistence.rb +33 -1
  38. data/lib/smith/workflow/prepared_branch_execution.rb +13 -4
  39. data/lib/smith/workflow/split_step_persistence/composite_branch_execution.rb +9 -1
  40. data/lib/smith/workflow/split_step_persistence/state_snapshot.rb +1 -0
  41. data/lib/smith/workflow/step_completion.rb +9 -0
  42. data/lib/smith/workflow/step_context.rb +46 -0
  43. data/lib/smith/workflow/thread_context_snapshot.rb +1 -0
  44. data/lib/smith/workflow/usage_entry.rb +32 -4
  45. data/lib/smith/workflow.rb +26 -1
  46. data/lib/smith.rb +7 -2
  47. metadata +5 -1
@@ -2,9 +2,24 @@
2
2
 
3
3
  module Smith
4
4
  class Workflow
5
- # One row per agent provider call. `usage_id` is a UUID generated at
6
- # recording time and stable across persist/restore so hosts can use it as an
7
- # idempotency anchor.
5
+ # One row per provider RESPONSE within an agent provider attempt.
6
+ # `usage_id` is a UUID generated at recording time and stable across
7
+ # persist/restore so hosts can use it as an idempotency anchor.
8
+ #
9
+ # `transition`, `branch_key`, `round`, and `workflow` come from the
10
+ # ambient Smith::Attribution at recording time, so hosts can attribute
11
+ # usage to the exact step, fan-out branch, optimizer round, and (for
12
+ # nested workflows) the exact graph that spent it.
13
+ # `attempt_id` is shared by every entry the same provider attempt
14
+ # produced (an attempt with an N-round tool loop records N entries):
15
+ # join on it against the `:provider_call` trace event for the attempt's
16
+ # single measured duration; never sum a duration across entries. Every
17
+ # attempt emits its `:provider_call` (an attempt aborted by a
18
+ # non-provider error emits with `outcome: :aborted` before re-raising),
19
+ # so prefix-accounted entries always have their join target.
20
+ # All optional members are nil on entries restored from checkpoints written by
21
+ # earlier Smith versions, and nil attribution members are omitted from
22
+ # serialization so those documents re-serialize byte-identically.
8
23
  # rubocop:disable Style/RedundantStructKeywordInit
9
24
  UsageEntry = Struct.new(
10
25
  :usage_id,
@@ -16,6 +31,11 @@ module Smith
16
31
  :cost,
17
32
  :attempt_kind,
18
33
  :recorded_at,
34
+ :transition,
35
+ :branch_key,
36
+ :round,
37
+ :attempt_id,
38
+ :workflow,
19
39
  keyword_init: true
20
40
  ) do
21
41
  def initialize(**attributes)
@@ -26,10 +46,18 @@ module Smith
26
46
  freeze
27
47
  end
28
48
 
49
+ # Serialization is additive only when present: nil attribution members
50
+ # are omitted so an entry restored from a pre-attribution checkpoint
51
+ # re-serializes byte-identically. Hosts that digest whole persisted
52
+ # documents (exact-mutation proofs) depend on this stability.
53
+ def to_h
54
+ super.reject { |key, value| value.nil? && %i[transition branch_key round attempt_id workflow].include?(key) }
55
+ end
56
+
29
57
  def self.from_h(hash)
30
58
  sym = hash.transform_keys(&:to_sym)
31
59
  filtered = sym.slice(*members)
32
- %i[agent_name provider attempt_kind].each do |attribute|
60
+ %i[agent_name provider attempt_kind transition branch_key].each do |attribute|
33
61
  filtered[attribute] = filtered[attribute].to_sym if filtered[attribute].is_a?(String)
34
62
  end
35
63
  new(**filtered)
@@ -58,6 +58,14 @@ module Smith
58
58
  @usage_entries = []
59
59
  @usage_mutex = Mutex.new
60
60
  @last_output = nil
61
+ # Durable attribution of the most recent serial `execute :agent` step:
62
+ # { model:, provider: } of the model that actually served it (post
63
+ # fallback resolution), exposed to the following deterministic step as
64
+ # `last_agent_model` / `last_agent_provider`. `@pending_agent_execution`
65
+ # is the transient per-step carrier from `execute_serial_step` to
66
+ # `complete_step`; it is never persisted.
67
+ @last_agent_execution = nil
68
+ @pending_agent_execution = nil
61
69
  @last_failed_step = nil
62
70
  # Optimistic-locking version. Incremented on each persist!; restored
63
71
  # from the persisted payload. Adapters that support store_versioned
@@ -157,6 +165,18 @@ module Smith
157
165
  end
158
166
  end
159
167
 
168
+ # Public, read-only view of the per-provider-call usage ledger, so hosts
169
+ # can diff usage across a step boundary without paying a full to_state
170
+ # serialization. The entries themselves are frozen; the returned array is
171
+ # a frozen copy taken under the recording mutex. Tolerates allocated but
172
+ # unrestored instances (no mutex yet), which have no entries.
173
+ def usage_entries
174
+ mutex = @usage_mutex
175
+ return [].freeze unless mutex
176
+
177
+ mutex.synchronize { @usage_entries.dup.freeze }
178
+ end
179
+
160
180
  private
161
181
 
162
182
  def initial_persist_auto_seed
@@ -189,6 +209,10 @@ module Smith
189
209
  # matches `RunResult#output`'s `.compact.first` semantics).
190
210
  @last_failed_step = nil
191
211
  @last_output = step_result[:output] if step_result.key?(:output) && !step_result[:output].nil?
212
+ # Capture serial agent attribution only on steps that carried it (an
213
+ # `execute :agent` step); deterministic steps have no :model key, so
214
+ # the last agent execution persists across intervening compute steps.
215
+ @last_agent_execution = { model: step_result[:model], provider: step_result[:provider] } if step_result.key?(:model)
192
216
  end
193
217
  end
194
218
 
@@ -340,7 +364,8 @@ module Smith
340
364
  # Same rule applies to nested-workflow rollup (see
341
365
  # `nested_execution.rb`).
342
366
  def snapshot_usage_entries
343
- @usage_entries.map { |entry| Workflow::UsageEntry.from_h(snapshot_value(entry.to_h)) }
367
+ entries = @usage_mutex.synchronize { @usage_entries.dup }
368
+ entries.map { |entry| Workflow::UsageEntry.from_h(snapshot_value(entry.to_h)) }
344
369
  end
345
370
 
346
371
  def tool_result_collector
data/lib/smith.rb CHANGED
@@ -40,11 +40,15 @@ module Smith
40
40
  setting :trace_transitions, default: true
41
41
  setting :trace_tool_calls, default: true
42
42
  setting :trace_token_usage, default: true
43
+ setting :trace_provider_calls, default: true
43
44
  setting :trace_cost, default: true
44
45
  setting :trace_fields
45
46
  setting :trace_content, default: false
46
- setting :trace_retention
47
- setting :trace_tenant_isolation, default: false
47
+ # Merge the ambient Smith::Attribution fields (execution_key, transition,
48
+ # branch_key, round, ...) into every trace payload. Attribution keys are
49
+ # identifiers, not content; they still pass through any configured
50
+ # trace_fields allowlist, and caller-supplied keys win on conflict.
51
+ setting :trace_attribution, default: true
48
52
 
49
53
  # Pricing (§4.5 — model-call cost computation)
50
54
  setting :pricing, default: nil
@@ -187,6 +191,7 @@ require_relative "smith/events"
187
191
  require_relative "smith/events/subscription"
188
192
  require_relative "smith/events/bus"
189
193
  require_relative "smith/events/step_completed"
194
+ require_relative "smith/events/step_failed"
190
195
 
191
196
  # Budget (depends on Errors)
192
197
  require_relative "smith/budget"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smith-agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Ralak
@@ -162,6 +162,7 @@ files:
162
162
  - lib/smith/agent/lifecycle.rb
163
163
  - lib/smith/agent/model_reference.rb
164
164
  - lib/smith/agent/provider_attempt.rb
165
+ - lib/smith/agent/provider_call_timing.rb
165
166
  - lib/smith/agent/provider_candidate_sequence.rb
166
167
  - lib/smith/agent/provider_completion.rb
167
168
  - lib/smith/agent/provider_failure_handling.rb
@@ -173,11 +174,13 @@ files:
173
174
  - lib/smith/agent/registry_binding.rb
174
175
  - lib/smith/agent/reserved_input_bridge.rb
175
176
  - lib/smith/agent/usage_entry_recording.rb
177
+ - lib/smith/agent/usage_traces.rb
176
178
  - lib/smith/agent/usage_tracking.rb
177
179
  - lib/smith/artifacts.rb
178
180
  - lib/smith/artifacts/file.rb
179
181
  - lib/smith/artifacts/memory.rb
180
182
  - lib/smith/artifacts/scoped_store.rb
183
+ - lib/smith/attribution.rb
181
184
  - lib/smith/budget.rb
182
185
  - lib/smith/budget/amount_contract.rb
183
186
  - lib/smith/budget/amount_representation.rb
@@ -218,6 +221,7 @@ files:
218
221
  - lib/smith/events/.keep
219
222
  - lib/smith/events/bus.rb
220
223
  - lib/smith/events/step_completed.rb
224
+ - lib/smith/events/step_failed.rb
221
225
  - lib/smith/events/subscription.rb
222
226
  - lib/smith/exponential_backoff.rb
223
227
  - lib/smith/guardrails.rb