agent_c 2.71828

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +10 -0
  3. data/.ruby-version +1 -0
  4. data/CLAUDE.md +21 -0
  5. data/README.md +360 -0
  6. data/Rakefile +16 -0
  7. data/TODO.md +12 -0
  8. data/agent_c.gemspec +38 -0
  9. data/docs/chat-methods.md +157 -0
  10. data/docs/cost-reporting.md +86 -0
  11. data/docs/pipeline-tips-and-tricks.md +71 -0
  12. data/docs/session-configuration.md +274 -0
  13. data/docs/testing.md +747 -0
  14. data/docs/tools.md +103 -0
  15. data/docs/versioned-store.md +840 -0
  16. data/lib/agent_c/agent/chat.rb +211 -0
  17. data/lib/agent_c/agent/chat_response.rb +32 -0
  18. data/lib/agent_c/agent/chats/anthropic_bedrock.rb +48 -0
  19. data/lib/agent_c/batch.rb +102 -0
  20. data/lib/agent_c/configs/repo.rb +90 -0
  21. data/lib/agent_c/context.rb +56 -0
  22. data/lib/agent_c/costs/data.rb +39 -0
  23. data/lib/agent_c/costs/report.rb +219 -0
  24. data/lib/agent_c/db/store.rb +162 -0
  25. data/lib/agent_c/errors.rb +19 -0
  26. data/lib/agent_c/pipeline.rb +188 -0
  27. data/lib/agent_c/processor.rb +98 -0
  28. data/lib/agent_c/prompts.yml +53 -0
  29. data/lib/agent_c/schema.rb +85 -0
  30. data/lib/agent_c/session.rb +207 -0
  31. data/lib/agent_c/store.rb +72 -0
  32. data/lib/agent_c/test_helpers.rb +173 -0
  33. data/lib/agent_c/tools/dir_glob.rb +46 -0
  34. data/lib/agent_c/tools/edit_file.rb +112 -0
  35. data/lib/agent_c/tools/file_metadata.rb +43 -0
  36. data/lib/agent_c/tools/grep.rb +119 -0
  37. data/lib/agent_c/tools/paths.rb +36 -0
  38. data/lib/agent_c/tools/read_file.rb +94 -0
  39. data/lib/agent_c/tools/run_rails_test.rb +87 -0
  40. data/lib/agent_c/tools.rb +60 -0
  41. data/lib/agent_c/utils/git.rb +75 -0
  42. data/lib/agent_c/utils/shell.rb +58 -0
  43. data/lib/agent_c/version.rb +5 -0
  44. data/lib/agent_c.rb +32 -0
  45. data/lib/versioned_store/base.rb +314 -0
  46. data/lib/versioned_store/config.rb +26 -0
  47. data/lib/versioned_store/stores/schema.rb +127 -0
  48. data/lib/versioned_store/version.rb +5 -0
  49. data/lib/versioned_store.rb +5 -0
  50. data/template/Gemfile +9 -0
  51. data/template/Gemfile.lock +152 -0
  52. data/template/README.md +61 -0
  53. data/template/Rakefile +50 -0
  54. data/template/bin/rake +27 -0
  55. data/template/lib/autoload.rb +10 -0
  56. data/template/lib/config.rb +59 -0
  57. data/template/lib/pipeline.rb +19 -0
  58. data/template/lib/prompts.yml +57 -0
  59. data/template/lib/store.rb +17 -0
  60. data/template/test/pipeline_test.rb +221 -0
  61. data/template/test/test_helper.rb +18 -0
  62. metadata +191 -0
