base-agentkit 0.1.0__tar.gz → 0.1.2__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.
- base_agentkit-0.1.2/PKG-INFO +159 -0
- base_agentkit-0.1.2/README.md +132 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/pyproject.toml +2 -2
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/agent/agent.py +3 -2
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/config/loader.py +17 -1
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/config/schema.py +11 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/constants.py +4 -2
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/__init__.py +2 -1
- base_agentkit-0.1.2/src/agentkit/tools/loader.py +246 -0
- base_agentkit-0.1.0/PKG-INFO +0 -142
- base_agentkit-0.1.0/README.md +0 -115
- base_agentkit-0.1.0/src/agentkit/tools/loader.py +0 -81
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/LICENSE +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/agent/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/agent/budgets.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/agent/report.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/agent/tool_runtime.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/cli/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/cli/main.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/config/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/config/provider_defaults.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/errors.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/base.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/factory.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/anthropic_provider.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/gemini_provider.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/openai_provider.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/qwen_provider.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/vllm_provider.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/types.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/usage.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/py.typed +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/runlog/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/runlog/events.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/runlog/jsonl.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/runlog/recorder.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/runlog/sinks.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/base.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/_fs_common.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/create_file.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/fs_tools.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/str_replace.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/view.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/library/word_count.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/registry.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/tools/types.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/workspace/__init__.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/workspace/fs.py +0 -0
- {base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/workspace/layout.py +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: base-agentkit
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Workspace-isolated Python framework for building tool-using LLM agents.
|
|
5
|
+
Keywords: agents,llm,tool-calling,workspace,ai
|
|
6
|
+
Author: sii-nyc
|
|
7
|
+
Author-email: sii-nyc <253108120111@sii.edu.cn>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Dist: openai>=2.21.0
|
|
18
|
+
Requires-Dist: pydantic>=2.12.5
|
|
19
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
20
|
+
Requires-Dist: requests>=2.32.5
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Project-URL: Homepage, https://github.com/Inkjourney/agentkit
|
|
23
|
+
Project-URL: Repository, https://github.com/Inkjourney/agentkit
|
|
24
|
+
Project-URL: Documentation, https://inkjourney.github.io/agentkit/
|
|
25
|
+
Project-URL: Issues, https://github.com/Inkjourney/agentkit/issues
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# AgentKit
|
|
29
|
+
|
|
30
|
+
[中文版本](docs/README.zh-CN.md)
|
|
31
|
+
|
|
32
|
+
AgentKit is a general-purpose framework for tool-using LLM agents. It provides
|
|
33
|
+
workspace isolation, a unified LLM abstraction, a tool-execution loop, and both
|
|
34
|
+
CLI and Python SDK entry points.
|
|
35
|
+
|
|
36
|
+
## Current Capabilities
|
|
37
|
+
|
|
38
|
+
- Strong workspace isolation: all agent-visible file access goes through
|
|
39
|
+
`WorkspaceFS`
|
|
40
|
+
- Unified LLM abstraction:
|
|
41
|
+
`ConversationItem` / `ConversationState` / `UnifiedLLMRequest` /
|
|
42
|
+
`UnifiedLLMResponse`
|
|
43
|
+
- Provider support (sync, non-streaming, text)
|
|
44
|
+
- OpenAI (`responses` / `chat_completions`)
|
|
45
|
+
- Anthropic (`messages`)
|
|
46
|
+
- Gemini (`generateContent`)
|
|
47
|
+
- Qwen (OpenAI-compatible `chat_completions`)
|
|
48
|
+
- vLLM (OpenAI-compatible `chat_completions`)
|
|
49
|
+
- Registry-based tool system with built-in tools under
|
|
50
|
+
`src/agentkit/tools/library/` and custom tool loading from project
|
|
51
|
+
file/directory entries
|
|
52
|
+
- Agent loop that coordinates model reasoning and tool execution under step/time
|
|
53
|
+
budgets
|
|
54
|
+
- Run logs written to `workspace/logs/run_<run_id>.jsonl`
|
|
55
|
+
|
|
56
|
+
## Status
|
|
57
|
+
|
|
58
|
+
- The repository includes unit tests for the agent loop, CLI, tool system,
|
|
59
|
+
runlog, and the OpenAI / Anthropic / Gemini / Qwen / vLLM provider adapters
|
|
60
|
+
- There is no smoke-test coverage against live upstream APIs yet, so a final
|
|
61
|
+
end-to-end validation against the target model is still recommended before
|
|
62
|
+
production use
|
|
63
|
+
|
|
64
|
+
## Documentation
|
|
65
|
+
|
|
66
|
+
Full documentation lives under `docs/agentkit/`.
|
|
67
|
+
The published site is [GitHub Pages](https://inkjourney.github.io/agentkit/) and
|
|
68
|
+
deploys automatically from pushes to `main`.
|
|
69
|
+
|
|
70
|
+
## Install From Source
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv sync
|
|
74
|
+
uv run agentkit --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Install From PyPI
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install base-agentkit
|
|
81
|
+
agentkit --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The import path and CLI command stay the same after installation:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from agentkit import create_agent
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Example Configuration
|
|
91
|
+
|
|
92
|
+
### vLLM
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
workspace:
|
|
96
|
+
root: "./vllm_workspace"
|
|
97
|
+
|
|
98
|
+
provider:
|
|
99
|
+
kind: "vllm"
|
|
100
|
+
model: "glm-5"
|
|
101
|
+
openai_api_variant: "chat_completions"
|
|
102
|
+
conversation_mode: "auto"
|
|
103
|
+
base_url: "http://localhost:8000/v1"
|
|
104
|
+
api_key: "empty"
|
|
105
|
+
temperature: 0.8
|
|
106
|
+
timeout_s: 600
|
|
107
|
+
retries: 2
|
|
108
|
+
enable_thinking: true
|
|
109
|
+
|
|
110
|
+
agent:
|
|
111
|
+
system_prompt: "You are a helpful agent. Use tools when needed."
|
|
112
|
+
budget:
|
|
113
|
+
max_steps: 200
|
|
114
|
+
time_budget_s: 1800
|
|
115
|
+
max_input_chars: 180000
|
|
116
|
+
|
|
117
|
+
tools:
|
|
118
|
+
entries:
|
|
119
|
+
- "./tools"
|
|
120
|
+
allowed:
|
|
121
|
+
- "view"
|
|
122
|
+
- "create_file"
|
|
123
|
+
- "str_replace"
|
|
124
|
+
- "word_count"
|
|
125
|
+
|
|
126
|
+
runlog:
|
|
127
|
+
enabled: true
|
|
128
|
+
redact: true
|
|
129
|
+
max_text_chars: 20000
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Run From The CLI
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
export OPENAI_API_KEY="your-key"
|
|
136
|
+
uv run agentkit --config path/to/config.yaml run --task "List the files in the current workspace"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Python SDK
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from agentkit import create_agent
|
|
143
|
+
|
|
144
|
+
agent = create_agent("path/to/config.yaml")
|
|
145
|
+
report = agent.run("Create notes/todo.txt in the workspace and write today's plan")
|
|
146
|
+
print(report.final_output)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Project Layout
|
|
150
|
+
|
|
151
|
+
The core implementation lives under `src/agentkit/`:
|
|
152
|
+
|
|
153
|
+
- `config/`: configuration schema and loader
|
|
154
|
+
- `workspace/`: strict filesystem isolation and workspace layout
|
|
155
|
+
- `llm/`: unified abstractions, provider base/factory, and adapters
|
|
156
|
+
- `tools/`: tool abstractions, registry, and autoloading
|
|
157
|
+
- `agent/`: budgets, reports, runtime, and the agent loop
|
|
158
|
+
- `runlog/`: structured events, event sinks, and JSONL runlogs
|
|
159
|
+
- `cli/`: command-line entry point
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# AgentKit
|
|
2
|
+
|
|
3
|
+
[中文版本](docs/README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
AgentKit is a general-purpose framework for tool-using LLM agents. It provides
|
|
6
|
+
workspace isolation, a unified LLM abstraction, a tool-execution loop, and both
|
|
7
|
+
CLI and Python SDK entry points.
|
|
8
|
+
|
|
9
|
+
## Current Capabilities
|
|
10
|
+
|
|
11
|
+
- Strong workspace isolation: all agent-visible file access goes through
|
|
12
|
+
`WorkspaceFS`
|
|
13
|
+
- Unified LLM abstraction:
|
|
14
|
+
`ConversationItem` / `ConversationState` / `UnifiedLLMRequest` /
|
|
15
|
+
`UnifiedLLMResponse`
|
|
16
|
+
- Provider support (sync, non-streaming, text)
|
|
17
|
+
- OpenAI (`responses` / `chat_completions`)
|
|
18
|
+
- Anthropic (`messages`)
|
|
19
|
+
- Gemini (`generateContent`)
|
|
20
|
+
- Qwen (OpenAI-compatible `chat_completions`)
|
|
21
|
+
- vLLM (OpenAI-compatible `chat_completions`)
|
|
22
|
+
- Registry-based tool system with built-in tools under
|
|
23
|
+
`src/agentkit/tools/library/` and custom tool loading from project
|
|
24
|
+
file/directory entries
|
|
25
|
+
- Agent loop that coordinates model reasoning and tool execution under step/time
|
|
26
|
+
budgets
|
|
27
|
+
- Run logs written to `workspace/logs/run_<run_id>.jsonl`
|
|
28
|
+
|
|
29
|
+
## Status
|
|
30
|
+
|
|
31
|
+
- The repository includes unit tests for the agent loop, CLI, tool system,
|
|
32
|
+
runlog, and the OpenAI / Anthropic / Gemini / Qwen / vLLM provider adapters
|
|
33
|
+
- There is no smoke-test coverage against live upstream APIs yet, so a final
|
|
34
|
+
end-to-end validation against the target model is still recommended before
|
|
35
|
+
production use
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
Full documentation lives under `docs/agentkit/`.
|
|
40
|
+
The published site is [GitHub Pages](https://inkjourney.github.io/agentkit/) and
|
|
41
|
+
deploys automatically from pushes to `main`.
|
|
42
|
+
|
|
43
|
+
## Install From Source
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv sync
|
|
47
|
+
uv run agentkit --help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Install From PyPI
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install base-agentkit
|
|
54
|
+
agentkit --help
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The import path and CLI command stay the same after installation:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from agentkit import create_agent
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Example Configuration
|
|
64
|
+
|
|
65
|
+
### vLLM
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
workspace:
|
|
69
|
+
root: "./vllm_workspace"
|
|
70
|
+
|
|
71
|
+
provider:
|
|
72
|
+
kind: "vllm"
|
|
73
|
+
model: "glm-5"
|
|
74
|
+
openai_api_variant: "chat_completions"
|
|
75
|
+
conversation_mode: "auto"
|
|
76
|
+
base_url: "http://localhost:8000/v1"
|
|
77
|
+
api_key: "empty"
|
|
78
|
+
temperature: 0.8
|
|
79
|
+
timeout_s: 600
|
|
80
|
+
retries: 2
|
|
81
|
+
enable_thinking: true
|
|
82
|
+
|
|
83
|
+
agent:
|
|
84
|
+
system_prompt: "You are a helpful agent. Use tools when needed."
|
|
85
|
+
budget:
|
|
86
|
+
max_steps: 200
|
|
87
|
+
time_budget_s: 1800
|
|
88
|
+
max_input_chars: 180000
|
|
89
|
+
|
|
90
|
+
tools:
|
|
91
|
+
entries:
|
|
92
|
+
- "./tools"
|
|
93
|
+
allowed:
|
|
94
|
+
- "view"
|
|
95
|
+
- "create_file"
|
|
96
|
+
- "str_replace"
|
|
97
|
+
- "word_count"
|
|
98
|
+
|
|
99
|
+
runlog:
|
|
100
|
+
enabled: true
|
|
101
|
+
redact: true
|
|
102
|
+
max_text_chars: 20000
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Run From The CLI
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
export OPENAI_API_KEY="your-key"
|
|
109
|
+
uv run agentkit --config path/to/config.yaml run --task "List the files in the current workspace"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Python SDK
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from agentkit import create_agent
|
|
116
|
+
|
|
117
|
+
agent = create_agent("path/to/config.yaml")
|
|
118
|
+
report = agent.run("Create notes/todo.txt in the workspace and write today's plan")
|
|
119
|
+
print(report.final_output)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Project Layout
|
|
123
|
+
|
|
124
|
+
The core implementation lives under `src/agentkit/`:
|
|
125
|
+
|
|
126
|
+
- `config/`: configuration schema and loader
|
|
127
|
+
- `workspace/`: strict filesystem isolation and workspace layout
|
|
128
|
+
- `llm/`: unified abstractions, provider base/factory, and adapters
|
|
129
|
+
- `tools/`: tool abstractions, registry, and autoloading
|
|
130
|
+
- `agent/`: budgets, reports, runtime, and the agent loop
|
|
131
|
+
- `runlog/`: structured events, event sinks, and JSONL runlogs
|
|
132
|
+
- `cli/`: command-line entry point
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "base-agentkit"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Workspace-isolated Python framework for building tool-using LLM agents."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "sii-nyc", email = "253108120111@sii.edu.cn" }]
|
|
@@ -30,7 +30,7 @@ agentkit = "agentkit.cli.main:main"
|
|
|
30
30
|
[project.urls]
|
|
31
31
|
Homepage = "https://github.com/Inkjourney/agentkit"
|
|
32
32
|
Repository = "https://github.com/Inkjourney/agentkit"
|
|
33
|
-
Documentation = "https://github.
|
|
33
|
+
Documentation = "https://inkjourney.github.io/agentkit/"
|
|
34
34
|
Issues = "https://github.com/Inkjourney/agentkit/issues"
|
|
35
35
|
|
|
36
36
|
[dependency-groups]
|
|
@@ -25,7 +25,7 @@ from agentkit.llm.types import (
|
|
|
25
25
|
)
|
|
26
26
|
from agentkit.llm.usage import merge_usage, usage_to_payload
|
|
27
27
|
from agentkit.runlog import JsonlRunLogSink
|
|
28
|
-
from agentkit.tools.loader import load_tools_from_library
|
|
28
|
+
from agentkit.tools.loader import load_tools_from_entries, load_tools_from_library
|
|
29
29
|
from agentkit.tools.registry import ToolRegistry
|
|
30
30
|
from agentkit.runlog.recorder import RunRecorder
|
|
31
31
|
from agentkit.workspace.fs import WorkspaceFS
|
|
@@ -78,9 +78,10 @@ class Agent:
|
|
|
78
78
|
# Build the configured LLM provider
|
|
79
79
|
provider = build_provider(config.provider)
|
|
80
80
|
|
|
81
|
-
# Load tools
|
|
81
|
+
# Load built-in tools plus any configured external tool entries.
|
|
82
82
|
registry = ToolRegistry()
|
|
83
83
|
registry.register_many(load_tools_from_library(fs))
|
|
84
|
+
registry.register_many(load_tools_from_entries(config.tools.entries, fs))
|
|
84
85
|
|
|
85
86
|
# Create tool runtime with allowlist filtering
|
|
86
87
|
tool_runtime = AgentToolRuntime(registry, config.tools.allowed)
|
|
@@ -31,7 +31,8 @@ def load_config(
|
|
|
31
31
|
overrides: Mapping[str, Any] | None = None,
|
|
32
32
|
) -> AgentkitConfig:
|
|
33
33
|
"""Load, merge, expand, and validate framework configuration."""
|
|
34
|
-
|
|
34
|
+
config_path = Path(path).expanduser()
|
|
35
|
+
raw = _read_raw_config(config_path)
|
|
35
36
|
if overrides:
|
|
36
37
|
raw = _deep_merge(raw, dict(overrides))
|
|
37
38
|
raw = _expand_env(raw)
|
|
@@ -46,6 +47,10 @@ def load_config(
|
|
|
46
47
|
agent = AgentConfig(budget=budget, **agent_data)
|
|
47
48
|
|
|
48
49
|
tools = ToolConfig(**raw.get("tools", {}))
|
|
50
|
+
tools.entries = _resolve_tool_entries(
|
|
51
|
+
tools.entries,
|
|
52
|
+
base_dir=config_path.resolve(strict=False).parent,
|
|
53
|
+
)
|
|
49
54
|
runlog = RunLogConfig(**raw.get("runlog", {}))
|
|
50
55
|
except TypeError as exc:
|
|
51
56
|
raise ConfigError(f"Invalid configuration fields: {exc}") from exc
|
|
@@ -106,3 +111,14 @@ def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any
|
|
|
106
111
|
else:
|
|
107
112
|
merged[key] = value
|
|
108
113
|
return merged
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _resolve_tool_entries(entries: list[str], *, base_dir: Path) -> list[str]:
|
|
117
|
+
"""Resolve configured tool entry paths relative to the config file."""
|
|
118
|
+
resolved_entries: list[str] = []
|
|
119
|
+
for entry in entries:
|
|
120
|
+
candidate = Path(entry).expanduser()
|
|
121
|
+
if not candidate.is_absolute():
|
|
122
|
+
candidate = base_dir / candidate
|
|
123
|
+
resolved_entries.append(str(candidate.resolve(strict=False)))
|
|
124
|
+
return resolved_entries
|
|
@@ -122,6 +122,17 @@ class ToolConfig:
|
|
|
122
122
|
allowed: list[str] = field(default_factory=list)
|
|
123
123
|
entries: list[str] = field(default_factory=list)
|
|
124
124
|
|
|
125
|
+
def __post_init__(self) -> None:
|
|
126
|
+
"""Validate allowlist and external entry shapes."""
|
|
127
|
+
if not isinstance(self.allowed, list) or any(
|
|
128
|
+
not isinstance(name, str) or not name.strip() for name in self.allowed
|
|
129
|
+
):
|
|
130
|
+
raise ConfigError("tools.allowed must be a list of non-empty strings.")
|
|
131
|
+
if not isinstance(self.entries, list) or any(
|
|
132
|
+
not isinstance(entry, str) or not entry.strip() for entry in self.entries
|
|
133
|
+
):
|
|
134
|
+
raise ConfigError("tools.entries must be a list of non-empty strings.")
|
|
135
|
+
|
|
125
136
|
|
|
126
137
|
@dataclass(slots=True)
|
|
127
138
|
class RunLogConfig:
|
|
@@ -13,9 +13,11 @@ DEFAULT_TIME_BUDGET_S = 300
|
|
|
13
13
|
DEFAULT_MAX_INPUT_CHARS = 20_000
|
|
14
14
|
|
|
15
15
|
SENSITIVE_KEYS: tuple[str, ...] = (
|
|
16
|
+
"access_token",
|
|
16
17
|
"api_key",
|
|
17
18
|
"apikey",
|
|
18
|
-
"secret",
|
|
19
|
-
"password",
|
|
20
19
|
"authorization",
|
|
20
|
+
"password",
|
|
21
|
+
"refresh_token",
|
|
22
|
+
"secret",
|
|
21
23
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Public tool abstraction and registry exports."""
|
|
2
2
|
|
|
3
3
|
from .base import FunctionTool, Tool
|
|
4
|
-
from .loader import load_tools_from_library
|
|
4
|
+
from .loader import load_tools_from_entries, load_tools_from_library
|
|
5
5
|
from .registry import ToolRegistry
|
|
6
6
|
from .types import ToolCallOutcome, ToolInvocation, ToolModelError
|
|
7
7
|
|
|
@@ -12,5 +12,6 @@ __all__ = [
|
|
|
12
12
|
"ToolInvocation",
|
|
13
13
|
"ToolModelError",
|
|
14
14
|
"ToolRegistry",
|
|
15
|
+
"load_tools_from_entries",
|
|
15
16
|
"load_tools_from_library",
|
|
16
17
|
]
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Automatic loading for built-in and user-provided tool modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import importlib.util
|
|
7
|
+
import inspect
|
|
8
|
+
import itertools
|
|
9
|
+
import pkgutil
|
|
10
|
+
import sys
|
|
11
|
+
from importlib.machinery import ModuleSpec
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from types import ModuleType
|
|
14
|
+
from typing import Any, Iterable
|
|
15
|
+
|
|
16
|
+
from agentkit.errors import ToolError
|
|
17
|
+
from agentkit.tools.base import Tool
|
|
18
|
+
from agentkit.workspace.fs import WorkspaceFS
|
|
19
|
+
|
|
20
|
+
TOOLS_LIBRARY_PACKAGE = "agentkit.tools.library"
|
|
21
|
+
_USER_TOOLS_PACKAGE_PREFIX = "_agentkit_user_tools"
|
|
22
|
+
_USER_TOOLS_COUNTER = itertools.count()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_tools_from_library(fs: WorkspaceFS) -> list[Tool]:
|
|
26
|
+
"""Load all tools exposed by modules under ``agentkit.tools.library``.
|
|
27
|
+
|
|
28
|
+
Tool modules can expose tools via:
|
|
29
|
+
- ``build_tools(fs)`` (or ``build_tools()``)
|
|
30
|
+
- ``TOOLS`` module variable
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
fs: Workspace filesystem injected into callable tool factories.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
list[Tool]: Flattened list of tools from all library modules.
|
|
37
|
+
"""
|
|
38
|
+
package = importlib.import_module(TOOLS_LIBRARY_PACKAGE)
|
|
39
|
+
package_path = getattr(package, "__path__", None)
|
|
40
|
+
if package_path is None:
|
|
41
|
+
return []
|
|
42
|
+
|
|
43
|
+
loaded: list[Tool] = []
|
|
44
|
+
for module_info in sorted(pkgutil.iter_modules(package_path), key=lambda m: m.name):
|
|
45
|
+
if module_info.name.startswith("_"):
|
|
46
|
+
continue
|
|
47
|
+
module_name = f"{TOOLS_LIBRARY_PACKAGE}.{module_info.name}"
|
|
48
|
+
module = importlib.import_module(module_name)
|
|
49
|
+
loaded.extend(_load_from_module(module, fs, module_name=module_name))
|
|
50
|
+
return loaded
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_tools_from_entries(
|
|
54
|
+
entries: Iterable[str | Path], fs: WorkspaceFS
|
|
55
|
+
) -> list[Tool]:
|
|
56
|
+
"""Load tools from configured file or directory entries.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
entries: Filesystem paths pointing to Python files or directories that
|
|
60
|
+
contain Python tool modules.
|
|
61
|
+
fs: Workspace filesystem injected into callable tool factories.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
list[Tool]: Flattened list of tools discovered from all configured entries.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
agentkit.errors.ToolError: If an entry is missing, unsupported, or does not
|
|
68
|
+
define any tools.
|
|
69
|
+
"""
|
|
70
|
+
loaded: list[Tool] = []
|
|
71
|
+
package_names: dict[Path, str] = {}
|
|
72
|
+
for entry in entries:
|
|
73
|
+
path = Path(entry).expanduser().resolve(strict=False)
|
|
74
|
+
loaded.extend(_load_tools_from_entry(path, fs, package_names=package_names))
|
|
75
|
+
return loaded
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _load_from_module(module: Any, fs: WorkspaceFS, *, module_name: str) -> list[Tool]:
|
|
79
|
+
"""Extract tools from one library module."""
|
|
80
|
+
if hasattr(module, "build_tools"):
|
|
81
|
+
return _coerce_to_tools(getattr(module, "build_tools"), fs, module_name)
|
|
82
|
+
if hasattr(module, "TOOLS"):
|
|
83
|
+
return _coerce_to_tools(getattr(module, "TOOLS"), fs, module_name)
|
|
84
|
+
return []
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _coerce_to_tools(candidate: Any, fs: WorkspaceFS, module_name: str) -> list[Tool]:
|
|
88
|
+
"""Normalize supported tool declarations into ``list[Tool]``."""
|
|
89
|
+
if isinstance(candidate, Tool):
|
|
90
|
+
return [candidate]
|
|
91
|
+
|
|
92
|
+
if callable(candidate):
|
|
93
|
+
built: Any
|
|
94
|
+
try:
|
|
95
|
+
signature = inspect.signature(candidate)
|
|
96
|
+
if len(signature.parameters) == 0:
|
|
97
|
+
built = candidate()
|
|
98
|
+
else:
|
|
99
|
+
built = candidate(fs)
|
|
100
|
+
except ValueError:
|
|
101
|
+
built = candidate(fs)
|
|
102
|
+
return _coerce_to_tools(built, fs, module_name)
|
|
103
|
+
|
|
104
|
+
if isinstance(candidate, Iterable) and not isinstance(
|
|
105
|
+
candidate, (str, bytes, dict)
|
|
106
|
+
):
|
|
107
|
+
tools = list(candidate)
|
|
108
|
+
if all(isinstance(item, Tool) for item in tools):
|
|
109
|
+
return tools
|
|
110
|
+
|
|
111
|
+
raise ToolError(
|
|
112
|
+
f"Module '{module_name}' must expose Tool, Iterable[Tool], build_tools(...), or TOOLS."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _load_tools_from_entry(
|
|
117
|
+
path: Path,
|
|
118
|
+
fs: WorkspaceFS,
|
|
119
|
+
*,
|
|
120
|
+
package_names: dict[Path, str],
|
|
121
|
+
) -> list[Tool]:
|
|
122
|
+
"""Load tools from one configured filesystem entry."""
|
|
123
|
+
if not path.exists():
|
|
124
|
+
raise ToolError(f"Tool entry not found: {path}")
|
|
125
|
+
if path.is_file():
|
|
126
|
+
return _load_tools_from_file_entry(path, fs, package_names=package_names)
|
|
127
|
+
if path.is_dir():
|
|
128
|
+
return _load_tools_from_directory_entry(path, fs, package_names=package_names)
|
|
129
|
+
raise ToolError(f"Unsupported tool entry type: {path}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _load_tools_from_file_entry(
|
|
133
|
+
path: Path,
|
|
134
|
+
fs: WorkspaceFS,
|
|
135
|
+
*,
|
|
136
|
+
package_names: dict[Path, str],
|
|
137
|
+
) -> list[Tool]:
|
|
138
|
+
"""Load tools from one Python file entry."""
|
|
139
|
+
if path.suffix != ".py":
|
|
140
|
+
raise ToolError(f"Tool entry file must be a Python module: {path}")
|
|
141
|
+
|
|
142
|
+
package_name = _ensure_entry_package(path.parent, package_names=package_names)
|
|
143
|
+
module_name = f"{package_name}.{_sanitize_module_name(path.stem)}"
|
|
144
|
+
module = _load_module_from_path(path, module_name=module_name)
|
|
145
|
+
tools = _load_from_module(module, fs, module_name=str(path))
|
|
146
|
+
if not tools:
|
|
147
|
+
raise ToolError(f"Tool entry did not define any tools: {path}")
|
|
148
|
+
return tools
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _load_tools_from_directory_entry(
|
|
152
|
+
path: Path,
|
|
153
|
+
fs: WorkspaceFS,
|
|
154
|
+
*,
|
|
155
|
+
package_names: dict[Path, str],
|
|
156
|
+
) -> list[Tool]:
|
|
157
|
+
"""Load tools from all eligible Python modules in one directory entry."""
|
|
158
|
+
package_name = _ensure_entry_package(path, package_names=package_names)
|
|
159
|
+
package_module = sys.modules[package_name]
|
|
160
|
+
loaded = _load_from_module(package_module, fs, module_name=str(path / "__init__.py"))
|
|
161
|
+
|
|
162
|
+
for child in sorted(path.iterdir(), key=lambda item: item.name):
|
|
163
|
+
if not child.is_file():
|
|
164
|
+
continue
|
|
165
|
+
if child.suffix != ".py":
|
|
166
|
+
continue
|
|
167
|
+
if child.name == "__init__.py" or child.name.startswith("_"):
|
|
168
|
+
continue
|
|
169
|
+
module_name = f"{package_name}.{_sanitize_module_name(child.stem)}"
|
|
170
|
+
module = _load_module_from_path(child, module_name=module_name)
|
|
171
|
+
loaded.extend(_load_from_module(module, fs, module_name=str(child)))
|
|
172
|
+
|
|
173
|
+
if not loaded:
|
|
174
|
+
raise ToolError(f"Tool entry did not define any tools: {path}")
|
|
175
|
+
return loaded
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _ensure_entry_package(path: Path, *, package_names: dict[Path, str]) -> str:
|
|
179
|
+
"""Create or reuse a synthetic package namespace for one filesystem root."""
|
|
180
|
+
root = path.resolve(strict=False)
|
|
181
|
+
existing = package_names.get(root)
|
|
182
|
+
if existing is not None:
|
|
183
|
+
return existing
|
|
184
|
+
|
|
185
|
+
package_name = _next_dynamic_package_name(root)
|
|
186
|
+
init_file = root / "__init__.py"
|
|
187
|
+
if init_file.is_file():
|
|
188
|
+
_load_module_from_path(
|
|
189
|
+
init_file,
|
|
190
|
+
module_name=package_name,
|
|
191
|
+
submodule_search_locations=[str(root)],
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
module = ModuleType(package_name)
|
|
195
|
+
module.__file__ = str(root)
|
|
196
|
+
module.__package__ = package_name
|
|
197
|
+
module.__path__ = [str(root)] # type: ignore[attr-defined]
|
|
198
|
+
spec = ModuleSpec(package_name, loader=None, is_package=True)
|
|
199
|
+
spec.submodule_search_locations = [str(root)]
|
|
200
|
+
module.__spec__ = spec
|
|
201
|
+
sys.modules[package_name] = module
|
|
202
|
+
|
|
203
|
+
package_names[root] = package_name
|
|
204
|
+
return package_name
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _load_module_from_path(
|
|
208
|
+
path: Path,
|
|
209
|
+
*,
|
|
210
|
+
module_name: str,
|
|
211
|
+
submodule_search_locations: list[str] | None = None,
|
|
212
|
+
) -> ModuleType:
|
|
213
|
+
"""Import one Python file from disk under a caller-controlled module name."""
|
|
214
|
+
spec = importlib.util.spec_from_file_location(
|
|
215
|
+
module_name,
|
|
216
|
+
path,
|
|
217
|
+
submodule_search_locations=submodule_search_locations,
|
|
218
|
+
)
|
|
219
|
+
if spec is None or spec.loader is None:
|
|
220
|
+
raise ToolError(f"Could not create an import spec for tool module: {path}")
|
|
221
|
+
|
|
222
|
+
module = importlib.util.module_from_spec(spec)
|
|
223
|
+
sys.modules[module_name] = module
|
|
224
|
+
try:
|
|
225
|
+
spec.loader.exec_module(module)
|
|
226
|
+
except Exception as exc:
|
|
227
|
+
sys.modules.pop(module_name, None)
|
|
228
|
+
raise ToolError(f"Failed to import tool module '{path}': {exc}") from exc
|
|
229
|
+
return module
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _next_dynamic_package_name(path: Path) -> str:
|
|
233
|
+
"""Return a unique synthetic package name for one configured tool root."""
|
|
234
|
+
suffix = next(_USER_TOOLS_COUNTER)
|
|
235
|
+
slug = _sanitize_module_name(path.stem or "entry")
|
|
236
|
+
return f"{_USER_TOOLS_PACKAGE_PREFIX}_{suffix}_{slug}"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _sanitize_module_name(name: str) -> str:
|
|
240
|
+
"""Normalize arbitrary filenames into valid Python identifier segments."""
|
|
241
|
+
cleaned = "".join(ch if ch.isalnum() or ch == "_" else "_" for ch in name)
|
|
242
|
+
if not cleaned:
|
|
243
|
+
return "tool_module"
|
|
244
|
+
if cleaned[0].isdigit():
|
|
245
|
+
return f"_{cleaned}"
|
|
246
|
+
return cleaned
|
base_agentkit-0.1.0/PKG-INFO
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: base-agentkit
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Workspace-isolated Python framework for building tool-using LLM agents.
|
|
5
|
-
Keywords: agents,llm,tool-calling,workspace,ai
|
|
6
|
-
Author: sii-nyc
|
|
7
|
-
Author-email: sii-nyc <253108120111@sii.edu.cn>
|
|
8
|
-
License-Expression: Apache-2.0
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Typing :: Typed
|
|
17
|
-
Requires-Dist: openai>=2.21.0
|
|
18
|
-
Requires-Dist: pydantic>=2.12.5
|
|
19
|
-
Requires-Dist: pyyaml>=6.0.3
|
|
20
|
-
Requires-Dist: requests>=2.32.5
|
|
21
|
-
Requires-Python: >=3.12
|
|
22
|
-
Project-URL: Homepage, https://github.com/Inkjourney/agentkit
|
|
23
|
-
Project-URL: Repository, https://github.com/Inkjourney/agentkit
|
|
24
|
-
Project-URL: Documentation, https://github.com/Inkjourney/agentkit/tree/main/docs/agentkit
|
|
25
|
-
Project-URL: Issues, https://github.com/Inkjourney/agentkit/issues
|
|
26
|
-
Description-Content-Type: text/markdown
|
|
27
|
-
|
|
28
|
-
# AgentKit
|
|
29
|
-
|
|
30
|
-
一个基于 LLM 的通用 Agent 框架,支持 workspace 隔离、统一 LLM 抽象、工具调用循环、CLI 与 Python SDK。
|
|
31
|
-
|
|
32
|
-
## 当前能力
|
|
33
|
-
|
|
34
|
-
- workspace 强隔离:所有读写通过 `WorkspaceFS`
|
|
35
|
-
- 统一 LLM 抽象:`ConversationItem` / `ConversationState` / `UnifiedLLMRequest` / `UnifiedLLMResponse`
|
|
36
|
-
- Provider 支持(同步、非流式、文本)
|
|
37
|
-
- OpenAI (`responses` / `chat_completions`)
|
|
38
|
-
- Anthropic (`messages`)
|
|
39
|
-
- Gemini (`generateContent`)
|
|
40
|
-
- Qwen(OpenAI-compatible `chat_completions`)
|
|
41
|
-
- vLLM(OpenAI-compatible `chat_completions`)
|
|
42
|
-
- 工具系统:注册式工具机制,工具模块放在 `src/agentkit/tools/library/`
|
|
43
|
-
- Agent loop:模型推理与工具执行闭环,受 step/time budget 约束
|
|
44
|
-
- runlog:每次任务写入 `workspace/logs/run_<run_id>.jsonl`
|
|
45
|
-
|
|
46
|
-
## 状态
|
|
47
|
-
|
|
48
|
-
- 当前仓库包含针对 Agent loop、CLI、工具系统、runlog 以及 OpenAI / Anthropic / Gemini / Qwen / vLLM provider 适配层的单元测试
|
|
49
|
-
- 尚未提供针对真实上游 API 的集成 smoke test,因此正式发布前仍建议用目标模型做一次端到端验证
|
|
50
|
-
|
|
51
|
-
## 文档
|
|
52
|
-
|
|
53
|
-
详细使用说明和完整文档见 `docs/agentkit/`。
|
|
54
|
-
|
|
55
|
-
## 从源码安装
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
uv sync
|
|
59
|
-
uv run agentkit --help
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## 从 PyPI 安装
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
pip install base-agentkit
|
|
66
|
-
agentkit --help
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
安装后的导入路径和 CLI 名称保持不变:
|
|
70
|
-
|
|
71
|
-
```python
|
|
72
|
-
from agentkit import create_agent
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## 配置示例
|
|
76
|
-
|
|
77
|
-
### vLLM
|
|
78
|
-
|
|
79
|
-
```yaml
|
|
80
|
-
workspace:
|
|
81
|
-
root: "./vllm_workspace"
|
|
82
|
-
|
|
83
|
-
provider:
|
|
84
|
-
kind: "vllm"
|
|
85
|
-
model: "glm-5"
|
|
86
|
-
openai_api_variant: "chat_completions"
|
|
87
|
-
conversation_mode: "auto"
|
|
88
|
-
base_url: "http://localhost:8000/v1"
|
|
89
|
-
api_key: "empty"
|
|
90
|
-
temperature: 0.8
|
|
91
|
-
timeout_s: 600
|
|
92
|
-
retries: 2
|
|
93
|
-
enable_thinking: true
|
|
94
|
-
|
|
95
|
-
agent:
|
|
96
|
-
system_prompt: "You are a helpful agent. Use tools when needed."
|
|
97
|
-
budget:
|
|
98
|
-
max_steps: 200
|
|
99
|
-
time_budget_s: 1800
|
|
100
|
-
max_input_chars: 180000
|
|
101
|
-
|
|
102
|
-
tools:
|
|
103
|
-
allowed:
|
|
104
|
-
- "view"
|
|
105
|
-
- "create_file"
|
|
106
|
-
- "str_replace"
|
|
107
|
-
- "word_count"
|
|
108
|
-
|
|
109
|
-
runlog:
|
|
110
|
-
enabled: true
|
|
111
|
-
redact: true
|
|
112
|
-
max_text_chars: 20000
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## CLI 运行
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
export OPENAI_API_KEY="your-key"
|
|
119
|
-
uv run agentkit --config path/to/config.yaml run --task "列出当前 workspace 的文件"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Python SDK
|
|
123
|
-
|
|
124
|
-
```python
|
|
125
|
-
from agentkit import create_agent
|
|
126
|
-
|
|
127
|
-
agent = create_agent("path/to/config.yaml")
|
|
128
|
-
report = agent.run("在 workspace 里新建 notes/todo.txt 并写入今天计划")
|
|
129
|
-
print(report.final_output)
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## 项目结构
|
|
133
|
-
|
|
134
|
-
核心实现位于 `src/agentkit/`:
|
|
135
|
-
|
|
136
|
-
- `config/`: 配置结构与加载
|
|
137
|
-
- `workspace/`: 强隔离文件系统和目录布局
|
|
138
|
-
- `llm/`: 统一抽象、provider base/factory 与四平台 adapter
|
|
139
|
-
- `tools/`: 工具抽象、注册表与自动加载
|
|
140
|
-
- `agent/`: budget、report、runtime、Agent loop
|
|
141
|
-
- `runlog/`: 结构化事件、event sink 与 JSONL runlog
|
|
142
|
-
- `cli/`: 命令行入口
|
base_agentkit-0.1.0/README.md
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# AgentKit
|
|
2
|
-
|
|
3
|
-
一个基于 LLM 的通用 Agent 框架,支持 workspace 隔离、统一 LLM 抽象、工具调用循环、CLI 与 Python SDK。
|
|
4
|
-
|
|
5
|
-
## 当前能力
|
|
6
|
-
|
|
7
|
-
- workspace 强隔离:所有读写通过 `WorkspaceFS`
|
|
8
|
-
- 统一 LLM 抽象:`ConversationItem` / `ConversationState` / `UnifiedLLMRequest` / `UnifiedLLMResponse`
|
|
9
|
-
- Provider 支持(同步、非流式、文本)
|
|
10
|
-
- OpenAI (`responses` / `chat_completions`)
|
|
11
|
-
- Anthropic (`messages`)
|
|
12
|
-
- Gemini (`generateContent`)
|
|
13
|
-
- Qwen(OpenAI-compatible `chat_completions`)
|
|
14
|
-
- vLLM(OpenAI-compatible `chat_completions`)
|
|
15
|
-
- 工具系统:注册式工具机制,工具模块放在 `src/agentkit/tools/library/`
|
|
16
|
-
- Agent loop:模型推理与工具执行闭环,受 step/time budget 约束
|
|
17
|
-
- runlog:每次任务写入 `workspace/logs/run_<run_id>.jsonl`
|
|
18
|
-
|
|
19
|
-
## 状态
|
|
20
|
-
|
|
21
|
-
- 当前仓库包含针对 Agent loop、CLI、工具系统、runlog 以及 OpenAI / Anthropic / Gemini / Qwen / vLLM provider 适配层的单元测试
|
|
22
|
-
- 尚未提供针对真实上游 API 的集成 smoke test,因此正式发布前仍建议用目标模型做一次端到端验证
|
|
23
|
-
|
|
24
|
-
## 文档
|
|
25
|
-
|
|
26
|
-
详细使用说明和完整文档见 `docs/agentkit/`。
|
|
27
|
-
|
|
28
|
-
## 从源码安装
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
uv sync
|
|
32
|
-
uv run agentkit --help
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## 从 PyPI 安装
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pip install base-agentkit
|
|
39
|
-
agentkit --help
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
安装后的导入路径和 CLI 名称保持不变:
|
|
43
|
-
|
|
44
|
-
```python
|
|
45
|
-
from agentkit import create_agent
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## 配置示例
|
|
49
|
-
|
|
50
|
-
### vLLM
|
|
51
|
-
|
|
52
|
-
```yaml
|
|
53
|
-
workspace:
|
|
54
|
-
root: "./vllm_workspace"
|
|
55
|
-
|
|
56
|
-
provider:
|
|
57
|
-
kind: "vllm"
|
|
58
|
-
model: "glm-5"
|
|
59
|
-
openai_api_variant: "chat_completions"
|
|
60
|
-
conversation_mode: "auto"
|
|
61
|
-
base_url: "http://localhost:8000/v1"
|
|
62
|
-
api_key: "empty"
|
|
63
|
-
temperature: 0.8
|
|
64
|
-
timeout_s: 600
|
|
65
|
-
retries: 2
|
|
66
|
-
enable_thinking: true
|
|
67
|
-
|
|
68
|
-
agent:
|
|
69
|
-
system_prompt: "You are a helpful agent. Use tools when needed."
|
|
70
|
-
budget:
|
|
71
|
-
max_steps: 200
|
|
72
|
-
time_budget_s: 1800
|
|
73
|
-
max_input_chars: 180000
|
|
74
|
-
|
|
75
|
-
tools:
|
|
76
|
-
allowed:
|
|
77
|
-
- "view"
|
|
78
|
-
- "create_file"
|
|
79
|
-
- "str_replace"
|
|
80
|
-
- "word_count"
|
|
81
|
-
|
|
82
|
-
runlog:
|
|
83
|
-
enabled: true
|
|
84
|
-
redact: true
|
|
85
|
-
max_text_chars: 20000
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## CLI 运行
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
export OPENAI_API_KEY="your-key"
|
|
92
|
-
uv run agentkit --config path/to/config.yaml run --task "列出当前 workspace 的文件"
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Python SDK
|
|
96
|
-
|
|
97
|
-
```python
|
|
98
|
-
from agentkit import create_agent
|
|
99
|
-
|
|
100
|
-
agent = create_agent("path/to/config.yaml")
|
|
101
|
-
report = agent.run("在 workspace 里新建 notes/todo.txt 并写入今天计划")
|
|
102
|
-
print(report.final_output)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## 项目结构
|
|
106
|
-
|
|
107
|
-
核心实现位于 `src/agentkit/`:
|
|
108
|
-
|
|
109
|
-
- `config/`: 配置结构与加载
|
|
110
|
-
- `workspace/`: 强隔离文件系统和目录布局
|
|
111
|
-
- `llm/`: 统一抽象、provider base/factory 与四平台 adapter
|
|
112
|
-
- `tools/`: 工具抽象、注册表与自动加载
|
|
113
|
-
- `agent/`: budget、report、runtime、Agent loop
|
|
114
|
-
- `runlog/`: 结构化事件、event sink 与 JSONL runlog
|
|
115
|
-
- `cli/`: 命令行入口
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"""Automatic tool loading from the tool library directory."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import importlib
|
|
6
|
-
import inspect
|
|
7
|
-
import pkgutil
|
|
8
|
-
from typing import Any, Iterable
|
|
9
|
-
|
|
10
|
-
from agentkit.errors import ToolError
|
|
11
|
-
from agentkit.tools.base import Tool
|
|
12
|
-
from agentkit.workspace.fs import WorkspaceFS
|
|
13
|
-
|
|
14
|
-
TOOLS_LIBRARY_PACKAGE = "agentkit.tools.library"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def load_tools_from_library(fs: WorkspaceFS) -> list[Tool]:
|
|
18
|
-
"""Load all tools exposed by modules under ``agentkit.tools.library``.
|
|
19
|
-
|
|
20
|
-
Tool modules can expose tools via:
|
|
21
|
-
- ``build_tools(fs)`` (or ``build_tools()``)
|
|
22
|
-
- ``TOOLS`` module variable
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
fs: Workspace filesystem injected into callable tool factories.
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
list[Tool]: Flattened list of tools from all library modules.
|
|
29
|
-
"""
|
|
30
|
-
package = importlib.import_module(TOOLS_LIBRARY_PACKAGE)
|
|
31
|
-
package_path = getattr(package, "__path__", None)
|
|
32
|
-
if package_path is None:
|
|
33
|
-
return []
|
|
34
|
-
|
|
35
|
-
loaded: list[Tool] = []
|
|
36
|
-
for module_info in sorted(pkgutil.iter_modules(package_path), key=lambda m: m.name):
|
|
37
|
-
if module_info.name.startswith("_"):
|
|
38
|
-
continue
|
|
39
|
-
module_name = f"{TOOLS_LIBRARY_PACKAGE}.{module_info.name}"
|
|
40
|
-
module = importlib.import_module(module_name)
|
|
41
|
-
loaded.extend(_load_from_module(module, fs, module_name=module_name))
|
|
42
|
-
return loaded
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _load_from_module(module: Any, fs: WorkspaceFS, *, module_name: str) -> list[Tool]:
|
|
46
|
-
"""Extract tools from one library module."""
|
|
47
|
-
if hasattr(module, "build_tools"):
|
|
48
|
-
return _coerce_to_tools(getattr(module, "build_tools"), fs, module_name)
|
|
49
|
-
if hasattr(module, "TOOLS"):
|
|
50
|
-
return _coerce_to_tools(getattr(module, "TOOLS"), fs, module_name)
|
|
51
|
-
return []
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _coerce_to_tools(candidate: Any, fs: WorkspaceFS, module_name: str) -> list[Tool]:
|
|
55
|
-
"""Normalize supported tool declarations into ``list[Tool]``."""
|
|
56
|
-
if isinstance(candidate, Tool):
|
|
57
|
-
return [candidate]
|
|
58
|
-
|
|
59
|
-
if callable(candidate):
|
|
60
|
-
built: Any
|
|
61
|
-
try:
|
|
62
|
-
signature = inspect.signature(candidate)
|
|
63
|
-
if len(signature.parameters) == 0:
|
|
64
|
-
built = candidate()
|
|
65
|
-
else:
|
|
66
|
-
built = candidate(fs)
|
|
67
|
-
except ValueError:
|
|
68
|
-
built = candidate(fs)
|
|
69
|
-
return _coerce_to_tools(built, fs, module_name)
|
|
70
|
-
|
|
71
|
-
if isinstance(candidate, Iterable) and not isinstance(
|
|
72
|
-
candidate, (str, bytes, dict)
|
|
73
|
-
):
|
|
74
|
-
tools = list(candidate)
|
|
75
|
-
if all(isinstance(item, Tool) for item in tools):
|
|
76
|
-
return tools
|
|
77
|
-
|
|
78
|
-
raise ToolError(
|
|
79
|
-
f"Module '{module_name}' must expose Tool, Iterable[Tool], build_tools(...), or TOOLS."
|
|
80
|
-
)
|
|
81
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{base_agentkit-0.1.0 → base_agentkit-0.1.2}/src/agentkit/llm/providers/anthropic_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|