a2a-ruby 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/.rspec +3 -0
- data/.rubocop.yml +137 -0
- data/.simplecov +46 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +33 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +165 -0
- data/Gemfile +43 -0
- data/Guardfile +34 -0
- data/LICENSE.txt +21 -0
- data/PUBLISHING_CHECKLIST.md +214 -0
- data/README.md +171 -0
- data/Rakefile +165 -0
- data/docs/agent_execution.md +309 -0
- data/docs/api_reference.md +792 -0
- data/docs/configuration.md +780 -0
- data/docs/events.md +475 -0
- data/docs/getting_started.md +668 -0
- data/docs/integration.md +262 -0
- data/docs/server_apps.md +621 -0
- data/docs/troubleshooting.md +765 -0
- data/lib/a2a/client/api_methods.rb +263 -0
- data/lib/a2a/client/auth/api_key.rb +161 -0
- data/lib/a2a/client/auth/interceptor.rb +288 -0
- data/lib/a2a/client/auth/jwt.rb +189 -0
- data/lib/a2a/client/auth/oauth2.rb +146 -0
- data/lib/a2a/client/auth.rb +137 -0
- data/lib/a2a/client/base.rb +316 -0
- data/lib/a2a/client/config.rb +210 -0
- data/lib/a2a/client/connection_pool.rb +233 -0
- data/lib/a2a/client/http_client.rb +524 -0
- data/lib/a2a/client/json_rpc_handler.rb +136 -0
- data/lib/a2a/client/middleware/circuit_breaker_interceptor.rb +245 -0
- data/lib/a2a/client/middleware/logging_interceptor.rb +371 -0
- data/lib/a2a/client/middleware/rate_limit_interceptor.rb +142 -0
- data/lib/a2a/client/middleware/retry_interceptor.rb +161 -0
- data/lib/a2a/client/middleware.rb +116 -0
- data/lib/a2a/client/performance_tracker.rb +60 -0
- data/lib/a2a/configuration/defaults.rb +34 -0
- data/lib/a2a/configuration/environment_loader.rb +76 -0
- data/lib/a2a/configuration/file_loader.rb +115 -0
- data/lib/a2a/configuration/inheritance.rb +101 -0
- data/lib/a2a/configuration/validator.rb +180 -0
- data/lib/a2a/configuration.rb +201 -0
- data/lib/a2a/errors.rb +291 -0
- data/lib/a2a/modules.rb +50 -0
- data/lib/a2a/monitoring/alerting.rb +490 -0
- data/lib/a2a/monitoring/distributed_tracing.rb +398 -0
- data/lib/a2a/monitoring/health_endpoints.rb +204 -0
- data/lib/a2a/monitoring/metrics_collector.rb +438 -0
- data/lib/a2a/monitoring.rb +463 -0
- data/lib/a2a/plugin.rb +358 -0
- data/lib/a2a/plugin_manager.rb +159 -0
- data/lib/a2a/plugins/example_auth.rb +81 -0
- data/lib/a2a/plugins/example_middleware.rb +118 -0
- data/lib/a2a/plugins/example_transport.rb +76 -0
- data/lib/a2a/protocol/agent_card.rb +8 -0
- data/lib/a2a/protocol/agent_card_server.rb +584 -0
- data/lib/a2a/protocol/capability.rb +496 -0
- data/lib/a2a/protocol/json_rpc.rb +254 -0
- data/lib/a2a/protocol/message.rb +8 -0
- data/lib/a2a/protocol/task.rb +8 -0
- data/lib/a2a/rails/a2a_controller.rb +258 -0
- data/lib/a2a/rails/controller_helpers.rb +499 -0
- data/lib/a2a/rails/engine.rb +167 -0
- data/lib/a2a/rails/generators/agent_generator.rb +311 -0
- data/lib/a2a/rails/generators/install_generator.rb +209 -0
- data/lib/a2a/rails/generators/migration_generator.rb +232 -0
- data/lib/a2a/rails/generators/templates/add_a2a_indexes.rb +57 -0
- data/lib/a2a/rails/generators/templates/agent_controller.rb +122 -0
- data/lib/a2a/rails/generators/templates/agent_controller_spec.rb +160 -0
- data/lib/a2a/rails/generators/templates/agent_readme.md +200 -0
- data/lib/a2a/rails/generators/templates/create_a2a_push_notification_configs.rb +68 -0
- data/lib/a2a/rails/generators/templates/create_a2a_tasks.rb +83 -0
- data/lib/a2a/rails/generators/templates/example_agent_controller.rb +228 -0
- data/lib/a2a/rails/generators/templates/initializer.rb +108 -0
- data/lib/a2a/rails/generators/templates/push_notification_config_model.rb +228 -0
- data/lib/a2a/rails/generators/templates/task_model.rb +200 -0
- data/lib/a2a/rails/tasks/a2a.rake +228 -0
- data/lib/a2a/server/a2a_methods.rb +520 -0
- data/lib/a2a/server/agent.rb +537 -0
- data/lib/a2a/server/agent_execution/agent_executor.rb +279 -0
- data/lib/a2a/server/agent_execution/request_context.rb +219 -0
- data/lib/a2a/server/apps/rack_app.rb +311 -0
- data/lib/a2a/server/apps/sinatra_app.rb +261 -0
- data/lib/a2a/server/default_request_handler.rb +350 -0
- data/lib/a2a/server/events/event_consumer.rb +116 -0
- data/lib/a2a/server/events/event_queue.rb +226 -0
- data/lib/a2a/server/example_agent.rb +248 -0
- data/lib/a2a/server/handler.rb +281 -0
- data/lib/a2a/server/middleware/authentication_middleware.rb +212 -0
- data/lib/a2a/server/middleware/cors_middleware.rb +171 -0
- data/lib/a2a/server/middleware/logging_middleware.rb +362 -0
- data/lib/a2a/server/middleware/rate_limit_middleware.rb +382 -0
- data/lib/a2a/server/middleware.rb +213 -0
- data/lib/a2a/server/push_notification_manager.rb +327 -0
- data/lib/a2a/server/request_handler.rb +136 -0
- data/lib/a2a/server/storage/base.rb +141 -0
- data/lib/a2a/server/storage/database.rb +266 -0
- data/lib/a2a/server/storage/memory.rb +274 -0
- data/lib/a2a/server/storage/redis.rb +320 -0
- data/lib/a2a/server/storage.rb +38 -0
- data/lib/a2a/server/task_manager.rb +534 -0
- data/lib/a2a/transport/grpc.rb +481 -0
- data/lib/a2a/transport/http.rb +415 -0
- data/lib/a2a/transport/sse.rb +499 -0
- data/lib/a2a/types/agent_card.rb +540 -0
- data/lib/a2a/types/artifact.rb +99 -0
- data/lib/a2a/types/base_model.rb +223 -0
- data/lib/a2a/types/events.rb +117 -0
- data/lib/a2a/types/message.rb +106 -0
- data/lib/a2a/types/part.rb +288 -0
- data/lib/a2a/types/push_notification.rb +139 -0
- data/lib/a2a/types/security.rb +167 -0
- data/lib/a2a/types/task.rb +154 -0
- data/lib/a2a/types.rb +88 -0
- data/lib/a2a/utils/helpers.rb +245 -0
- data/lib/a2a/utils/message_buffer.rb +278 -0
- data/lib/a2a/utils/performance.rb +247 -0
- data/lib/a2a/utils/rails_detection.rb +97 -0
- data/lib/a2a/utils/structured_logger.rb +306 -0
- data/lib/a2a/utils/time_helpers.rb +167 -0
- data/lib/a2a/utils/validation.rb +8 -0
- data/lib/a2a/version.rb +6 -0
- data/lib/a2a-rails.rb +58 -0
- data/lib/a2a.rb +198 -0
- metadata +437 -0
@@ -0,0 +1,200 @@
|
|
1
|
+
# <%= class_name.humanize %> A2A Agent
|
2
|
+
|
3
|
+
<%= agent_description %>
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
This agent provides A2A (Agent2Agent) protocol endpoints for <%= class_name.humanize.downcase %> functionality. It was generated using the A2A Ruby SDK Rails generator.
|
8
|
+
|
9
|
+
## Endpoints
|
10
|
+
|
11
|
+
### Agent Card
|
12
|
+
- **URL**: `<%= agent_card_path %>`
|
13
|
+
- **Method**: GET
|
14
|
+
- **Description**: Returns the agent card with capabilities and interface information
|
15
|
+
|
16
|
+
### JSON-RPC Interface
|
17
|
+
- **URL**: `<%= rpc_path %>`
|
18
|
+
- **Method**: POST
|
19
|
+
- **Content-Type**: application/json
|
20
|
+
- **Description**: Handles A2A JSON-RPC method calls
|
21
|
+
|
22
|
+
## Skills and Capabilities
|
23
|
+
|
24
|
+
<% if skills.any? %>
|
25
|
+
This agent provides the following skills:
|
26
|
+
|
27
|
+
<% skills.each do |skill| %>
|
28
|
+
### <%= skill.humanize %>
|
29
|
+
- **Method**: `<%= skill.underscore %>`
|
30
|
+
- **Description**: <%= skill.humanize %> functionality
|
31
|
+
- **Tags**: `<%= skill %>`, `generated`
|
32
|
+
|
33
|
+
<% end %>
|
34
|
+
<% else %>
|
35
|
+
### Default Processing
|
36
|
+
- **Method**: `process`
|
37
|
+
- **Description**: Default <%= class_name.humanize.downcase %> processing functionality
|
38
|
+
- **Tags**: `<%= class_name.underscore %>`, `default`
|
39
|
+
<% end %>
|
40
|
+
|
41
|
+
### Status
|
42
|
+
- **Method**: `status`
|
43
|
+
- **Description**: Get agent status and health information
|
44
|
+
- **Tags**: `status`, `health`
|
45
|
+
|
46
|
+
## Usage Examples
|
47
|
+
|
48
|
+
### Get Agent Card
|
49
|
+
```bash
|
50
|
+
curl -X GET <%= agent_card_path %>
|
51
|
+
```
|
52
|
+
|
53
|
+
### Call Agent Method
|
54
|
+
```bash
|
55
|
+
curl -X POST <%= rpc_path %> \
|
56
|
+
-H "Content-Type: application/json" \
|
57
|
+
-d '{
|
58
|
+
"jsonrpc": "2.0",
|
59
|
+
"method": "status",
|
60
|
+
"params": {},
|
61
|
+
"id": 1
|
62
|
+
}'
|
63
|
+
```
|
64
|
+
|
65
|
+
<% if skills.any? %>
|
66
|
+
<% skills.first(2).each do |skill| %>
|
67
|
+
### <%= skill.humanize %> Example
|
68
|
+
```bash
|
69
|
+
curl -X POST <%= rpc_path %> \
|
70
|
+
-H "Content-Type: application/json" \
|
71
|
+
-d '{
|
72
|
+
"jsonrpc": "2.0",
|
73
|
+
"method": "<%= skill.underscore %>",
|
74
|
+
"params": {
|
75
|
+
"action": "<%= skill %>"
|
76
|
+
},
|
77
|
+
"id": 1
|
78
|
+
}'
|
79
|
+
```
|
80
|
+
<% end %>
|
81
|
+
<% end %>
|
82
|
+
|
83
|
+
<% if with_authentication? %>
|
84
|
+
## Authentication
|
85
|
+
|
86
|
+
This agent requires authentication for certain methods:
|
87
|
+
|
88
|
+
<% authentication_methods.each do |method| %>
|
89
|
+
- `<%= method %>` - Requires valid authentication
|
90
|
+
<% end %>
|
91
|
+
|
92
|
+
### Authentication Example
|
93
|
+
```bash
|
94
|
+
curl -X POST <%= rpc_path %> \
|
95
|
+
-H "Content-Type: application/json" \
|
96
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
97
|
+
-d '{
|
98
|
+
"jsonrpc": "2.0",
|
99
|
+
"method": "<%= authentication_methods.first %>",
|
100
|
+
"params": {},
|
101
|
+
"id": 1
|
102
|
+
}'
|
103
|
+
```
|
104
|
+
<% end %>
|
105
|
+
|
106
|
+
## Development
|
107
|
+
|
108
|
+
### Customizing the Agent
|
109
|
+
|
110
|
+
1. **Add New Skills**: Define new skills using the `a2a_skill` DSL in the controller
|
111
|
+
2. **Implement Methods**: Add method implementations using the `a2a_method` DSL
|
112
|
+
3. **Configure Authentication**: Customize authentication requirements using `a2a_authenticate`
|
113
|
+
|
114
|
+
### Example: Adding a New Skill
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
# In <%= controller_file_path %>
|
118
|
+
|
119
|
+
a2a_skill "new_feature" do |skill|
|
120
|
+
skill.description = "Description of the new feature"
|
121
|
+
skill.tags = ["new", "feature"]
|
122
|
+
skill.examples = [
|
123
|
+
{
|
124
|
+
input: { param: "value" },
|
125
|
+
output: { result: "success" }
|
126
|
+
}
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
a2a_method "new_feature" do |params|
|
131
|
+
# Implement your logic here
|
132
|
+
{
|
133
|
+
result: "Feature implemented",
|
134
|
+
params: params,
|
135
|
+
timestamp: Time.current.iso8601
|
136
|
+
}
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
### Testing
|
141
|
+
|
142
|
+
Run the generated tests:
|
143
|
+
|
144
|
+
```bash
|
145
|
+
# RSpec
|
146
|
+
bundle exec rspec spec/controllers/<%= namespace ? "#{namespace}/" : "" %><%= file_name %>_controller_spec.rb
|
147
|
+
|
148
|
+
# Or Rails test
|
149
|
+
bundle exec rails test test/controllers/<%= namespace ? "#{namespace}/" : "" %><%= file_name %>_controller_test.rb
|
150
|
+
```
|
151
|
+
|
152
|
+
### Debugging
|
153
|
+
|
154
|
+
Use the Rails console to test your agent:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
# Start Rails console
|
158
|
+
rails console
|
159
|
+
|
160
|
+
# Test agent methods directly
|
161
|
+
controller = <%= controller_class_name %>.new
|
162
|
+
controller.request = ActionDispatch::Request.new({})
|
163
|
+
|
164
|
+
# Generate agent card
|
165
|
+
card = controller.send(:generate_agent_card)
|
166
|
+
puts JSON.pretty_generate(card.to_h)
|
167
|
+
|
168
|
+
# Test method execution
|
169
|
+
result = controller.send(:handle_a2a_request,
|
170
|
+
A2A::Protocol::JsonRpc::Request.new(
|
171
|
+
jsonrpc: "2.0",
|
172
|
+
method: "status",
|
173
|
+
params: {},
|
174
|
+
id: 1
|
175
|
+
)
|
176
|
+
)
|
177
|
+
puts JSON.pretty_generate(result)
|
178
|
+
```
|
179
|
+
|
180
|
+
## Configuration
|
181
|
+
|
182
|
+
The agent inherits configuration from `config/initializers/a2a.rb`. You can customize:
|
183
|
+
|
184
|
+
- Authentication requirements
|
185
|
+
- Middleware settings
|
186
|
+
- Storage backends
|
187
|
+
- Logging levels
|
188
|
+
|
189
|
+
## Documentation
|
190
|
+
|
191
|
+
- [A2A Protocol Specification](https://a2a-protocol.org/)
|
192
|
+
- [A2A Ruby SDK Documentation](https://a2a-protocol.org/sdk/ruby/)
|
193
|
+
- [Rails Integration Guide](https://a2a-protocol.org/sdk/ruby/rails/)
|
194
|
+
|
195
|
+
## Support
|
196
|
+
|
197
|
+
For issues and questions:
|
198
|
+
- Check the [A2A Ruby SDK Issues](https://github.com/a2aproject/a2a-ruby/issues)
|
199
|
+
- Review the [Rails Integration Documentation](https://a2a-protocol.org/sdk/ruby/rails/)
|
200
|
+
- Join the [A2A Community](https://a2a-protocol.org/community/)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Create<%= table_prefix.classify %>PushNotificationConfigs < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def change
|
5
|
+
create_table :<%= push_notification_configs_table_name %>, id: false do |t|
|
6
|
+
# Primary key
|
7
|
+
<% if postgresql? %>
|
8
|
+
t.uuid :id, primary_key: true, default: "gen_random_uuid()"
|
9
|
+
t.uuid :task_id, null: false
|
10
|
+
<% else %>
|
11
|
+
t.string :id, primary_key: true, limit: 36, null: false
|
12
|
+
t.string :task_id, limit: 36, null: false
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
# Push notification configuration
|
16
|
+
t.string :url, null: false
|
17
|
+
t.string :token, null: true
|
18
|
+
t.<%= json_column_type %> :authentication, null: true, default: -> {
|
19
|
+
<% if postgresql? %>
|
20
|
+
"'{}'::jsonb"
|
21
|
+
<% else %>
|
22
|
+
"'{}'"
|
23
|
+
<% end %>
|
24
|
+
}
|
25
|
+
|
26
|
+
# Configuration metadata
|
27
|
+
t.<%= json_column_type %> :metadata, null: true, default: -> {
|
28
|
+
<% if postgresql? %>
|
29
|
+
"'{}'::jsonb"
|
30
|
+
<% else %>
|
31
|
+
"'{}'"
|
32
|
+
<% end %>
|
33
|
+
}
|
34
|
+
|
35
|
+
# Status tracking
|
36
|
+
t.boolean :active, null: false, default: true
|
37
|
+
t.integer :retry_count, null: false, default: 0
|
38
|
+
t.datetime :last_success_at, null: true
|
39
|
+
t.datetime :last_failure_at, null: true
|
40
|
+
t.text :last_error, null: true
|
41
|
+
|
42
|
+
# Timestamps
|
43
|
+
t.timestamps null: false
|
44
|
+
|
45
|
+
# Soft delete support
|
46
|
+
t.datetime :deleted_at, null: true
|
47
|
+
end
|
48
|
+
|
49
|
+
<% if with_indexes? %>
|
50
|
+
# Add indexes for performance
|
51
|
+
add_index :<%= push_notification_configs_table_name %>, :id, unique: true
|
52
|
+
add_index :<%= push_notification_configs_table_name %>, :task_id
|
53
|
+
add_index :<%= push_notification_configs_table_name %>, :active
|
54
|
+
add_index :<%= push_notification_configs_table_name %>, :created_at
|
55
|
+
add_index :<%= push_notification_configs_table_name %>, :deleted_at
|
56
|
+
|
57
|
+
<% if postgresql? %>
|
58
|
+
# PostgreSQL-specific indexes for JSON columns
|
59
|
+
add_index :<%= push_notification_configs_table_name %>, :authentication, using: :gin
|
60
|
+
add_index :<%= push_notification_configs_table_name %>, :metadata, using: :gin
|
61
|
+
<% end %>
|
62
|
+
<% end %>
|
63
|
+
|
64
|
+
# Foreign key constraint to tasks table
|
65
|
+
add_foreign_key :<%= push_notification_configs_table_name %>, :<%= tasks_table_name %>,
|
66
|
+
column: :task_id, primary_key: :id, on_delete: :cascade
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Create<%= table_prefix.classify %>Tasks < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def change
|
5
|
+
create_table :<%= tasks_table_name %>, id: false do |t|
|
6
|
+
# Primary key - use UUID if PostgreSQL, string otherwise
|
7
|
+
<% if postgresql? %>
|
8
|
+
t.uuid :id, primary_key: true, default: "gen_random_uuid()"
|
9
|
+
t.uuid :context_id, null: false
|
10
|
+
<% else %>
|
11
|
+
t.string :id, primary_key: true, limit: 36, null: false
|
12
|
+
t.string :context_id, limit: 36, null: false
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
# Task metadata
|
16
|
+
t.string :kind, null: false, default: "task"
|
17
|
+
t.string :type, null: true # For task type classification
|
18
|
+
|
19
|
+
# Task status information
|
20
|
+
t.string :status_state, null: false, default: "submitted"
|
21
|
+
t.text :status_message, null: true
|
22
|
+
t.decimal :status_progress, precision: 5, scale: 2, null: true
|
23
|
+
t.<%= json_column_type %> :status_result, null: true
|
24
|
+
t.<%= json_column_type %> :status_error, null: true
|
25
|
+
t.datetime :status_updated_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
26
|
+
|
27
|
+
# Task data
|
28
|
+
t.<%= json_column_type %> :artifacts, null: true, default: -> {
|
29
|
+
<% if postgresql? %>
|
30
|
+
"'[]'::jsonb"
|
31
|
+
<% else %>
|
32
|
+
"'[]'"
|
33
|
+
<% end %>
|
34
|
+
}
|
35
|
+
t.<%= json_column_type %> :history, null: true, default: -> {
|
36
|
+
<% if postgresql? %>
|
37
|
+
"'[]'::jsonb"
|
38
|
+
<% else %>
|
39
|
+
"'[]'"
|
40
|
+
<% end %>
|
41
|
+
}
|
42
|
+
t.<%= json_column_type %> :metadata, null: true, default: -> {
|
43
|
+
<% if postgresql? %>
|
44
|
+
"'{}'::jsonb"
|
45
|
+
<% else %>
|
46
|
+
"'{}'"
|
47
|
+
<% end %>
|
48
|
+
}
|
49
|
+
|
50
|
+
# Task parameters and configuration
|
51
|
+
t.<%= json_column_type %> :params, null: true, default: -> {
|
52
|
+
<% if postgresql? %>
|
53
|
+
"'{}'::jsonb"
|
54
|
+
<% else %>
|
55
|
+
"'{}'"
|
56
|
+
<% end %>
|
57
|
+
}
|
58
|
+
|
59
|
+
# Timestamps
|
60
|
+
t.timestamps null: false
|
61
|
+
|
62
|
+
# Soft delete support
|
63
|
+
t.datetime :deleted_at, null: true
|
64
|
+
end
|
65
|
+
|
66
|
+
<% if with_indexes? %>
|
67
|
+
# Add indexes for performance
|
68
|
+
add_index :<%= tasks_table_name %>, :id, unique: true
|
69
|
+
add_index :<%= tasks_table_name %>, :context_id
|
70
|
+
add_index :<%= tasks_table_name %>, :status_state
|
71
|
+
add_index :<%= tasks_table_name %>, :type
|
72
|
+
add_index :<%= tasks_table_name %>, :created_at
|
73
|
+
add_index :<%= tasks_table_name %>, :status_updated_at
|
74
|
+
add_index :<%= tasks_table_name %>, :deleted_at
|
75
|
+
|
76
|
+
<% if postgresql? %>
|
77
|
+
# PostgreSQL-specific indexes for JSON columns
|
78
|
+
add_index :<%= tasks_table_name %>, :metadata, using: :gin
|
79
|
+
add_index :<%= tasks_table_name %>, :params, using: :gin
|
80
|
+
<% end %>
|
81
|
+
<% end %>
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Example A2A Agent Controller
|
5
|
+
#
|
6
|
+
# This controller demonstrates how to create an A2A agent using the Rails integration.
|
7
|
+
# It includes examples of skills, capabilities, and method definitions.
|
8
|
+
#
|
9
|
+
# To test this agent:
|
10
|
+
# 1. Start your Rails server
|
11
|
+
# 2. Visit <%= mount_path %>/agent-card to see the agent card
|
12
|
+
# 3. Send JSON-RPC requests to <%= mount_path %>/rpc
|
13
|
+
#
|
14
|
+
class ExampleAgentController < ApplicationController
|
15
|
+
include A2A::Rails::ControllerHelpers
|
16
|
+
|
17
|
+
# Configure this agent
|
18
|
+
a2a_agent name: "Example Agent",
|
19
|
+
description: "A demonstration A2A agent for Rails integration",
|
20
|
+
version: "1.0.0",
|
21
|
+
tags: ["example", "demo", "rails"]
|
22
|
+
|
23
|
+
<% if with_authentication? %>
|
24
|
+
# Configure authentication (uncomment and customize as needed)
|
25
|
+
# a2a_authenticate :<%= authentication_strategy %>, methods: ["secure_greeting"]
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
# Define agent skills
|
29
|
+
a2a_skill "greeting" do |skill|
|
30
|
+
skill.description = "Greet users with personalized messages"
|
31
|
+
skill.tags = ["greeting", "conversation", "social"]
|
32
|
+
skill.examples = [
|
33
|
+
{
|
34
|
+
input: { name: "Alice" },
|
35
|
+
output: { message: "Hello, Alice! Welcome to our A2A agent." }
|
36
|
+
}
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
a2a_skill "echo" do |skill|
|
41
|
+
skill.description = "Echo back any message sent to the agent"
|
42
|
+
skill.tags = ["utility", "testing", "echo"]
|
43
|
+
skill.examples = [
|
44
|
+
{
|
45
|
+
input: { message: "Hello, world!" },
|
46
|
+
output: { echo: "Hello, world!" }
|
47
|
+
}
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
a2a_skill "time" do |skill|
|
52
|
+
skill.description = "Get current server time in various formats"
|
53
|
+
skill.tags = ["utility", "time", "datetime"]
|
54
|
+
skill.examples = [
|
55
|
+
{
|
56
|
+
input: { format: "iso8601" },
|
57
|
+
output: { time: "2024-01-01T12:00:00Z", format: "iso8601" }
|
58
|
+
}
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
# A2A method implementations
|
63
|
+
|
64
|
+
##
|
65
|
+
# Greet a user with a personalized message
|
66
|
+
#
|
67
|
+
# @param params [Hash] Request parameters
|
68
|
+
# @option params [String] :name The name to greet
|
69
|
+
# @option params [String] :style Greeting style (formal, casual, friendly)
|
70
|
+
# @return [Hash] Greeting response
|
71
|
+
#
|
72
|
+
a2a_method "greeting" do |params|
|
73
|
+
name = params[:name] || "there"
|
74
|
+
style = params[:style] || "friendly"
|
75
|
+
|
76
|
+
message = case style
|
77
|
+
when "formal"
|
78
|
+
"Good day, #{name}. It is a pleasure to make your acquaintance."
|
79
|
+
when "casual"
|
80
|
+
"Hey #{name}! What's up?"
|
81
|
+
else
|
82
|
+
"Hello, #{name}! Welcome to our A2A agent."
|
83
|
+
end
|
84
|
+
|
85
|
+
{
|
86
|
+
message: message,
|
87
|
+
name: name,
|
88
|
+
style: style,
|
89
|
+
timestamp: Time.now.iso8601
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Echo back the provided message
|
95
|
+
#
|
96
|
+
# @param params [Hash] Request parameters
|
97
|
+
# @option params [String] :message The message to echo
|
98
|
+
# @return [Hash] Echo response
|
99
|
+
#
|
100
|
+
a2a_method "echo" do |params|
|
101
|
+
message = params[:message] || ""
|
102
|
+
|
103
|
+
{
|
104
|
+
echo: message,
|
105
|
+
length: message.length,
|
106
|
+
timestamp: Time.now.iso8601,
|
107
|
+
agent: "ExampleAgent"
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Get current server time
|
113
|
+
#
|
114
|
+
# @param params [Hash] Request parameters
|
115
|
+
# @option params [String] :format Time format (iso8601, unix, human)
|
116
|
+
# @option params [String] :timezone Timezone (default: UTC)
|
117
|
+
# @return [Hash] Time response
|
118
|
+
#
|
119
|
+
a2a_method "get_time" do |params|
|
120
|
+
format = params[:format] || "iso8601"
|
121
|
+
timezone = params[:timezone] || "UTC"
|
122
|
+
|
123
|
+
begin
|
124
|
+
time = Time.now.in_time_zone(timezone)
|
125
|
+
|
126
|
+
formatted_time = case format
|
127
|
+
when "unix"
|
128
|
+
time.to_i
|
129
|
+
when "human"
|
130
|
+
time.strftime("%B %d, %Y at %I:%M %p %Z")
|
131
|
+
else
|
132
|
+
time.iso8601
|
133
|
+
end
|
134
|
+
|
135
|
+
{
|
136
|
+
time: formatted_time,
|
137
|
+
format: format,
|
138
|
+
timezone: timezone,
|
139
|
+
server_timezone: Time.zone.name
|
140
|
+
}
|
141
|
+
rescue ArgumentError => e
|
142
|
+
raise A2A::Errors::InvalidParams, "Invalid timezone: #{timezone}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
<% if with_authentication? %>
|
147
|
+
##
|
148
|
+
# Secure greeting method (requires authentication)
|
149
|
+
#
|
150
|
+
# @param params [Hash] Request parameters
|
151
|
+
# @option params [String] :message Personal message
|
152
|
+
# @return [Hash] Authenticated greeting response
|
153
|
+
#
|
154
|
+
a2a_method "secure_greeting" do |params|
|
155
|
+
message = params[:message] || "Hello from the secure zone!"
|
156
|
+
|
157
|
+
{
|
158
|
+
message: message,
|
159
|
+
user: current_user_info,
|
160
|
+
permissions: current_user_permissions,
|
161
|
+
authenticated_at: Time.now.iso8601,
|
162
|
+
security_level: "authenticated"
|
163
|
+
}
|
164
|
+
end
|
165
|
+
<% end %>
|
166
|
+
|
167
|
+
##
|
168
|
+
# Get agent status and health information
|
169
|
+
#
|
170
|
+
# @param params [Hash] Request parameters (unused)
|
171
|
+
# @return [Hash] Status response
|
172
|
+
#
|
173
|
+
a2a_method "status" do |params|
|
174
|
+
{
|
175
|
+
status: "healthy",
|
176
|
+
version: "1.0.0",
|
177
|
+
uptime: Time.now - Rails.application.config.booted_at,
|
178
|
+
rails_version: Rails.version,
|
179
|
+
a2a_version: A2A::VERSION,
|
180
|
+
capabilities: self.class._a2a_capabilities&.map(&:name) || [],
|
181
|
+
methods: self.class._a2a_methods&.keys || [],
|
182
|
+
timestamp: Time.now.iso8601
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
# Standard Rails actions for agent card and RPC handling
|
187
|
+
|
188
|
+
##
|
189
|
+
# Serve the agent card
|
190
|
+
#
|
191
|
+
def agent_card
|
192
|
+
render_agent_card
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Handle JSON-RPC requests
|
197
|
+
#
|
198
|
+
def rpc
|
199
|
+
handle_a2a_rpc
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
# Override authentication methods if needed
|
205
|
+
<% if with_authentication? %>
|
206
|
+
|
207
|
+
def current_user_info
|
208
|
+
if respond_to?(:current_user) && current_user.present?
|
209
|
+
{
|
210
|
+
id: current_user.id,
|
211
|
+
email: current_user.email,
|
212
|
+
name: current_user.name || current_user.email
|
213
|
+
}
|
214
|
+
else
|
215
|
+
{ id: "anonymous", name: "Anonymous User" }
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def current_user_permissions
|
220
|
+
# Customize based on your authorization system
|
221
|
+
if respond_to?(:current_user) && current_user.present?
|
222
|
+
["read", "write"] # Example permissions
|
223
|
+
else
|
224
|
+
["read"] # Anonymous permissions
|
225
|
+
end
|
226
|
+
end
|
227
|
+
<% end %>
|
228
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A2A Ruby SDK Configuration
|
4
|
+
#
|
5
|
+
# This file configures the A2A (Agent2Agent) Protocol integration for your Rails application.
|
6
|
+
# For more information, visit: https://a2a-protocol.org/sdk/ruby/
|
7
|
+
|
8
|
+
A2A.configure do |config|
|
9
|
+
# Enable Rails integration
|
10
|
+
config.rails_integration = true
|
11
|
+
|
12
|
+
# Mount path for A2A endpoints (default: "/a2a")
|
13
|
+
config.mount_path = "<%= mount_path %>"
|
14
|
+
|
15
|
+
# Protocol configuration
|
16
|
+
config.protocol_version = "0.3.0"
|
17
|
+
config.default_transport = "JSONRPC"
|
18
|
+
|
19
|
+
# Feature flags
|
20
|
+
config.streaming_enabled = true
|
21
|
+
config.push_notifications_enabled = true
|
22
|
+
|
23
|
+
# Default MIME types for input/output
|
24
|
+
config.default_input_modes = ["text/plain", "application/json"]
|
25
|
+
config.default_output_modes = ["text/plain", "application/json"]
|
26
|
+
|
27
|
+
# Middleware configuration
|
28
|
+
config.middleware_enabled = true
|
29
|
+
config.cors_enabled = true
|
30
|
+
config.rate_limiting_enabled = false
|
31
|
+
config.logging_enabled = Rails.env.development?
|
32
|
+
|
33
|
+
# Authentication configuration
|
34
|
+
config.authentication_required = <%= with_authentication? %>
|
35
|
+
config.webhook_authentication_required = false
|
36
|
+
|
37
|
+
# Storage configuration
|
38
|
+
<% case storage_backend %>
|
39
|
+
<% when "database" %>
|
40
|
+
# Database storage (requires running migrations)
|
41
|
+
config.task_storage = :database
|
42
|
+
<% when "redis" %>
|
43
|
+
# Redis storage configuration
|
44
|
+
config.task_storage = :redis
|
45
|
+
config.redis_config = {
|
46
|
+
url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"),
|
47
|
+
pool_size: 5,
|
48
|
+
pool_timeout: 5
|
49
|
+
}
|
50
|
+
<% else %>
|
51
|
+
# In-memory storage (default - not recommended for production)
|
52
|
+
config.task_storage = :memory
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
# Timeout configuration
|
56
|
+
config.default_timeout = 30
|
57
|
+
|
58
|
+
# Logging configuration
|
59
|
+
config.log_level = Rails.env.production? ? :info : :debug
|
60
|
+
|
61
|
+
# User agent for HTTP requests
|
62
|
+
config.user_agent = "#{Rails.application.class.module_parent_name}/#{Rails.application.config.version rescue '1.0.0'} A2A-Ruby/<%= a2a_version %>"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Rails-specific configuration
|
66
|
+
Rails.application.configure do
|
67
|
+
# A2A engine configuration
|
68
|
+
config.a2a.enabled = true
|
69
|
+
config.a2a.mount_path = "<%= mount_path %>"
|
70
|
+
config.a2a.auto_mount = true
|
71
|
+
|
72
|
+
# Middleware configuration
|
73
|
+
config.a2a.middleware_enabled = true
|
74
|
+
config.a2a.cors_enabled = true
|
75
|
+
config.a2a.rate_limiting_enabled = false
|
76
|
+
config.a2a.logging_enabled = Rails.env.development?
|
77
|
+
|
78
|
+
# Authentication configuration
|
79
|
+
config.a2a.authentication_required = <%= with_authentication? %>
|
80
|
+
end
|
81
|
+
|
82
|
+
# Environment-specific configuration
|
83
|
+
case Rails.env
|
84
|
+
when "development"
|
85
|
+
A2A.configure do |config|
|
86
|
+
config.log_level = :debug
|
87
|
+
config.logging_enabled = true
|
88
|
+
end
|
89
|
+
|
90
|
+
when "test"
|
91
|
+
A2A.configure do |config|
|
92
|
+
config.task_storage = :memory
|
93
|
+
config.log_level = :warn
|
94
|
+
config.logging_enabled = false
|
95
|
+
end
|
96
|
+
|
97
|
+
when "production"
|
98
|
+
A2A.configure do |config|
|
99
|
+
config.log_level = :info
|
100
|
+
config.authentication_required = true
|
101
|
+
config.webhook_authentication_required = true
|
102
|
+
|
103
|
+
# Use Redis in production if available
|
104
|
+
if ENV["REDIS_URL"].present?
|
105
|
+
config.task_storage = :redis
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|