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.
Files changed (53) hide show
  1. ai_forge/__about__.py +1 -0
  2. ai_forge/__init__.py +27 -0
  3. ai_forge/auth.py +32 -0
  4. ai_forge/cli.py +44 -0
  5. ai_forge/client.py +128 -0
  6. ai_forge/config.py +46 -0
  7. ai_forge/exceptions.py +58 -0
  8. ai_forge/logger.py +8 -0
  9. ai_forge/models/__init__.py +43 -0
  10. ai_forge/models/auth.py +37 -0
  11. ai_forge/models/auth_recovery.py +29 -0
  12. ai_forge/models/base.py +48 -0
  13. ai_forge/models/certificate.py +20 -0
  14. ai_forge/models/common.py +31 -0
  15. ai_forge/models/compute.py +80 -0
  16. ai_forge/models/connector.py +70 -0
  17. ai_forge/models/dashboard.py +25 -0
  18. ai_forge/models/iam.py +92 -0
  19. ai_forge/models/organization.py +56 -0
  20. ai_forge/models/service_catalog.py +15 -0
  21. ai_forge/models/service_template.py +43 -0
  22. ai_forge/models/sso.py +24 -0
  23. ai_forge/models/storage.py +29 -0
  24. ai_forge/models/tenant_user.py +33 -0
  25. ai_forge/models/workspace.py +79 -0
  26. ai_forge/pagination.py +24 -0
  27. ai_forge/py.typed +0 -0
  28. ai_forge/resources/__init__.py +39 -0
  29. ai_forge/resources/auth.py +84 -0
  30. ai_forge/resources/base.py +69 -0
  31. ai_forge/resources/certificates.py +38 -0
  32. ai_forge/resources/compute.py +23 -0
  33. ai_forge/resources/connectors.py +47 -0
  34. ai_forge/resources/dashboard.py +61 -0
  35. ai_forge/resources/databases.py +71 -0
  36. ai_forge/resources/iam.py +314 -0
  37. ai_forge/resources/namespaces.py +51 -0
  38. ai_forge/resources/notebooks.py +43 -0
  39. ai_forge/resources/operations.py +42 -0
  40. ai_forge/resources/organizations.py +83 -0
  41. ai_forge/resources/service_catalog.py +18 -0
  42. ai_forge/resources/service_templates.py +55 -0
  43. ai_forge/resources/sso.py +19 -0
  44. ai_forge/resources/storage.py +61 -0
  45. ai_forge/resources/tenant_users.py +57 -0
  46. ai_forge/resources/vms.py +78 -0
  47. ai_forge/resources/workspaces.py +135 -0
  48. ai_forge/transport.py +179 -0
  49. ai_forge_sdk-0.1.0.dist-info/METADATA +938 -0
  50. ai_forge_sdk-0.1.0.dist-info/RECORD +53 -0
  51. ai_forge_sdk-0.1.0.dist-info/WHEEL +4 -0
  52. ai_forge_sdk-0.1.0.dist-info/entry_points.txt +2 -0
  53. 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,8 @@
1
+ import logging
2
+
3
+ # Define the package-level logger
4
+ logger = logging.getLogger("ai_forge")
5
+
6
+ # Add a NullHandler so the SDK remains silent unless the user
7
+ # configures logging in their own application.
8
+ logger.addHandler(logging.NullHandler())
@@ -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
+ ]
@@ -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
@@ -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)