featrixsphere 0.2.5566__py3-none-any.whl → 0.2.5978__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.
- featrixsphere/__init__.py +37 -18
- featrixsphere/api/__init__.py +50 -0
- featrixsphere/api/api_endpoint.py +280 -0
- featrixsphere/api/client.py +396 -0
- featrixsphere/api/foundational_model.py +658 -0
- featrixsphere/api/http_client.py +209 -0
- featrixsphere/api/notebook_helper.py +584 -0
- featrixsphere/api/prediction_result.py +231 -0
- featrixsphere/api/predictor.py +537 -0
- featrixsphere/api/reference_record.py +227 -0
- featrixsphere/api/vector_database.py +269 -0
- featrixsphere/client.py +211 -8
- {featrixsphere-0.2.5566.dist-info → featrixsphere-0.2.5978.dist-info}/METADATA +1 -1
- featrixsphere-0.2.5978.dist-info/RECORD +17 -0
- featrixsphere-0.2.5566.dist-info/RECORD +0 -7
- {featrixsphere-0.2.5566.dist-info → featrixsphere-0.2.5978.dist-info}/WHEEL +0 -0
- {featrixsphere-0.2.5566.dist-info → featrixsphere-0.2.5978.dist-info}/entry_points.txt +0 -0
- {featrixsphere-0.2.5566.dist-info → featrixsphere-0.2.5978.dist-info}/top_level.txt +0 -0
featrixsphere/__init__.py
CHANGED
|
@@ -3,42 +3,61 @@ Featrix Sphere API Client
|
|
|
3
3
|
|
|
4
4
|
Transform any CSV into a production-ready ML model in minutes, not months.
|
|
5
5
|
|
|
6
|
-
The Featrix Sphere API automatically builds neural embedding spaces from your data
|
|
7
|
-
and trains high-accuracy predictors without requiring any ML expertise.
|
|
6
|
+
The Featrix Sphere API automatically builds neural embedding spaces from your data
|
|
7
|
+
and trains high-accuracy predictors without requiring any ML expertise.
|
|
8
8
|
Just upload your data, specify what you want to predict, and get a production API endpoint.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
TWO API OPTIONS:
|
|
11
|
+
----------------
|
|
12
|
+
|
|
13
|
+
1. NEW Object-Oriented API (recommended for new projects):
|
|
14
|
+
|
|
15
|
+
>>> from featrixsphere.api import FeatrixSphere
|
|
16
|
+
>>>
|
|
17
|
+
>>> featrix = FeatrixSphere("http://your-server.com")
|
|
18
|
+
>>>
|
|
19
|
+
>>> # Create foundational model
|
|
20
|
+
>>> fm = featrix.create_foundational_model(
|
|
21
|
+
... name="my_model",
|
|
22
|
+
... csv_file="data.csv"
|
|
23
|
+
... )
|
|
24
|
+
>>> fm.wait_for_training()
|
|
25
|
+
>>>
|
|
26
|
+
>>> # Create predictor
|
|
27
|
+
>>> predictor = fm.create_classifier(
|
|
28
|
+
... target_column="target",
|
|
29
|
+
... name="my_predictor"
|
|
30
|
+
... )
|
|
31
|
+
>>> predictor.wait_for_training()
|
|
32
|
+
>>>
|
|
33
|
+
>>> # Make predictions
|
|
34
|
+
>>> result = predictor.predict({"feature": "value"})
|
|
35
|
+
>>> print(result.predicted_class)
|
|
36
|
+
>>> print(result.confidence)
|
|
37
|
+
|
|
38
|
+
2. Classic API (for existing code):
|
|
11
39
|
|
|
12
|
-
Example:
|
|
13
40
|
>>> from featrixsphere import FeatrixSphereClient
|
|
14
41
|
>>> import pandas as pd
|
|
15
|
-
>>>
|
|
42
|
+
>>>
|
|
16
43
|
>>> client = FeatrixSphereClient("http://your-server.com")
|
|
17
|
-
>>>
|
|
44
|
+
>>>
|
|
18
45
|
>>> # Upload DataFrame directly
|
|
19
46
|
>>> df = pd.read_csv("data.csv")
|
|
20
47
|
>>> session = client.upload_df_and_create_session(df=df)
|
|
21
|
-
>>>
|
|
48
|
+
>>>
|
|
22
49
|
>>> # Or upload CSV file directly (with automatic gzip compression)
|
|
23
50
|
>>> session = client.upload_df_and_create_session(file_path="data.csv")
|
|
24
|
-
>>>
|
|
51
|
+
>>>
|
|
25
52
|
>>> # Train a predictor
|
|
26
53
|
>>> client.train_single_predictor(session.session_id, "target_column", "set")
|
|
27
|
-
>>>
|
|
54
|
+
>>>
|
|
28
55
|
>>> # Make predictions
|
|
29
56
|
>>> result = client.predict(session.session_id, {"feature": "value"})
|
|
30
57
|
>>> print(result['prediction'])
|
|
31
|
-
>>>
|
|
32
|
-
>>> # NEW: Visualize training progress with beautiful plots!
|
|
33
|
-
>>> fig = client.plot_training_loss(session.session_id, style='notebook')
|
|
34
|
-
>>> # Returns matplotlib Figure - perfect for Jupyter notebooks!
|
|
35
|
-
>>>
|
|
36
|
-
>>> # Compare multiple training runs
|
|
37
|
-
>>> client.plot_training_comparison(['session1', 'session2'],
|
|
38
|
-
... labels=['Experiment A', 'Experiment B'])
|
|
39
58
|
"""
|
|
40
59
|
|
|
41
|
-
__version__ = "0.2.
|
|
60
|
+
__version__ = "0.2.5978"
|
|
42
61
|
__author__ = "Featrix"
|
|
43
62
|
__email__ = "support@featrix.com"
|
|
44
63
|
__license__ = "MIT"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FeatrixSphere New API
|
|
3
|
+
|
|
4
|
+
A clean, object-oriented API for interacting with FeatrixSphere.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from featrixsphere.api import FeatrixSphere
|
|
8
|
+
|
|
9
|
+
featrix = FeatrixSphere("https://sphere-api.featrix.com")
|
|
10
|
+
|
|
11
|
+
# Create foundational model
|
|
12
|
+
fm = featrix.create_foundational_model(
|
|
13
|
+
name="my_model",
|
|
14
|
+
csv_file="data.csv"
|
|
15
|
+
)
|
|
16
|
+
fm.wait_for_training()
|
|
17
|
+
|
|
18
|
+
# Create predictor
|
|
19
|
+
predictor = fm.create_classifier(
|
|
20
|
+
name="my_classifier",
|
|
21
|
+
target_column="target"
|
|
22
|
+
)
|
|
23
|
+
predictor.wait_for_training()
|
|
24
|
+
|
|
25
|
+
# Make predictions
|
|
26
|
+
result = predictor.predict({"feature1": "value1"})
|
|
27
|
+
print(result.predicted_class)
|
|
28
|
+
print(result.confidence)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from .client import FeatrixSphere
|
|
32
|
+
from .foundational_model import FoundationalModel
|
|
33
|
+
from .predictor import Predictor
|
|
34
|
+
from .prediction_result import PredictionResult, PredictionFeedback
|
|
35
|
+
from .vector_database import VectorDatabase
|
|
36
|
+
from .reference_record import ReferenceRecord
|
|
37
|
+
from .api_endpoint import APIEndpoint
|
|
38
|
+
from .notebook_helper import FeatrixNotebookHelper
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
'FeatrixSphere',
|
|
42
|
+
'FoundationalModel',
|
|
43
|
+
'Predictor',
|
|
44
|
+
'PredictionResult',
|
|
45
|
+
'PredictionFeedback',
|
|
46
|
+
'VectorDatabase',
|
|
47
|
+
'ReferenceRecord',
|
|
48
|
+
'APIEndpoint',
|
|
49
|
+
'FeatrixNotebookHelper',
|
|
50
|
+
]
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""
|
|
2
|
+
APIEndpoint class for FeatrixSphere API.
|
|
3
|
+
|
|
4
|
+
Represents a named API endpoint for a predictor with optional API key authentication.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Dict, Any, Optional, TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .http_client import ClientContext
|
|
14
|
+
from .predictor import Predictor
|
|
15
|
+
from .prediction_result import PredictionResult
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class APIEndpoint:
|
|
22
|
+
"""
|
|
23
|
+
Represents a named API endpoint for a predictor.
|
|
24
|
+
|
|
25
|
+
API endpoints allow you to create named, versioned access points for
|
|
26
|
+
predictions with optional API key authentication.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
id: Endpoint ID
|
|
30
|
+
name: Endpoint name
|
|
31
|
+
predictor_id: Associated predictor ID
|
|
32
|
+
session_id: Parent session ID
|
|
33
|
+
api_key: API key (if configured)
|
|
34
|
+
api_key_created_at: When API key was created
|
|
35
|
+
description: Endpoint description
|
|
36
|
+
url: Full endpoint URL
|
|
37
|
+
created_at: Creation timestamp
|
|
38
|
+
last_used_at: Last usage timestamp
|
|
39
|
+
usage_count: Number of predictions made via this endpoint
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
# Create from predictor
|
|
43
|
+
endpoint = predictor.create_api_endpoint(
|
|
44
|
+
name="production_api",
|
|
45
|
+
description="Production endpoint"
|
|
46
|
+
)
|
|
47
|
+
print(f"API Key: {endpoint.api_key}")
|
|
48
|
+
|
|
49
|
+
# Make predictions via endpoint
|
|
50
|
+
result = endpoint.predict({"age": 35, "income": 50000})
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
id: str
|
|
54
|
+
name: str
|
|
55
|
+
predictor_id: str
|
|
56
|
+
session_id: str
|
|
57
|
+
api_key: Optional[str] = None
|
|
58
|
+
api_key_created_at: Optional[datetime] = None
|
|
59
|
+
description: Optional[str] = None
|
|
60
|
+
url: Optional[str] = None
|
|
61
|
+
created_at: Optional[datetime] = None
|
|
62
|
+
last_used_at: Optional[datetime] = None
|
|
63
|
+
usage_count: int = 0
|
|
64
|
+
|
|
65
|
+
# Internal
|
|
66
|
+
_ctx: Optional['ClientContext'] = field(default=None, repr=False)
|
|
67
|
+
_predictor: Optional['Predictor'] = field(default=None, repr=False)
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def from_response(
|
|
71
|
+
cls,
|
|
72
|
+
response: Dict[str, Any],
|
|
73
|
+
predictor_id: str,
|
|
74
|
+
session_id: str,
|
|
75
|
+
ctx: Optional['ClientContext'] = None,
|
|
76
|
+
predictor: Optional['Predictor'] = None
|
|
77
|
+
) -> 'APIEndpoint':
|
|
78
|
+
"""Create APIEndpoint from API response."""
|
|
79
|
+
return cls(
|
|
80
|
+
id=response.get('endpoint_id') or response.get('id', ''),
|
|
81
|
+
name=response.get('name', ''),
|
|
82
|
+
predictor_id=predictor_id,
|
|
83
|
+
session_id=session_id,
|
|
84
|
+
api_key=response.get('api_key'),
|
|
85
|
+
api_key_created_at=datetime.fromisoformat(response['api_key_created_at'])
|
|
86
|
+
if response.get('api_key_created_at') else None,
|
|
87
|
+
description=response.get('description'),
|
|
88
|
+
url=response.get('url') or response.get('endpoint_url'),
|
|
89
|
+
created_at=datetime.fromisoformat(response['created_at'])
|
|
90
|
+
if response.get('created_at') else datetime.now(),
|
|
91
|
+
last_used_at=datetime.fromisoformat(response['last_used_at'])
|
|
92
|
+
if response.get('last_used_at') else None,
|
|
93
|
+
usage_count=response.get('usage_count', 0),
|
|
94
|
+
_ctx=ctx,
|
|
95
|
+
_predictor=predictor,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def predictor(self) -> Optional['Predictor']:
|
|
100
|
+
"""Get the associated predictor."""
|
|
101
|
+
return self._predictor
|
|
102
|
+
|
|
103
|
+
def predict(
|
|
104
|
+
self,
|
|
105
|
+
record: Dict[str, Any],
|
|
106
|
+
api_key: Optional[str] = None
|
|
107
|
+
) -> 'PredictionResult':
|
|
108
|
+
"""
|
|
109
|
+
Make a prediction via this endpoint.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
record: Input record dictionary
|
|
113
|
+
api_key: Override endpoint API key
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
PredictionResult with prediction, confidence, and prediction_uuid
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
result = endpoint.predict(
|
|
120
|
+
{"age": 35, "income": 50000},
|
|
121
|
+
api_key="sk_live_abc123" # Optional if endpoint has key
|
|
122
|
+
)
|
|
123
|
+
print(result.predicted_class)
|
|
124
|
+
"""
|
|
125
|
+
if not self._ctx:
|
|
126
|
+
raise ValueError("APIEndpoint not connected to client")
|
|
127
|
+
|
|
128
|
+
from .prediction_result import PredictionResult
|
|
129
|
+
|
|
130
|
+
# Clean the record
|
|
131
|
+
cleaned_record = self._clean_record(record)
|
|
132
|
+
|
|
133
|
+
# Build request with API key if provided
|
|
134
|
+
headers = {}
|
|
135
|
+
key_to_use = api_key or self.api_key
|
|
136
|
+
if key_to_use:
|
|
137
|
+
headers['X-API-Key'] = key_to_use
|
|
138
|
+
|
|
139
|
+
# Make request to endpoint
|
|
140
|
+
request_payload = {
|
|
141
|
+
"query_record": cleaned_record,
|
|
142
|
+
"predictor_id": self.predictor_id,
|
|
143
|
+
"endpoint_id": self.id,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
response = self._ctx.post_json(
|
|
147
|
+
f"/session/{self.session_id}/predict",
|
|
148
|
+
data=request_payload,
|
|
149
|
+
headers=headers
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Update usage stats
|
|
153
|
+
self.usage_count += 1
|
|
154
|
+
self.last_used_at = datetime.now()
|
|
155
|
+
|
|
156
|
+
return PredictionResult.from_response(response, cleaned_record, self._ctx)
|
|
157
|
+
|
|
158
|
+
def regenerate_api_key(self) -> str:
|
|
159
|
+
"""
|
|
160
|
+
Regenerate the API key for this endpoint.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
The new API key
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
new_key = endpoint.regenerate_api_key()
|
|
167
|
+
print(f"New API Key: {new_key}")
|
|
168
|
+
"""
|
|
169
|
+
if not self._ctx:
|
|
170
|
+
raise ValueError("APIEndpoint not connected to client")
|
|
171
|
+
|
|
172
|
+
response = self._ctx.post_json(
|
|
173
|
+
f"/session/{self.session_id}/endpoint/{self.id}/regenerate_key",
|
|
174
|
+
data={}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
self.api_key = response.get('api_key')
|
|
178
|
+
self.api_key_created_at = datetime.now()
|
|
179
|
+
|
|
180
|
+
return self.api_key or ""
|
|
181
|
+
|
|
182
|
+
def revoke_api_key(self) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Revoke/remove the API key (endpoint becomes public).
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
endpoint.revoke_api_key() # Endpoint now allows unauthenticated access
|
|
188
|
+
"""
|
|
189
|
+
if not self._ctx:
|
|
190
|
+
raise ValueError("APIEndpoint not connected to client")
|
|
191
|
+
|
|
192
|
+
self._ctx.post_json(
|
|
193
|
+
f"/session/{self.session_id}/endpoint/{self.id}/revoke_key",
|
|
194
|
+
data={}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
self.api_key = None
|
|
198
|
+
self.api_key_created_at = None
|
|
199
|
+
|
|
200
|
+
def get_usage_stats(self) -> Dict[str, Any]:
|
|
201
|
+
"""
|
|
202
|
+
Get usage statistics for this endpoint.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Dictionary with usage statistics
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
stats = endpoint.get_usage_stats()
|
|
209
|
+
print(f"Total predictions: {stats['usage_count']}")
|
|
210
|
+
print(f"Last used: {stats['last_used_at']}")
|
|
211
|
+
"""
|
|
212
|
+
if not self._ctx:
|
|
213
|
+
raise ValueError("APIEndpoint not connected to client")
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
response = self._ctx.get_json(
|
|
217
|
+
f"/session/{self.session_id}/endpoint/{self.id}/stats"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
self.usage_count = response.get('usage_count', self.usage_count)
|
|
221
|
+
if response.get('last_used_at'):
|
|
222
|
+
self.last_used_at = datetime.fromisoformat(response['last_used_at'])
|
|
223
|
+
|
|
224
|
+
return response
|
|
225
|
+
except Exception:
|
|
226
|
+
# Return cached values if endpoint doesn't exist yet
|
|
227
|
+
return {
|
|
228
|
+
'endpoint_id': self.id,
|
|
229
|
+
'usage_count': self.usage_count,
|
|
230
|
+
'last_used_at': self.last_used_at.isoformat() if self.last_used_at else None,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
def delete(self) -> None:
|
|
234
|
+
"""
|
|
235
|
+
Delete this endpoint.
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
endpoint.delete() # Endpoint is now deleted
|
|
239
|
+
"""
|
|
240
|
+
if not self._ctx:
|
|
241
|
+
raise ValueError("APIEndpoint not connected to client")
|
|
242
|
+
|
|
243
|
+
self._ctx.delete_json(
|
|
244
|
+
f"/session/{self.session_id}/endpoint/{self.id}"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def _clean_record(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
248
|
+
"""Clean a record for API submission."""
|
|
249
|
+
import math
|
|
250
|
+
|
|
251
|
+
cleaned = {}
|
|
252
|
+
for key, value in record.items():
|
|
253
|
+
if isinstance(value, float):
|
|
254
|
+
if math.isnan(value) or math.isinf(value):
|
|
255
|
+
value = None
|
|
256
|
+
if hasattr(value, 'item'):
|
|
257
|
+
value = value.item()
|
|
258
|
+
cleaned[key] = value
|
|
259
|
+
return cleaned
|
|
260
|
+
|
|
261
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
262
|
+
"""Convert to dictionary representation."""
|
|
263
|
+
return {
|
|
264
|
+
'id': self.id,
|
|
265
|
+
'name': self.name,
|
|
266
|
+
'predictor_id': self.predictor_id,
|
|
267
|
+
'session_id': self.session_id,
|
|
268
|
+
'api_key': self.api_key,
|
|
269
|
+
'api_key_created_at': self.api_key_created_at.isoformat()
|
|
270
|
+
if self.api_key_created_at else None,
|
|
271
|
+
'description': self.description,
|
|
272
|
+
'url': self.url,
|
|
273
|
+
'created_at': self.created_at.isoformat() if self.created_at else None,
|
|
274
|
+
'last_used_at': self.last_used_at.isoformat() if self.last_used_at else None,
|
|
275
|
+
'usage_count': self.usage_count,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
def __repr__(self) -> str:
|
|
279
|
+
key_str = ", has_key=True" if self.api_key else ""
|
|
280
|
+
return f"APIEndpoint(id='{self.id}', name='{self.name}'{key_str})"
|