xun-agent 1.0.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.
- xun_agent-1.0.0/PKG-INFO +216 -0
- xun_agent-1.0.0/README.md +189 -0
- xun_agent-1.0.0/pyproject.toml +54 -0
- xun_agent-1.0.0/pyproject.toml.orig +49 -0
- xun_agent-1.0.0/src/xun/__init__.py +25 -0
- xun_agent-1.0.0/src/xun/agent.py +295 -0
- xun_agent-1.0.0/src/xun/assets/conversation.template.html +134 -0
- xun_agent-1.0.0/src/xun/assets/login.template.html +36 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/fira-code-latin-400-normal-DGosTW8U.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/fira-code-latin-500-normal-B6gioGu8.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/index-B1sjzEzZ.js +70 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/index-DkGPJqYa.css +1 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/manrope-latin-400-normal-PaqtzbVb.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/manrope-latin-500-normal-BYYD-dBL.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/manrope-latin-600-normal-4f0koTD-.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/manrope-latin-700-normal-BZp_XxE4.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/assets/newsreader-latin-600-normal-30OJ_TG_.woff2 +0 -0
- xun_agent-1.0.0/src/xun/assets/web/index.html +14 -0
- xun_agent-1.0.0/src/xun/command.py +260 -0
- xun_agent-1.0.0/src/xun/compact.py +182 -0
- xun_agent-1.0.0/src/xun/config.py +190 -0
- xun_agent-1.0.0/src/xun/conversation.py +453 -0
- xun_agent-1.0.0/src/xun/display_abstract.py +306 -0
- xun_agent-1.0.0/src/xun/displays/__init__.py +14 -0
- xun_agent-1.0.0/src/xun/displays/display.py +191 -0
- xun_agent-1.0.0/src/xun/displays/null_display.py +14 -0
- xun_agent-1.0.0/src/xun/displays/web_display.py +361 -0
- xun_agent-1.0.0/src/xun/displays/web_file.py +319 -0
- xun_agent-1.0.0/src/xun/displays/web_service.py +423 -0
- xun_agent-1.0.0/src/xun/entrypoint.py +355 -0
- xun_agent-1.0.0/src/xun/error_catch.py +37 -0
- xun_agent-1.0.0/src/xun/hooks.py +121 -0
- xun_agent-1.0.0/src/xun/loop.py +243 -0
- xun_agent-1.0.0/src/xun/openai_helper.py +75 -0
- xun_agent-1.0.0/src/xun/prompt.py +59 -0
- xun_agent-1.0.0/src/xun/running_state.py +85 -0
- xun_agent-1.0.0/src/xun/store.py +49 -0
- xun_agent-1.0.0/src/xun/supervisor/__init__.py +3 -0
- xun_agent-1.0.0/src/xun/supervisor/cli.py +93 -0
- xun_agent-1.0.0/src/xun/supervisor/runtime.py +214 -0
- xun_agent-1.0.0/src/xun/supervisor/service.py +236 -0
- xun_agent-1.0.0/src/xun/supervisor/users.py +73 -0
- xun_agent-1.0.0/src/xun/toolbox.py +167 -0
- xun_agent-1.0.0/src/xun/toolcall.py +202 -0
- xun_agent-1.0.0/src/xun/tools/__init__.py +26 -0
- xun_agent-1.0.0/src/xun/tools/_extra.py +68 -0
- xun_agent-1.0.0/src/xun/tools/agent_factory.py +118 -0
- xun_agent-1.0.0/src/xun/tools/browser.py +788 -0
- xun_agent-1.0.0/src/xun/tools/cmd.py +604 -0
- xun_agent-1.0.0/src/xun/tools/common.py +272 -0
- xun_agent-1.0.0/src/xun/tools/diagnostic.py +152 -0
- xun_agent-1.0.0/src/xun/tools/framework.py +54 -0
- xun_agent-1.0.0/src/xun/tools/fs.py +441 -0
- xun_agent-1.0.0/src/xun/tools/patch.py +274 -0
- xun_agent-1.0.0/src/xun/tools/search.py +263 -0
- xun_agent-1.0.0/src/xun/tools/system.py +50 -0
- xun_agent-1.0.0/src/xun/types.py +93 -0
- xun_agent-1.0.0/src/xun/util.py +109 -0
- xun_agent-1.0.0/src/xun/workspace.py +103 -0
xun_agent-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: xun-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: An autonomous mini LLM agent with tooling.
|
|
5
|
+
Keywords: agent,llm,llm-agent
|
|
6
|
+
Author: limengxun
|
|
7
|
+
Author-email: limengxun <limengxun45@outlook.com>
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: aiohttp>=3.12.0
|
|
11
|
+
Requires-Dist: docker>=7.1.0
|
|
12
|
+
Requires-Dist: html-to-markdown>=3.12.2
|
|
13
|
+
Requires-Dist: jinja2>=3.1.6
|
|
14
|
+
Requires-Dist: json-repair>=0.63.4
|
|
15
|
+
Requires-Dist: markdown>=3.10.3
|
|
16
|
+
Requires-Dist: openai>=3.8.0
|
|
17
|
+
Requires-Dist: pillow>=12.3.0
|
|
18
|
+
Requires-Dist: playwright>=1.62.0
|
|
19
|
+
Requires-Dist: puremagic>=2.2.0
|
|
20
|
+
Requires-Dist: pydantic>=2.13.5
|
|
21
|
+
Requires-Dist: python-dotenv>=1.2.3
|
|
22
|
+
Requires-Dist: rich>=15.0.0
|
|
23
|
+
Requires-Dist: typing-extensions>=4.16.0
|
|
24
|
+
Requires-Dist: fastapi[standard]>=0.141.1
|
|
25
|
+
Requires-Python: >=3.12
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Xun
|
|
29
|
+
|
|
30
|
+
A mini LLM agent framework with function-based tools and sub-agent spawning.
|
|
31
|
+
|
|
32
|
+
The core codebase is compact: about 3000 lines in `src/xun/*.py` (mostly hand written), with comprehensive type hints.
|
|
33
|
+
|
|
34
|
+
<!-- <details>
|
|
35
|
+
<summary>Why this name?</summary>
|
|
36
|
+
|
|
37
|
+
取此名称有两点考虑。其一是技术含义:智能体的执行过程本身即一种搜索,在可
|
|
38
|
+
用工具与不断变化的会话状态中检索、试探与回退,直至任务收敛,模型侧的优化
|
|
39
|
+
过程同样可作此理解;其同音字“询”对应以对话问询驱动的交互方式,“训”对应以
|
|
40
|
+
指令与工具配置约束并扩展智能体行为的方式。
|
|
41
|
+
|
|
42
|
+
其二是使用便利:xun 为单音节三字母拼音,在命令行中无需切换输入法即可连续
|
|
43
|
+
键入,符合本软件以命令行与终端交互为主要入口的使用习惯。
|
|
44
|
+
|
|
45
|
+
</details> -->
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
Requires Python 3.12+ (PEP 695)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 1. Install dependencies
|
|
54
|
+
pip install git+https://github.com/MenxLi/xun.git
|
|
55
|
+
|
|
56
|
+
# 2. Install Playwright browsers (if using the default browser tools)
|
|
57
|
+
playwright install
|
|
58
|
+
|
|
59
|
+
# 3. Configure environment variables (see `Configuration` section below)
|
|
60
|
+
vim .env
|
|
61
|
+
|
|
62
|
+
# 4. Run the agent in interactive mode
|
|
63
|
+
xun
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Optionally, run the agent in web mode:
|
|
67
|
+
```sh
|
|
68
|
+
# - Build the web frontend (if using the web display)
|
|
69
|
+
make build-web
|
|
70
|
+
# - Start the web server at current directory
|
|
71
|
+
xuns .
|
|
72
|
+
# - Use a temporary workspace instead
|
|
73
|
+
xuns
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`xuns` accepts at most one workspace directory: every session shares it, or each session gets its own temporary workspace if omitted. Session management is on by default; disable it with `--no-manage-sessions`.
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
**Basic**: Quickly set up an agent with plain functions as tools — no decorators, no classes needed.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from xun import setup_agent
|
|
85
|
+
|
|
86
|
+
def add(a: int, b: int) -> int:
|
|
87
|
+
"""Add two numbers."""
|
|
88
|
+
return a + b
|
|
89
|
+
|
|
90
|
+
agent = setup_agent(tools = [add])
|
|
91
|
+
agent.instruct("Add 2 and 3.").execute()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Advanced**: The framework is flexible and extensible.
|
|
95
|
+
Additional features are shown in [demo.ipynb](demo.ipynb), including:
|
|
96
|
+
- `Agent` configuration
|
|
97
|
+
- Display extension
|
|
98
|
+
- Output validation
|
|
99
|
+
- Tool attributes
|
|
100
|
+
- Context injection
|
|
101
|
+
- Type-state transition
|
|
102
|
+
- Sub-agent spawning
|
|
103
|
+
- Lifecycle hooks
|
|
104
|
+
- ...
|
|
105
|
+
|
|
106
|
+
Do check out [demo.ipynb](demo.ipynb) for detailed examples.
|
|
107
|
+
|
|
108
|
+
## CLI
|
|
109
|
+
|
|
110
|
+
Run `xun` in your terminal to start an interactive session.
|
|
111
|
+
You can also pass a prompt as an argument to begin with a specific instruction.
|
|
112
|
+
```bash
|
|
113
|
+
xun "Write a hello world python script and save it to hello.py"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Image attachments are supported in the format of `[image:path_or_url]`. For example:
|
|
117
|
+
```
|
|
118
|
+
>>> [image:cat.png image:https://example.com/dog.png] compare them.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Input `/help` to see the full list of commands.
|
|
122
|
+
|
|
123
|
+
## Web
|
|
124
|
+
|
|
125
|
+
`web_session` starts the same managed web experience available through `xuns`:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from xun import web_session
|
|
129
|
+
|
|
130
|
+
web_session(workdir=".", base_path="/xun", manage_sessions=True)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`WebDisplay` provides lower-level access to the interactive web interface for custom service composition.
|
|
134
|
+
It can be used as a chat-based web application, or as a backend for other applications.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from xun import WebDisplay, WebDisplayService, setup_agent
|
|
138
|
+
|
|
139
|
+
display = WebDisplay(expose_files=True)
|
|
140
|
+
agent = setup_agent(display=display, default_tools=True)
|
|
141
|
+
service = WebDisplayService().mount("/", display)
|
|
142
|
+
service.start(blocking=True)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Open any tokenized URL printed at startup; the query token is exchanged for an HttpOnly cookie, so the browser reaches every mounted display without logging in again. API clients can use `Authorization: Bearer <token>`. File browsing, upload, download, and deletion require `expose_files=True`.
|
|
146
|
+
|
|
147
|
+
Multiple displays can share one authenticated service, each keeping its own agents, event history, and file policy:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
service = WebDisplayService()
|
|
151
|
+
service.mount("/research", research_display)
|
|
152
|
+
service.mount("/coding", coding_display)
|
|
153
|
+
service.start(blocking=True)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`display.build_routes()` and `display.build_app()` do not add authentication — use `WebDisplayService`, or provide your own in a custom ASGI host.
|
|
157
|
+
|
|
158
|
+
## Docker
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
make build-docker # builds the web frontend, then the `xun` image
|
|
162
|
+
|
|
163
|
+
xunc # sandbox: temporary workspace inside the container
|
|
164
|
+
xunc . # bind mount the current directory as /workspace
|
|
165
|
+
xunc --copy . # copy the current directory into /workspace instead
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`xunc` runs `xuns --host 0.0.0.0` in the container and publishes port 18960 (bridge mode), so the web UI is reachable from the host at the tokenized URL printed at startup. Other options: `--exec CMD` (e.g. `--exec bash`), `--port LIST`, `--network host` (avoid on macOS — not reachable from a host browser), `--env PATTERNS` (extra env vars to forward; `XUN_*`/`_XUN_*` are always forwarded), `--image` / `--name`.
|
|
169
|
+
|
|
170
|
+
### Multiplexed server
|
|
171
|
+
|
|
172
|
+
`xunx` runs one temporary container per registered user, proxied through one public server:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
xunx user-add alice # prints the access token
|
|
176
|
+
xunx user-list
|
|
177
|
+
xunx user-del alice # disconnects the user and removes its container
|
|
178
|
+
xunx serve --host 0.0.0.0 --port 18960 --port-range 20000-20100
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Open `http://localhost:18960/alice?token=TOKEN`. Users live in `$XUN_HOME/x/xunx.db`; container ports are drawn randomly from `--port-range` and bound to host loopback only. Workspaces are temporary, and managed containers are cleaned up on shutdown (stale ones on next start). `XUN_*`/`_XUN_*` env vars except `XUN_HOME` are forwarded into each container.
|
|
182
|
+
|
|
183
|
+
<details>
|
|
184
|
+
<summary>Frontend development</summary>
|
|
185
|
+
The frontend development command starts both the backend and Vite with Vue DevTools:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cd web
|
|
189
|
+
npm install
|
|
190
|
+
npm run dev
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Open `http://127.0.0.1:5173`. Build a production bundle with `npm run build`. See [web/README.md](web/README.md) for connecting the UI to a separately managed backend.
|
|
194
|
+
</details>
|
|
195
|
+
|
|
196
|
+
## Configuration
|
|
197
|
+
|
|
198
|
+
xun reads optional configuration from `.xun/config.json` (override the location with `XUN_HOME`); missing fields fall back to built-in defaults. Include only the fields you want to change, for example to override the model:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"model": {
|
|
203
|
+
"name": "my-model"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The config supports `${XUN_...}` placeholders which are substituted from environment variables (e.g. `${XUN_OPENAI_API_KEY}`), so secrets can live in a `.env` file instead. A placeholder with no matching environment variable causes a startup error.
|
|
209
|
+
|
|
210
|
+
| Config field | Environment variable | Description |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| `provider.openai_base_url` | `${XUN_OPENAI_BASE_URL}` | OpenAI-compatible API endpoint. |
|
|
213
|
+
| `provider.openai_api_key` | `${XUN_OPENAI_API_KEY}` | API key. |
|
|
214
|
+
| `model.name` | `${XUN_OPENAI_MODEL}` (empty) | Model identifier. If the resolved value is empty, available models are auto-detected from the API. |
|
|
215
|
+
|
|
216
|
+
More configuration options are available; see the source code at [src/xun/config.py](src/xun/config.py).
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Xun
|
|
2
|
+
|
|
3
|
+
A mini LLM agent framework with function-based tools and sub-agent spawning.
|
|
4
|
+
|
|
5
|
+
The core codebase is compact: about 3000 lines in `src/xun/*.py` (mostly hand written), with comprehensive type hints.
|
|
6
|
+
|
|
7
|
+
<!-- <details>
|
|
8
|
+
<summary>Why this name?</summary>
|
|
9
|
+
|
|
10
|
+
取此名称有两点考虑。其一是技术含义:智能体的执行过程本身即一种搜索,在可
|
|
11
|
+
用工具与不断变化的会话状态中检索、试探与回退,直至任务收敛,模型侧的优化
|
|
12
|
+
过程同样可作此理解;其同音字“询”对应以对话问询驱动的交互方式,“训”对应以
|
|
13
|
+
指令与工具配置约束并扩展智能体行为的方式。
|
|
14
|
+
|
|
15
|
+
其二是使用便利:xun 为单音节三字母拼音,在命令行中无需切换输入法即可连续
|
|
16
|
+
键入,符合本软件以命令行与终端交互为主要入口的使用习惯。
|
|
17
|
+
|
|
18
|
+
</details> -->
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
Requires Python 3.12+ (PEP 695)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Install dependencies
|
|
27
|
+
pip install git+https://github.com/MenxLi/xun.git
|
|
28
|
+
|
|
29
|
+
# 2. Install Playwright browsers (if using the default browser tools)
|
|
30
|
+
playwright install
|
|
31
|
+
|
|
32
|
+
# 3. Configure environment variables (see `Configuration` section below)
|
|
33
|
+
vim .env
|
|
34
|
+
|
|
35
|
+
# 4. Run the agent in interactive mode
|
|
36
|
+
xun
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Optionally, run the agent in web mode:
|
|
40
|
+
```sh
|
|
41
|
+
# - Build the web frontend (if using the web display)
|
|
42
|
+
make build-web
|
|
43
|
+
# - Start the web server at current directory
|
|
44
|
+
xuns .
|
|
45
|
+
# - Use a temporary workspace instead
|
|
46
|
+
xuns
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`xuns` accepts at most one workspace directory: every session shares it, or each session gets its own temporary workspace if omitted. Session management is on by default; disable it with `--no-manage-sessions`.
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
**Basic**: Quickly set up an agent with plain functions as tools — no decorators, no classes needed.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from xun import setup_agent
|
|
58
|
+
|
|
59
|
+
def add(a: int, b: int) -> int:
|
|
60
|
+
"""Add two numbers."""
|
|
61
|
+
return a + b
|
|
62
|
+
|
|
63
|
+
agent = setup_agent(tools = [add])
|
|
64
|
+
agent.instruct("Add 2 and 3.").execute()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Advanced**: The framework is flexible and extensible.
|
|
68
|
+
Additional features are shown in [demo.ipynb](demo.ipynb), including:
|
|
69
|
+
- `Agent` configuration
|
|
70
|
+
- Display extension
|
|
71
|
+
- Output validation
|
|
72
|
+
- Tool attributes
|
|
73
|
+
- Context injection
|
|
74
|
+
- Type-state transition
|
|
75
|
+
- Sub-agent spawning
|
|
76
|
+
- Lifecycle hooks
|
|
77
|
+
- ...
|
|
78
|
+
|
|
79
|
+
Do check out [demo.ipynb](demo.ipynb) for detailed examples.
|
|
80
|
+
|
|
81
|
+
## CLI
|
|
82
|
+
|
|
83
|
+
Run `xun` in your terminal to start an interactive session.
|
|
84
|
+
You can also pass a prompt as an argument to begin with a specific instruction.
|
|
85
|
+
```bash
|
|
86
|
+
xun "Write a hello world python script and save it to hello.py"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Image attachments are supported in the format of `[image:path_or_url]`. For example:
|
|
90
|
+
```
|
|
91
|
+
>>> [image:cat.png image:https://example.com/dog.png] compare them.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Input `/help` to see the full list of commands.
|
|
95
|
+
|
|
96
|
+
## Web
|
|
97
|
+
|
|
98
|
+
`web_session` starts the same managed web experience available through `xuns`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from xun import web_session
|
|
102
|
+
|
|
103
|
+
web_session(workdir=".", base_path="/xun", manage_sessions=True)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`WebDisplay` provides lower-level access to the interactive web interface for custom service composition.
|
|
107
|
+
It can be used as a chat-based web application, or as a backend for other applications.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from xun import WebDisplay, WebDisplayService, setup_agent
|
|
111
|
+
|
|
112
|
+
display = WebDisplay(expose_files=True)
|
|
113
|
+
agent = setup_agent(display=display, default_tools=True)
|
|
114
|
+
service = WebDisplayService().mount("/", display)
|
|
115
|
+
service.start(blocking=True)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Open any tokenized URL printed at startup; the query token is exchanged for an HttpOnly cookie, so the browser reaches every mounted display without logging in again. API clients can use `Authorization: Bearer <token>`. File browsing, upload, download, and deletion require `expose_files=True`.
|
|
119
|
+
|
|
120
|
+
Multiple displays can share one authenticated service, each keeping its own agents, event history, and file policy:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
service = WebDisplayService()
|
|
124
|
+
service.mount("/research", research_display)
|
|
125
|
+
service.mount("/coding", coding_display)
|
|
126
|
+
service.start(blocking=True)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`display.build_routes()` and `display.build_app()` do not add authentication — use `WebDisplayService`, or provide your own in a custom ASGI host.
|
|
130
|
+
|
|
131
|
+
## Docker
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
make build-docker # builds the web frontend, then the `xun` image
|
|
135
|
+
|
|
136
|
+
xunc # sandbox: temporary workspace inside the container
|
|
137
|
+
xunc . # bind mount the current directory as /workspace
|
|
138
|
+
xunc --copy . # copy the current directory into /workspace instead
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`xunc` runs `xuns --host 0.0.0.0` in the container and publishes port 18960 (bridge mode), so the web UI is reachable from the host at the tokenized URL printed at startup. Other options: `--exec CMD` (e.g. `--exec bash`), `--port LIST`, `--network host` (avoid on macOS — not reachable from a host browser), `--env PATTERNS` (extra env vars to forward; `XUN_*`/`_XUN_*` are always forwarded), `--image` / `--name`.
|
|
142
|
+
|
|
143
|
+
### Multiplexed server
|
|
144
|
+
|
|
145
|
+
`xunx` runs one temporary container per registered user, proxied through one public server:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
xunx user-add alice # prints the access token
|
|
149
|
+
xunx user-list
|
|
150
|
+
xunx user-del alice # disconnects the user and removes its container
|
|
151
|
+
xunx serve --host 0.0.0.0 --port 18960 --port-range 20000-20100
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Open `http://localhost:18960/alice?token=TOKEN`. Users live in `$XUN_HOME/x/xunx.db`; container ports are drawn randomly from `--port-range` and bound to host loopback only. Workspaces are temporary, and managed containers are cleaned up on shutdown (stale ones on next start). `XUN_*`/`_XUN_*` env vars except `XUN_HOME` are forwarded into each container.
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>Frontend development</summary>
|
|
158
|
+
The frontend development command starts both the backend and Vite with Vue DevTools:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
cd web
|
|
162
|
+
npm install
|
|
163
|
+
npm run dev
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Open `http://127.0.0.1:5173`. Build a production bundle with `npm run build`. See [web/README.md](web/README.md) for connecting the UI to a separately managed backend.
|
|
167
|
+
</details>
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
xun reads optional configuration from `.xun/config.json` (override the location with `XUN_HOME`); missing fields fall back to built-in defaults. Include only the fields you want to change, for example to override the model:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"model": {
|
|
176
|
+
"name": "my-model"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The config supports `${XUN_...}` placeholders which are substituted from environment variables (e.g. `${XUN_OPENAI_API_KEY}`), so secrets can live in a `.env` file instead. A placeholder with no matching environment variable causes a startup error.
|
|
182
|
+
|
|
183
|
+
| Config field | Environment variable | Description |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `provider.openai_base_url` | `${XUN_OPENAI_BASE_URL}` | OpenAI-compatible API endpoint. |
|
|
186
|
+
| `provider.openai_api_key` | `${XUN_OPENAI_API_KEY}` | API key. |
|
|
187
|
+
| `model.name` | `${XUN_OPENAI_MODEL}` (empty) | Model identifier. If the resolved value is empty, available models are auto-detected from the API. |
|
|
188
|
+
|
|
189
|
+
More configuration options are available; see the source code at [src/xun/config.py](src/xun/config.py).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xun-agent"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "An autonomous mini LLM agent with tooling."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
keywords = [
|
|
7
|
+
"agent",
|
|
8
|
+
"llm",
|
|
9
|
+
"llm-agent",
|
|
10
|
+
]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">=3.12"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"aiohttp>=3.12.0",
|
|
18
|
+
"docker>=7.1.0",
|
|
19
|
+
"html-to-markdown>=3.12.2",
|
|
20
|
+
"jinja2>=3.1.6",
|
|
21
|
+
"json-repair>=0.63.4",
|
|
22
|
+
"markdown>=3.10.3",
|
|
23
|
+
"openai>=3.8.0",
|
|
24
|
+
"pillow>=12.3.0",
|
|
25
|
+
"playwright>=1.62.0",
|
|
26
|
+
"puremagic>=2.2.0",
|
|
27
|
+
"pydantic>=2.13.5",
|
|
28
|
+
"python-dotenv>=1.2.3",
|
|
29
|
+
"rich>=15.0.0",
|
|
30
|
+
"typing-extensions>=4.16.0",
|
|
31
|
+
"fastapi[standard]>=0.141.1",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[[project.authors]]
|
|
35
|
+
name = "limengxun"
|
|
36
|
+
email = "limengxun45@outlook.com"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
xun = "xun:main"
|
|
40
|
+
xuns = "xun:main_serve"
|
|
41
|
+
xunc = "xun:main_container"
|
|
42
|
+
xunx = "xun.supervisor:main"
|
|
43
|
+
|
|
44
|
+
[[tool.uv.index]]
|
|
45
|
+
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
46
|
+
default = true
|
|
47
|
+
|
|
48
|
+
[tool.uv.build-backend]
|
|
49
|
+
module-root = "src"
|
|
50
|
+
module-name = "xun"
|
|
51
|
+
|
|
52
|
+
[build-system]
|
|
53
|
+
requires = ["uv_build"]
|
|
54
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xun-agent"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "An autonomous mini LLM agent with tooling."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
keywords = ["agent", "llm", "llm-agent"]
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Programming Language :: Python :: 3",
|
|
9
|
+
"Programming Language :: Python :: 3.12",
|
|
10
|
+
]
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "limengxun", email = "limengxun45@outlook.com" }
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"aiohttp>=3.12.0",
|
|
17
|
+
"docker>=7.1.0",
|
|
18
|
+
"html-to-markdown>=3.12.2",
|
|
19
|
+
"jinja2>=3.1.6",
|
|
20
|
+
"json-repair>=0.63.4",
|
|
21
|
+
"markdown>=3.10.3",
|
|
22
|
+
"openai>=3.8.0",
|
|
23
|
+
"pillow>=12.3.0",
|
|
24
|
+
"playwright>=1.62.0",
|
|
25
|
+
"puremagic>=2.2.0",
|
|
26
|
+
"pydantic>=2.13.5",
|
|
27
|
+
"python-dotenv>=1.2.3",
|
|
28
|
+
"rich>=15.0.0",
|
|
29
|
+
"typing-extensions>=4.16.0",
|
|
30
|
+
"fastapi[standard]>=0.141.1",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
xun = "xun:main"
|
|
35
|
+
xuns = "xun:main_serve"
|
|
36
|
+
xunc = "xun:main_container"
|
|
37
|
+
xunx = "xun.supervisor:main"
|
|
38
|
+
|
|
39
|
+
[[tool.uv.index]]
|
|
40
|
+
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
41
|
+
default = true
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["uv_build"]
|
|
45
|
+
build-backend = "uv_build"
|
|
46
|
+
|
|
47
|
+
[tool.uv.build-backend]
|
|
48
|
+
module-root = "src"
|
|
49
|
+
module-name = "xun"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .entrypoint import setup_agent, interactive_session, web_session, main, main_serve, main_container
|
|
2
|
+
from .display_abstract import DisplayAbstract
|
|
3
|
+
from .displays import Display, NullDisplay, WebDisplay, WebDisplayService
|
|
4
|
+
from .types import Result, ToolResultType, ErrorInfo, CancelledError
|
|
5
|
+
from .hooks import HookArgs, Hooks
|
|
6
|
+
from .command import Command, CommandRegistry
|
|
7
|
+
from .compact import CompactorAbstract, AutoCompactor
|
|
8
|
+
from .toolbox import ToolBox, ToolCallContext
|
|
9
|
+
from .toolcall import tool_attr
|
|
10
|
+
from .config import AgentConfig
|
|
11
|
+
from .workspace import Workspace
|
|
12
|
+
from .agent import Agent
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Agent", "AgentConfig",
|
|
16
|
+
"Workspace",
|
|
17
|
+
"tool_attr", "ToolBox", "ToolCallContext",
|
|
18
|
+
"DisplayAbstract", "Display", "NullDisplay", "WebDisplay", "WebDisplayService",
|
|
19
|
+
"Command", "CommandRegistry",
|
|
20
|
+
"HookArgs", "Hooks",
|
|
21
|
+
"CompactorAbstract", "AutoCompactor",
|
|
22
|
+
"setup_agent", "interactive_session", "web_session",
|
|
23
|
+
"main", "main_serve", "main_container",
|
|
24
|
+
"Result", "ToolResultType", "ErrorInfo", "CancelledError",
|
|
25
|
+
]
|