composable_agents 2.1.3 → 2.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +32 -0
- data/TODO.md +4 -0
- data/lib/composable_agents/mixins/retriable.rb +118 -0
- data/lib/composable_agents/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28c6711f6b4e8807c0e62349cfd5e246bf3ed56c9c1a613d14843d5b1769477d
|
|
4
|
+
data.tar.gz: a7433cc6cc01b80eb12c1b4f0eb04c8b5391e2bd10ec88aa47608ab5d25e0f0b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dcffae4c60bfb6c5424cc41134ab5466e15c1f3a0b2580e4cef7d9d6c2634b5b43d721cd9f5e4bf604a968af35c02b561409cd9885f028b27bf61d7bbb2fc775
|
|
7
|
+
data.tar.gz: c83544d5ab8f321a63c87a7eb035763b12a573a3d3495e653b301c0ce71edc076838e5af84216d64395c497ec8efc7aff13be136e96c399e84b277f9cd7db0b0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
# [v2.2.0](https://github.com/Muriel-Salvan/composable_agents/compare/v2.1.3...v2.2.0) (2026-10-29 20:11:23)
|
|
2
|
+
|
|
3
|
+
## Global changes
|
|
4
|
+
### Patches
|
|
5
|
+
|
|
6
|
+
* [feat(composable_agents): add Retriable mixin with conditional retries](https://github.com/Muriel-Salvan/composable_agents/commit/5217fb5b448f7fffc155f864888d55da48d66074)
|
|
7
|
+
|
|
8
|
+
## Changes for composable_agents
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* [feat(composable_agents): add Retriable mixin with conditional retries](https://github.com/Muriel-Salvan/composable_agents/commit/5217fb5b448f7fffc155f864888d55da48d66074)
|
|
12
|
+
|
|
13
|
+
## Changes for retriable
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* [feat(composable_agents): add Retriable mixin with conditional retries](https://github.com/Muriel-Salvan/composable_agents/commit/5217fb5b448f7fffc155f864888d55da48d66074)
|
|
17
|
+
|
|
1
18
|
# [v2.1.3](https://github.com/Muriel-Salvan/composable_agents/compare/v2.1.2...v2.1.3) (2026-10-27 13:52:46)
|
|
2
19
|
|
|
3
20
|
### Patches
|
data/README.md
CHANGED
|
@@ -53,6 +53,7 @@ Whether you're building a code review assistant, a document summarizer, or a mul
|
|
|
53
53
|
- [`ComposableAgents::Mixins::Resumable`](#composableagentsmixinsresumable)
|
|
54
54
|
- [`ComposableAgents::Mixins::UserInteraction`](#composableagentsmixinsuserinteraction)
|
|
55
55
|
- [`ComposableAgents::Mixins::ArtifactContract`](#composableagentsmixinsartifactcontract)
|
|
56
|
+
- [`ComposableAgents::Mixins::Retriable`](#composableagentsmixinsretriable)
|
|
56
57
|
- [Additional notes](#additional-notes)
|
|
57
58
|
- [Documentation](#documentation)
|
|
58
59
|
- [How it works](#how-it-works)
|
|
@@ -203,6 +204,7 @@ itinerary_agent = ComposableAgents::Cline::Agent.new(
|
|
|
203
204
|
- 📝 **Two prompt rendering strategies** — Choose between clean **Markdown** for simple agents, or **MarkdownHeavy** with structured output parsing, execution checklists, and typed artifact support for complex agentic systems.
|
|
204
205
|
- 📦 **Typed artifact contracts** — Define and validate input/output schemas with descriptions, optional flags, and types (`:text`, `:markdown`, `:json`). The framework raises clear `MissingInputArtifactError`, `MissingOutputArtifactError`, or `ArtifactTypeError` on violations.
|
|
205
206
|
- 💾 **Resumable execution** — Persist step-by-step state to disk (via the `Resumable` mixin). Interrupted runs can be resumed seamlessly — previously completed steps are skipped, saving time and API costs.
|
|
207
|
+
- 🔁 **Conditional retries** — Wrap an agent's `#run` with the `Retriable` mixin to retry on transient failures. Register condition procs via `#retry_on` that receive the agent and any raised exception (or `nil`) and return a policy hash (optional `:wait_secs`, optional `:modifier`) to wait-and-retry, or `nil` to skip. Unhandled exceptions are re-raised when no policy applies.
|
|
206
208
|
- 🗣️ **User interaction** — Agents can ask users clarifying questions mid-execution. Works out of the box via the terminal, or through an `ai-agents` tool integration for LLM-controlled workflows.
|
|
207
209
|
- 📋 **Automatic conversation tracking** — Every prompt and response is automatically recorded with timestamps in a structured `conversation` store, ready for debugging or replay.
|
|
208
210
|
- ⚙️ **Flexible instruction system** — Use raw text, structured ordered-lists, or a mix of both to define agent instructions, rendered consistently by the chosen strategy.
|
|
@@ -448,8 +450,36 @@ Mixins are `prepend`-ed into an agent class to add specific capabilities.
|
|
|
448
450
|
agent.run(name: 'World') # => { greeting: "Hello, World!" }
|
|
449
451
|
agent.run(foo: 'bar') # Raises MissingInputArtifactError
|
|
450
452
|
```
|
|
453
|
+
|
|
451
454
|
- **Full documentation:** [Mixins::ArtifactContract on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/ArtifactContract) | [artifact_contract.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/artifact_contract.rb)
|
|
452
455
|
|
|
456
|
+
#### `ComposableAgents::Mixins::Retriable`
|
|
457
|
+
|
|
458
|
+
- **Description:** Adds conditional retry capabilities to agents. Retry condition procs are registered with `#retry_on` (or `#with_retry_on` for temporary activation) and are evaluated at the end of each run. A proc receives the agent instance and the exception raised by `run` (or `nil` if none) and returns either `nil` (no retry) or a policy hash with `:wait_secs` and optional `:modifier` keys. When a proc returns a policy, the agent waits `wait_secs` seconds, optionally calls the policy's `modifier`, and re-runs. If `run` raised an exception and no proc returned a retry policy, the exception is re-raised to the caller.
|
|
459
|
+
- **Public methods:**
|
|
460
|
+
- `#retry_on(proc)` — Register a retry condition proc. Returns `self` so calls can be chained.
|
|
461
|
+
- `#with_retry_on(proc, &block)` — Register a retry proc for the duration of a block, then unregister it when the block returns (even if it raises).
|
|
462
|
+
- policy hash — Configuration with optional `:wait_secs` (seconds to wait before retrying, defaults to 0) and optional `:modifier` (proc called with the agent before retrying).
|
|
463
|
+
- **Usage example:**
|
|
464
|
+
```ruby
|
|
465
|
+
class MyAgent < ComposableAgents::Agent
|
|
466
|
+
prepend ComposableAgents::Mixins::Retriable
|
|
467
|
+
|
|
468
|
+
def run(**inputs)
|
|
469
|
+
# Your processing here, may raise transient errors
|
|
470
|
+
{ result: inputs[:input].upcase }
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
agent = MyAgent.new
|
|
475
|
+
# Retry once with a short wait when the first run fails.
|
|
476
|
+
agent.retry_on(lambda do |agent_instance, exception|
|
|
477
|
+
agent_instance.run_count == 1 ? { wait_secs: 1 } : nil
|
|
478
|
+
end)
|
|
479
|
+
agent.run(input: 'hello')
|
|
480
|
+
```
|
|
481
|
+
- **Full documentation:** [Mixins::Retriable on RubyDoc](https://www.rubydoc.info/gems/composable_agents/ComposableAgents/Mixins/Retriable) | [retriable.rb on GitHub](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/retriable.rb)
|
|
482
|
+
|
|
453
483
|
---
|
|
454
484
|
|
|
455
485
|
### Additional notes
|
|
@@ -477,6 +507,7 @@ Mixins are `prepend`-ed into an agent class to add specific capabilities.
|
|
|
477
507
|
- [`Mixins::Resumable`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/resumable.rb) — Resumable workflow mixin
|
|
478
508
|
- [`Mixins::ArtifactContract`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/artifact_contract.rb) — Artifact validation mixin
|
|
479
509
|
- [`Mixins::UserInteraction`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/user_interaction.rb) — User question-asking mixin
|
|
510
|
+
- [`Mixins::Retriable`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/mixins/retriable.rb) — Conditional retry mixin
|
|
480
511
|
- [`Logger`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/logger.rb) — Standard logger class
|
|
481
512
|
- [`PromptRenderingStrategy::Markdown`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_rendering_strategy/markdown.rb) — Markdown prompt strategy
|
|
482
513
|
- [`PromptRenderingStrategy::MarkdownHeavy`](https://github.com/Muriel-Salvan/composable_agents/blob/main/lib/composable_agents/prompt_rendering_strategy/markdown_heavy.rb) — Heavy Markdown prompt strategy
|
|
@@ -579,6 +610,7 @@ Two strategies are included, mixed into the agent at initialization via `singlet
|
|
|
579
610
|
|
|
580
611
|
Mixins are **prepended** (using `prepend`) or **included** (using `include`) to override `#run` or add new methods:
|
|
581
612
|
|
|
613
|
+
- **[`Mixins::Retriable`](lib/composable_agents/mixins/retriable.rb)** — Wraps `#run` with conditional retry logic. Register condition procs via `#retry_on` that receive the agent and any exception raised by `run` (or `nil`) and return a policy hash (`:wait_secs`, `:modifier`) to trigger a wait-and-retry, or `nil` to skip retrying. If `run` raises and no policy is returned, the exception is re-raised.
|
|
582
614
|
- **[`Mixins::ArtifactContract`](lib/composable_agents/mixins/artifact_contract.rb)** — Wraps `#run` to validate inputs before and outputs after execution against declared contracts. Raises `MissingInputArtifactError`, `MissingOutputArtifactError`, or `ArtifactTypeError` on violations.
|
|
583
615
|
- **[`Mixins::Resumable`](lib/composable_agents/mixins/resumable.rb)** — Overrides `#run` with a **step-based execution model**. Each `step` block is persisted to `.composable_agents/runs/{run_id}/` as JSON. On re-run, completed steps are skipped — only new steps execute. Supports nested steps and agent state serialization via `export_state`/`import_state`.
|
|
584
616
|
- **[`Mixins::UserInteraction`](lib/composable_agents/mixins/user_interaction.rb)** — Adds an `#ask(question)` method. By default prompts the terminal; override `#answer_to` for custom behavior.
|
data/TODO.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
* Add the Retriable mixin to Cline::Agent with proper methods or config to define fallback models or changes in case of errors, with exponential waits.
|
|
2
|
+
* Use the Retriable mixin with ArtifactContract.
|
|
3
|
+
* Add a way to detect infinite loops in agents and act on it (maybe a mixin looking at prompts/messages).
|
|
4
|
+
* Add a way to kill an agent run on a given timeout (generic mixin).
|
|
1
5
|
* Check Rubocop-rspec exceptions and try to remove them.
|
|
2
6
|
* Add agents manifesto for contributors.
|
|
3
7
|
* Add some info logging (not debug) that would allow detecting when infinite loops or blocked agents occur.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module ComposableAgents
|
|
2
|
+
module Mixins
|
|
3
|
+
# Mixin adding retriable capabilities to agents.
|
|
4
|
+
#
|
|
5
|
+
# An agent prepending this mixin can define retry conditions that are
|
|
6
|
+
# evaluated at the end of each run. The first condition that decides to
|
|
7
|
+
# retry triggers a wait and a re-execution of the run.
|
|
8
|
+
#
|
|
9
|
+
# # Public interface
|
|
10
|
+
#
|
|
11
|
+
# * `#retry_on(&block)` registers a new retry condition proc.
|
|
12
|
+
# * `#with_retry_on(proc, &block)` registers a retry proc for the duration
|
|
13
|
+
# of a code block and unregisters it afterwards.
|
|
14
|
+
#
|
|
15
|
+
# # Behaviour
|
|
16
|
+
#
|
|
17
|
+
# * Each registered proc is kept in an internal array, in registration order.
|
|
18
|
+
# * A proc receives the agent's instance and the exception raised by `run`
|
|
19
|
+
# (or `nil` if none) and returns either `nil` (no retry) or a policy hash
|
|
20
|
+
# with optional `:wait_secs` and optional `:modifier` keys.
|
|
21
|
+
# * The run method is wrapped: it calls the original run, then evaluates the
|
|
22
|
+
# registered procs in order until one returns a non-nil policy hash.
|
|
23
|
+
# * If `run` raised an exception and no proc returned a non-nil policy hash,
|
|
24
|
+
# the exception is re-raised to the caller.
|
|
25
|
+
# * If a proc decides to retry: wait for `wait_secs` seconds, then call the
|
|
26
|
+
# `modifier` proc (if any), then call run again.
|
|
27
|
+
#
|
|
28
|
+
module Retriable
|
|
29
|
+
# @!group Public API
|
|
30
|
+
|
|
31
|
+
# Register a proc that evaluates whether the agent should be retried
|
|
32
|
+
# after a run.
|
|
33
|
+
#
|
|
34
|
+
# This method can be called several times with different procs to define
|
|
35
|
+
# various criteria to retry. The registered procs are evaluated in the
|
|
36
|
+
# order they were registered.
|
|
37
|
+
#
|
|
38
|
+
# @param proc [Proc] the retry condition proc. It receives the agent's
|
|
39
|
+
# instance and the exception raised by `run` (or `nil` if none).
|
|
40
|
+
# - param agent [Agent] The agent for which the retry is considered.
|
|
41
|
+
# - param exception [Exception, nil] The exception that was raised by the run, or nil if none.
|
|
42
|
+
# - result [Hash\\{Symbol => Object}] Definition of the retry policy, with optional properties:
|
|
43
|
+
# - wait_secs [Integer, Float] Wait time in seconds before retrying (defaults to 0).
|
|
44
|
+
# - modifier [Proc] Code called to modify the agent's state before retrying (defaults to doing nothing).
|
|
45
|
+
# - param agent [Agent] The agent for which the retry is considered.
|
|
46
|
+
# @return [self]
|
|
47
|
+
def retry_on(proc)
|
|
48
|
+
retry_policies << proc
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Register a retry proc for the duration of a code block, then
|
|
53
|
+
# unregister it when the block returns (even if it raises).
|
|
54
|
+
#
|
|
55
|
+
# The block can itself register other procs (with {#retry_on}) and
|
|
56
|
+
# those are kept after the block ends; only the proc given to this
|
|
57
|
+
# method is unregistered.
|
|
58
|
+
#
|
|
59
|
+
# @param proc [Proc] the retry condition proc (see #retry_on)
|
|
60
|
+
# @yield the code block in which the proc is active
|
|
61
|
+
def with_retry_on(proc, &)
|
|
62
|
+
retry_on(proc)
|
|
63
|
+
yield
|
|
64
|
+
ensure
|
|
65
|
+
retry_policies.delete(proc)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Wrap the agent's run method with retry logic.
|
|
69
|
+
#
|
|
70
|
+
# It calls the original run, then evaluates the registered procs in
|
|
71
|
+
# order until one returns a non-nil policy hash. Each proc receives
|
|
72
|
+
# the agent's instance and the exception raised by `run` (or `nil` if
|
|
73
|
+
# none). If `run` raised an exception and no proc returned a non-nil
|
|
74
|
+
# policy hash, the exception is re-raised to the caller. If one does,
|
|
75
|
+
# it waits for the policy's `wait_secs`, applies the `modifier` proc
|
|
76
|
+
# (if any) and calls run again.
|
|
77
|
+
#
|
|
78
|
+
# @param input_artifacts [Hash{Symbol => Object}] input artifacts
|
|
79
|
+
# @return [Hash{Symbol => Object}] output artifacts from the last run
|
|
80
|
+
def run(**input_artifacts)
|
|
81
|
+
output_artifacts = nil
|
|
82
|
+
retry_index = 0
|
|
83
|
+
loop do
|
|
84
|
+
exception = nil
|
|
85
|
+
begin
|
|
86
|
+
output_artifacts = super
|
|
87
|
+
rescue StandardError => e
|
|
88
|
+
exception = e
|
|
89
|
+
end
|
|
90
|
+
policy = nil
|
|
91
|
+
retry_policies.each do |policy_proc|
|
|
92
|
+
policy = policy_proc.call(self, exception)
|
|
93
|
+
break if policy
|
|
94
|
+
end
|
|
95
|
+
if policy
|
|
96
|
+
retry_index += 1
|
|
97
|
+
wait_secs = policy[:wait_secs] || 0
|
|
98
|
+
logger.debug "Retry ##{retry_index}: re-running in #{wait_secs}s#{" (exception #{exception})" if exception}"
|
|
99
|
+
sleep(wait_secs) if wait_secs.positive?
|
|
100
|
+
policy[:modifier]&.call(self)
|
|
101
|
+
else
|
|
102
|
+
raise exception if exception
|
|
103
|
+
|
|
104
|
+
break
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
output_artifacts
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# @return [Array<Proc>] the registered retry condition procs
|
|
113
|
+
def retry_policies
|
|
114
|
+
@retry_policies ||= []
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: composable_agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Muriel Salvan
|
|
@@ -87,6 +87,7 @@ files:
|
|
|
87
87
|
- lib/composable_agents/mixins/ai_agent_user_interaction.rb
|
|
88
88
|
- lib/composable_agents/mixins/artifact_contract.rb
|
|
89
89
|
- lib/composable_agents/mixins/resumable.rb
|
|
90
|
+
- lib/composable_agents/mixins/retriable.rb
|
|
90
91
|
- lib/composable_agents/mixins/user_interaction.rb
|
|
91
92
|
- lib/composable_agents/prompt_driven_agent.rb
|
|
92
93
|
- lib/composable_agents/prompt_rendering_strategy/markdown.rb
|