@@ -0,0 +1,86 @@
1
+ # Cost Reporting
2
+
3
+ Note: You probably want to make a `Batch`. See the [main README](../README.md)
4
+
5
+ ## Overview
6
+
7
+ AgentC automatically tracks all LLM interactions in a SQLite database, allowing you to generate detailed cost reports.
8
+
9
+ ## Generating Reports
10
+
11
+ Track your LLM usage and costs:
12
+
13
+ ```ruby
14
+ # See the [configuration](./session-configuration.md) for session args
15
+ session = AgentC::Session.new(...)
16
+
17
+ # Generate a cost report for all projects
18
+ AgentC::Costs::Report.call(agent_store: session.agent_store)
19
+
20
+ # For a specific project
21
+ AgentC::Costs::Report.call(
22
+ agent_store: session.agent_store,
23
+ project: 'my_project'
24
+ )
25
+
26
+ # For a specific run
27
+ AgentC::Costs::Report.call(
28
+ agent_store: session.agent_store,
29
+ project: 'my_project',
30
+ run_id: 1234567890
31
+ )
32
+
33
+ # Or use the session's cost method for current project/run
34
+ puts "Project cost: $#{session.cost.project}"
35
+ puts "Run cost: $#{session.cost.run}"
36
+ ```
37
+
38
+ ## What's Included
39
+
40
+ The report includes:
41
+ - Input/output token counts
42
+ - Cache hit rates
43
+ - Per-interaction costs
44
+ - Total spending
45
+ - Pricing for both normal and long-context models
46
+
47
+ ## Report Format
48
+
49
+ The cost report displays information organized by project and run:
50
+
51
+ ```
52
+ Project: my_project
53
+ Run ID: 1234567890
54
+
55
+ Interaction 1 (2024-01-15 10:30:00)
56
+ Input tokens: 1,250
57
+ Output tokens: 450
58
+ Cached tokens: 800
59
+ Cost: $0.0234
60
+
61
+ Interaction 2 (2024-01-15 10:35:00)
62
+ Input tokens: 2,100
63
+ Output tokens: 680
64
+ Cached tokens: 1,500
65
+ Cost: $0.0356
66
+
67
+ Total Cost: $0.0590
68
+ ```
69
+
70
+ ## Cost Optimization Tips
71
+
72
+ 1. **Use cached prompts** - System prompts that rarely change can be cached, significantly reducing costs
73
+ 2. **Choose appropriate models** - Use lighter models (Haiku) for simple tasks, heavier models (Sonnet/Opus) for complex ones
74
+ 3. **Batch operations** - Group similar tasks together to maximize cache hits
75
+ 4. **Monitor costs regularly** - Run cost reports after each pipeline run to identify expensive operations
76
+
77
+ ## Database Schema
78
+
79
+ All queries are persisted in SQLite with:
80
+ - Full conversation history
81
+ - Token usage metrics
82
+ - Timestamps and metadata
83
+ - Tool calls and responses
84
+ - Project and run ID associations
85
+
86
+ This allows for detailed analysis and debugging of AI interactions over time.
@@ -0,0 +1,71 @@
1
+ # Pipeline Tips and Tricks
2
+
3
+ This document contains useful patterns and techniques for working with AgentC pipelines.
4
+
5
+ ## Custom I18n Attributes
6
+
7
+ By default, when using i18n interpolation in your prompts, AgentC will use `record.attributes` to provide values for interpolation. However, you can customize this behavior by implementing an `i18n_attributes` method on your record.
8
+
9
+ ### Use Case
10
+
11
+ This is useful when:
12
+ - You want to interpolate values that aren't stored as attributes on the record
13
+ - You need to compute or format values specifically for prompts
14
+ - You want to limit which attributes are exposed to i18n interpolation
15
+ - You need to provide different data than what's in the database
16
+
17
+ ### Example
18
+
19
+ ```ruby
20
+ class MyStore < VersionedStore::Base
21
+ include AgentC::Store
22
+
23
+ record(:my_record) do
24
+ schema do |t|
25
+ t.string(:file_path)
26
+ t.text(:file_contents)
27
+ end
28
+
29
+ # Override the default i18n_attributes
30
+ def i18n_attributes
31
+ {
32
+ file_name: File.basename(file_path),
33
+ file_extension: File.extname(file_path),
34
+ lines_count: file_contents&.lines&.count || 0
35
+ }
36
+ end
37
+ end
38
+ end
39
+ ```
40
+
41
+ Now in your prompts, you can interpolate these computed values:
42
+
43
+ ```yaml
44
+ en:
45
+ analyze_file:
46
+ prompt: "Analyze %{file_name} which has %{lines_count} lines and is a %{file_extension} file"
47
+ ```
48
+
49
+ ### How It Works
50
+
51
+ When you use `agent_step` with i18n (either via `prompt_key` or the shorthand syntax), AgentC checks if your record responds to `i18n_attributes`. If it does, that method's return value is used for interpolation. Otherwise, it falls back to `record.attributes`.
52
+
53
+ This works with both explicit prompt keys:
54
+
55
+ ```ruby
56
+ agent_step(
57
+ :my_step,
58
+ prompt_key: "my_step.prompt",
59
+ cached_prompt_keys: ["my_step.cached"]
60
+ )
61
+ ```
62
+
63
+ And with the shorthand syntax:
64
+
65
+ ```ruby
66
+ agent_step(:my_step)
67
+ ```
68
+
69
+ ### Return Value
70
+
71
+ The `i18n_attributes` method should return a Hash with symbol or string keys. These keys will be used for interpolation in your i18n strings.
@@ -0,0 +1,274 @@
1
+ # Session Configuration
2
+
3
+ Note: You probably want to make a `Batch`. See the [main README](../README.md)
4
+
5
+ This describes how to create a Session object if you just want to chat with Claude through AgentC.
6
+
7
+ ## Overview
8
+
9
+ AgentC uses a session-based configuration approach that provides isolated configuration with no global state. Each session maintains its own configuration and RubyLLM context, making it ideal for:
10
+ - Testing (no configuration pollution between tests)
11
+ - Multiple concurrent configurations in the same process
12
+ - Better dependency injection and code organization
13
+
14
+ ## Basic Session Configuration
15
+
16
+ ```ruby
17
+ session = Session.new(
18
+ # all chats with claude are saved to a sqlite db.
19
+ # this is separate than your Store's db because
20
+ # why throw anything away. Can be useful for
21
+ # debugging why Claude did what it did
22
+ agent_db_path: "/path/to/your/claude/db.sqlite",
23
+ logger: Logger.new("/dev/null"), # probably use the same logger for everything...
24
+ i18n_path: "/path/to/your/prompts.yml",
25
+
26
+ # as you debug your pipeline, you'll probably run it
27
+ # many times. We tag all Claude chat records with a
28
+ # project so you can track costs.
29
+ project: "SomeProject",
30
+
31
+ # only available for Bedrock...
32
+ ruby_llm: {
33
+ bedrock_api_key: ENV.fetch("AWS_ACCESS_KEY_ID"),
34
+ bedrock_secret_key: ENV.fetch("AWS_SECRET_ACCESS_KEY"),
35
+ bedrock_session_token: ENV.fetch("AWS_SESSION_TOKEN"),
36
+ bedrock_region: ENV.fetch("AWS_REGION", "us-west-2"),
37
+ default_model: ENV.fetch("LLM_MODEL", "us.anthropic.claude-sonnet-4-5-20250929-v1:0")
38
+ }
39
+ )
40
+
41
+ # Create chats from the session
42
+ chat = session.chat
43
+ response = chat.ask("What is Ruby?")
44
+
45
+ # Or use the prompt method for one-off requests
46
+ result = session.prompt(
47
+ prompt: "What is Ruby?",
48
+ schema: -> { string(:answer) }
49
+ )
50
+ ```
51
+
52
+ ## Configuration Options
53
+
54
+ All session parameters are optional except where noted. If database-related features are needed, `agent_db_path` and `project` become required.
55
+
56
+ ### agent_db_path
57
+
58
+ Path to the SQLite database file where all LLM interactions will be stored.
59
+
60
+ ```ruby
61
+ session = Session.new(
62
+ agent_db_path: 'tmp/db/agent.sqlite3'
63
+ )
64
+ ```
65
+
66
+ The database is automatically created if it doesn't exist. All conversations, token usage, and costs are persisted here. Required if using database features (cost tracking, persistence).
67
+
68
+ ### project (required with agent_db_path)
69
+
70
+ A string identifier for your project. Used to organize and filter cost reports.
71
+
72
+ ```ruby
73
+ session = Session.new(
74
+ agent_db_path: 'tmp/db/agent.sqlite3',
75
+ project: 'my_project'
76
+ )
77
+ ```
78
+
79
+ Required when `agent_db_path` is provided.
80
+
81
+ ### workspace_dir
82
+
83
+ The working directory for file-based tools. Tools like `read_file`, `edit_file`, and `dir_glob` operate relative to this directory.
84
+
85
+ ```ruby
86
+ session = Session.new(
87
+ workspace_dir: Dir.pwd
88
+ )
89
+ ```
90
+
91
+ Defaults to `Dir.pwd` if not specified.
92
+
93
+ ### run_id
94
+
95
+ An optional identifier to group related queries. Useful for tracking multiple pipeline runs or sessions.
96
+
97
+ ```ruby
98
+ session = Session.new(
99
+ run_id: Time.now.to_i
100
+ )
101
+ ```
102
+
103
+ Auto-generated if not provided when `agent_db_path` is configured.
104
+
105
+ ### logger
106
+
107
+ Custom logger for debugging:
108
+
109
+ ```ruby
110
+ session = Session.new(
111
+ logger: Logger.new($stdout)
112
+ )
113
+ ```
114
+
115
+ ### i18n_path
116
+
117
+ Path to custom I18n translations file for prompts:
118
+
119
+ ```ruby
120
+ session = Session.new(
121
+ i18n_path: 'config/locales/prompts.yml'
122
+ )
123
+ ```
124
+
125
+ This is particularly useful when using `agent_step` in Pipeline definitions, where prompts and schemas can be loaded from i18n YAML files.
126
+
127
+ ### max_spend_project
128
+
129
+ Maximum project cost threshold in dollars. Raises `AgentC::Errors::AbortCostExceeded` when exceeded:
130
+
131
+ ```ruby
132
+ session = Session.new(
133
+ agent_db_path: 'tmp/db/agent.sqlite3',
134
+ project: 'my_project',
135
+ max_spend_project: 10.0 # Abort if project costs exceed $10
136
+ )
137
+ ```
138
+
139
+ ### max_spend_run
140
+
141
+ Maximum run cost threshold in dollars. Raises `AgentC::Errors::AbortCostExceeded` when exceeded:
142
+
143
+ ```ruby
144
+ session = Session.new(
145
+ agent_db_path: 'tmp/db/agent.sqlite3',
146
+ project: 'my_project',
147
+ run_id: 'run_123',
148
+ max_spend_run: 5.0 # Abort if this run costs exceed $5
149
+ )
150
+ ```
151
+
152
+ ### ruby_llm
153
+
154
+ RubyLLM configuration hash. Each session has its own isolated RubyLLM context:
155
+
156
+ ```ruby
157
+ session = Session.new(
158
+ ruby_llm: {
159
+ bedrock_api_key: ENV['AWS_ACCESS_KEY_ID'],
160
+ bedrock_secret_key: ENV['AWS_SECRET_ACCESS_KEY'],
161
+ bedrock_region: 'us-west-2',
162
+ default_model: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0'
163
+ }
164
+ )
165
+ ```
166
+
167
+ Available RubyLLM options:
168
+ - `bedrock_api_key` - AWS access key
169
+ - `bedrock_secret_key` - AWS secret key
170
+ - `bedrock_session_token` - Optional AWS session token
171
+ - `bedrock_region` - AWS region (e.g., 'us-west-2')
172
+ - `default_model` - Model ID to use
173
+
174
+ ### extra_tools
175
+
176
+ A hash mapping tool names (symbols) to custom tool classes or instances. This allows you to add custom tools beyond the built-in AgentC tools.
177
+
178
+ ```ruby
179
+ session = Session.new(
180
+ extra_tools: {
181
+ my_tool: MyCustomTool, # Class will be initialized
182
+ another_tool: MyOtherTool.new # Instance used directly
183
+ }
184
+ )
185
+ ```
186
+
187
+ When a tool class is provided, AgentC will instantiate it with `workspace_dir:` and `env:` keyword arguments:
188
+
189
+ ```ruby
190
+ # AgentC will call:
191
+ # MyCustomTool.new(workspace_dir: session.workspace_dir, env: session.env)
192
+ ```
193
+
194
+ When a tool instance is provided, it will be used as-is without initialization.
195
+
196
+ Custom tools must implement the tool interface expected by RubyLLM. See the [Custom Tools documentation](custom-tools.md) for details on implementing custom tools.
197
+
198
+ ## Complete Example
199
+
200
+ ```ruby
201
+ require 'agent_c'
202
+ require 'logger'
203
+
204
+ # Create a fully configured session
205
+ session = Session.new(
206
+ # Database and project
207
+ agent_db_path: 'tmp/db/agent.sqlite3',
208
+ project: 'document_processor',
209
+ run_id: Time.now.to_i,
210
+
211
+ # Working directory and i18n
212
+ workspace_dir: Dir.pwd,
213
+ i18n_path: 'config/locales/agent_prompts.yml',
214
+
215
+ # Logging
216
+ logger: Logger.new($stdout, level: Logger::INFO),
217
+
218
+ # Cost controls
219
+ max_spend_project: 100.0,
220
+ max_spend_run: 10.0,
221
+
222
+ # RubyLLM configuration
223
+ ruby_llm: {
224
+ bedrock_api_key: ENV['AWS_ACCESS_KEY_ID'],
225
+ bedrock_secret_key: ENV['AWS_SECRET_ACCESS_KEY'],
226
+ bedrock_region: 'us-west-2',
227
+ default_model: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0'
228
+ },
229
+
230
+ # Custom tools (optional)
231
+ extra_tools: {
232
+ custom_search: MySearchTool,
233
+ api_client: MyApiClient.new(api_key: ENV['API_KEY'])
234
+ }
235
+ )
236
+
237
+ # Create chats from the session
238
+ chat = session.chat(tools: [:read_file, :edit_file])
239
+ response = chat.ask("What is Ruby?")
240
+
241
+ # Or use the prompt method for one-off requests
242
+ result = session.prompt(
243
+ prompt: "Summarize this file",
244
+ schema: -> { string(:summary) },
245
+ tools: [:read_file]
246
+ )
247
+ ```
248
+
249
+ ## Environment-Specific Configuration
250
+
251
+ ```ruby
252
+ session = Session.new(
253
+ agent_db_path: ENV['RACK_ENV'] == 'production' ?
254
+ '/var/db/agent_production.sqlite3' :
255
+ 'tmp/db/agent_development.sqlite3',
256
+ project: ENV['PROJECT_NAME'] || 'default_project',
257
+ workspace_dir: Dir.pwd,
258
+ logger: Logger.new($stdout, level: ENV['LOG_LEVEL'] || 'INFO'),
259
+ ruby_llm: {
260
+ bedrock_region: ENV['AWS_REGION'] || 'us-west-2',
261
+ default_model: ENV['LLM_MODEL'] || 'us.anthropic.claude-sonnet-4-5-20250929-v1:0'
262
+ }
263
+ )
264
+ ```
265
+
266
+ ## AWS Credentials
267
+
268
+ AgentC uses AWS Bedrock for LLM access. Ensure your AWS credentials are configured via:
269
+
270
+ - Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`)
271
+ - AWS credentials file (`~/.aws/credentials`)
272
+ - IAM role (when running on EC2/ECS)
273
+
274
+ No additional configuration is needed in AgentC for AWS credentials.