psi-agent 0.0.1a2__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.
Files changed (33) hide show
  1. psi_agent-0.0.1a2/.git +1 -0
  2. psi_agent-0.0.1a2/.github/workflows/ci.yml +90 -0
  3. psi_agent-0.0.1a2/.gitignore +67 -0
  4. psi_agent-0.0.1a2/CLAUDE.md +313 -0
  5. psi_agent-0.0.1a2/LICENSE.md +660 -0
  6. psi_agent-0.0.1a2/PKG-INFO +18 -0
  7. psi_agent-0.0.1a2/README.md +169 -0
  8. psi_agent-0.0.1a2/SPEC.md +573 -0
  9. psi_agent-0.0.1a2/examples/simple_example/AGENT.md +5 -0
  10. psi_agent-0.0.1a2/examples/simple_example/skills/example/SKILL.md +14 -0
  11. psi_agent-0.0.1a2/examples/simple_example/systems/builder.py +39 -0
  12. psi_agent-0.0.1a2/examples/simple_example/tools/bash.py +32 -0
  13. psi_agent-0.0.1a2/examples/simple_example/tools/read_file.py +26 -0
  14. psi_agent-0.0.1a2/pyproject.toml +57 -0
  15. psi_agent-0.0.1a2/src/psi_agent/__init__.py +1 -0
  16. psi_agent-0.0.1a2/src/psi_agent/ai/__init__.py +5 -0
  17. psi_agent-0.0.1a2/src/psi_agent/ai/openai/__init__.py +197 -0
  18. psi_agent-0.0.1a2/src/psi_agent/channel/__init__.py +5 -0
  19. psi_agent-0.0.1a2/src/psi_agent/channel/tui/__init__.py +117 -0
  20. psi_agent-0.0.1a2/src/psi_agent/common/__init__.py +5 -0
  21. psi_agent-0.0.1a2/src/psi_agent/common/protocol.py +49 -0
  22. psi_agent-0.0.1a2/src/psi_agent/session/__init__.py +522 -0
  23. psi_agent-0.0.1a2/src/psi_agent/workspace/__init__.py +556 -0
  24. psi_agent-0.0.1a2/tests/integration/__init__.py +1 -0
  25. psi_agent-0.0.1a2/tests/integration/test_ai_session.py +206 -0
  26. psi_agent-0.0.1a2/tests/integration/test_session_channel.py +289 -0
  27. psi_agent-0.0.1a2/tests/unit/__init__.py +1 -0
  28. psi_agent-0.0.1a2/tests/unit/test_ai.py +286 -0
  29. psi_agent-0.0.1a2/tests/unit/test_channel.py +176 -0
  30. psi_agent-0.0.1a2/tests/unit/test_protocol.py +109 -0
  31. psi_agent-0.0.1a2/tests/unit/test_session.py +870 -0
  32. psi_agent-0.0.1a2/tests/unit/test_workspace.py +396 -0
  33. psi_agent-0.0.1a2/uv.lock +842 -0
