api-operator 0.9.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.
- api_operator-0.9.0/.env.example +6 -0
- api_operator-0.9.0/.github/workflows/tests.yml +28 -0
- api_operator-0.9.0/.gitignore +35 -0
- api_operator-0.9.0/CHANGELOG.md +24 -0
- api_operator-0.9.0/LICENSE +21 -0
- api_operator-0.9.0/PKG-INFO +206 -0
- api_operator-0.9.0/README.md +169 -0
- api_operator-0.9.0/api_operator/__init__.py +8 -0
- api_operator-0.9.0/api_operator/adapters/__init__.py +1 -0
- api_operator-0.9.0/api_operator/adapters/base.py +25 -0
- api_operator-0.9.0/api_operator/adapters/mock.py +145 -0
- api_operator-0.9.0/api_operator/adapters/openapi_generator.py +238 -0
- api_operator-0.9.0/api_operator/adapters/registry.py +71 -0
- api_operator-0.9.0/api_operator/adapters/yaml_adapter.py +175 -0
- api_operator-0.9.0/api_operator/adapters/yaml_spec.py +154 -0
- api_operator-0.9.0/api_operator/core/agent.py +160 -0
- api_operator-0.9.0/api_operator/core/config.py +18 -0
- api_operator-0.9.0/api_operator/core/executor.py +38 -0
- api_operator-0.9.0/api_operator/core/guardrails.py +53 -0
- api_operator-0.9.0/api_operator/core/memory.py +46 -0
- api_operator-0.9.0/api_operator/core/planner.py +189 -0
- api_operator-0.9.0/api_operator/core/planner_openai.py +71 -0
- api_operator-0.9.0/api_operator/factory.py +26 -0
- api_operator-0.9.0/api_operator/rag/indexer.py +37 -0
- api_operator-0.9.0/api_operator/server/app.py +114 -0
- api_operator-0.9.0/api_operator/server/cli.py +201 -0
- api_operator-0.9.0/api_operator/tools/base.py +69 -0
- api_operator-0.9.0/api_operator/tools/schema.py +34 -0
- api_operator-0.9.0/examples/tenant-kit-adapter/README.md +71 -0
- api_operator-0.9.0/examples/tenant-kit-adapter/adapter.yaml +81 -0
- api_operator-0.9.0/pyproject.toml +57 -0
- api_operator-0.9.0/scripts/integration_tenant_kit.py +305 -0
- api_operator-0.9.0/tests/__init__.py +1 -0
- api_operator-0.9.0/tests/test_agent.py +95 -0
- api_operator-0.9.0/tests/test_api.py +152 -0
- api_operator-0.9.0/tests/test_cli.py +89 -0
- api_operator-0.9.0/tests/test_core.py +151 -0
- api_operator-0.9.0/tests/test_integration_tenant_kit.py +34 -0
- api_operator-0.9.0/tests/test_mock_adapter.py +93 -0
- api_operator-0.9.0/tests/test_scenarios_e2e.py +111 -0
- api_operator-0.9.0/tests/test_yaml_adapter.py +185 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install
|
|
25
|
+
run: pip install -e ".[dev]"
|
|
26
|
+
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: pytest -q
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.eggs/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.egg
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
ENV/
|
|
15
|
+
|
|
16
|
+
# Tools
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# Environment & secrets
|
|
24
|
+
.env
|
|
25
|
+
.env.local
|
|
26
|
+
*.pem
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
*.swp
|
|
32
|
+
|
|
33
|
+
# OS
|
|
34
|
+
.DS_Store
|
|
35
|
+
Thumbs.db
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.9.0] - 2026-06-14
|
|
6
|
+
|
|
7
|
+
Renamed to **`api-operator`** (GitHub + PyPI). PyPI blocks names starting with `operator` (stdlib conflict).
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Core agent runtime: planning, guardrails, tool execution, session memory
|
|
12
|
+
- `mock` adapter for offline demos and tests
|
|
13
|
+
- `yaml` adapter — configure HTTP tools without Python
|
|
14
|
+
- OpenAPI scaffold CLI (`generate-from-openapi`, `scaffold-adapter`)
|
|
15
|
+
- CLI: `chat`, `demo`, `tools`, `serve`
|
|
16
|
+
- FastAPI server: `/health`, `/v1/adapters`, `/v1/tools`, `/v1/chat`
|
|
17
|
+
- Example adapter for [Laravel Tenant Kit](https://github.com/mohammedelkarsh/laravel-tenant-kit)
|
|
18
|
+
- Live integration script: `scripts/integration_tenant_kit.py`
|
|
19
|
+
- 69 unit tests; optional pytest integration marker
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
|
|
23
|
+
- Default planner is rule-based (`mock`); OpenAI planner is optional (`pip install api-operator[llm]`)
|
|
24
|
+
- Tenant team invites require a tenant-scoped Sanctum token (see example adapter README)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammed Elkarsh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: api-operator
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Standalone AI API Operator with pluggable adapters for multi-project APIs
|
|
5
|
+
Project-URL: Homepage, https://github.com/mohammedelkarsh/api-operator
|
|
6
|
+
Project-URL: Repository, https://github.com/mohammedelkarsh/api-operator
|
|
7
|
+
Project-URL: Issues, https://github.com/mohammedelkarsh/api-operator/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/mohammedelkarsh/api-operator/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Mohammed Elkarsh
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,ai,llm,multi-tenant,openapi,saas,tools,yaml
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: fastapi>=0.111.0
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: pydantic-settings>=2.3.0
|
|
25
|
+
Requires-Dist: pydantic>=2.7.0
|
|
26
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
27
|
+
Requires-Dist: rich>=13.7.0
|
|
28
|
+
Requires-Dist: typer>=0.12.0
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.2.0; extra == 'dev'
|
|
34
|
+
Provides-Extra: llm
|
|
35
|
+
Requires-Dist: openai>=1.35.0; extra == 'llm'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# API Operator
|
|
39
|
+
|
|
40
|
+
[](https://github.com/mohammedelkarsh/api-operator/actions/workflows/tests.yml)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://www.python.org/downloads/)
|
|
43
|
+
|
|
44
|
+
Standalone **AI operator runtime** with pluggable adapters. Talk to your HTTP APIs instead of clicking through admin panels.
|
|
45
|
+
|
|
46
|
+
Works with Laravel, Go, connectivity platforms, or any backend with a REST API.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/mohammedelkarsh/api-operator.git
|
|
52
|
+
cd api-operator
|
|
53
|
+
pip install -e ".[dev]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Optional OpenAI planner:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e ".[dev,llm]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
api-operator demo
|
|
66
|
+
api-operator tools --adapter mock
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## What ships in the core package
|
|
70
|
+
|
|
71
|
+
| In core | In your projects |
|
|
72
|
+
|---------|------------------|
|
|
73
|
+
| Agent runtime (plan, guard, execute) | `adapter.yaml` per API |
|
|
74
|
+
| `mock` adapter (demo + tests) | OpenAPI specs |
|
|
75
|
+
| `yaml` adapter | API tokens in `.env` |
|
|
76
|
+
| CLI + HTTP server | Optional custom adapters |
|
|
77
|
+
|
|
78
|
+
## Build an adapter without Python
|
|
79
|
+
|
|
80
|
+
### Scaffold template
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
api-operator scaffold-adapter my-api --output examples
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Generate from OpenAPI
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
api-operator generate-from-openapi openapi.yaml \
|
|
90
|
+
--output adapter.yaml \
|
|
91
|
+
--base-url http://api.example.test \
|
|
92
|
+
--path-prefix /api
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Laravel Tenant Kit example
|
|
96
|
+
|
|
97
|
+
See [`examples/tenant-kit-adapter/`](examples/tenant-kit-adapter/) — pairs with [laravel-tenant-kit](https://github.com/mohammedelkarsh/laravel-tenant-kit).
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
export TENANT_KIT_API_TOKEN="your-sanctum-token"
|
|
101
|
+
api-operator chat \
|
|
102
|
+
--adapter yaml \
|
|
103
|
+
--config examples/tenant-kit-adapter/adapter.yaml \
|
|
104
|
+
--base-url http://laravel-tenant-kit.test
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## adapter.yaml (minimal)
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
name: my_project
|
|
111
|
+
description: My API adapter
|
|
112
|
+
base_url: http://api.example.test
|
|
113
|
+
|
|
114
|
+
auth:
|
|
115
|
+
type: bearer
|
|
116
|
+
env_token: MY_PROJECT_API_TOKEN
|
|
117
|
+
|
|
118
|
+
tools:
|
|
119
|
+
- name: list_items
|
|
120
|
+
description: List items
|
|
121
|
+
method: GET
|
|
122
|
+
path: /api/items
|
|
123
|
+
|
|
124
|
+
- name: create_item
|
|
125
|
+
description: Create item
|
|
126
|
+
method: POST
|
|
127
|
+
path: /api/items
|
|
128
|
+
dangerous: true
|
|
129
|
+
parameters:
|
|
130
|
+
title:
|
|
131
|
+
type: string
|
|
132
|
+
required: true
|
|
133
|
+
body:
|
|
134
|
+
title: "{title}"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Tenant subdomain APIs:
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
- name: invite_member
|
|
141
|
+
method: POST
|
|
142
|
+
path: /api/team/invitations
|
|
143
|
+
host: tenant
|
|
144
|
+
tenant_param: subdomain
|
|
145
|
+
parameters:
|
|
146
|
+
subdomain: { type: string, required: true }
|
|
147
|
+
email: { type: string, required: true }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## HTTP server
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
api-operator serve --port 8100
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
POST /v1/chat
|
|
158
|
+
{
|
|
159
|
+
"adapter": "yaml",
|
|
160
|
+
"config_path": "examples/tenant-kit-adapter/adapter.yaml",
|
|
161
|
+
"adapter_config": { "token": "YOUR_TOKEN" },
|
|
162
|
+
"message": "list workspaces",
|
|
163
|
+
"abilities": ["workspaces:read"]
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Integration test (Tenant Kit)
|
|
168
|
+
|
|
169
|
+
With [Tenant Kit](https://github.com/mohammedelkarsh/laravel-tenant-kit) running:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
python scripts/integration_tenant_kit.py
|
|
173
|
+
# Docker on port 8080:
|
|
174
|
+
python scripts/integration_tenant_kit.py --base-url http://laravel-tenant-kit.test:8080
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Optional pytest marker (requires env vars):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
export TENANT_KIT_BASE_URL=http://laravel-tenant-kit.test
|
|
181
|
+
export TENANT_KIT_API_TOKEN=your-token
|
|
182
|
+
pytest -m integration -q
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Tests
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pytest -q
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Configuration
|
|
192
|
+
|
|
193
|
+
Copy `.env.example` to `.env` for local defaults (`api_operator_PLANNER`, port, etc.).
|
|
194
|
+
|
|
195
|
+
## Architecture
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
api-operator (core) your projects
|
|
199
|
+
├── agent runtime ├── adapter.yaml
|
|
200
|
+
├── mock + yaml adapters ├── openapi.yaml
|
|
201
|
+
└── scaffold / generate CLI └── HTTP APIs (Laravel, Go, …)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# API Operator
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mohammedelkarsh/api-operator/actions/workflows/tests.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
|
|
7
|
+
Standalone **AI operator runtime** with pluggable adapters. Talk to your HTTP APIs instead of clicking through admin panels.
|
|
8
|
+
|
|
9
|
+
Works with Laravel, Go, connectivity platforms, or any backend with a REST API.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/mohammedelkarsh/api-operator.git
|
|
15
|
+
cd api-operator
|
|
16
|
+
pip install -e ".[dev]"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Optional OpenAI planner:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install -e ".[dev,llm]"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
api-operator demo
|
|
29
|
+
api-operator tools --adapter mock
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## What ships in the core package
|
|
33
|
+
|
|
34
|
+
| In core | In your projects |
|
|
35
|
+
|---------|------------------|
|
|
36
|
+
| Agent runtime (plan, guard, execute) | `adapter.yaml` per API |
|
|
37
|
+
| `mock` adapter (demo + tests) | OpenAPI specs |
|
|
38
|
+
| `yaml` adapter | API tokens in `.env` |
|
|
39
|
+
| CLI + HTTP server | Optional custom adapters |
|
|
40
|
+
|
|
41
|
+
## Build an adapter without Python
|
|
42
|
+
|
|
43
|
+
### Scaffold template
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
api-operator scaffold-adapter my-api --output examples
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Generate from OpenAPI
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
api-operator generate-from-openapi openapi.yaml \
|
|
53
|
+
--output adapter.yaml \
|
|
54
|
+
--base-url http://api.example.test \
|
|
55
|
+
--path-prefix /api
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Laravel Tenant Kit example
|
|
59
|
+
|
|
60
|
+
See [`examples/tenant-kit-adapter/`](examples/tenant-kit-adapter/) — pairs with [laravel-tenant-kit](https://github.com/mohammedelkarsh/laravel-tenant-kit).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export TENANT_KIT_API_TOKEN="your-sanctum-token"
|
|
64
|
+
api-operator chat \
|
|
65
|
+
--adapter yaml \
|
|
66
|
+
--config examples/tenant-kit-adapter/adapter.yaml \
|
|
67
|
+
--base-url http://laravel-tenant-kit.test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## adapter.yaml (minimal)
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
name: my_project
|
|
74
|
+
description: My API adapter
|
|
75
|
+
base_url: http://api.example.test
|
|
76
|
+
|
|
77
|
+
auth:
|
|
78
|
+
type: bearer
|
|
79
|
+
env_token: MY_PROJECT_API_TOKEN
|
|
80
|
+
|
|
81
|
+
tools:
|
|
82
|
+
- name: list_items
|
|
83
|
+
description: List items
|
|
84
|
+
method: GET
|
|
85
|
+
path: /api/items
|
|
86
|
+
|
|
87
|
+
- name: create_item
|
|
88
|
+
description: Create item
|
|
89
|
+
method: POST
|
|
90
|
+
path: /api/items
|
|
91
|
+
dangerous: true
|
|
92
|
+
parameters:
|
|
93
|
+
title:
|
|
94
|
+
type: string
|
|
95
|
+
required: true
|
|
96
|
+
body:
|
|
97
|
+
title: "{title}"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Tenant subdomain APIs:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- name: invite_member
|
|
104
|
+
method: POST
|
|
105
|
+
path: /api/team/invitations
|
|
106
|
+
host: tenant
|
|
107
|
+
tenant_param: subdomain
|
|
108
|
+
parameters:
|
|
109
|
+
subdomain: { type: string, required: true }
|
|
110
|
+
email: { type: string, required: true }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## HTTP server
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
api-operator serve --port 8100
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
POST /v1/chat
|
|
121
|
+
{
|
|
122
|
+
"adapter": "yaml",
|
|
123
|
+
"config_path": "examples/tenant-kit-adapter/adapter.yaml",
|
|
124
|
+
"adapter_config": { "token": "YOUR_TOKEN" },
|
|
125
|
+
"message": "list workspaces",
|
|
126
|
+
"abilities": ["workspaces:read"]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Integration test (Tenant Kit)
|
|
131
|
+
|
|
132
|
+
With [Tenant Kit](https://github.com/mohammedelkarsh/laravel-tenant-kit) running:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
python scripts/integration_tenant_kit.py
|
|
136
|
+
# Docker on port 8080:
|
|
137
|
+
python scripts/integration_tenant_kit.py --base-url http://laravel-tenant-kit.test:8080
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Optional pytest marker (requires env vars):
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
export TENANT_KIT_BASE_URL=http://laravel-tenant-kit.test
|
|
144
|
+
export TENANT_KIT_API_TOKEN=your-token
|
|
145
|
+
pytest -m integration -q
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Tests
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pytest -q
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Configuration
|
|
155
|
+
|
|
156
|
+
Copy `.env.example` to `.env` for local defaults (`api_operator_PLANNER`, port, etc.).
|
|
157
|
+
|
|
158
|
+
## Architecture
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
api-operator (core) your projects
|
|
162
|
+
├── agent runtime ├── adapter.yaml
|
|
163
|
+
├── mock + yaml adapters ├── openapi.yaml
|
|
164
|
+
└── scaffold / generate CLI └── HTTP APIs (Laravel, Go, …)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""API Operator — standalone AI operator with pluggable adapters."""
|
|
2
|
+
|
|
3
|
+
from api_operator.core.agent import Agent, AgentResponse
|
|
4
|
+
from api_operator.core.config import Settings
|
|
5
|
+
from api_operator.tools.base import Tool, ToolRegistry
|
|
6
|
+
|
|
7
|
+
__all__ = ["Agent", "AgentResponse", "Settings", "Tool", "ToolRegistry"]
|
|
8
|
+
__version__ = "0.9.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from api_operator.tools.base import ToolRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Adapter(ABC):
|
|
10
|
+
name: str
|
|
11
|
+
description: str
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def build_registry(self) -> ToolRegistry:
|
|
15
|
+
raise NotImplementedError
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def system_prompt(self) -> str:
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
|
|
21
|
+
def auth_context(self) -> dict[str, Any]:
|
|
22
|
+
return {}
|
|
23
|
+
|
|
24
|
+
def docs_paths(self) -> list[str]:
|
|
25
|
+
return []
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from api_operator.adapters.base import Adapter
|
|
6
|
+
from api_operator.tools.base import Tool, ToolRegistry, ToolResult
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _MockStore:
|
|
10
|
+
workspaces: dict[str, dict[str, Any]] = {}
|
|
11
|
+
invitations: list[dict[str, Any]] = []
|
|
12
|
+
connections: dict[str, dict[str, Any]] = {}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def reset_mock_store() -> None:
|
|
16
|
+
_MockStore.workspaces.clear()
|
|
17
|
+
_MockStore.invitations.clear()
|
|
18
|
+
_MockStore.connections.clear()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MockAdapter(Adapter):
|
|
22
|
+
name = "mock"
|
|
23
|
+
description = "In-memory demo adapter for SaaS + connectivity scenarios"
|
|
24
|
+
|
|
25
|
+
def build_registry(self) -> ToolRegistry:
|
|
26
|
+
registry = ToolRegistry()
|
|
27
|
+
registry.register(
|
|
28
|
+
Tool(
|
|
29
|
+
name="list_workspaces",
|
|
30
|
+
description="List all mock workspaces",
|
|
31
|
+
handler=self._list_workspaces,
|
|
32
|
+
parameters={},
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
registry.register(
|
|
36
|
+
Tool(
|
|
37
|
+
name="create_workspace",
|
|
38
|
+
description="Create a mock workspace with name and subdomain",
|
|
39
|
+
handler=self._create_workspace,
|
|
40
|
+
parameters={"name": str, "subdomain": str},
|
|
41
|
+
required=["name", "subdomain"],
|
|
42
|
+
dangerous=True,
|
|
43
|
+
requires_ability="workspaces:write",
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
registry.register(
|
|
47
|
+
Tool(
|
|
48
|
+
name="invite_member",
|
|
49
|
+
description="Invite a team member to a workspace by subdomain",
|
|
50
|
+
handler=self._invite_member,
|
|
51
|
+
parameters={"subdomain": str, "email": str, "role": str},
|
|
52
|
+
required=["subdomain", "email"],
|
|
53
|
+
dangerous=True,
|
|
54
|
+
requires_ability="team:invite",
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
registry.register(
|
|
58
|
+
Tool(
|
|
59
|
+
name="list_connections",
|
|
60
|
+
description="List network connections (connectivity demo)",
|
|
61
|
+
handler=self._list_connections,
|
|
62
|
+
parameters={},
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
registry.register(
|
|
66
|
+
Tool(
|
|
67
|
+
name="provision_link",
|
|
68
|
+
description="Provision a network link between two sites",
|
|
69
|
+
handler=self._provision_link,
|
|
70
|
+
parameters={"site_a": str, "site_b": str, "bandwidth_mbps": int},
|
|
71
|
+
required=["site_a", "site_b"],
|
|
72
|
+
dangerous=True,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
return registry
|
|
76
|
+
|
|
77
|
+
def system_prompt(self) -> str:
|
|
78
|
+
return (
|
|
79
|
+
"You are API Operator (mock mode). You help operators manage "
|
|
80
|
+
"workspaces and network links using registered tools only. "
|
|
81
|
+
"Reply in the user's language (Arabic or English). "
|
|
82
|
+
"Never claim an action succeeded unless a tool returned ok=true."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
async def _list_workspaces(self) -> ToolResult:
|
|
86
|
+
items = list(_MockStore.workspaces.values())
|
|
87
|
+
return ToolResult(ok=True, data={"workspaces": items, "count": len(items)})
|
|
88
|
+
|
|
89
|
+
async def _create_workspace(self, name: str, subdomain: str) -> ToolResult:
|
|
90
|
+
key = subdomain.strip().lower()
|
|
91
|
+
if key in _MockStore.workspaces:
|
|
92
|
+
return ToolResult(ok=False, error=f"Workspace '{key}' already exists.")
|
|
93
|
+
workspace = {
|
|
94
|
+
"id": key,
|
|
95
|
+
"name": name.strip(),
|
|
96
|
+
"subdomain": key,
|
|
97
|
+
"url": f"https://{key}.example.test",
|
|
98
|
+
}
|
|
99
|
+
_MockStore.workspaces[key] = workspace
|
|
100
|
+
return ToolResult(ok=True, data=workspace)
|
|
101
|
+
|
|
102
|
+
async def _invite_member(
|
|
103
|
+
self,
|
|
104
|
+
subdomain: str,
|
|
105
|
+
email: str,
|
|
106
|
+
role: str = "member",
|
|
107
|
+
) -> ToolResult:
|
|
108
|
+
key = subdomain.strip().lower()
|
|
109
|
+
if key not in _MockStore.workspaces:
|
|
110
|
+
return ToolResult(ok=False, error=f"Workspace '{key}' not found.")
|
|
111
|
+
invitation = {
|
|
112
|
+
"workspace": key,
|
|
113
|
+
"email": email.strip().lower(),
|
|
114
|
+
"role": role.strip().lower() or "member",
|
|
115
|
+
"status": "sent",
|
|
116
|
+
}
|
|
117
|
+
_MockStore.invitations.append(invitation)
|
|
118
|
+
return ToolResult(ok=True, data=invitation)
|
|
119
|
+
|
|
120
|
+
async def _list_connections(self) -> ToolResult:
|
|
121
|
+
items = list(_MockStore.connections.values())
|
|
122
|
+
return ToolResult(ok=True, data={"connections": items, "count": len(items)})
|
|
123
|
+
|
|
124
|
+
async def _provision_link(
|
|
125
|
+
self,
|
|
126
|
+
site_a: str,
|
|
127
|
+
site_b: str,
|
|
128
|
+
bandwidth_mbps: int = 100,
|
|
129
|
+
) -> ToolResult:
|
|
130
|
+
link_id = f"{site_a.strip().lower()}-{site_b.strip().lower()}"
|
|
131
|
+
if link_id in _MockStore.connections:
|
|
132
|
+
return ToolResult(ok=False, error=f"Link '{link_id}' already exists.")
|
|
133
|
+
link = {
|
|
134
|
+
"id": link_id,
|
|
135
|
+
"site_a": site_a.strip(),
|
|
136
|
+
"site_b": site_b.strip(),
|
|
137
|
+
"bandwidth_mbps": bandwidth_mbps,
|
|
138
|
+
"status": "active",
|
|
139
|
+
}
|
|
140
|
+
_MockStore.connections[link_id] = link
|
|
141
|
+
return ToolResult(ok=True, data=link)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_mock_store() -> _MockStore:
|
|
145
|
+
return _MockStore
|