agex 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.
- agex-0.1.0/LICENSE +21 -0
- agex-0.1.0/PKG-INFO +114 -0
- agex-0.1.0/README.md +53 -0
- agex-0.1.0/agex/__init__.py +60 -0
- agex-0.1.0/agex/agent/__init__.py +86 -0
- agex-0.1.0/agex/agent/base.py +126 -0
- agex-0.1.0/agex/agent/console.py +369 -0
- agex-0.1.0/agex/agent/conversation.py +94 -0
- agex-0.1.0/agex/agent/datatypes.py +131 -0
- agex-0.1.0/agex/agent/events.py +613 -0
- agex-0.1.0/agex/agent/fingerprint.py +172 -0
- agex-0.1.0/agex/agent/formatting.py +88 -0
- agex-0.1.0/agex/agent/loop.py +514 -0
- agex-0.1.0/agex/agent/policy/__init__.py +27 -0
- agex-0.1.0/agex/agent/policy/datatypes.py +99 -0
- agex-0.1.0/agex/agent/policy/describe.py +449 -0
- agex-0.1.0/agex/agent/policy/policy.py +193 -0
- agex-0.1.0/agex/agent/policy/resolve.py +287 -0
- agex-0.1.0/agex/agent/primer_text.py +443 -0
- agex-0.1.0/agex/agent/registration.py +368 -0
- agex-0.1.0/agex/agent/task.py +486 -0
- agex-0.1.0/agex/agent/task.pyi +64 -0
- agex-0.1.0/agex/agent/task_messages.py +104 -0
- agex-0.1.0/agex/agent/utils.py +93 -0
- agex-0.1.0/agex/bench/__init__.py +78 -0
- agex-0.1.0/agex/bench/aggregators.py +110 -0
- agex-0.1.0/agex/bench/core.py +299 -0
- agex-0.1.0/agex/bench/types.py +106 -0
- agex-0.1.0/agex/eval/analysis.py +103 -0
- agex-0.1.0/agex/eval/arguments.py +102 -0
- agex-0.1.0/agex/eval/base.py +155 -0
- agex-0.1.0/agex/eval/binop.py +147 -0
- agex-0.1.0/agex/eval/builtins.py +548 -0
- agex-0.1.0/agex/eval/call.py +276 -0
- agex-0.1.0/agex/eval/comprehension.py +79 -0
- agex-0.1.0/agex/eval/constants.py +42 -0
- agex-0.1.0/agex/eval/core.py +126 -0
- agex-0.1.0/agex/eval/error.py +22 -0
- agex-0.1.0/agex/eval/expressions.py +124 -0
- agex-0.1.0/agex/eval/functions.py +392 -0
- agex-0.1.0/agex/eval/loops.py +102 -0
- agex-0.1.0/agex/eval/objects.py +337 -0
- agex-0.1.0/agex/eval/resolver.py +251 -0
- agex-0.1.0/agex/eval/safe.py +125 -0
- agex-0.1.0/agex/eval/statements.py +679 -0
- agex-0.1.0/agex/eval/user_errors.py +78 -0
- agex-0.1.0/agex/eval/utils.py +62 -0
- agex-0.1.0/agex/eval/validation.py +180 -0
- agex-0.1.0/agex/helpers/__init__.py +23 -0
- agex-0.1.0/agex/helpers/numpy_helper.py +45 -0
- agex-0.1.0/agex/helpers/pandas_helper.py +42 -0
- agex-0.1.0/agex/helpers/plotly_helper.py +54 -0
- agex-0.1.0/agex/helpers/stdlib.py +69 -0
- agex-0.1.0/agex/llm/__init__.py +88 -0
- agex-0.1.0/agex/llm/anthropic_client.py +159 -0
- agex-0.1.0/agex/llm/config.py +51 -0
- agex-0.1.0/agex/llm/core.py +102 -0
- agex-0.1.0/agex/llm/dummy_client.py +81 -0
- agex-0.1.0/agex/llm/gemini_client.py +148 -0
- agex-0.1.0/agex/llm/openai_client.py +94 -0
- agex-0.1.0/agex/py.typed +0 -0
- agex-0.1.0/agex/render/context.py +51 -0
- agex-0.1.0/agex/render/definitions.py +917 -0
- agex-0.1.0/agex/render/stream.py +222 -0
- agex-0.1.0/agex/render/value.py +387 -0
- agex-0.1.0/agex/render/view.py +87 -0
- agex-0.1.0/agex/state/__init__.py +79 -0
- agex-0.1.0/agex/state/closure.py +123 -0
- agex-0.1.0/agex/state/core.py +61 -0
- agex-0.1.0/agex/state/kv.py +163 -0
- agex-0.1.0/agex/state/live.py +47 -0
- agex-0.1.0/agex/state/log.py +67 -0
- agex-0.1.0/agex/state/namespaced.py +74 -0
- agex-0.1.0/agex/state/scoped.py +55 -0
- agex-0.1.0/agex/state/transient.py +97 -0
- agex-0.1.0/agex/state/versioned.py +272 -0
- agex-0.1.0/agex/tokenizers/__init__.py +11 -0
- agex-0.1.0/agex/tokenizers/core.py +16 -0
- agex-0.1.0/agex/tokenizers/tiktoken.py +20 -0
- agex-0.1.0/agex.egg-info/PKG-INFO +114 -0
- agex-0.1.0/agex.egg-info/SOURCES.txt +84 -0
- agex-0.1.0/agex.egg-info/dependency_links.txt +1 -0
- agex-0.1.0/agex.egg-info/requires.txt +43 -0
- agex-0.1.0/agex.egg-info/top_level.txt +2 -0
- agex-0.1.0/pyproject.toml +101 -0
- agex-0.1.0/setup.cfg +4 -0
agex-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ashenfad
|
|
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.
|
agex-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agex
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Library-friendly agents that work directly with your existing Python codebase.
|
|
5
|
+
Author: ashenfad
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://ashenfad.github.io/agex/
|
|
8
|
+
Project-URL: Documentation, https://ashenfad.github.io/agex/
|
|
9
|
+
Project-URL: Repository, https://github.com/ashenfad/agex
|
|
10
|
+
Project-URL: Issues, https://github.com/ashenfad/agex/issues
|
|
11
|
+
Keywords: agents,ai,llm,python,runtime,sandbox
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: tiktoken
|
|
25
|
+
Requires-Dist: xxhash
|
|
26
|
+
Requires-Dist: diskcache
|
|
27
|
+
Requires-Dist: pydantic
|
|
28
|
+
Requires-Dist: pygments
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: ruff; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Provides-Extra: test
|
|
34
|
+
Requires-Dist: pytest; extra == "test"
|
|
35
|
+
Requires-Dist: anyio; extra == "test"
|
|
36
|
+
Requires-Dist: numpy; extra == "test"
|
|
37
|
+
Requires-Dist: pandas; extra == "test"
|
|
38
|
+
Requires-Dist: anthropic; extra == "test"
|
|
39
|
+
Requires-Dist: google-generativeai; extra == "test"
|
|
40
|
+
Requires-Dist: openai; extra == "test"
|
|
41
|
+
Requires-Dist: matplotlib; extra == "test"
|
|
42
|
+
Provides-Extra: examples
|
|
43
|
+
Requires-Dist: plotly[express]; extra == "examples"
|
|
44
|
+
Requires-Dist: kaleido; extra == "examples"
|
|
45
|
+
Requires-Dist: numpy; extra == "examples"
|
|
46
|
+
Requires-Dist: nbformat; extra == "examples"
|
|
47
|
+
Requires-Dist: pandas; extra == "examples"
|
|
48
|
+
Requires-Dist: osmnx>=2.0.0; extra == "examples"
|
|
49
|
+
Requires-Dist: folium; extra == "examples"
|
|
50
|
+
Provides-Extra: openai
|
|
51
|
+
Requires-Dist: openai; extra == "openai"
|
|
52
|
+
Provides-Extra: anthropic
|
|
53
|
+
Requires-Dist: anthropic; extra == "anthropic"
|
|
54
|
+
Provides-Extra: gemini
|
|
55
|
+
Requires-Dist: google-generativeai; extra == "gemini"
|
|
56
|
+
Provides-Extra: all-providers
|
|
57
|
+
Requires-Dist: agex[openai]; extra == "all-providers"
|
|
58
|
+
Requires-Dist: agex[anthropic]; extra == "all-providers"
|
|
59
|
+
Requires-Dist: agex[gemini]; extra == "all-providers"
|
|
60
|
+
Dynamic: license-file
|
|
61
|
+
|
|
62
|
+
# agex: Library-Friendly Agents
|
|
63
|
+
|
|
64
|
+
**`agex`** (a portmanteau of **age**nt **ex**ecution) is a Python-native agentic framework that enables AI agents to work directly with your existing libraries and codebase.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
**This works because** `agex` agents can accept and return complex types like `pandas.DataFrame` and `plotly.Figure` objects without intermediate JSON serialization. For a deeper dive, check out the full **[agex101.ipynb tutorial](https://ashenfad.github.io/agex/examples/agex101/)** or see **[geospatial routing with OSMnx](https://ashenfad.github.io/agex/examples/routing/)** for advanced multi-library integration.
|
|
69
|
+
|
|
70
|
+
## What Makes This Different
|
|
71
|
+
|
|
72
|
+
`agex` uses a subset of Python as the agent action space, executing actions in a sandboxed environment within your process. This approach avoids the complexity of JSON serialization and allows complex objects to flow directly between your code and the agent. You control exactly what functions, classes, and modules are available, creating a powerful yet secure environment.
|
|
73
|
+
|
|
74
|
+
- **Code-as-Action**: Secure, sandboxed Python execution for agents.
|
|
75
|
+
- **Library Integration**: Use your existing code directly, no tool-making required.
|
|
76
|
+
- **Workspace Persistence**: Git-like versioning for agent state and memory.
|
|
77
|
+
- **Multi-Agent**: Orchestrate agents with natural Python control flow.
|
|
78
|
+
- **Event Streams**: Real-time, notebook-friendly observability.
|
|
79
|
+
- **Benchmarking**: A framework for data-driven agent evaluation.
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
Complete documentation is hosted at **[ashenfad.github.io/agex](https://ashenfad.github.io/agex/)**.
|
|
84
|
+
|
|
85
|
+
Key sections:
|
|
86
|
+
- **[📚 Quick Start Guide](https://ashenfad.github.io/agex/quick-start/)**
|
|
87
|
+
- **[🔭 The Big Picture](https://ashenfad.github.io/agex/concepts/big-picture/)**
|
|
88
|
+
- **[💡 Examples](https://ashenfad.github.io/agex/examples/overview/)**
|
|
89
|
+
- **[📖 API Reference](https://ashenfad.github.io/agex/api/overview/)**
|
|
90
|
+
|
|
91
|
+
## Installation
|
|
92
|
+
|
|
93
|
+
Install agex with your preferred LLM provider:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Install with a specific provider
|
|
97
|
+
pip install "agex[openai]" # For OpenAI models
|
|
98
|
+
pip install "agex[anthropic]" # For Anthropic Claude models
|
|
99
|
+
pip install "agex[gemini]" # For Google Gemini models
|
|
100
|
+
|
|
101
|
+
# Or install with all providers
|
|
102
|
+
pip install "agex[all-providers]"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Project Status
|
|
106
|
+
|
|
107
|
+
> **⚠️ Pre-Release**
|
|
108
|
+
> `agex` is a new framework in active development. While the core concepts are stabilizing, the API should be considered experimental and is subject to change.
|
|
109
|
+
|
|
110
|
+
For teams looking for a more battle-tested library built on the same "agents-that-think-in-code" philosophy, we highly recommend Hugging Face's excellent [`smolagents`](https://github.com/huggingface/smolagents) project. `agex` explores a different architectural path, focusing on deep runtime interoperability and a secure, sandboxed environment for direct integration with existing Python libraries.
|
|
111
|
+
|
|
112
|
+
## Contributing
|
|
113
|
+
|
|
114
|
+
We welcome contributions! See our [Contributing Guide](CONTRIBUTING.md) for details on our development workflow, code style, and how to submit pull requests. For bug reports and feature requests, please use [GitHub Issues](https://github.com/ashenfad/agex/issues).
|
agex-0.1.0/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# agex: Library-Friendly Agents
|
|
2
|
+
|
|
3
|
+
**`agex`** (a portmanteau of **age**nt **ex**ecution) is a Python-native agentic framework that enables AI agents to work directly with your existing libraries and codebase.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
**This works because** `agex` agents can accept and return complex types like `pandas.DataFrame` and `plotly.Figure` objects without intermediate JSON serialization. For a deeper dive, check out the full **[agex101.ipynb tutorial](https://ashenfad.github.io/agex/examples/agex101/)** or see **[geospatial routing with OSMnx](https://ashenfad.github.io/agex/examples/routing/)** for advanced multi-library integration.
|
|
8
|
+
|
|
9
|
+
## What Makes This Different
|
|
10
|
+
|
|
11
|
+
`agex` uses a subset of Python as the agent action space, executing actions in a sandboxed environment within your process. This approach avoids the complexity of JSON serialization and allows complex objects to flow directly between your code and the agent. You control exactly what functions, classes, and modules are available, creating a powerful yet secure environment.
|
|
12
|
+
|
|
13
|
+
- **Code-as-Action**: Secure, sandboxed Python execution for agents.
|
|
14
|
+
- **Library Integration**: Use your existing code directly, no tool-making required.
|
|
15
|
+
- **Workspace Persistence**: Git-like versioning for agent state and memory.
|
|
16
|
+
- **Multi-Agent**: Orchestrate agents with natural Python control flow.
|
|
17
|
+
- **Event Streams**: Real-time, notebook-friendly observability.
|
|
18
|
+
- **Benchmarking**: A framework for data-driven agent evaluation.
|
|
19
|
+
|
|
20
|
+
## Documentation
|
|
21
|
+
|
|
22
|
+
Complete documentation is hosted at **[ashenfad.github.io/agex](https://ashenfad.github.io/agex/)**.
|
|
23
|
+
|
|
24
|
+
Key sections:
|
|
25
|
+
- **[📚 Quick Start Guide](https://ashenfad.github.io/agex/quick-start/)**
|
|
26
|
+
- **[🔭 The Big Picture](https://ashenfad.github.io/agex/concepts/big-picture/)**
|
|
27
|
+
- **[💡 Examples](https://ashenfad.github.io/agex/examples/overview/)**
|
|
28
|
+
- **[📖 API Reference](https://ashenfad.github.io/agex/api/overview/)**
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install agex with your preferred LLM provider:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Install with a specific provider
|
|
36
|
+
pip install "agex[openai]" # For OpenAI models
|
|
37
|
+
pip install "agex[anthropic]" # For Anthropic Claude models
|
|
38
|
+
pip install "agex[gemini]" # For Google Gemini models
|
|
39
|
+
|
|
40
|
+
# Or install with all providers
|
|
41
|
+
pip install "agex[all-providers]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Project Status
|
|
45
|
+
|
|
46
|
+
> **⚠️ Pre-Release**
|
|
47
|
+
> `agex` is a new framework in active development. While the core concepts are stabilizing, the API should be considered experimental and is subject to change.
|
|
48
|
+
|
|
49
|
+
For teams looking for a more battle-tested library built on the same "agents-that-think-in-code" philosophy, we highly recommend Hugging Face's excellent [`smolagents`](https://github.com/huggingface/smolagents) project. `agex` explores a different architectural path, focusing on deep runtime interoperability and a secure, sandboxed environment for direct integration with existing Python libraries.
|
|
50
|
+
|
|
51
|
+
## Contributing
|
|
52
|
+
|
|
53
|
+
We welcome contributions! See our [Contributing Guide](CONTRIBUTING.md) for details on our development workflow, code style, and how to submit pull requests. For bug reports and feature requests, please use [GitHub Issues](https://github.com/ashenfad/agex/issues).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from .agent import (
|
|
2
|
+
Agent,
|
|
3
|
+
MemberSpec,
|
|
4
|
+
TaskFail,
|
|
5
|
+
clear_agent_registry,
|
|
6
|
+
)
|
|
7
|
+
from .agent.console import pprint_events
|
|
8
|
+
from .agent.datatypes import TaskClarify, TaskTimeout
|
|
9
|
+
from .agent.events import (
|
|
10
|
+
ActionEvent,
|
|
11
|
+
ClarifyEvent,
|
|
12
|
+
ErrorEvent,
|
|
13
|
+
Event,
|
|
14
|
+
FailEvent,
|
|
15
|
+
OutputEvent,
|
|
16
|
+
SuccessEvent,
|
|
17
|
+
TaskStartEvent,
|
|
18
|
+
)
|
|
19
|
+
from .llm import LLMClient, connect_llm
|
|
20
|
+
from .render.view import view
|
|
21
|
+
from .state import Live, Namespaced, Versioned, events
|
|
22
|
+
from .state.kv import Cache, Disk, Memory
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
# Core Classes
|
|
26
|
+
"Agent",
|
|
27
|
+
"LLMClient",
|
|
28
|
+
# State Management
|
|
29
|
+
"Versioned",
|
|
30
|
+
"Live",
|
|
31
|
+
"Namespaced",
|
|
32
|
+
"events",
|
|
33
|
+
# Task Control Exceptions & Functions
|
|
34
|
+
"TaskFail",
|
|
35
|
+
"TaskClarify",
|
|
36
|
+
"TaskTimeout",
|
|
37
|
+
# Registration
|
|
38
|
+
"MemberSpec",
|
|
39
|
+
# Events
|
|
40
|
+
"Event",
|
|
41
|
+
"TaskStartEvent",
|
|
42
|
+
"ActionEvent",
|
|
43
|
+
"OutputEvent",
|
|
44
|
+
"SuccessEvent",
|
|
45
|
+
"FailEvent",
|
|
46
|
+
"ClarifyEvent",
|
|
47
|
+
"ErrorEvent",
|
|
48
|
+
# Agent Registry
|
|
49
|
+
"clear_agent_registry",
|
|
50
|
+
# LLM Client Factory
|
|
51
|
+
"connect_llm",
|
|
52
|
+
# View
|
|
53
|
+
"view",
|
|
54
|
+
# KV backends
|
|
55
|
+
"Memory",
|
|
56
|
+
"Disk",
|
|
57
|
+
"Cache",
|
|
58
|
+
# Console
|
|
59
|
+
"pprint_events",
|
|
60
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Main agent functionality
|
|
2
|
+
from ..llm import LLMClient
|
|
3
|
+
from .base import BaseAgent, clear_agent_registry, register_agent, resolve_agent
|
|
4
|
+
|
|
5
|
+
# Data types and exceptions
|
|
6
|
+
from .datatypes import (
|
|
7
|
+
RESERVED_NAMES,
|
|
8
|
+
AttrDescriptor,
|
|
9
|
+
MemberSpec,
|
|
10
|
+
Pattern,
|
|
11
|
+
RegisteredClass,
|
|
12
|
+
RegisteredFn,
|
|
13
|
+
RegisteredItem,
|
|
14
|
+
RegisteredModule,
|
|
15
|
+
TaskContinue,
|
|
16
|
+
TaskFail,
|
|
17
|
+
TaskSuccess,
|
|
18
|
+
Visibility,
|
|
19
|
+
)
|
|
20
|
+
from .loop import TaskLoopMixin
|
|
21
|
+
|
|
22
|
+
# Fingerprinting (usually internal, but exported for testing)
|
|
23
|
+
from .registration import RegistrationMixin
|
|
24
|
+
from .task import TaskMixin, clear_dynamic_dataclass_registry
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Core functionality
|
|
28
|
+
"register_agent",
|
|
29
|
+
"resolve_agent",
|
|
30
|
+
"clear_agent_registry",
|
|
31
|
+
"clear_dynamic_dataclass_registry",
|
|
32
|
+
# Task control functions
|
|
33
|
+
"TaskSuccess",
|
|
34
|
+
"TaskFail",
|
|
35
|
+
"TaskContinue",
|
|
36
|
+
# Registration types
|
|
37
|
+
"MemberSpec",
|
|
38
|
+
"AttrDescriptor",
|
|
39
|
+
"RegisteredItem",
|
|
40
|
+
"RegisteredFn",
|
|
41
|
+
"RegisteredClass",
|
|
42
|
+
"RegisteredModule",
|
|
43
|
+
# Type aliases and constants
|
|
44
|
+
"Pattern",
|
|
45
|
+
"Visibility",
|
|
46
|
+
"RESERVED_NAMES",
|
|
47
|
+
# Fingerprinting
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Agent(RegistrationMixin, TaskMixin, TaskLoopMixin, BaseAgent):
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
primer: str | None = None,
|
|
55
|
+
timeout_seconds: float = 5.0,
|
|
56
|
+
max_iterations: int = 10,
|
|
57
|
+
max_tokens: int = 2**16,
|
|
58
|
+
# Agent identification
|
|
59
|
+
name: str | None = None,
|
|
60
|
+
# LLM configuration (optional, uses smart defaults)
|
|
61
|
+
llm_client: LLMClient | None = None,
|
|
62
|
+
# LLM retry controls
|
|
63
|
+
llm_max_retries: int = 2,
|
|
64
|
+
llm_retry_backoff: float = 0.25,
|
|
65
|
+
):
|
|
66
|
+
"""
|
|
67
|
+
An agent that can be used to execute tasks.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
primer: A string to guide the agent's behavior.
|
|
71
|
+
timeout_seconds: The maximum time in seconds for a single action evaluation.
|
|
72
|
+
max_iterations: The maximum number of think-act cycles for a task.
|
|
73
|
+
max_tokens: The maximum number of tokens to use for context rendering.
|
|
74
|
+
name: Unique identifier for this agent (for sub-agent namespacing).
|
|
75
|
+
llm_client: An instantiated LLMClient for the agent to use.
|
|
76
|
+
"""
|
|
77
|
+
super().__init__(
|
|
78
|
+
primer,
|
|
79
|
+
timeout_seconds,
|
|
80
|
+
max_iterations,
|
|
81
|
+
max_tokens,
|
|
82
|
+
name=name,
|
|
83
|
+
llm_client=llm_client,
|
|
84
|
+
llm_max_retries=llm_max_retries,
|
|
85
|
+
llm_retry_backoff=llm_retry_backoff,
|
|
86
|
+
)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Any, Callable, Dict, Literal
|
|
3
|
+
|
|
4
|
+
from ..llm import LLMClient, connect_llm
|
|
5
|
+
from .datatypes import (
|
|
6
|
+
MemberSpec,
|
|
7
|
+
RegisteredClass,
|
|
8
|
+
)
|
|
9
|
+
from .fingerprint import compute_agent_fingerprint_from_policy
|
|
10
|
+
from .policy.policy import AgentPolicy
|
|
11
|
+
|
|
12
|
+
# Global registry mapping fingerprints to agents
|
|
13
|
+
_AGENT_REGISTRY: Dict[str, "BaseAgent"] = {}
|
|
14
|
+
# Global registry mapping agent names to agents
|
|
15
|
+
_AGENT_REGISTRY_BY_NAME: Dict[str, "BaseAgent"] = {}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register_agent(agent: "BaseAgent") -> str:
|
|
19
|
+
"""
|
|
20
|
+
Register an agent in the global registry.
|
|
21
|
+
|
|
22
|
+
Returns the agent's fingerprint.
|
|
23
|
+
"""
|
|
24
|
+
# Enforce unique agent names if provided
|
|
25
|
+
if hasattr(agent, "name") and agent.name is not None:
|
|
26
|
+
if agent.name in _AGENT_REGISTRY_BY_NAME:
|
|
27
|
+
existing_agent = _AGENT_REGISTRY_BY_NAME[agent.name]
|
|
28
|
+
if existing_agent is not agent: # Allow re-registration of same agent
|
|
29
|
+
raise ValueError(f"Agent name '{agent.name}' already exists")
|
|
30
|
+
_AGENT_REGISTRY_BY_NAME[agent.name] = agent
|
|
31
|
+
|
|
32
|
+
fingerprint = compute_agent_fingerprint_from_policy(agent)
|
|
33
|
+
_AGENT_REGISTRY[fingerprint] = agent
|
|
34
|
+
return fingerprint
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def resolve_agent(fingerprint: str) -> "BaseAgent":
|
|
38
|
+
"""
|
|
39
|
+
Resolve an agent by its fingerprint.
|
|
40
|
+
|
|
41
|
+
Raises RuntimeError if no matching agent is found.
|
|
42
|
+
"""
|
|
43
|
+
agent = _AGENT_REGISTRY.get(fingerprint)
|
|
44
|
+
if not agent:
|
|
45
|
+
available = list(_AGENT_REGISTRY.keys())
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
f"No agent found with fingerprint '{fingerprint[:8]}...'. "
|
|
48
|
+
f"Available fingerprints: {[fp[:8] + '...' for fp in available]}"
|
|
49
|
+
)
|
|
50
|
+
return agent
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def clear_agent_registry() -> None:
|
|
54
|
+
"""Clear the global registry. Primarily for testing."""
|
|
55
|
+
from .task import clear_dynamic_dataclass_registry
|
|
56
|
+
|
|
57
|
+
global _AGENT_REGISTRY, _AGENT_REGISTRY_BY_NAME
|
|
58
|
+
_AGENT_REGISTRY = {}
|
|
59
|
+
_AGENT_REGISTRY_BY_NAME = {}
|
|
60
|
+
clear_dynamic_dataclass_registry()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _random_name() -> str:
|
|
64
|
+
return f"agent_{uuid.uuid4().hex[:8]}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class BaseAgent:
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
primer: str | None,
|
|
71
|
+
timeout_seconds: float,
|
|
72
|
+
max_iterations: int,
|
|
73
|
+
max_tokens: int,
|
|
74
|
+
# Agent identification
|
|
75
|
+
name: str | None = None,
|
|
76
|
+
# LLM configuration (optional, uses smart defaults)
|
|
77
|
+
llm_client: LLMClient | None = None,
|
|
78
|
+
# LLM retry controls
|
|
79
|
+
llm_max_retries: int = 2,
|
|
80
|
+
llm_retry_backoff: float = 0.25,
|
|
81
|
+
):
|
|
82
|
+
self.name = name or _random_name()
|
|
83
|
+
self.primer = primer
|
|
84
|
+
self.timeout_seconds = timeout_seconds
|
|
85
|
+
self.max_iterations = max_iterations
|
|
86
|
+
self.max_tokens = max_tokens
|
|
87
|
+
|
|
88
|
+
# Create LLM client using the resolved configuration
|
|
89
|
+
self.llm_client = llm_client or connect_llm()
|
|
90
|
+
# LLM retry settings
|
|
91
|
+
self.llm_max_retries = llm_max_retries
|
|
92
|
+
self.llm_retry_backoff = llm_retry_backoff
|
|
93
|
+
|
|
94
|
+
# private, host-side registry for live, unpickleable objects
|
|
95
|
+
self._host_object_registry: dict[str, Any] = {}
|
|
96
|
+
|
|
97
|
+
self._policy: AgentPolicy = AgentPolicy()
|
|
98
|
+
|
|
99
|
+
# Auto-register this agent
|
|
100
|
+
self.fingerprint = register_agent(self)
|
|
101
|
+
|
|
102
|
+
def _update_fingerprint(self):
|
|
103
|
+
"""Update the fingerprint after registration changes."""
|
|
104
|
+
self.fingerprint = register_agent(self)
|
|
105
|
+
|
|
106
|
+
def module(
|
|
107
|
+
self,
|
|
108
|
+
obj: Any,
|
|
109
|
+
*,
|
|
110
|
+
name: str | None = None,
|
|
111
|
+
visibility: Literal["high", "medium", "low"] = "medium",
|
|
112
|
+
include: list[str] | None = None,
|
|
113
|
+
exclude: list[str] | None = None,
|
|
114
|
+
configure: dict[str, MemberSpec | RegisteredClass] | None = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Stub implementation of module registration.
|
|
118
|
+
The full implementation with include/exclude support is in RegistrationMixin.
|
|
119
|
+
This method should not be called directly - use Agent class instead.
|
|
120
|
+
"""
|
|
121
|
+
raise NotImplementedError(
|
|
122
|
+
"This is a stub implementation. Use the Agent class which inherits from "
|
|
123
|
+
"RegistrationMixin for full include/exclude support."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def task(self, prompt: str | Callable) -> Callable[..., Any]: ...
|