composable_agents 1.0.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 +7 -0
- data/CHANGELOG.md +109 -0
- data/README.md +29 -0
- data/TODO.md +5 -0
- data/lib/composable_agents/agent.rb +53 -0
- data/lib/composable_agents/ai_agents/agent.rb +105 -0
- data/lib/composable_agents/ai_agents/tools/ask_user_tool.rb +38 -0
- data/lib/composable_agents/ai_agents/tools/create_artifact_tool.rb +40 -0
- data/lib/composable_agents/ai_agents/tools/get_artifact_tool.rb +39 -0
- data/lib/composable_agents/cline/agent.rb +246 -0
- data/lib/composable_agents/instructions.rb +42 -0
- data/lib/composable_agents/mixins/ai_agent_user_interaction.rb +23 -0
- data/lib/composable_agents/mixins/artifact_contract.rb +181 -0
- data/lib/composable_agents/mixins/logger.rb +47 -0
- data/lib/composable_agents/mixins/resumable.rb +214 -0
- data/lib/composable_agents/mixins/user_interaction.rb +42 -0
- data/lib/composable_agents/prompt_driven_agent.rb +241 -0
- data/lib/composable_agents/prompt_rendering_strategy/markdown.rb +108 -0
- data/lib/composable_agents/prompt_rendering_strategy/markdown_heavy.rb +301 -0
- data/lib/composable_agents/ruby_agent.rb +28 -0
- data/lib/composable_agents/utils/markdown.rb +56 -0
- data/lib/composable_agents/version.rb +6 -0
- data/lib/composable_agents.rb +7 -0
- metadata +117 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module ComposableAgents
|
|
4
|
+
module PromptRenderingStrategy
|
|
5
|
+
# Render prompt as Markdown documents with a lot of emphasis for complex agentic systems.
|
|
6
|
+
# This prompt strategy needs to be used conjointly with the ArtifactContract mixin.
|
|
7
|
+
# This mixin also adds the following methods to be used:
|
|
8
|
+
# - `#parse_output_artifacts(text)` Parses a text that comes from the agent to retrieve output artifacts from it.
|
|
9
|
+
# - `#artifact_ref(artifact_name) -> String` Returns the artifact name as seen by the assistant.
|
|
10
|
+
# This can be used to refer to the artifact names properly in the user or system instructions.
|
|
11
|
+
module MarkdownHeavy
|
|
12
|
+
# @!group Internal
|
|
13
|
+
|
|
14
|
+
include Markdown
|
|
15
|
+
|
|
16
|
+
# Render an instruction of type ordered_list
|
|
17
|
+
#
|
|
18
|
+
# @param instruction [Array<String>] The instruction to render
|
|
19
|
+
# @return [String] The rendered instruction
|
|
20
|
+
def render_instruction_ordered_list(instruction)
|
|
21
|
+
return '' if instruction.empty?
|
|
22
|
+
|
|
23
|
+
checklist_name = "#{name || 'Agent'} Execution Checklist"
|
|
24
|
+
<<~EO_INSTRUCTIONS
|
|
25
|
+
Always follow all those sequential steps.
|
|
26
|
+
|
|
27
|
+
# 1. Create the #{checklist_name} (MANDATORY)
|
|
28
|
+
|
|
29
|
+
- Before executing anything, create a checklist named #{checklist_name} with all steps of these instructions.
|
|
30
|
+
- Do not create files to track this checklist: keep it in your memory.
|
|
31
|
+
- The #{checklist_name} must include all numbered steps explicitly.
|
|
32
|
+
- After completing each step of these instructions, mark the item in the #{checklist_name} as completed.
|
|
33
|
+
- Do not skip any item.
|
|
34
|
+
- If an item cannot be executed, explicitly explain why.
|
|
35
|
+
- Never mark the task as completed while any item from the #{checklist_name} remains open.
|
|
36
|
+
|
|
37
|
+
#{instruction.map.with_index { |step, step_idx| "# #{step_idx + 2}. #{Utils::Markdown.align_markdown_headers(step, level: 2).strip}" }.join("\n\n")}
|
|
38
|
+
|
|
39
|
+
# #{instruction.size + 2}. Final Verification (MANDATORY)
|
|
40
|
+
|
|
41
|
+
Before declaring the task complete:
|
|
42
|
+
|
|
43
|
+
- Re-list all numbered steps from the #{checklist_name}.
|
|
44
|
+
- Confirm each one was executed.
|
|
45
|
+
- If any step was not executed, execute it now.
|
|
46
|
+
EO_INSTRUCTIONS
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Render the system prompt
|
|
50
|
+
# The following instance variables are accessible to render the prompt:
|
|
51
|
+
# - `@input_artifacts`
|
|
52
|
+
# - `@role`
|
|
53
|
+
# - `@objective`
|
|
54
|
+
# - `@constraints`
|
|
55
|
+
#
|
|
56
|
+
# @param rendered_instructions [String, nil] The rendered system instructions, or nil if none
|
|
57
|
+
# @return [String] The rendered system prompt
|
|
58
|
+
def render_system_prompt(rendered_instructions)
|
|
59
|
+
sections = [super]
|
|
60
|
+
# Don't document the user instructions themselves
|
|
61
|
+
input_contracts = normalized_input_artifacts_contracts.except(:user_instructions)
|
|
62
|
+
unless input_contracts.empty?
|
|
63
|
+
sections << <<~EO_SECTION
|
|
64
|
+
# Input artifacts' concept and usage
|
|
65
|
+
|
|
66
|
+
- Artifacts are documents that you can get as input.
|
|
67
|
+
- Each artifact is identified by a name, like `#{artifact_ref(:plan)}`.
|
|
68
|
+
- You must consider all artifacts given in the "Input artifacts" section of the user prompt.
|
|
69
|
+
- All artifacts presented in the "Input artifacts" section of the user prompt provide their content in an in-line JSON document.
|
|
70
|
+
#{
|
|
71
|
+
input_contracts.map do |artifact_name, artifact_contract|
|
|
72
|
+
art_ref = artifact_ref(artifact_name)
|
|
73
|
+
[
|
|
74
|
+
"- The content of input artifact `#{art_ref}` describes this: #{artifact_contract[:description]}"
|
|
75
|
+
] + (
|
|
76
|
+
if artifact_contract[:optional]
|
|
77
|
+
["- The input artifact `#{art_ref}` is optional and may not be given to you."]
|
|
78
|
+
else
|
|
79
|
+
["- The input artifact `#{art_ref}` is expected to be in the user prompt."]
|
|
80
|
+
end
|
|
81
|
+
) + [
|
|
82
|
+
"- The input artifact `#{art_ref}` artifact content is embedded directly in the user prompt as in-line JSON. It is NOT a file. Do NOT try to open it as a file."
|
|
83
|
+
]
|
|
84
|
+
end.compact.flatten(1).join("\n")
|
|
85
|
+
}
|
|
86
|
+
EO_SECTION
|
|
87
|
+
end
|
|
88
|
+
unless normalized_output_artifacts_contracts.empty?
|
|
89
|
+
sections << <<~EO_SECTION
|
|
90
|
+
# Output artifacts' concept and usage
|
|
91
|
+
|
|
92
|
+
- The user will ask you to provide some artifacts as output.
|
|
93
|
+
- You must always return the required artifact as a JSON document in your response, with its name in the JSON header, like this:
|
|
94
|
+
```json output_artifact=#{artifact_ref(:name)}
|
|
95
|
+
{artifact_content}
|
|
96
|
+
```
|
|
97
|
+
- Do not create files for output artifacts: always give them inside embedded JSON in your last response.
|
|
98
|
+
- Always return output artifacts that the user is asking you to provide.
|
|
99
|
+
- You can return several output artifacts in your responses if needed.
|
|
100
|
+
|
|
101
|
+
Following sections enumerate all expected output artifacts.
|
|
102
|
+
|
|
103
|
+
#{
|
|
104
|
+
normalized_output_artifacts_contracts.map do |artifact_name, artifact_contract|
|
|
105
|
+
art_ref = artifact_ref(artifact_name)
|
|
106
|
+
(
|
|
107
|
+
[
|
|
108
|
+
"## Output artifact `#{art_ref}`",
|
|
109
|
+
'',
|
|
110
|
+
"- The content of output artifact `#{art_ref}` should describe this: #{artifact_contract[:description]}"
|
|
111
|
+
] + (
|
|
112
|
+
artifact_contract[:type] ? ["- The output artifact `#{art_ref}` content format should be #{artifact_contract[:type]}"] : []
|
|
113
|
+
) + [
|
|
114
|
+
<<~EO_ITEM.strip
|
|
115
|
+
- The output artifact `#{art_ref}` should be given in a block like this:
|
|
116
|
+
#{example_json_block(artifact_name)}
|
|
117
|
+
EO_ITEM
|
|
118
|
+
]
|
|
119
|
+
).join("\n")
|
|
120
|
+
end.join("\n\n")
|
|
121
|
+
}
|
|
122
|
+
EO_SECTION
|
|
123
|
+
end
|
|
124
|
+
sections.map(&:strip).join("\n\n")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Render the user prompt
|
|
128
|
+
# The following instance variables are accessible to render the prompt:
|
|
129
|
+
# - `@role`
|
|
130
|
+
# - `@objective`
|
|
131
|
+
# - `@constraints`
|
|
132
|
+
#
|
|
133
|
+
# @param rendered_instructions [String, nil] The rendered instructions, or nil if none
|
|
134
|
+
# @param input_artifacts [Hash{Symbol => Object}] The input artifacts content for which we render this prompt, per artifact name
|
|
135
|
+
# @return [String] The rendered user prompt
|
|
136
|
+
def render_user_prompt(rendered_instructions, input_artifacts:)
|
|
137
|
+
sections = []
|
|
138
|
+
unless input_artifacts.empty?
|
|
139
|
+
sections << <<~EO_SECTION.strip
|
|
140
|
+
# Input artifacts
|
|
141
|
+
|
|
142
|
+
#{
|
|
143
|
+
input_artifacts.map do |artifact_name, artifact_content|
|
|
144
|
+
art_ref = artifact_ref(artifact_name)
|
|
145
|
+
<<~EO_ARTIFACT_SECTION.strip
|
|
146
|
+
## `#{art_ref}`
|
|
147
|
+
|
|
148
|
+
```json input_artifact=#{art_ref}
|
|
149
|
+
#{JSON.dump(artifact_content)}
|
|
150
|
+
```
|
|
151
|
+
EO_ARTIFACT_SECTION
|
|
152
|
+
end.join("\n\n")
|
|
153
|
+
}
|
|
154
|
+
EO_SECTION
|
|
155
|
+
end
|
|
156
|
+
sections << <<~EO_SECTION if rendered_instructions && !rendered_instructions.empty?
|
|
157
|
+
# User instructions
|
|
158
|
+
|
|
159
|
+
#{Utils::Markdown.align_markdown_headers(rendered_instructions, level: 2)}
|
|
160
|
+
EO_SECTION
|
|
161
|
+
sections.map(&:strip).join("\n\n")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Get user instructions for missing output artifacts
|
|
165
|
+
#
|
|
166
|
+
# @param missing_output_artifacts [Hash{Symbol => Object}] The missing output artifacts information, per artifact name
|
|
167
|
+
# Information can contain the following attributes:
|
|
168
|
+
# - description [String] The artifact's description.
|
|
169
|
+
# - error [String, nil] An error message related to this missing artifact.
|
|
170
|
+
# @return [Object] The user instructions (see Instructions#initialize)
|
|
171
|
+
def missing_output_user_instructions(missing_output_artifacts)
|
|
172
|
+
log_debug "[Artifact] - Asking assistant for missing output artifacts `#{missing_output_artifacts.keys.join(', ')}` to be returned in its next answer."
|
|
173
|
+
<<~EO_PROMPT
|
|
174
|
+
The following output artifacts are missing from your previous responses:
|
|
175
|
+
#{
|
|
176
|
+
missing_output_artifacts.map do |artifact_name, missing_info|
|
|
177
|
+
["- `#{artifact_ref(artifact_name)}`: #{missing_info[:description]}"] +
|
|
178
|
+
(missing_info[:error] ? [" An error occurred while reading this artifact from your previous responses: #{missing_info[:error]}"] : [])
|
|
179
|
+
end.flatten(1).join("\n")
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
You must provide each one of them in your next response using embedded JSON blocks like this:
|
|
183
|
+
|
|
184
|
+
#{
|
|
185
|
+
missing_output_artifacts.keys.map { |artifact_name| example_json_block(artifact_name) }.join("\n\n")
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
- You must return all those artifacts in your next response (MANDATORY).
|
|
189
|
+
EO_PROMPT
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Get the artifact reference name communicated to the assistant
|
|
193
|
+
#
|
|
194
|
+
# @param artifact_name [Symbol] The artifact name
|
|
195
|
+
# @return [String] The artifact reference name used for the assistant
|
|
196
|
+
def artifact_ref(artifact_name)
|
|
197
|
+
"ARTIFACT_#{artifact_name.to_s.upcase}"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Parse some text to find output artifacts in it.
|
|
201
|
+
#
|
|
202
|
+
# @param text [String] The text to be parsed
|
|
203
|
+
def parse_output_artifacts(text)
|
|
204
|
+
# Scan for JSON documents with artifact markers in the format:
|
|
205
|
+
# ```json output_artifact=ARTIFACT_<NAME>
|
|
206
|
+
# {artifact_content}
|
|
207
|
+
# ```
|
|
208
|
+
text.scan(/```json\s+output_artifact=(\S+)\n(.*?)```(?=\n|\z)/m) do
|
|
209
|
+
art_ref = Regexp.last_match(1)
|
|
210
|
+
content = Regexp.last_match(2).strip
|
|
211
|
+
# Convert the assistant artifact name (e.g. ARTIFACT_PLAN) back to a symbol (e.g. :plan)
|
|
212
|
+
artifact_name = (art_ref.start_with?('ARTIFACT_') ? art_ref.sub(/^ARTIFACT_/, '') : art_ref).downcase.to_sym
|
|
213
|
+
artifact_json_content = nil
|
|
214
|
+
begin
|
|
215
|
+
artifact_json_content = JSON.parse(content)
|
|
216
|
+
rescue JSON::ParserError => e
|
|
217
|
+
report_error_for_output_artifact(artifact_name, e.to_s)
|
|
218
|
+
next
|
|
219
|
+
end
|
|
220
|
+
# Use the type info to better parse the JSON into the real artifact content
|
|
221
|
+
artifact_type = normalized_output_artifacts_contracts.dig(artifact_name, :type)
|
|
222
|
+
artifact_content =
|
|
223
|
+
case artifact_type
|
|
224
|
+
when nil, :json
|
|
225
|
+
artifact_json_content
|
|
226
|
+
when :text
|
|
227
|
+
if artifact_json_content.key?('text')
|
|
228
|
+
if artifact_json_content['text'].is_a?(String)
|
|
229
|
+
artifact_json_content['text']
|
|
230
|
+
else
|
|
231
|
+
report_error_for_output_artifact(
|
|
232
|
+
artifact_name,
|
|
233
|
+
'Wrong format for artifact content in key "text": ' \
|
|
234
|
+
"expecting a raw String but got #{artifact_json_content['text'].class.name} instead."
|
|
235
|
+
)
|
|
236
|
+
nil
|
|
237
|
+
end
|
|
238
|
+
else
|
|
239
|
+
report_error_for_output_artifact(
|
|
240
|
+
artifact_name,
|
|
241
|
+
'Missing required key "text" containing the artifact text content in the JSON artifact response.'
|
|
242
|
+
)
|
|
243
|
+
nil
|
|
244
|
+
end
|
|
245
|
+
when :markdown
|
|
246
|
+
if artifact_json_content.key?('markdown')
|
|
247
|
+
if artifact_json_content['markdown'].is_a?(String)
|
|
248
|
+
artifact_json_content['markdown']
|
|
249
|
+
else
|
|
250
|
+
report_error_for_output_artifact(
|
|
251
|
+
artifact_name,
|
|
252
|
+
'Wrong format for artifact content in key "markdown": ' \
|
|
253
|
+
"expecting a Markdown string but got #{artifact_json_content['markdown'].class.name} instead."
|
|
254
|
+
)
|
|
255
|
+
nil
|
|
256
|
+
end
|
|
257
|
+
else
|
|
258
|
+
report_error_for_output_artifact(
|
|
259
|
+
artifact_name,
|
|
260
|
+
'Missing required key "markdown" containing the artifact Markdown content in the JSON artifact response.'
|
|
261
|
+
)
|
|
262
|
+
nil
|
|
263
|
+
end
|
|
264
|
+
else
|
|
265
|
+
raise "Unknown artifact type: #{artifact_type}"
|
|
266
|
+
end
|
|
267
|
+
save_output_artifact(artifact_name, artifact_content) if artifact_content
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
private
|
|
272
|
+
|
|
273
|
+
# Provide a Markdown example of a JSON block for a given output artifact
|
|
274
|
+
#
|
|
275
|
+
# @param artifact_name [Symbol] The output artifact name
|
|
276
|
+
# @return [String] Corresponding Markdown example
|
|
277
|
+
def example_json_block(artifact_name)
|
|
278
|
+
art_ref = artifact_ref(artifact_name)
|
|
279
|
+
artifact_type = normalized_output_artifacts_contracts.dig(artifact_name, :type)
|
|
280
|
+
<<~EO_MARKDOWN.strip
|
|
281
|
+
```json output_artifact=#{art_ref}
|
|
282
|
+
#{
|
|
283
|
+
case artifact_type
|
|
284
|
+
when nil
|
|
285
|
+
"#{art_ref}_content"
|
|
286
|
+
when :text
|
|
287
|
+
"{\"text\":\"#{art_ref}_raw_text_content\"}"
|
|
288
|
+
when :markdown
|
|
289
|
+
"{\"markdown\":\"#{art_ref}_markdown_content\"}"
|
|
290
|
+
when :json
|
|
291
|
+
"{#{art_ref}_json_content}"
|
|
292
|
+
else
|
|
293
|
+
raise "Unknown artifact type: #{artifact_type}"
|
|
294
|
+
end
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
EO_MARKDOWN
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module ComposableAgents
|
|
2
|
+
# Agent implementation that wraps a Ruby Proc for custom logic
|
|
3
|
+
#
|
|
4
|
+
# This agent allows wrapping arbitrary Ruby logic as an Agent by providing
|
|
5
|
+
# a Proc that handles the transformation of input artifacts to output artifacts.
|
|
6
|
+
class RubyAgent < Agent
|
|
7
|
+
# @!group Public API
|
|
8
|
+
|
|
9
|
+
# Initialize a new RubyAgent with a processing proc
|
|
10
|
+
#
|
|
11
|
+
# @param processor [#call(Hash{Symbol => Object}) => Hash{Symbol => Object}] The agent logic.
|
|
12
|
+
# This proc will receive the input artifacts hash and must return a hash of output artifacts.
|
|
13
|
+
# - Param input_artifacts [Hash\\{Symbol => Object}] Input artifacts provided to the agent
|
|
14
|
+
# - Return [Hash\\{Symbol => Object}] Output artifacts produced by the agent
|
|
15
|
+
def initialize(processor, *args, **kwargs)
|
|
16
|
+
super(*args, **kwargs)
|
|
17
|
+
@processor = processor
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Execute the agent to generate some output artifacts based on some input artifacts.
|
|
21
|
+
#
|
|
22
|
+
# @param input_artifacts [Hash{Symbol => Object}] The input artifacts content, per artifact name
|
|
23
|
+
# @return [Hash{Symbol => Object}] The output artifacts returned by the Proc
|
|
24
|
+
def run(**input_artifacts)
|
|
25
|
+
@processor.call(input_artifacts)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'commonmarker'
|
|
2
|
+
|
|
3
|
+
module ComposableAgents
|
|
4
|
+
# Various internal helpers and utilities
|
|
5
|
+
module Utils
|
|
6
|
+
# Internal util methods to handle Markdown
|
|
7
|
+
module Markdown
|
|
8
|
+
class << self
|
|
9
|
+
# @!group Internal
|
|
10
|
+
|
|
11
|
+
# Align markdown headers in a String to a given level.
|
|
12
|
+
# This method parses the String as a markdown document, sees the minimum current header level,
|
|
13
|
+
# and changes it while preserving the structure and hierarchy so that this min level is equal to `level`.
|
|
14
|
+
#
|
|
15
|
+
# @param markdown [String] The markdown content to align
|
|
16
|
+
# @param level [Integer] The target level for the minimum header
|
|
17
|
+
# @return [String] The aligned markdown content
|
|
18
|
+
def align_markdown_headers(markdown, level: 2)
|
|
19
|
+
doc = Commonmarker.parse(markdown)
|
|
20
|
+
min_level = find_minimum_header_level(doc)
|
|
21
|
+
return markdown if min_level.nil? || min_level == level
|
|
22
|
+
|
|
23
|
+
adjust_header_levels(doc, level - min_level)
|
|
24
|
+
# Unescape dots in headers
|
|
25
|
+
# TODO: Remove this gsub when CommonMarker will be fixed.
|
|
26
|
+
doc.to_commonmark.gsub(/^\#{1,6}\s+\h+\K\\\. /, '. ')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Find the minimum header level in a CommonMarker document
|
|
30
|
+
#
|
|
31
|
+
# @param doc [CommonMarker::Document] The parsed CommonMarker document
|
|
32
|
+
# @return [Integer, nil] The minimum header level found, or nil if no headers exist
|
|
33
|
+
def find_minimum_header_level(doc)
|
|
34
|
+
min_level = nil
|
|
35
|
+
doc.walk do |node|
|
|
36
|
+
if node.type == :heading
|
|
37
|
+
current_level = node.header_level
|
|
38
|
+
min_level = current_level if min_level.nil? || current_level < min_level
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
min_level
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Adjust header levels in a CommonMarker document by a given difference
|
|
45
|
+
#
|
|
46
|
+
# @param doc [CommonMarker::Document] The parsed CommonMarker document
|
|
47
|
+
# @param level_diff [Integer] The difference to add to each header level
|
|
48
|
+
def adjust_header_levels(doc, level_diff)
|
|
49
|
+
doc.walk do |node|
|
|
50
|
+
node.header_level = node.header_level + level_diff if node.type == :heading
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: composable_agents
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Muriel Salvan
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ai-agents
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.10'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.10'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: cline-rb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: commonmarker
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.7'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.7'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: zeitwerk
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '2.7'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '2.7'
|
|
68
|
+
email: muriel@x-aeon.com
|
|
69
|
+
executables: []
|
|
70
|
+
extensions: []
|
|
71
|
+
extra_rdoc_files: []
|
|
72
|
+
files:
|
|
73
|
+
- CHANGELOG.md
|
|
74
|
+
- README.md
|
|
75
|
+
- TODO.md
|
|
76
|
+
- lib/composable_agents.rb
|
|
77
|
+
- lib/composable_agents/agent.rb
|
|
78
|
+
- lib/composable_agents/ai_agents/agent.rb
|
|
79
|
+
- lib/composable_agents/ai_agents/tools/ask_user_tool.rb
|
|
80
|
+
- lib/composable_agents/ai_agents/tools/create_artifact_tool.rb
|
|
81
|
+
- lib/composable_agents/ai_agents/tools/get_artifact_tool.rb
|
|
82
|
+
- lib/composable_agents/cline/agent.rb
|
|
83
|
+
- lib/composable_agents/instructions.rb
|
|
84
|
+
- lib/composable_agents/mixins/ai_agent_user_interaction.rb
|
|
85
|
+
- lib/composable_agents/mixins/artifact_contract.rb
|
|
86
|
+
- lib/composable_agents/mixins/logger.rb
|
|
87
|
+
- lib/composable_agents/mixins/resumable.rb
|
|
88
|
+
- lib/composable_agents/mixins/user_interaction.rb
|
|
89
|
+
- lib/composable_agents/prompt_driven_agent.rb
|
|
90
|
+
- lib/composable_agents/prompt_rendering_strategy/markdown.rb
|
|
91
|
+
- lib/composable_agents/prompt_rendering_strategy/markdown_heavy.rb
|
|
92
|
+
- lib/composable_agents/ruby_agent.rb
|
|
93
|
+
- lib/composable_agents/utils/markdown.rb
|
|
94
|
+
- lib/composable_agents/version.rb
|
|
95
|
+
homepage: https://github.com/Muriel-Salvan/composable_agents
|
|
96
|
+
licenses:
|
|
97
|
+
- BSD-3-Clause
|
|
98
|
+
metadata:
|
|
99
|
+
rubygems_mfa_required: 'true'
|
|
100
|
+
rdoc_options: []
|
|
101
|
+
require_paths:
|
|
102
|
+
- lib
|
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '3.1'
|
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - ">="
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '0'
|
|
113
|
+
requirements: []
|
|
114
|
+
rubygems_version: 3.6.9
|
|
115
|
+
specification_version: 4
|
|
116
|
+
summary: Composable AI agents framework
|
|
117
|
+
test_files: []
|