bonito-cli 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.
- bonito_cli-0.1.0/.gitignore +12 -0
- bonito_cli-0.1.0/CLI_SPEC.md +262 -0
- bonito_cli-0.1.0/PKG-INFO +108 -0
- bonito_cli-0.1.0/README.md +79 -0
- bonito_cli-0.1.0/bonito_cli/__init__.py +3 -0
- bonito_cli-0.1.0/bonito_cli/__main__.py +4 -0
- bonito_cli-0.1.0/bonito_cli/api.py +167 -0
- bonito_cli-0.1.0/bonito_cli/app.py +118 -0
- bonito_cli-0.1.0/bonito_cli/commands/__init__.py +1 -0
- bonito_cli-0.1.0/bonito_cli/commands/analytics.py +337 -0
- bonito_cli-0.1.0/bonito_cli/commands/auth.py +171 -0
- bonito_cli-0.1.0/bonito_cli/commands/chat.py +380 -0
- bonito_cli-0.1.0/bonito_cli/commands/deployments.py +240 -0
- bonito_cli-0.1.0/bonito_cli/commands/gateway.py +311 -0
- bonito_cli-0.1.0/bonito_cli/commands/models.py +223 -0
- bonito_cli-0.1.0/bonito_cli/commands/policies.py +299 -0
- bonito_cli-0.1.0/bonito_cli/commands/providers.py +274 -0
- bonito_cli-0.1.0/bonito_cli/config.py +183 -0
- bonito_cli-0.1.0/bonito_cli/utils/__init__.py +0 -0
- bonito_cli-0.1.0/bonito_cli/utils/auth.py +49 -0
- bonito_cli-0.1.0/bonito_cli/utils/display.py +264 -0
- bonito_cli-0.1.0/pyproject.toml +46 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Bonito CLI — Design Spec
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
`bonito` is a CLI tool that gives enterprise AI teams a unified command-line interface
|
|
5
|
+
to manage multi-cloud AI workloads through the Bonito platform. Instead of juggling
|
|
6
|
+
`aws bedrock`, `az cognitiveservices`, and `gcloud ai`, teams use one tool.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
```bash
|
|
10
|
+
pip install bonito-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Tech Stack
|
|
14
|
+
- **Python 3.10+** with **Typer** (CLI framework, auto-help, auto-completion)
|
|
15
|
+
- **Rich** for beautiful terminal output (tables, panels, progress bars, syntax highlighting)
|
|
16
|
+
- **httpx** for async HTTP calls to the Bonito API
|
|
17
|
+
- **keyring** (optional) for secure credential storage, fallback to file-based config
|
|
18
|
+
|
|
19
|
+
## Config Storage
|
|
20
|
+
- `~/.bonito/config.json` — API endpoint, default org, preferences
|
|
21
|
+
- `~/.bonito/credentials.json` — API key (or use env var `BONITO_API_KEY`)
|
|
22
|
+
- Environment variables override file config:
|
|
23
|
+
- `BONITO_API_KEY` — API key
|
|
24
|
+
- `BONITO_API_URL` — API endpoint (default: `https://getbonito.com/api`)
|
|
25
|
+
|
|
26
|
+
## Authentication Flow
|
|
27
|
+
Users sign up on the Bonito web app, then:
|
|
28
|
+
```bash
|
|
29
|
+
bonito auth login # Opens browser for OAuth, or prompts for API key
|
|
30
|
+
bonito auth login --api-key bk-xxx # Direct API key auth
|
|
31
|
+
bonito auth status # Show current auth state + org info
|
|
32
|
+
bonito auth logout # Clear stored credentials
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Command Structure
|
|
36
|
+
|
|
37
|
+
### `bonito auth` — Authentication & API Keys
|
|
38
|
+
```bash
|
|
39
|
+
bonito auth login [--api-key KEY] # Authenticate (browser OAuth or API key)
|
|
40
|
+
bonito auth logout # Clear credentials
|
|
41
|
+
bonito auth status # Show auth status, org, user info
|
|
42
|
+
bonito auth keys list # List API/gateway keys
|
|
43
|
+
bonito auth keys create [--name N] # Create new gateway key
|
|
44
|
+
bonito auth keys revoke KEY_ID # Revoke a key
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `bonito providers` — Cloud Provider Management
|
|
48
|
+
```bash
|
|
49
|
+
bonito providers list # List connected providers
|
|
50
|
+
bonito providers add aws --access-key X --secret-key Y --region us-east-1
|
|
51
|
+
bonito providers add azure --tenant-id X --client-id Y --client-secret Z --subscription-id S --endpoint E
|
|
52
|
+
bonito providers add gcp --project-id X --service-account-json path/to/sa.json --region us-central1
|
|
53
|
+
bonito providers test PROVIDER_ID # Verify credentials
|
|
54
|
+
bonito providers remove PROVIDER_ID # Disconnect provider
|
|
55
|
+
bonito providers models PROVIDER_ID # List models for a provider
|
|
56
|
+
bonito providers costs PROVIDER_ID [--days 30] # Show provider costs
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `bonito models` — Model Management
|
|
60
|
+
```bash
|
|
61
|
+
bonito models list [--provider aws] [--enabled-only] [--search QUERY]
|
|
62
|
+
bonito models info MODEL_ID # Detailed model info (pricing, capabilities, status)
|
|
63
|
+
bonito models enable MODEL_ID # Activate model on cloud account
|
|
64
|
+
bonito models enable --bulk ID1 ID2 ID3 # Bulk activate
|
|
65
|
+
bonito models sync [--provider PROVIDER_ID] # Sync model catalog from cloud
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `bonito chat` — Interactive AI Chat (Playground)
|
|
69
|
+
```bash
|
|
70
|
+
bonito chat # Interactive chat (picks default model)
|
|
71
|
+
bonito chat -m claude-3-sonnet # Chat with specific model
|
|
72
|
+
bonito chat -m gpt-4o --temperature 0.3 # With parameters
|
|
73
|
+
bonito chat --compare model1 model2 # Compare mode
|
|
74
|
+
echo "Summarize this" | bonito chat -m claude # Pipe input
|
|
75
|
+
bonito chat -m claude "What is 2+2?" # One-shot (non-interactive)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `bonito gateway` — API Gateway Management
|
|
79
|
+
```bash
|
|
80
|
+
bonito gateway status # Gateway health + config
|
|
81
|
+
bonito gateway keys list # List gateway API keys
|
|
82
|
+
bonito gateway keys create [--name N] # Create key
|
|
83
|
+
bonito gateway keys revoke KEY_ID # Revoke key
|
|
84
|
+
bonito gateway logs [--limit 50] [--model X] # View recent gateway logs
|
|
85
|
+
bonito gateway config # Show gateway config
|
|
86
|
+
bonito gateway config set FIELD VALUE # Update gateway config
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `bonito policies` — Routing Policies
|
|
90
|
+
```bash
|
|
91
|
+
bonito policies list # List routing policies
|
|
92
|
+
bonito policies create --name N --strategy cost_optimized --models M1,M2
|
|
93
|
+
bonito policies info POLICY_ID # Policy details + stats
|
|
94
|
+
bonito policies test POLICY_ID "test prompt" # Dry-run test
|
|
95
|
+
bonito policies toggle POLICY_ID # Enable/disable
|
|
96
|
+
bonito policies delete POLICY_ID # Delete policy
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `bonito analytics` — Usage Analytics & Costs
|
|
100
|
+
```bash
|
|
101
|
+
bonito analytics overview # Dashboard summary (requests, cost, top model)
|
|
102
|
+
bonito analytics usage [--period day|week|month] # Usage over time
|
|
103
|
+
bonito analytics costs [--period daily|weekly|monthly] # Cost breakdown
|
|
104
|
+
bonito analytics trends # Trend analysis
|
|
105
|
+
bonito analytics digest # Weekly digest
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `bonito costs` — Cloud Cost Intelligence
|
|
109
|
+
```bash
|
|
110
|
+
bonito costs summary [--period monthly] # Total spend across providers
|
|
111
|
+
bonito costs breakdown # By provider, model, department
|
|
112
|
+
bonito costs forecast # 14-day cost forecast
|
|
113
|
+
bonito costs recommendations # Optimization recommendations
|
|
114
|
+
bonito costs export [--format csv] # Export cost data
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `bonito config` — CLI Configuration
|
|
118
|
+
```bash
|
|
119
|
+
bonito config show # Show current config
|
|
120
|
+
bonito config set api_url https://... # Set API endpoint
|
|
121
|
+
bonito config set default_model claude-3-sonnet # Set default model
|
|
122
|
+
bonito config reset # Reset to defaults
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `bonito completion` — Shell Completions
|
|
126
|
+
```bash
|
|
127
|
+
bonito completion install bash # Install bash completions
|
|
128
|
+
bonito completion install zsh # Install zsh completions
|
|
129
|
+
bonito completion install fish # Install fish completions
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Output Formatting
|
|
133
|
+
- Default: Rich-formatted tables, panels, and styled text
|
|
134
|
+
- `--json` flag on any command: raw JSON output (for piping/scripting)
|
|
135
|
+
- `--quiet` flag: minimal output (for CI/CD)
|
|
136
|
+
- Color auto-detection (disable with `--no-color` or `NO_COLOR=1`)
|
|
137
|
+
|
|
138
|
+
## Interactive Chat UX
|
|
139
|
+
```
|
|
140
|
+
╭─ Bonito Chat ─────────────────────────────────────╮
|
|
141
|
+
│ Model: claude-3-sonnet (AWS Bedrock) │
|
|
142
|
+
│ Temperature: 0.7 │ Max Tokens: 1000 │
|
|
143
|
+
╰────────────────────────────────────────────────────╯
|
|
144
|
+
|
|
145
|
+
You: What are the main differences between transformers and RNNs?
|
|
146
|
+
|
|
147
|
+
Claude 3 Sonnet: Transformers and RNNs differ in several key ways...
|
|
148
|
+
[tokens: 847 | cost: $0.0042 | latency: 1.2s]
|
|
149
|
+
|
|
150
|
+
You: /help
|
|
151
|
+
Commands: /model <name>, /temp <0-2>, /tokens <n>, /clear, /export, /quit
|
|
152
|
+
|
|
153
|
+
You: /quit
|
|
154
|
+
Session saved. Total: 3 messages, $0.012, 2847 tokens.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Error Handling
|
|
158
|
+
- Clear error messages with suggested fixes
|
|
159
|
+
- `bonito doctor` command to diagnose common issues (connectivity, auth, provider status)
|
|
160
|
+
- Retry logic for transient failures (network timeouts, rate limits)
|
|
161
|
+
|
|
162
|
+
## API Endpoint Reference
|
|
163
|
+
The CLI talks to the existing Bonito backend. Key endpoints:
|
|
164
|
+
|
|
165
|
+
### Auth
|
|
166
|
+
- POST /api/auth/login → TokenResponse
|
|
167
|
+
- GET /api/auth/me → UserResponse
|
|
168
|
+
|
|
169
|
+
### Providers
|
|
170
|
+
- GET /api/providers/ → List[ProviderResponse]
|
|
171
|
+
- POST /api/providers/connect → ProviderResponse
|
|
172
|
+
- POST /api/providers/{id}/verify → VerifyResponse
|
|
173
|
+
- DELETE /api/providers/{id}
|
|
174
|
+
- GET /api/providers/{id}/models → List[ModelInfo]
|
|
175
|
+
- GET /api/providers/{id}/costs → CostDataResponse
|
|
176
|
+
|
|
177
|
+
### Models
|
|
178
|
+
- GET /api/models/ → List[ModelResponse]
|
|
179
|
+
- GET /api/models/{id} → ModelResponse
|
|
180
|
+
- GET /api/models/{id}/details → ModelDetailsResponse
|
|
181
|
+
- POST /api/models/{id}/playground → PlaygroundResponse
|
|
182
|
+
- POST /api/models/compare → CompareResponse
|
|
183
|
+
- POST /api/models/{id}/activate
|
|
184
|
+
- POST /api/models/activate-bulk
|
|
185
|
+
- POST /api/models/sync
|
|
186
|
+
|
|
187
|
+
### Gateway
|
|
188
|
+
- GET /api/gateway/keys → List[GatewayKeyResponse]
|
|
189
|
+
- POST /api/gateway/keys → GatewayKeyCreated
|
|
190
|
+
- DELETE /api/gateway/keys/{id}
|
|
191
|
+
- GET /api/gateway/logs → List[GatewayLogEntry]
|
|
192
|
+
- GET /api/gateway/config → GatewayConfigResponse
|
|
193
|
+
- PUT /api/gateway/config → GatewayConfigResponse
|
|
194
|
+
- GET /api/gateway/usage → UsageSummary
|
|
195
|
+
|
|
196
|
+
### Gateway Proxy (OpenAI-compatible)
|
|
197
|
+
- POST /v1/chat/completions
|
|
198
|
+
- POST /v1/completions
|
|
199
|
+
- POST /v1/embeddings
|
|
200
|
+
- GET /v1/models
|
|
201
|
+
|
|
202
|
+
### Routing Policies
|
|
203
|
+
- GET /api/routing-policies/ → List[RoutingPolicyResponse]
|
|
204
|
+
- POST /api/routing-policies/ → RoutingPolicyResponse
|
|
205
|
+
- GET /api/routing-policies/{id} → RoutingPolicyDetailResponse
|
|
206
|
+
- PUT /api/routing-policies/{id}
|
|
207
|
+
- DELETE /api/routing-policies/{id}
|
|
208
|
+
- POST /api/routing-policies/{id}/test → PolicyTestResult
|
|
209
|
+
- GET /api/routing-policies/{id}/stats → PolicyStats
|
|
210
|
+
|
|
211
|
+
### Analytics
|
|
212
|
+
- GET /api/analytics/overview
|
|
213
|
+
- GET /api/analytics/usage?period=day|week|month
|
|
214
|
+
- GET /api/analytics/costs
|
|
215
|
+
- GET /api/analytics/trends
|
|
216
|
+
- GET /api/analytics/digest
|
|
217
|
+
|
|
218
|
+
### Costs
|
|
219
|
+
- GET /api/costs/?period=daily|weekly|monthly
|
|
220
|
+
- GET /api/costs/breakdown
|
|
221
|
+
- GET /api/costs/forecast
|
|
222
|
+
- GET /api/costs/recommendations
|
|
223
|
+
|
|
224
|
+
### Health
|
|
225
|
+
- GET /api/health
|
|
226
|
+
- GET /api/health/ready
|
|
227
|
+
|
|
228
|
+
## File Structure
|
|
229
|
+
```
|
|
230
|
+
cli/
|
|
231
|
+
├── pyproject.toml # Package config, entry point
|
|
232
|
+
├── README.md # CLI documentation
|
|
233
|
+
├── bonito_cli/
|
|
234
|
+
│ ├── __init__.py # Version
|
|
235
|
+
│ ├── __main__.py # python -m bonito_cli
|
|
236
|
+
│ ├── app.py # Main Typer app, register subcommands
|
|
237
|
+
│ ├── config.py # Config file management (~/.bonito/)
|
|
238
|
+
│ ├── api.py # HTTP client (httpx) wrapper
|
|
239
|
+
│ ├── commands/
|
|
240
|
+
│ │ ├── __init__.py
|
|
241
|
+
│ │ ├── auth.py # auth login/logout/status/keys
|
|
242
|
+
│ │ ├── providers.py # providers list/add/test/remove
|
|
243
|
+
│ │ ├── models.py # models list/info/enable/sync
|
|
244
|
+
│ │ ├── chat.py # Interactive chat + one-shot
|
|
245
|
+
│ │ ├── gateway.py # gateway status/keys/logs/config
|
|
246
|
+
│ │ ├── policies.py # routing policies CRUD + test
|
|
247
|
+
│ │ ├── analytics.py # analytics overview/usage/costs/trends
|
|
248
|
+
│ │ ├── costs.py # cost intelligence
|
|
249
|
+
│ │ └── config_cmd.py # CLI config management
|
|
250
|
+
│ └── utils/
|
|
251
|
+
│ ├── __init__.py
|
|
252
|
+
│ ├── display.py # Rich formatting helpers (tables, panels, etc.)
|
|
253
|
+
│ └── auth.py # Token refresh, credential storage
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Notes
|
|
257
|
+
- The CLI must work with the EXISTING backend API — no new backend endpoints needed
|
|
258
|
+
- Auth tokens: store access_token + refresh_token, auto-refresh on 401
|
|
259
|
+
- All commands that require auth should check credentials first and give clear "run bonito auth login" messages
|
|
260
|
+
- The `bonito chat` interactive mode is the killer feature — make it feel great
|
|
261
|
+
- Support piping: `cat file.txt | bonito chat -m claude "Summarize this"`
|
|
262
|
+
- The `--json` flag is critical for CI/CD automation
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bonito-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Bonito CLI — Unified multi-cloud AI management from your terminal
|
|
5
|
+
Project-URL: Homepage, https://getbonito.com
|
|
6
|
+
Project-URL: Documentation, https://getbonito.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/ShabariRepo/bonito
|
|
8
|
+
Project-URL: Issues, https://github.com/ShabariRepo/bonito/issues
|
|
9
|
+
Author-email: Bonito <hello@getbonito.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: ai,aws,azure,bedrock,cli,gcp,llm,multi-cloud,openai,vertex-ai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
26
|
+
Requires-Dist: rich>=13.0.0
|
|
27
|
+
Requires-Dist: typer[all]>=0.9.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# 🐟 Bonito CLI
|
|
31
|
+
|
|
32
|
+
**Unified multi-cloud AI management from your terminal.**
|
|
33
|
+
|
|
34
|
+
Bonito gives enterprise AI teams a single CLI to manage models, costs, and workloads across AWS Bedrock, Azure OpenAI, and Google Vertex AI — instead of juggling `aws bedrock`, `az cognitiveservices`, and `gcloud ai`.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install bonito-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Authenticate
|
|
46
|
+
bonito auth login
|
|
47
|
+
|
|
48
|
+
# List connected cloud providers
|
|
49
|
+
bonito providers list
|
|
50
|
+
|
|
51
|
+
# Browse 300+ models across all providers
|
|
52
|
+
bonito models list
|
|
53
|
+
bonito models list --search "claude"
|
|
54
|
+
|
|
55
|
+
# Chat with any model
|
|
56
|
+
bonito chat -m <model-id> "What is quantum computing?"
|
|
57
|
+
|
|
58
|
+
# Interactive chat
|
|
59
|
+
bonito chat
|
|
60
|
+
|
|
61
|
+
# View deployments
|
|
62
|
+
bonito deployments list
|
|
63
|
+
|
|
64
|
+
# Check gateway logs
|
|
65
|
+
bonito gateway logs
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Commands
|
|
69
|
+
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---------|------------|
|
|
72
|
+
| `bonito auth` | 🔐 Authentication & API keys |
|
|
73
|
+
| `bonito providers` | ☁️ Cloud provider management |
|
|
74
|
+
| `bonito models` | 🤖 AI model catalogue |
|
|
75
|
+
| `bonito deployments` | 🚀 Deployment management |
|
|
76
|
+
| `bonito chat` | 💬 Interactive AI chat |
|
|
77
|
+
| `bonito gateway` | 🌐 API gateway management |
|
|
78
|
+
| `bonito policies` | 🎯 Routing policies |
|
|
79
|
+
| `bonito analytics` | 📊 Usage analytics & costs |
|
|
80
|
+
|
|
81
|
+
## Features
|
|
82
|
+
|
|
83
|
+
- **Multi-cloud** — AWS Bedrock, Azure OpenAI, Google Vertex AI in one tool
|
|
84
|
+
- **Interactive chat** — Talk to any model with `/model`, `/temp`, `/export` commands
|
|
85
|
+
- **Compare models** — `bonito chat --compare model1 --compare model2 "prompt"`
|
|
86
|
+
- **Routing policies** — Cost-optimized, failover, A/B testing
|
|
87
|
+
- **JSON output** — `--json` flag on every command for CI/CD automation
|
|
88
|
+
- **Rich terminal UI** — Beautiful tables, progress bars, and formatted output
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Environment variables (override config file)
|
|
94
|
+
export BONITO_API_KEY=your-api-key
|
|
95
|
+
export BONITO_API_URL=https://your-instance.example.com
|
|
96
|
+
|
|
97
|
+
# Or use config file (~/.bonito/config.json)
|
|
98
|
+
bonito auth login --email you@company.com
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Requirements
|
|
102
|
+
|
|
103
|
+
- Python 3.10+
|
|
104
|
+
- A [Bonito](https://getbonito.com) account
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# 🐟 Bonito CLI
|
|
2
|
+
|
|
3
|
+
**Unified multi-cloud AI management from your terminal.**
|
|
4
|
+
|
|
5
|
+
Bonito gives enterprise AI teams a single CLI to manage models, costs, and workloads across AWS Bedrock, Azure OpenAI, and Google Vertex AI — instead of juggling `aws bedrock`, `az cognitiveservices`, and `gcloud ai`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install bonito-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Authenticate
|
|
17
|
+
bonito auth login
|
|
18
|
+
|
|
19
|
+
# List connected cloud providers
|
|
20
|
+
bonito providers list
|
|
21
|
+
|
|
22
|
+
# Browse 300+ models across all providers
|
|
23
|
+
bonito models list
|
|
24
|
+
bonito models list --search "claude"
|
|
25
|
+
|
|
26
|
+
# Chat with any model
|
|
27
|
+
bonito chat -m <model-id> "What is quantum computing?"
|
|
28
|
+
|
|
29
|
+
# Interactive chat
|
|
30
|
+
bonito chat
|
|
31
|
+
|
|
32
|
+
# View deployments
|
|
33
|
+
bonito deployments list
|
|
34
|
+
|
|
35
|
+
# Check gateway logs
|
|
36
|
+
bonito gateway logs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
|---------|------------|
|
|
43
|
+
| `bonito auth` | 🔐 Authentication & API keys |
|
|
44
|
+
| `bonito providers` | ☁️ Cloud provider management |
|
|
45
|
+
| `bonito models` | 🤖 AI model catalogue |
|
|
46
|
+
| `bonito deployments` | 🚀 Deployment management |
|
|
47
|
+
| `bonito chat` | 💬 Interactive AI chat |
|
|
48
|
+
| `bonito gateway` | 🌐 API gateway management |
|
|
49
|
+
| `bonito policies` | 🎯 Routing policies |
|
|
50
|
+
| `bonito analytics` | 📊 Usage analytics & costs |
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Multi-cloud** — AWS Bedrock, Azure OpenAI, Google Vertex AI in one tool
|
|
55
|
+
- **Interactive chat** — Talk to any model with `/model`, `/temp`, `/export` commands
|
|
56
|
+
- **Compare models** — `bonito chat --compare model1 --compare model2 "prompt"`
|
|
57
|
+
- **Routing policies** — Cost-optimized, failover, A/B testing
|
|
58
|
+
- **JSON output** — `--json` flag on every command for CI/CD automation
|
|
59
|
+
- **Rich terminal UI** — Beautiful tables, progress bars, and formatted output
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Environment variables (override config file)
|
|
65
|
+
export BONITO_API_KEY=your-api-key
|
|
66
|
+
export BONITO_API_URL=https://your-instance.example.com
|
|
67
|
+
|
|
68
|
+
# Or use config file (~/.bonito/config.json)
|
|
69
|
+
bonito auth login --email you@company.com
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
- Python 3.10+
|
|
75
|
+
- A [Bonito](https://getbonito.com) account
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Synchronous HTTP API client for the Bonito backend."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Dict, Generator, Optional
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from .config import get_api_key, get_api_url
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class APIError(Exception):
|
|
17
|
+
"""Raised when an API request fails."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: str, status_code: Optional[int] = None):
|
|
20
|
+
super().__init__(message)
|
|
21
|
+
self.status_code = status_code
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BonitoAPI:
|
|
25
|
+
"""Synchronous Bonito API client backed by ``httpx.Client``."""
|
|
26
|
+
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
self.base_url: str = get_api_url()
|
|
29
|
+
self._client: Optional[httpx.Client] = None
|
|
30
|
+
|
|
31
|
+
# ── internal helpers ────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def client(self) -> httpx.Client:
|
|
35
|
+
if self._client is None or self._client.is_closed:
|
|
36
|
+
self._client = httpx.Client(
|
|
37
|
+
base_url=self.base_url,
|
|
38
|
+
timeout=30.0,
|
|
39
|
+
follow_redirects=True,
|
|
40
|
+
)
|
|
41
|
+
return self._client
|
|
42
|
+
|
|
43
|
+
def _headers(self) -> Dict[str, str]:
|
|
44
|
+
headers: Dict[str, str] = {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"User-Agent": "bonito-cli/0.1.0",
|
|
47
|
+
}
|
|
48
|
+
token = get_api_key()
|
|
49
|
+
if token:
|
|
50
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
51
|
+
return headers
|
|
52
|
+
|
|
53
|
+
def _request(
|
|
54
|
+
self,
|
|
55
|
+
method: str,
|
|
56
|
+
endpoint: str,
|
|
57
|
+
data: Any = None,
|
|
58
|
+
params: Optional[Dict[str, Any]] = None,
|
|
59
|
+
) -> Any:
|
|
60
|
+
url = endpoint if endpoint.startswith("http") else f"/api{endpoint}"
|
|
61
|
+
try:
|
|
62
|
+
resp = self.client.request(
|
|
63
|
+
method, url, json=data, params=params, headers=self._headers()
|
|
64
|
+
)
|
|
65
|
+
except httpx.RequestError as exc:
|
|
66
|
+
raise APIError(f"Connection failed: {exc}") from exc
|
|
67
|
+
|
|
68
|
+
if resp.status_code == 401:
|
|
69
|
+
raise APIError(
|
|
70
|
+
"Authentication failed — run [cyan]bonito auth login[/cyan].", 401
|
|
71
|
+
)
|
|
72
|
+
if resp.status_code == 204:
|
|
73
|
+
return {"status": "ok"}
|
|
74
|
+
if resp.status_code >= 400:
|
|
75
|
+
try:
|
|
76
|
+
body = resp.json()
|
|
77
|
+
detail = body.get(
|
|
78
|
+
"detail",
|
|
79
|
+
body.get("error", {}).get("message", f"HTTP {resp.status_code}"),
|
|
80
|
+
)
|
|
81
|
+
except Exception:
|
|
82
|
+
detail = f"HTTP {resp.status_code}: {resp.text[:200]}"
|
|
83
|
+
|
|
84
|
+
# Parse Pydantic 422 validation errors into friendly messages
|
|
85
|
+
if resp.status_code == 422 and isinstance(detail, list):
|
|
86
|
+
parts: list[str] = []
|
|
87
|
+
for err in detail:
|
|
88
|
+
if isinstance(err, dict):
|
|
89
|
+
err_type = err.get("type", "")
|
|
90
|
+
err_msg = err.get("msg", "Validation error")
|
|
91
|
+
loc = err.get("loc", [])
|
|
92
|
+
field = str(loc[-1]).replace("_", " ") if loc else "input"
|
|
93
|
+
if err_type == "uuid_parsing":
|
|
94
|
+
parts.append(
|
|
95
|
+
f"Invalid {field} format. "
|
|
96
|
+
"Expected a UUID (e.g. a1b2c3d4-e5f6-...)"
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
parts.append(f"{err_msg} (field: {field})")
|
|
100
|
+
else:
|
|
101
|
+
parts.append(str(err))
|
|
102
|
+
msg = "; ".join(parts) if parts else "Validation error"
|
|
103
|
+
elif isinstance(detail, list):
|
|
104
|
+
msg = "; ".join(str(d) for d in detail)
|
|
105
|
+
else:
|
|
106
|
+
msg = str(detail)
|
|
107
|
+
|
|
108
|
+
raise APIError(msg, resp.status_code)
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
return resp.json()
|
|
112
|
+
except Exception:
|
|
113
|
+
return {"status": "ok", "text": resp.text}
|
|
114
|
+
|
|
115
|
+
# ── public verbs ────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Any:
|
|
118
|
+
return self._request("GET", endpoint, params=params)
|
|
119
|
+
|
|
120
|
+
def post(self, endpoint: str, data: Any = None) -> Any:
|
|
121
|
+
return self._request("POST", endpoint, data=data)
|
|
122
|
+
|
|
123
|
+
def put(self, endpoint: str, data: Any = None) -> Any:
|
|
124
|
+
return self._request("PUT", endpoint, data=data)
|
|
125
|
+
|
|
126
|
+
def patch(self, endpoint: str, data: Any = None) -> Any:
|
|
127
|
+
return self._request("PATCH", endpoint, data=data)
|
|
128
|
+
|
|
129
|
+
def delete(self, endpoint: str) -> Any:
|
|
130
|
+
return self._request("DELETE", endpoint)
|
|
131
|
+
|
|
132
|
+
# ── streaming (SSE) ─────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
def stream_post(
|
|
135
|
+
self,
|
|
136
|
+
url: str,
|
|
137
|
+
data: dict,
|
|
138
|
+
headers: Optional[Dict[str, str]] = None,
|
|
139
|
+
) -> Generator[dict, None, None]:
|
|
140
|
+
"""Stream a POST request that returns SSE ``data:`` lines."""
|
|
141
|
+
hdrs = self._headers()
|
|
142
|
+
if headers:
|
|
143
|
+
hdrs.update(headers)
|
|
144
|
+
|
|
145
|
+
full_url = url if url.startswith("http") else f"{self.base_url}{url}"
|
|
146
|
+
|
|
147
|
+
with httpx.stream(
|
|
148
|
+
"POST", full_url, json=data, headers=hdrs, timeout=120.0
|
|
149
|
+
) as resp:
|
|
150
|
+
if resp.status_code >= 400:
|
|
151
|
+
raise APIError(
|
|
152
|
+
f"HTTP {resp.status_code}: {resp.read().decode()[:200]}",
|
|
153
|
+
resp.status_code,
|
|
154
|
+
)
|
|
155
|
+
for line in resp.iter_lines():
|
|
156
|
+
if line.startswith("data: "):
|
|
157
|
+
chunk = line[6:]
|
|
158
|
+
if chunk.strip() == "[DONE]":
|
|
159
|
+
break
|
|
160
|
+
try:
|
|
161
|
+
yield json.loads(chunk)
|
|
162
|
+
except json.JSONDecodeError:
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ── module-level singleton ──────────────────────────────────────
|
|
167
|
+
api = BonitoAPI()
|