smith-agents 0.4.3 → 0.4.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b8a1ad3fc2cb40ef0287063f81e0bc01a6bca349b1625c4173daef763785202
4
- data.tar.gz: b870358edea9686d15e164bb3d44e00edf8e50da8cb2d68b8a6a666bbc5d0f79
3
+ metadata.gz: 23c6a49b741d4ff6cfab0707ca1d917b484dc708106d1c892b4f90e2002e5b80
4
+ data.tar.gz: 72cfe59171b0f759f0312ff062f58c23aeb9a9f7e738f1603dfaf4a9174f9fbb
5
5
  SHA512:
6
- metadata.gz: bd9dd710fb5008efc7896e950b52621d3c80dab9be902b323efdc76c3d4d12e4b1c066b2219e5e36c56edea5652add7406ecb1d740db79b320035eee9047cee6
7
- data.tar.gz: 4d5e6b412d39269276096b697a399ad476dd1d4bc370792d8acfdd8f49041b567da2120e56a0fc1e090cbf4c351c9849201c8b0030fd5518f6c69495b175f534
6
+ metadata.gz: 245ac5f48372808860db62cc661bf9c9e5bf2a1a3bc4082533b16159b09c50b113bead696d4147e10e850fe0b7fff8cef775da17aa749fc4b7a5574058b765f7
7
+ data.tar.gz: 7837da6f0c9855f03a0936bdec22279ec6da9c8864fae27de8ab495a334b98654617147b36b7759d171dc19ad13ae80448b18fa3c7da4f16bf84f7c378c004bf
data/CHANGELOG.md CHANGED
@@ -6,6 +6,32 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.4.4] - 2026-07-10
10
+
11
+ Patch release for provider-safe workflow handoffs. Smith keeps accepted agent
12
+ outputs in durable session history while adapting only the next provider call
13
+ when a completed workflow stage would otherwise look like an unsupported
14
+ assistant prefill.
15
+
16
+ ### Fixed
17
+
18
+ - Adapt workflow-prepared provider input that ends with an accepted assistant
19
+ result by adding a non-persisted user continuation for the next agent call.
20
+ This preserves Smith session history while avoiding unsupported assistant
21
+ prefilling on provider models that require a user turn before completion.
22
+ Provider preparation now also reads string-keyed roles and content restored
23
+ through JSON host persistence. Explicit assistant-prefill seed messages remain
24
+ unchanged unless they match Smith's recorded accepted workflow output.
25
+
26
+ ### Verification
27
+
28
+ - Default suite: 932 examples, 0 failures.
29
+ - Practical gem-level JSON persistence/restore probe with a 25-branch parallel
30
+ handoff, provider-safe message ordering, non-persisted continuation, and
31
+ explicit assistant-prefill preservation.
32
+ - Smith Runtime host verification on Ruby 4.0.1 and Rails 8.1.3: 139 tests,
33
+ 391 assertions, 0 failures, plus a process-level restored workflow run.
34
+
9
35
  ## [0.4.3] - 2026-07-05
10
36
 
11
37
  ### Documentation
data/README.md CHANGED
@@ -17,7 +17,7 @@ environment before calling the slice complete.
17
17
 
18
18
  ```ruby
19
19
  # Gemfile
20
- gem "smith-agents", "~> 0.4.3", require: "smith"
20
+ gem "smith-agents", "0.4.4", require: "smith"
21
21
  ```
22
22
 
23
23
  ```bash
@@ -3,6 +3,10 @@
3
3
  module Smith
4
4
  class Agent
5
5
  module Lifecycle
6
+ WORKFLOW_CONTINUATION_MESSAGE =
7
+ "Use the preceding assistant result as input and perform your assigned workflow step."
8
+ private_constant :WORKFLOW_CONTINUATION_MESSAGE
9
+
6
10
  TRANSIENT_ERRORS = [
7
11
  RubyLLM::ServerError, RubyLLM::ServiceUnavailableError,
8
12
  RubyLLM::OverloadedError, RubyLLM::RateLimitError
@@ -111,6 +115,7 @@ module Smith
111
115
  def add_prepared_input(chat, prepared_input)
112
116
  return unless prepared_input
113
117
 
118
+ prepared_input = provider_safe_prepared_input(prepared_input)
114
119
  system_messages, other_messages = prepared_input.partition do |message|
115
120
  message_role(message) == :system
116
121
  end
@@ -119,6 +124,22 @@ module Smith
119
124
  other_messages.each { |message| chat.add_message(message) }
120
125
  end
121
126
 
127
+ def provider_safe_prepared_input(prepared_input)
128
+ messages = prepared_input.to_a
129
+ return messages unless workflow_handoff?(messages)
130
+
131
+ messages + [{ role: :user, content: WORKFLOW_CONTINUATION_MESSAGE }]
132
+ end
133
+
134
+ def workflow_handoff?(messages)
135
+ message = messages.reverse_each.find { |candidate| message_role(candidate) != :system }
136
+ return false unless message
137
+ return false unless message_role(message) == :assistant
138
+ return false unless defined?(@last_output) && !@last_output.nil?
139
+
140
+ message_content(message) == @last_output
141
+ end
142
+
122
143
  def merge_system_messages!(chat, prepared_system_messages)
123
144
  return prepared_system_messages.each { |message| chat.add_message(message) } unless chat.respond_to?(:messages)
124
145
 
@@ -143,19 +164,18 @@ module Smith
143
164
  end
144
165
 
145
166
  def message_role(message)
146
- if message.respond_to?(:role)
147
- message.role&.to_sym
148
- else
149
- message[:role]&.to_sym
150
- end
167
+ message_attribute(message, :role)&.to_sym
151
168
  end
152
169
 
153
170
  def message_content(message)
154
- if message.respond_to?(:content)
155
- message.content
156
- else
157
- message[:content]
158
- end
171
+ message_attribute(message, :content)
172
+ end
173
+
174
+ def message_attribute(message, name)
175
+ return message.public_send(name) if message.respond_to?(name)
176
+ return message[name] if message.respond_to?(:key?) && message.key?(name)
177
+
178
+ message[name.to_s]
159
179
  end
160
180
 
161
181
  def fallback_eligible?(error)
data/lib/smith/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Smith
4
- VERSION = "0.4.3"
4
+ VERSION = "0.4.4"
5
5
  end
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.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Ralak