toolbase 0.1.0__py3-none-any.whl
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.
- toolbase/__init__.py +22 -0
- toolbase/_setup_host.py +243 -0
- toolbase/_toolkit_host.py +585 -0
- toolbase/astro.py +9 -0
- toolbase/auth.py +631 -0
- toolbase/cli.py +5510 -0
- toolbase/config.py +41 -0
- toolbase/envs/__init__.py +147 -0
- toolbase/envs/cache.py +326 -0
- toolbase/envs/config.py +122 -0
- toolbase/envs/discovery.py +115 -0
- toolbase/envs/manifest.py +209 -0
- toolbase/envs/paths.py +163 -0
- toolbase/envs/schema.py +348 -0
- toolbase/hep.py +8 -0
- toolbase/ingest.py +913 -0
- toolbase/logging/__init__.py +5 -0
- toolbase/logging/logger.py +558 -0
- toolbase/neutrino.py +7 -0
- toolbase/quantum.py +7 -0
- toolbase/serve/__init__.py +7 -0
- toolbase/serve/config.py +436 -0
- toolbase/serve/orchestrator.py +1526 -0
- toolbase/serve/proxy_tool.py +134 -0
- toolbase/serve/tool_groups.py +189 -0
- toolbase/setup/__init__.py +91 -0
- toolbase/setup/_rpc.py +326 -0
- toolbase/setup/context.py +379 -0
- toolbase/setup/declarative.py +363 -0
- toolbase/setup/downloads.py +416 -0
- toolbase/setup/prompts.py +271 -0
- toolbase/setup/runner.py +1179 -0
- toolbase/setup/schema.py +465 -0
- toolbase/setup/storage.py +364 -0
- toolbase/setup/validate_cache.py +152 -0
- toolbase/skills.py +256 -0
- toolbase/templates/Dockerfile.template +25 -0
- toolbase/templates/README.md.template +50 -0
- toolbase/templates/__init__.py.template +10 -0
- toolbase/templates/mcp/__init__.py.template +4 -0
- toolbase/templates/mcp/server_stdio.py.template +46 -0
- toolbase/templates/requirements.txt.template +11 -0
- toolbase/templates/setup.py.template +94 -0
- toolbase/templates/skills/example_skill.md +44 -0
- toolbase/templates/tool_example.py +100 -0
- toolbase/templates/toolkit.yaml.template +68 -0
- toolbase/toolkit.py +387 -0
- toolbase/validation.py +801 -0
- toolbase/versioning.py +100 -0
- toolbase-0.1.0.dist-info/METADATA +247 -0
- toolbase-0.1.0.dist-info/RECORD +55 -0
- toolbase-0.1.0.dist-info/WHEEL +5 -0
- toolbase-0.1.0.dist-info/entry_points.txt +3 -0
- toolbase-0.1.0.dist-info/licenses/LICENSE +21 -0
- toolbase-0.1.0.dist-info/top_level.txt +1 -0
toolbase/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Toolbase - The community registry and CLI for AI agent toolkits
|
|
3
|
+
|
|
4
|
+
Toolbase provides a centralized platform for discovering, sharing, and using
|
|
5
|
+
tools for AI agents. Think of it as an app store for agent toolkits across any
|
|
6
|
+
domain - from general-purpose utilities to specialized fields like astrophysics,
|
|
7
|
+
high-energy physics, and quantum computing.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- Easy tool creation and sharing
|
|
11
|
+
- Curated categories across domains
|
|
12
|
+
- MCP (Model Context Protocol) compatibility
|
|
13
|
+
- Integration with Orchestral AI and other agent frameworks
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
__author__ = "Alex Roman"
|
|
18
|
+
|
|
19
|
+
# Placeholder imports for future toolkit categories
|
|
20
|
+
# from .astro import aster
|
|
21
|
+
# from .hep import heptapod
|
|
22
|
+
# from .quantum import quantum_toolkit
|
toolbase/_setup_host.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Per-toolkit subprocess host for Phase 3C-2 setup.
|
|
3
|
+
|
|
4
|
+
Mirrors ``_toolkit_host.py``'s shape but for the setup lifecycle
|
|
5
|
+
instead of the serve lifecycle:
|
|
6
|
+
|
|
7
|
+
- Runs *inside* a toolkit's Python interpreter (its own venv/conda env)
|
|
8
|
+
so ``setup.py`` can import the toolkit's declared dependencies.
|
|
9
|
+
- Talks to the parent process over line-mode JSON-RPC on stdin/stdout
|
|
10
|
+
(see ``toolbase/setup/_rpc.py``). This is **not** MCP and is not
|
|
11
|
+
the same protocol the serve-time host speaks.
|
|
12
|
+
- Imports only stdlib + a minimal slice of ``toolbase.setup`` (the
|
|
13
|
+
RPC primitives and ``SetupContext``). The parent ships those into
|
|
14
|
+
the toolkit env via ``PYTHONPATH``, the same way ``_toolkit_host.py``
|
|
15
|
+
is reachable from the toolkit env today.
|
|
16
|
+
|
|
17
|
+
The lifecycle:
|
|
18
|
+
|
|
19
|
+
1. Parse ``--toolkit-dir``, ``--name`` from argv.
|
|
20
|
+
2. Open the line-mode RPC channel over stdin/stdout.
|
|
21
|
+
3. Send a ``hello`` message announcing protocol version + which
|
|
22
|
+
functions ``setup.py`` exports (``setup`` and/or ``validate``).
|
|
23
|
+
4. Wait for ``go`` from parent. ``go`` carries the mode (``setup``
|
|
24
|
+
or ``validate``), prompt mode, current config snapshot, and the
|
|
25
|
+
four standard paths.
|
|
26
|
+
5. Construct ``SetupContext`` from the ``go`` payload.
|
|
27
|
+
6. Invoke ``setup.py::setup(ctx)`` or ``setup.py::validate(ctx)``.
|
|
28
|
+
7. Send ``done`` with the function's return value (cast to bool) and
|
|
29
|
+
any traceback if it raised.
|
|
30
|
+
8. Exit.
|
|
31
|
+
|
|
32
|
+
Errors during setup.py loading or invocation are captured as a
|
|
33
|
+
``done`` message with ``result=false`` + a traceback. The runner on
|
|
34
|
+
the parent side renders the one-line summary and writes the full
|
|
35
|
+
traceback to ``~/.toolbase/logs/setup-<toolkit>-<date>.log`` per
|
|
36
|
+
the spec's error-handling rules.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import argparse
|
|
42
|
+
import importlib.util
|
|
43
|
+
import io
|
|
44
|
+
import json
|
|
45
|
+
import sys
|
|
46
|
+
import traceback
|
|
47
|
+
from pathlib import Path
|
|
48
|
+
from typing import Any, Optional
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _load_setup_module(toolkit_dir: Path):
|
|
52
|
+
"""Import ``<toolkit_dir>/setup.py`` without polluting sys.path.
|
|
53
|
+
|
|
54
|
+
Same discipline as ``_toolkit_host.py::_import_tools_package``:
|
|
55
|
+
use an explicit ``spec_from_file_location`` so the toolkit's
|
|
56
|
+
sibling directories don't shadow installed packages. ``setup.py``
|
|
57
|
+
sits at toolkit root; tools live in ``tools/``. The two are
|
|
58
|
+
independent imports.
|
|
59
|
+
|
|
60
|
+
Returns the loaded module, or None if no setup.py exists.
|
|
61
|
+
"""
|
|
62
|
+
setup_file = toolkit_dir / "setup.py"
|
|
63
|
+
if not setup_file.exists():
|
|
64
|
+
return None
|
|
65
|
+
# Use a unique module name so we don't collide with anything else
|
|
66
|
+
# the toolkit might import that happens to be called 'setup'.
|
|
67
|
+
spec = importlib.util.spec_from_file_location(
|
|
68
|
+
"_toolbase_toolkit_setup",
|
|
69
|
+
str(setup_file),
|
|
70
|
+
)
|
|
71
|
+
if spec is None or spec.loader is None:
|
|
72
|
+
raise ImportError(f"could not build module spec for {setup_file}")
|
|
73
|
+
module = importlib.util.module_from_spec(spec)
|
|
74
|
+
sys.modules["_toolbase_toolkit_setup"] = module
|
|
75
|
+
spec.loader.exec_module(module)
|
|
76
|
+
return module
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def main(argv: Optional[list] = None) -> int:
|
|
80
|
+
parser = argparse.ArgumentParser(
|
|
81
|
+
prog="python -m toolbase._setup_host",
|
|
82
|
+
description="Per-toolkit subprocess host for toolbase setup.",
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--toolkit-dir", required=True, type=Path,
|
|
86
|
+
help="Path to the installed toolkit directory.",
|
|
87
|
+
)
|
|
88
|
+
parser.add_argument(
|
|
89
|
+
"--name", required=True,
|
|
90
|
+
help="Toolkit name (used in error messages).",
|
|
91
|
+
)
|
|
92
|
+
args = parser.parse_args(argv)
|
|
93
|
+
|
|
94
|
+
# Import the RPC primitives and SetupContext from the parent's
|
|
95
|
+
# PYTHONPATH-injected toolbase slice.
|
|
96
|
+
try:
|
|
97
|
+
from toolbase.setup import _rpc
|
|
98
|
+
from toolbase.setup.context import SetupContext, _SetupContextRPC
|
|
99
|
+
except ImportError as e: # pragma: no cover — defensive
|
|
100
|
+
sys.stderr.write(
|
|
101
|
+
f"setup-host: failed to import toolbase.setup: {e}\n"
|
|
102
|
+
"(parent should have set PYTHONPATH; this is a bug in the "
|
|
103
|
+
"orchestrator's spawn wiring)\n"
|
|
104
|
+
)
|
|
105
|
+
return 2
|
|
106
|
+
|
|
107
|
+
# The line-mode helpers expect text streams. stdout/stdin should
|
|
108
|
+
# already be text-mode under Python 3, but force UTF-8 to match the
|
|
109
|
+
# parent's expectations exactly (the parent uses ensure_ascii=False
|
|
110
|
+
# when emitting messages with potential Unicode, so the channel
|
|
111
|
+
# must be UTF-8 capable end-to-end).
|
|
112
|
+
rx = sys.stdin
|
|
113
|
+
tx = sys.stdout
|
|
114
|
+
# Reconfigure for UTF-8 if possible. ``reconfigure`` exists on
|
|
115
|
+
# Python 3.7+; safe to call multiple times.
|
|
116
|
+
try:
|
|
117
|
+
rx.reconfigure(encoding="utf-8") # type: ignore[union-attr]
|
|
118
|
+
tx.reconfigure(encoding="utf-8") # type: ignore[union-attr]
|
|
119
|
+
except (AttributeError, io.UnsupportedOperation):
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
# Step 1: try to load setup.py. We hold any load-time exception
|
|
123
|
+
# to be reported as the eventual ``done``, not as a pre-handshake
|
|
124
|
+
# error — the protocol is "always send hello first," and the
|
|
125
|
+
# parent's pump treats a pre-hello ``done`` as a protocol violation.
|
|
126
|
+
setup_module = None
|
|
127
|
+
load_traceback: Optional[str] = None
|
|
128
|
+
try:
|
|
129
|
+
setup_module = _load_setup_module(args.toolkit_dir)
|
|
130
|
+
except Exception:
|
|
131
|
+
load_traceback = traceback.format_exc()
|
|
132
|
+
|
|
133
|
+
has_setup = bool(setup_module and callable(getattr(setup_module, "setup", None)))
|
|
134
|
+
has_validate = bool(setup_module and callable(getattr(setup_module, "validate", None)))
|
|
135
|
+
|
|
136
|
+
# Step 2: send hello. If the setup.py couldn't even load, we
|
|
137
|
+
# advertise has_setup=has_validate=False AND include the load
|
|
138
|
+
# traceback in ``load_error``. The parent uses load_error to
|
|
139
|
+
# surface the actual root cause (syntax error, ImportError) to
|
|
140
|
+
# the user rather than the misleading "setup() not defined."
|
|
141
|
+
try:
|
|
142
|
+
_rpc.write_message(tx, _rpc.make_hello(
|
|
143
|
+
has_setup=has_setup,
|
|
144
|
+
has_validate=has_validate,
|
|
145
|
+
load_error=load_traceback,
|
|
146
|
+
))
|
|
147
|
+
except Exception:
|
|
148
|
+
return 4
|
|
149
|
+
|
|
150
|
+
# Step 3: wait for go. The parent may decline to send 'go' if it
|
|
151
|
+
# decided based on the hello alone that there's nothing to do
|
|
152
|
+
# (e.g., validate-mode + has_validate=False); EOF here is fine.
|
|
153
|
+
msg = _rpc.read_message(rx)
|
|
154
|
+
if msg is None:
|
|
155
|
+
# Parent disconnected without sending go — clean exit.
|
|
156
|
+
return 0
|
|
157
|
+
if msg.method != "go":
|
|
158
|
+
try:
|
|
159
|
+
_rpc.write_message(tx, _rpc.make_done(
|
|
160
|
+
result=False,
|
|
161
|
+
traceback_str=f"expected 'go' message, got {msg.method!r}",
|
|
162
|
+
))
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
return 6
|
|
166
|
+
go_params = msg.params
|
|
167
|
+
|
|
168
|
+
mode = go_params.get("mode", "setup")
|
|
169
|
+
prompt_mode = go_params.get("prompt_mode", "ask")
|
|
170
|
+
config_snapshot = go_params.get("config") or {}
|
|
171
|
+
toolkit_path = Path(go_params.get("toolkit_path") or args.toolkit_dir)
|
|
172
|
+
data_dir = Path(go_params.get("data_dir") or "")
|
|
173
|
+
cache_dir = Path(go_params.get("cache_dir") or "")
|
|
174
|
+
config_path = Path(go_params.get("config_path") or "")
|
|
175
|
+
|
|
176
|
+
# Step 4: build the RPC client and ctx.
|
|
177
|
+
rpc_client = _SetupContextRPC(rx=rx, tx=tx)
|
|
178
|
+
ctx = SetupContext(
|
|
179
|
+
rpc=rpc_client,
|
|
180
|
+
mode=mode,
|
|
181
|
+
prompt_mode=prompt_mode,
|
|
182
|
+
config_snapshot=config_snapshot,
|
|
183
|
+
toolkit_path=toolkit_path,
|
|
184
|
+
data_dir=data_dir,
|
|
185
|
+
cache_dir=cache_dir,
|
|
186
|
+
config_path=config_path,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Step 5: invoke the requested function.
|
|
190
|
+
if mode == "validate":
|
|
191
|
+
if not has_validate:
|
|
192
|
+
# No validate(ctx) defined → trivially passes. The
|
|
193
|
+
# canonical "no setup_script means no validate" path goes
|
|
194
|
+
# through the orchestrator's _resolve_state_config, not
|
|
195
|
+
# through here, but defending against the edge is cheap.
|
|
196
|
+
_rpc.write_message(tx, _rpc.make_done(result=True))
|
|
197
|
+
return 0
|
|
198
|
+
target_fn = setup_module.validate
|
|
199
|
+
target_label = "validate"
|
|
200
|
+
else:
|
|
201
|
+
if not has_setup:
|
|
202
|
+
_rpc.write_message(tx, _rpc.make_done(
|
|
203
|
+
result=False,
|
|
204
|
+
traceback_str=(
|
|
205
|
+
f"setup.py at {args.toolkit_dir / 'setup.py'} does not "
|
|
206
|
+
"define `setup(ctx)`. See "
|
|
207
|
+
"https://toolbase-ai.com/docs/configuration#setup-script "
|
|
208
|
+
"(or tb-package/docs/SETUP_SYSTEM_SPEC.md §'Tier 2 — "
|
|
209
|
+
"script' until that page lands)."
|
|
210
|
+
),
|
|
211
|
+
))
|
|
212
|
+
return 0
|
|
213
|
+
target_fn = setup_module.setup
|
|
214
|
+
target_label = "setup"
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
result = target_fn(ctx)
|
|
218
|
+
except Exception:
|
|
219
|
+
tb = traceback.format_exc()
|
|
220
|
+
try:
|
|
221
|
+
_rpc.write_message(tx, _rpc.make_done(
|
|
222
|
+
result=False,
|
|
223
|
+
traceback_str=tb,
|
|
224
|
+
))
|
|
225
|
+
except Exception:
|
|
226
|
+
pass
|
|
227
|
+
return 0 # not 7 — we communicated cleanly; the *result* is failure.
|
|
228
|
+
|
|
229
|
+
# Allow setup/validate to return None (treat as success) for
|
|
230
|
+
# author-friendly ergonomics. Authors who explicitly return False
|
|
231
|
+
# signal failure; everyone else just returns.
|
|
232
|
+
success = True if result is None else bool(result)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
_rpc.write_message(tx, _rpc.make_done(result=success))
|
|
236
|
+
except Exception:
|
|
237
|
+
return 8
|
|
238
|
+
|
|
239
|
+
return 0
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if __name__ == "__main__":
|
|
243
|
+
sys.exit(main())
|