agent99 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/A2A_SPEC-dev.md +1829 -0
- data/CHANGELOG.md +31 -0
- data/COMMITS.md +196 -0
- data/DOCS.md +96 -0
- data/README.md +200 -78
- data/Rakefile +62 -0
- data/docs/AI/htm.md +215 -0
- data/docs/AI/htm.rb +141 -0
- data/docs/AI/htm_demo.db +0 -0
- data/docs/AI/notes_on_htm_implementation.md +1319 -0
- data/docs/AI/some_code.rb +692 -0
- data/docs/advanced-topics/a2a-protocol.md +13 -0
- data/docs/{control_actions.md → advanced-topics/control-actions.md} +2 -0
- data/docs/advanced-topics/model-context-protocol.md +4 -0
- data/docs/advanced-topics/multi-agent-processing.md +674 -0
- data/docs/agent-development/request-response-handling.md +512 -0
- data/docs/api-reference/agent99-base.md +463 -0
- data/docs/api-reference/message-clients.md +495 -0
- data/docs/api-reference/registry-client.md +470 -0
- data/docs/api-reference/schemas.md +518 -0
- data/docs/assets/css/custom.css +27 -0
- data/docs/assets/images/agent-lifecycle.svg +73 -0
- data/docs/assets/images/agent-registry-process.svg +86 -0
- data/docs/assets/images/agent-registry-processes.svg +114 -0
- data/docs/assets/images/agent-types-overview.svg +51 -0
- data/docs/assets/images/agent99-architecture.svg +85 -0
- data/docs/assets/images/agent99_logo.png +0 -0
- data/docs/assets/images/control-actions-state.svg +83 -0
- data/docs/assets/images/knowledge-graph.svg +77 -0
- data/docs/assets/images/message-processing-flow.svg +148 -0
- data/docs/assets/images/multi-agent-system.svg +66 -0
- data/docs/assets/images/proxy-pattern-sequence.svg +48 -0
- data/docs/assets/images/request-flow.svg +97 -0
- data/docs/assets/images/request-processing-lifecycle.svg +50 -0
- data/docs/assets/images/request-response-sequence.svg +39 -0
- data/docs/{agent_lifecycle.md → core-concepts/agent-lifecycle.md} +2 -0
- data/docs/core-concepts/agent-types.md +255 -0
- data/docs/{architecture.md → core-concepts/architecture.md} +5 -5
- data/docs/{what_is_an_agent.md → core-concepts/what-is-an-agent.md} +1 -1
- data/docs/diagrams/message-flow-sequence.svg +198 -0
- data/docs/diagrams/p2p-network-topology.svg +181 -0
- data/docs/diagrams/smart-transport-routing.svg +165 -0
- data/docs/diagrams/three-layer-architecture.svg +77 -0
- data/docs/diagrams/transport-extension-api.svg +309 -0
- data/docs/diagrams/transport-extension-architecture.svg +234 -0
- data/docs/diagrams/transport-selection-flowchart.svg +264 -0
- data/docs/examples/advanced-examples.md +951 -0
- data/docs/examples/basic-examples.md +268 -0
- data/docs/{agent_registry_processes.md → framework-components/agent-registry.md} +1 -1
- data/docs/{message_processing.md → framework-components/message-processing.md} +3 -1
- data/docs/getting-started/basic-example.md +306 -0
- data/docs/getting-started/installation.md +160 -0
- data/docs/getting-started/overview.md +64 -0
- data/docs/getting-started/quick-start.md +179 -0
- data/docs/index.md +97 -0
- data/examples/DEMO.md +148 -0
- data/examples/README.md +50 -0
- data/examples/bad_agent.rb +32 -0
- data/examples/registry.rb +0 -8
- data/examples/run_demo.rb +433 -0
- data/lib/agent99/amqp_message_client.rb +2 -2
- data/lib/agent99/base.rb +1 -1
- data/lib/agent99/message_processing.rb +6 -12
- data/lib/agent99/registry_client.rb +4 -1
- data/lib/agent99/version.rb +1 -1
- data/lib/agent99.rb +1 -1
- data/mkdocs.yml +195 -0
- data/p2p_plan.md +533 -0
- data/p2p_roadmap.md +299 -0
- data/registry_plan.md +1818 -0
- metadata +89 -32
- data/docs/README.md +0 -57
- data/docs/diagrams/agent_registry_processes.dot +0 -42
- data/docs/diagrams/agent_registry_processes.png +0 -0
- data/docs/diagrams/high_level_architecture.dot +0 -26
- data/docs/diagrams/high_level_architecture.png +0 -0
- data/docs/diagrams/request_flow.dot +0 -42
- data/docs/diagrams/request_flow.png +0 -0
- /data/docs/{advanced_features.md → advanced-topics/advanced-features.md} +0 -0
- /data/docs/{extending_the_framework.md → advanced-topics/extending-the-framework.md} +0 -0
- /data/docs/{custom_agent_implementation.md → agent-development/custom-agent-implementation.md} +0 -0
- /data/docs/{error_handling_and_logging.md → agent-development/error-handling-and-logging.md} +0 -0
- /data/docs/{schema_definition.md → agent-development/schema-definition.md} +0 -0
- /data/docs/{api_reference.md → api-reference/overview.md} +0 -0
- /data/docs/{agent_discovery.md → framework-components/agent-discovery.md} +0 -0
- /data/docs/{messaging_system.md → framework-components/messaging-system.md} +0 -0
- /data/docs/{breaking_change_v0.0.4.md → operations/breaking-changes.md} +0 -0
- /data/docs/{configuration.md → operations/configuration.md} +0 -0
- /data/docs/{preformance_considerations.md → operations/performance-considerations.md} +0 -0
- /data/docs/{security.md → operations/security.md} +0 -0
- /data/docs/{troubleshooting.md → operations/troubleshooting.md} +0 -0
@@ -0,0 +1,518 @@
|
|
1
|
+
# Schemas API
|
2
|
+
|
3
|
+
Agent99 uses JSON Schema validation to ensure data integrity in agent communication. This document covers the schema system, built-in schemas, and how to create custom validation schemas.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
Agent99 leverages `simple_json_schema_builder` to define and validate request/response schemas, providing:
|
8
|
+
|
9
|
+
- **Type safety** - Ensure data types match expectations
|
10
|
+
- **Required field validation** - Enforce mandatory fields
|
11
|
+
- **Format validation** - Validate emails, URLs, dates, etc.
|
12
|
+
- **Custom validation** - Define business-specific rules
|
13
|
+
- **Auto-documentation** - Schemas serve as API documentation
|
14
|
+
|
15
|
+
## Built-in Schemas
|
16
|
+
|
17
|
+
### Header Schema
|
18
|
+
|
19
|
+
All Agent99 messages include a standard header:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class Agent99::HeaderSchema < SimpleJsonSchemaBuilder::Base
|
23
|
+
object do
|
24
|
+
string :request_id, required: true, format: :uuid
|
25
|
+
string :agent_name, required: true
|
26
|
+
string :correlation_id, format: :uuid
|
27
|
+
string :timestamp, required: true, format: :datetime
|
28
|
+
string :reply_to
|
29
|
+
object :metadata do
|
30
|
+
# Additional metadata fields
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
**Example usage:**
|
37
|
+
```ruby
|
38
|
+
header = {
|
39
|
+
request_id: SecureRandom.uuid,
|
40
|
+
agent_name: "CalculatorAgent",
|
41
|
+
correlation_id: SecureRandom.uuid,
|
42
|
+
timestamp: Time.now.iso8601,
|
43
|
+
metadata: { version: "1.0.0" }
|
44
|
+
}
|
45
|
+
|
46
|
+
# Validate header
|
47
|
+
Agent99::HeaderSchema.new.validate!(header)
|
48
|
+
```
|
49
|
+
|
50
|
+
### Basic Message Schema
|
51
|
+
|
52
|
+
Base schema for all agent messages:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
class Agent99::MessageSchema < SimpleJsonSchemaBuilder::Base
|
56
|
+
object do
|
57
|
+
object :header, schema: Agent99::HeaderSchema, required: true
|
58
|
+
# Payload varies by message type
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
## Creating Custom Schemas
|
64
|
+
|
65
|
+
### Simple Request Schema
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require 'simple_json_schema_builder'
|
69
|
+
|
70
|
+
class GreetingRequest < SimpleJsonSchemaBuilder::Base
|
71
|
+
object do
|
72
|
+
object :header, schema: Agent99::HeaderSchema
|
73
|
+
string :name, required: true, minLength: 1, maxLength: 100
|
74
|
+
string :language, enum: %w[en es fr de], default: 'en'
|
75
|
+
boolean :formal, default: false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Usage in agent
|
80
|
+
class GreeterAgent < Agent99::Base
|
81
|
+
def info
|
82
|
+
{
|
83
|
+
name: self.class.to_s,
|
84
|
+
type: :server,
|
85
|
+
capabilities: ['greeting'],
|
86
|
+
request_schema: GreetingRequest.schema
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def process_request(payload)
|
91
|
+
# Payload is automatically validated against schema
|
92
|
+
name = payload[:name]
|
93
|
+
language = payload[:language] || 'en'
|
94
|
+
formal = payload[:formal] || false
|
95
|
+
|
96
|
+
greeting = generate_greeting(name, language, formal)
|
97
|
+
send_response(message: greeting)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
### Complex Schema with Nested Objects
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class OrderRequest < SimpleJsonSchemaBuilder::Base
|
106
|
+
object do
|
107
|
+
object :header, schema: Agent99::HeaderSchema
|
108
|
+
|
109
|
+
string :customer_id, required: true, format: :uuid
|
110
|
+
|
111
|
+
array :items, required: true, minItems: 1 do
|
112
|
+
object do
|
113
|
+
string :product_id, required: true, format: :uuid
|
114
|
+
integer :quantity, required: true, minimum: 1
|
115
|
+
number :unit_price, required: true, minimum: 0
|
116
|
+
object :metadata do
|
117
|
+
string :color
|
118
|
+
string :size, enum: %w[XS S M L XL XXL]
|
119
|
+
boolean :gift_wrap, default: false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
object :shipping_address, required: true do
|
125
|
+
string :street, required: true
|
126
|
+
string :city, required: true
|
127
|
+
string :state, required: true
|
128
|
+
string :zip_code, required: true, pattern: '^\d{5}(-\d{4})?$'
|
129
|
+
string :country, required: true, enum: %w[US CA MX]
|
130
|
+
end
|
131
|
+
|
132
|
+
object :payment do
|
133
|
+
string :method, required: true, enum: %w[credit_card paypal bank_transfer]
|
134
|
+
string :token, required: true
|
135
|
+
number :amount, required: true, minimum: 0
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
### Response Schema
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class OrderResponse < SimpleJsonSchemaBuilder::Base
|
145
|
+
object do
|
146
|
+
object :header, schema: Agent99::HeaderSchema
|
147
|
+
|
148
|
+
string :order_id, required: true, format: :uuid
|
149
|
+
string :status, required: true, enum: %w[pending confirmed processing shipped delivered cancelled]
|
150
|
+
number :total_amount, required: true, minimum: 0
|
151
|
+
string :estimated_delivery, format: :datetime
|
152
|
+
|
153
|
+
array :items do
|
154
|
+
object do
|
155
|
+
string :product_id, required: true
|
156
|
+
integer :quantity, required: true
|
157
|
+
number :line_total, required: true
|
158
|
+
string :status, enum: %w[available backordered discontinued]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
object :tracking do
|
163
|
+
string :carrier
|
164
|
+
string :tracking_number
|
165
|
+
string :tracking_url, format: :uri
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
## Schema Validation
|
172
|
+
|
173
|
+
### Automatic Validation
|
174
|
+
|
175
|
+
Agent99 automatically validates incoming requests against defined schemas:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
class ValidatingAgent < Agent99::Base
|
179
|
+
def info
|
180
|
+
{
|
181
|
+
name: self.class.to_s,
|
182
|
+
type: :server,
|
183
|
+
capabilities: ['validation_example'],
|
184
|
+
request_schema: OrderRequest.schema,
|
185
|
+
response_schema: OrderResponse.schema
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
def process_request(payload)
|
190
|
+
# payload is already validated by framework
|
191
|
+
|
192
|
+
order_id = SecureRandom.uuid
|
193
|
+
total = calculate_total(payload[:items])
|
194
|
+
|
195
|
+
response = {
|
196
|
+
order_id: order_id,
|
197
|
+
status: 'confirmed',
|
198
|
+
total_amount: total,
|
199
|
+
estimated_delivery: (Time.now + 7.days).iso8601,
|
200
|
+
items: process_items(payload[:items])
|
201
|
+
}
|
202
|
+
|
203
|
+
# Response will be validated before sending
|
204
|
+
send_response(response)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
### Manual Validation
|
210
|
+
|
211
|
+
For custom validation scenarios:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
def process_request(payload)
|
215
|
+
# Additional business logic validation
|
216
|
+
begin
|
217
|
+
validate_business_rules(payload)
|
218
|
+
rescue ValidationError => e
|
219
|
+
return send_error("Business validation failed: #{e.message}", "BUSINESS_VALIDATION_ERROR")
|
220
|
+
end
|
221
|
+
|
222
|
+
# Continue processing...
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def validate_business_rules(payload)
|
228
|
+
customer_id = payload[:customer_id]
|
229
|
+
|
230
|
+
# Check customer exists and is active
|
231
|
+
customer = Customer.find(customer_id)
|
232
|
+
raise ValidationError, "Customer not found" unless customer
|
233
|
+
raise ValidationError, "Customer account suspended" unless customer.active?
|
234
|
+
|
235
|
+
# Validate inventory
|
236
|
+
payload[:items].each do |item|
|
237
|
+
product = Product.find(item[:product_id])
|
238
|
+
raise ValidationError, "Product #{item[:product_id]} not available" unless product&.available?
|
239
|
+
raise ValidationError, "Insufficient inventory for #{product.name}" if product.stock < item[:quantity]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
## Advanced Schema Features
|
245
|
+
|
246
|
+
### Conditional Schemas
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
class ConditionalRequest < SimpleJsonSchemaBuilder::Base
|
250
|
+
object do
|
251
|
+
object :header, schema: Agent99::HeaderSchema
|
252
|
+
string :operation, required: true, enum: %w[create update delete]
|
253
|
+
|
254
|
+
# Conditional fields based on operation
|
255
|
+
if_property :operation, equals: 'create' do
|
256
|
+
string :name, required: true
|
257
|
+
string :email, required: true, format: :email
|
258
|
+
end
|
259
|
+
|
260
|
+
if_property :operation, equals: 'update' do
|
261
|
+
string :id, required: true, format: :uuid
|
262
|
+
string :name
|
263
|
+
string :email, format: :email
|
264
|
+
end
|
265
|
+
|
266
|
+
if_property :operation, equals: 'delete' do
|
267
|
+
string :id, required: true, format: :uuid
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
273
|
+
### Custom Format Validators
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
# Define custom formats
|
277
|
+
SimpleJsonSchemaBuilder.configure do |config|
|
278
|
+
config.add_format :phone_number, /^\+?[\d\s\-\(\)]+$/
|
279
|
+
config.add_format :product_sku, /^[A-Z]{2}\d{6}$/
|
280
|
+
end
|
281
|
+
|
282
|
+
class ProductRequest < SimpleJsonSchemaBuilder::Base
|
283
|
+
object do
|
284
|
+
object :header, schema: Agent99::HeaderSchema
|
285
|
+
string :sku, required: true, format: :product_sku
|
286
|
+
string :support_phone, format: :phone_number
|
287
|
+
end
|
288
|
+
end
|
289
|
+
```
|
290
|
+
|
291
|
+
### Schema Composition
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# Base schemas for reuse
|
295
|
+
class AddressSchema < SimpleJsonSchemaBuilder::Base
|
296
|
+
object do
|
297
|
+
string :street, required: true
|
298
|
+
string :city, required: true
|
299
|
+
string :state, required: true
|
300
|
+
string :zip_code, required: true
|
301
|
+
string :country, required: true
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class PersonSchema < SimpleJsonSchemaBuilder::Base
|
306
|
+
object do
|
307
|
+
string :first_name, required: true
|
308
|
+
string :last_name, required: true
|
309
|
+
string :email, format: :email
|
310
|
+
string :phone, format: :phone_number
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Compose into larger schema
|
315
|
+
class CustomerRequest < SimpleJsonSchemaBuilder::Base
|
316
|
+
object do
|
317
|
+
object :header, schema: Agent99::HeaderSchema
|
318
|
+
object :personal_info, schema: PersonSchema, required: true
|
319
|
+
object :billing_address, schema: AddressSchema, required: true
|
320
|
+
object :shipping_address, schema: AddressSchema
|
321
|
+
end
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
## Schema Testing
|
326
|
+
|
327
|
+
### Unit Tests
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
require 'minitest/autorun'
|
331
|
+
|
332
|
+
class TestOrderSchema < Minitest::Test
|
333
|
+
def test_valid_order_request
|
334
|
+
valid_payload = {
|
335
|
+
header: {
|
336
|
+
request_id: SecureRandom.uuid,
|
337
|
+
agent_name: "TestAgent",
|
338
|
+
timestamp: Time.now.iso8601
|
339
|
+
},
|
340
|
+
customer_id: SecureRandom.uuid,
|
341
|
+
items: [
|
342
|
+
{
|
343
|
+
product_id: SecureRandom.uuid,
|
344
|
+
quantity: 2,
|
345
|
+
unit_price: 29.99
|
346
|
+
}
|
347
|
+
],
|
348
|
+
shipping_address: {
|
349
|
+
street: "123 Main St",
|
350
|
+
city: "Anytown",
|
351
|
+
state: "CA",
|
352
|
+
zip_code: "12345",
|
353
|
+
country: "US"
|
354
|
+
}
|
355
|
+
}
|
356
|
+
|
357
|
+
schema = OrderRequest.new
|
358
|
+
assert schema.valid?(valid_payload)
|
359
|
+
end
|
360
|
+
|
361
|
+
def test_invalid_order_request
|
362
|
+
invalid_payload = {
|
363
|
+
header: {
|
364
|
+
request_id: "not-a-uuid", # Invalid format
|
365
|
+
agent_name: "TestAgent",
|
366
|
+
timestamp: Time.now.iso8601
|
367
|
+
},
|
368
|
+
items: [], # Empty array not allowed
|
369
|
+
shipping_address: {
|
370
|
+
# Missing required fields
|
371
|
+
street: "123 Main St"
|
372
|
+
}
|
373
|
+
}
|
374
|
+
|
375
|
+
schema = OrderRequest.new
|
376
|
+
refute schema.valid?(invalid_payload)
|
377
|
+
|
378
|
+
errors = schema.validate(invalid_payload)
|
379
|
+
assert errors.any? { |e| e.include?("request_id") }
|
380
|
+
assert errors.any? { |e| e.include?("items") }
|
381
|
+
end
|
382
|
+
end
|
383
|
+
```
|
384
|
+
|
385
|
+
### Schema Documentation Generation
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
class SchemaDocGenerator
|
389
|
+
def self.generate_docs(schema_class, output_file)
|
390
|
+
schema = schema_class.schema
|
391
|
+
|
392
|
+
docs = {
|
393
|
+
title: schema_class.name,
|
394
|
+
description: extract_description(schema),
|
395
|
+
properties: extract_properties(schema),
|
396
|
+
required: schema.dig('required') || [],
|
397
|
+
examples: generate_examples(schema)
|
398
|
+
}
|
399
|
+
|
400
|
+
File.write(output_file, docs.to_json)
|
401
|
+
end
|
402
|
+
|
403
|
+
private
|
404
|
+
|
405
|
+
def self.extract_properties(schema)
|
406
|
+
schema.dig('properties') || {}
|
407
|
+
end
|
408
|
+
|
409
|
+
def self.generate_examples(schema)
|
410
|
+
# Generate example data based on schema
|
411
|
+
example_generator = JsonSchemaExampleGenerator.new(schema)
|
412
|
+
example_generator.generate
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# Generate documentation
|
417
|
+
SchemaDocGenerator.generate_docs(OrderRequest, 'docs/schemas/order_request.json')
|
418
|
+
```
|
419
|
+
|
420
|
+
## Error Handling
|
421
|
+
|
422
|
+
### Schema Validation Errors
|
423
|
+
|
424
|
+
```ruby
|
425
|
+
class OrderAgent < Agent99::Base
|
426
|
+
def process_request(payload)
|
427
|
+
# Framework automatically validates, but you can catch validation errors
|
428
|
+
begin
|
429
|
+
validate_additional_rules(payload)
|
430
|
+
rescue Agent99::SchemaValidationError => e
|
431
|
+
return send_error(
|
432
|
+
"Validation failed: #{e.message}",
|
433
|
+
"SCHEMA_VALIDATION_ERROR",
|
434
|
+
{
|
435
|
+
errors: e.validation_errors,
|
436
|
+
schema_version: "1.0.0"
|
437
|
+
}
|
438
|
+
)
|
439
|
+
end
|
440
|
+
|
441
|
+
# Process valid request...
|
442
|
+
end
|
443
|
+
|
444
|
+
private
|
445
|
+
|
446
|
+
def validate_additional_rules(payload)
|
447
|
+
# Custom validation beyond schema
|
448
|
+
if payload[:items].sum { |item| item[:quantity] } > 100
|
449
|
+
raise Agent99::SchemaValidationError, "Order too large (max 100 items)"
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
```
|
454
|
+
|
455
|
+
### Schema Version Management
|
456
|
+
|
457
|
+
```ruby
|
458
|
+
class VersionedSchema
|
459
|
+
SCHEMA_VERSIONS = {
|
460
|
+
"1.0" => OrderRequestV1,
|
461
|
+
"1.1" => OrderRequestV11,
|
462
|
+
"2.0" => OrderRequestV2
|
463
|
+
}.freeze
|
464
|
+
|
465
|
+
def self.validate(payload, version = "2.0")
|
466
|
+
schema_class = SCHEMA_VERSIONS[version]
|
467
|
+
raise ArgumentError, "Unsupported schema version: #{version}" unless schema_class
|
468
|
+
|
469
|
+
schema = schema_class.new
|
470
|
+
unless schema.valid?(payload)
|
471
|
+
errors = schema.validate(payload)
|
472
|
+
raise Agent99::SchemaValidationError, "Validation failed: #{errors.join(', ')}"
|
473
|
+
end
|
474
|
+
|
475
|
+
payload
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
# Usage in agent
|
480
|
+
def process_request(payload)
|
481
|
+
schema_version = header_value('schema_version') || "2.0"
|
482
|
+
validated_payload = VersionedSchema.validate(payload, schema_version)
|
483
|
+
|
484
|
+
# Process with validated payload...
|
485
|
+
end
|
486
|
+
```
|
487
|
+
|
488
|
+
## Best Practices
|
489
|
+
|
490
|
+
### 1. Schema Design
|
491
|
+
- **Be explicit**: Define all expected fields and types
|
492
|
+
- **Use descriptive names**: Clear field names and descriptions
|
493
|
+
- **Version your schemas**: Plan for schema evolution
|
494
|
+
- **Provide examples**: Include example payloads in documentation
|
495
|
+
|
496
|
+
### 2. Validation Strategy
|
497
|
+
- **Validate early**: Catch errors as soon as possible
|
498
|
+
- **Provide clear errors**: Include helpful validation messages
|
499
|
+
- **Use business validation**: Complement schema validation with business rules
|
500
|
+
- **Test thoroughly**: Cover valid and invalid cases
|
501
|
+
|
502
|
+
### 3. Performance
|
503
|
+
- **Cache compiled schemas**: Don't recompile schemas on every request
|
504
|
+
- **Validate incrementally**: Only validate changed portions when possible
|
505
|
+
- **Monitor validation time**: Track validation performance
|
506
|
+
- **Use appropriate depth**: Don't over-validate in performance-critical paths
|
507
|
+
|
508
|
+
### 4. Evolution
|
509
|
+
- **Backward compatibility**: Plan for schema changes
|
510
|
+
- **Optional fields**: Use optional fields for new features
|
511
|
+
- **Deprecation strategy**: Plan how to retire old schema versions
|
512
|
+
- **Migration support**: Provide tools to migrate between schema versions
|
513
|
+
|
514
|
+
## Next Steps
|
515
|
+
|
516
|
+
- **[Agent99::Base](agent99-base.md)** - Core agent class reference
|
517
|
+
- **[Message Clients](message-clients.md)** - Message broker APIs
|
518
|
+
- **[Schema Definition](../agent-development/schema-definition.md)** - Detailed schema guide
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/* Custom CSS for Agent99 Documentation */
|
2
|
+
|
3
|
+
/* Header background - dark red */
|
4
|
+
.md-header {
|
5
|
+
background-color: #8B0000 !important; /* Dark red color */
|
6
|
+
}
|
7
|
+
|
8
|
+
/* Ensure navigation tabs have proper contrast with dark red background */
|
9
|
+
.md-header__nav {
|
10
|
+
background-color: #8B0000 !important;
|
11
|
+
}
|
12
|
+
|
13
|
+
/* Tab bar under header - slightly darker red */
|
14
|
+
.md-tabs {
|
15
|
+
background-color: #660000 !important;
|
16
|
+
}
|
17
|
+
|
18
|
+
/* Active tab styling */
|
19
|
+
.md-tabs__link--active {
|
20
|
+
color: #ffffff !important;
|
21
|
+
border-bottom: 2px solid #ffffff;
|
22
|
+
}
|
23
|
+
|
24
|
+
/* Hover state for tabs */
|
25
|
+
.md-tabs__link:hover {
|
26
|
+
color: #ffcccc !important;
|
27
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400" style="background: transparent;">
|
2
|
+
<defs>
|
3
|
+
<marker id="arrow-life" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto">
|
4
|
+
<path d="M0,0 L10,5 L0,10 L5,5 z" fill="#66b3ff"/>
|
5
|
+
</marker>
|
6
|
+
<filter id="shadow">
|
7
|
+
<feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
|
8
|
+
<feOffset dx="2" dy="2" result="offsetblur"/>
|
9
|
+
<feComponentTransfer>
|
10
|
+
<feFuncA type="linear" slope="0.3"/>
|
11
|
+
</feComponentTransfer>
|
12
|
+
<feMerge>
|
13
|
+
<feMergeNode/>
|
14
|
+
<feMergeNode in="SourceGraphic"/>
|
15
|
+
</feMerge>
|
16
|
+
</filter>
|
17
|
+
</defs>
|
18
|
+
|
19
|
+
<!-- Title -->
|
20
|
+
<text x="400" y="30" text-anchor="middle" font-family="monospace" font-size="18" fill="#e0e0e0" font-weight="bold">Agent Lifecycle State Machine</text>
|
21
|
+
|
22
|
+
<!-- States -->
|
23
|
+
<!-- Created -->
|
24
|
+
<circle cx="120" cy="200" r="50" fill="#2a2a3e" stroke="#66b3ff" stroke-width="3" filter="url(#shadow)"/>
|
25
|
+
<text x="120" y="200" text-anchor="middle" font-family="monospace" font-size="14" fill="#99ccff" font-weight="bold">Created</text>
|
26
|
+
<text x="120" y="220" text-anchor="middle" font-family="monospace" font-size="10" fill="#66b3ff">new()</text>
|
27
|
+
|
28
|
+
<!-- Initialized -->
|
29
|
+
<circle cx="320" cy="200" r="50" fill="#2a2a3e" stroke="#66ff99" stroke-width="3" filter="url(#shadow)"/>
|
30
|
+
<text x="320" y="200" text-anchor="middle" font-family="monospace" font-size="14" fill="#99ffcc" font-weight="bold">Initialized</text>
|
31
|
+
<text x="320" y="220" text-anchor="middle" font-family="monospace" font-size="10" fill="#66ff99">init() called</text>
|
32
|
+
|
33
|
+
<!-- Running -->
|
34
|
+
<circle cx="520" cy="200" r="50" fill="#2a2a3e" stroke="#ffcc66" stroke-width="3" filter="url(#shadow)"/>
|
35
|
+
<text x="520" y="200" text-anchor="middle" font-family="monospace" font-size="14" fill="#ffdd99" font-weight="bold">Running</text>
|
36
|
+
<text x="520" y="220" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffcc66">Processing</text>
|
37
|
+
|
38
|
+
<!-- Shutdown -->
|
39
|
+
<circle cx="700" cy="200" r="50" fill="#2a2a3e" stroke="#ff6666" stroke-width="3" filter="url(#shadow)"/>
|
40
|
+
<text x="700" y="200" text-anchor="middle" font-family="monospace" font-size="14" fill="#ff9999" font-weight="bold">Shutdown</text>
|
41
|
+
<text x="700" y="220" text-anchor="middle" font-family="monospace" font-size="10" fill="#ff6666">fini() called</text>
|
42
|
+
|
43
|
+
<!-- Transitions -->
|
44
|
+
<!-- Created to Initialized -->
|
45
|
+
<path d="M 170 200 L 270 200" stroke="#66b3ff" stroke-width="2" marker-end="url(#arrow-life)"/>
|
46
|
+
<text x="220" y="190" text-anchor="middle" font-family="monospace" font-size="11" fill="#99ccff">initialize()</text>
|
47
|
+
|
48
|
+
<!-- Initialized to Running -->
|
49
|
+
<path d="M 370 200 L 470 200" stroke="#66ff99" stroke-width="2" marker-end="url(#arrow-life)"/>
|
50
|
+
<text x="420" y="190" text-anchor="middle" font-family="monospace" font-size="11" fill="#99ffcc">start()</text>
|
51
|
+
|
52
|
+
<!-- Running to Shutdown -->
|
53
|
+
<path d="M 570 200 L 650 200" stroke="#ffcc66" stroke-width="2" marker-end="url(#arrow-life)"/>
|
54
|
+
<text x="610" y="190" text-anchor="middle" font-family="monospace" font-size="11" fill="#ffdd99">stop()</text>
|
55
|
+
|
56
|
+
<!-- Error paths -->
|
57
|
+
<!-- Created to Shutdown (error) -->
|
58
|
+
<path d="M 120 250 Q 400 350 700 250" stroke="#ff6666" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#arrow-life)" opacity="0.6"/>
|
59
|
+
<text x="400" y="340" text-anchor="middle" font-family="monospace" font-size="10" fill="#ff9999">Error/Abort</text>
|
60
|
+
|
61
|
+
<!-- Initialized to Shutdown (error) -->
|
62
|
+
<path d="M 320 250 Q 500 320 700 250" stroke="#ff6666" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#arrow-life)" opacity="0.6"/>
|
63
|
+
|
64
|
+
<!-- Self-loop on Running -->
|
65
|
+
<path d="M 520 150 Q 580 100 520 150" stroke="#ffcc66" stroke-width="1.5" marker-end="url(#arrow-life)" fill="none"/>
|
66
|
+
<text x="550" y="95" text-anchor="middle" font-family="monospace" font-size="10" fill="#ffdd99">process_request()</text>
|
67
|
+
|
68
|
+
<!-- Phase labels -->
|
69
|
+
<text x="120" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#666" font-style="italic">Setup</text>
|
70
|
+
<text x="320" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#666" font-style="italic">Configuration</text>
|
71
|
+
<text x="520" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#666" font-style="italic">Operation</text>
|
72
|
+
<text x="700" y="100" text-anchor="middle" font-family="monospace" font-size="12" fill="#666" font-style="italic">Cleanup</text>
|
73
|
+
</svg>
|