ninetrix-sdk 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.
@@ -0,0 +1,30 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published] # trigger when you publish a GitHub Release
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi # optional: require manual approval in GitHub UI
11
+ permissions:
12
+ id-token: write # needed for trusted publishing (no token required)
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.11"
20
+
21
+ - name: Install build tools
22
+ run: pip install build
23
+
24
+ - name: Build wheel and sdist
25
+ run: python -m build
26
+ working-directory: . # sdk/ is the repo root
27
+
28
+ - name: Publish to PyPI
29
+ uses: pypa/gh-action-pypi-publish@release/v1
30
+ # Uses PyPI Trusted Publishing — no API token needed at all
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: ninetrix-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Ninetrix — define agent tools in code
5
+ Project-URL: Homepage, https://ninetrix.io
6
+ Project-URL: Repository, https://github.com/Ninetrix-ai/ninetrix
7
+ Project-URL: Documentation, https://ninetrix.io/docs
8
+ License: Apache-2.0
9
+ Requires-Python: >=3.11
10
+ Provides-Extra: dev
11
+ Requires-Dist: mypy>=1.10; extra == 'dev'
12
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
13
+ Requires-Dist: pytest>=8.0; extra == 'dev'
14
+ Requires-Dist: ruff>=0.4; extra == 'dev'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # ninetrix-sdk
18
+
19
+ Python SDK for [Ninetrix](https://ninetrix.io) — define AI agent tools in code.
20
+
21
+ ## Phase 1: `@Tool` decorator
22
+
23
+ Connect any Python function to a Ninetrix agent. The function is auto-discovered by `ninetrix build`, bundled into the Docker image, and dispatched at runtime alongside MCP tools.
24
+
25
+ ```bash
26
+ pip install ninetrix-sdk
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Quick start
32
+
33
+ **1. Define your tools**
34
+
35
+ ```python
36
+ # tools/db_tools.py
37
+ from ninetrix import Tool
38
+
39
+ @Tool
40
+ def query_customers(sql: str, limit: int = 100) -> list[dict]:
41
+ """Execute a read-only SQL query against the customer database.
42
+
43
+ Args:
44
+ sql: A valid SELECT statement.
45
+ limit: Maximum rows to return.
46
+ """
47
+ return db.execute(sql, limit=limit)
48
+
49
+
50
+ @Tool
51
+ def send_notification(channel: str, message: str) -> bool:
52
+ """Send a message to an internal Slack channel.
53
+
54
+ Args:
55
+ channel: Destination channel (e.g. "#ops").
56
+ message: Notification text.
57
+ """
58
+ return slack.post(channel, message)
59
+ ```
60
+
61
+ **2. Reference in `agentfile.yaml`**
62
+
63
+ ```yaml
64
+ agents:
65
+ my-agent:
66
+ metadata:
67
+ role: Operations assistant
68
+ goal: Answer questions and send alerts using internal tools
69
+
70
+ runtime:
71
+ provider: anthropic
72
+ model: claude-sonnet-4-6
73
+
74
+ tools:
75
+ - name: web_search
76
+ source: mcp://brave-search # MCP tool — unchanged
77
+
78
+ - name: db_tools
79
+ source: ./tools/db_tools.py # Python tool file — NEW
80
+ ```
81
+
82
+ **3. Build and run**
83
+
84
+ ```bash
85
+ ninetrix build # discovers @Tool functions, bundles them into the image
86
+ ninetrix run # agent can now call query_customers and send_notification
87
+ ```
88
+
89
+ ---
90
+
91
+ ## How it works
92
+
93
+ | Step | What happens |
94
+ |------|-------------|
95
+ | `ninetrix build` | Scans `source: ./...py` entries, imports the file, extracts `@Tool` schemas |
96
+ | Dockerfile | Copies the `.py` files into the image, installs `ninetrix-sdk` |
97
+ | Container start | Imports the tool files, registers functions in the local registry |
98
+ | LLM call | Agent receives local tool schemas alongside MCP tool schemas |
99
+ | Tool dispatch | When the LLM calls a local tool, the Python function is invoked directly |
100
+
101
+ ---
102
+
103
+ ## `@Tool` API
104
+
105
+ ### Bare decorator
106
+
107
+ ```python
108
+ @Tool
109
+ def my_function(param: str, count: int = 5) -> str:
110
+ """One-line description used as the tool description.
111
+
112
+ Args:
113
+ param: Injected into the tool's parameter schema.
114
+ count: Optional param — not required, default shown to LLM.
115
+ """
116
+ ...
117
+ ```
118
+
119
+ ### Decorator factory (with overrides)
120
+
121
+ ```python
122
+ @Tool(name="custom_name", description="Override the docstring description.")
123
+ def my_function(param: str) -> str:
124
+ ...
125
+ ```
126
+
127
+ ### Supported parameter types
128
+
129
+ | Python type | JSON Schema |
130
+ |------------|------------|
131
+ | `str` | `{"type": "string"}` |
132
+ | `int` | `{"type": "integer"}` |
133
+ | `float` | `{"type": "number"}` |
134
+ | `bool` | `{"type": "boolean"}` |
135
+ | `list[str]` | `{"type": "array", "items": {"type": "string"}}` |
136
+ | `dict` | `{"type": "object"}` |
137
+ | `Optional[str]` | `{"type": "string"}` (not required) |
138
+ | `Literal["a","b"]` | `{"type": "string", "enum": ["a","b"]}` |
139
+
140
+ Parameters without defaults → `required`. Parameters with defaults → optional (default shown in schema).
141
+
142
+ ---
143
+
144
+ ## Multiple tool files
145
+
146
+ ```yaml
147
+ tools:
148
+ - name: db_tools
149
+ source: ./tools/db_tools.py
150
+
151
+ - name: api_tools
152
+ source: ./tools/api_tools.py
153
+
154
+ - name: web_search
155
+ source: mcp://brave-search
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Testing your tools
161
+
162
+ Tools are plain Python functions — test them directly:
163
+
164
+ ```python
165
+ # tests/test_db_tools.py
166
+ from tools.db_tools import query_customers
167
+
168
+ def test_query_customers():
169
+ result = query_customers("SELECT id FROM customers LIMIT 1")
170
+ assert isinstance(result, list)
171
+ ```
172
+
173
+ To inspect the generated schema:
174
+
175
+ ```python
176
+ from ninetrix import _registry
177
+ from tools import db_tools # triggers @Tool registrations
178
+
179
+ td = _registry.get("query_customers")
180
+ print(td.to_anthropic_schema())
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Roadmap
186
+
187
+ - **Phase 1** ✅ — `@Tool` decorator, build discovery, runtime dispatch
188
+ - **Phase 2** — `Agent(...)` class + `@Workflow` decorator for Python-first agent definition
189
+ - **Phase 3** — Durable execution: checkpoint every workflow step, resume on crash
190
+
191
+ ---
192
+
193
+ ## License
194
+
195
+ Apache 2.0
196
+
@@ -0,0 +1,180 @@
1
+ # ninetrix-sdk
2
+
3
+ Python SDK for [Ninetrix](https://ninetrix.io) — define AI agent tools in code.
4
+
5
+ ## Phase 1: `@Tool` decorator
6
+
7
+ Connect any Python function to a Ninetrix agent. The function is auto-discovered by `ninetrix build`, bundled into the Docker image, and dispatched at runtime alongside MCP tools.
8
+
9
+ ```bash
10
+ pip install ninetrix-sdk
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Quick start
16
+
17
+ **1. Define your tools**
18
+
19
+ ```python
20
+ # tools/db_tools.py
21
+ from ninetrix import Tool
22
+
23
+ @Tool
24
+ def query_customers(sql: str, limit: int = 100) -> list[dict]:
25
+ """Execute a read-only SQL query against the customer database.
26
+
27
+ Args:
28
+ sql: A valid SELECT statement.
29
+ limit: Maximum rows to return.
30
+ """
31
+ return db.execute(sql, limit=limit)
32
+
33
+
34
+ @Tool
35
+ def send_notification(channel: str, message: str) -> bool:
36
+ """Send a message to an internal Slack channel.
37
+
38
+ Args:
39
+ channel: Destination channel (e.g. "#ops").
40
+ message: Notification text.
41
+ """
42
+ return slack.post(channel, message)
43
+ ```
44
+
45
+ **2. Reference in `agentfile.yaml`**
46
+
47
+ ```yaml
48
+ agents:
49
+ my-agent:
50
+ metadata:
51
+ role: Operations assistant
52
+ goal: Answer questions and send alerts using internal tools
53
+
54
+ runtime:
55
+ provider: anthropic
56
+ model: claude-sonnet-4-6
57
+
58
+ tools:
59
+ - name: web_search
60
+ source: mcp://brave-search # MCP tool — unchanged
61
+
62
+ - name: db_tools
63
+ source: ./tools/db_tools.py # Python tool file — NEW
64
+ ```
65
+
66
+ **3. Build and run**
67
+
68
+ ```bash
69
+ ninetrix build # discovers @Tool functions, bundles them into the image
70
+ ninetrix run # agent can now call query_customers and send_notification
71
+ ```
72
+
73
+ ---
74
+
75
+ ## How it works
76
+
77
+ | Step | What happens |
78
+ |------|-------------|
79
+ | `ninetrix build` | Scans `source: ./...py` entries, imports the file, extracts `@Tool` schemas |
80
+ | Dockerfile | Copies the `.py` files into the image, installs `ninetrix-sdk` |
81
+ | Container start | Imports the tool files, registers functions in the local registry |
82
+ | LLM call | Agent receives local tool schemas alongside MCP tool schemas |
83
+ | Tool dispatch | When the LLM calls a local tool, the Python function is invoked directly |
84
+
85
+ ---
86
+
87
+ ## `@Tool` API
88
+
89
+ ### Bare decorator
90
+
91
+ ```python
92
+ @Tool
93
+ def my_function(param: str, count: int = 5) -> str:
94
+ """One-line description used as the tool description.
95
+
96
+ Args:
97
+ param: Injected into the tool's parameter schema.
98
+ count: Optional param — not required, default shown to LLM.
99
+ """
100
+ ...
101
+ ```
102
+
103
+ ### Decorator factory (with overrides)
104
+
105
+ ```python
106
+ @Tool(name="custom_name", description="Override the docstring description.")
107
+ def my_function(param: str) -> str:
108
+ ...
109
+ ```
110
+
111
+ ### Supported parameter types
112
+
113
+ | Python type | JSON Schema |
114
+ |------------|------------|
115
+ | `str` | `{"type": "string"}` |
116
+ | `int` | `{"type": "integer"}` |
117
+ | `float` | `{"type": "number"}` |
118
+ | `bool` | `{"type": "boolean"}` |
119
+ | `list[str]` | `{"type": "array", "items": {"type": "string"}}` |
120
+ | `dict` | `{"type": "object"}` |
121
+ | `Optional[str]` | `{"type": "string"}` (not required) |
122
+ | `Literal["a","b"]` | `{"type": "string", "enum": ["a","b"]}` |
123
+
124
+ Parameters without defaults → `required`. Parameters with defaults → optional (default shown in schema).
125
+
126
+ ---
127
+
128
+ ## Multiple tool files
129
+
130
+ ```yaml
131
+ tools:
132
+ - name: db_tools
133
+ source: ./tools/db_tools.py
134
+
135
+ - name: api_tools
136
+ source: ./tools/api_tools.py
137
+
138
+ - name: web_search
139
+ source: mcp://brave-search
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Testing your tools
145
+
146
+ Tools are plain Python functions — test them directly:
147
+
148
+ ```python
149
+ # tests/test_db_tools.py
150
+ from tools.db_tools import query_customers
151
+
152
+ def test_query_customers():
153
+ result = query_customers("SELECT id FROM customers LIMIT 1")
154
+ assert isinstance(result, list)
155
+ ```
156
+
157
+ To inspect the generated schema:
158
+
159
+ ```python
160
+ from ninetrix import _registry
161
+ from tools import db_tools # triggers @Tool registrations
162
+
163
+ td = _registry.get("query_customers")
164
+ print(td.to_anthropic_schema())
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Roadmap
170
+
171
+ - **Phase 1** ✅ — `@Tool` decorator, build discovery, runtime dispatch
172
+ - **Phase 2** — `Agent(...)` class + `@Workflow` decorator for Python-first agent definition
173
+ - **Phase 3** — Durable execution: checkpoint every workflow step, resume on crash
174
+
175
+ ---
176
+
177
+ ## License
178
+
179
+ Apache 2.0
180
+
@@ -0,0 +1,68 @@
1
+ """
2
+ Example: custom database + API tools for a Ninetrix agent.
3
+
4
+ Reference in agentfile.yaml:
5
+ tools:
6
+ - name: db_tools
7
+ source: ./examples/db_tools.py
8
+
9
+ Then ninetrix build bundles this file and registers all @Tool functions.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Literal
15
+
16
+ from ninetrix import Tool
17
+
18
+
19
+ @Tool
20
+ def query_customers(sql: str, limit: int = 100) -> list[dict]:
21
+ """Execute a read-only SQL query against the customer database.
22
+
23
+ Args:
24
+ sql: A valid SELECT statement. UPDATE/DELETE/INSERT are not allowed.
25
+ limit: Maximum number of rows to return. Hard-capped at 1000.
26
+ """
27
+ # Replace with real DB call
28
+ raise NotImplementedError("Connect to your database here")
29
+
30
+
31
+ @Tool
32
+ def get_customer(customer_id: str) -> dict:
33
+ """Fetch a single customer record by ID.
34
+
35
+ Args:
36
+ customer_id: The unique customer identifier (UUID or integer string).
37
+ """
38
+ raise NotImplementedError
39
+
40
+
41
+ @Tool
42
+ def search_products(
43
+ query: str,
44
+ category: str | None = None,
45
+ sort: Literal["price_asc", "price_desc", "relevance"] = "relevance",
46
+ limit: int = 20,
47
+ ) -> list[dict]:
48
+ """Search the product catalog.
49
+
50
+ Args:
51
+ query: Full-text search query.
52
+ category: Optional category filter (e.g. "electronics", "apparel").
53
+ sort: Sort order for results.
54
+ limit: Maximum products to return.
55
+ """
56
+ raise NotImplementedError
57
+
58
+
59
+ @Tool(name="send_internal_notification")
60
+ def notify(channel: str, message: str, priority: Literal["low", "high"] = "low") -> bool:
61
+ """Send a notification to an internal Slack channel or PagerDuty.
62
+
63
+ Args:
64
+ channel: Destination channel name (e.g. "#ops-alerts").
65
+ message: Notification body. Keep it under 500 characters.
66
+ priority: "high" pages on-call, "low" posts silently.
67
+ """
68
+ raise NotImplementedError
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ninetrix-sdk"
7
+ version = "0.1.0"
8
+ description = "Python SDK for Ninetrix — define agent tools in code"
9
+ readme = "README.md"
10
+ license = { text = "Apache-2.0" }
11
+ requires-python = ">=3.11"
12
+ dependencies = []
13
+
14
+ [project.optional-dependencies]
15
+ dev = [
16
+ "pytest>=8.0",
17
+ "pytest-cov>=5.0",
18
+ "ruff>=0.4",
19
+ "mypy>=1.10",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://ninetrix.io"
24
+ Repository = "https://github.com/Ninetrix-ai/ninetrix"
25
+ Documentation = "https://ninetrix.io/docs"
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["src/ninetrix"]
29
+
30
+ [tool.ruff]
31
+ line-length = 88
32
+ target-version = "py311"
33
+
34
+ [tool.ruff.lint]
35
+ select = ["E", "F", "I", "UP", "B", "SIM"]
36
+ ignore = ["E501"]
37
+
38
+ [tool.mypy]
39
+ python_version = "3.11"
40
+ strict = true
41
+ ignore_missing_imports = true
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+ addopts = "--tb=short -q"
46
+
47
+ [tool.coverage.run]
48
+ source = ["src/ninetrix"]
49
+ omit = ["tests/*"]
@@ -0,0 +1,60 @@
1
+ """
2
+ Ninetrix SDK — Python primitives for building AI agent tools.
3
+
4
+ Phase 1: @Tool decorator
5
+ Define Python functions as agent-callable tools. They are auto-discovered
6
+ by ``ninetrix build``, bundled into the Docker image, and dispatched at
7
+ runtime alongside MCP tools.
8
+
9
+ Phase 2 (upcoming): Agent + Workflow
10
+ Define full agents and complex workflows in Python.
11
+
12
+ Quick start::
13
+
14
+ from ninetrix import Tool
15
+
16
+ @Tool
17
+ def query_customers(sql: str, limit: int = 100) -> list[dict]:
18
+ \"\"\"Run a read-only SQL query against the customer database.
19
+
20
+ Args:
21
+ sql: A valid SELECT statement.
22
+ limit: Maximum rows to return.
23
+ \"\"\"
24
+ return db.execute(sql, limit=limit)
25
+
26
+ Reference the tool in ``agentfile.yaml``::
27
+
28
+ tools:
29
+ - name: query_customers
30
+ source: ./tools/db_tools.py
31
+
32
+ Then build and run as usual::
33
+
34
+ ninetrix build
35
+ ninetrix run
36
+ """
37
+
38
+ from ninetrix.tool import Tool
39
+ from ninetrix.registry import ToolDef, ToolRegistry, _registry
40
+ from ninetrix.discover import (
41
+ discover_tools_in_file,
42
+ discover_tools_in_files,
43
+ load_local_tools,
44
+ )
45
+
46
+ __version__ = "0.1.0"
47
+ __all__ = [
48
+ # Primary public API
49
+ "Tool",
50
+ # Registry types (useful for type hints and testing)
51
+ "ToolDef",
52
+ "ToolRegistry",
53
+ # Build-time discovery
54
+ "discover_tools_in_file",
55
+ "discover_tools_in_files",
56
+ # Runtime loader (called by generated entrypoint)
57
+ "load_local_tools",
58
+ # Internal registry (advanced / testing)
59
+ "_registry",
60
+ ]