Agency

Latest version: v1.6.3

Safety actively analyzes 681748 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 3

1.6.3

Includes [Parameterization of Max Workers in ResourceManager 185](https://github.com/operand/agency/pull/185) from migueldl96 🎉

1.6.1

Updates `AMQPSpace` to use a topic exchange for better observability. Thanks wwj718! 🙏

1.6.0

I'm proud to announce Agency v1.6.0. Technically, it represents a big step forward and I believe is a milestone in terms of clarity for the project.

It took some iterating and lots of thoughtful feedback, but I think the learnings from the last few versions have come together to create a clearer picture for the API and architecture. I believe there is a good foundation now to focus on the next steps: [js client](https://github.com/operand/agency/issues/136), [code execution](https://github.com/operand/agency/issues/137), and [multimodal support](https://github.com/operand/agency/issues/26).

Special thanks to wwj718 and hidaris for their reviews and feedback that helped shape this release!

As with any major release, please do not hesitate to open issues or discussions if you have any questions or feedback. 🙇‍♂️

Summary of Changes


`Space` types now support mixed threading/multiprocessing for agents

[Version 1.4]() introduced multiprocessing but did not allow threading and multiprocessing based agents to be used together. Additionally the API prevented some use cases that were previously possible, due to not allowing direct access to agents from the main thread.

In 1.6.0, all agents are now `add`'ed using `multiprocessing` based subprocesses by default. Agents added this way may not be directly accessed, similar to 1.4.

With 1.6.0 you now have the option to run agents in the "foreground" using `Space.add_foreground`. When adding an agent this way, an instance of the agent is returned in the current thread. For example:

py
my_agent = my_space.add_foreground(MyAgent, "my_agent")


The tradeoff for adding an agent in the foreground is performance. Due to the python GIL, agents running in the foreground may not be fully parallel with other agents, and may block the main thread.

It's recommended to use `Space.add_foreground` only when needed.

`MultiprocessSpace` and `ThreadSpace` classes replaced with `LocalSpace`

Since all `Space` types now support mixed threading/multiprocessing, `LocalSpace` combines these older types into a single class for in-application communication.

Context manager support and `Space.destroy` method added

These two additions allow for improved resource management. Creating and cleaning up a `Space` can now be done using the context manager syntax:

py
with AMQPSpace() as space:
space.add(Host, "Host")
...


This form will automatically clean up Space related resources upon exit of the `with` block.

Direct control of cleanup is also possible using the `Space.destroy` method.

py
space = AMQPSpace()
space.add(Host, "Host")
...
space.destroy()



`Agent.respond_with` and `Agent.raise_with` methods added

These methods are intended for use with `Agent.request` and the response callbacks: `Agent.handle_action_value` and `Agent.handle_action_error`.

Automatic response behavior has changed from v1.5. Actions will no longer automatically return their return value to the sender.

To explicitly return a value to the sender associated with the current message, use `Agent.respond_with`. For example:

py
action
def ping(self):
self.respond_with("pong")


The above will invoke the `handle_action_value` callback on the sender with the value `"pong"`, or return the value `"pong"` directly when used with the `request` method.

Similarly you may return an error with:
py
action
def ping(self):
self.raise_with(Exception("oops"))


Actions that raise exceptions will automatically call the `raise_with` method.

Note that `respond_with` and `raise_with` may be called multiple times in a single action. Each call will invoke the appropriate callback on the sender.

When using `request` the first call to `respond_with` will return the value to the calling thread. Subsequent responses will invoke the `handle_action_value` callback.

Performance Improvements

All busy waiting has been replaced with event based processing. The test suite completes about 80% faster than in 1.5 and CPU overhead should now be minimized.

`Agent.__init__` signature updated

The Agent constructor has been reverted to its older signature which does not include a `Queue` parameter. Make sure to update your classes.
py
def __init__(self, id: str, receive_own_broadcasts: bool = True):

1.5.0

This release addresses the request related issues brought up [here](https://github.com/operand/agency/issues/142) regarding the use of multiple "meta" id's and the semantics of automatic responses to messages. Shoutout to wwj718 for the great feedback for these updates!

Since this includes breaking changes, the version has been bumped to 1.5.0.

Summary:

- All messages now populate the `meta.id` field with a UUID by default. Agents may set their own custom `meta.id` upon sending to overwrite the generated ID.

In addition, `Agent.send()` now returns the `meta.id` of the sent message. This way you may store the generated ID for your own purposes.

- Removed the use of `meta.response_id` and `meta.request_id`. These have been replaced by `meta.parent_id`. I think the term "parent" is clearer. This choice is inspired by the [Jupyter messaging docs](https://jupyter-client.readthedocs.io/en/stable/messaging.html).

To be clear, `meta.parent_id` if set, refers to the `meta.id` of the original message which the current message is responding to.

To illustrate, the following would be typical meta fields in a send/response pair:
py
send
{
"meta": {
"id": "123", the message ID here
},
...
}
response
{
"meta": {
"id": "456", response has its own unique meta.id
"parent_id": "123", refers to the prior message
},
...
}


- All messages will now result in a `[response]` message like the above, returning the return value or error result from the message's action. These messages (unless using the `request()` method) will invoke the `handle_action_value()` and `handle_action_error()` methods appropriately.

One might want to filter these messages from view in a user interface. The demo Gradio app has been updated to demonstrate this. See the `_should_render()` and `get_chatbot_message()` methods for an example.

- `Agent.original_message()` has been renamed to `Agent.parent_message()`. Additionally `parent_message()` takes a message as a parameter, allowing you to traverse a chain of messages in reverse.

- Internally renamed `Agent._pending_responses` to `Agent._pending_requests` which I think is clearer.

- The logger has been updated to provide colorization of output for easier reading. You may change the default "monokai" pygments style used for object output, by setting the `LOG_PYGMENTS_STYLE` environment variable.

- Log "warning" messages for unimplemented `handle_action_value` and `handle_action_error` methods have been updated to only print once per session, in order to reduce logging noise in the default setting.

- Tests and documentation updated.

1.4.1

Fixes a regression in the 1.4.0 release related to the AMQP exchange parameter

1.4.0

Summary of Changes

This release brings multiprocessing support and a few nice API improvements. Notably, I've added an implementation of synchronous messaging with the new `request()` method, and I've introduced logging support for development and debugging.

There are a number of breaking API changes in this release. Please read through the following summary to update your codebase.

Since this update required a rather large internal change, please beware of bugs and report any issues that you find. ❤️


`Space` Class Changes

`Space.add()`

`Space.add()` no longer accepts an `Agent` instance as an argument. Spaces will now create the `Agent` instance for you when adding them to the space.

An example of the updated syntax:

space.add(MyAgent, "my_agent_id")


Note how it accepts the `Agent` type. Any remaining parameters starting with the `id` are passed to the type's constructor when creating the agent.

`MultiprocessSpace` added

The new `MultiprocessSpace` class allows for better parallelism on a single host. Adding this option was the initial focus of this update. It uses the multiprocessing library for both concurrency and communication.

`NativeSpace` renamed to `ThreadSpace`

The `NativeSpace` class has been renamed to `ThreadSpace` for clarity.

`AMQPSpace` updated to use multiprocessing for local concurrency

Since multiprocessing is now supported, it made sense to use it rather than threading with the `AMQPSpace` class for local concurrency. This means that you do not need to define separate applications to achieve parallelism on a single host when using AMQP.

`Agent` Class Changes

Synchronous messaging support added with `Agent.request()`

To allow calling actions synchronously, the `Agent` class now includes a `request()` method.

For example:

python
try:
return_value = self.request({
"to": "ExampleAgent",
"action": {
"name": "example_action",
"args": {
"content": "hello"
}
}
}, timeout=5)
except ActionError as e:
print(e.message)


The `Agent.request()` method is a synchronous version of the `send()` method that allows you to call an action and receive its return value or exception synchronously.

`response()` and `error()` renamed to `handle_action_value()` and `handle_action_error()`

Additionally, these methods are no longer implemented as actions. You should remove the `action` decorator from these methods when you update them.

To clarify moving forward, an action (a method decorated by `action`) should be considered a public method available to other agents. Callbacks are called upon certain events, rather than being directly invoked by an agent.

In the cases of `handle_action_value()` and `handle_action_error()`, these callbacks are invoked when a previously sent action returns a value or raises an exception.

Also note that their signatures have removed the `original_message_id` argument and simplified to:

python
def handle_action_value(self, value):

and
python
def handle_action_error(self, error: ActionError):


See the `original_message()` method for how the original message may now be accessed.

`Agent.current_message()` is now a method

`Agent.current_message()` may be called during any action or action related callback to inspect the full message being processed.

`Agent.original_message()` added

Similar to `current_message()`, `original_message()` may be called during the `handle_action_value()` and `handle_action_error()` callbacks to inspect the original message that the current message is responding to.

The original message must have defined the `meta.id` field for this method to return it. Otherwise, this method will return None.

Message schema changes

Moved `id` field to `meta.id`

The `id` field has been moved within the `meta` field to be treated similarly as any other metadata.

If set, the `id` field may be used to correlate response messages (internal messages that return a value or exception) with the original message they correspond to. See the `original_message()` method for more details.

`action.args` is now optional

`action.args` can be omitted when calling an action that takes no arguments. For example, given the following action:

python
action
def example_action(self):
...


The following message is valid:
python
self.send({
"to": "ExampleAgent",
"action": {
"name": "example_action"
}
})


Beware that this may cause errors if your code currently inspects messages and expects the `action.args` field to be present.

Additional Changes

Added Logging Support

Some basic logging has been added. Set the `LOGLEVEL` environment variable to see the logs.

The default setting is `warning`. `info` will show messages as they are being sent. `debug` shows more fine grained handling of messages as they are received and processed.

If you'd like to add more logging or make changes, please open a PR or issue.

Demo updates

* A `multiprocess` example has been added.

Run `demo run multiprocess` to try it out

* The `native` service has been renamed to `threaded`

* The `request_permission` callback on `Host` class has been bypassed temporarily.

Asking for terminal input in a multiprocess application doesn't work well yet. A future update should address this. So for the time being, all actions on the host will be allowed.

* Unfortunately gradio live reloading also had to be disabled. You'll have to restart the app for code changes to take effect. If either of the above changes cause problems for you, please open an issue.

Page 1 of 3

© 2024 Safety CLI Cybersecurity Inc. All Rights Reserved.