psi_agent-0.0.1a2/.git ADDED
@@ -0,0 +1 @@
1
+ gitdir: /home/hzhangxyz/Cloud/Desktop/psi/.git/worktrees/dev
@@ -0,0 +1,90 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ lint-and-unit-test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
13
+ fail-fast: false
14
+
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0 # Required for hatch-vcs versioning
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v5
23
+ with:
24
+ version: "latest"
25
+
26
+ - name: Install dependencies with Python ${{ matrix.python-version }}
27
+ run: uv sync --all-extras --python ${{ matrix.python-version }}
28
+
29
+ - name: Run ruff check
30
+ run: uv run ruff check examples/ tests/ src/
31
+
32
+ - name: Run ruff format check
33
+ run: uv run ruff format examples/ tests/ src/ --check
34
+
35
+ - name: Run ty type check
36
+ run: uv run ty check examples/ tests/ src/
37
+
38
+ - name: Run pytest (unit tests only)
39
+ run: uv run pytest tests/unit/ -v
40
+
41
+ integration-test:
42
+ runs-on: ubuntu-latest
43
+ needs: lint-and-unit-test
44
+
45
+ steps:
46
+ - name: Checkout
47
+ uses: actions/checkout@v4
48
+ with:
49
+ fetch-depth: 0 # Required for hatch-vcs versioning
50
+
51
+ - name: Install uv
52
+ uses: astral-sh/setup-uv@v5
53
+ with:
54
+ version: "latest"
55
+
56
+ - name: Install dependencies
57
+ run: uv sync --all-extras --python 3.12
58
+
59
+ - name: Run integration tests
60
+ env:
61
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
62
+ OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
63
+ OPENAI_MODEL: ${{ secrets.OPENAI_MODEL }}
64
+ run: uv run pytest tests/integration/ -v
65
+
66
+ publish:
67
+ runs-on: ubuntu-latest
68
+ needs: [lint-and-unit-test, integration-test]
69
+ if: startsWith(github.ref, 'refs/tags/v')
70
+ environment: pypi
71
+
72
+ permissions:
73
+ id-token: write # Required for trusted publishing
74
+
75
+ steps:
76
+ - name: Checkout
77
+ uses: actions/checkout@v4
78
+ with:
79
+ fetch-depth: 0 # Required for hatch-vcs versioning
80
+
81
+ - name: Install uv
82
+ uses: astral-sh/setup-uv@v5
83
+ with:
84
+ version: "latest"
85
+
86
+ - name: Build package
87
+ run: uv build
88
+
89
+ - name: Publish to PyPI
90
+ uses: pypa/gh-action-pypi-publish@release/v4
@@ -0,0 +1,67 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # uv
29
+ .uv/
30
+
31
+ # Sockets
32
+ *.sock
33
+
34
+ # Session state databases
35
+ example_workspace/state/*.db
36
+ *.db
37
+
38
+ # Workspace overlay directories
39
+ *.upper/
40
+ *.work/
41
+ *.lower/
42
+
43
+ # Manifest (runtime generated)
44
+ manifest.json
45
+
46
+ # IDE
47
+ .vscode/
48
+ .idea/
49
+ *.swp
50
+ *.swo
51
+ *~
52
+
53
+ # OS
54
+ .DS_Store
55
+ Thumbs.db
56
+
57
+ # Logs
58
+ *.log
59
+
60
+ # Test coverage
61
+ .coverage
62
+ coverage.xml
63
+ htmlcov/
64
+ .pytest_cache/
65
+
66
+ # SquashFS images
67
+ *.sqfs
@@ -0,0 +1,313 @@
1
+ # Psi Agent Platform
2
+
3
+ 一个绿色可移植的 AI Agent 平台,由独立的模块通过 Unix socket 通信组成。
4
+
5
+ ## 快速开始
6
+
7
+ ```bash
8
+ # 1. 启动 LLM Caller
9
+ uv run psi-ai-openai --session-socket ./ai.sock \
10
+ --model qwen3.5-plus \
11
+ --api-key $API_KEY \
12
+ --base-url https://coding.dashscope.aliyuncs.com/v1
13
+
14
+ # 2. 启动 Session
15
+ uv run psi-session --workspace ./examples/simple_example \
16
+ --channel-socket ./channel.sock \
17
+ --ai-socket ./ai.sock
18
+
19
+ # 3. 启动 TUI
20
+ uv run psi-channel-tui --session-socket ./channel.sock
21
+ ```
22
+
23
+ ## 项目结构
24
+
25
+ ```
26
+ src/
27
+ ├── psi_agent/ # Psi Agent 包
28
+ │ ├── session/ # ReAct 循环引擎(核心)
29
+ │ ├── channel/tui/ # TUI 用户界面
30
+ │ ├── ai/openai/ # OpenAI 兼容 LLM Caller
31
+ │ ├── workspace/ # SquashFS/OverlayFS 管理器
32
+ │ └── common/ # 共享协议
33
+
34
+ examples/
35
+ └── simple_example/ # 示例 workspace
36
+ ```
37
+
38
+ ## 核心概念
39
+
40
+ ### Workspace
41
+
42
+ 一个 workspace 是一个完整的 agent,包含:
43
+ - `AGENT.md`: 身份描述
44
+ - `tools/`: 工具(每个 .py 导出 `async run(params, workspace_path)`)
45
+ - `skills/`: 技能(SKILL.md 格式)
46
+ - `systems/builder.py`: 系统提示词构造器
47
+
48
+ ### 通信协议
49
+
50
+ 所有模块通过 Unix socket 通信,使用 JSON Lines 格式:
51
+
52
+ - Session ↔ AI: OpenAI Chat Completion 格式(流式)
53
+ - Session ↔ Channel: `{"role": "user/assistant", "content": "..."}`
54
+
55
+ ### ReAct 循环
56
+
57
+ Session 接收用户消息后:
58
+ 1. 构建 system prompt(通过 builder.py)
59
+ 2. 调用 LLM
60
+ 3. 如果有 tool_calls → 执行工具 → 继续循环
61
+ 4. 如果无 tool_calls → 返回文本给 Channel
62
+
63
+ ## 设计原则
64
+
65
+ ### Let it Crash
66
+
67
+ 核心原则:**除了外部网络故障,所有错误都应该让进程 crash**。
68
+
69
+ **分类说明:**
70
+
71
+ | 类型 | 处理方式 | 示例 |
72
+ |------|----------|------|
73
+ | 外部用户连接断开 | 优雅处理 | Channel断开(用户Ctrl+C/Ctrl+D) |
74
+ | 外部LLM API问题 | 优雅处理或crash | API超时、认证错误(让OpenAI SDK报错) |
75
+ | 组件间连接异常 | **crash** | AI Caller断开(未发送done)、Session断开 |
76
+ | 配置错误 | **crash** | API key缺失、文件不存在、参数无效 |
77
+ | 数据错误 | **crash** | JSON解析失败、schema不匹配 |
78
+ | 业务逻辑错误 | **crash** | 工具不存在、版本链断裂 |
79
+
80
+ **具体规则:**
81
+
82
+ 1. **启动时配置错误**: 不要额外检查,让系统自然crash并给出错误信息
83
+ - 例:API key缺失 → 让OpenAI SDK初始化时报错,不要提前检查并sys.exit()
84
+
85
+ 2. **组件间连接**: 假设连接正常,异常时直接crash
86
+ - 例:AI Caller发送流式响应但未发送`done`标记就断开 → Session应该crash(`RuntimeError`)
87
+
88
+ 3. **工具执行错误**: **不crash**,返回错误给LLM
89
+ - 工具级别错误(如命令不存在)应返回`{"success": False, "error": "..."}`
90
+ - Session继续运行,让LLM决定下一步
91
+
92
+ 4. **用户主动退出**: **优雅处理**
93
+ - KeyboardInterrupt/EOFError → 打印退出信息,清理资源,退出
94
+
95
+ 5. **可选功能缺失**: 优雅处理(warning/return),不crash
96
+ - tools目录不存在 → warning并继续
97
+ - skills目录不存在 → warning并继续
98
+ - snapshot无改动 → warning并return
99
+
100
+ ### 其他原则
101
+
102
+ - **绿色可移植**: workspace 可整体复制/移动
103
+ - **组件化**: 独立进程,socket 通信
104
+ - **不考虑兼容性**: 目前是第一版,不需要向后兼容,可直接删除旧代码
105
+
106
+ ## 日志
107
+
108
+ 所有模块使用 loguru:
109
+ ```
110
+ 2026-04-20 18:03:42 | INFO | session | Session initialized | id=test
111
+ ```
112
+
113
+ 控制级别:`--log-level DEBUG/INFO/WARNING/ERROR`
114
+
115
+ **TUI 特殊处理**:默认 `WARNING` 级别,避免日志干扰界面。
116
+
117
+ ## 数据模型
118
+
119
+ 使用 pydantic BaseModel:
120
+ - `LLMRequest`/`LLMResponse`: LLM 通信
121
+ - `ToolResult`: 工具结果
122
+ - `UserMessage`/`AssistantMessage`: Channel ↔ Session 通信
123
+ - `DeltaInfo`/`Manifest`/`MountInfo`: Workspace 元数据
124
+
125
+ **模型使用约定**:
126
+ - 所有协议通信应使用定义的模型(如 Channel 使用 `UserMessage`/`AssistantMessage` 而非 raw dict)
127
+ - 内部方法传递完整对象而非逐个提取字段
128
+
129
+ ## Session 默认行为
130
+
131
+ Session 默认使用自动生成的 uuid 作为 session_id,因此:
132
+ - **不提供 session_id 时**:每次启动都是新 session,无历史记录
133
+ - **提供 session_id 时**:继续该 session 的历史(如 `--session-id main`)
134
+
135
+ 这是为了让"默认行为无历史"更符合直觉。
136
+
137
+ ## 开发
138
+
139
+ 本仓库使用 **ruff** (lint/格式化) 和 **ty** (类型检查) 进行代码质量控制。
140
+
141
+ ```bash
142
+ # Lint 检查
143
+ uv run ruff check examples/ tests/ src/
144
+ uv run ruff check --fix examples/ tests/ src/
145
+
146
+ # 格式检查
147
+ uv run ruff format examples/ tests/ src/ --check
148
+
149
+ # 类型检查
150
+ uv run ty check examples/ tests/ src/
151
+
152
+ # 测试
153
+ uv run pytest tests/unit/ -v
154
+
155
+ # 集成测试(需要设置环境变量)
156
+ export OPENAI_API_KEY="your-api-key"
157
+ export OPENAI_BASE_URL="https://api.openai.com/v1"
158
+ export OPENAI_MODEL="gpt-4o-mini"
159
+ uv run pytest tests/integration/ -v
160
+ ```
161
+
162
+ **CI/CD**: GitHub Actions 自动运行 ruff check、ruff format、ty 和测试(`.github/workflows/test.yml`)。
163
+
164
+ **CLI**: 所有命令使用 tyro 实现,参数通过 dataclass 定义。
165
+
166
+ ## 常用命令
167
+
168
+ ```bash
169
+ # 安装依赖
170
+ uv sync
171
+
172
+ # 运行测试
173
+ uv run psi-session --workspace ./examples/simple_example ...
174
+
175
+ # Workspace 管理(使用 FUSE,无需 root)
176
+ # 先安装依赖: sudo apt install squashfuse fuse-overlayfs squashfs-tools
177
+ psi-workspace-create ./examples/simple_example base.sqfs --tag base
178
+ psi-workspace-mount base.sqfs ./workspace
179
+ psi-workspace-snapshot ./workspace v2.sqfs --tag v1
180
+ psi-workspace-umount ./workspace
181
+ ```
182
+
183
+ ## Python API
184
+
185
+ 所有模块同时提供 Python function 接口:
186
+
187
+ ```python
188
+ from psi_agent.session import run_session
189
+ from psi_agent.ai.openai import run_ai
190
+ from psi_agent.channel.tui import run_channel
191
+ from psi_agent.workspace import run_create, run_mount, run_umount, run_snapshot
192
+
193
+ # 启动 AI Caller
194
+ await run_ai(
195
+ session_socket="./ai.sock",
196
+ model="qwen3.5-plus",
197
+ api_key="...",
198
+ base_url="https://coding.dashscope.aliyuncs.com/v1",
199
+ log_level="INFO"
200
+ )
201
+
202
+ # 启动 Session
203
+ await run_session(
204
+ workspace_path="./examples/simple_example",
205
+ channel_socket="./channel.sock",
206
+ ai_socket="./ai.sock",
207
+ session_id="main",
208
+ log_level="INFO"
209
+ )
210
+
211
+ # 启动 TUI
212
+ await run_channel(session_socket="./channel.sock")
213
+
214
+ # Workspace 管理
215
+ await run_create("./examples/simple_example", "base.sqfs", tag="base")
216
+ await run_mount("base.sqfs", "./workspace")
217
+ await run_snapshot("./workspace", "v2.sqfs", tag="v1")
218
+ await run_umount("./workspace")
219
+
220
+ # Workspace 管理(需要 root)
221
+ await run_mount("agent.sqfs", "./workspace")
222
+ await run_unmount("./workspace")
223
+ await run_snapshot("./workspace", "new.sqfs", description="v2")
224
+ run_list("./workspace")
225
+ ```
226
+
227
+ ## 详细规格
228
+
229
+ 见 `SPEC.md`。
230
+
231
+ ## 代码风格
232
+
233
+ ### 类设计
234
+
235
+ - **内部状态用私有属性**: 类内部状态用 `_` 前缀(如 `_messages`, `_tools`, `_workspace_path`)
236
+ - **配置用 Pydantic**: 配置参数封装在 `SessionConfig` 等 Pydantic model 中
237
+ - **配置与实现分离**: 配置类只存参数,业务类接收配置对象
238
+
239
+ ### 函数设计
240
+
241
+ - **辅助函数独立**: 通用辅助函数独立定义(如 `_is_valid_tool_call_name`, `_load_python_module`)
242
+ - **函数有单一职责**: 每个函数只做一件事,名字描述职责
243
+ - **缓存复用**: 重复加载的资源缓存(如 `_builder_module`)
244
+ - **参数类型一致**: 私有方法参数类型应统一(如全部用 `Path` 而非混用 `str` 和 `Path`)
245
+ - **参数命名一致**: 内部变量名与参数名保持一致(如 `source_dir` → `source_dir`,不要改成 `source`)
246
+ - **数据模型统一**: 同一模块内的相似操作应使用相同的数据模型(如 `_handle_stream` 和 `_handle_non_stream` 都用 `LLMRequest` 对象)
247
+ - **传递对象而非字段**: 内部方法应传递完整的 request/response 对象,而非逐个提取字段传递(避免 adhoc 参数列表)
248
+ - **接口简洁**:
249
+ - 数据处理放在调用方而非被调用方(如JSON序列化在发送方完成)
250
+ - 类型转换尽量靠近数据源(如Path转换在参数入口处完成)
251
+ - **避免过度封装**: 简单逻辑不需要额外抽象,三行重复代码比过早抽象更好
252
+
253
+ ### 代码组织
254
+
255
+ - **模块顶部 import**: 所有 import 放文件顶部,不在函数内部 import
256
+ - **类型注解**: 所有函数参数和返回值有类型注解
257
+ - **docstring 简洁**: docstring 只说明功能,不写冗余说明
258
+
259
+ ### 日志风格
260
+
261
+ - **格式统一**: `logger.info("Action | key={value}")` 格式,用 `|` 分隔
262
+ - **级别合理**:
263
+ - `INFO`: 重要状态变化(初始化、连接、完成)
264
+ - `DEBUG`: 详细过程(参数、中间状态)
265
+ - `WARNING`: 预期内的异常情况
266
+ - `ERROR`: 错误和失败
267
+
268
+ ### 其他约定
269
+
270
+ - **避免 ad-hoc**: 不写重复逻辑,提取为辅助函数
271
+ - **f-string 有变量**: f-string 必须有插值,否则用普通字符串
272
+ - **is 比较**: `True/False/None` 用 `is` 比较,不用 `==`
273
+
274
+ ### 命名风格
275
+
276
+ #### CLI 参数命名
277
+
278
+ - **snake_case**: 所有 CLI 参数使用 snake_case(如 `channel_socket`, `log_level`)
279
+ - **位置参数**: 简短单词(如 `workspace`, `model`, `output`)
280
+ - **socket 参数**: `*_socket` 格式(如 `channel_socket`, `ai_socket`, `session_socket`)
281
+ - **log_level**: 所有模块统一有 `log_level` 参数
282
+
283
+ #### 文件命名
284
+
285
+ - **模块目录**: `psi_agent/<name>` 格式(如 `psi_agent/session`, `psi_agent/ai`, `psi_agent/channel`, `psi_agent/workspace`)
286
+ - **Python 文件**: snake_case(如 `builder.py`, `protocol.py`)
287
+ - **配置文件**: 小写无扩展名约定(如 `AGENT.md`, `SKILL.md`)
288
+
289
+ #### 变量命名
290
+
291
+ - **私有属性**: `_` 前缀(如 `_messages`, `_tools`, `_config`)
292
+ - **常量**: UPPER_CASE 或 snake_case(如 `MAX_ITERATIONS` 或 `max_iterations`)
293
+ - **函数**: snake_case(如 `load_tools`, `build_system_prompt`)
294
+ - **类**: PascalCase(如 `Session`, `SessionConfig`, `WorkspaceManager`)
295
+
296
+ #### Socket 命名
297
+
298
+ **变量命名(代码中):**
299
+ - `session_socket`: AI Caller 监听的 socket(Session 连接到此)
300
+ - `channel_socket`: Session 监听的 socket(Channel 连接到此)
301
+ - `ai_socket`: Session 连接 AI 的 socket(与 AI 的 `session_socket` 对应)
302
+
303
+ **文件命名(示例和测试):**
304
+ - `ai.sock`: AI Caller socket 文件
305
+ - `channel.sock`: Channel/Session socket 文件
306
+
307
+ **路径原则:**
308
+ - 生产环境:相对路径(如 `./channel.sock`, `./ai.sock`)
309
+ - 测试环境:pytest `tmp_path` fixture(如 `tmp_path / "channel.sock"`)
310
+
311
+ #### 数据库/JSON 字段
312
+
313
+ - **snake_case**: 与 Python 保持一致(如 `session_id`, `created_at`)