agent99 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 827e521c4a6580cfe954cf07cf04f1a822c00b9ed6eebf75598d925aeb555626
4
+ data.tar.gz: 1b3e9b499610b9b3853757432fd4e3e39acab8fb70e29feb94eae1d22f6097aa
5
+ SHA512:
6
+ metadata.gz: ce549f64fb88cd2f152e7af27b9724dbfedfda0f6255033ea1773dda7eaf89dd71a6b622667d023b3093ae1eca72ae3a55e7bd6b1f538e3035fe889ab52ac7c4
7
+ data.tar.gz: 959b0c9c342738bb9ee75f2dd82a4caaa825a13656f6d12dba2a11434857c08f11d746c1754f03ce1902f9e4ced1f3b7e1067ab1cdbe39c054250b23592ae94f
data/.envrc ADDED
@@ -0,0 +1,3 @@
1
+ # ai_agent/.envrc
2
+
3
+ export RR=`pwd`
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-11-28
4
+
5
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dewayne VanHoozer
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,99 @@
1
+ # Agent99 Framework (AFW)
2
+
3
+ **Under Development** Initial release has no AI components - its just a generic client-server / request-response micro-services system using a peer-to-peer messaging broker and a centralized agent registry.
4
+
5
+ Agent99 is a Ruby-based framework for building and managing AI agents in a distributed system. It provides a robust foundation for creating intelligent agents that can communicate, discover each other, and perform various tasks.
6
+
7
+ ## Features
8
+
9
+ - Agent Lifecycle Management: Easy setup and teardown of agents
10
+ - Message Processing: Handle requests, responses, and control messages
11
+ - Agent Discovery: Find other agents based on capabilities
12
+ - Flexible Communication: Support for both AMQP and NATS messaging systems
13
+ - Registry Integration: Register and discover agents through a central registry
14
+ - Error Handling and Logging: Built-in error management and logging capabilities
15
+ - Control Actions: Pause, resume, update configuration, and request status of agents
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'agent99'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ ```
28
+ $ bundle install
29
+ ```
30
+
31
+ Or install it yourself as:
32
+
33
+ ```
34
+ $ gem install agent99
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Here's a basic example of how to create an AI agent:
40
+
41
+ ```ruby
42
+ require 'agent99'
43
+
44
+ class MyAgentRequest < SimpleJsonSchemaBuilder::Base
45
+ object do
46
+ object :header, schema: Agent99::HeaderSchema
47
+
48
+ # Define your agents parameters ....
49
+ string :greeting, required: false, examples: ["Hello"]
50
+ string :name, required: true, examples: ["World"]
51
+ end
52
+ end
53
+
54
+ class MyAgent < Agent99::Base
55
+ REQUEST_SCHEMA = MyAgentRequest.schema
56
+
57
+ def capabilities
58
+ ['text_processing', 'sentiment_analysis']
59
+ # TODO: make up mind on keyword or unstructured text
60
+ end
61
+
62
+ def receive_request
63
+ # Handle the validated incoming requests
64
+ response = { result: "Processed request" }
65
+
66
+ # Not every request needs a response
67
+ send_response(response)
68
+ end
69
+
70
+ def receive_response
71
+ # You sent a request to another agent
72
+ # now handle the response.
73
+ end
74
+ end
75
+
76
+ agent = MyAgent.new
77
+ agent.run
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ The framework can be configured through environment variables:
83
+
84
+ - `REGISTRY_BASE_URL`: URL of the agent registry service (default: 'http://localhost:4567') See the default registry service in the examples folder.
85
+
86
+ ## Contributing
87
+
88
+ Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/agent99.
89
+
90
+ ## Short-term Roadmap
91
+
92
+ - In the example registry, replace the Array(Hash) datastore with sqlite3 with a vector table to support discovery using semantic search.
93
+ - Treat the agent like a Tool w/r/t RAG for prompts.
94
+ - Add AgentRequest schema to agent's info in the registry.
95
+ - Add AgentResponse schema to define the `result` element in the response JSON payload
96
+
97
+ ## License
98
+
99
+ 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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
data/docs/todo.md ADDED
@@ -0,0 +1,66 @@
1
+ # Documentation
2
+
3
+ TODO: Lets get some more detailed documentation underway that can be linked to from the main README.md file.
4
+
5
+ Here are some key areas that should be covered in comprehensive documentation files:
6
+
7
+ 1. Architecture Overview:
8
+ - Explain the overall structure of the AiAgent framework
9
+ - Describe the roles of different components (Base, MessageClient, RegistryClient, etc.)
10
+ - Illustrate how agents communicate and interact within the system
11
+
12
+ 2. Agent Lifecycle:
13
+ - Detail the process of creating, initializing, running, and shutting down an agent
14
+ - Explain the registration and withdrawal process with the registry service
15
+
16
+ 3. Message Processing:
17
+ - Describe the different types of messages (request, response, control)
18
+ - Explain how messages are routed and processed
19
+ - Detail the schema validation process for incoming messages
20
+
21
+ 4. Agent Discovery:
22
+ - Explain how agents can discover other agents based on capabilities
23
+ - Describe the process of querying the registry for available agents
24
+
25
+ 5. Control Actions:
26
+ - List and explain all available control actions (shutdown, pause, resume, etc.)
27
+ - Describe how to implement custom control actions
28
+
29
+ 6. Configuration:
30
+ - Detail all configuration options available for agents
31
+ - Explain how to use environment variables for configuration
32
+
33
+ 7. Error Handling and Logging:
34
+ - Describe the error handling mechanisms in place
35
+ - Explain how to configure and use the logging system effectively
36
+
37
+ 8. Messaging Systems:
38
+ - Provide details on both AMQP and NATS messaging systems
39
+ - Explain how to switch between different messaging backends
40
+
41
+ 9. Custom Agent Implementation:
42
+ - Provide a step-by-step guide on creating a custom agent
43
+ - Explain how to define capabilities, handle requests, and send responses
44
+
45
+ 10. Schema Definition:
46
+ - Explain how to define and use request and response schemas
47
+ - Provide examples of complex schema definitions
48
+
49
+ 11. Performance Considerations:
50
+ - Discuss any performance optimizations in the framework
51
+ - Provide guidelines for writing efficient agents
52
+
53
+ 12. Security:
54
+ - Explain any security measures in place (if any)
55
+ - Provide best practices for securing agent communications
56
+
57
+ 13. Extending the Framework:
58
+ - Describe how to add new features or modify existing functionality
59
+ - Explain the plugin system (if one exists)
60
+
61
+ 14. Troubleshooting:
62
+ - Provide a list of common issues and their solutions
63
+ - Explain how to debug agents effectively
64
+
65
+ 15. API Reference:
66
+ - Provide a comprehensive API reference for all public methods and classes
@@ -0,0 +1,79 @@
1
+ # Agent99 Framework Examples
2
+
3
+ - TODO
4
+ - Review and edit
5
+ - Add instructions for brew install rabbitmq nats-server boxes
6
+ - review example agents to see code can be tightened
7
+
8
+ This folder contains example implementations using the Agent99 framework. The framework provides a foundation for building AI agents that can communicate with each other through a message-based system.
9
+
10
+ ## Files
11
+
12
+ ### 1. hello_world.rb
13
+
14
+ This file demonstrates a basic AI agent implementation using the Agent99 framework.
15
+
16
+ - Class: `HelloWorld < Agent99::Base`
17
+ - Functionality: Responds to "hello world" requests
18
+ - Key methods:
19
+ - `receive_request`: Handles incoming requests
20
+ - `validate_request`: Validates the request against a schema
21
+ - `process`: Generates the response
22
+
23
+ ### 2. hello_world_client.rb
24
+
25
+ This file shows how to create a client that interacts with the HelloWorld agent.
26
+
27
+ - Class: `HelloWorldClient < Agent99::Base`
28
+ - Functionality: Sends a request to a HelloWorld agent and processes the response
29
+ - Key methods:
30
+ - `init`: Initiates the request sending process
31
+ - `send_request`: Builds and sends the request
32
+ - `receive_response`: Handles the response from the HelloWorld agent
33
+
34
+ ### 3. hello_world_request.rb
35
+
36
+ This file defines the schema for HelloWorld requests using SimpleJsonSchemaBuilder.
37
+
38
+ - Class: `HelloWorldRequest < SimpleJsonSchemaBuilder::Base`
39
+ - Defines the structure of a valid HelloWorld request
40
+
41
+ ### 4. registry.rb
42
+
43
+ This file implements a simple registry service for AI agents using Sinatra.
44
+
45
+ - Functionality: Allows agents to register, discover other agents, and withdraw from the registry
46
+ - Endpoints:
47
+ - GET `/healthcheck`: Returns the number of registered agents
48
+ - POST `/register`: Registers a new agent
49
+ - GET `/discover`: Discovers agents by capability
50
+ - DELETE `/withdraw/:uuid`: Withdraws an agent from the registry
51
+ - GET `/`: Lists all registered agents
52
+
53
+ ## Usage
54
+
55
+ 1. Start the registry service:
56
+ ```
57
+ ruby registry.rb
58
+ ```
59
+
60
+ 2. Run the HelloWorld agent:
61
+ ```
62
+ ruby hello_world.rb
63
+ ```
64
+
65
+ 3. Run the HelloWorld client:
66
+ ```
67
+ ruby hello_world_client.rb
68
+ ```
69
+
70
+ ## Dependencies
71
+
72
+ - Ruby 3.3+
73
+ - Gems: json, json_schema, sinatra, bunny, securerandom
74
+
75
+ ## Notes
76
+
77
+ - The framework uses modern Ruby 3.3 syntax, especially for hashes and method signatures with named parameters.
78
+ - The examples demonstrate basic usage of the Agent99 framework, including request/response handling, validation, and agent discovery.
79
+ - The registry service uses an in-memory store (AGENT_REGISTRY) for simplicity, but it's recommended to use a more robust solution like SQLite for production use.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/hello_world.rb
3
+
4
+ require 'json'
5
+ require 'json_schema'
6
+
7
+ require_relative '../lib/agent99'
8
+ require_relative 'hello_world_request'
9
+
10
+ class HelloWorld < Agent99::Base
11
+ REQUEST_SCHEMA = HelloWorldRequest.schema
12
+ # RESPONSE_SCHEMA = Agent99::RESPONSE.schema
13
+ # ERROR_SCHEMA = Agent99::ERROR.schema
14
+
15
+ # The request is in @payload
16
+ def receive_request
17
+ send_response( validate_request || process )
18
+ end
19
+
20
+ # This method validates the incoming request and returns any errors found
21
+ # or nil if there are no errors.
22
+ # It allows for returning an array of errors.
23
+ #
24
+ # TODO: consider a 4th message type of error
25
+ #
26
+ def validate_request
27
+ responses = []
28
+
29
+ # Check if the ID matches
30
+ unless id == to_uuid
31
+ logger.error <<~ERROR
32
+ #{name} received someone else's request
33
+ id: #{id} timestamp: #{timestamp}
34
+ to_uuid: #{to_uuid}
35
+ from_uuid: #{from_uuid}
36
+ event_uuid:#{event_uuid}
37
+ ERROR
38
+ responses << {
39
+ error: "Incorrect message queue for header",
40
+ details: {
41
+ id: id,
42
+ header: header
43
+ }
44
+ }
45
+ end
46
+
47
+ # Validate the incoming request body against the schema
48
+ validation_errors = validate_schema
49
+ unless validation_errors.empty?
50
+ logger.error "Validation errors: #{validation_errors}"
51
+ responses << {
52
+ error: "Invalid request",
53
+ details: validation_errors
54
+ }
55
+ end
56
+
57
+ responses.empty? ? nil : responses
58
+ end
59
+
60
+ # Returns the response value
61
+ # All response message have the same schema in that
62
+ # they have a header (all messages have headers) and
63
+ # a result element that is a String. Could it be
64
+ # a JSON string, sure but then we would need a
65
+ # RESPONSE_SCHEMA constant for the class.
66
+ def process
67
+ {
68
+ result: get(:greeting) + ' ' + get(:name)
69
+ }
70
+ end
71
+
72
+
73
+ # As a server type, HelloWorld should never receive
74
+ # a response message.
75
+ def receive_response(response)
76
+ loger.warn("Unexpected response type message: response.inspect")
77
+ end
78
+
79
+ private
80
+
81
+ # NOTE: what I'm thinking about here is similar to the
82
+ # prompt tool (aka function) callback facility
83
+ # where descriptive text is used to describe
84
+ # what the tool does.
85
+ #
86
+ # TODO: scale this idea back to just keywords
87
+ # until the registry program gets more
88
+ # stuff added to its discovery process.
89
+ #
90
+ def capabilities
91
+ %w[ greeter hello_world hello-world hello]
92
+ end
93
+ end
94
+
95
+ # Example usage
96
+ agent = HelloWorld.new
97
+ agent.run # Starts listening for messages
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/hello_world_client.rb
3
+
4
+ require 'json'
5
+ require 'json_schema'
6
+ require 'securerandom'
7
+ require_relative '../lib/agent99'
8
+
9
+ class HelloWorldClient < Agent99::Base
10
+ def init
11
+ send_request
12
+ end
13
+
14
+ def send_request
15
+ to_uuid = discover_agent(capability: 'greeter', how_many: 1).first[:uuid]
16
+
17
+ request = build_request(
18
+ to_uuid:,
19
+ greeting: 'Hey',
20
+ name: 'MadBomber'
21
+ )
22
+
23
+ result = @message_client.publish(request)
24
+ logger.info "Sent request: #{request.inspect}; status? #{result.inspect}"
25
+ end
26
+
27
+ def build_request(
28
+ to_uuid:,
29
+ greeting: 'Hello',
30
+ name: 'World'
31
+ )
32
+
33
+ {
34
+ header: {
35
+ type: 'request',
36
+ from_uuid: @id,
37
+ to_uuid:,
38
+ event_uuid: SecureRandom.uuid,
39
+ timestamp: Agent99::Timestamp.new.to_i
40
+ },
41
+ greeting:,
42
+ name:
43
+ }
44
+ end
45
+
46
+
47
+ def receive_response
48
+ logger.info "Received response: #{payload.inspect}"
49
+ result = payload[:result]
50
+
51
+ puts
52
+ puts `echo "#{result}" | boxes -d info`
53
+ puts
54
+
55
+ exit(0)
56
+ end
57
+
58
+ #####################################################
59
+ private
60
+
61
+ def capabilities
62
+ ['hello_world_client']
63
+ end
64
+ end
65
+
66
+
67
+ # Example usage
68
+ client = HelloWorldClient.new
69
+ client.run
70
+
@@ -0,0 +1,12 @@
1
+ # examples/hello_world_request.rb
2
+
3
+ require_relative '../lib/agent99/header_schema'
4
+
5
+ class HelloWorldRequest < SimpleJsonSchemaBuilder::Base
6
+ object do
7
+ object :header, schema: Agent99::HeaderSchema
8
+
9
+ string :greeting, required: false, examples: ["Hello"]
10
+ string :name, required: true, examples: ["World"]
11
+ end
12
+ end
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ # examples/registry.rb
3
+
4
+ require 'debug_me'
5
+ include DebugMe
6
+
7
+ require 'sinatra'
8
+ require 'json'
9
+ require 'bunny'
10
+ require 'securerandom'
11
+
12
+ # In-memory registry to store agent capabilities
13
+ # Array(Hash)
14
+ # TODO: change this data store to a sqlite database
15
+ # maybe with a vector search capability.
16
+ #
17
+ AGENT_REGISTRY = []
18
+
19
+ # Health check endpoint
20
+ get '/healthcheck' do
21
+ content_type :json
22
+ { agent_count: AGENT_REGISTRY.size }.to_json
23
+ end
24
+
25
+ # Endpoint to register an agent
26
+ post '/register' do
27
+ request.body.rewind
28
+ agent_info = JSON.parse(request.body.read, symbolize_names: true)
29
+
30
+ agent_name = agent_info[:name]
31
+ capabilities = agent_info[:capabilities]
32
+
33
+ agent_uuid = SecureRandom.uuid
34
+
35
+ AGENT_REGISTRY << agent_info.merge({uuid: agent_uuid})
36
+
37
+ status 201
38
+ content_type :json
39
+ { uuid: agent_uuid }.to_json
40
+ end
41
+
42
+ # Endpoint to discover agents by capability
43
+ # TODO: This is a simple keyword matcher. Looking
44
+ # => for a semantic match process.
45
+ get '/discover' do
46
+ capability = params['capability']
47
+
48
+ matching_agents = AGENT_REGISTRY.select do |agent|
49
+ agent[:capabilities].include?(capability)
50
+ end
51
+
52
+ content_type :json
53
+ matching_agents.to_json
54
+ end
55
+
56
+ # Withdraw an agent from the registry
57
+ delete '/withdraw/:uuid' do
58
+ uuid = params['uuid']
59
+ how_many = AGENT_REGISTRY.size
60
+
61
+ AGENT_REGISTRY.delete_if { |agent_info| agent_info[:uuid] == uuid }
62
+
63
+ if AGENT_REGISTRY.size == how_many
64
+ status 404 # Not Found
65
+ content_type :json
66
+ { error: "Agent with UUID #{uuid} not found." }.to_json
67
+ else
68
+ status 204 # No Content
69
+ end
70
+ end
71
+
72
+ # Display all registered agents
73
+ get '/' do
74
+ content_type :json
75
+ AGENT_REGISTRY.to_json
76
+ end
77
+
78
+ # Start the Sinatra server
79
+ if __FILE__ == $PROGRAM_NAME
80
+ Sinatra::Application.run!
81
+ end
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ # Start the registry server
4
+ echo "Starting registry server..."
5
+ # Assuming the registry server is a separate process, replace with actual command
6
+ # e.g., ruby registry_server.rb &
7
+ # Replace 'registry_server_command' with the actual command to start your registry server
8
+ # Example: ruby registry_server.rb &
9
+ ./registry.rb &
10
+
11
+ # Start the HelloWorld agent
12
+ echo "Starting HelloWorld agent..."
13
+ ./hello_world.rb &
14
+
15
+ # Start the HelloWorldClient agent
16
+ echo "Starting HelloWorldClient agent..."
17
+ ./hello_world_client.rb &
18
+
19
+ # Wait for all background processes to finish
20
+ wait
@@ -0,0 +1,5 @@
1
+ module Agent99
2
+ # empty namespace
3
+ end
4
+
5
+ require_relative 'timestamp'
@@ -0,0 +1,35 @@
1
+ # lib/agent99/agent_discovery.rb
2
+
3
+ module Agent99::AgentDiscovery
4
+
5
+ # Returns the agent's capabilities (to be overridden by subclasses).
6
+ #
7
+ # @return [Array<String>] The agent's capabilities
8
+ #
9
+ def capabilities
10
+ []
11
+ end
12
+
13
+
14
+ ################################################
15
+ private
16
+
17
+ # Discovers other agents with a specific capability.
18
+ #
19
+ # @param capability [String] The capability to search for
20
+ # @param how_many [Integer] The number of agents to return
21
+ # @param all [Boolean] Whether to return all matching agents
22
+ # @return [Array<Hash>] The discovered agents
23
+ # @raise [RuntimeError] If no agents are found
24
+ #
25
+ def discover_agent(capability:, how_many: 1, all: false)
26
+ result = @registry_client.discover(capability:)
27
+
28
+ if result.empty?
29
+ logger.error "No agents found for capability: #{capability}"
30
+ raise "No agents available"
31
+ end
32
+
33
+ all ? result : result.sample(how_many)
34
+ end
35
+ end