diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f1da3e..c7853b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,6 +6,7 @@ repos: hooks: - id: debug-statements - id: trailing-whitespace + exclude: tests/api-mocks/aiapi.yaml - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.2 diff --git a/examples/api-sync.py b/examples/api-sync.py index b32c559..a1683f5 100644 --- a/examples/api-sync.py +++ b/examples/api-sync.py @@ -27,7 +27,7 @@ def send_impact(): from scope3ai.api.types import ImpactRow print("Sending impact") - impact = ImpactRow(model="gpt_4o", input_tokens=100, output_tokens=100) + impact = ImpactRow(model_id="gpt_4o", input_tokens=100, output_tokens=100) response = client.impact(rows=[impact]) print(response) diff --git a/scope3ai/api/client.py b/scope3ai/api/client.py index e5496fc..f72f6d1 100644 --- a/scope3ai/api/client.py +++ b/scope3ai/api/client.py @@ -1,5 +1,5 @@ from os import getenv -from typing import List, Optional, Union +from typing import Any, List, Optional, TypeVar import httpx from pydantic import BaseModel @@ -15,13 +15,19 @@ NodeResponse, ) +ClientType = TypeVar("ClientType", httpx.Client, httpx.AsyncClient) + class Scope3AIError(Exception): pass class ClientBase: - def __init__(self, api_key: str = None, api_url: str = None) -> None: + def __init__( + self, + api_key: Optional[str] = None, + api_url: Optional[str] = None, + ) -> None: self.api_key = api_key or getenv("SCOPE3AI_API_KEY") self.api_url = api_url or getenv("SCOPE3AI_API_URL") or DEFAULT_API_URL if not self.api_key: @@ -38,7 +44,7 @@ def __init__(self, api_key: str = None, api_url: str = None) -> None: ) @property - def client(self) -> Union[httpx.Client, httpx.AsyncClient]: + def client(self) -> ClientType: """ Obtain an httpx client for synchronous or asynchronous operation with the necessary authentication headers included. @@ -47,8 +53,14 @@ def client(self) -> Union[httpx.Client, httpx.AsyncClient]: self._client = self.create_client() return self._client + def create_client(self) -> ClientType: + raise NotImplementedError + class ClientCommands: + def execute_request(self, *args, **kwargs) -> Any: + raise NotImplementedError + def model( self, family: Optional[Family] = None, @@ -144,7 +156,7 @@ class Client(ClientBase, ClientCommands): Synchronous Client to the Scope3AI HTTP API """ - def create_client(self): + def create_client(self) -> httpx.Client: return httpx.Client(headers={"Authorization": f"Bearer {self.api_key}"}) def execute_request( diff --git a/scope3ai/api/typesgen.py b/scope3ai/api/typesgen.py index 22264bc..b8f50ec 100644 --- a/scope3ai/api/typesgen.py +++ b/scope3ai/api/typesgen.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: aiapi.yaml -# timestamp: 2025-01-21T22:57:07+00:00 +# timestamp: 2025-01-24T16:50:00+00:00 from __future__ import annotations @@ -102,6 +102,105 @@ class NodeUpdateRequest(BaseModel): ) +class ModelCreateRequest(BaseModel): + """ + Create a new model + """ + + id: Annotated[str, Field(examples=["gpt-4-turbo"])] + name: Annotated[Optional[str], Field(examples=["GPT-4 Turbo"])] = None + family: Annotated[Optional[str], Field(examples=["gpt"])] = None + hugging_face_path: Annotated[ + Optional[str], Field(examples=["EleutherAI/gpt-neo-2.7B"]) + ] = None + benchmark_model_id: Annotated[Optional[str], Field(examples=["gpt-4-turbo"])] = None + estimated_use_life_days: Annotated[Optional[float], Field(examples=[365])] = None + estimated_requests_per_day: Annotated[Optional[float], Field(examples=[1000])] = ( + None + ) + fine_tuned_from_model_id: Annotated[ + Optional[str], Field(examples=["gpt-4-turbo"]) + ] = None + aliases: Annotated[ + Optional[List[str]], Field(examples=[["claude-latest"]], max_length=100) + ] = None + total_params_billions: Annotated[Optional[float], Field(examples=[175])] = None + number_of_experts: Annotated[Optional[int], Field(examples=[7])] = None + params_per_expert_billions: Annotated[Optional[float], Field(examples=[8])] = None + training_usage_energy_kwh: Annotated[Optional[float], Field(examples=[1013.1])] = ( + None + ) + training_usage_emissions_kgco2e: Annotated[ + Optional[float], Field(examples=[1013.1]) + ] = None + training_usage_water_l: Annotated[Optional[float], Field(examples=[1013.1])] = None + training_embodied_emissions_kgco2e: Annotated[ + Optional[float], Field(examples=[11013.1]) + ] = None + training_embodied_water_l: Annotated[Optional[float], Field(examples=[11013.1])] = ( + None + ) + + +class ModelUpdateRequest(BaseModel): + """ + Update an existing model + """ + + name: Annotated[Optional[str], Field(examples=["GPT-4 Turbo"])] = None + family: Annotated[Optional[str], Field(examples=["gpt"])] = None + hugging_face_path: Annotated[ + Optional[str], Field(examples=["EleutherAI/gpt-neo-2.7B"]) + ] = None + benchmark_model_id: Annotated[Optional[str], Field(examples=["gpt-4-turbo"])] = None + estimated_use_life_days: Annotated[Optional[float], Field(examples=[365])] = None + estimated_requests_per_day: Annotated[Optional[float], Field(examples=[1000])] = ( + None + ) + fine_tuned_from_model_id: Annotated[ + Optional[str], Field(examples=["gpt-4-turbo"]) + ] = None + total_params_billions: Annotated[Optional[float], Field(examples=[175])] = None + number_of_experts: Annotated[Optional[int], Field(examples=[7])] = None + params_per_expert_billions: Annotated[Optional[float], Field(examples=[8])] = None + training_usage_energy_kwh: Annotated[Optional[float], Field(examples=[1013.1])] = ( + None + ) + training_usage_emissions_kgco2e: Annotated[ + Optional[float], Field(examples=[1013.1]) + ] = None + training_usage_water_l: Annotated[Optional[float], Field(examples=[1013.1])] = None + training_embodied_emissions_kgco2e: Annotated[ + Optional[float], Field(examples=[11013.1]) + ] = None + training_embodied_water_l: Annotated[Optional[float], Field(examples=[11013.1])] = ( + None + ) + + +class GPUCreateRequest(BaseModel): + name: Annotated[str, Field(examples=["NVIDIA A100 40GB"])] + id: Annotated[str, Field(examples=["a100_40gb"])] + max_power_w: Annotated[float, Field(examples=[700])] + embodied_emissions_kgco2e: Annotated[float, Field(examples=[282.1])] + embodied_water_mlh2o: Annotated[float, Field(examples=[181.1])] + performance_ratio_to_h200: Annotated[float, Field(examples=[1.5])] + ols_coefficient_gpu_count: Annotated[float, Field(examples=[11.4])] + ols_intercept: Annotated[float, Field(examples=[11.4])] + + +class GPUUpdateRequest(BaseModel): + name: Annotated[Optional[str], Field(examples=["NVIDIA A100 40GB"])] = None + max_power_w: Annotated[Optional[float], Field(examples=[700])] = None + embodied_emissions_kgco2e: Annotated[Optional[float], Field(examples=[282.1])] = ( + None + ) + embodied_water_mlh2o: Annotated[Optional[float], Field(examples=[181.1])] = None + performance_ratio_to_h200: Annotated[Optional[float], Field(examples=[1.5])] = None + ols_coefficient_gpu_count: Annotated[Optional[float], Field(examples=[11.4])] = None + ols_intercept: Annotated[Optional[float], Field(examples=[11.4])] = None + + class Call(RootModel[List[Union[str, int]]]): root: Annotated[ List[Union[str, int]], @@ -151,7 +250,7 @@ class ImpactBigQueryRequest(BaseModel): Optional[Dict[str, Any]], Field(description="User-defined context from BigQuery"), ] = None - calls: List[Call] + calls: Annotated[List[Call], Field(max_length=1000)] class ImpactBigQueryResponse(BaseModel): @@ -210,7 +309,7 @@ class Error(BaseModel): class Node(NodeCreateRequest): customer_id: Annotated[ - Optional[Any], + Optional[int], Field( description="ID of the customer who owns this node (visible to admins only)" ), @@ -227,7 +326,7 @@ class GPU(BaseModel): model_config = ConfigDict( extra="forbid", ) - name: Annotated[Optional[str], Field(examples=["NVIDIA A100 40GB"])] = None + name: Annotated[str, Field(examples=["NVIDIA A100 40GB"])] id: Annotated[str, Field(examples=["a100_40gb"])] max_power_w: Annotated[float, Field(examples=[700])] embodied_emissions_kgco2e: Annotated[float, Field(examples=[282.1])] @@ -235,6 +334,18 @@ class GPU(BaseModel): performance_ratio_to_h200: Annotated[float, Field(examples=[1.5])] ols_coefficient_gpu_count: Annotated[float, Field(examples=[11.4])] ols_intercept: Annotated[float, Field(examples=[11.4])] + customer_id: Annotated[ + int, + Field( + description="ID of the customer who owns this node (visible to admins only)" + ), + ] + created_at: datetime + updated_at: datetime + created_by: Annotated[ + str, + Field(description="ID of the user who created the node (admin or owner only)"), + ] class Image(RootModel[str]): @@ -517,12 +628,12 @@ class ImpactRow(BaseModel): ), ] = None input_audio_seconds: Annotated[ - Optional[int], + Optional[float], Field( description="the duration of audio input in seconds", examples=[60], - ge=0, - le=100000, + ge=0.0, + le=100000.0, ), ] = None output_tokens: Annotated[ @@ -589,6 +700,14 @@ class Model(BaseModel): extra="forbid", ) id: Annotated[str, Field(examples=["gpt-4-turbo"])] + aliases: Annotated[ + List[str], + Field( + description="List of aliases for this model; must be globally-unique with id", + examples=[["claude-latest", "claude-3-sonnet-current"]], + max_length=100, + ), + ] name: Annotated[Optional[str], Field(examples=["GPT-4 Turbo"])] = None family: Annotated[Optional[str], Field(examples=["gpt"])] = None hugging_face_path: Annotated[ @@ -621,6 +740,18 @@ class Model(BaseModel): fine_tuned_from_model_id: Annotated[ Optional[str], Field(examples=["llama_31_8b"]) ] = None + customer_id: Annotated[ + int, + Field( + description="ID of the customer who owns this node (visible to admins only)" + ), + ] + created_at: datetime + updated_at: datetime + created_by: Annotated[ + str, + Field(description="ID of the user who created the node (admin or owner only)"), + ] class GridMix(BaseModel): diff --git a/tests/api-mocks/aiapi.yaml b/tests/api-mocks/aiapi.yaml index cb32c7a..669d446 100644 --- a/tests/api-mocks/aiapi.yaml +++ b/tests/api-mocks/aiapi.yaml @@ -13,41 +13,42 @@ paths: operationId: status responses: "200": - description: Model details retrieved successfully + description: Server is behaving normally content: application/json: schema: $ref: "#/components/schemas/StatusResponse" - "406": - description: Not acceptable response + "403": + description: Forbidden content: application/json: schema: $ref: "#/components/schemas/Error" - "429": - description: Too many requests + default: + description: Unexpected error content: application/json: schema: $ref: "#/components/schemas/Error" - "401": - description: Unauthorized + /reload: + get: + servers: + - url: https://aiapi.scope3.com + description: API server + operationId: reload + responses: + "200": + description: Cache reloaded successfully content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: "#/components/schemas/StatusResponse" "403": description: Forbidden content: application/json: schema: $ref: "#/components/schemas/Error" - "415": - description: Unsupported media type - content: - application/json: - schema: - $ref: "#/components/schemas/Error" default: description: Unexpected error content: @@ -114,6 +115,262 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + post: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Create a model + operationId: createModel + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ModelCreateRequest" + responses: + "201": + description: Model created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Model" + "400": + description: Invalid request format + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Model ID already exists + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '429': + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /model/{modelId}: + parameters: + - name: modelId + in: path + required: true + schema: + type: string + get: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Get a specific model (global or custom) + operationId: getModel + responses: + "200": + description: Model retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Model" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Model not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + put: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Update a model + operationId: updateModel + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ModelUpdateRequest" + responses: + "200": + description: Model updated successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Model" + "400": + description: Invalid request format + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Model not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Delete a model + operationId: deleteModel + responses: + "204": + description: Model deleted successfully + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Model not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /model/{modelId}/alias: + parameters: + - name: modelId + in: path + required: true + schema: + type: string + put: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Add a new alias to a model + operationId: addModelAlias + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - alias + properties: + alias: + type: string + example: "claude-latest" + responses: + "204": + description: Alias deleted successfully + "400": + description: Invalid request format + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Model not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: Alias already exists + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /model/{modelId}/alias/{alias}: + parameters: + - name: modelId + in: path + required: true + schema: + type: string + - name: alias + in: path + required: true + schema: + type: string + delete: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Remove an alias from a model + operationId: removeModelAlias + responses: + "204": + description: Alias deleted successfully + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Alias not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /node: get: @@ -382,14 +639,174 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" - "415": - description: Unsupported media type - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - default: - description: Unexpected error + "415": + description: Unsupported media type + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + default: + description: Unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Create a GPU + operationId: createGPU + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GPUCreateRequest" + responses: + "201": + description: GPU created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/GPU" + "400": + description: Invalid request format + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "409": + description: GPU ID already exists + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /gpu/{gpuId}: + parameters: + - name: gpuId + in: path + required: true + schema: + type: string + get: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Get a specific GPU (global or custom) + operationId: getGPU + responses: + "200": + description: GPU retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/GPU" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: GPU not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + put: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Update a GPU + operationId: updateGPU + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GPUUpdateRequest" + responses: + "200": + description: GPU updated successfully + content: + application/json: + schema: + $ref: "#/components/schemas/GPU" + "400": + description: Invalid request format + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: GPU not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + delete: + servers: + - url: https://aiapi.scope3.com + description: API server + security: + - bearerAuth: [] + summary: Delete a GPU + operationId: deleteGPU + responses: + "204": + description: GPU deleted successfully + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: GPU not found content: application/json: schema: @@ -732,6 +1149,241 @@ components: items: $ref: "#/components/schemas/GPU" + ModelCreateRequest: + type: object + required: + - id + description: Create a new model + properties: + id: + type: string + example: "gpt-4-turbo" + name: + type: string + example: "GPT-4 Turbo" + family: + type: string + example: "gpt" + hugging_face_path: + type: string + example: "EleutherAI/gpt-neo-2.7B" + benchmark_model_id: + type: string + example: "gpt-4-turbo" + estimated_use_life_days: + type: number + example: 365 + format: float + x-go-type: float64 + estimated_requests_per_day: + type: number + example: 1000 + format: float + x-go-type: float64 + fine_tuned_from_model_id: + type: string + example: "gpt-4-turbo" + aliases: + type: array + items: + type: string + maxItems: 100 + example: ["claude-latest"] + total_params_billions: + type: number + example: 175 + format: float + x-go-type: float64 + number_of_experts: + type: integer + x-go-type: int64 + example: 7 + params_per_expert_billions: + type: number + example: 8 + format: float + x-go-type: float64 + training_usage_energy_kwh: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_usage_emissions_kgco2e: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_usage_water_l: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_embodied_emissions_kgco2e: + type: number + example: 11013.1 + format: float + x-go-type: float64 + training_embodied_water_l: + type: number + example: 11013.1 + format: float + x-go-type: float64 + + ModelUpdateRequest: + type: object + description: Update an existing model + properties: + name: + type: string + example: "GPT-4 Turbo" + family: + type: string + example: "gpt" + hugging_face_path: + type: string + example: "EleutherAI/gpt-neo-2.7B" + benchmark_model_id: + type: string + example: "gpt-4-turbo" + estimated_use_life_days: + type: number + format: float + example: 365 + x-go-type: float64 + estimated_requests_per_day: + type: number + example: 1000 + format: float + x-go-type: float64 + fine_tuned_from_model_id: + type: string + example: "gpt-4-turbo" + total_params_billions: + type: number + example: 175 + format: float + x-go-type: float64 + number_of_experts: + type: integer + x-go-type: int64 + example: 7 + params_per_expert_billions: + type: number + example: 8 + format: float + x-go-type: float64 + training_usage_energy_kwh: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_usage_emissions_kgco2e: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_usage_water_l: + type: number + example: 1013.1 + format: float + x-go-type: float64 + training_embodied_emissions_kgco2e: + type: number + example: 11013.1 + format: float + x-go-type: float64 + training_embodied_water_l: + type: number + example: 11013.1 + format: float + x-go-type: float64 + + GPUCreateRequest: + type: object + required: + - id + - name + - max_power_w + - embodied_emissions_kgco2e + - embodied_water_mlh2o + - performance_ratio_to_h200 + - ols_intercept + - ols_coefficient_gpu_count + properties: + name: + type: string + example: "NVIDIA A100 40GB" + id: + type: string + example: "a100_40gb" + max_power_w: + type: number + example: 700 + format: float + x-go-type: float64 + embodied_emissions_kgco2e: + type: number + example: 282.1 + format: float + x-go-type: float64 + embodied_water_mlh2o: + type: number + example: 181.1 + format: float + x-go-type: float64 + performance_ratio_to_h200: + type: number + example: 1.5 + format: float + x-go-type: float64 + ols_coefficient_gpu_count: + type: number + example: 11.4 + format: float + x-go-type: float64 + ols_intercept: + type: number + example: 11.4 + format: float + x-go-type: float64 + + GPUUpdateRequest: + type: object + properties: + name: + type: string + example: "NVIDIA A100 40GB" + max_power_w: + type: number + example: 700 + format: float + x-go-type: float64 + embodied_emissions_kgco2e: + type: number + example: 282.1 + format: float + x-go-type: float64 + embodied_water_mlh2o: + type: number + example: 181.1 + format: float + x-go-type: float64 + performance_ratio_to_h200: + type: number + example: 1.5 + format: float + x-go-type: float64 + ols_coefficient_gpu_count: + type: number + example: 11.4 + format: float + x-go-type: float64 + ols_intercept: + type: number + example: 11.4 + format: float + x-go-type: float64 + ImpactBigQueryRequest: type: object required: @@ -757,6 +1409,7 @@ components: description: User-defined context from BigQuery calls: type: array + maxItems: 1000 items: description: | Array of function call parameters in this exact order: @@ -940,8 +1593,9 @@ components: maximum: 100000000 example: 128 input_audio_seconds: - type: integer - x-go-type: int64 + type: number + format: float + x-go-type: float64 description: the duration of audio input in seconds minimum: 0 maximum: 100000 @@ -1004,11 +1658,23 @@ components: type: object required: - id + - created_at + - updated_at + - created_by + - customer_id + - aliases additionalProperties: false properties: id: type: string example: "gpt-4-turbo" + aliases: + type: array + maxItems: 100 + items: + type: string + description: List of aliases for this model; must be globally-unique with id + example: ["claude-latest", "claude-3-sonnet-current"] name: type: string example: "GPT-4 Turbo" @@ -1082,6 +1748,19 @@ components: fine_tuned_from_model_id: type: string example: llama_31_8b + customer_id: + type: integer + x-go-type: int64 + description: ID of the customer who owns this node (visible to admins only) + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + created_by: + type: string + description: ID of the user who created the node (admin or owner only) ImpactResponse: title: Impact Response @@ -1250,7 +1929,7 @@ components: - updated_at properties: customer_id: - type: int + type: integer x-go-type: int64 description: ID of the customer who owns this node (visible to admins only) created_at: @@ -1267,12 +1946,17 @@ components: type: object required: - id + - name - max_power_w - embodied_emissions_kgco2e - embodied_water_mlh2o - performance_ratio_to_h200 - ols_intercept - ols_coefficient_gpu_count + - created_at + - updated_at + - customer_id + - created_by additionalProperties: false properties: name: @@ -1311,6 +1995,20 @@ components: example: 11.4 format: float x-go-type: float64 + customer_id: + type: integer + x-go-type: int64 + description: ID of the customer who owns this node (visible to admins only) + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + created_by: + type: string + description: ID of the user who created the node (admin or owner only) + Image: type: string diff --git a/tools/sync-api.py b/tools/sync-api.py index bd4a76b..c595b48 100644 --- a/tools/sync-api.py +++ b/tools/sync-api.py @@ -1,6 +1,6 @@ import shutil -import tempfile import subprocess +import tempfile from pathlib import Path @@ -35,23 +35,10 @@ def remove_number_of_experts(text): raise ValueError("ERROR: 'number_of_experts' not found in the file.") return "\n".join(new_lines) - def fix_pattern(text): - print("- Fixing image pattern") - old_pattern = r"pattern: /^(\d{1,4})x(\d{1,4})$/" - new_pattern = r"pattern: ^(\d{1,4})x(\d{1,4})$" - - if old_pattern not in text: - raise ValueError( - r"ERROR: 'pattern: /^(\d{1,4})x(\d{1,4})$/' not found in the file." - ) - - return text.replace(old_pattern, new_pattern) - try: print(f"Patching aiapi.yaml: {filename}") content = filename.read_text() content = remove_number_of_experts(content) - content = fix_pattern(content) filename.write_text(content) except Exception as e: