pnotp 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.
pnotp-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: pnotp
3
+ Version: 0.1.0
4
+ Summary: PnotP client and API wrappers for PathoVision models.
5
+ Project-URL: Homepage, https://pnotp.ai
6
+ Author-email: PnotP <support@pnotp.ai>
7
+ Requires-Python: >=3.9
8
+ Requires-Dist: httpx<0.29,>=0.24
9
+ Provides-Extra: server
10
+ Requires-Dist: fastapi<1.0,>=0.110; extra == 'server'
11
+ Requires-Dist: pillow>=9.0; extra == 'server'
12
+ Requires-Dist: pydantic<3.0,>=2.0; extra == 'server'
13
+ Requires-Dist: python-multipart>=0.0.6; extra == 'server'
14
+ Requires-Dist: uvicorn<0.29,>=0.23; extra == 'server'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # PnotP
18
+
19
+ Unified API and Python client for PathoVision vision models.
20
+
21
+ ## Packages
22
+ - `pnotp_api`: FastAPI service that wraps PathoVision ensembles under a consistent `/v1` surface with API-key auth.
23
+ - `pnotp`: Python client for calling the PnotP API.
24
+
25
+ ## Install (client only)
26
+ ```bash
27
+ pip install pnotp
28
+ ```
29
+
30
+ ## Install (client + server extra)
31
+ ```bash
32
+ pip install "pnotp[server]"
33
+ ```
34
+
35
+ ## Usage (client)
36
+ ```python
37
+ import pnotp as pnp
38
+
39
+ client = pnp.client(api_key="YOUR_KEY") # default base URL is production
40
+ client.models()
41
+ model = client.models("pneumonia_binary_classification")
42
+ result = model.predict("image.jpg")
43
+ heatmap = model.attention_map("image.jpg")
44
+ ```
45
+
46
+ Set your key once per shell:
47
+ ```bash
48
+ export PNOTP_API_KEY="your_api_key_here"
49
+ python your_script.py # pnp.client() will read the env var automatically
50
+ ```
51
+
52
+ ## Run the API locally
53
+ ```bash
54
+ export PNOTP_INTERNAL_SECRET="dev-secret"
55
+ export PATHOVISION_ROOT="/absolute/path/to/PathoVision" # if not default
56
+ uvicorn pnotp_api.main:app --host 0.0.0.0 --port 8000
57
+ ```
58
+
59
+ ## Issue an API key (internal)
60
+ ```bash
61
+ curl -X POST http://127.0.0.1:8000/internal/api-keys \
62
+ -H "X-Internal-Secret: $PNOTP_INTERNAL_SECRET" \
63
+ -H "Content-Type: application/json" \
64
+ -d '{"user_external_id":"demo-user","plan_code":"pathovision_starter"}'
65
+ ```
pnotp-0.1.0/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # PnotP
2
+
3
+ Unified API and Python client for PathoVision vision models.
4
+
5
+ ## Packages
6
+ - `pnotp_api`: FastAPI service that wraps PathoVision ensembles under a consistent `/v1` surface with API-key auth.
7
+ - `pnotp`: Python client for calling the PnotP API.
8
+
9
+ ## Install (client only)
10
+ ```bash
11
+ pip install pnotp
12
+ ```
13
+
14
+ ## Install (client + server extra)
15
+ ```bash
16
+ pip install "pnotp[server]"
17
+ ```
18
+
19
+ ## Usage (client)
20
+ ```python
21
+ import pnotp as pnp
22
+
23
+ client = pnp.client(api_key="YOUR_KEY") # default base URL is production
24
+ client.models()
25
+ model = client.models("pneumonia_binary_classification")
26
+ result = model.predict("image.jpg")
27
+ heatmap = model.attention_map("image.jpg")
28
+ ```
29
+
30
+ Set your key once per shell:
31
+ ```bash
32
+ export PNOTP_API_KEY="your_api_key_here"
33
+ python your_script.py # pnp.client() will read the env var automatically
34
+ ```
35
+
36
+ ## Run the API locally
37
+ ```bash
38
+ export PNOTP_INTERNAL_SECRET="dev-secret"
39
+ export PATHOVISION_ROOT="/absolute/path/to/PathoVision" # if not default
40
+ uvicorn pnotp_api.main:app --host 0.0.0.0 --port 8000
41
+ ```
42
+
43
+ ## Issue an API key (internal)
44
+ ```bash
45
+ curl -X POST http://127.0.0.1:8000/internal/api-keys \
46
+ -H "X-Internal-Secret: $PNOTP_INTERNAL_SECRET" \
47
+ -H "Content-Type: application/json" \
48
+ -d '{"user_external_id":"demo-user","plan_code":"pathovision_starter"}'
49
+ ```
pnotp-0.1.0/dev.db ADDED
Binary file
@@ -0,0 +1,226 @@
1
+ # P¬P Brand Identity Construction Guide
2
+
3
+ ## Monogram Construction
4
+
5
+ The P¬P monogram is built on a 64×64px viewBox using geometric precision.
6
+
7
+ ### Construction Grid
8
+ ```
9
+ ViewBox: 0 0 64 64
10
+ Stroke width: 1.5px
11
+ Stroke linecap: square
12
+ Stroke linejoin: miter
13
+ ```
14
+
15
+ ### Element Coordinates
16
+
17
+ **P Shape (Inverted-L Bracket Skeleton):**
18
+ 1. Left vertical stem: (18, 14) → (18, 50)
19
+ 2. Top horizontal: (18, 14) → (38, 14)
20
+ 3. Right vertical (P bowl): (38, 14) → (38, 32)
21
+ 4. Bottom horizontal (P bowl): (38, 32) → (18, 32)
22
+
23
+ **Negation Mark (¬):**
24
+ 1. Horizontal line: (42, 14) → (46, 14)
25
+ 2. Vertical line: (42, 14) → (42, 18)
26
+
27
+ ### Visual Representation
28
+ ```
29
+ ¬
30
+ ┌───────┐
31
+ │ │
32
+ │ │ (P bowl)
33
+ ├───────┘
34
+ │
35
+ │ (stem)
36
+ │
37
+ │
38
+ └
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Wordmark Construction
44
+
45
+ **Typography:**
46
+ - Font: Inter Medium (500 weight)
47
+ - Letter spacing: +2% (0.96px at 48px font size)
48
+ - Characters: P¬P (using Unicode ¬ character: U+00AC)
49
+
50
+ **Specifications:**
51
+ - Font family: Inter, system-ui, sans-serif
52
+ - Font weight: 500
53
+ - Letter spacing: 0.02em
54
+
55
+ ---
56
+
57
+ ## Icon Sizes
58
+
59
+ All icons use the same viewBox (0 0 64 64) but scale to different display sizes:
60
+
61
+ - **16×16px** - Favicons, small UI elements
62
+ - **32×32px** - Toolbar icons, small badges
63
+ - **64×64px** - Standard icons, medium usage
64
+ - **128×128px** - Large icons, app icons
65
+ - **256×256px** - High-resolution displays, app stores
66
+
67
+ ---
68
+
69
+ ## Color Variants
70
+
71
+ ### Black Version
72
+ - Stroke: #000000
73
+ - Background: transparent or #FFFFFF
74
+
75
+ ### White Version
76
+ - Stroke: #FFFFFF
77
+ - Background: #000000
78
+
79
+ ### No other colors permitted
80
+ - Pure monochrome only
81
+ - Optional gray (#C6C6C6) for dividers only, never for logo
82
+
83
+ ---
84
+
85
+ ## Converting to Base64
86
+
87
+ ### Method 1: Command Line
88
+ ```bash
89
+ # For macOS/Linux
90
+ base64 -i monogram-black.svg -o monogram-black-base64.txt
91
+
92
+ # For Windows PowerShell
93
+ [Convert]::ToBase64String([IO.File]::ReadAllBytes("monogram-black.svg"))
94
+ ```
95
+
96
+ ### Method 2: Online Tools
97
+ 1. Visit: https://base64.guru/converter/encode/image/svg
98
+ 2. Upload the SVG file
99
+ 3. Copy the Base64 output
100
+
101
+ ### Method 3: JavaScript/Node
102
+ ```javascript
103
+ const fs = require('fs');
104
+ const svg = fs.readFileSync('monogram-black.svg', 'utf8');
105
+ const base64 = Buffer.from(svg).toString('base64');
106
+ console.log(base64);
107
+ ```
108
+
109
+ ### Usage in HTML/CSS
110
+ ```html
111
+ <!-- As img src -->
112
+ <img src="data:image/svg+xml;base64,[YOUR_BASE64_HERE]" alt="P¬P Logo" />
113
+
114
+ <!-- As CSS background -->
115
+ <style>
116
+ .logo {
117
+ background-image: url('data:image/svg+xml;base64,[YOUR_BASE64_HERE]');
118
+ }
119
+ </style>
120
+ ```
121
+
122
+ ---
123
+
124
+ ## File Inventory
125
+
126
+ ### Monogram Files
127
+ - `monogram-black.svg` - Black stroke, transparent background
128
+ - `monogram-white.svg` - White stroke, transparent background
129
+
130
+ ### Icon Files (All Black)
131
+ - `icon-16.svg` - 16×16px display size
132
+ - `icon-32.svg` - 32×32px display size
133
+ - `icon-64.svg` - 64×64px display size
134
+ - `icon-128.svg` - 128×128px display size
135
+ - `icon-256.svg` - 256×256px display size
136
+
137
+ ### Wordmark Files
138
+ - `wordmark-black.svg` - Black text
139
+ - `wordmark-white.svg` - White text
140
+
141
+ ### Lockup Files
142
+ - `horizontal-lockup-black.svg` - Monogram + wordmark, black
143
+ - `horizontal-lockup-white.svg` - Monogram + wordmark, white
144
+
145
+ ---
146
+
147
+ ## Technical Specifications
148
+
149
+ ### Minimum Clear Space
150
+ - All sides: Equal to the height of the P stem (36px in a 64×64 viewBox)
151
+ - Ratio: Minimum 56% of logo height as clear space
152
+
153
+ ### Minimum Size
154
+ - Digital: 16×16px
155
+ - Print: 0.5 inch (12.7mm)
156
+
157
+ ### Stroke Weight
158
+ - Always: 1.5px at base viewBox
159
+ - Scales proportionally with size
160
+ - Never adjust stroke weight manually
161
+
162
+ ### Export Settings
163
+ - Format: SVG (vector)
164
+ - Additional: PNG at 2400px width minimum for raster needs
165
+ - Color mode: RGB for digital, CMYK for print
166
+ - Background: Transparent
167
+
168
+ ---
169
+
170
+ ## Construction Principles
171
+
172
+ 1. **Geometric Precision**: All coordinates are integers. No sub-pixel positioning.
173
+ 2. **Right Angles Only**: No curves, no rounded corners.
174
+ 3. **Mathematical Alignment**: Elements align to grid intersections.
175
+ 4. **Minimal Strokes**: Constructed from 6 lines total.
176
+ 5. **Binary Structure**: P and ¬P represent logical opposition.
177
+
178
+ ---
179
+
180
+ ## Usage Notes
181
+
182
+ - The negation mark (¬) must always appear in the upper-right
183
+ - Maintain exact proportions when scaling
184
+ - Never distort, rotate, or add effects
185
+ - Use only in black or white—no color variants
186
+ - Preserve stroke width ratio when scaling
187
+ - Export at multiple sizes from the same base vector
188
+
189
+ ---
190
+
191
+ ## Quick Reference: Base Monogram SVG
192
+
193
+ ```xml
194
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
195
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
196
+ <line x1="18" y1="14" x2="18" y2="50" />
197
+ <line x1="18" y1="14" x2="38" y2="14" />
198
+ <line x1="38" y1="14" x2="38" y2="32" />
199
+ <line x1="38" y1="32" x2="18" y2="32" />
200
+ <line x1="42" y1="14" x2="46" y2="14" />
201
+ <line x1="42" y1="14" x2="42" y2="18" />
202
+ </g>
203
+ </svg>
204
+ ```
205
+
206
+ To change to white, replace `stroke="#000000"` with `stroke="#FFFFFF"`.
207
+
208
+ ---
209
+
210
+ ## Platform-Specific Exports
211
+
212
+ ### Favicon
213
+ Use `icon-32.svg` or convert to ICO format
214
+
215
+ ### Social Media
216
+ - Twitter/X: 400×400px PNG (export from icon-256.svg)
217
+ - LinkedIn: 300×300px PNG
218
+ - Facebook: 180×180px PNG
219
+
220
+ ### API/SDK
221
+ Use horizontal-lockup for documentation
222
+ Use monogram for API badges and small UI elements
223
+
224
+ ### Presentations
225
+ Use horizontal-lockup-black.svg or horizontal-lockup-white.svg
226
+ Export at 2400px width for high-resolution displays
@@ -0,0 +1,16 @@
1
+ <svg width="300" height="80" viewBox="0 0 300 80" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Monogram -->
3
+ <g transform="translate(20, 8)">
4
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
5
+ <line x1="18" y1="14" x2="18" y2="50" />
6
+ <line x1="18" y1="14" x2="38" y2="14" />
7
+ <line x1="38" y1="14" x2="38" y2="32" />
8
+ <line x1="38" y1="32" x2="18" y2="32" />
9
+ <line x1="42" y1="14" x2="46" y2="14" />
10
+ <line x1="42" y1="14" x2="42" y2="18" />
11
+ </g>
12
+ </g>
13
+
14
+ <!-- Wordmark -->
15
+ <text x="120" y="50%" dominant-baseline="middle" font-family="Inter, system-ui, sans-serif" font-size="48" font-weight="500" fill="#000000" letter-spacing="0.96">P¬P</text>
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg width="300" height="80" viewBox="0 0 300 80" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Monogram -->
3
+ <g transform="translate(20, 8)">
4
+ <g stroke-width="1.5" stroke="#FFFFFF" stroke-linecap="square" stroke-linejoin="miter">
5
+ <line x1="18" y1="14" x2="18" y2="50" />
6
+ <line x1="18" y1="14" x2="38" y2="14" />
7
+ <line x1="38" y1="14" x2="38" y2="32" />
8
+ <line x1="38" y1="32" x2="18" y2="32" />
9
+ <line x1="42" y1="14" x2="46" y2="14" />
10
+ <line x1="42" y1="14" x2="42" y2="18" />
11
+ </g>
12
+ </g>
13
+
14
+ <!-- Wordmark -->
15
+ <text x="120" y="50%" dominant-baseline="middle" font-family="Inter, system-ui, sans-serif" font-size="48" font-weight="500" fill="#FFFFFF" letter-spacing="0.96">P¬P</text>
16
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg width="128" height="128" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <line x1="18" y1="14" x2="18" y2="50" />
4
+ <line x1="18" y1="14" x2="38" y2="14" />
5
+ <line x1="38" y1="14" x2="38" y2="32" />
6
+ <line x1="38" y1="32" x2="18" y2="32" />
7
+ <line x1="42" y1="14" x2="46" y2="14" />
8
+ <line x1="42" y1="14" x2="42" y2="18" />
9
+ </g>
10
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg width="16" height="16" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <line x1="18" y1="14" x2="18" y2="50" />
4
+ <line x1="18" y1="14" x2="38" y2="14" />
5
+ <line x1="38" y1="14" x2="38" y2="32" />
6
+ <line x1="38" y1="32" x2="18" y2="32" />
7
+ <line x1="42" y1="14" x2="46" y2="14" />
8
+ <line x1="42" y1="14" x2="42" y2="18" />
9
+ </g>
10
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg width="256" height="256" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <line x1="18" y1="14" x2="18" y2="50" />
4
+ <line x1="18" y1="14" x2="38" y2="14" />
5
+ <line x1="38" y1="14" x2="38" y2="32" />
6
+ <line x1="38" y1="32" x2="18" y2="32" />
7
+ <line x1="42" y1="14" x2="46" y2="14" />
8
+ <line x1="42" y1="14" x2="42" y2="18" />
9
+ </g>
10
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg width="32" height="32" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <line x1="18" y1="14" x2="18" y2="50" />
4
+ <line x1="18" y1="14" x2="38" y2="14" />
5
+ <line x1="38" y1="14" x2="38" y2="32" />
6
+ <line x1="38" y1="32" x2="18" y2="32" />
7
+ <line x1="42" y1="14" x2="46" y2="14" />
8
+ <line x1="42" y1="14" x2="42" y2="18" />
9
+ </g>
10
+ </svg>
@@ -0,0 +1,10 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <line x1="18" y1="14" x2="18" y2="50" />
4
+ <line x1="18" y1="14" x2="38" y2="14" />
5
+ <line x1="38" y1="14" x2="38" y2="32" />
6
+ <line x1="38" y1="32" x2="18" y2="32" />
7
+ <line x1="42" y1="14" x2="46" y2="14" />
8
+ <line x1="42" y1="14" x2="42" y2="18" />
9
+ </g>
10
+ </svg>
Binary file
@@ -0,0 +1,15 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#000000" stroke-linecap="square" stroke-linejoin="miter">
3
+ <!-- Left vertical stem -->
4
+ <line x1="18" y1="14" x2="18" y2="50" />
5
+ <!-- Top horizontal -->
6
+ <line x1="18" y1="14" x2="38" y2="14" />
7
+ <!-- Right vertical for P bowl -->
8
+ <line x1="38" y1="14" x2="38" y2="32" />
9
+ <!-- Bottom horizontal of P bowl -->
10
+ <line x1="38" y1="32" x2="18" y2="32" />
11
+ <!-- Negation mark (¬) in upper-right -->
12
+ <line x1="42" y1="14" x2="46" y2="14" />
13
+ <line x1="42" y1="14" x2="42" y2="18" />
14
+ </g>
15
+ </svg>
@@ -0,0 +1,15 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g stroke-width="1.5" stroke="#FFFFFF" stroke-linecap="square" stroke-linejoin="miter">
3
+ <!-- Left vertical stem -->
4
+ <line x1="18" y1="14" x2="18" y2="50" />
5
+ <!-- Top horizontal -->
6
+ <line x1="18" y1="14" x2="38" y2="14" />
7
+ <!-- Right vertical for P bowl -->
8
+ <line x1="38" y1="14" x2="38" y2="32" />
9
+ <!-- Bottom horizontal of P bowl -->
10
+ <line x1="38" y1="32" x2="18" y2="32" />
11
+ <!-- Negation mark (¬) in upper-right -->
12
+ <line x1="42" y1="14" x2="46" y2="14" />
13
+ <line x1="42" y1="14" x2="42" y2="18" />
14
+ </g>
15
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="200" height="80" viewBox="0 0 200 80" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="48" font-weight="500" fill="#000000" letter-spacing="0.96">P¬P</text>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="200" height="80" viewBox="0 0 200 80" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="Inter, system-ui, sans-serif" font-size="48" font-weight="500" fill="#FFFFFF" letter-spacing="0.96">P¬P</text>
3
+ </svg>
Binary file
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ """Public entrypoint for the pnotp client library."""
4
+
5
+ import os
6
+
7
+ from .client import Client as _Client, DEFAULT_BASE_URL
8
+
9
+
10
+ def client(api_key: str | None = None, base_url: str | None = None) -> _Client:
11
+ """
12
+ Factory for the PnotP client.
13
+
14
+ If no api_key is provided, the loader will pull it from the environment
15
+ using (in order): PNOTP_API_KEY, pnotp_API_KEY.
16
+ The base URL defaults to the production endpoint unless explicitly overridden.
17
+ """
18
+ resolved_key = api_key or os.getenv("PNOTP_API_KEY") or os.getenv("pnotp_API_KEY")
19
+ resolved_base = base_url or DEFAULT_BASE_URL
20
+ return _Client(api_key=resolved_key, base_url=resolved_base)
21
+
22
+
23
+ __all__ = ["client"]
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Optional, Union
4
+
5
+ import httpx
6
+
7
+ from .models import RemoteModel
8
+
9
+ DEFAULT_BASE_URL = "https://api.pnotp.ai/v1"
10
+
11
+
12
+ class Client:
13
+ """
14
+ Main PnotP client.
15
+
16
+ Usage:
17
+ import pnotp as pnp
18
+ client = pnp.client(api_key="API_KEY", base_url="http://127.0.0.1:8000/v1")
19
+ client.models() # list models
20
+ model = client.models("pneumonia_binary_classification")
21
+ result = model.predict("image.jpg")
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ api_key: str | None,
27
+ base_url: str = DEFAULT_BASE_URL,
28
+ timeout: float = 30.0,
29
+ ) -> None:
30
+ if not api_key:
31
+ raise ValueError("api_key is required. Pass it explicitly or set PNOTP_API_KEY.")
32
+ base = base_url.rstrip("/")
33
+ base_for_httpx = base if base.endswith("/") else f"{base}/"
34
+
35
+ self.api_key = api_key
36
+ self.base_url = base
37
+ self._http = httpx.Client(
38
+ base_url=base_for_httpx,
39
+ timeout=timeout,
40
+ headers={"X-API-Key": self.api_key},
41
+ )
42
+
43
+ def close(self) -> None:
44
+ self._http.close()
45
+
46
+ def __enter__(self) -> "Client":
47
+ return self
48
+
49
+ def __exit__(self, exc_type, exc, tb) -> None:
50
+ self.close()
51
+
52
+ def models(
53
+ self,
54
+ model: Optional[str] = None,
55
+ ) -> Union[List[str], RemoteModel]:
56
+ """
57
+ - If called with no args: lists all available model ids.
58
+ - If `model` is provided: returns a RemoteModel handle.
59
+ """
60
+ if model is None:
61
+ resp = self._http.get("models")
62
+ resp.raise_for_status()
63
+ data = resp.json() # {"models": [{...}, ...]}
64
+
65
+ model_ids = [m["id"] for m in data.get("models", [])]
66
+
67
+ print("You have access to the following models:")
68
+ for mid in model_ids:
69
+ print(f"- {mid}")
70
+
71
+ return model_ids
72
+
73
+ return RemoteModel(name=model, http_client=self._http)
74
+
75
+
76
+ __all__ = ["Client", "DEFAULT_BASE_URL"]
@@ -0,0 +1,13 @@
1
+ """Custom exceptions for the pnotp client."""
2
+
3
+
4
+ class PnotPError(Exception):
5
+ """Base error for all pnotp client exceptions."""
6
+
7
+
8
+ class VisionOnlyError(PnotPError):
9
+ """Raised when attention maps are requested for non-vision models."""
10
+
11
+
12
+ __all__ = ["PnotPError", "VisionOnlyError"]
13
+