academy-dashboard 0.1.0__tar.gz
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.
- academy_dashboard-0.1.0/.github/workflows/publish.yml +107 -0
- academy_dashboard-0.1.0/.pre-commit-config.yaml +34 -0
- academy_dashboard-0.1.0/LICENSE +21 -0
- academy_dashboard-0.1.0/PKG-INFO +115 -0
- academy_dashboard-0.1.0/README.md +68 -0
- academy_dashboard-0.1.0/TODO.md +64 -0
- academy_dashboard-0.1.0/academy_dashboard/__init__.py +11 -0
- academy_dashboard-0.1.0/academy_dashboard/agents/__init__.py +6 -0
- academy_dashboard-0.1.0/academy_dashboard/agents/sleeper.py +28 -0
- academy_dashboard-0.1.0/academy_dashboard/agents/spinner.py +52 -0
- academy_dashboard-0.1.0/academy_dashboard/monitored_agent.py +215 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/__init__.py +7 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS1224 University of Illinois.png +0 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS160 The University of Chicago.svg +247 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS2936 National Energy Research Scientific Computing Center.svg +801 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS32093 University of Texas at Austin.svg +1 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS3671 SLAC National Accelerator Laboratory.svg +163 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS397511 Purdue University.svg +7 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/AS683 Argonne National Laboratory.png +0 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/Ames National Laboratory.svg +1 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/Globus_logo_WHITE_square.png +0 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/ai_eye.html +123 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/logo-Academy-2025-200x200-dark-bg.png +0 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/logo.svg +56 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/assets/logo_dark.svg +56 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/dashboard.py +297 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/message.py +58 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/user_agent.py +113 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent/web_elements.py +1055 -0
- academy_dashboard-0.1.0/academy_dashboard/user_agent_launcher.py +125 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/PKG-INFO +115 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/SOURCES.txt +42 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/dependency_links.txt +1 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/entry_points.txt +2 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/requires.txt +33 -0
- academy_dashboard-0.1.0/academy_dashboard.egg-info/top_level.txt +3 -0
- academy_dashboard-0.1.0/examples/__init__.py +0 -0
- academy_dashboard-0.1.0/examples/basic.py +81 -0
- academy_dashboard-0.1.0/examples/example.py +69 -0
- academy_dashboard-0.1.0/examples/federated_example.py +83 -0
- academy_dashboard-0.1.0/pyproject.toml +183 -0
- academy_dashboard-0.1.0/setup.cfg +4 -0
- academy_dashboard-0.1.0/tests/__init__.py +0 -0
- academy_dashboard-0.1.0/tests/user_agent_test.py +102 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Release process (read carefully):
|
|
2
|
+
#
|
|
3
|
+
# 1. Choose the next semver version number (e.g., 1.3.2) with optional
|
|
4
|
+
# release candidate segments. See: https://peps.python.org/pep-0440/
|
|
5
|
+
#
|
|
6
|
+
# 2. Open and merge any PRs required for documentation updates
|
|
7
|
+
# (e.g., migration guides for version bumps with breaking changes).
|
|
8
|
+
# Documentation changes must be made *before* running this publish
|
|
9
|
+
# workflow for the changes to be included in the tagged version of
|
|
10
|
+
# the docs site.
|
|
11
|
+
#
|
|
12
|
+
# 3. Tag the target commit on main for the release. Note that
|
|
13
|
+
# setuptools_scm requires the version tag is prepended with "v".
|
|
14
|
+
# This command creates a signed tag (recommended) using your GPG key.
|
|
15
|
+
#
|
|
16
|
+
# export VERSION=v1.3.2
|
|
17
|
+
# git tag -s $VERSION <SHORT_COMMIT_HASH> -m "Dashboard $VERSION"
|
|
18
|
+
# git push origin $VERSION
|
|
19
|
+
#
|
|
20
|
+
# 4. Create a new release on GitHub corresponding to the tag. Releases
|
|
21
|
+
# will trigger this publish workflow that will publish the package
|
|
22
|
+
# to PyPI and push a new version of the docs.
|
|
23
|
+
#
|
|
24
|
+
# * Go to https://github.com/academy-agents/nexus/releases
|
|
25
|
+
# * Click "Draft a new release"
|
|
26
|
+
# * Select the tag you created
|
|
27
|
+
#
|
|
28
|
+
# Official releases:
|
|
29
|
+
#
|
|
30
|
+
# * Select the previous tag as the previous *official* release. For
|
|
31
|
+
# example, use v0.4.0 as the previous release tag and NOT v0.4.0rc1.
|
|
32
|
+
# This is necessary for generating the diff link on the release
|
|
33
|
+
# correctly
|
|
34
|
+
# * Click "Generate release notes"
|
|
35
|
+
# * Add an "Upgrade Steps" section at the top (see previous releases
|
|
36
|
+
# for examples)
|
|
37
|
+
# * Review the generated notes and edit as needed. PRs are organized
|
|
38
|
+
# by tag, but some PRs will be missing tags and need to be moved
|
|
39
|
+
# from the "Other Changes" section to the correct section
|
|
40
|
+
# * Select "Set as the latest release"
|
|
41
|
+
# * Publish the release
|
|
42
|
+
#
|
|
43
|
+
# Pre releases:
|
|
44
|
+
#
|
|
45
|
+
# * Do NOT generate release notes. A description of "Development
|
|
46
|
+
# pre-release for <VERSION>" is sufficient
|
|
47
|
+
# * Leave the previous tag as "auto"
|
|
48
|
+
# * Select "Set as a pre-release"
|
|
49
|
+
# * Publish the release
|
|
50
|
+
#
|
|
51
|
+
name: publish
|
|
52
|
+
|
|
53
|
+
on:
|
|
54
|
+
release:
|
|
55
|
+
types: [published]
|
|
56
|
+
workflow_dispatch:
|
|
57
|
+
|
|
58
|
+
permissions:
|
|
59
|
+
contents: write
|
|
60
|
+
id-token: write
|
|
61
|
+
|
|
62
|
+
jobs:
|
|
63
|
+
publish:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Checkout
|
|
67
|
+
uses: actions/checkout@v6
|
|
68
|
+
with:
|
|
69
|
+
fetch-depth: 0
|
|
70
|
+
|
|
71
|
+
- name: Extract package version from tag
|
|
72
|
+
run: |
|
|
73
|
+
# Removes 'v' prefix from tag (e.g., v1.0.0 -> 1.0.0)
|
|
74
|
+
VERSION=${GITHUB_REF_NAME#v}
|
|
75
|
+
echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
|
|
76
|
+
echo "Extracted version from tag: $VERSION"
|
|
77
|
+
|
|
78
|
+
- name: Setup Python 3.13
|
|
79
|
+
uses: actions/setup-python@v6
|
|
80
|
+
with:
|
|
81
|
+
python-version: '3.13'
|
|
82
|
+
|
|
83
|
+
- name: Install pypa/build and build
|
|
84
|
+
run: |
|
|
85
|
+
pip install build
|
|
86
|
+
python -m build --sdist --wheel --outdir dist/ .
|
|
87
|
+
|
|
88
|
+
- name: Upload files to release
|
|
89
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
90
|
+
uses: softprops/action-gh-release@v3
|
|
91
|
+
with:
|
|
92
|
+
files: dist/*
|
|
93
|
+
|
|
94
|
+
- name: Publish to PyPI
|
|
95
|
+
if: startsWith(github.ref, 'refs/tags')
|
|
96
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
97
|
+
|
|
98
|
+
- name: Configure git
|
|
99
|
+
run: |
|
|
100
|
+
git config --local user.name "GitHub Actions Bot"
|
|
101
|
+
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
102
|
+
|
|
103
|
+
- name: Deploy docs for release version to gh-pages branch
|
|
104
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
105
|
+
run: |
|
|
106
|
+
pip install .[docs]
|
|
107
|
+
mike deploy --push --update-aliases "${{ env.PACKAGE_VERSION }}" stable
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
ci:
|
|
2
|
+
autofix_prs: false
|
|
3
|
+
repos:
|
|
4
|
+
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
|
|
5
|
+
rev: v6.0.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: mixed-line-ending
|
|
8
|
+
- id: trailing-whitespace
|
|
9
|
+
- id: end-of-file-fixer
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: check-docstring-first
|
|
12
|
+
- id: check-json
|
|
13
|
+
- id: check-yaml
|
|
14
|
+
- id: check-merge-conflict
|
|
15
|
+
- id: name-tests-test
|
|
16
|
+
- repo: 'https://github.com/codespell-project/codespell'
|
|
17
|
+
rev: v2.4.2
|
|
18
|
+
hooks:
|
|
19
|
+
- id: codespell
|
|
20
|
+
exclude: ".svg$"
|
|
21
|
+
- repo: 'https://github.com/astral-sh/ruff-pre-commit'
|
|
22
|
+
rev: v0.14.11
|
|
23
|
+
hooks:
|
|
24
|
+
- id: ruff-check
|
|
25
|
+
args:
|
|
26
|
+
- '--fix'
|
|
27
|
+
- '--exit-non-zero-on-fix'
|
|
28
|
+
- id: ruff-format
|
|
29
|
+
- repo: 'https://github.com/pre-commit/mirrors-mypy'
|
|
30
|
+
rev: v1.19.1
|
|
31
|
+
hooks:
|
|
32
|
+
- id: mypy
|
|
33
|
+
additional_dependencies:
|
|
34
|
+
- types-requests
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Academy Agents
|
|
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.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: academy-dashboard
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Observability and Oversight components for Academy Agents
|
|
5
|
+
Author: ModCon BASE Core Agentic Frameworks Team
|
|
6
|
+
Author-email: yadunand@uchicago.edu
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
|
+
Requires-Python: ==3.13.*
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: academy-py>=0.5.0
|
|
17
|
+
Requires-Dist: dotenv
|
|
18
|
+
Requires-Dist: globus-compute-sdk
|
|
19
|
+
Requires-Dist: tox>=4.34.1
|
|
20
|
+
Requires-Dist: flask>=3.1
|
|
21
|
+
Requires-Dist: psutil
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: covdefaults>=2.2; extra == "dev"
|
|
24
|
+
Requires-Dist: coverage; extra == "dev"
|
|
25
|
+
Requires-Dist: mypy; extra == "dev"
|
|
26
|
+
Requires-Dist: types-requests; extra == "dev"
|
|
27
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit>=4.5.1; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.15.0; extra == "dev"
|
|
33
|
+
Requires-Dist: tox; extra == "dev"
|
|
34
|
+
Requires-Dist: virtualenv; extra == "dev"
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: black==24.4.2; extra == "docs"
|
|
37
|
+
Requires-Dist: mkdocs-autorefs==1.3.1; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocs-click==0.8.1; extra == "docs"
|
|
39
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "docs"
|
|
40
|
+
Requires-Dist: mkdocs-literate-nav==0.6.1; extra == "docs"
|
|
41
|
+
Requires-Dist: mkdocs-material==9.5.32; extra == "docs"
|
|
42
|
+
Requires-Dist: mkdocs-section-index==0.3.9; extra == "docs"
|
|
43
|
+
Requires-Dist: mkdocstrings==0.25.2; extra == "docs"
|
|
44
|
+
Requires-Dist: mkdocstrings-python==1.10.8; extra == "docs"
|
|
45
|
+
Requires-Dist: mike==2.1.3; extra == "docs"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# Academy-Dashboard
|
|
49
|
+
|
|
50
|
+
**Observability and Oversight for Academy Agents**
|
|
51
|
+
|
|
52
|
+
As agents act autonomously, the ability to observe what they are doing and intervene
|
|
53
|
+
when appropriate becomes a critical capability. `academy-dashboard` addresses these
|
|
54
|
+
challenges in agentic deployments through the following extensions for `Academy`:
|
|
55
|
+
|
|
56
|
+
* `UserAgent`: Specialized Academy agents that 1. accepts information from MonitoredAgents \
|
|
57
|
+
and 2. presents this information over a web based dashboard.
|
|
58
|
+
* `MonitoredAgent`: Extends academy's `Agent` class to support observability and control features.
|
|
59
|
+
|
|
60
|
+
Here are some of the supported capabilities:
|
|
61
|
+
|
|
62
|
+
* Logging: Live log stream from `MonitoredAgents` is presented via the `UserAgent`
|
|
63
|
+
* Human-in-the-loop: `MonitoredAgents` can send user prompts to the `UserAgent` where a user can respond over a webpage
|
|
64
|
+
* Performance Stats: Dashboard presents performance stats collected from `MonitoredAgents`
|
|
65
|
+
* Shutdown: `MonitoredAgent`s can be terminated from the dashboard provided by `UserAgents`
|
|
66
|
+
* Location: Locate your agents on a map
|
|
67
|
+
|
|
68
|
+
## Using the `UserAgent`
|
|
69
|
+
|
|
70
|
+
There are two supported modes for using starting a `UserAgent`:
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
#### `Hosted UserAgent`
|
|
74
|
+
|
|
75
|
+
>> Go to [dashboard.academy-agents.org](https://dashboard.academy-agents.org/)
|
|
76
|
+
|
|
77
|
+
1) Login with Globus Auth \
|
|
78
|
+
2) Click the `[+]` button to launch a new `UserAgent` \
|
|
79
|
+
3) Copy the agent UUID and plug this back into your scripts \
|
|
80
|
+
|
|
81
|
+
### `Local Deployment`:
|
|
82
|
+
|
|
83
|
+
Launch locally with: `user-agent-launcher`,
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
:>user-agent-launcher
|
|
87
|
+
User Agent UUID >>>>
|
|
88
|
+
a9337401-3d22-4205-923a-0040bb8d3a7b
|
|
89
|
+
* Serving Flask app 'academy_dashboard.user_agent.dashboard'
|
|
90
|
+
* Debug mode: off
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> NOTE: MonitoredAgents need the User Agent's UUID (printed above) to establish a connection.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
:> user-agent-launcher -h
|
|
98
|
+
usage: user-agent-launcher [-h] [-p PORT] [-r REGISTRATION_FILE] [-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
|
|
99
|
+
|
|
100
|
+
options:
|
|
101
|
+
-h, --help show this help message and exit
|
|
102
|
+
-p, --port PORT Port at which the flask service is listening
|
|
103
|
+
-r, --registration_file REGISTRATION_FILE
|
|
104
|
+
UserAgent registration file
|
|
105
|
+
-l, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
106
|
+
Log level
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Using the `MonitoredAgent`
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import os
|
|
113
|
+
|
|
114
|
+
USER_AGENT_ID=os.environ('USER_AGENT_UUID')
|
|
115
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Academy-Dashboard
|
|
2
|
+
|
|
3
|
+
**Observability and Oversight for Academy Agents**
|
|
4
|
+
|
|
5
|
+
As agents act autonomously, the ability to observe what they are doing and intervene
|
|
6
|
+
when appropriate becomes a critical capability. `academy-dashboard` addresses these
|
|
7
|
+
challenges in agentic deployments through the following extensions for `Academy`:
|
|
8
|
+
|
|
9
|
+
* `UserAgent`: Specialized Academy agents that 1. accepts information from MonitoredAgents \
|
|
10
|
+
and 2. presents this information over a web based dashboard.
|
|
11
|
+
* `MonitoredAgent`: Extends academy's `Agent` class to support observability and control features.
|
|
12
|
+
|
|
13
|
+
Here are some of the supported capabilities:
|
|
14
|
+
|
|
15
|
+
* Logging: Live log stream from `MonitoredAgents` is presented via the `UserAgent`
|
|
16
|
+
* Human-in-the-loop: `MonitoredAgents` can send user prompts to the `UserAgent` where a user can respond over a webpage
|
|
17
|
+
* Performance Stats: Dashboard presents performance stats collected from `MonitoredAgents`
|
|
18
|
+
* Shutdown: `MonitoredAgent`s can be terminated from the dashboard provided by `UserAgents`
|
|
19
|
+
* Location: Locate your agents on a map
|
|
20
|
+
|
|
21
|
+
## Using the `UserAgent`
|
|
22
|
+
|
|
23
|
+
There are two supported modes for using starting a `UserAgent`:
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#### `Hosted UserAgent`
|
|
27
|
+
|
|
28
|
+
>> Go to [dashboard.academy-agents.org](https://dashboard.academy-agents.org/)
|
|
29
|
+
|
|
30
|
+
1) Login with Globus Auth \
|
|
31
|
+
2) Click the `[+]` button to launch a new `UserAgent` \
|
|
32
|
+
3) Copy the agent UUID and plug this back into your scripts \
|
|
33
|
+
|
|
34
|
+
### `Local Deployment`:
|
|
35
|
+
|
|
36
|
+
Launch locally with: `user-agent-launcher`,
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
:>user-agent-launcher
|
|
40
|
+
User Agent UUID >>>>
|
|
41
|
+
a9337401-3d22-4205-923a-0040bb8d3a7b
|
|
42
|
+
* Serving Flask app 'academy_dashboard.user_agent.dashboard'
|
|
43
|
+
* Debug mode: off
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
> NOTE: MonitoredAgents need the User Agent's UUID (printed above) to establish a connection.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
:> user-agent-launcher -h
|
|
51
|
+
usage: user-agent-launcher [-h] [-p PORT] [-r REGISTRATION_FILE] [-l {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
|
|
52
|
+
|
|
53
|
+
options:
|
|
54
|
+
-h, --help show this help message and exit
|
|
55
|
+
-p, --port PORT Port at which the flask service is listening
|
|
56
|
+
-r, --registration_file REGISTRATION_FILE
|
|
57
|
+
UserAgent registration file
|
|
58
|
+
-l, --log_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
|
|
59
|
+
Log level
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Using the `MonitoredAgent`
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import os
|
|
66
|
+
|
|
67
|
+
USER_AGENT_ID=os.environ('USER_AGENT_UUID')
|
|
68
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Known bugs:
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Heartbeats don't work
|
|
5
|
+
[Pending]
|
|
6
|
+
The UserAgent treats every message from an agent as a heartbeat,
|
|
7
|
+
but this mechanism appears to be broken now.
|
|
8
|
+
We should switch over to using mailbox/agent liveness info provided
|
|
9
|
+
by the exchange directly.
|
|
10
|
+
|
|
11
|
+
# Shutdown doesn't work
|
|
12
|
+
[Pending]
|
|
13
|
+
The shutdown button on the agent cards no longer trigger agent shutdown
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Log issues
|
|
17
|
+
[Pending]
|
|
18
|
+
Logging issues are being worked out with the switch away from `init_logging()` to the `recommended` loggers from BenC.
|
|
19
|
+
The issue here is that logConfig objects are handled by the manager and we do not use a manager in the `user-agent-launcher`.
|
|
20
|
+
We use `http_exchange_client.register_agent -> make Runtime(registration=AgentReg)` which leaves out a point to plug in log configuration.
|
|
21
|
+
|
|
22
|
+
# HTTP Timeouts
|
|
23
|
+
[Addressed]
|
|
24
|
+
The following bug was addressed by upgrading academy-py to latest which has support for handling
|
|
25
|
+
aiohttp timeouts within the HTTPExchangeClient. The fix basically is:
|
|
26
|
+
|
|
27
|
+
> factory = HttpExchangeFactory(client_timeout=aiohttp.ClientTimeout(total=None, sock_connect=30))
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
[2026-05-15 23:35:57.671] ERROR (academy.logging) Background task raised an exception.
|
|
31
|
+
Traceback (most recent call last):
|
|
32
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/streams.py", line 372, in _wait
|
|
33
|
+
await waiter
|
|
34
|
+
asyncio.exceptions.CancelledError
|
|
35
|
+
|
|
36
|
+
The above exception was the direct cause of the following exception:
|
|
37
|
+
|
|
38
|
+
Traceback (most recent call last):
|
|
39
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/academy/logging.py", line 213, in execute_and_log_traceback
|
|
40
|
+
return await fut
|
|
41
|
+
^^^^^^^^^
|
|
42
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/academy/exchange/client.py", line 224, in _listen_for_messages
|
|
43
|
+
async for message in self._transport.listen():
|
|
44
|
+
...<10 lines>...
|
|
45
|
+
await asyncio.sleep(0)
|
|
46
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/academy/exchange/cloud/client.py", line 252, in listen
|
|
47
|
+
async for line_in_bytes in response.content:
|
|
48
|
+
...<11 lines>...
|
|
49
|
+
current_message_lines.append(line)
|
|
50
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/streams.py", line 53, in __anext__
|
|
51
|
+
rv = await self.read_func()
|
|
52
|
+
^^^^^^^^^^^^^^^^^^^^^^
|
|
53
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/streams.py", line 377, in readline
|
|
54
|
+
return await self.readuntil(max_size=max_line_length)
|
|
55
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
56
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/streams.py", line 414, in readuntil
|
|
57
|
+
await self._wait("readuntil")
|
|
58
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/streams.py", line 371, in _wait
|
|
59
|
+
with self._timer:
|
|
60
|
+
^^^^^^^^^^^
|
|
61
|
+
File "/Users/yadu/src/nexus/.venv/lib/python3.13/site-packages/aiohttp/helpers.py", line 713, in __exit__
|
|
62
|
+
raise asyncio.TimeoutError from exc_val
|
|
63
|
+
TimeoutError
|
|
64
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Observability components for agentic blueprints."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from academy_dashboard.monitored_agent import MonitoredAgent
|
|
6
|
+
from academy_dashboard.user_agent.user_agent import UserAgent
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'MonitoredAgent',
|
|
10
|
+
'UserAgent',
|
|
11
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from academy.agent import loop
|
|
7
|
+
from academy.handle import Handle
|
|
8
|
+
|
|
9
|
+
from academy_dashboard.monitored_agent import MonitoredAgent
|
|
10
|
+
from academy_dashboard.user_agent.user_agent import UserAgent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Sleeper(MonitoredAgent):
|
|
14
|
+
"""An Agent that sleeps."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, user_agent_handle: Handle[UserAgent]) -> None:
|
|
17
|
+
super().__init__(user_agent_handle=user_agent_handle)
|
|
18
|
+
print('Spinner init done')
|
|
19
|
+
|
|
20
|
+
@loop
|
|
21
|
+
async def cycle(self, shutdown: asyncio.Event) -> None:
|
|
22
|
+
"""Log and sleep in loop."""
|
|
23
|
+
counter = 0
|
|
24
|
+
while not shutdown.is_set():
|
|
25
|
+
await asyncio.sleep(30)
|
|
26
|
+
logging.info('Sleeper iteration %d', counter)
|
|
27
|
+
counter += 1
|
|
28
|
+
logging.info('Sleeper exiting!!!! ')
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from academy.agent import action
|
|
7
|
+
from academy.handle import Handle
|
|
8
|
+
|
|
9
|
+
from academy_dashboard.monitored_agent import MonitoredAgent
|
|
10
|
+
from academy_dashboard.user_agent.user_agent import UserAgent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Spinner(MonitoredAgent):
|
|
14
|
+
"""A concrete MonitoredAgent that does some work and reports progress."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, user_agent_handle: Handle[UserAgent]) -> None:
|
|
17
|
+
super().__init__(user_agent_handle=user_agent_handle)
|
|
18
|
+
print('Spinner init done')
|
|
19
|
+
|
|
20
|
+
@action
|
|
21
|
+
async def run(self, iterations: int = 3) -> str:
|
|
22
|
+
"""Simulate work and emit log messages that are forwarded to UserAgent."""
|
|
23
|
+
logging.info('Starting run!')
|
|
24
|
+
for i in range(iterations):
|
|
25
|
+
logging.info(
|
|
26
|
+
f'{self.agent_name} iteration %d/%d',
|
|
27
|
+
i + 1,
|
|
28
|
+
iterations,
|
|
29
|
+
)
|
|
30
|
+
await asyncio.sleep(10)
|
|
31
|
+
logging.warning('Finishing run!')
|
|
32
|
+
return f'Finished {iterations} iterations'
|
|
33
|
+
|
|
34
|
+
@action
|
|
35
|
+
async def poke(self) -> int:
|
|
36
|
+
"""Mock action that emits a log."""
|
|
37
|
+
logging.warning('I just got Poked!')
|
|
38
|
+
return 4
|
|
39
|
+
|
|
40
|
+
@action
|
|
41
|
+
async def trigger_user_query(self) -> None:
|
|
42
|
+
"""Trigger a user query."""
|
|
43
|
+
logging.info('Triggering user query!')
|
|
44
|
+
response = await self.prompt_user_agent(
|
|
45
|
+
'Should I continue?',
|
|
46
|
+
responses=['Yes', 'No'],
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
_ = 5 / 0
|
|
50
|
+
except ZeroDivisionError:
|
|
51
|
+
logging.exception('Something went wrong!')
|
|
52
|
+
logging.info(f'Got response: {response} from user query')
|