cave-agent 0.5.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.
- cave_agent-0.5.0/.gitignore +210 -0
- cave_agent-0.5.0/CONTRIBUTING.md +21 -0
- cave_agent-0.5.0/LICENSE +21 -0
- cave_agent-0.5.0/PKG-INFO +346 -0
- cave_agent-0.5.0/README.md +319 -0
- cave_agent-0.5.0/examples/basic_usage.py +112 -0
- cave_agent-0.5.0/examples/multi_turn.py +101 -0
- cave_agent-0.5.0/examples/object_methods.py +85 -0
- cave_agent-0.5.0/examples/runtime_state.py +54 -0
- cave_agent-0.5.0/examples/stream.py +89 -0
- cave_agent-0.5.0/pyproject.toml +56 -0
- cave_agent-0.5.0/src/cave_agent/__init__.py +25 -0
- cave_agent-0.5.0/src/cave_agent/agent.py +521 -0
- cave_agent-0.5.0/src/cave_agent/constant.py +1 -0
- cave_agent-0.5.0/src/cave_agent/models.py +159 -0
- cave_agent-0.5.0/src/cave_agent/prompts.py +116 -0
- cave_agent-0.5.0/src/cave_agent/python_runtime.py +263 -0
- cave_agent-0.5.0/src/cave_agent/security_checker.py +264 -0
- cave_agent-0.5.0/src/cave_agent/streaming_text_parser.py +276 -0
- cave_agent-0.5.0/src/cave_agent/utils.py +38 -0
- cave_agent-0.5.0/tests/__init__.py +0 -0
- cave_agent-0.5.0/tests/conftest.py +12 -0
- cave_agent-0.5.0/tests/test_basic_usage.py +40 -0
- cave_agent-0.5.0/tests/test_multi_turn.py +81 -0
- cave_agent-0.5.0/tests/test_object_methods.py +84 -0
- cave_agent-0.5.0/tests/test_python_runtime.py +88 -0
- cave_agent-0.5.0/tests/test_runtime_state.py +48 -0
- cave_agent-0.5.0/tests/test_security_checker.py +333 -0
- cave_agent-0.5.0/tests/test_streaming_text_parser.py +728 -0
- cave_agent-0.5.0/uv.lock +2545 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/macos,python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,python
|
|
3
|
+
|
|
4
|
+
### macOS ###
|
|
5
|
+
# General
|
|
6
|
+
.DS_Store
|
|
7
|
+
.AppleDouble
|
|
8
|
+
.LSOverride
|
|
9
|
+
|
|
10
|
+
# Icon must end with two \r
|
|
11
|
+
Icon
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Thumbnails
|
|
15
|
+
._*
|
|
16
|
+
|
|
17
|
+
# Files that might appear in the root of a volume
|
|
18
|
+
.DocumentRevisions-V100
|
|
19
|
+
.fseventsd
|
|
20
|
+
.Spotlight-V100
|
|
21
|
+
.TemporaryItems
|
|
22
|
+
.Trashes
|
|
23
|
+
.VolumeIcon.icns
|
|
24
|
+
.com.apple.timemachine.donotpresent
|
|
25
|
+
|
|
26
|
+
# Directories potentially created on remote AFP share
|
|
27
|
+
.AppleDB
|
|
28
|
+
.AppleDesktop
|
|
29
|
+
Network Trash Folder
|
|
30
|
+
Temporary Items
|
|
31
|
+
.apdisk
|
|
32
|
+
|
|
33
|
+
### macOS Patch ###
|
|
34
|
+
# iCloud generated files
|
|
35
|
+
*.icloud
|
|
36
|
+
|
|
37
|
+
### Python ###
|
|
38
|
+
# Byte-compiled / optimized / DLL files
|
|
39
|
+
__pycache__/
|
|
40
|
+
*.py[cod]
|
|
41
|
+
*$py.class
|
|
42
|
+
|
|
43
|
+
# C extensions
|
|
44
|
+
*.so
|
|
45
|
+
|
|
46
|
+
# Distribution / packaging
|
|
47
|
+
.Python
|
|
48
|
+
build/
|
|
49
|
+
develop-eggs/
|
|
50
|
+
dist/
|
|
51
|
+
downloads/
|
|
52
|
+
eggs/
|
|
53
|
+
.eggs/
|
|
54
|
+
lib/
|
|
55
|
+
lib64/
|
|
56
|
+
parts/
|
|
57
|
+
sdist/
|
|
58
|
+
var/
|
|
59
|
+
wheels/
|
|
60
|
+
share/python-wheels/
|
|
61
|
+
*.egg-info/
|
|
62
|
+
.installed.cfg
|
|
63
|
+
*.egg
|
|
64
|
+
MANIFEST
|
|
65
|
+
|
|
66
|
+
# PyInstaller
|
|
67
|
+
# Usually these files are written by a python script from a template
|
|
68
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
69
|
+
*.manifest
|
|
70
|
+
*.spec
|
|
71
|
+
|
|
72
|
+
# Installer logs
|
|
73
|
+
pip-log.txt
|
|
74
|
+
pip-delete-this-directory.txt
|
|
75
|
+
|
|
76
|
+
# Unit test / coverage reports
|
|
77
|
+
htmlcov/
|
|
78
|
+
.tox/
|
|
79
|
+
.nox/
|
|
80
|
+
.coverage
|
|
81
|
+
.coverage.*
|
|
82
|
+
.cache
|
|
83
|
+
nosetests.xml
|
|
84
|
+
coverage.xml
|
|
85
|
+
*.cover
|
|
86
|
+
*.py,cover
|
|
87
|
+
.hypothesis/
|
|
88
|
+
.pytest_cache/
|
|
89
|
+
cover/
|
|
90
|
+
|
|
91
|
+
# Translations
|
|
92
|
+
*.mo
|
|
93
|
+
*.pot
|
|
94
|
+
|
|
95
|
+
# Django stuff:
|
|
96
|
+
*.log
|
|
97
|
+
local_settings.py
|
|
98
|
+
db.sqlite3
|
|
99
|
+
db.sqlite3-journal
|
|
100
|
+
|
|
101
|
+
# Flask stuff:
|
|
102
|
+
instance/
|
|
103
|
+
.webassets-cache
|
|
104
|
+
|
|
105
|
+
# Scrapy stuff:
|
|
106
|
+
.scrapy
|
|
107
|
+
|
|
108
|
+
# Sphinx documentation
|
|
109
|
+
docs/_build/
|
|
110
|
+
|
|
111
|
+
# PyBuilder
|
|
112
|
+
.pybuilder/
|
|
113
|
+
target/
|
|
114
|
+
|
|
115
|
+
# Jupyter Notebook
|
|
116
|
+
.ipynb_checkpoints
|
|
117
|
+
|
|
118
|
+
# IPython
|
|
119
|
+
profile_default/
|
|
120
|
+
ipython_config.py
|
|
121
|
+
|
|
122
|
+
# pyenv
|
|
123
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
124
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
125
|
+
# .python-version
|
|
126
|
+
|
|
127
|
+
# pipenv
|
|
128
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
129
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
130
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
131
|
+
# install all needed dependencies.
|
|
132
|
+
#Pipfile.lock
|
|
133
|
+
|
|
134
|
+
# poetry
|
|
135
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
136
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
137
|
+
# commonly ignored for libraries.
|
|
138
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
139
|
+
#poetry.lock
|
|
140
|
+
|
|
141
|
+
# pdm
|
|
142
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
143
|
+
#pdm.lock
|
|
144
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
145
|
+
# in version control.
|
|
146
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
147
|
+
.pdm.toml
|
|
148
|
+
|
|
149
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
150
|
+
__pypackages__/
|
|
151
|
+
|
|
152
|
+
# Celery stuff
|
|
153
|
+
celerybeat-schedule
|
|
154
|
+
celerybeat.pid
|
|
155
|
+
|
|
156
|
+
# SageMath parsed files
|
|
157
|
+
*.sage.py
|
|
158
|
+
|
|
159
|
+
# Environments
|
|
160
|
+
.env
|
|
161
|
+
.env.*
|
|
162
|
+
.venv
|
|
163
|
+
env/
|
|
164
|
+
venv/
|
|
165
|
+
ENV/
|
|
166
|
+
env.bak/
|
|
167
|
+
venv.bak/
|
|
168
|
+
|
|
169
|
+
# Spyder project settings
|
|
170
|
+
.spyderproject
|
|
171
|
+
.spyproject
|
|
172
|
+
|
|
173
|
+
# Rope project settings
|
|
174
|
+
.ropeproject
|
|
175
|
+
|
|
176
|
+
# mkdocs documentation
|
|
177
|
+
/site
|
|
178
|
+
|
|
179
|
+
# mypy
|
|
180
|
+
.mypy_cache/
|
|
181
|
+
.dmypy.json
|
|
182
|
+
dmypy.json
|
|
183
|
+
|
|
184
|
+
# Pyre type checker
|
|
185
|
+
.pyre/
|
|
186
|
+
|
|
187
|
+
# pytype static type analyzer
|
|
188
|
+
.pytype/
|
|
189
|
+
|
|
190
|
+
# Cython debug symbols
|
|
191
|
+
cython_debug/
|
|
192
|
+
|
|
193
|
+
# PyCharm
|
|
194
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
195
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
196
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
197
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
198
|
+
.idea/
|
|
199
|
+
|
|
200
|
+
### Python Patch ###
|
|
201
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
202
|
+
poetry.toml
|
|
203
|
+
|
|
204
|
+
# ruff
|
|
205
|
+
.ruff_cache/
|
|
206
|
+
|
|
207
|
+
# LSP config files
|
|
208
|
+
pyrightconfig.json
|
|
209
|
+
|
|
210
|
+
# End of https://www.toptal.com/developers/gitignore/api/macos,python
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## Contribute to CaveAgent
|
|
2
|
+
|
|
3
|
+
We welcome contributions to CaveAgent! Here's how you can help:
|
|
4
|
+
|
|
5
|
+
### Types of Contributions
|
|
6
|
+
|
|
7
|
+
- Bug fixes
|
|
8
|
+
- Feature additions
|
|
9
|
+
- Documentation improvements
|
|
10
|
+
- Test coverage improvements
|
|
11
|
+
|
|
12
|
+
### Development Process
|
|
13
|
+
|
|
14
|
+
1. Fork the repository
|
|
15
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
16
|
+
3. Make your changes
|
|
17
|
+
4. Run tests to ensure everything works
|
|
18
|
+
5. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
19
|
+
6. Push to your branch (`git push origin feature/amazing-feature`)
|
|
20
|
+
7. Open a Pull Request
|
|
21
|
+
|
cave_agent-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ram, Copper
|
|
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,346 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cave-agent
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A Python agent framework that enables function-calling through LLM code generation
|
|
5
|
+
Project-URL: Homepage, https://github.com/acodercat/cave-agent
|
|
6
|
+
Project-URL: Repository, https://github.com/acodercat/cave-agent
|
|
7
|
+
Author-email: Ram <codermao@gmail.com>, Cooper <cooperimmaculate@gmail.com>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: agent,function-calling,llm
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: ipython>=9.0.2
|
|
18
|
+
Requires-Dist: rich>=13.9.4
|
|
19
|
+
Provides-Extra: all
|
|
20
|
+
Requires-Dist: litellm>=1.73.0; extra == 'all'
|
|
21
|
+
Requires-Dist: openai>=1.91.0; extra == 'all'
|
|
22
|
+
Provides-Extra: litellm
|
|
23
|
+
Requires-Dist: litellm>=1.73.0; extra == 'litellm'
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.91.0; extra == 'openai'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# 🤖 CaveAgent
|
|
29
|
+
**🚀 AI that executes, not just generates!**
|
|
30
|
+
|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
[](https://www.python.org/downloads/)
|
|
33
|
+
[](https://pypi.org/project/cave-agent)
|
|
34
|
+
|
|
35
|
+
CaveAgent is a tool-augmented agent framework that enables function-calling through LLM code generation and provides runtime state management. Unlike traditional JSON-schema approaches, it leverages LLM's inherent coding capabilities to interact with tools through a Python runtime environment, allowing direct access to execution results and runtime state.
|
|
36
|
+
|
|
37
|
+
> *"When your AI needs to run code, not just write it"*
|
|
38
|
+
|
|
39
|
+
## Why CaveAgent?
|
|
40
|
+
|
|
41
|
+
**Traditional function calling is broken.** JSON schemas are rigid, error-prone, and limit what your AI can do. CaveAgent unleashes your LLM's natural coding abilities:
|
|
42
|
+
|
|
43
|
+
- 🧠 **Native Code Generation** - LLMs excel at writing code, not parsing JSON
|
|
44
|
+
- ⚡ **Fewer Iterations** - Execute complex multi-step workflows in a single turn
|
|
45
|
+
- 🔄 **Persistent State** - Maintain variables and objects across conversations
|
|
46
|
+
- 🎯 **Maximum Flexibility** - Handle dynamic workflows that JSON schemas can't express
|
|
47
|
+
- 🛡️ **Secure by Design** - AST validation prevents dangerous code execution
|
|
48
|
+
- 📡 **Real-time Streaming** - Watch your AI think and execute in real-time
|
|
49
|
+
- 🌐 **Universal LLM Support** - Works with OpenAI, Anthropic, Google, and 100+ providers
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install 'cave-agent[all]'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Choose your installation:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# OpenAI support
|
|
61
|
+
pip install 'cave-agent[openai]'
|
|
62
|
+
|
|
63
|
+
# 100+ LLM providers via LiteLLM
|
|
64
|
+
pip install 'cave-agent[litellm]'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Simple Function Calling
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import asyncio
|
|
71
|
+
from cave_agent import CaveAgent
|
|
72
|
+
from cave_agent.models import OpenAIServerModel
|
|
73
|
+
from cave_agent.python_runtime import PythonRuntime, Function, Variable
|
|
74
|
+
|
|
75
|
+
async def main():
|
|
76
|
+
# Initialize LLM model
|
|
77
|
+
model = OpenAIServerModel(
|
|
78
|
+
model_id="your-model",
|
|
79
|
+
api_key="your-api-key",
|
|
80
|
+
base_url="your-base-url"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Define tool functions
|
|
84
|
+
def add_task(task_name: str) -> str:
|
|
85
|
+
"""Add a new task to the task list"""
|
|
86
|
+
tasks.append({"name": task_name, "done": False})
|
|
87
|
+
return f"Added task: {task_name}"
|
|
88
|
+
|
|
89
|
+
def complete_task(task_name: str) -> str:
|
|
90
|
+
"""Mark a task as completed"""
|
|
91
|
+
for task in tasks:
|
|
92
|
+
if task_name.lower() in task["name"].lower():
|
|
93
|
+
task["done"] = True
|
|
94
|
+
return f"Completed: {task['name']}"
|
|
95
|
+
return f"Task '{task_name}' not found"
|
|
96
|
+
|
|
97
|
+
def send_reminder(message: str) -> str:
|
|
98
|
+
"""Send a reminder notification"""
|
|
99
|
+
return f"Reminder: {message}"
|
|
100
|
+
|
|
101
|
+
# Initialize data
|
|
102
|
+
tasks = []
|
|
103
|
+
|
|
104
|
+
# Setup Runtime
|
|
105
|
+
runtime = PythonRuntime(
|
|
106
|
+
variables=[
|
|
107
|
+
Variable("tasks", tasks, "List of user's tasks. Example: [{'name': 'walk the dog', 'done': False}]")
|
|
108
|
+
],
|
|
109
|
+
functions=[
|
|
110
|
+
Function(add_task),
|
|
111
|
+
Function(complete_task),
|
|
112
|
+
Function(send_reminder)
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
agent = CaveAgent(model, runtime=runtime)
|
|
117
|
+
|
|
118
|
+
await agent.run("Add buy groceries and call mom to my tasks")
|
|
119
|
+
print(f"Current tasks: {runtime.get_variable_value('tasks')}")
|
|
120
|
+
|
|
121
|
+
await agent.run("Mark groceries done and remind me about mom")
|
|
122
|
+
print(f"Final state: {runtime.get_variable_value('tasks')}")
|
|
123
|
+
|
|
124
|
+
response = await agent.run("What's my progress?")
|
|
125
|
+
print(response.content)
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
asyncio.run(main())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Advanced: Stateful Object Interactions
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import asyncio
|
|
135
|
+
from cave_agent import CaveAgent
|
|
136
|
+
from cave_agent.models import LiteLLMModel
|
|
137
|
+
from cave_agent.python_runtime import PythonRuntime, Function, Variable
|
|
138
|
+
|
|
139
|
+
async def main():
|
|
140
|
+
# Initialize LLM model
|
|
141
|
+
model = LiteLLMModel(
|
|
142
|
+
model_id="your-model",
|
|
143
|
+
api_key="your-api-key",
|
|
144
|
+
base_url="your-base-url"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Define a class with methods
|
|
148
|
+
class DataProcessor:
|
|
149
|
+
"""A utility class for processing and filtering data collections.
|
|
150
|
+
|
|
151
|
+
This class provides methods for basic data processing operations such as
|
|
152
|
+
sorting, removing duplicates, and filtering based on thresholds.
|
|
153
|
+
|
|
154
|
+
Example:
|
|
155
|
+
>>> processor = DataProcessor()
|
|
156
|
+
>>> processor.process_list([3, 1, 2, 1, 3])
|
|
157
|
+
[1, 2, 3]
|
|
158
|
+
>>> processor.filter_numbers([1, 5, 3, 8, 2], 4)
|
|
159
|
+
[5, 8]
|
|
160
|
+
"""
|
|
161
|
+
def process_list(self, data: list) -> list:
|
|
162
|
+
"""Sort a list and remove duplicates"""
|
|
163
|
+
return sorted(set(data))
|
|
164
|
+
|
|
165
|
+
def filter_numbers(self, data: list, threshold: int) -> list:
|
|
166
|
+
"""Filter numbers greater than threshold"""
|
|
167
|
+
return [x for x in data if x > threshold]
|
|
168
|
+
|
|
169
|
+
# Prepare context
|
|
170
|
+
processor = DataProcessor()
|
|
171
|
+
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
|
|
172
|
+
|
|
173
|
+
# Create runtime with variables and functions
|
|
174
|
+
runtime = PythonRuntime(
|
|
175
|
+
variables=[
|
|
176
|
+
Variable(
|
|
177
|
+
name="processor",
|
|
178
|
+
value=processor,
|
|
179
|
+
description="Data processing tool with various methods"
|
|
180
|
+
),
|
|
181
|
+
Variable(
|
|
182
|
+
name="numbers",
|
|
183
|
+
value=numbers,
|
|
184
|
+
description="Input list of numbers"
|
|
185
|
+
),
|
|
186
|
+
Variable(
|
|
187
|
+
name="processed_data",
|
|
188
|
+
description="Store processed data in this variable"
|
|
189
|
+
),
|
|
190
|
+
Variable(
|
|
191
|
+
name="filtered_data",
|
|
192
|
+
description="Store filtered data in this variable"
|
|
193
|
+
)
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Create agent
|
|
198
|
+
agent = CaveAgent(model, runtime=runtime)
|
|
199
|
+
|
|
200
|
+
# Process data
|
|
201
|
+
await agent.run("Use processor to sort and deduplicate numbers")
|
|
202
|
+
processed_data = agent.runtime.get_variable_value('processed_data')
|
|
203
|
+
print("Processed data:", processed_data)
|
|
204
|
+
|
|
205
|
+
# Filter data
|
|
206
|
+
await agent.run("Filter numbers greater than 4")
|
|
207
|
+
filtered_data = agent.runtime.get_variable_value('filtered_data')
|
|
208
|
+
print("Filtered data:", filtered_data)
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
asyncio.run(main())
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Real-time Streaming
|
|
215
|
+
|
|
216
|
+
Watch your AI think and execute code in real-time:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
async for event in agent.stream_events("Analyze this data and create a summary"):
|
|
220
|
+
if event.type.value == 'CODE':
|
|
221
|
+
print(f"🔧 Executing: {event.content}")
|
|
222
|
+
elif event.type.value == 'EXECUTION_RESULT':
|
|
223
|
+
print(f"✅ Result: {event.content}")
|
|
224
|
+
elif event.type.value == 'TEXT':
|
|
225
|
+
print(event.content, end="", flush=True)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Security Features
|
|
229
|
+
|
|
230
|
+
CaveAgent includes rule-based security to prevent dangerous code execution:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
import asyncio
|
|
234
|
+
from cave_agent import CaveAgent
|
|
235
|
+
from cave_agent.models import OpenAIServerModel
|
|
236
|
+
from cave_agent.python_runtime import PythonRuntime
|
|
237
|
+
from cave_agent.security_checker import (
|
|
238
|
+
SecurityChecker, ImportRule, FunctionRule, AttributeRule, RegexRule
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
async def main():
|
|
242
|
+
model = OpenAIServerModel(
|
|
243
|
+
model_id="gpt-4",
|
|
244
|
+
api_key="your-api-key",
|
|
245
|
+
base_url="https://api.openai.com/v1"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Configure security with specific rules
|
|
249
|
+
rules = [
|
|
250
|
+
ImportRule({"os", "subprocess", "sys", "socket"}), # Block dangerous imports
|
|
251
|
+
FunctionRule({"eval", "exec", "compile", "open"}), # Block dangerous functions
|
|
252
|
+
AttributeRule({"__globals__", "__builtins__"}), # Block attribute access
|
|
253
|
+
RegexRule("no_print", "Block print statements", r"print\s*\(") # Custom regex
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
checker = SecurityChecker(rules)
|
|
257
|
+
runtime = PythonRuntime(security_checker=checker)
|
|
258
|
+
|
|
259
|
+
agent = CaveAgent(model, runtime=runtime)
|
|
260
|
+
|
|
261
|
+
# This will be blocked by security
|
|
262
|
+
try:
|
|
263
|
+
await agent.run("import os and list files")
|
|
264
|
+
except Exception as e:
|
|
265
|
+
print(f"Blocked: {e}")
|
|
266
|
+
|
|
267
|
+
if __name__ == "__main__":
|
|
268
|
+
asyncio.run(main())
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Key Features
|
|
272
|
+
|
|
273
|
+
- **🤖 Code-Based Function Calling**: Leverages LLM's natural coding abilities instead of rigid JSON schemas
|
|
274
|
+
- **🛡️ Secure Runtime Environment**:
|
|
275
|
+
- Inject Python objects, variables, and functions as tools
|
|
276
|
+
- Rule-based security validation prevents dangerous code execution
|
|
277
|
+
- Flexible security rules: ImportRule, FunctionRule, AttributeRule, RegexRule
|
|
278
|
+
- Customizable security policies for different use cases
|
|
279
|
+
- Access execution results and maintain state across interactions
|
|
280
|
+
- **💬 Multi-Turn Conversations**: Persistent context and runtime state across multiple interactions
|
|
281
|
+
- **⚡ Streaming & Async**: Real-time event streaming and full async/await support for optimal performance
|
|
282
|
+
- **🛡️ Execution Control**: Configurable step limits and error handling to prevent infinite loops
|
|
283
|
+
- **🎯 Unmatched Flexibility**: JSON schemas break with dynamic workflows. Python code adapts to any situation - conditional logic, loops, and complex data transformations.
|
|
284
|
+
- **🌐 Flexible LLM Support**: Works with any LLM provider via OpenAI-compatible APIs or LiteLLM
|
|
285
|
+
|
|
286
|
+
## Real-World Examples
|
|
287
|
+
|
|
288
|
+
For more examples, check out the [examples](examples) directory:
|
|
289
|
+
|
|
290
|
+
- [Basic Usage](examples/basic_usage.py): Simple function calling and object processing
|
|
291
|
+
- [Runtime State](examples/runtime_state.py): Managing runtime state across interactions
|
|
292
|
+
- [Object Methods](examples/object_methods.py): Using class methods and complex objects
|
|
293
|
+
- [Multi-Turn](examples/multi_turn.py): Complex analysis conversations with state persistence
|
|
294
|
+
- [Stream](examples/stream.py): Streaming responses and execution events
|
|
295
|
+
|
|
296
|
+
## LLM Provider Support
|
|
297
|
+
|
|
298
|
+
CaveAgent supports multiple LLM providers:
|
|
299
|
+
|
|
300
|
+
### OpenAI-Compatible Models
|
|
301
|
+
```python
|
|
302
|
+
from cave_agent.models import OpenAIServerModel
|
|
303
|
+
|
|
304
|
+
model = OpenAIServerModel(
|
|
305
|
+
model_id="gpt-4",
|
|
306
|
+
api_key="your-api-key",
|
|
307
|
+
base_url="https://api.openai.com/v1" # or your custom endpoint
|
|
308
|
+
)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### LiteLLM Models (Recommended)
|
|
312
|
+
LiteLLM provides unified access to hundreds of LLM providers:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from cave_agent.models import LiteLLMModel
|
|
316
|
+
|
|
317
|
+
# OpenAI
|
|
318
|
+
model = LiteLLMModel(
|
|
319
|
+
model_id="gpt-4",
|
|
320
|
+
api_key="your-api-key",
|
|
321
|
+
custom_llm_provider='openai'
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Anthropic Claude
|
|
325
|
+
model = LiteLLMModel(
|
|
326
|
+
model_id="claude-3-sonnet-20240229",
|
|
327
|
+
api_key="your-api-key",
|
|
328
|
+
custom_llm_provider='anthropic'
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Google Gemini
|
|
332
|
+
model = LiteLLMModel(
|
|
333
|
+
model_id="gemini/gemini-pro",
|
|
334
|
+
api_key="your-api-key"
|
|
335
|
+
)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
## Contributing
|
|
340
|
+
|
|
341
|
+
Contributions are welcome! Please feel free to submit a PR.
|
|
342
|
+
For more details, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
MIT License - see [LICENSE](LICENSE) for details.
|