From af441f0fefbc7c8712b1933895ff201396617463 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Fri, 13 Dec 2024 18:10:33 -0600 Subject: [PATCH 1/4] feat: initial pytest implementation for openai (still use scope3 api) --- pyproject.toml | 2 ++ uv.lock | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4d325d1..a02ffb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,8 @@ requires-python = ">=3.9" dependencies = [ "httpx>=0.28.1", "pydantic>=2.10.3", + "pytest>=8.3.4", + "pytest-vcr>=1.0.2", "wrapt>=1.17.0", ] diff --git a/uv.lock b/uv.lock index b8af064..bdba818 100644 --- a/uv.lock +++ b/uv.lock @@ -1941,6 +1941,8 @@ source = { virtual = "." } dependencies = [ { name = "httpx" }, { name = "pydantic" }, + { name = "pytest" }, + { name = "pytest-vcr" }, { name = "wrapt" }, ] @@ -1991,6 +1993,8 @@ requires-dist = [ { name = "mistralai", marker = "extra == 'mistralai'", specifier = ">=0.4.2" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.57.1" }, { name = "pydantic", specifier = ">=2.10.3" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-vcr", specifier = ">=1.0.2" }, { name = "rapidfuzz", marker = "extra == 'litellm'", specifier = ">=3.10.1" }, { name = "tiktoken", marker = "extra == 'huggingface-hub'", specifier = ">=0.8.0" }, { name = "wrapt", specifier = ">=1.17.0" }, From bc36d55133a4e967b3fe39ab1488f4acbc58e68f Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 16 Dec 2024 09:47:46 -0600 Subject: [PATCH 2/4] ci: include pytest with python matrix --- pyproject.toml | 2 -- uv.lock | 4 ---- 2 files changed, 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a02ffb4..4d325d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,6 @@ requires-python = ">=3.9" dependencies = [ "httpx>=0.28.1", "pydantic>=2.10.3", - "pytest>=8.3.4", - "pytest-vcr>=1.0.2", "wrapt>=1.17.0", ] diff --git a/uv.lock b/uv.lock index bdba818..b8af064 100644 --- a/uv.lock +++ b/uv.lock @@ -1941,8 +1941,6 @@ source = { virtual = "." } dependencies = [ { name = "httpx" }, { name = "pydantic" }, - { name = "pytest" }, - { name = "pytest-vcr" }, { name = "wrapt" }, ] @@ -1993,8 +1991,6 @@ requires-dist = [ { name = "mistralai", marker = "extra == 'mistralai'", specifier = ">=0.4.2" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.57.1" }, { name = "pydantic", specifier = ">=2.10.3" }, - { name = "pytest", specifier = ">=8.3.4" }, - { name = "pytest-vcr", specifier = ">=1.0.2" }, { name = "rapidfuzz", marker = "extra == 'litellm'", specifier = ">=3.10.1" }, { name = "tiktoken", marker = "extra == 'huggingface-hub'", specifier = ">=0.8.0" }, { name = "wrapt", specifier = ">=1.17.0" }, From 43393da4356c3d7e86b2b804baba42962f807b6b Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 16 Dec 2024 17:00:02 -0600 Subject: [PATCH 3/4] feat: autogen types from openapi scope3ai yaml --- README.md | 12 + pyproject.toml | 3 + scope3ai/api/client.py | 30 +- scope3ai/api/types.py | 369 ++++--------------- scope3ai/api/typesgen.py | 571 ++++++++++++++++++++++++++++++ scope3ai/lib.py | 50 ++- scope3ai/tracers/openai_tracer.py | 13 +- tests/api-mocks/aiapi.yaml | 15 +- tests/test_openai_tracer.py | 1 - uv.lock | 210 ++++++++++- 10 files changed, 905 insertions(+), 369 deletions(-) create mode 100644 scope3ai/api/typesgen.py diff --git a/README.md b/README.md index 163ef9c..a247ae3 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,15 @@ $ export UV_ENV_FILE=.env $ uv sync --all-extras --all-groups $ uv run python -m examples.openai-sync-chat ``` + +## Update typesgen.py + +```bash +$ uv run datamodel-codegen \ + --input tests/api-mocks/aiapi.yaml \ + --input-file-type openapi \ + --output scope3ai/api/typesgen.py \ + --output-model-type pydantic_v2.BaseModel \ + --use-schema-description \ + --allow-extra-fields +``` diff --git a/pyproject.toml b/pyproject.toml index 4d325d1..e2d016e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,3 +43,6 @@ tests = [ "pytest>=8.3.4", "pytest-docker>=3.1.1", ] +openapi = [ + "datamodel-code-generator>=0.26.4", +] diff --git a/scope3ai/api/client.py b/scope3ai/api/client.py index f5044e2..5c37615 100644 --- a/scope3ai/api/client.py +++ b/scope3ai/api/client.py @@ -6,15 +6,15 @@ from .defaults import DEFAULT_API_URL from .types import ( - GpuResponse, + GPU, ImpactRequest, - ImpactRequestRow, + ImpactRow, ImpactResponse, - ModelFamily, + Family, ModelResponse, - NodeCloud, + CloudProvider, NodeResponse, - NodeService, + ManagedServiceProvider, ) @@ -53,7 +53,7 @@ def client(self) -> Union[httpx.Client, httpx.AsyncClient]: class ClientCommands: def model( self, - family: Optional[ModelFamily] = None, + family: Optional[Family] = None, with_response: Optional[bool] = True, ) -> ModelResponse: """ @@ -73,21 +73,21 @@ def model( def gpu( self, with_response: Optional[bool] = True, - ) -> GpuResponse: + ) -> GPU: """ List GPUs """ return self.execute_request( "/gpu", method="GET", - response_model=GpuResponse, + response_model=GPU, with_response=with_response, ) def node( self, - service: Optional[NodeService] = None, - cloud: Optional[NodeCloud] = None, + service: Optional[ManagedServiceProvider] = None, + cloud: Optional[CloudProvider] = None, with_response: Optional[bool] = True, ) -> NodeResponse: """ @@ -108,7 +108,7 @@ def node( def impact( self, - rows: List[ImpactRequestRow], + rows: List[ImpactRow], debug: Optional[bool] = None, with_response: Optional[bool] = True, ) -> ImpactResponse: @@ -201,17 +201,17 @@ async def execute_request( funcs_to_test = [ ["model", {}], - ["model", {"family": ModelFamily.CLAUDE}], + ["model", {"family": Family.claud}], ["gpu", {}], ["node", {}], - ["node", {"cloud": NodeCloud.AWS}], - # ["node", {"service": NodeService.AZURE_ML}], + ["node", {"cloud": CloudProvider.aws}], + # ["node", {"service": ManagedServiceProvider.azure_ml}], ["impact", {"rows": []}], [ "impact", { "rows": [ - ImpactRequestRow( + ImpactRow( model=Model(id="gpt_4o"), input_tokens=1000, output_tokens=200, diff --git a/scope3ai/api/types.py b/scope3ai/api/types.py index 1d18f9a..e68f0be 100644 --- a/scope3ai/api/types.py +++ b/scope3ai/api/types.py @@ -1,312 +1,81 @@ from pydantic import BaseModel, Field -from typing import Optional, List -from enum import Enum -from datetime import datetime, timezone - - -class Task(str, Enum): - TEXT_GENERATION = "text-generation" - CHAT = "chat" - TEXT_EMBEDDING = "text-embedding" - TEXT_CLASSIFICATION = "text-classification" - SENTIMENT_ANALYSIS = "sentiment-analysis" - NAMED_ENTITY_RECOGNITION = "named-entity-recognition" - QUESTION_ANSWERING = "question-answering" - SUMMARIZATION = "summarization" - TRANSLATION = "translation" - IMAGE_CLASSIFICATION = "image-classification" - OBJECT_DETECTION = "object-detection" - IMAGE_SEGMENTATION = "image-segmentation" - IMAGE_GENERATION = "image-generation" - IMAGE_TO_TEXT = "image-to-text" - TEXT_TO_IMAGE = "text-to-image" - STYLE_TRANSFER = "style-transfer" - FACE_DETECTION = "face-detection" - FACIAL_RECOGNITION = "facial-recognition" - SPEECH_TO_TEXT = "speech-to-text" - TEXT_TO_SPEECH = "text-to-speech" - SPEAKER_IDENTIFICATION = "speaker-identification" - AUDIO_CLASSIFICATION = "audio-classification" - MUSIC_GENERATION = "music-generation" - MULTIMODAL_EMBEDDING = "multimodal-embedding" - MULTIMODAL_GENERATION = "multimodal-generation" - VISUAL_QUESTION_ANSWERING = "visual-question-answering" - RECOMMENDATION_SYSTEM = "recommendation-system" - REINFORCEMENT_LEARNING = "reinforcement-learning" - ANOMALY_DETECTION = "anomaly-detection" - TIME_SERIES_FORECASTING = "time-series-forecasting" - CLUSTERING = "clustering" - - -class DataType(str, Enum): - FP8 = "fp8" - FP16 = "fp16" - FP32 = "fp32" - INT1 = "int1" - INT2 = "int2" - INT4 = "int4" - INT8 = "int8" - INT16 = "int16" - - -class CloudProvider(str, Enum): - AWS = "aws" - AZURE = "azure" - GCP = "gcp" - ORACLE = "oracle" - IBM = "ibm" - - -class ManagedServiceProvider(str, Enum): - AWS_BEDROCK = "aws-bedrock" - AZURE_ML = "azure-ml" - GOOGLE_VERTEX = "google-vertex" - IBM_WATSON = "ibm-watson" - HUGGING_FACE = "hugging-face" - - -class Gpu(BaseModel): - """Configuration for GPU hardware""" - - name: Optional[str] = Field(None, description="GPU name (e.g., 'NVIDIA A100 40GB')") - id: Optional[str] = Field(None, description="GPU identifier (e.g., 'a100_40gb')") - max_power_w: Optional[float] = Field( - None, description="Maximum power consumption in watts" - ) - embodied_emissions_kgco2e: Optional[float] = None - embodied_water_mlh2o: Optional[float] = None - performance_ratio_to_h200: Optional[float] = None - - -class Node(BaseModel): - """Configuration for compute node""" - - id: Optional[str] = None - cloud_provider: Optional[CloudProvider] = Field( - None, description="Cloud provider (e.g., 'aws')" - ) - cloud_instance_id: Optional[str] = Field( - None, description="Cloud instance type (e.g., 'a2-highgpu-1g')" - ) - managed_service: Optional[ManagedServiceProvider] = None - gpu: Optional[Gpu] = None - gpu_count: Optional[int] = Field(None, ge=0, le=10000) - cpu_count: Optional[int] = Field(None, ge=1, le=10000) - idle_power_w_ex_gpu: Optional[float] = Field(None, ge=0, le=10000) - average_utilization_rate: Optional[float] = Field(None, ge=0, le=1) - embodied_emissions_kgco2e_ex_gpu: Optional[float] = Field(None, ge=0, le=100000) - embodied_water_l_ex_gpu: Optional[float] = Field(None, ge=0, le=100000) - use_life_years: Optional[float] = Field(None, ge=1, le=30) - - -class Model(BaseModel): - """Configuration for AI model""" - - id: str = Field(..., description="Model identifier") - name: Optional[str] = None - family: Optional[str] = None - hugging_face_path: Optional[str] = None - benchmark_model_id: Optional[str] = None - total_params_billions: Optional[float] = None - number_of_experts: Optional[int] = None - params_per_expert_billions: Optional[float] = None - tensor_parallelism: Optional[int] = None - datatype: Optional[DataType] = None - task: Optional[Task] = None - training_usage_energy_kwh: Optional[float] = None - training_usage_emissions_kgco2e: Optional[float] = None - training_usage_water_l: Optional[float] = None - training_embodied_emissions_kgco2e: Optional[float] = None - training_embodied_water_l: Optional[float] = None - estimated_use_life_days: Optional[float] = None - estimated_requests_per_day: Optional[float] = None - fine_tuned_from_model_id: Optional[str] = None - - -class ImageDimensions(BaseModel): - """Image dimensions specification""" - - dimensions: str = Field(..., pattern=r"^(\d{1,4})x(\d{1,4})$") - - -class LocationConfig(BaseModel): - """Geographic location configuration""" - - cloud_region: Optional[str] = Field( - None, description="Cloud region (e.g., 'us-east-1')" - ) - country: Optional[str] = Field( - None, - pattern="^[A-Z]{2}$", - min_length=2, - max_length=2, - description="Two-letter country code (e.g., 'US')", - ) - region: Optional[str] = Field( - None, - pattern="^[A-Z]{2}$", - min_length=2, - max_length=2, - description="Two-letter region code (e.g., 'VA')", - ) - - -class ImpactRequestRow(BaseModel): - """Complete input for an impact request""" - - model: Model - location: Optional[LocationConfig] = None - node: Optional[Node] = None - utc_timestamp: Optional[datetime] = Field( - default_factory=lambda: datetime.now(timezone.utc), - description="UTC timestamp for the request", - ) - # Context of the request - session_id: Optional[str] = None - trace_id: Optional[str] = None # not implemented in the API yet - parent_trace_id: Optional[str] = None # not implemented in the API yet - request_id: Optional[str] = None - client_id: Optional[str] = None - project_id: Optional[str] = None - application_id: Optional[str] = None - - # Metrics about the model usage - task: Optional[Task] = None - input_tokens: Optional[int] = Field(None, ge=0, le=100000000) - output_tokens: Optional[int] = Field(None, ge=0, le=100000000) - input_audio_seconds: Optional[int] = Field(None, ge=0, le=100000) - input_images: Optional[List[ImageDimensions]] = Field(None, max_length=100) - input_steps: Optional[int] = Field(None, ge=1, le=10000) - output_images: Optional[List[ImageDimensions]] = Field(None, max_length=100) - output_video_frames: Optional[int] = Field(None, ge=0, le=100000000) - output_video_resolution: Optional[int] = None - - -class ImpactMetrics(BaseModel): - """Impact metrics for the model usage""" - - usage_energy_wh: Optional[float] = Field(0, ge=0, le=100000000) - usage_emissions_gco2e: Optional[float] = Field(0, ge=0, le=100000000) - usage_water_ml: Optional[float] = Field(0, ge=0, le=100000000) - embodied_emissions_gco2e: Optional[float] = Field(0, ge=0, le=100000000) - embodied_water_ml: Optional[float] = Field(0, ge=0, le=100000000) - errors: Optional[List[str]] = None - - def __add__(self, other: "ImpactMetrics") -> "ImpactMetrics": - if not isinstance(other, ImpactMetrics): - raise ValueError( - "Can only add ImpactMetrics with another ImpactMetrics instance" - ) - - errors = (self.errors or []) + (other.errors or []) - if len(errors) == 0: - errors = None - - return ImpactMetrics( - usage_energy_wh=(self.usage_energy_wh or 0) + (other.usage_energy_wh or 0), - usage_emissions_gco2e=(self.usage_emissions_gco2e or 0) - + (other.usage_emissions_gco2e or 0), - usage_water_ml=(self.usage_water_ml or 0) + (other.usage_water_ml or 0), - embodied_emissions_gco2e=(self.embodied_emissions_gco2e or 0) - + (other.embodied_emissions_gco2e or 0), - embodied_water_ml=(self.embodied_water_ml or 0) - + (other.embodied_water_ml or 0), - errors=errors, - ) - - -class ImpactResponseRowError(BaseModel): - """Error response from the API""" - - message: str - - -class ImpactResponseRow(BaseModel): - """Single row of impact data from the API response""" - - fine_tuning_impact: Optional[ImpactMetrics] = None - inference_impact: Optional[ImpactMetrics] = None - training_impact: Optional[ImpactMetrics] = None - total_impact: ImpactMetrics - error: Optional[ImpactResponseRowError] = None - - -class ImpactResponse(BaseModel): - """Complete response from the impact API""" - - has_errors: bool - rows: List[ImpactResponseRow] - - -class ImpactRequest(BaseModel): - """Final request structure for the API""" - - rows: List[ImpactRequestRow] = Field(..., max_length=1000) +from typing import Optional + +from .typesgen import ( + StatusResponse, + ImpactBigQueryRequest, + ImpactBigQueryResponse, + ImpactBigQueryError, + ImpactMetrics, + PredictionStep, + Details, + Error, + GPU, + ManagedServiceProvider, + Image, + CloudProvider, + Task, + Family, + DataType, + CountryCode, + RegionCode, + GPUResponse, + ImpactLogRow, + Model, + GridMix, + Node, + ModelResponse, + NodeResponse, + ImpactLogRequest, + ImpactRow, + DebugInfo, + ImpactRequest, + ModeledRow, + ImpactResponse, +) class Scope3AIContext(BaseModel): - request: Optional[ImpactRequestRow] = Field( + request: Optional[ImpactRow] = Field( None, description="The impact request information. Contains `trace_id` and `record_id`", ) - impact: Optional[ImpactResponseRow] = Field( + impact: Optional[ModeledRow] = Field( None, description="The impact response if `include_impact_response` is set to True", ) -class ModelFamily(str, Enum): - CLAUDE = "claude" - GPT = "gpt" - DALL_E = "dall-e" - WHISPER = "whisper" - GEMINI = "gemini" - PALM = "palm" - BERT = "bert" - T5 = "t5" - LLAMA = "llama" - OPT = "opt" - GALACTICA = "galactica" - PHI = "phi" - STABLE_DIFFUSION = "stable-diffusion" - STABLE_LM = "stable-lm" - MISTRAL = "mistral" - MIXTRAL = "mixtral" - COMMAND = "command" - EMBED = "embed" - FALCON = "falcon" - MPT = "mpt" - PYTHIA = "pythia" - DOLLY = "dolly" - BLOOM = "bloom" - ROBERTA = "roberta" - GPT_NEO = "gpt-neo" - GPT_J = "gpt-j" - - -class ModelResponse(BaseModel): - models: List[Model] - - -class NodeService(str, Enum): - AWS_BEDROCK = "aws-bedrock" - AZURE_ML = "azure-ml" - GOOGLE_VERTEX = "google-vertex" - IBM_WATSON = "ibm-watson" - HUGGING_FACE = "hugging-face" - - -class NodeCloud(str, Enum): - AWS = "aws" - AZURE = "azure" - GCP = "gcp" - ORACLE = "oracle" - IBM = "ibm" - - -class NodeResponse(BaseModel): - nodes: List[Node] - - -class GpuResponse(BaseModel): - gpus: List[Gpu] +__all__ = [ + "Scope3AIContext", + "StatusResponse", + "ImpactBigQueryRequest", + "ImpactBigQueryResponse", + "ImpactBigQueryError", + "ImpactMetrics", + "PredictionStep", + "Details", + "Error", + "GPU", + "ManagedServiceProvider", + "Image", + "CloudProvider", + "Task", + "Family", + "DataType", + "CountryCode", + "RegionCode", + "GPUResponse", + "ImpactLogRow", + "Model", + "GridMix", + "Node", + "ModelResponse", + "NodeResponse", + "ImpactLogRequest", + "ImpactRow", + "DebugInfo", + "ImpactRequest", + "ModeledRow", + "ImpactResponse", +] diff --git a/scope3ai/api/typesgen.py b/scope3ai/api/typesgen.py new file mode 100644 index 0000000..b7c890b --- /dev/null +++ b/scope3ai/api/typesgen.py @@ -0,0 +1,571 @@ +# generated by datamodel-codegen: +# filename: aiapi.yaml +# timestamp: 2024-12-17T23:27:51+00:00 + +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, ConfigDict, Field, RootModel, confloat, conint, constr + + +class StatusResponse(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + ready: bool + reason: Optional[str] = None + + +class ImpactBigQueryRequest(BaseModel): + requestId: str = Field( + ..., description='Unique identifier for the request', examples=['124ab1c'] + ) + caller: str = Field( + ..., + description='Full resource name of the BigQuery job', + examples=[ + '//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf' + ], + ) + sessionUser: str = Field( + ..., + description='Email of the user executing the BigQuery query', + examples=['user@company.com'], + ) + userDefinedContext: Optional[Dict[str, Any]] = Field( + None, description='User-defined context from BigQuery' + ) + calls: List[List[Union[str, int]]] + + +class ImpactBigQueryResponse(BaseModel): + replies: List[str] = Field( + ..., description='Array of impact metric results', max_length=1000, min_length=0 + ) + errorMessage: Optional[str] = None + + +class ImpactBigQueryError(BaseModel): + errorMessage: str = Field( + ..., + description='Error message for BigQuery', + examples=["Invalid request format: missing required field 'calls'"], + ) + + +class ImpactMetrics(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + usage_energy_wh: float = Field(..., examples=[0.13]) + usage_emissions_gco2e: float = Field(..., examples=[0.81]) + usage_water_ml: float = Field(..., examples=[1.32]) + embodied_emissions_gco2e: float = Field(..., examples=[0.81]) + embodied_water_ml: float = Field(..., examples=[1.32]) + + +class PredictionStep(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + description: str + duration_ms: float + inferences: int + + +class Details(BaseModel): + reason: Optional[str] = None + field: Optional[str] = None + + +class Error(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + code: Optional[str] = None + message: str + details: Optional[Details] = None + + +class GPU(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + name: Optional[str] = Field(None, examples=['NVIDIA A100 40GB']) + id: Optional[str] = Field(None, examples=['a100_40gb']) + max_power_w: Optional[float] = Field(None, examples=[700]) + embodied_emissions_kgco2e: Optional[float] = Field(None, examples=[282.1]) + embodied_water_mlh2o: Optional[float] = Field(None, examples=[181.1]) + performance_ratio_to_h200: Optional[float] = Field(None, examples=[1.5]) + + +class ManagedServiceProvider(Enum): + aws_bedrock = 'aws-bedrock' + azure_ml = 'azure-ml' + google_vertex = 'google-vertex' + ibm_watson = 'ibm-watson' + hugging_face = 'hugging-face' + + +class Image(RootModel[constr(pattern=r'^\d{1,4}x\d{1,4}$')]): + root: constr(pattern=r'^\d{1,4}x\d{1,4}$') = Field(..., examples=['1024x1024']) + + +class CloudProvider(Enum): + aws = 'aws' + azure = 'azure' + gcp = 'gcp' + oracle = 'oracle' + ibm = 'ibm' + + +class Task(Enum): + """ + Common types of AI/ML models and their primary functions: + - Text-based models for natural language processing + - Vision models for image analysis and generation + - Audio models for speech and sound processing + - Multimodal models that combine different types of inputs/outputs + - Specialized models for specific use cases + + """ + + text_generation = 'text-generation' + chat = 'chat' + text_embedding = 'text-embedding' + text_classification = 'text-classification' + sentiment_analysis = 'sentiment-analysis' + named_entity_recognition = 'named-entity-recognition' + question_answering = 'question-answering' + summarization = 'summarization' + translation = 'translation' + image_classification = 'image-classification' + object_detection = 'object-detection' + image_segmentation = 'image-segmentation' + image_generation = 'image-generation' + image_to_text = 'image-to-text' + text_to_image = 'text-to-image' + style_transfer = 'style-transfer' + face_detection = 'face-detection' + facial_recognition = 'facial-recognition' + speech_to_text = 'speech-to-text' + text_to_speech = 'text-to-speech' + speaker_identification = 'speaker-identification' + audio_classification = 'audio-classification' + music_generation = 'music-generation' + multimodal_embedding = 'multimodal-embedding' + multimodal_generation = 'multimodal-generation' + visual_question_answering = 'visual-question-answering' + recommendation_system = 'recommendation-system' + reinforcement_learning = 'reinforcement-learning' + anomaly_detection = 'anomaly-detection' + time_series_forecasting = 'time-series-forecasting' + clustering = 'clustering' + + +class Family(Enum): + """ + Core AI model families from various organizations: + - Commercial models from major AI companies + - Open source model families + - Research/academic model families + - Models may appear in multiple categories if they have both commercial and open source variants + + """ + + claude = 'claude' + gpt = 'gpt' + dall_e = 'dall-e' + whisper = 'whisper' + gemini = 'gemini' + palm = 'palm' + bert = 'bert' + t5 = 't5' + llama = 'llama' + opt = 'opt' + galactica = 'galactica' + phi = 'phi' + stable_diffusion = 'stable-diffusion' + stable_lm = 'stable-lm' + mistral = 'mistral' + mixtral = 'mixtral' + command = 'command' + embed = 'embed' + falcon = 'falcon' + mpt = 'mpt' + pythia = 'pythia' + dolly = 'dolly' + bloom = 'bloom' + roberta = 'roberta' + gpt_neo = 'gpt-neo' + gpt_j = 'gpt-j' + + +class DataType(Enum): + fp8 = 'fp8' + fp8_e4m3 = 'fp8-e4m3' + fp8_e5m2 = 'fp8-e5m2' + fp16 = 'fp16' + tf32 = 'tf32' + fp32 = 'fp32' + fp64 = 'fp64' + bfloat8 = 'bfloat8' + bfloat16 = 'bfloat16' + bf16 = 'bf16' + int4 = 'int4' + int8 = 'int8' + int16 = 'int16' + int32 = 'int32' + int64 = 'int64' + uint4 = 'uint4' + uint8 = 'uint8' + uint16 = 'uint16' + uint32 = 'uint32' + uint64 = 'uint64' + + +class CountryCode(RootModel[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)]): + root: constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) = Field( + ..., + description='Two-letter country code as defined by ISO 3166-1 alpha-2', + examples=['US'], + ) + + +class RegionCode(RootModel[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)]): + root: constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) = Field( + ..., + description='Two-letter region code as defined by ISO 3166-1 alpha-2', + examples=['NY'], + ) + + +class GPUResponse(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + gpus: List[GPU] = Field(..., max_length=100) + + +class ImpactLogRow(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + start_time_utc: Optional[datetime] = Field( + None, + description='The start time of the inference', + examples=['2024-10-01T00:00:00Z'], + ) + request_duration_ms: Optional[float] = Field( + None, + description='The time the request took (as measured by client or proxy)', + examples=[283], + ) + processing_duration_ms: Optional[float] = Field( + None, + description='The time taken in processing the request (as measured at execution)', + examples=[238], + ) + integration_source: Optional[str] = Field( + None, + description='The integration used to source the data', + examples=['litellm'], + ) + client_id: Optional[str] = Field( + None, description='The client to attribute this call to' + ) + project_id: Optional[str] = Field( + None, description='The project to attribute this call to' + ) + application_id: Optional[str] = Field( + None, description='The application to attribute this call to' + ) + session_id: Optional[str] = Field( + None, description='The ID of the session (multiple requests)' + ) + request_id: Optional[str] = Field( + None, description='The unique identifier of this request' + ) + environment: Optional[str] = Field( + None, + description='Environment (prod/production indicates production)', + examples=['staging'], + ) + model_id: Optional[str] = Field( + None, description='The ID of the model requested', examples=['llama_31_8b'] + ) + model_id_used: Optional[str] = Field( + None, + description='The ID of the model that did the inference', + examples=['llama_31_8b_0125'], + ) + model_name: Optional[str] = Field( + None, description='The name of the model', examples=['LLaMa v3.1 8B'] + ) + model_family: Optional[str] = Field( + None, description='The family of the model', examples=['llama'] + ) + model_hugging_face_path: Optional[str] = Field( + None, + description='The Hugging Face path of the model', + examples=['meta/llama31_8b'], + ) + cloud_id: Optional[str] = Field( + None, description='The ID of the cloud', examples=['aws'] + ) + cloud_region: Optional[str] = Field( + None, description='The region of cloud hosting', examples=['us-central1'] + ) + cloud_instance_id: Optional[str] = Field( + None, description='The instance type in the cloud', examples=['xl-4g-8a100'] + ) + managed_service_id: Optional[str] = Field( + None, + description='The ID of a managed service provider', + examples=['aws-bedrock'], + ) + node_id: Optional[str] = Field( + None, description='The ID of a proprietary node', examples=['h200-2024-build'] + ) + node_country: Optional[ + constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) + ] = Field( + None, description='The country where the servers are hosted', examples=['US'] + ) + node_region: Optional[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)] = ( + Field( + None, description='The region where the servers are hosted', examples=['CA'] + ) + ) + task: Optional[Task] = Field( + None, description='The task the AI is performing', examples=['text-generation'] + ) + input_tokens: Optional[conint(ge=0, le=100000000)] = Field( + None, description='the number of input tokens', examples=[1033] + ) + output_tokens: Optional[conint(ge=0, le=100000000)] = Field( + None, description='the number of output tokens', examples=[2300] + ) + input_audio_seconds: Optional[conint(ge=0, le=100000)] = Field( + None, description='the duration of audio input in seconds', examples=[60] + ) + input_images: Optional[str] = Field( + None, + description='a comma delimited list of image sizes', + examples=['512x512,1024x1024'], + ) + input_steps: Optional[conint(ge=1, le=10000)] = Field( + None, description='the number of steps in the model', examples=[50] + ) + output_images: Optional[str] = Field( + None, + description='a comma delimited list of output sizes', + examples=['512x512,1024x1024'], + ) + output_video_frames: Optional[conint(ge=0, le=100000000)] = Field( + None, + description='the number of video frames (frame rate x duration)', + examples=[60], + ) + output_video_resolution: Optional[int] = Field( + None, + description='the resolution of the video in number of lines (for instance, 1080 for 1080p)', + examples=[1080], + ) + request_cost: Optional[float] = Field(None, description='the cost of this request') + currency: Optional[str] = Field(None, description='the currency for cost data') + + +class Model(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + id: Optional[str] = Field(None, examples=['gpt-4-turbo']) + name: Optional[str] = Field(None, examples=['GPT-4 Turbo']) + family: Optional[str] = Field(None, examples=['gpt']) + hugging_face_path: Optional[str] = Field(None, examples=['EleutherAI/gpt-neo-2.7B']) + benchmark_model_id: Optional[str] = Field(None, examples=['GPTJ-6B']) + total_params_billions: Optional[float] = Field(None, examples=[175]) + number_of_experts: Optional[int] = Field(None, examples=[7]) + params_per_expert_billions: Optional[float] = Field(None, examples=[8]) + tensor_parallelism: Optional[int] = Field(None, examples=[1]) + datatype: Optional[DataType] = Field(None, examples=['fp8']) + task: Optional[Task] = Field(None, examples=['text-generation']) + training_usage_energy_kwh: Optional[float] = Field(None, examples=[1013.1]) + training_usage_emissions_kgco2e: Optional[float] = Field(None, examples=[1013.1]) + training_usage_water_l: Optional[float] = Field(None, examples=[1013.1]) + training_embodied_emissions_kgco2e: Optional[float] = Field( + None, examples=[11013.1] + ) + training_embodied_water_l: Optional[float] = Field(None, examples=[11013.1]) + estimated_use_life_days: Optional[float] = Field(None, examples=[1013.1]) + estimated_requests_per_day: Optional[float] = Field(None, examples=[1013.1]) + fine_tuned_from_model_id: Optional[str] = Field(None, examples=['llama_31_8b']) + + +class GridMix(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + country: CountryCode + region: Optional[RegionCode] = None + gco2e_per_kwh: confloat(ge=0.0, le=2000.0) = Field(..., examples=[475]) + + +class Node(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + id: Optional[str] = Field(None, examples=['base-node-xl']) + cloud_id: Optional[str] = Field(None, examples=['aws']) + cloud_instance_id: Optional[str] = Field(None, examples=['a2-highgpu-1g']) + managed_service_id: Optional[str] = Field(None, examples=['aws-bedrock']) + gpu_id: Optional[str] = Field(None, examples=['a100_40gb']) + gpu: Optional[GPU] = None + gpu_count: Optional[conint(ge=0, le=10000)] = Field(None, examples=[8]) + cpu_count: Optional[conint(ge=1, le=10000)] = Field(None, examples=[2]) + idle_power_w_ex_gpu: Optional[confloat(ge=0.0, le=10000.0)] = Field( + None, examples=[100] + ) + average_utilization_rate: Optional[confloat(ge=0.0, le=1.0)] = Field( + None, examples=[0.8] + ) + embodied_emissions_kgco2e_ex_gpu: Optional[confloat(ge=0.0, le=100000.0)] = Field( + None, examples=[2500] + ) + embodied_water_l_ex_gpu: Optional[confloat(ge=0.0, le=100000.0)] = Field( + None, examples=[2500] + ) + use_life_years: Optional[confloat(ge=1.0, le=30.0)] = Field(None, examples=[5]) + + +class ModelResponse(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + models: List[Model] = Field(..., max_length=100) + + +class NodeResponse(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + nodes: List[Node] = Field(..., max_length=100) + + +class ImpactLogRequest(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + rows: List[ImpactLogRow] = Field(..., max_length=1000) + + +class ImpactRow(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + utc_datetime: Optional[datetime] = Field( + None, + description='The start time of the request in UTC', + examples=['2022-01-01T00:00:00Z'], + ) + model: Model + cloud_region: Optional[str] = Field( + None, description='The region of cloud hosting', examples=['us-central1'] + ) + node: Optional[Node] = None + country: Optional[CountryCode] = None + region: Optional[RegionCode] = None + task: Optional[Task] = Field(None, examples=['text-generation']) + input_tokens: Optional[conint(ge=0, le=100000000)] = Field( + None, description='the number of input (or prompt) tokens', examples=[128] + ) + input_audio_seconds: Optional[conint(ge=0, le=100000)] = Field( + None, description='the duration of audio input in seconds', examples=[60] + ) + output_tokens: Optional[conint(ge=0, le=100000000)] = Field( + None, description='the number of output (or completion) tokens', examples=[128] + ) + input_images: Optional[List[Image]] = Field(None, max_length=100) + input_steps: Optional[conint(ge=1, le=10000)] = Field( + None, description='the number of steps to use in the model', examples=[50] + ) + output_images: Optional[List[Image]] = Field( + None, description='a list of output image sizes', max_length=100 + ) + output_video_frames: Optional[conint(ge=0, le=100000000)] = Field( + None, + description='the number of video frames (frame rate x duration)', + examples=[60], + ) + output_video_resolution: Optional[int] = Field( + None, + description='the resolution of the video in number of lines (for instance, 1080 for 1080p)', + examples=[1080], + ) + request_id: Optional[str] = Field( + None, description='The unique identifier of this request', examples=['124ab1c'] + ) + session_id: Optional[str] = Field( + None, description='The ID of the session', examples=['124ab1c'] + ) + trace_id: Optional[str] = Field( + None, description='The ID of the trace', examples=['124ab1c'] + ) + parent_trace_id: Optional[str] = Field( + None, description='The ID of the parent trace', examples=['124ab1c'] + ) + request_duration_ms: Optional[float] = Field( + None, + description='The time the request took (as measured by client or proxy)', + examples=[283], + ) + managed_service_id: Optional[str] = Field( + None, + description='The ID of a managed service provider', + examples=['aws-bedrock'], + ) + model_used: Optional[Model] = None + + +class DebugInfo(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + model: Optional[Model] = None + hardware_node: Optional[Node] = None + grid_mix: Optional[GridMix] = None + steps: Optional[List[PredictionStep]] = Field(None, max_length=100) + + +class ImpactRequest(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + rows: List[ImpactRow] = Field(..., max_length=1000) + + +class ModeledRow(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + inference_impact: Optional[ImpactMetrics] = None + training_impact: Optional[ImpactMetrics] = None + fine_tuning_impact: Optional[ImpactMetrics] = None + total_impact: ImpactMetrics + debug: Optional[DebugInfo] = None + error: Optional[Error] = None + + +class ImpactResponse(BaseModel): + model_config = ConfigDict( + extra='forbid', + ) + rows: List[ModeledRow] = Field(..., max_length=1000) + total_energy_wh: Optional[float] = Field(None, examples=[0.13]) + total_gco2e: Optional[float] = Field(None, examples=[0.81]) + total_mlh2o: Optional[float] = Field(None, examples=[1.32]) + has_errors: bool = Field(..., examples=[False]) diff --git a/scope3ai/lib.py b/scope3ai/lib.py index 2435f66..9a1d85a 100644 --- a/scope3ai/lib.py +++ b/scope3ai/lib.py @@ -10,7 +10,7 @@ import atexit from .api.client import Client, AsyncClient -from .api.types import ImpactRequestRow, ImpactResponse, Scope3AIContext +from .api.types import ImpactRow, ImpactResponse, Scope3AIContext from .api.defaults import DEFAULT_API_URL from .worker import BackgroundWorker @@ -163,16 +163,16 @@ async def aimpact( def submit_impact( self, - impact_request_row: ImpactRequestRow, + impact_row: ImpactRow, ) -> Scope3AIContext: """ Submit an impact request to the Scope3 AI API. - This function sends an impact request represented by the `impact_request_row` + This function sends an impact request represented by the `impact_row` to the Scope3 AI API and optionally returns the response. Args: - impact_request_row (ImpactRequestRow): The impact request data + impact_row (ImpactRow): The impact request data that needs to be submitted to the Scope3 AI API. Returns: @@ -182,57 +182,53 @@ def submit_impact( """ def submit_impact( - impact_request_row: ImpactRequestRow, + impact_row: ImpactRow, with_response=True, ) -> Optional[ImpactResponse]: return self._sync_client.impact( - rows=[impact_request_row], + rows=[impact_row], with_response=with_response, ) - self._mark_impact_row(impact_request_row) - ctx = Scope3AIContext(request=impact_request_row) + self._mark_impact_row(impact_row) + ctx = Scope3AIContext(request=impact_row) if self.include_impact_response: - response = submit_impact(impact_request_row, with_response=True) + response = submit_impact(impact_row, with_response=True) ctx.impact = response.rows[0] return ctx self._ensure_worker() - self._worker.submit( - partial(submit_impact, impact_request_row=impact_request_row) - ) + self._worker.submit(partial(submit_impact, impact_row=impact_row)) return ctx async def asubmit_impact( self, - impact_request_row: ImpactRequestRow, + impact_row: ImpactRow, ) -> Scope3AIContext: """ Async version of Scope3AI::submit_impact. """ async def submit_impact( - impact_request_row: ImpactRequestRow, + impact_row: ImpactRow, with_response=True, ) -> Optional[ImpactResponse]: return await self._async_client.impact( - rows=[impact_request_row], + rows=[impact_row], with_response=with_response, ) - self._mark_impact_row(impact_request_row) - ctx = Scope3AIContext(request=impact_request_row) + self._mark_impact_row(impact_row) + ctx = Scope3AIContext(request=impact_row) if self.include_impact_response: - impact = await submit_impact(impact_request_row, with_response=True) + impact = await submit_impact(impact_row, with_response=True) ctx.impact = impact return ctx self._ensure_worker() - self._worker.submit( - partial(submit_impact, impact_request_row=impact_request_row) - ) + self._worker.submit(partial(submit_impact, impact_row=impact_row)) return ctx @property @@ -327,13 +323,13 @@ def _shutdown(): scope3ai._worker._queue.join() logging.debug("Shutting down Scope3AI") - def _mark_impact_row(self, impact_request_row: ImpactRequestRow) -> None: - # augment the impact_request_row with the current information from the tracer - impact_request_row.request_id = generate_id() + def _mark_impact_row(self, impact_row: ImpactRow) -> None: + # augment the impact_row with the current information from the tracer + impact_row.request_id = generate_id() current_tracer = self.current_tracer if current_tracer: - impact_request_row.trace_id = current_tracer.trace_id - impact_request_row.parent_trace_id = current_tracer.parent_trace_id + impact_row.trace_id = current_tracer.trace_id + impact_row.parent_trace_id = current_tracer.parent_trace_id root_tracer = self.root_tracer if root_tracer: - impact_request_row.session_id = root_tracer.session_id + impact_row.session_id = root_tracer.session_id diff --git a/scope3ai/tracers/openai_tracer.py b/scope3ai/tracers/openai_tracer.py index e98bc1f..26a6057 100644 --- a/scope3ai/tracers/openai_tracer.py +++ b/scope3ai/tracers/openai_tracer.py @@ -4,7 +4,7 @@ from wrapt import wrap_function_wrapper from scope3ai.lib import Scope3AI -from scope3ai.api.types import Scope3AIContext, Model, ImpactRequestRow +from scope3ai.api.types import Scope3AIContext, Model, ImpactRow try: from openai import AsyncStream, Stream @@ -53,13 +53,12 @@ def openai_chat_wrapper_non_stream( model_requested = kwargs["model"] model_used = response.model - scope3_row = ImpactRequestRow( + scope3_row = ImpactRow( model=Model(id=model_requested), model_used=Model(id=model_used), input_tokens=response.usage.prompt_tokens, output_tokens=response.usage.completion_tokens, - request_duration_ms=request_latency - * 1000, # TODO: can we get the header that has the processing time + request_duration_ms=request_latency * 1000, managed_service_id=PROVIDER, ) @@ -88,7 +87,7 @@ def openai_chat_wrapper_stream( model_requested = kwargs["model"] model_used = chunk.model - scope3_row = ImpactRequestRow( + scope3_row = ImpactRow( model=Model(id=model_requested), model_used=Model(id=model_used), input_tokens=chunk.usage.prompt_tokens, @@ -128,7 +127,7 @@ async def openai_async_chat_wrapper_base( model_requested = kwargs["model"] model_used = response.model - scope3_row = ImpactRequestRow( + scope3_row = ImpactRow( model=Model(id=model_requested), model_used=Model(id=model_used), input_tokens=response.usage.prompt_tokens, @@ -169,7 +168,7 @@ async def openai_async_chat_wrapper_stream( model_used = chunk.model if chunk.usage is not None: - scope3_row = ImpactRequestRow( + scope3_row = ImpactRow( model=Model(id=model_requested), model_used=Model(id=model_used), input_tokens=chunk.usage.prompt_tokens, diff --git a/tests/api-mocks/aiapi.yaml b/tests/api-mocks/aiapi.yaml index 45f0994..db7d90e 100644 --- a/tests/api-mocks/aiapi.yaml +++ b/tests/api-mocks/aiapi.yaml @@ -800,6 +800,19 @@ components: description: The ID of the parent trace example: "124ab1c" nullable: true + request_duration_ms: + type: number + format: float + description: The time the request took (as measured by client or proxy) + example: 283 + nullable: true + managed_service_id: + type: string + description: The ID of a managed service provider + example: "aws-bedrock" + nullable: true + model_used: + $ref: "#/components/schemas/Model" Model: title: Model @@ -1121,7 +1134,7 @@ components: Image: type: string example: "1024x1024" - format: /^(\d{1,4})x(\d{1,4})$/ + pattern: '^\d{1,4}x\d{1,4}$' CloudProvider: type: string diff --git a/tests/test_openai_tracer.py b/tests/test_openai_tracer.py index b8afc46..8e1b205 100644 --- a/tests/test_openai_tracer.py +++ b/tests/test_openai_tracer.py @@ -32,4 +32,3 @@ def test_openai_chat_with_response(tracer_with_response_init): assert response.scope3ai.impact.total_impact.usage_energy_wh > 0 assert response.scope3ai.impact.total_impact.usage_emissions_gco2e > 0 assert response.scope3ai.impact.total_impact.embodied_emissions_gco2e > 0 - assert response.scope3ai.impact.total_impact.errors is None diff --git a/uv.lock b/uv.lock index b8af064..8b81104 100644 --- a/uv.lock +++ b/uv.lock @@ -4,11 +4,15 @@ resolution-markers = [ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'", "python_full_version < '3.10' and platform_python_implementation != 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation == 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation == 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation != 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation != 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation != 'PyPy'", ] [[package]] @@ -167,6 +171,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/7a/4daaf3b6c08ad7ceffea4634ec206faeff697526421c20f07628c7372156/anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352", size = 93052 }, ] +[[package]] +name = "argcomplete" +version = "3.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/03/581b1c29d88fffaa08abbced2e628c34dd92d32f1adaed7e42fc416938b0/argcomplete-3.5.2.tar.gz", hash = "sha256:23146ed7ac4403b70bd6026402468942ceba34a6732255b9edf5b7354f68a6bb", size = 82341 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/37/3fa718aaadd36e073891138dc3ebd919a71bafd4881c97d8a133265af191/argcomplete-3.5.2-py3-none-any.whl", hash = "sha256:036d020d79048a5d525bc63880d7a4b8d1668566b8a76daf1144c0bbe0f63472", size = 43506 }, +] + [[package]] name = "async-timeout" version = "5.0.1" @@ -185,6 +198,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, ] +[[package]] +name = "black" +version = "24.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/f3/465c0eb5cddf7dbbfe1fecd9b875d1dcf51b88923cd2c1d7e9ab95c6336b/black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", size = 1623211 }, + { url = "https://files.pythonhosted.org/packages/df/57/b6d2da7d200773fdfcc224ffb87052cf283cec4d7102fab450b4a05996d8/black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", size = 1457139 }, + { url = "https://files.pythonhosted.org/packages/6e/c5/9023b7673904a5188f9be81f5e129fff69f51f5515655fbd1d5a4e80a47b/black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", size = 1753774 }, + { url = "https://files.pythonhosted.org/packages/e1/32/df7f18bd0e724e0d9748829765455d6643ec847b3f87e77456fc99d0edab/black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e", size = 1414209 }, + { url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 }, + { url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 }, + { url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 }, + { url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 }, + { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, + { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, + { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, + { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 }, + { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 }, + { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 }, + { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 }, + { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 }, + { url = "https://files.pythonhosted.org/packages/fe/02/f408c804e0ee78c367dcea0a01aedde4f1712af93b8b6e60df981e0228c7/black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd", size = 1622516 }, + { url = "https://files.pythonhosted.org/packages/f8/b9/9b706ed2f55bfb28b436225a9c57da35990c9005b90b8c91f03924454ad7/black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f", size = 1456181 }, + { url = "https://files.pythonhosted.org/packages/0a/1c/314d7f17434a5375682ad097f6f4cc0e3f414f3c95a9b1bb4df14a0f11f9/black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800", size = 1752801 }, + { url = "https://files.pythonhosted.org/packages/39/a7/20e5cd9237d28ad0b31438de5d9f01c8b99814576f4c0cda1edd62caf4b0/black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7", size = 1413626 }, + { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, +] + [[package]] name = "cachetools" version = "5.5.0" @@ -331,6 +382,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "datamodel-code-generator" +version = "0.26.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "black" }, + { name = "genson" }, + { name = "inflect" }, + { name = "isort" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pydantic", extra = ["email"], marker = "python_full_version < '4.0'" }, + { name = "pyyaml" }, + { name = "toml", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d3/4989ef484c41c35f45a16b54ffc25ffb4972d2fefde576a604f1c8c0675d/datamodel_code_generator-0.26.4.tar.gz", hash = "sha256:9881124fec15655a3a635808ea5ded63afb0540c0c402998070ccf60a9dab225", size = 92241 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/e7/66a7c16bfbf5596e9e556fa9841d3cb3fb61ff79026b084328a9c7e04a00/datamodel_code_generator-0.26.4-py3-none-any.whl", hash = "sha256:95bdaa91fe87a8c369b1c9147bb2ef2eead918964270451e6223235131974098", size = 114595 }, +] + [[package]] name = "distro" version = "1.9.0" @@ -340,6 +412,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] +[[package]] +name = "dnspython" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, +] + +[[package]] +name = "email-validator" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython", marker = "python_full_version < '4.0'" }, + { name = "idna", marker = "python_full_version < '4.0'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -483,6 +577,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/b2/454d6e7f0158951d8a78c2e1eb4f69ae81beb8dca5fee9809c6c99e9d0d0/fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871", size = 179641 }, ] +[[package]] +name = "genson" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/2303c8ad276dcf5ee2ad6cf69c4338fd86ef0f471a5207b069adf7a393cf/genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37", size = 34919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470 }, +] + [[package]] name = "google-ai-generativelanguage" version = "0.6.10" @@ -757,6 +860,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, ] +[[package]] +name = "inflect" +version = "5.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/db/cae5d8524c4b5e574c281895b212062f3b06d0e14186904ed71c538b4e90/inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9", size = 69378 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/d8/3e1a32d305215166f5c32652c473aa766bd7809cd10b34c544dbc31facb5/inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238", size = 33704 }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -766,6 +878,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + [[package]] name = "jinja2" version = "3.1.4" @@ -866,14 +987,14 @@ wheels = [ [[package]] name = "jsonschema-specifications" -version = "2024.10.1" +version = "2023.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +sdist = { url = "https://files.pythonhosted.org/packages/12/ce/eb5396b34c28cbac19a6a8632f0e03d309135d77285536258b82120198d8/jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb", size = 12689 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, + { url = "https://files.pythonhosted.org/packages/1c/24/83349ac2189cc2435e84da3f69ba3c97314d3c0622628e55171c6798ed80/jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", size = 17835 }, ] [[package]] @@ -1082,6 +1203,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, ] +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + [[package]] name = "numpy" version = "1.26.4" @@ -1231,6 +1361,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475 }, ] +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -1392,6 +1540,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 }, ] +[package.optional-dependencies] +email = [ + { name = "email-validator", marker = "python_full_version < '4.0'" }, +] + [[package]] name = "pydantic-core" version = "2.27.1" @@ -1700,15 +1853,15 @@ wheels = [ [[package]] name = "referencing" -version = "0.35.1" +version = "0.30.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991 } +sdist = { url = "https://files.pythonhosted.org/packages/e1/43/d3f6cf3e1ec9003520c5fb31dc363ee488c517f09402abd2a1c90df63bbb/referencing-0.30.2.tar.gz", hash = "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0", size = 53386 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684 }, + { url = "https://files.pythonhosted.org/packages/be/8e/56d6f1e2d591f4d6cbcba446cac4a1b0dc4f584537e2071d9bcee8eeab6b/referencing-0.30.2-py3-none-any.whl", hash = "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", size = 25617 }, ] [[package]] @@ -1972,6 +2125,9 @@ openai = [ ] [package.dev-dependencies] +openapi = [ + { name = "datamodel-code-generator" }, +] tests = [ { name = "pytest" }, { name = "pytest-docker" }, @@ -1997,6 +2153,7 @@ requires-dist = [ ] [package.metadata.requires-dev] +openapi = [{ name = "datamodel-code-generator", specifier = ">=0.26.4" }] tests = [ { name = "pytest", specifier = ">=8.3.4" }, { name = "pytest-docker", specifier = ">=3.1.1" }, @@ -2079,6 +2236,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192 }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + [[package]] name = "tomli" version = "2.2.1" @@ -2138,8 +2304,10 @@ resolution-markers = [ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'", "python_full_version < '3.10' and platform_python_implementation != 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation == 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation == 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'", ] dependencies = [ @@ -2155,8 +2323,10 @@ name = "types-requests" version = "2.32.0.20241016" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation != 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation != 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation != 'PyPy'", ] dependencies = [ { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_python_implementation != 'PyPy'" }, @@ -2201,8 +2371,10 @@ resolution-markers = [ "python_full_version < '3.10' and platform_python_implementation == 'PyPy'", "python_full_version < '3.10' and platform_python_implementation != 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation == 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation == 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation == 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation == 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation == 'PyPy'", "python_full_version == '3.10.*' and platform_python_implementation != 'PyPy'", ] sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380 } @@ -2215,8 +2387,10 @@ name = "urllib3" version = "2.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11' and python_full_version < '3.13' and platform_python_implementation != 'PyPy'", - "python_full_version >= '3.13' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.11.*' and platform_python_implementation != 'PyPy'", + "python_full_version == '3.12.*' and platform_python_implementation != 'PyPy'", + "python_full_version >= '3.13' and python_full_version < '4.0' and platform_python_implementation != 'PyPy'", + "python_full_version >= '4.0' and platform_python_implementation != 'PyPy'", ] sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } wheels = [ From 150de6e91e159acdfeffc0626a6ab9065fbff351 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Tue, 17 Dec 2024 17:43:18 -0600 Subject: [PATCH 4/4] fix: fix typegen for formatting --- README.md | 3 +- scope3ai/api/typesgen.py | 404 +++++++++++++++++++-------------------- 2 files changed, 204 insertions(+), 203 deletions(-) diff --git a/README.md b/README.md index a247ae3..72b2815 100644 --- a/README.md +++ b/README.md @@ -147,5 +147,6 @@ $ uv run datamodel-codegen \ --output scope3ai/api/typesgen.py \ --output-model-type pydantic_v2.BaseModel \ --use-schema-description \ - --allow-extra-fields + --allow-extra-fields \ + && uv run ruff format scope3ai/api/typesgen.py ``` diff --git a/scope3ai/api/typesgen.py b/scope3ai/api/typesgen.py index b7c890b..9a58bfa 100644 --- a/scope3ai/api/typesgen.py +++ b/scope3ai/api/typesgen.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: aiapi.yaml -# timestamp: 2024-12-17T23:27:51+00:00 +# timestamp: 2024-12-17T23:42:18+00:00 from __future__ import annotations @@ -13,7 +13,7 @@ class StatusResponse(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) ready: bool reason: Optional[str] = None @@ -21,29 +21,29 @@ class StatusResponse(BaseModel): class ImpactBigQueryRequest(BaseModel): requestId: str = Field( - ..., description='Unique identifier for the request', examples=['124ab1c'] + ..., description="Unique identifier for the request", examples=["124ab1c"] ) caller: str = Field( ..., - description='Full resource name of the BigQuery job', + description="Full resource name of the BigQuery job", examples=[ - '//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf' + "//bigquery.googleapis.com/projects/myproject/jobs/myproject:US.bquxjob_5b4c112c_17961fafeaf" ], ) sessionUser: str = Field( ..., - description='Email of the user executing the BigQuery query', - examples=['user@company.com'], + description="Email of the user executing the BigQuery query", + examples=["user@company.com"], ) userDefinedContext: Optional[Dict[str, Any]] = Field( - None, description='User-defined context from BigQuery' + None, description="User-defined context from BigQuery" ) calls: List[List[Union[str, int]]] class ImpactBigQueryResponse(BaseModel): replies: List[str] = Field( - ..., description='Array of impact metric results', max_length=1000, min_length=0 + ..., description="Array of impact metric results", max_length=1000, min_length=0 ) errorMessage: Optional[str] = None @@ -51,14 +51,14 @@ class ImpactBigQueryResponse(BaseModel): class ImpactBigQueryError(BaseModel): errorMessage: str = Field( ..., - description='Error message for BigQuery', + description="Error message for BigQuery", examples=["Invalid request format: missing required field 'calls'"], ) class ImpactMetrics(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) usage_energy_wh: float = Field(..., examples=[0.13]) usage_emissions_gco2e: float = Field(..., examples=[0.81]) @@ -69,7 +69,7 @@ class ImpactMetrics(BaseModel): class PredictionStep(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) description: str duration_ms: float @@ -83,7 +83,7 @@ class Details(BaseModel): class Error(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) code: Optional[str] = None message: str @@ -92,10 +92,10 @@ class Error(BaseModel): class GPU(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) - name: Optional[str] = Field(None, examples=['NVIDIA A100 40GB']) - id: Optional[str] = Field(None, examples=['a100_40gb']) + name: Optional[str] = Field(None, examples=["NVIDIA A100 40GB"]) + id: Optional[str] = Field(None, examples=["a100_40gb"]) max_power_w: Optional[float] = Field(None, examples=[700]) embodied_emissions_kgco2e: Optional[float] = Field(None, examples=[282.1]) embodied_water_mlh2o: Optional[float] = Field(None, examples=[181.1]) @@ -103,23 +103,23 @@ class GPU(BaseModel): class ManagedServiceProvider(Enum): - aws_bedrock = 'aws-bedrock' - azure_ml = 'azure-ml' - google_vertex = 'google-vertex' - ibm_watson = 'ibm-watson' - hugging_face = 'hugging-face' + aws_bedrock = "aws-bedrock" + azure_ml = "azure-ml" + google_vertex = "google-vertex" + ibm_watson = "ibm-watson" + hugging_face = "hugging-face" -class Image(RootModel[constr(pattern=r'^\d{1,4}x\d{1,4}$')]): - root: constr(pattern=r'^\d{1,4}x\d{1,4}$') = Field(..., examples=['1024x1024']) +class Image(RootModel[constr(pattern=r"^\d{1,4}x\d{1,4}$")]): + root: constr(pattern=r"^\d{1,4}x\d{1,4}$") = Field(..., examples=["1024x1024"]) class CloudProvider(Enum): - aws = 'aws' - azure = 'azure' - gcp = 'gcp' - oracle = 'oracle' - ibm = 'ibm' + aws = "aws" + azure = "azure" + gcp = "gcp" + oracle = "oracle" + ibm = "ibm" class Task(Enum): @@ -133,37 +133,37 @@ class Task(Enum): """ - text_generation = 'text-generation' - chat = 'chat' - text_embedding = 'text-embedding' - text_classification = 'text-classification' - sentiment_analysis = 'sentiment-analysis' - named_entity_recognition = 'named-entity-recognition' - question_answering = 'question-answering' - summarization = 'summarization' - translation = 'translation' - image_classification = 'image-classification' - object_detection = 'object-detection' - image_segmentation = 'image-segmentation' - image_generation = 'image-generation' - image_to_text = 'image-to-text' - text_to_image = 'text-to-image' - style_transfer = 'style-transfer' - face_detection = 'face-detection' - facial_recognition = 'facial-recognition' - speech_to_text = 'speech-to-text' - text_to_speech = 'text-to-speech' - speaker_identification = 'speaker-identification' - audio_classification = 'audio-classification' - music_generation = 'music-generation' - multimodal_embedding = 'multimodal-embedding' - multimodal_generation = 'multimodal-generation' - visual_question_answering = 'visual-question-answering' - recommendation_system = 'recommendation-system' - reinforcement_learning = 'reinforcement-learning' - anomaly_detection = 'anomaly-detection' - time_series_forecasting = 'time-series-forecasting' - clustering = 'clustering' + text_generation = "text-generation" + chat = "chat" + text_embedding = "text-embedding" + text_classification = "text-classification" + sentiment_analysis = "sentiment-analysis" + named_entity_recognition = "named-entity-recognition" + question_answering = "question-answering" + summarization = "summarization" + translation = "translation" + image_classification = "image-classification" + object_detection = "object-detection" + image_segmentation = "image-segmentation" + image_generation = "image-generation" + image_to_text = "image-to-text" + text_to_image = "text-to-image" + style_transfer = "style-transfer" + face_detection = "face-detection" + facial_recognition = "facial-recognition" + speech_to_text = "speech-to-text" + text_to_speech = "text-to-speech" + speaker_identification = "speaker-identification" + audio_classification = "audio-classification" + music_generation = "music-generation" + multimodal_embedding = "multimodal-embedding" + multimodal_generation = "multimodal-generation" + visual_question_answering = "visual-question-answering" + recommendation_system = "recommendation-system" + reinforcement_learning = "reinforcement-learning" + anomaly_detection = "anomaly-detection" + time_series_forecasting = "time-series-forecasting" + clustering = "clustering" class Family(Enum): @@ -176,224 +176,224 @@ class Family(Enum): """ - claude = 'claude' - gpt = 'gpt' - dall_e = 'dall-e' - whisper = 'whisper' - gemini = 'gemini' - palm = 'palm' - bert = 'bert' - t5 = 't5' - llama = 'llama' - opt = 'opt' - galactica = 'galactica' - phi = 'phi' - stable_diffusion = 'stable-diffusion' - stable_lm = 'stable-lm' - mistral = 'mistral' - mixtral = 'mixtral' - command = 'command' - embed = 'embed' - falcon = 'falcon' - mpt = 'mpt' - pythia = 'pythia' - dolly = 'dolly' - bloom = 'bloom' - roberta = 'roberta' - gpt_neo = 'gpt-neo' - gpt_j = 'gpt-j' + claude = "claude" + gpt = "gpt" + dall_e = "dall-e" + whisper = "whisper" + gemini = "gemini" + palm = "palm" + bert = "bert" + t5 = "t5" + llama = "llama" + opt = "opt" + galactica = "galactica" + phi = "phi" + stable_diffusion = "stable-diffusion" + stable_lm = "stable-lm" + mistral = "mistral" + mixtral = "mixtral" + command = "command" + embed = "embed" + falcon = "falcon" + mpt = "mpt" + pythia = "pythia" + dolly = "dolly" + bloom = "bloom" + roberta = "roberta" + gpt_neo = "gpt-neo" + gpt_j = "gpt-j" class DataType(Enum): - fp8 = 'fp8' - fp8_e4m3 = 'fp8-e4m3' - fp8_e5m2 = 'fp8-e5m2' - fp16 = 'fp16' - tf32 = 'tf32' - fp32 = 'fp32' - fp64 = 'fp64' - bfloat8 = 'bfloat8' - bfloat16 = 'bfloat16' - bf16 = 'bf16' - int4 = 'int4' - int8 = 'int8' - int16 = 'int16' - int32 = 'int32' - int64 = 'int64' - uint4 = 'uint4' - uint8 = 'uint8' - uint16 = 'uint16' - uint32 = 'uint32' - uint64 = 'uint64' - - -class CountryCode(RootModel[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)]): - root: constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) = Field( + fp8 = "fp8" + fp8_e4m3 = "fp8-e4m3" + fp8_e5m2 = "fp8-e5m2" + fp16 = "fp16" + tf32 = "tf32" + fp32 = "fp32" + fp64 = "fp64" + bfloat8 = "bfloat8" + bfloat16 = "bfloat16" + bf16 = "bf16" + int4 = "int4" + int8 = "int8" + int16 = "int16" + int32 = "int32" + int64 = "int64" + uint4 = "uint4" + uint8 = "uint8" + uint16 = "uint16" + uint32 = "uint32" + uint64 = "uint64" + + +class CountryCode(RootModel[constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2)]): + root: constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2) = Field( ..., - description='Two-letter country code as defined by ISO 3166-1 alpha-2', - examples=['US'], + description="Two-letter country code as defined by ISO 3166-1 alpha-2", + examples=["US"], ) -class RegionCode(RootModel[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)]): - root: constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) = Field( +class RegionCode(RootModel[constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2)]): + root: constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2) = Field( ..., - description='Two-letter region code as defined by ISO 3166-1 alpha-2', - examples=['NY'], + description="Two-letter region code as defined by ISO 3166-1 alpha-2", + examples=["NY"], ) class GPUResponse(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) gpus: List[GPU] = Field(..., max_length=100) class ImpactLogRow(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) start_time_utc: Optional[datetime] = Field( None, - description='The start time of the inference', - examples=['2024-10-01T00:00:00Z'], + description="The start time of the inference", + examples=["2024-10-01T00:00:00Z"], ) request_duration_ms: Optional[float] = Field( None, - description='The time the request took (as measured by client or proxy)', + description="The time the request took (as measured by client or proxy)", examples=[283], ) processing_duration_ms: Optional[float] = Field( None, - description='The time taken in processing the request (as measured at execution)', + description="The time taken in processing the request (as measured at execution)", examples=[238], ) integration_source: Optional[str] = Field( None, - description='The integration used to source the data', - examples=['litellm'], + description="The integration used to source the data", + examples=["litellm"], ) client_id: Optional[str] = Field( - None, description='The client to attribute this call to' + None, description="The client to attribute this call to" ) project_id: Optional[str] = Field( - None, description='The project to attribute this call to' + None, description="The project to attribute this call to" ) application_id: Optional[str] = Field( - None, description='The application to attribute this call to' + None, description="The application to attribute this call to" ) session_id: Optional[str] = Field( - None, description='The ID of the session (multiple requests)' + None, description="The ID of the session (multiple requests)" ) request_id: Optional[str] = Field( - None, description='The unique identifier of this request' + None, description="The unique identifier of this request" ) environment: Optional[str] = Field( None, - description='Environment (prod/production indicates production)', - examples=['staging'], + description="Environment (prod/production indicates production)", + examples=["staging"], ) model_id: Optional[str] = Field( - None, description='The ID of the model requested', examples=['llama_31_8b'] + None, description="The ID of the model requested", examples=["llama_31_8b"] ) model_id_used: Optional[str] = Field( None, - description='The ID of the model that did the inference', - examples=['llama_31_8b_0125'], + description="The ID of the model that did the inference", + examples=["llama_31_8b_0125"], ) model_name: Optional[str] = Field( - None, description='The name of the model', examples=['LLaMa v3.1 8B'] + None, description="The name of the model", examples=["LLaMa v3.1 8B"] ) model_family: Optional[str] = Field( - None, description='The family of the model', examples=['llama'] + None, description="The family of the model", examples=["llama"] ) model_hugging_face_path: Optional[str] = Field( None, - description='The Hugging Face path of the model', - examples=['meta/llama31_8b'], + description="The Hugging Face path of the model", + examples=["meta/llama31_8b"], ) cloud_id: Optional[str] = Field( - None, description='The ID of the cloud', examples=['aws'] + None, description="The ID of the cloud", examples=["aws"] ) cloud_region: Optional[str] = Field( - None, description='The region of cloud hosting', examples=['us-central1'] + None, description="The region of cloud hosting", examples=["us-central1"] ) cloud_instance_id: Optional[str] = Field( - None, description='The instance type in the cloud', examples=['xl-4g-8a100'] + None, description="The instance type in the cloud", examples=["xl-4g-8a100"] ) managed_service_id: Optional[str] = Field( None, - description='The ID of a managed service provider', - examples=['aws-bedrock'], + description="The ID of a managed service provider", + examples=["aws-bedrock"], ) node_id: Optional[str] = Field( - None, description='The ID of a proprietary node', examples=['h200-2024-build'] + None, description="The ID of a proprietary node", examples=["h200-2024-build"] ) node_country: Optional[ - constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2) + constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2) ] = Field( - None, description='The country where the servers are hosted', examples=['US'] + None, description="The country where the servers are hosted", examples=["US"] ) - node_region: Optional[constr(pattern=r'^[A-Z]{2}$', min_length=2, max_length=2)] = ( + node_region: Optional[constr(pattern=r"^[A-Z]{2}$", min_length=2, max_length=2)] = ( Field( - None, description='The region where the servers are hosted', examples=['CA'] + None, description="The region where the servers are hosted", examples=["CA"] ) ) task: Optional[Task] = Field( - None, description='The task the AI is performing', examples=['text-generation'] + None, description="The task the AI is performing", examples=["text-generation"] ) input_tokens: Optional[conint(ge=0, le=100000000)] = Field( - None, description='the number of input tokens', examples=[1033] + None, description="the number of input tokens", examples=[1033] ) output_tokens: Optional[conint(ge=0, le=100000000)] = Field( - None, description='the number of output tokens', examples=[2300] + None, description="the number of output tokens", examples=[2300] ) input_audio_seconds: Optional[conint(ge=0, le=100000)] = Field( - None, description='the duration of audio input in seconds', examples=[60] + None, description="the duration of audio input in seconds", examples=[60] ) input_images: Optional[str] = Field( None, - description='a comma delimited list of image sizes', - examples=['512x512,1024x1024'], + description="a comma delimited list of image sizes", + examples=["512x512,1024x1024"], ) input_steps: Optional[conint(ge=1, le=10000)] = Field( - None, description='the number of steps in the model', examples=[50] + None, description="the number of steps in the model", examples=[50] ) output_images: Optional[str] = Field( None, - description='a comma delimited list of output sizes', - examples=['512x512,1024x1024'], + description="a comma delimited list of output sizes", + examples=["512x512,1024x1024"], ) output_video_frames: Optional[conint(ge=0, le=100000000)] = Field( None, - description='the number of video frames (frame rate x duration)', + description="the number of video frames (frame rate x duration)", examples=[60], ) output_video_resolution: Optional[int] = Field( None, - description='the resolution of the video in number of lines (for instance, 1080 for 1080p)', + description="the resolution of the video in number of lines (for instance, 1080 for 1080p)", examples=[1080], ) - request_cost: Optional[float] = Field(None, description='the cost of this request') - currency: Optional[str] = Field(None, description='the currency for cost data') + request_cost: Optional[float] = Field(None, description="the cost of this request") + currency: Optional[str] = Field(None, description="the currency for cost data") class Model(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) - id: Optional[str] = Field(None, examples=['gpt-4-turbo']) - name: Optional[str] = Field(None, examples=['GPT-4 Turbo']) - family: Optional[str] = Field(None, examples=['gpt']) - hugging_face_path: Optional[str] = Field(None, examples=['EleutherAI/gpt-neo-2.7B']) - benchmark_model_id: Optional[str] = Field(None, examples=['GPTJ-6B']) + id: Optional[str] = Field(None, examples=["gpt-4-turbo"]) + name: Optional[str] = Field(None, examples=["GPT-4 Turbo"]) + family: Optional[str] = Field(None, examples=["gpt"]) + hugging_face_path: Optional[str] = Field(None, examples=["EleutherAI/gpt-neo-2.7B"]) + benchmark_model_id: Optional[str] = Field(None, examples=["GPTJ-6B"]) total_params_billions: Optional[float] = Field(None, examples=[175]) number_of_experts: Optional[int] = Field(None, examples=[7]) params_per_expert_billions: Optional[float] = Field(None, examples=[8]) tensor_parallelism: Optional[int] = Field(None, examples=[1]) - datatype: Optional[DataType] = Field(None, examples=['fp8']) - task: Optional[Task] = Field(None, examples=['text-generation']) + datatype: Optional[DataType] = Field(None, examples=["fp8"]) + task: Optional[Task] = Field(None, examples=["text-generation"]) training_usage_energy_kwh: Optional[float] = Field(None, examples=[1013.1]) training_usage_emissions_kgco2e: Optional[float] = Field(None, examples=[1013.1]) training_usage_water_l: Optional[float] = Field(None, examples=[1013.1]) @@ -403,12 +403,12 @@ class Model(BaseModel): training_embodied_water_l: Optional[float] = Field(None, examples=[11013.1]) estimated_use_life_days: Optional[float] = Field(None, examples=[1013.1]) estimated_requests_per_day: Optional[float] = Field(None, examples=[1013.1]) - fine_tuned_from_model_id: Optional[str] = Field(None, examples=['llama_31_8b']) + fine_tuned_from_model_id: Optional[str] = Field(None, examples=["llama_31_8b"]) class GridMix(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) country: CountryCode region: Optional[RegionCode] = None @@ -417,13 +417,13 @@ class GridMix(BaseModel): class Node(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) - id: Optional[str] = Field(None, examples=['base-node-xl']) - cloud_id: Optional[str] = Field(None, examples=['aws']) - cloud_instance_id: Optional[str] = Field(None, examples=['a2-highgpu-1g']) - managed_service_id: Optional[str] = Field(None, examples=['aws-bedrock']) - gpu_id: Optional[str] = Field(None, examples=['a100_40gb']) + id: Optional[str] = Field(None, examples=["base-node-xl"]) + cloud_id: Optional[str] = Field(None, examples=["aws"]) + cloud_instance_id: Optional[str] = Field(None, examples=["a2-highgpu-1g"]) + managed_service_id: Optional[str] = Field(None, examples=["aws-bedrock"]) + gpu_id: Optional[str] = Field(None, examples=["a100_40gb"]) gpu: Optional[GPU] = None gpu_count: Optional[conint(ge=0, le=10000)] = Field(None, examples=[8]) cpu_count: Optional[conint(ge=1, le=10000)] = Field(None, examples=[2]) @@ -444,96 +444,96 @@ class Node(BaseModel): class ModelResponse(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) models: List[Model] = Field(..., max_length=100) class NodeResponse(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) nodes: List[Node] = Field(..., max_length=100) class ImpactLogRequest(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) rows: List[ImpactLogRow] = Field(..., max_length=1000) class ImpactRow(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) utc_datetime: Optional[datetime] = Field( None, - description='The start time of the request in UTC', - examples=['2022-01-01T00:00:00Z'], + description="The start time of the request in UTC", + examples=["2022-01-01T00:00:00Z"], ) model: Model cloud_region: Optional[str] = Field( - None, description='The region of cloud hosting', examples=['us-central1'] + None, description="The region of cloud hosting", examples=["us-central1"] ) node: Optional[Node] = None country: Optional[CountryCode] = None region: Optional[RegionCode] = None - task: Optional[Task] = Field(None, examples=['text-generation']) + task: Optional[Task] = Field(None, examples=["text-generation"]) input_tokens: Optional[conint(ge=0, le=100000000)] = Field( - None, description='the number of input (or prompt) tokens', examples=[128] + None, description="the number of input (or prompt) tokens", examples=[128] ) input_audio_seconds: Optional[conint(ge=0, le=100000)] = Field( - None, description='the duration of audio input in seconds', examples=[60] + None, description="the duration of audio input in seconds", examples=[60] ) output_tokens: Optional[conint(ge=0, le=100000000)] = Field( - None, description='the number of output (or completion) tokens', examples=[128] + None, description="the number of output (or completion) tokens", examples=[128] ) input_images: Optional[List[Image]] = Field(None, max_length=100) input_steps: Optional[conint(ge=1, le=10000)] = Field( - None, description='the number of steps to use in the model', examples=[50] + None, description="the number of steps to use in the model", examples=[50] ) output_images: Optional[List[Image]] = Field( - None, description='a list of output image sizes', max_length=100 + None, description="a list of output image sizes", max_length=100 ) output_video_frames: Optional[conint(ge=0, le=100000000)] = Field( None, - description='the number of video frames (frame rate x duration)', + description="the number of video frames (frame rate x duration)", examples=[60], ) output_video_resolution: Optional[int] = Field( None, - description='the resolution of the video in number of lines (for instance, 1080 for 1080p)', + description="the resolution of the video in number of lines (for instance, 1080 for 1080p)", examples=[1080], ) request_id: Optional[str] = Field( - None, description='The unique identifier of this request', examples=['124ab1c'] + None, description="The unique identifier of this request", examples=["124ab1c"] ) session_id: Optional[str] = Field( - None, description='The ID of the session', examples=['124ab1c'] + None, description="The ID of the session", examples=["124ab1c"] ) trace_id: Optional[str] = Field( - None, description='The ID of the trace', examples=['124ab1c'] + None, description="The ID of the trace", examples=["124ab1c"] ) parent_trace_id: Optional[str] = Field( - None, description='The ID of the parent trace', examples=['124ab1c'] + None, description="The ID of the parent trace", examples=["124ab1c"] ) request_duration_ms: Optional[float] = Field( None, - description='The time the request took (as measured by client or proxy)', + description="The time the request took (as measured by client or proxy)", examples=[283], ) managed_service_id: Optional[str] = Field( None, - description='The ID of a managed service provider', - examples=['aws-bedrock'], + description="The ID of a managed service provider", + examples=["aws-bedrock"], ) model_used: Optional[Model] = None class DebugInfo(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) model: Optional[Model] = None hardware_node: Optional[Node] = None @@ -543,14 +543,14 @@ class DebugInfo(BaseModel): class ImpactRequest(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) rows: List[ImpactRow] = Field(..., max_length=1000) class ModeledRow(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) inference_impact: Optional[ImpactMetrics] = None training_impact: Optional[ImpactMetrics] = None @@ -562,7 +562,7 @@ class ModeledRow(BaseModel): class ImpactResponse(BaseModel): model_config = ConfigDict( - extra='forbid', + extra="forbid", ) rows: List[ModeledRow] = Field(..., max_length=1000) total_energy_wh: Optional[float] = Field(None, examples=[0.13])