deepagents 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c68db07095761fd89fad4e2d5c27427ae659d94eaf9d2ebc8f4d5b7f2e243727
4
+ data.tar.gz: aca823c82b3ae695af8e2f75e5450a10d55a94e064fb59ec901a48007a2c9ccf
5
+ SHA512:
6
+ metadata.gz: 69998915abd938c0de71ef6f0248a52708fa3f0680e076f0421cfd4a6305980da9456a7646fdcc8f969256de7da20ad17121314b2b653cfc88b25ded2fad27ac
7
+ data.tar.gz: 5fdf4e47952dcfb7b83e8e6c3c02c369491a219b07d16dbf544f12b761e8a313577db66956f78d9c929d53971e30e5f0a66745227afac55ff0f470cd983f109c
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chris
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # šŸ§ šŸ¤– DeepAgents for Ruby
2
+
3
+ Using an LLM to call tools in a loop is the simplest form of an agent. This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks. Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things: a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
4
+
5
+ `deepagents` is a Ruby gem that implements these in a general purpose way so that you can easily create a Deep Agent for your application.
6
+
7
+ **Acknowledgements: This project was inspired by the Python [deepagents](https://github.com/langchain-ai/deepagents) library and is a Ruby implementation of its functionality.**
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'deepagents'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```bash
20
+ $ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```bash
26
+ $ gem install deepagents
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Basic Usage
32
+
33
+ ```ruby
34
+ require 'deepagents'
35
+ require 'anthropic'
36
+
37
+ # Initialize the Anthropic client
38
+ anthropic_client = Anthropic::Client.new(api_key: ENV['ANTHROPIC_API_KEY'])
39
+
40
+ # Define a search tool
41
+ def internet_search(query, max_results: 5, topic: 'general', include_raw_content: false)
42
+ # In a real implementation, this would call a search API
43
+ # For example, you could use a Ruby wrapper for Tavily or another search API
44
+ "Search results for: #{query}"
45
+ end
46
+
47
+ # Create a tool object
48
+ search_tool = DeepAgents::Tool.new(
49
+ "internet_search",
50
+ "Run a web search"
51
+ ) do |query, max_results: 5, topic: 'general', include_raw_content: false|
52
+ internet_search(query, max_results: max_results, topic: topic, include_raw_content: include_raw_content)
53
+ end
54
+
55
+ # Prompt prefix to steer the agent to be an expert researcher
56
+ research_instructions = <<~INSTRUCTIONS
57
+ You are an expert researcher. Your job is to conduct thorough research, and then write a polished report.
58
+
59
+ You have access to a few tools.
60
+
61
+ ## `internet_search`
62
+
63
+ Use this to run an internet search for a given query. You can specify the number of results, the topic, and whether raw content should be included.
64
+ INSTRUCTIONS
65
+
66
+ # Create the agent using built-in model adapters
67
+ agent = DeepAgents.create_deep_agent(
68
+ [search_tool],
69
+ research_instructions,
70
+ model: DeepAgents.claude_model() # or DeepAgents.openai_model()
71
+ )
72
+
73
+ # Invoke the agent
74
+ result = agent.invoke({
75
+ messages: [{role: "user", content: "what is Ruby on Rails?"}]
76
+ })
77
+
78
+ # Access the agent's response
79
+ puts result.messages.last[:content]
80
+ ```
81
+
82
+ ### Using with langchainrb and langgraph_rb
83
+
84
+ ```ruby
85
+ require 'deepagents'
86
+ require 'langchainrb'
87
+ require 'langgraph_rb'
88
+
89
+ # Create a search tool using langchainrb's tool system
90
+ search_tool = Langchain::Tool.new(
91
+ name: "internet_search",
92
+ description: "Run a web search",
93
+ function: ->(query:, max_results: 5) {
94
+ # In a real implementation, this would call a search API
95
+ "Search results for: #{query}"
96
+ }
97
+ )
98
+
99
+ # Convert to DeepAgents tool
100
+ deep_search_tool = DeepAgents::Tool.from_langchain(search_tool)
101
+
102
+ # Create a deep agent with langchainrb integration
103
+ agent = DeepAgents.create_deep_agent(
104
+ [deep_search_tool],
105
+ "You are an expert researcher. Your job is to conduct thorough research.",
106
+ model: "claude-3-sonnet-20240229" # Will automatically create appropriate model adapter
107
+ )
108
+
109
+ # Run the agent
110
+ result = agent.run("What is Ruby on Rails?")
111
+ puts result
112
+ ```
113
+
114
+ ## Creating a custom deep agent
115
+
116
+ There are three parameters you can pass to `create_deep_agent` to create your own custom deep agent.
117
+
118
+ ### `tools` (Required)
119
+
120
+ The first argument to `create_deep_agent` is `tools`.
121
+ This should be an array of tool objects created using `DeepAgents::Tool.new`.
122
+ The agent (and any subagents) will have access to these tools.
123
+
124
+ ### `instructions` (Required)
125
+
126
+ The second argument to `create_deep_agent` is `instructions`.
127
+ This will serve as part of the prompt of the deep agent.
128
+ Note that there is a built-in system prompt as well, so this is not the *entire* prompt the agent will see.
129
+
130
+ ### `model` (Optional)
131
+
132
+ By default, `deepagents` uses a simple placeholder model. You should provide your own model implementation that interfaces with an LLM API like Anthropic's Claude or OpenAI.
133
+
134
+ Your model class should implement a `generate` method that takes a prompt and messages array and returns a response string.
135
+
136
+ ### `subagents` (Optional)
137
+
138
+ A keyword parameter to `create_deep_agent` is `subagents`.
139
+ This can be used to specify any custom subagents this deep agent will have access to.
140
+
141
+ `subagents` should be an array of `DeepAgents::SubAgent` objects, where each has:
142
+
143
+ - **name**: The name of the subagent, and how the main agent will call the subagent
144
+ - **description**: The description of the subagent that is shown to the main agent
145
+ - **prompt**: The prompt used for the subagent
146
+ - **tools**: The list of tools that the subagent has access to (optional)
147
+
148
+ To use it looks like:
149
+
150
+ ```ruby
151
+ research_sub_agent = DeepAgents::SubAgent.new(
152
+ name: "research-agent",
153
+ description: "Used to research more in depth questions",
154
+ prompt: sub_research_prompt
155
+ )
156
+
157
+ agent = DeepAgents.create_deep_agent(
158
+ tools,
159
+ prompt,
160
+ subagents: [research_sub_agent]
161
+ )
162
+ ```
163
+
164
+ ## Integration with langchainrb and langgraph_rb
165
+
166
+ DeepAgents now integrates with the popular Ruby gems `langchainrb` and `langgraph_rb` to provide enhanced functionality and compatibility with the broader LLM ecosystem.
167
+
168
+ ### langchainrb Integration
169
+
170
+ The `langchainrb` integration provides:
171
+
172
+ - **Model Compatibility**: All DeepAgents model adapters can be converted to langchainrb models using the `to_langchain_model` method
173
+ - **Tool Interoperability**: Convert between DeepAgents tools and langchainrb tools
174
+ - **Vector Database Access**: Leverage langchainrb's vector database integrations for RAG applications
175
+ - **Prompt Management**: Use langchainrb's prompt templates with DeepAgents
176
+
177
+ ### langgraph_rb Integration
178
+
179
+ The `langgraph_rb` integration enables:
180
+
181
+ - **Stateful Agents**: Build agents that maintain state across interactions
182
+ - **Directed Graphs**: Create complex agent workflows with directed graph structures
183
+ - **Multi-Actor Systems**: Coordinate multiple agents working together
184
+ - **Checkpointing**: Save and restore agent state
185
+
186
+ ## Deep Agent Details
187
+
188
+ The below components are built into `deepagents` and help make it work for deep tasks off-the-shelf.
189
+
190
+ ### Planning Tool
191
+
192
+ `deepagents` comes with a built-in planning tool. This planning tool is very simple and is based on ClaudeCode's TodoWrite tool.
193
+ This tool doesn't actually do anything - it is just a way for the agent to come up with a plan, and then have that in the context to help keep it on track.
194
+
195
+ ### File System Tools
196
+
197
+ `deepagents` comes with four built-in file system tools: `ls`, `edit_file`, `read_file`, `write_file`.
198
+ These do not actually use a file system - rather, they mock out a file system using the agent's state object.
199
+
200
+ Right now the "file system" will only be one level deep (no sub directories).
201
+
202
+ These files can be passed in (and also retrieved) by using the `files` key in the agent's state.
203
+
204
+ ```ruby
205
+ agent = DeepAgents.create_deep_agent(...)
206
+
207
+ result = agent.invoke({
208
+ messages: [...],
209
+ # Pass in files to the agent using this key
210
+ # files: {"foo.txt" => "foo", ...}
211
+ })
212
+
213
+ # Access any files afterwards like this
214
+ result.files
215
+ ```
216
+
217
+ ### Sub Agents
218
+
219
+ `deepagents` comes with the built-in ability to call sub agents.
220
+ It has access to a `general-purpose` subagent at all times - this is a subagent with the same instructions as the main agent and all the tools that it has access to.
221
+ You can also specify custom sub agents with their own instructions and tools.
222
+
223
+ Sub agents are useful for "context quarantine" (to help not pollute the overall context of the main agent) as well as custom instructions.
224
+
225
+ ## Development
226
+
227
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
228
+
229
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
230
+
231
+ ## Contributing
232
+
233
+ Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/cdaviis/deepagents/blob/main/CODE_OF_CONDUCT.md).
234
+
235
+ ## License
236
+
237
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ desc "Run tests with coverage"
9
+ task :coverage do
10
+ ENV['COVERAGE'] = 'true'
11
+ Rake::Task["spec"].execute
12
+ end
13
+
14
+ desc "Run the example research agent"
15
+ task :example do
16
+ ruby "examples/research_agent.rb"
17
+ end
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "deepagents"
5
+ require "langchainrb"
6
+ require "langgraph_rb"
7
+
8
+ # Example showing integration between DeepAgents, langchainrb, and langgraph_rb
9
+
10
+ # 1. Create a langchainrb tool
11
+ search_tool = Langchain::Tool.new(
12
+ name: "internet_search",
13
+ description: "Search the internet for information",
14
+ function: ->(query:) {
15
+ puts "Searching for: #{query}"
16
+ "Here are some results for '#{query}': [Example search results]"
17
+ }
18
+ )
19
+
20
+ # 2. Create a DeepAgents tool
21
+ calculator_tool = DeepAgents::Tool.new(
22
+ "calculator",
23
+ "Perform mathematical calculations"
24
+ ) do |expression|
25
+ begin
26
+ eval(expression).to_s
27
+ rescue => e
28
+ "Error: #{e.message}"
29
+ end
30
+ end
31
+
32
+ # 3. Create a Claude model using DeepAgents
33
+ claude_model = DeepAgents.claude_model(model: "claude-3-sonnet-20240229")
34
+
35
+ # 4. Create a research sub-agent
36
+ research_agent = DeepAgents::SubAgent.new(
37
+ name: "research-agent",
38
+ description: "Used for in-depth research on specific topics",
39
+ prompt: "You are a specialized research agent. Your task is to provide detailed information on specific topics."
40
+ )
41
+
42
+ # 5. Create a deep agent with both tools and the sub-agent
43
+ agent = DeepAgents.create_deep_agent(
44
+ [calculator_tool],
45
+ "You are a helpful assistant that can perform calculations and search for information.",
46
+ model: claude_model,
47
+ subagents: [research_agent]
48
+ )
49
+
50
+ # 6. Run the agent with a user query
51
+ if ARGV.length > 0
52
+ result = agent.run(ARGV.join(" "))
53
+ puts "Agent response:"
54
+ puts result
55
+ else
56
+ puts "Please provide a query as a command-line argument."
57
+ puts "Example: ruby langchain_integration.rb 'What is 42 * 73?'"
58
+ end
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Add the lib directory to the load path
5
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
6
+
7
+ require 'deepagents'
8
+
9
+ # Load environment variables from .env file if present
10
+ begin
11
+ require 'dotenv'
12
+ Dotenv.load
13
+ rescue LoadError
14
+ # dotenv gem not installed, skipping
15
+ puts "Note: dotenv gem not installed. Using environment variables directly."
16
+ end
17
+
18
+ # Define a search tool (in a real implementation, this would use a search API)
19
+ class MockInternetSearch
20
+ def initialize
21
+ # Mock search results
22
+ @results = {
23
+ "ruby gem" => "Ruby gems are packages of code that can be downloaded and used in Ruby projects. They are distributed via RubyGems.org.",
24
+ "anthropic claude" => "Claude is a family of AI assistants created by Anthropic. Claude excels at thoughtful dialogue and complex reasoning.",
25
+ "deep agent" => "Deep agents are AI systems that can perform complex tasks by breaking them down into smaller steps and using tools to accomplish goals."
26
+ }
27
+ end
28
+
29
+ def search(query)
30
+ # Find the most relevant result
31
+ best_match = @results.keys.max_by do |key|
32
+ # Simple relevance scoring based on word overlap
33
+ (query.downcase.split & key.downcase.split).size
34
+ end
35
+
36
+ # Return the result or a default message
37
+ @results[best_match] || "No results found for '#{query}'."
38
+ end
39
+ end
40
+
41
+ # Create a search tool using the mock internet search
42
+ search_engine = MockInternetSearch.new
43
+ search_tool = DeepAgents::Tool.new(
44
+ "search",
45
+ "Search the internet for information"
46
+ ) do |query, state:, tool_call_id:, **kwargs|
47
+ begin
48
+ raise ArgumentError, "Query cannot be empty" if query.nil? || query.strip.empty?
49
+ result = search_engine.search(query)
50
+ DeepAgents::ToolMessage.new(
51
+ "Search results for '#{query}':\n#{result}",
52
+ tool_call_id: tool_call_id
53
+ )
54
+ rescue => e
55
+ DeepAgents::ToolMessage.new(
56
+ "Error searching for '#{query}': #{e.message}",
57
+ tool_call_id: tool_call_id
58
+ )
59
+ end
60
+ end
61
+
62
+ # Define the agent's instructions
63
+ instructions = <<~INSTRUCTIONS
64
+ You are a research assistant that helps users find information and write reports.
65
+
66
+ When given a research topic:
67
+ 1. Break down the topic into key questions to investigate
68
+ 2. Use the search tool to find information about each question
69
+ 3. Synthesize the information into a coherent report
70
+ 4. Use the write_file tool to save your report
71
+
72
+ Be thorough but concise in your research.
73
+ Always cite your sources.
74
+ INSTRUCTIONS
75
+
76
+ # Create a research sub-agent for more focused research
77
+ sub_research_prompt = <<~SUB_PROMPT
78
+ You are a specialized research assistant. Your job is to dive deep into specific topics and extract detailed information.
79
+
80
+ When given a research task:
81
+ 1. Break it down into specific questions
82
+ 2. Search for information on each question
83
+ 3. Organize the information in a structured way
84
+ 4. Return a detailed analysis
85
+ SUB_PROMPT
86
+
87
+ research_sub_agent = DeepAgents::SubAgent.new(
88
+ name: "research-agent",
89
+ description: "Used to research more in depth questions",
90
+ prompt: sub_research_prompt
91
+ )
92
+
93
+ # Create the agent with proper error handling
94
+ begin
95
+ puts "Creating research agent..."
96
+
97
+ # Try to use the real API if keys are available
98
+ if ENV['ANTHROPIC_API_KEY']
99
+ puts "Using Anthropic Claude API"
100
+ model = DeepAgents.claude_model
101
+ elsif ENV['OPENAI_API_KEY']
102
+ puts "Using OpenAI API"
103
+ model = DeepAgents.openai_model
104
+ else
105
+ # Fall back to the mock model
106
+ puts "Warning: No API keys found. Using mock model."
107
+ model = DeepAgents::Models::MockModel.new({
108
+ "ruby gems" => <<~RESPONSE
109
+ I'll research Ruby gems for you.
110
+
111
+ I'll break this down into steps:
112
+
113
+ ```tool_call
114
+ {"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "pending"}, {"content": "Synthesize findings into a report", "status": "pending"}, {"content": "Save report to a file", "status": "pending"}]}}
115
+ ```
116
+
117
+ Let me search for information about Ruby gems.
118
+
119
+ ```tool_call
120
+ {"name": "search", "arguments": {"query": "ruby gem"}}
121
+ ```
122
+
123
+ Now I'll write a report based on this information.
124
+
125
+ ```tool_call
126
+ {"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "completed"}, {"content": "Synthesize findings into a report", "status": "in_progress"}, {"content": "Save report to a file", "status": "pending"}]}}
127
+ ```
128
+
129
+ ```tool_call
130
+ {"name": "write_file", "arguments": {"file_path": "/ruby_gems_report.md", "content": "# Ruby Gems Report\n\nRuby gems are packages of code that can be downloaded and used in Ruby projects. They are distributed via RubyGems.org.\n\n## Key Points\n\n- Ruby gems are reusable code packages\n- They are distributed through RubyGems.org\n- They make it easy to share and use code across projects\n\n## Sources\n\n- Internet search results"}}
131
+ ```
132
+
133
+ ```tool_call
134
+ {"name": "write_todos", "arguments": {"todos": [{"content": "Search for Ruby gem information", "status": "completed"}, {"content": "Synthesize findings into a report", "status": "completed"}, {"content": "Save report to a file", "status": "completed"}]}}
135
+ ```
136
+
137
+ I've completed the research on Ruby gems and saved a report to `/ruby_gems_report.md`. The report includes information about what Ruby gems are, how they're distributed, and their purpose in the Ruby ecosystem.
138
+ RESPONSE
139
+ })
140
+ end
141
+
142
+ agent = DeepAgents.create_deep_agent(
143
+ [search_tool],
144
+ instructions,
145
+ model: model,
146
+ subagents: [research_sub_agent]
147
+ )
148
+
149
+ # Run the agent with a research topic
150
+ puts "\nšŸ¤– Running research agent..."
151
+ result = agent.invoke(messages: [{role: "user", content: "Please research Ruby gems and write a report."}])
152
+
153
+ # Print the final state
154
+ puts "\nšŸ“Š Final State:\n"
155
+ puts "Messages:"
156
+ result.messages.each do |message|
157
+ puts "#{message[:role]}: #{message[:content].split('\n').first}..."
158
+ end
159
+
160
+ puts "\nšŸ“ Files:"
161
+ result.files.each do |path, content|
162
+ puts "#{path}:\n#{content}\n"
163
+ end
164
+
165
+ puts "\nšŸ“‹ Todos:"
166
+ result.todos.each do |todo|
167
+ puts "- [#{todo.status == 'completed' ? 'x' : ' '}] #{todo.content}"
168
+ end
169
+ rescue DeepAgents::CredentialsError => e
170
+ puts "\nāŒ API Credential Error: #{e.message}"
171
+ puts "Please set the appropriate API key in your environment variables."
172
+ rescue DeepAgents::ModelError => e
173
+ puts "\nāŒ Model Error: #{e.message}"
174
+ puts "There was an issue with the LLM model."
175
+ rescue DeepAgents::Error => e
176
+ puts "\nāŒ DeepAgents Error: #{e.message}"
177
+ rescue => e
178
+ puts "\nāŒ Unexpected Error: #{e.message}"
179
+ puts e.backtrace.join("\n")
180
+ end
@@ -0,0 +1,80 @@
1
+ module DeepAgentsRb
2
+ # Base error class for all DeepAgentsRb errors
3
+ class Error < StandardError; end
4
+
5
+ # Error raised when there's an issue with the state
6
+ class StateError < Error; end
7
+
8
+ # Error raised when there's an issue with a tool
9
+ class ToolError < Error
10
+ attr_reader :tool_name
11
+
12
+ def initialize(tool_name, message)
13
+ @tool_name = tool_name
14
+ super("Error in tool '#{tool_name}': #{message}")
15
+ end
16
+ end
17
+
18
+ # Error raised when a tool is not found
19
+ class ToolNotFoundError < ToolError
20
+ def initialize(tool_name)
21
+ super(tool_name, "Tool not found")
22
+ end
23
+ end
24
+
25
+ # Error raised when there's an issue with a file operation
26
+ class FileError < Error; end
27
+
28
+ # Error raised when a file is not found
29
+ class FileNotFoundError < FileError
30
+ attr_reader :path
31
+
32
+ def initialize(path)
33
+ @path = path
34
+ super("File not found: #{path}")
35
+ end
36
+ end
37
+
38
+ # Error raised when there's an issue with a model
39
+ class ModelError < Error; end
40
+
41
+ # Error raised when there's an issue with API credentials
42
+ class CredentialsError < ModelError
43
+ def initialize(model_name)
44
+ super("Missing API credentials for #{model_name}")
45
+ end
46
+ end
47
+
48
+ # Error raised when there's an issue with a sub-agent
49
+ class SubAgentError < Error; end
50
+
51
+ # Error raised when a sub-agent is not found
52
+ class SubAgentNotFoundError < SubAgentError
53
+ attr_reader :agent_name
54
+
55
+ def initialize(agent_name)
56
+ @agent_name = agent_name
57
+ super("Sub-agent not found: #{agent_name}")
58
+ end
59
+ end
60
+
61
+ # Error raised when there's an issue with parsing a tool call
62
+ class ToolCallParseError < Error
63
+ attr_reader :raw_content
64
+
65
+ def initialize(message, raw_content = nil)
66
+ @raw_content = raw_content
67
+ super(message)
68
+ end
69
+ end
70
+
71
+ # Error raised when there's an issue with the LLM response
72
+ class LLMResponseError < Error; end
73
+
74
+ # Error raised when the maximum number of iterations is reached
75
+ class MaxIterationsError < Error
76
+ def initialize(iterations)
77
+ super("Maximum number of iterations (#{iterations}) reached")
78
+ end
79
+ end
80
+ end