# coding: utf-8

"""
    Mixpeek API

    This is the Mixpeek API, providing access to various endpoints for data processing and retrieval.

    The version of the OpenAPI document: 1.3.0
    Contact: info@mixpeek.com
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from mixpeek.models.compute_tier import ComputeTier
from typing import Optional, Set
from typing_extensions import Self

class NamespaceInfrastructure(BaseModel):
    """
    Infrastructure configuration associated with a namespace.  Defines infrastructure resources for a specific namespace. This configuration can override organization-level defaults, enabling flexible deployment patterns where different namespaces use different infrastructure.  Resolution Priority:     When a namespace has infrastructure configured with DEDICATED tier, it takes     precedence over organization-level infrastructure. This allows:     - ENTERPRISE org with SHARED namespace (cost savings for dev/test)     - ENTERPRISE org with dedicated GPU namespace (ML workloads)     - Mixed infrastructure within a single organization  Tier Behaviors:     SHARED:         - Namespace uses organization infrastructure (if configured)         - Falls back to Mixpeek's shared infrastructure         - All infrastructure URLs should be None         - Lowest cost, multi-tenant      DEDICATED_CPU:         - Namespace uses its own dedicated CPU infrastructure         - Requires qdrant_url, qdrant_api_key, ray_head_node_url         - Single-tenant CPU compute         - Medium cost      DEDICATED_GPU:         - Namespace uses its own dedicated GPU infrastructure         - Requires qdrant_url, qdrant_api_key, ray_head_node_url         - Requires gpu_type and gpus_per_worker configuration         - Single-tenant GPU compute         - Highest cost  Use Cases:     - Development namespace: Set compute_tier=SHARED to use organization's infrastructure     - Production namespace: Inherit organization's DEDICATED infrastructure (don't override)     - ML namespace: Override with DEDICATED_GPU and GPU configuration     - Cost optimization: Override ENTERPRISE org to SHARED for dev/test namespaces  Examples:     Inherits organization infrastructure (no override):         NamespaceInfrastructure(             qdrant_collection=\"ns_production\",             compute_tier=ComputeTier.SHARED  # Uses org or shared infrastructure         )      Override to dedicated CPU:         NamespaceInfrastructure(             qdrant_url=\"http://qdrant-ns-prod:6333\",             qdrant_api_key=\"qdrant_key_ns_123\",             qdrant_collection=\"ns_production\",             ray_head_node_url=\"ray://ray-ns-prod:10001\",             ray_dashboard_url=\"http://ray-ns-dashboard:8265\",             compute_tier=ComputeTier.DEDICATED_CPU,             max_concurrent_jobs=50         )      Override to dedicated GPU:         NamespaceInfrastructure(             qdrant_url=\"http://qdrant-gpu:6333\",             qdrant_api_key=\"qdrant_key_gpu\",             qdrant_collection=\"ns_ml\",             ray_head_node_url=\"ray://ray-gpu:10001\",             compute_tier=ComputeTier.DEDICATED_GPU,             gpu_type=\"A100\",             gpus_per_worker=2         )
    """ # noqa: E501
    ray_cluster_id: Optional[Annotated[str, Field(strict=True)]] = Field(default=None, description="Dedicated Ray cluster identifier for this namespace.")
    ray_head_node_url: Optional[StrictStr] = Field(default=None, description="Ray head node address for job submission (ray://host:port).")
    ray_dashboard_url: Optional[StrictStr] = Field(default=None, description="Ray dashboard URL for monitoring (http://host:8265).")
    qdrant_url: Optional[StrictStr] = Field(default=None, description="Dedicated Qdrant instance URL for this namespace. When set, this namespace uses its own Qdrant instance instead of organization or shared infrastructure. Format: http://hostname:port or https://hostname:port. REQUIRED when compute_tier is DEDICATED_CPU or DEDICATED_GPU. NOT REQUIRED for SHARED tier (inherits from organization or uses shared).")
    qdrant_api_key: Optional[StrictStr] = Field(default=None, description="API key for dedicated Qdrant instance. REQUIRED when qdrant_url is set. NOT REQUIRED for shared tier.")
    qdrant_collection: Annotated[str, Field(min_length=3, strict=True, max_length=128)] = Field(description="Qdrant collection backing this namespace's vector data.")
    compute_tier: Optional[ComputeTier] = Field(default=None, description="Compute tier controlling isolation and performance characteristics.")
    max_concurrent_jobs: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = Field(default=10, description="Maximum concurrent Ray jobs allowed for the namespace.")
    autoscaling_enabled: Optional[StrictBool] = Field(default=True, description="Toggle autoscaling for dedicated clusters (ignored for shared tier).")
    min_workers: Optional[Annotated[int, Field(le=100, strict=True, ge=0)]] = Field(default=1, description="Lower bound for Ray workers when autoscaling is enabled.")
    max_workers: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = Field(default=10, description="Upper bound for Ray workers when autoscaling is enabled.")
    gpu_type: Optional[StrictStr] = Field(default=None, description="GPU type for dedicated GPU clusters (e.g. A100, T4).")
    gpus_per_worker: Optional[Annotated[int, Field(le=8, strict=True, ge=0)]] = Field(default=1, description="Number of GPUs allocated to each Ray worker when using GPUs.")
    s3_plugin_bucket: Optional[StrictStr] = Field(default='mixpeek-plugins', description="S3 bucket for storing custom plugins and model weights.")
    s3_plugin_prefix: Optional[StrictStr] = Field(default=None, description="S3 prefix for namespace-scoped plugin storage. Format: {namespace_id}/ when custom plugins are enabled.")
    max_custom_plugins: Optional[Annotated[int, Field(le=100, strict=True, ge=0)]] = Field(default=0, description="Maximum number of custom plugins allowed for this namespace. 0 = custom plugins disabled (shared tier). Set to >0 for dedicated tiers to enable custom plugins.")
    max_custom_models: Optional[Annotated[int, Field(le=50, strict=True, ge=0)]] = Field(default=0, description="Maximum number of custom model weights allowed for this namespace. 0 = custom models disabled (shared tier). Set to >0 for dedicated tiers to enable custom model uploads.")
    __properties: ClassVar[List[str]] = ["ray_cluster_id", "ray_head_node_url", "ray_dashboard_url", "qdrant_url", "qdrant_api_key", "qdrant_collection", "compute_tier", "max_concurrent_jobs", "autoscaling_enabled", "min_workers", "max_workers", "gpu_type", "gpus_per_worker", "s3_plugin_bucket", "s3_plugin_prefix", "max_custom_plugins", "max_custom_models"]

    @field_validator('ray_cluster_id')
    def ray_cluster_id_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^ray_[a-zA-Z0-9_]{3,64}$", value):
            raise ValueError(r"must validate the regular expression /^ray_[a-zA-Z0-9_]{3,64}$/")
        return value

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )


    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of NamespaceInfrastructure from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of NamespaceInfrastructure from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "ray_cluster_id": obj.get("ray_cluster_id"),
            "ray_head_node_url": obj.get("ray_head_node_url"),
            "ray_dashboard_url": obj.get("ray_dashboard_url"),
            "qdrant_url": obj.get("qdrant_url"),
            "qdrant_api_key": obj.get("qdrant_api_key"),
            "qdrant_collection": obj.get("qdrant_collection"),
            "compute_tier": obj.get("compute_tier"),
            "max_concurrent_jobs": obj.get("max_concurrent_jobs") if obj.get("max_concurrent_jobs") is not None else 10,
            "autoscaling_enabled": obj.get("autoscaling_enabled") if obj.get("autoscaling_enabled") is not None else True,
            "min_workers": obj.get("min_workers") if obj.get("min_workers") is not None else 1,
            "max_workers": obj.get("max_workers") if obj.get("max_workers") is not None else 10,
            "gpu_type": obj.get("gpu_type"),
            "gpus_per_worker": obj.get("gpus_per_worker") if obj.get("gpus_per_worker") is not None else 1,
            "s3_plugin_bucket": obj.get("s3_plugin_bucket") if obj.get("s3_plugin_bucket") is not None else 'mixpeek-plugins',
            "s3_plugin_prefix": obj.get("s3_plugin_prefix"),
            "max_custom_plugins": obj.get("max_custom_plugins") if obj.get("max_custom_plugins") is not None else 0,
            "max_custom_models": obj.get("max_custom_models") if obj.get("max_custom_models") is not None else 0
        })
        return _obj


