modis-py-tools 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modis_py_tools-0.1.0/PKG-INFO +281 -0
- modis_py_tools-0.1.0/README.md +262 -0
- modis_py_tools-0.1.0/pyproject.toml +57 -0
- modis_py_tools-0.1.0/setup.cfg +4 -0
- modis_py_tools-0.1.0/src/modis_py_tools.egg-info/PKG-INFO +281 -0
- modis_py_tools-0.1.0/src/modis_py_tools.egg-info/SOURCES.txt +38 -0
- modis_py_tools-0.1.0/src/modis_py_tools.egg-info/dependency_links.txt +1 -0
- modis_py_tools-0.1.0/src/modis_py_tools.egg-info/requires.txt +15 -0
- modis_py_tools-0.1.0/src/modis_py_tools.egg-info/top_level.txt +1 -0
- modis_py_tools-0.1.0/src/modis_tools/__init__.py +65 -0
- modis_py_tools-0.1.0/src/modis_tools/_version.py +3 -0
- modis_py_tools-0.1.0/src/modis_tools/builtins/__init__.py +25 -0
- modis_py_tools-0.1.0/src/modis_tools/builtins/python.py +119 -0
- modis_py_tools-0.1.0/src/modis_tools/builtins/shell.py +227 -0
- modis_py_tools-0.1.0/src/modis_tools/builtins/shell_policy.py +240 -0
- modis_py_tools-0.1.0/src/modis_tools/builtins/web.py +281 -0
- modis_py_tools-0.1.0/src/modis_tools/conversion.py +175 -0
- modis_py_tools-0.1.0/src/modis_tools/providers/__init__.py +13 -0
- modis_py_tools-0.1.0/src/modis_tools/providers/valyu.py +268 -0
- modis_py_tools-0.1.0/src/modis_tools/providers/web.py +166 -0
- modis_py_tools-0.1.0/src/modis_tools/py.typed +1 -0
- modis_py_tools-0.1.0/src/modis_tools/registry.py +165 -0
- modis_py_tools-0.1.0/src/modis_tools/results.py +48 -0
- modis_py_tools-0.1.0/src/modis_tools/schemas.py +121 -0
- modis_py_tools-0.1.0/src/modis_tools/serialization.py +59 -0
- modis_py_tools-0.1.0/src/modis_tools/skills/__init__.py +22 -0
- modis_py_tools-0.1.0/src/modis_tools/skills/models.py +45 -0
- modis_py_tools-0.1.0/src/modis_tools/skills/prompt.py +60 -0
- modis_py_tools-0.1.0/src/modis_tools/skills/registry.py +153 -0
- modis_py_tools-0.1.0/src/modis_tools/skills/tools.py +128 -0
- modis_py_tools-0.1.0/tests/test_conversion.py +76 -0
- modis_py_tools-0.1.0/tests/test_live_web.py +70 -0
- modis_py_tools-0.1.0/tests/test_live_web_eval.py +104 -0
- modis_py_tools-0.1.0/tests/test_python_tool.py +55 -0
- modis_py_tools-0.1.0/tests/test_registry.py +87 -0
- modis_py_tools-0.1.0/tests/test_results.py +48 -0
- modis_py_tools-0.1.0/tests/test_shell_tool.py +171 -0
- modis_py_tools-0.1.0/tests/test_skills.py +171 -0
- modis_py_tools-0.1.0/tests/test_valyu_provider.py +164 -0
- modis_py_tools-0.1.0/tests/test_web_group.py +157 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modis-py-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Common MoDIS-compatible Python tools and tool-call execution helpers
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: pydantic>=2.7.0
|
|
8
|
+
Provides-Extra: web
|
|
9
|
+
Requires-Dist: valyu>=2.0.0; extra == "web"
|
|
10
|
+
Provides-Extra: browser
|
|
11
|
+
Requires-Dist: markdownify>=0.12.0; extra == "browser"
|
|
12
|
+
Requires-Dist: playwright>=1.45.0; extra == "browser"
|
|
13
|
+
Requires-Dist: readability-lxml>=0.8.1; extra == "browser"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: build>=1.2.0; extra == "dev"
|
|
16
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
18
|
+
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# MoDIS Python Tools
|
|
21
|
+
|
|
22
|
+
`modis-py-tools` provides MoDIS-compatible function tool definitions, tool
|
|
23
|
+
groups, and tool-call execution helpers for Python applications.
|
|
24
|
+
|
|
25
|
+
The distribution name is currently `modis-py-tools`; the import package is
|
|
26
|
+
`modis_tools`.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python -m pip install modis-py-tools
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Optional web provider dependency:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python -m pip install "modis-py-tools[web]"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Development:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python -m pip install -e ".[dev,web]"
|
|
44
|
+
.venv/bin/python -m pytest
|
|
45
|
+
.venv/bin/python -m ruff check .
|
|
46
|
+
.venv/bin/python -m ruff format --check .
|
|
47
|
+
.venv/bin/python -m mypy src
|
|
48
|
+
.venv/bin/python -m build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Function Conversion
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from modis_tools import function_to_tool
|
|
55
|
+
|
|
56
|
+
def lookup_order(order_id: str, include_history: bool = False) -> dict:
|
|
57
|
+
"""Look up an order.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
order_id: Order identifier.
|
|
61
|
+
include_history: Whether to include status history.
|
|
62
|
+
"""
|
|
63
|
+
return {"order_id": order_id, "status": "processing"}
|
|
64
|
+
|
|
65
|
+
tool = function_to_tool(lookup_order, name="orders.lookup")
|
|
66
|
+
tool_definition = tool.to_wire()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Registry Execution
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from modis_tools import ToolRegistry
|
|
73
|
+
from modis_tools.builtins import python_group, shell_group
|
|
74
|
+
|
|
75
|
+
registry = ToolRegistry()
|
|
76
|
+
registry.include(shell_group())
|
|
77
|
+
registry.include(python_group())
|
|
78
|
+
|
|
79
|
+
tool_definitions = registry.definitions()
|
|
80
|
+
results = registry.execute_tool_calls(model_tool_calls)
|
|
81
|
+
messages = [result.to_chat_message() for result in results]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`execute_tool_call()` and `execute_tool_calls()` return failed `ToolResult`
|
|
85
|
+
objects by default for unknown tools, invalid arguments, and tool exceptions.
|
|
86
|
+
Pass `raise_on_error=True` when the host application should handle exceptions.
|
|
87
|
+
|
|
88
|
+
For a fuller host integration guide, including unattended pipeline setup and
|
|
89
|
+
future image generation extension notes, see `docs/INTEGRATION.md`.
|
|
90
|
+
|
|
91
|
+
## Built-In Tools
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from modis_tools.builtins import python_group, shell_group, skills_group, web_group
|
|
95
|
+
|
|
96
|
+
shell_tools = shell_group()
|
|
97
|
+
python_tools = python_group()
|
|
98
|
+
skill_tools = skills_group(skill_home="./skills")
|
|
99
|
+
web_tools = web_group(api_key="...")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`shell.run` captures `stdout`, `stderr`, exit code, duration, timeout state,
|
|
103
|
+
working directory, policy decision metadata, and truncation metadata.
|
|
104
|
+
`python.run` executes Python source in a subprocess using the current
|
|
105
|
+
interpreter by default.
|
|
106
|
+
|
|
107
|
+
Shell and Python tools execute local code. Only expose them in trusted contexts
|
|
108
|
+
where the caller owns policy, sandboxing, and approval decisions.
|
|
109
|
+
|
|
110
|
+
Shell tools support host-owned policies. Policies are bound by the application
|
|
111
|
+
when registering the group and are not exposed as model tool-call parameters.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from modis_tools import ToolRegistry
|
|
115
|
+
from modis_tools.builtins import ShellPolicy, shell_group
|
|
116
|
+
|
|
117
|
+
registry = ToolRegistry()
|
|
118
|
+
registry.include(shell_group(policy=ShellPolicy.readonly_project("./")))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Built-in shell policy profiles:
|
|
122
|
+
|
|
123
|
+
| Profile | Purpose |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `trusted_local` | Backward-compatible default for trusted local use. |
|
|
126
|
+
| `disabled` | Denies every shell request. |
|
|
127
|
+
| `restricted` | Deterministic allowlist for unattended pipelines. |
|
|
128
|
+
| `readonly_project` | Read-oriented `argv` commands inside one project root. |
|
|
129
|
+
|
|
130
|
+
`shell.run` accepts exactly one of `cmd` or `argv`. `cmd` executes with
|
|
131
|
+
`shell=True`; `argv` executes with `shell=False`. Restricted policies should
|
|
132
|
+
prefer `argv` mode. Policy controls are guardrails, not a strong OS sandbox; use
|
|
133
|
+
containers, VMs, or other isolation for untrusted execution.
|
|
134
|
+
|
|
135
|
+
## Skill Tools
|
|
136
|
+
|
|
137
|
+
Skill support is split into runtime visibility, optional host-owned matching, and
|
|
138
|
+
tool-based skill use. `modis-tools` provides the use layer: a registry, a prompt
|
|
139
|
+
generator, and `skills.*` tools for progressive loading.
|
|
140
|
+
|
|
141
|
+
`skill_home` is required in every mode because all skill files must resolve from
|
|
142
|
+
a trusted root.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from modis_tools import ToolRegistry
|
|
146
|
+
from modis_tools.builtins import skills_group
|
|
147
|
+
from modis_tools.skills import SkillRegistry, build_skill_system_prompt
|
|
148
|
+
|
|
149
|
+
skill_home = "./skills"
|
|
150
|
+
registry = ToolRegistry()
|
|
151
|
+
registry.include(skills_group(skill_home=skill_home, mode="hybrid"))
|
|
152
|
+
|
|
153
|
+
skill_registry = SkillRegistry.from_home(skill_home)
|
|
154
|
+
system_prompt = build_skill_system_prompt(skill_registry, mode="hybrid")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The skills group exposes:
|
|
158
|
+
|
|
159
|
+
- `skills.list()`
|
|
160
|
+
- `skills.read(name)`
|
|
161
|
+
- `skills.list_resources(name)`
|
|
162
|
+
- `skills.read_resource(name, path)`
|
|
163
|
+
- `skills.system_prompt(active_skill=None)`
|
|
164
|
+
|
|
165
|
+
Execution modes:
|
|
166
|
+
|
|
167
|
+
| Mode | Behavior |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `tools_only` | Models use only `skills.*` tools to read instructions and resources. |
|
|
170
|
+
| `shell_only` | Prompt tells the model where `skill_home` is; host shell/file policy must handle access. |
|
|
171
|
+
| `hybrid` | Models use `skills.*` for reads and policy-gated shell for commands only when needed. |
|
|
172
|
+
|
|
173
|
+
Skill matching is intentionally optional and host-owned. The package defines
|
|
174
|
+
`PromptSkillEvaluator` as a protocol, but does not decide when a skill should be
|
|
175
|
+
used.
|
|
176
|
+
|
|
177
|
+
## Web Tools
|
|
178
|
+
|
|
179
|
+
The web group exposes:
|
|
180
|
+
|
|
181
|
+
- `web.search(query, num_results=10, search_type="all", relevance_threshold=0.5, included_sources=None, excluded_sources=None, country_code=None, response_length=None, category=None, start_date=None, end_date=None, max_price=None, fast_mode=False, url_only=False, source_biases=None, instructions=None)`
|
|
182
|
+
- `web.open(id=None, cursor=-1, loc=-1, num_lines=-1)`
|
|
183
|
+
- `web.find(pattern, cursor=-1, context_lines=3)`
|
|
184
|
+
|
|
185
|
+
Valyu is the default provider when no provider is supplied. Search uses Valyu
|
|
186
|
+
search, and direct URL opens use Valyu contents extraction.
|
|
187
|
+
Opened pages and find results include source ids, URLs, and one-based line
|
|
188
|
+
ranges so responses can be cited or re-opened later in the same tool session.
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from modis_tools import ToolRegistry
|
|
192
|
+
from modis_tools.builtins import web_group
|
|
193
|
+
|
|
194
|
+
registry = ToolRegistry()
|
|
195
|
+
registry.include(web_group(api_key="..."))
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Search options map to Valyu search controls. For example:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
result = registry.execute_tool_call({
|
|
202
|
+
"id": "search_1",
|
|
203
|
+
"type": "function",
|
|
204
|
+
"function": {
|
|
205
|
+
"name": "web.search",
|
|
206
|
+
"arguments": """
|
|
207
|
+
{
|
|
208
|
+
"query": "recent multimodal retrieval papers",
|
|
209
|
+
"num_results": 5,
|
|
210
|
+
"relevance_threshold": 0.45,
|
|
211
|
+
"included_sources": ["valyu/valyu-arxiv"],
|
|
212
|
+
"response_length": "medium",
|
|
213
|
+
"start_date": "2026-04-29",
|
|
214
|
+
"end_date": "2026-05-06",
|
|
215
|
+
"country_code": "US"
|
|
216
|
+
}
|
|
217
|
+
"""
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Default search values are copied from the installed Valyu SDK where exposed:
|
|
223
|
+
|
|
224
|
+
| Option | Default | Notes |
|
|
225
|
+
| --- | --- | --- |
|
|
226
|
+
| `num_results` | `10` | Sent to Valyu as `max_num_results`. |
|
|
227
|
+
| `search_type` | `"all"` | Valyu scopes are `web`, `proprietary`, `all`, and `news`. |
|
|
228
|
+
| `relevance_threshold` | `0.5` | Set to `None` to omit the threshold parameter. |
|
|
229
|
+
| `included_sources` | `None` | Valyu source ids or arbitrary URLs. |
|
|
230
|
+
| `excluded_sources` | `None` | Valyu source ids or arbitrary URLs. |
|
|
231
|
+
| `country_code` | `None` | Optional ISO country code. |
|
|
232
|
+
| `response_length` | `None` | Supports `short`, `medium`, `large`, `max`, integer, or numeric string. |
|
|
233
|
+
| `category` | `None` | Provider-specific category. |
|
|
234
|
+
| `start_date` / `end_date` | `None` | Optional `YYYY-MM-DD` bounds. |
|
|
235
|
+
| `max_price` | `None` | Provider cost limit if used. |
|
|
236
|
+
| `fast_mode` | `False` | Sent explicitly. |
|
|
237
|
+
| `url_only` | `False` | Sent explicitly. |
|
|
238
|
+
| `source_biases` | `None` | Optional per-source integer biases. |
|
|
239
|
+
| `instructions` | `None` | Optional provider instructions. |
|
|
240
|
+
|
|
241
|
+
`is_tool_call` is not exposed to the model; the Valyu provider sends it as
|
|
242
|
+
`True`.
|
|
243
|
+
|
|
244
|
+
Valyu provider metadata that is not part of the normalized result shape, such as
|
|
245
|
+
scores or ranking fields when returned by the SDK, is preserved in each result's
|
|
246
|
+
`metadata` object.
|
|
247
|
+
|
|
248
|
+
Custom providers implement `SearchProvider`:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from modis_tools.providers.web import Page, SearchResult
|
|
252
|
+
|
|
253
|
+
class MyProvider:
|
|
254
|
+
def search(self, query: str, *, num_results: int = 10, **kwargs: object) -> list[SearchResult]:
|
|
255
|
+
return [SearchResult(title="Example", url="https://example.test", content="Page text")]
|
|
256
|
+
|
|
257
|
+
def fetch(self, url: str) -> Page:
|
|
258
|
+
return Page(title="Fetched", url=url, markdown="Fetched markdown")
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`web.open` can open prior search results by index/result id/URL, or an arbitrary
|
|
262
|
+
direct URL. Browser-backed fetching is still kept behind the optional `browser`
|
|
263
|
+
extra for future extension.
|
|
264
|
+
|
|
265
|
+
## Live Web Tests
|
|
266
|
+
|
|
267
|
+
Live web tests are skipped unless `VALYU_API_KEY` is set and the `web` extra is
|
|
268
|
+
installed:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
python -m pip install -e ".[dev,web]"
|
|
272
|
+
VALYU_API_KEY=... pytest -m live
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The broader web quality eval is opt-in because it consumes live provider quota:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
VALYU_API_KEY=... MODIS_TOOLS_RUN_WEB_EVAL=1 pytest -m "live and eval" -vv
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
See `docs/WEB_EVAL.md` for the current query set and comparison checklist.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# MoDIS Python Tools
|
|
2
|
+
|
|
3
|
+
`modis-py-tools` provides MoDIS-compatible function tool definitions, tool
|
|
4
|
+
groups, and tool-call execution helpers for Python applications.
|
|
5
|
+
|
|
6
|
+
The distribution name is currently `modis-py-tools`; the import package is
|
|
7
|
+
`modis_tools`.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python -m pip install modis-py-tools
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Optional web provider dependency:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m pip install "modis-py-tools[web]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Development:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install -e ".[dev,web]"
|
|
25
|
+
.venv/bin/python -m pytest
|
|
26
|
+
.venv/bin/python -m ruff check .
|
|
27
|
+
.venv/bin/python -m ruff format --check .
|
|
28
|
+
.venv/bin/python -m mypy src
|
|
29
|
+
.venv/bin/python -m build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Function Conversion
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from modis_tools import function_to_tool
|
|
36
|
+
|
|
37
|
+
def lookup_order(order_id: str, include_history: bool = False) -> dict:
|
|
38
|
+
"""Look up an order.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
order_id: Order identifier.
|
|
42
|
+
include_history: Whether to include status history.
|
|
43
|
+
"""
|
|
44
|
+
return {"order_id": order_id, "status": "processing"}
|
|
45
|
+
|
|
46
|
+
tool = function_to_tool(lookup_order, name="orders.lookup")
|
|
47
|
+
tool_definition = tool.to_wire()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Registry Execution
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from modis_tools import ToolRegistry
|
|
54
|
+
from modis_tools.builtins import python_group, shell_group
|
|
55
|
+
|
|
56
|
+
registry = ToolRegistry()
|
|
57
|
+
registry.include(shell_group())
|
|
58
|
+
registry.include(python_group())
|
|
59
|
+
|
|
60
|
+
tool_definitions = registry.definitions()
|
|
61
|
+
results = registry.execute_tool_calls(model_tool_calls)
|
|
62
|
+
messages = [result.to_chat_message() for result in results]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`execute_tool_call()` and `execute_tool_calls()` return failed `ToolResult`
|
|
66
|
+
objects by default for unknown tools, invalid arguments, and tool exceptions.
|
|
67
|
+
Pass `raise_on_error=True` when the host application should handle exceptions.
|
|
68
|
+
|
|
69
|
+
For a fuller host integration guide, including unattended pipeline setup and
|
|
70
|
+
future image generation extension notes, see `docs/INTEGRATION.md`.
|
|
71
|
+
|
|
72
|
+
## Built-In Tools
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from modis_tools.builtins import python_group, shell_group, skills_group, web_group
|
|
76
|
+
|
|
77
|
+
shell_tools = shell_group()
|
|
78
|
+
python_tools = python_group()
|
|
79
|
+
skill_tools = skills_group(skill_home="./skills")
|
|
80
|
+
web_tools = web_group(api_key="...")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`shell.run` captures `stdout`, `stderr`, exit code, duration, timeout state,
|
|
84
|
+
working directory, policy decision metadata, and truncation metadata.
|
|
85
|
+
`python.run` executes Python source in a subprocess using the current
|
|
86
|
+
interpreter by default.
|
|
87
|
+
|
|
88
|
+
Shell and Python tools execute local code. Only expose them in trusted contexts
|
|
89
|
+
where the caller owns policy, sandboxing, and approval decisions.
|
|
90
|
+
|
|
91
|
+
Shell tools support host-owned policies. Policies are bound by the application
|
|
92
|
+
when registering the group and are not exposed as model tool-call parameters.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from modis_tools import ToolRegistry
|
|
96
|
+
from modis_tools.builtins import ShellPolicy, shell_group
|
|
97
|
+
|
|
98
|
+
registry = ToolRegistry()
|
|
99
|
+
registry.include(shell_group(policy=ShellPolicy.readonly_project("./")))
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Built-in shell policy profiles:
|
|
103
|
+
|
|
104
|
+
| Profile | Purpose |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `trusted_local` | Backward-compatible default for trusted local use. |
|
|
107
|
+
| `disabled` | Denies every shell request. |
|
|
108
|
+
| `restricted` | Deterministic allowlist for unattended pipelines. |
|
|
109
|
+
| `readonly_project` | Read-oriented `argv` commands inside one project root. |
|
|
110
|
+
|
|
111
|
+
`shell.run` accepts exactly one of `cmd` or `argv`. `cmd` executes with
|
|
112
|
+
`shell=True`; `argv` executes with `shell=False`. Restricted policies should
|
|
113
|
+
prefer `argv` mode. Policy controls are guardrails, not a strong OS sandbox; use
|
|
114
|
+
containers, VMs, or other isolation for untrusted execution.
|
|
115
|
+
|
|
116
|
+
## Skill Tools
|
|
117
|
+
|
|
118
|
+
Skill support is split into runtime visibility, optional host-owned matching, and
|
|
119
|
+
tool-based skill use. `modis-tools` provides the use layer: a registry, a prompt
|
|
120
|
+
generator, and `skills.*` tools for progressive loading.
|
|
121
|
+
|
|
122
|
+
`skill_home` is required in every mode because all skill files must resolve from
|
|
123
|
+
a trusted root.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from modis_tools import ToolRegistry
|
|
127
|
+
from modis_tools.builtins import skills_group
|
|
128
|
+
from modis_tools.skills import SkillRegistry, build_skill_system_prompt
|
|
129
|
+
|
|
130
|
+
skill_home = "./skills"
|
|
131
|
+
registry = ToolRegistry()
|
|
132
|
+
registry.include(skills_group(skill_home=skill_home, mode="hybrid"))
|
|
133
|
+
|
|
134
|
+
skill_registry = SkillRegistry.from_home(skill_home)
|
|
135
|
+
system_prompt = build_skill_system_prompt(skill_registry, mode="hybrid")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The skills group exposes:
|
|
139
|
+
|
|
140
|
+
- `skills.list()`
|
|
141
|
+
- `skills.read(name)`
|
|
142
|
+
- `skills.list_resources(name)`
|
|
143
|
+
- `skills.read_resource(name, path)`
|
|
144
|
+
- `skills.system_prompt(active_skill=None)`
|
|
145
|
+
|
|
146
|
+
Execution modes:
|
|
147
|
+
|
|
148
|
+
| Mode | Behavior |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `tools_only` | Models use only `skills.*` tools to read instructions and resources. |
|
|
151
|
+
| `shell_only` | Prompt tells the model where `skill_home` is; host shell/file policy must handle access. |
|
|
152
|
+
| `hybrid` | Models use `skills.*` for reads and policy-gated shell for commands only when needed. |
|
|
153
|
+
|
|
154
|
+
Skill matching is intentionally optional and host-owned. The package defines
|
|
155
|
+
`PromptSkillEvaluator` as a protocol, but does not decide when a skill should be
|
|
156
|
+
used.
|
|
157
|
+
|
|
158
|
+
## Web Tools
|
|
159
|
+
|
|
160
|
+
The web group exposes:
|
|
161
|
+
|
|
162
|
+
- `web.search(query, num_results=10, search_type="all", relevance_threshold=0.5, included_sources=None, excluded_sources=None, country_code=None, response_length=None, category=None, start_date=None, end_date=None, max_price=None, fast_mode=False, url_only=False, source_biases=None, instructions=None)`
|
|
163
|
+
- `web.open(id=None, cursor=-1, loc=-1, num_lines=-1)`
|
|
164
|
+
- `web.find(pattern, cursor=-1, context_lines=3)`
|
|
165
|
+
|
|
166
|
+
Valyu is the default provider when no provider is supplied. Search uses Valyu
|
|
167
|
+
search, and direct URL opens use Valyu contents extraction.
|
|
168
|
+
Opened pages and find results include source ids, URLs, and one-based line
|
|
169
|
+
ranges so responses can be cited or re-opened later in the same tool session.
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from modis_tools import ToolRegistry
|
|
173
|
+
from modis_tools.builtins import web_group
|
|
174
|
+
|
|
175
|
+
registry = ToolRegistry()
|
|
176
|
+
registry.include(web_group(api_key="..."))
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Search options map to Valyu search controls. For example:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
result = registry.execute_tool_call({
|
|
183
|
+
"id": "search_1",
|
|
184
|
+
"type": "function",
|
|
185
|
+
"function": {
|
|
186
|
+
"name": "web.search",
|
|
187
|
+
"arguments": """
|
|
188
|
+
{
|
|
189
|
+
"query": "recent multimodal retrieval papers",
|
|
190
|
+
"num_results": 5,
|
|
191
|
+
"relevance_threshold": 0.45,
|
|
192
|
+
"included_sources": ["valyu/valyu-arxiv"],
|
|
193
|
+
"response_length": "medium",
|
|
194
|
+
"start_date": "2026-04-29",
|
|
195
|
+
"end_date": "2026-05-06",
|
|
196
|
+
"country_code": "US"
|
|
197
|
+
}
|
|
198
|
+
"""
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Default search values are copied from the installed Valyu SDK where exposed:
|
|
204
|
+
|
|
205
|
+
| Option | Default | Notes |
|
|
206
|
+
| --- | --- | --- |
|
|
207
|
+
| `num_results` | `10` | Sent to Valyu as `max_num_results`. |
|
|
208
|
+
| `search_type` | `"all"` | Valyu scopes are `web`, `proprietary`, `all`, and `news`. |
|
|
209
|
+
| `relevance_threshold` | `0.5` | Set to `None` to omit the threshold parameter. |
|
|
210
|
+
| `included_sources` | `None` | Valyu source ids or arbitrary URLs. |
|
|
211
|
+
| `excluded_sources` | `None` | Valyu source ids or arbitrary URLs. |
|
|
212
|
+
| `country_code` | `None` | Optional ISO country code. |
|
|
213
|
+
| `response_length` | `None` | Supports `short`, `medium`, `large`, `max`, integer, or numeric string. |
|
|
214
|
+
| `category` | `None` | Provider-specific category. |
|
|
215
|
+
| `start_date` / `end_date` | `None` | Optional `YYYY-MM-DD` bounds. |
|
|
216
|
+
| `max_price` | `None` | Provider cost limit if used. |
|
|
217
|
+
| `fast_mode` | `False` | Sent explicitly. |
|
|
218
|
+
| `url_only` | `False` | Sent explicitly. |
|
|
219
|
+
| `source_biases` | `None` | Optional per-source integer biases. |
|
|
220
|
+
| `instructions` | `None` | Optional provider instructions. |
|
|
221
|
+
|
|
222
|
+
`is_tool_call` is not exposed to the model; the Valyu provider sends it as
|
|
223
|
+
`True`.
|
|
224
|
+
|
|
225
|
+
Valyu provider metadata that is not part of the normalized result shape, such as
|
|
226
|
+
scores or ranking fields when returned by the SDK, is preserved in each result's
|
|
227
|
+
`metadata` object.
|
|
228
|
+
|
|
229
|
+
Custom providers implement `SearchProvider`:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from modis_tools.providers.web import Page, SearchResult
|
|
233
|
+
|
|
234
|
+
class MyProvider:
|
|
235
|
+
def search(self, query: str, *, num_results: int = 10, **kwargs: object) -> list[SearchResult]:
|
|
236
|
+
return [SearchResult(title="Example", url="https://example.test", content="Page text")]
|
|
237
|
+
|
|
238
|
+
def fetch(self, url: str) -> Page:
|
|
239
|
+
return Page(title="Fetched", url=url, markdown="Fetched markdown")
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`web.open` can open prior search results by index/result id/URL, or an arbitrary
|
|
243
|
+
direct URL. Browser-backed fetching is still kept behind the optional `browser`
|
|
244
|
+
extra for future extension.
|
|
245
|
+
|
|
246
|
+
## Live Web Tests
|
|
247
|
+
|
|
248
|
+
Live web tests are skipped unless `VALYU_API_KEY` is set and the `web` extra is
|
|
249
|
+
installed:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
python -m pip install -e ".[dev,web]"
|
|
253
|
+
VALYU_API_KEY=... pytest -m live
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The broader web quality eval is opt-in because it consumes live provider quota:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
VALYU_API_KEY=... MODIS_TOOLS_RUN_WEB_EVAL=1 pytest -m "live and eval" -vv
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
See `docs/WEB_EVAL.md` for the current query set and comparison checklist.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=67", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modis-py-tools"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Common MoDIS-compatible Python tools and tool-call execution helpers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pydantic>=2.7.0"
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
web = [
|
|
17
|
+
"valyu>=2.0.0"
|
|
18
|
+
]
|
|
19
|
+
browser = [
|
|
20
|
+
"markdownify>=0.12.0",
|
|
21
|
+
"playwright>=1.45.0",
|
|
22
|
+
"readability-lxml>=0.8.1"
|
|
23
|
+
]
|
|
24
|
+
dev = [
|
|
25
|
+
"build>=1.2.0",
|
|
26
|
+
"mypy>=1.10.0",
|
|
27
|
+
"pytest>=8.0.0",
|
|
28
|
+
"ruff>=0.8.0"
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
include = ["modis_tools*"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
modis_tools = ["py.typed"]
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 120
|
|
40
|
+
target-version = "py311"
|
|
41
|
+
|
|
42
|
+
[tool.ruff.lint]
|
|
43
|
+
select = ["E", "F", "I"]
|
|
44
|
+
|
|
45
|
+
[tool.mypy]
|
|
46
|
+
python_version = "3.11"
|
|
47
|
+
check_untyped_defs = true
|
|
48
|
+
ignore_missing_imports = true
|
|
49
|
+
no_implicit_optional = true
|
|
50
|
+
warn_unused_configs = true
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
markers = [
|
|
55
|
+
"live: tests that call live external providers",
|
|
56
|
+
"eval: opt-in quality evaluation tests that may be slower or provider-costly"
|
|
57
|
+
]
|