ai-forge-sdk 0.1.0__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.
- ai_forge/__about__.py +1 -0
- ai_forge/__init__.py +27 -0
- ai_forge/auth.py +32 -0
- ai_forge/cli.py +44 -0
- ai_forge/client.py +128 -0
- ai_forge/config.py +46 -0
- ai_forge/exceptions.py +58 -0
- ai_forge/logger.py +8 -0
- ai_forge/models/__init__.py +43 -0
- ai_forge/models/auth.py +37 -0
- ai_forge/models/auth_recovery.py +29 -0
- ai_forge/models/base.py +48 -0
- ai_forge/models/certificate.py +20 -0
- ai_forge/models/common.py +31 -0
- ai_forge/models/compute.py +80 -0
- ai_forge/models/connector.py +70 -0
- ai_forge/models/dashboard.py +25 -0
- ai_forge/models/iam.py +92 -0
- ai_forge/models/organization.py +56 -0
- ai_forge/models/service_catalog.py +15 -0
- ai_forge/models/service_template.py +43 -0
- ai_forge/models/sso.py +24 -0
- ai_forge/models/storage.py +29 -0
- ai_forge/models/tenant_user.py +33 -0
- ai_forge/models/workspace.py +79 -0
- ai_forge/pagination.py +24 -0
- ai_forge/py.typed +0 -0
- ai_forge/resources/__init__.py +39 -0
- ai_forge/resources/auth.py +84 -0
- ai_forge/resources/base.py +69 -0
- ai_forge/resources/certificates.py +38 -0
- ai_forge/resources/compute.py +23 -0
- ai_forge/resources/connectors.py +47 -0
- ai_forge/resources/dashboard.py +61 -0
- ai_forge/resources/databases.py +71 -0
- ai_forge/resources/iam.py +314 -0
- ai_forge/resources/namespaces.py +51 -0
- ai_forge/resources/notebooks.py +43 -0
- ai_forge/resources/operations.py +42 -0
- ai_forge/resources/organizations.py +83 -0
- ai_forge/resources/service_catalog.py +18 -0
- ai_forge/resources/service_templates.py +55 -0
- ai_forge/resources/sso.py +19 -0
- ai_forge/resources/storage.py +61 -0
- ai_forge/resources/tenant_users.py +57 -0
- ai_forge/resources/vms.py +78 -0
- ai_forge/resources/workspaces.py +135 -0
- ai_forge/transport.py +179 -0
- ai_forge_sdk-0.1.0.dist-info/METADATA +938 -0
- ai_forge_sdk-0.1.0.dist-info/RECORD +53 -0
- ai_forge_sdk-0.1.0.dist-info/WHEEL +4 -0
- ai_forge_sdk-0.1.0.dist-info/entry_points.txt +2 -0
- ai_forge_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
ai_forge/__about__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
ai_forge/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""AI Forge Python SDK."""
|
|
2
|
+
|
|
3
|
+
from ai_forge.__about__ import __version__
|
|
4
|
+
from ai_forge.client import AIForgeClient
|
|
5
|
+
from ai_forge.config import AIForgeConfig
|
|
6
|
+
from ai_forge.exceptions import (
|
|
7
|
+
AIForgeAPIError,
|
|
8
|
+
AIForgeAuthenticationError,
|
|
9
|
+
AIForgeConflictError,
|
|
10
|
+
AIForgeError,
|
|
11
|
+
AIForgeNotFoundError,
|
|
12
|
+
AIForgeRateLimitError,
|
|
13
|
+
AIForgeValidationError,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AIForgeAPIError",
|
|
18
|
+
"AIForgeAuthenticationError",
|
|
19
|
+
"AIForgeClient",
|
|
20
|
+
"AIForgeConfig",
|
|
21
|
+
"AIForgeConflictError",
|
|
22
|
+
"AIForgeError",
|
|
23
|
+
"AIForgeNotFoundError",
|
|
24
|
+
"AIForgeRateLimitError",
|
|
25
|
+
"AIForgeValidationError",
|
|
26
|
+
"__version__",
|
|
27
|
+
]
|
ai_forge/auth.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Protocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AuthProvider(Protocol):
|
|
8
|
+
"""Protocol for auth providers that can enrich request headers."""
|
|
9
|
+
|
|
10
|
+
def headers(self) -> dict[str, str]:
|
|
11
|
+
"""Return HTTP headers used for authentication."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True, slots=True)
|
|
15
|
+
class ApiKeyAuth:
|
|
16
|
+
api_key: str
|
|
17
|
+
|
|
18
|
+
def headers(self) -> dict[str, str]:
|
|
19
|
+
return {"x-auth-token": self.api_key}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class BearerTokenAuth:
|
|
24
|
+
token: str
|
|
25
|
+
|
|
26
|
+
def headers(self) -> dict[str, str]:
|
|
27
|
+
return {"x-auth-token": self.token}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class NoAuth:
|
|
31
|
+
def headers(self) -> dict[str, str]:
|
|
32
|
+
return {}
|
ai_forge/cli.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ai_forge import __version__
|
|
8
|
+
from ai_forge.client import AIForgeClient
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main(argv: list[str] | None = None) -> int:
|
|
12
|
+
parser = argparse.ArgumentParser(prog="ai-forge", description="AI Forge SDK utility CLI")
|
|
13
|
+
parser.add_argument("--version", action="store_true", help="Show SDK version")
|
|
14
|
+
|
|
15
|
+
subcommands = parser.add_subparsers(dest="command")
|
|
16
|
+
subcommands.add_parser("workspaces", help="List workspaces")
|
|
17
|
+
subcommands.add_parser("catalog", help="List service catalog items")
|
|
18
|
+
|
|
19
|
+
args = parser.parse_args(argv)
|
|
20
|
+
|
|
21
|
+
if args.version:
|
|
22
|
+
print(__version__)
|
|
23
|
+
return 0
|
|
24
|
+
|
|
25
|
+
if args.command == "workspaces":
|
|
26
|
+
with AIForgeClient.from_env() as client:
|
|
27
|
+
_print_json(client.workspaces.list().model_dump(mode="json"))
|
|
28
|
+
return 0
|
|
29
|
+
|
|
30
|
+
if args.command == "catalog":
|
|
31
|
+
with AIForgeClient.from_env() as client:
|
|
32
|
+
_print_json(client.service_catalog.list().model_dump(mode="json"))
|
|
33
|
+
return 0
|
|
34
|
+
|
|
35
|
+
parser.print_help()
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _print_json(data: Any) -> None:
|
|
40
|
+
print(json.dumps(data, indent=2, sort_keys=True))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
raise SystemExit(main())
|
ai_forge/client.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from types import TracebackType
|
|
4
|
+
|
|
5
|
+
from ai_forge.auth import ApiKeyAuth, AuthProvider, BearerTokenAuth, NoAuth
|
|
6
|
+
from ai_forge.config import AIForgeConfig
|
|
7
|
+
from ai_forge.models.auth import LoginResponse, RefreshTokenResponse
|
|
8
|
+
from ai_forge.resources import (
|
|
9
|
+
AuthResource,
|
|
10
|
+
CertificatesResource,
|
|
11
|
+
ComputeResource,
|
|
12
|
+
ConnectorsResource,
|
|
13
|
+
DashboardResource,
|
|
14
|
+
DatabaseResource,
|
|
15
|
+
IAMResource,
|
|
16
|
+
NamespacesResource,
|
|
17
|
+
NotebookResource,
|
|
18
|
+
OperationsResource,
|
|
19
|
+
OrganizationsResource,
|
|
20
|
+
ServiceCatalogResource,
|
|
21
|
+
ServiceTemplatesResource,
|
|
22
|
+
SSOResource,
|
|
23
|
+
StorageResource,
|
|
24
|
+
TenantUsersResource,
|
|
25
|
+
VMResource,
|
|
26
|
+
WorkspacesResource,
|
|
27
|
+
)
|
|
28
|
+
from ai_forge.transport import Transport
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AIForgeClient:
|
|
32
|
+
"""Main entry point for the AI Forge SDK."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, config: AIForgeConfig, auth_provider: AuthProvider | None = None) -> None:
|
|
35
|
+
self.config = config
|
|
36
|
+
auth = auth_provider or self._auth_provider_from_config(config)
|
|
37
|
+
self._transport = Transport(config=config, auth_headers=auth.headers())
|
|
38
|
+
|
|
39
|
+
self.auth = AuthResource(self._transport)
|
|
40
|
+
self.certificates = CertificatesResource(self._transport)
|
|
41
|
+
self.organizations = OrganizationsResource(self._transport)
|
|
42
|
+
self.workspaces = WorkspacesResource(self._transport)
|
|
43
|
+
self.connectors = ConnectorsResource(self._transport)
|
|
44
|
+
self.namespaces = NamespacesResource(self._transport)
|
|
45
|
+
self.vms = VMResource(self._transport)
|
|
46
|
+
self.dashboard = DashboardResource(self._transport)
|
|
47
|
+
self.databases = DatabaseResource(self._transport)
|
|
48
|
+
self.notebooks = NotebookResource(self._transport)
|
|
49
|
+
self.service_catalog = ServiceCatalogResource(self._transport)
|
|
50
|
+
self.service_templates = ServiceTemplatesResource(self._transport)
|
|
51
|
+
self.sso = SSOResource(self._transport)
|
|
52
|
+
self.tenant_users = TenantUsersResource(self._transport)
|
|
53
|
+
self.compute = ComputeResource(self._transport)
|
|
54
|
+
self.storage = StorageResource(self._transport)
|
|
55
|
+
self.iam = IAMResource(self._transport)
|
|
56
|
+
self.operations = OperationsResource(self._transport)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_env(cls) -> "AIForgeClient":
|
|
60
|
+
return cls(AIForgeConfig.from_env())
|
|
61
|
+
|
|
62
|
+
def close(self) -> None:
|
|
63
|
+
self._transport.close()
|
|
64
|
+
|
|
65
|
+
def login(self, email: str, password: str, tenant_slug: str) -> LoginResponse:
|
|
66
|
+
"""
|
|
67
|
+
Log in to AI Forge and automatically update the client's authentication state.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
email: User's email address.
|
|
71
|
+
password: User's password.
|
|
72
|
+
tenant_slug: The slug of the tenant to log in to.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
LoginResponse containing tokens and user profile.
|
|
76
|
+
"""
|
|
77
|
+
resp = self.auth.login(email, password, tenant_slug)
|
|
78
|
+
self.set_token(resp.tokens.access_token)
|
|
79
|
+
self._refresh_token = resp.tokens.refresh_token
|
|
80
|
+
return resp
|
|
81
|
+
|
|
82
|
+
def refresh_token(self) -> RefreshTokenResponse:
|
|
83
|
+
"""
|
|
84
|
+
Refresh the access token using the stored refresh token.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
RefreshTokenResponse with new tokens.
|
|
88
|
+
"""
|
|
89
|
+
if not hasattr(self, "_refresh_token") or not self._refresh_token:
|
|
90
|
+
raise ValueError("No refresh token available. Please login first.")
|
|
91
|
+
|
|
92
|
+
resp = self.auth.refresh_token(self._refresh_token)
|
|
93
|
+
self.set_token(resp.tokens.access_token)
|
|
94
|
+
self._refresh_token = resp.tokens.refresh_token
|
|
95
|
+
return resp
|
|
96
|
+
|
|
97
|
+
def logout(self) -> None:
|
|
98
|
+
"""Log out and clear the client's authentication state."""
|
|
99
|
+
refresh_token = getattr(self, "_refresh_token", None)
|
|
100
|
+
self.auth.logout(refresh_token=refresh_token)
|
|
101
|
+
self._transport.set_auth_headers({})
|
|
102
|
+
self._refresh_token = None
|
|
103
|
+
|
|
104
|
+
def set_token(self, token: str) -> None:
|
|
105
|
+
"""Update the client to use a Bearer token for authentication."""
|
|
106
|
+
self._transport.set_auth_headers(BearerTokenAuth(token).headers())
|
|
107
|
+
|
|
108
|
+
def set_api_key(self, api_key: str) -> None:
|
|
109
|
+
"""Update the client to use an API key for authentication."""
|
|
110
|
+
self._transport.set_auth_headers(ApiKeyAuth(api_key).headers())
|
|
111
|
+
|
|
112
|
+
def __enter__(self) -> "AIForgeClient":
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
def __exit__(
|
|
116
|
+
self,
|
|
117
|
+
exc_type: type[BaseException] | None,
|
|
118
|
+
exc: BaseException | None,
|
|
119
|
+
traceback: TracebackType | None,
|
|
120
|
+
) -> None:
|
|
121
|
+
self.close()
|
|
122
|
+
|
|
123
|
+
def _auth_provider_from_config(self, config: AIForgeConfig) -> AuthProvider:
|
|
124
|
+
if config.bearer_token:
|
|
125
|
+
return BearerTokenAuth(config.bearer_token)
|
|
126
|
+
if config.api_key:
|
|
127
|
+
return ApiKeyAuth(config.api_key)
|
|
128
|
+
return NoAuth()
|
ai_forge/config.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class AIForgeConfig:
|
|
9
|
+
"""Runtime configuration for the AI Forge SDK.
|
|
10
|
+
|
|
11
|
+
Keep this object immutable so the same config can be safely shared across
|
|
12
|
+
resource clients.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
base_url: str
|
|
16
|
+
api_key: str | None = None
|
|
17
|
+
bearer_token: str | None = None
|
|
18
|
+
tenant_id: str | None = None
|
|
19
|
+
workspace_id: str | None = None
|
|
20
|
+
timeout_seconds: float = 30.0
|
|
21
|
+
max_retries: int = 3
|
|
22
|
+
user_agent: str = "ai-forge-sdk-python"
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_env(cls) -> "AIForgeConfig":
|
|
26
|
+
"""Create config from environment variables."""
|
|
27
|
+
|
|
28
|
+
return cls(
|
|
29
|
+
base_url=_required_env("AI_FORGE_BASE_URL"),
|
|
30
|
+
api_key=os.getenv("AI_FORGE_API_KEY"),
|
|
31
|
+
bearer_token=os.getenv("AI_FORGE_BEARER_TOKEN"),
|
|
32
|
+
tenant_id=os.getenv("AI_FORGE_TENANT_ID"),
|
|
33
|
+
workspace_id=os.getenv("AI_FORGE_WORKSPACE_ID"),
|
|
34
|
+
timeout_seconds=float(os.getenv("AI_FORGE_TIMEOUT_SECONDS", "30")),
|
|
35
|
+
max_retries=int(os.getenv("AI_FORGE_MAX_RETRIES", "3")),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def normalized_base_url(self) -> str:
|
|
39
|
+
return self.base_url.rstrip("/")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _required_env(name: str) -> str:
|
|
43
|
+
value = os.getenv(name)
|
|
44
|
+
if not value:
|
|
45
|
+
raise RuntimeError(f"Required environment variable is missing: {name}")
|
|
46
|
+
return value
|
ai_forge/exceptions.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AIForgeError(Exception):
|
|
8
|
+
"""Base exception for all SDK errors."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class AIForgeAPIError(AIForgeError):
|
|
13
|
+
"""Raised when AI Forge returns a non-successful HTTP status."""
|
|
14
|
+
|
|
15
|
+
message: str
|
|
16
|
+
status_code: int | None = None
|
|
17
|
+
error_code: str | None = None
|
|
18
|
+
request_id: str | None = None
|
|
19
|
+
details: Any | None = None
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
msg = self.message or "AI Forge API request failed"
|
|
23
|
+
parts = [msg]
|
|
24
|
+
if self.status_code is not None:
|
|
25
|
+
parts.append(f"status={self.status_code}")
|
|
26
|
+
if self.error_code:
|
|
27
|
+
parts.append(f"code={self.error_code}")
|
|
28
|
+
if self.request_id:
|
|
29
|
+
parts.append(f"request_id={self.request_id}")
|
|
30
|
+
return " | ".join(parts)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AIForgeAuthenticationError(AIForgeAPIError):
|
|
34
|
+
"""Authentication failed."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AIForgePermissionError(AIForgeAPIError):
|
|
38
|
+
"""Authorization failed."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AIForgeValidationError(AIForgeAPIError):
|
|
42
|
+
"""Request validation failed."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AIForgeNotFoundError(AIForgeAPIError):
|
|
46
|
+
"""Requested resource was not found."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AIForgeConflictError(AIForgeAPIError):
|
|
50
|
+
"""Request conflicts with current resource state."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AIForgeRateLimitError(AIForgeAPIError):
|
|
54
|
+
"""Rate limit exceeded."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AIForgeInternalServerError(AIForgeAPIError):
|
|
58
|
+
"""Internal server error occurred."""
|
ai_forge/logger.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from ai_forge.models.base import AIForgeModel, Operation, Page, ResourceModel
|
|
2
|
+
from ai_forge.models.compute import CreateComputeRequest, Notebook, ServiceInstance
|
|
3
|
+
from ai_forge.models.connector import (
|
|
4
|
+
Connector,
|
|
5
|
+
ConnectorCreateRequest,
|
|
6
|
+
Namespace,
|
|
7
|
+
NamespaceCreateRequest,
|
|
8
|
+
)
|
|
9
|
+
from ai_forge.models.iam import Policy, Role, User
|
|
10
|
+
from ai_forge.models.organization import (
|
|
11
|
+
Organization,
|
|
12
|
+
OrganizationCreateRequest,
|
|
13
|
+
OrganizationUpdateRequest,
|
|
14
|
+
)
|
|
15
|
+
from ai_forge.models.service_catalog import ServiceCatalogItem
|
|
16
|
+
from ai_forge.models.storage import CreateBucketRequest, StorageBucket, StorageFile
|
|
17
|
+
from ai_forge.models.workspace import CreateWorkspaceRequest, Workspace
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"AIForgeModel",
|
|
21
|
+
"Connector",
|
|
22
|
+
"ConnectorCreateRequest",
|
|
23
|
+
"Namespace",
|
|
24
|
+
"NamespaceCreateRequest",
|
|
25
|
+
"CreateBucketRequest",
|
|
26
|
+
"Organization",
|
|
27
|
+
"OrganizationCreateRequest",
|
|
28
|
+
"OrganizationUpdateRequest",
|
|
29
|
+
"CreateComputeRequest",
|
|
30
|
+
"CreateWorkspaceRequest",
|
|
31
|
+
"StorageBucket",
|
|
32
|
+
"StorageFile",
|
|
33
|
+
"Operation",
|
|
34
|
+
"Page",
|
|
35
|
+
"Policy",
|
|
36
|
+
"ResourceModel",
|
|
37
|
+
"Role",
|
|
38
|
+
"ServiceCatalogItem",
|
|
39
|
+
"User",
|
|
40
|
+
"ServiceInstance",
|
|
41
|
+
"Notebook",
|
|
42
|
+
"Workspace",
|
|
43
|
+
]
|
ai_forge/models/auth.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from ai_forge.models.base import AIForgeModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LoginRequest(AIForgeModel):
|
|
9
|
+
tenant_slug: str = Field(alias="tenantSlug")
|
|
10
|
+
email: str
|
|
11
|
+
password: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TokenPair(AIForgeModel):
|
|
15
|
+
access_token: str = Field(alias="accessToken")
|
|
16
|
+
refresh_token: str = Field(alias="refreshToken")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UserProfile(AIForgeModel):
|
|
20
|
+
first_name: str | None = Field(default=None, alias="firstName")
|
|
21
|
+
last_name: str | None = Field(default=None, alias="lastName")
|
|
22
|
+
email: str
|
|
23
|
+
profile_image: str | None = Field(default=None, alias="profileImage")
|
|
24
|
+
is_platform_admin: bool = Field(default=False, alias="isPlatformAdmin")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LoginResponse(AIForgeModel):
|
|
28
|
+
tokens: TokenPair
|
|
29
|
+
user: UserProfile
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RefreshTokenRequest(AIForgeModel):
|
|
33
|
+
refresh_token: str = Field(alias="refreshToken")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RefreshTokenResponse(AIForgeModel):
|
|
37
|
+
tokens: TokenPair
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from ai_forge.models.base import AIForgeModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ForgotPasswordRequest(AIForgeModel):
|
|
8
|
+
tenant_slug: str = Field(alias="tenantSlug")
|
|
9
|
+
email: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ForgotPasswordResponse(AIForgeModel):
|
|
13
|
+
attempts_remaining: int = Field(alias="attemptsRemaining")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class VerifyOtpRequest(AIForgeModel):
|
|
17
|
+
tenant_slug: str = Field(alias="tenantSlug")
|
|
18
|
+
email: str
|
|
19
|
+
otp: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class VerifyOtpResponse(AIForgeModel):
|
|
23
|
+
reset_token: str = Field(alias="resetToken")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ResetPasswordRequest(AIForgeModel):
|
|
27
|
+
tenant_slug: str = Field(alias="tenantSlug")
|
|
28
|
+
email: str
|
|
29
|
+
password: str
|
ai_forge/models/base.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AIForgeModel(BaseModel):
|
|
12
|
+
"""Base model used by all SDK DTOs."""
|
|
13
|
+
|
|
14
|
+
model_config = ConfigDict(
|
|
15
|
+
populate_by_name=True,
|
|
16
|
+
extra="allow",
|
|
17
|
+
use_enum_values=True,
|
|
18
|
+
validate_assignment=False,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ResourceModel(AIForgeModel):
|
|
23
|
+
id: str
|
|
24
|
+
name: str | None = None
|
|
25
|
+
created_at: datetime | None = Field(default=None, alias="createdAt")
|
|
26
|
+
updated_at: datetime | None = Field(default=None, alias="updatedAt")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Page(AIForgeModel, Generic[T]):
|
|
30
|
+
"""Generic list response matching AI Forge pagination structure."""
|
|
31
|
+
|
|
32
|
+
data: list[T] = Field(default_factory=list)
|
|
33
|
+
total_elements: int = Field(default=0, alias="totalElements")
|
|
34
|
+
total_pages: int = Field(default=0, alias="totalPages")
|
|
35
|
+
page_no: int = Field(default=1, alias="pageNo")
|
|
36
|
+
page_size: int = Field(default=10, alias="pageSize")
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def items(self) -> list[T]:
|
|
40
|
+
"""Alias for data for backward compatibility or convenience."""
|
|
41
|
+
return self.data
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Operation(ResourceModel):
|
|
45
|
+
status: str
|
|
46
|
+
resource_id: str | None = Field(default=None, alias="resourceId")
|
|
47
|
+
resource_type: str | None = Field(default=None, alias="resourceType")
|
|
48
|
+
error: str | None = None
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from ai_forge.models.base import AIForgeModel
|
|
9
|
+
|
|
10
|
+
from ai_forge.models.organization import Organization
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Certificate(AIForgeModel):
|
|
14
|
+
id: str | None = None
|
|
15
|
+
public_key: str = Field(alias="publicKey")
|
|
16
|
+
tenant_id: str | None = Field(None, alias="tenantId")
|
|
17
|
+
# This matches the 'belongsTo' association in your Sequelize model
|
|
18
|
+
tenant: Organization | None = None
|
|
19
|
+
version: str | None = None
|
|
20
|
+
timestamp: datetime | None = None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from ai_forge.models.base import AIForgeModel
|
|
8
|
+
|
|
9
|
+
Status = Literal["pending", "provisioning", "running", "degraded", "failed", "terminated"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Metadata(AIForgeModel):
|
|
13
|
+
labels: dict[str, str] = Field(default_factory=dict)
|
|
14
|
+
annotations: dict[str, str] = Field(default_factory=dict)
|
|
15
|
+
tags: dict[str, str] = Field(default_factory=dict)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ErrorResponse(AIForgeModel):
|
|
19
|
+
message: str | None = Field("AI Forge API request failed")
|
|
20
|
+
code: str | None = None
|
|
21
|
+
details: Any | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AttachmentSummary(AIForgeModel):
|
|
25
|
+
attached: int = 0
|
|
26
|
+
skipped: int = 0
|
|
27
|
+
detached: int = 0
|
|
28
|
+
not_found: int = Field(0, alias="notFound")
|
|
29
|
+
added: int = 0
|
|
30
|
+
removed: int = 0
|
|
31
|
+
updated: int = 0
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from ai_forge.models.base import AIForgeModel, ResourceModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ServiceInstance(ResourceModel):
|
|
12
|
+
kind: str
|
|
13
|
+
name: str
|
|
14
|
+
status: str
|
|
15
|
+
tenant_id: str = Field(alias="tenantId")
|
|
16
|
+
workspace_id: str = Field(alias="workspaceId")
|
|
17
|
+
connector_id: str | None = Field(None, alias="connectorId")
|
|
18
|
+
namespace_id: str | None = Field(None, alias="namespaceId")
|
|
19
|
+
desired_spec: dict[str, Any] = Field(default_factory=dict, alias="desiredSpec")
|
|
20
|
+
network_info: dict[str, Any] | None = Field(None, alias="networkInfo")
|
|
21
|
+
ports: list[dict[str, Any]] | None = None
|
|
22
|
+
outputs: dict[str, Any] = Field(default_factory=dict)
|
|
23
|
+
tags: dict[str, Any] = Field(default_factory=dict)
|
|
24
|
+
is_public: bool = Field(False, alias="isPublic")
|
|
25
|
+
last_error: str | None = Field(None, alias="lastError")
|
|
26
|
+
|
|
27
|
+
# Relations
|
|
28
|
+
tenant: dict[str, Any] | None = None
|
|
29
|
+
workspace: dict[str, Any] | None = None
|
|
30
|
+
connector: dict[str, Any] | None = None
|
|
31
|
+
namespace: dict[str, Any] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Notebook(ResourceModel):
|
|
35
|
+
service_instance_id: str = Field(alias="serviceInstanceId")
|
|
36
|
+
name: str
|
|
37
|
+
image: str
|
|
38
|
+
token: str
|
|
39
|
+
url: str | None = None
|
|
40
|
+
status: str
|
|
41
|
+
meta: dict[str, Any] = Field(default_factory=dict)
|
|
42
|
+
# Relation
|
|
43
|
+
service_instance: ServiceInstance | None = Field(None, alias="serviceInstance")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class VMUpdateRequest(AIForgeModel):
|
|
47
|
+
resource_quota_spec: dict[str, Any] | None = Field(None, alias="resourceQuotaSpec")
|
|
48
|
+
is_public: bool | None = Field(None, alias="isPublic")
|
|
49
|
+
ports: list[dict[str, Any]] | None = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class VMExposeRequest(AIForgeModel):
|
|
53
|
+
ports: list[dict[str, Any]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NotebookCreateRequest(AIForgeModel):
|
|
57
|
+
notebook_name: str = Field(alias="notebookName")
|
|
58
|
+
notebook_token: str = Field(alias="notebookToken")
|
|
59
|
+
storage_size: str = Field(alias="storageSize")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class DatabaseCreateRequest(AIForgeModel):
|
|
63
|
+
db_name: str = Field(alias="dbName")
|
|
64
|
+
db_user: str | None = Field(None, alias="dbUser")
|
|
65
|
+
db_password: str = Field(alias="dbPassword")
|
|
66
|
+
storage_size: str = Field(alias="storageSize")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DatabaseExposeRequest(AIForgeModel):
|
|
70
|
+
ports: list[dict[str, Any]]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CreateComputeRequest(AIForgeModel):
|
|
74
|
+
kind: str
|
|
75
|
+
name: str
|
|
76
|
+
connector_id: str | None = Field(None, alias="connectorId")
|
|
77
|
+
namespace_id: str | None = Field(None, alias="namespaceId")
|
|
78
|
+
template_id: str | None = Field(None, alias="templateId")
|
|
79
|
+
desired_spec: dict[str, Any] = Field(default_factory=dict, alias="desiredSpec")
|
|
80
|
+
tags: dict[str, Any] = Field(default_factory=dict)
|