From ef5aeb17a1c30987bfa099adbb0cd0f0032d07e4 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 31 Jul 2024 12:41:39 -0700 Subject: [PATCH] fix pydantic obj for FT endpoints --- litellm/fine_tuning/main.py | 10 +- litellm/llms/fine_tuning_apis/openai.py | 8 +- .../fine_tuning_endpoints.py | 157 ------------------ litellm/types/llms/openai.py | 45 ++--- 4 files changed, 36 insertions(+), 184 deletions(-) delete mode 100644 litellm/proxy/fine_tuning_endpoints.py/fine_tuning_endpoints.py diff --git a/litellm/fine_tuning/main.py b/litellm/fine_tuning/main.py index 5206cb7897..ede85351d1 100644 --- a/litellm/fine_tuning/main.py +++ b/litellm/fine_tuning/main.py @@ -18,6 +18,7 @@ import httpx import litellm from litellm import get_secret +from litellm._logging import verbose_logger from litellm.llms.fine_tuning_apis.azure import AzureOpenAIFineTuningAPI from litellm.llms.fine_tuning_apis.openai import ( FineTuningJob, @@ -51,6 +52,9 @@ async def acreate_fine_tuning_job( Async: Creates and executes a batch from an uploaded file of request """ + verbose_logger.debug( + "inside acreate_fine_tuning_job model=%s and kwargs=%s", model, kwargs + ) try: loop = asyncio.get_event_loop() kwargs["acreate_fine_tuning_job"] = True @@ -156,11 +160,15 @@ def create_fine_tuning_job( seed=seed, ) + create_fine_tuning_job_data_dict = create_fine_tuning_job_data.model_dump( + exclude_none=True + ) + response = openai_fine_tuning_apis_instance.create_fine_tuning_job( api_base=api_base, api_key=api_key, organization=organization, - create_fine_tuning_job_data=create_fine_tuning_job_data, + create_fine_tuning_job_data=create_fine_tuning_job_data_dict, timeout=timeout, max_retries=optional_params.max_retries, _is_async=_is_async, diff --git a/litellm/llms/fine_tuning_apis/openai.py b/litellm/llms/fine_tuning_apis/openai.py index 2f6d89ea0b..6f3cd60211 100644 --- a/litellm/llms/fine_tuning_apis/openai.py +++ b/litellm/llms/fine_tuning_apis/openai.py @@ -50,18 +50,18 @@ class OpenAIFineTuningAPI(BaseLLM): async def acreate_fine_tuning_job( self, - create_fine_tuning_job_data: FineTuningJobCreate, + create_fine_tuning_job_data: dict, openai_client: AsyncOpenAI, ) -> FineTuningJob: response = await openai_client.fine_tuning.jobs.create( - **create_fine_tuning_job_data # type: ignore + **create_fine_tuning_job_data ) return response def create_fine_tuning_job( self, _is_async: bool, - create_fine_tuning_job_data: FineTuningJobCreate, + create_fine_tuning_job_data: dict, api_key: Optional[str], api_base: Optional[str], timeout: Union[float, httpx.Timeout], @@ -95,7 +95,7 @@ class OpenAIFineTuningAPI(BaseLLM): verbose_logger.debug( "creating fine tuning job, args= %s", create_fine_tuning_job_data ) - response = openai_client.fine_tuning.jobs.create(**create_fine_tuning_job_data) # type: ignore + response = openai_client.fine_tuning.jobs.create(**create_fine_tuning_job_data) return response async def acancel_fine_tuning_job( diff --git a/litellm/proxy/fine_tuning_endpoints.py/fine_tuning_endpoints.py b/litellm/proxy/fine_tuning_endpoints.py/fine_tuning_endpoints.py deleted file mode 100644 index 36531a8260..0000000000 --- a/litellm/proxy/fine_tuning_endpoints.py/fine_tuning_endpoints.py +++ /dev/null @@ -1,157 +0,0 @@ -######################################################################### - -# /v1/fine_tuning Endpoints - -# Equivalent of https://platform.openai.com/docs/api-reference/fine-tuning -########################################################################## - -import asyncio -import traceback -from datetime import datetime, timedelta, timezone -from typing import List, Optional - -import fastapi -import httpx -from fastapi import ( - APIRouter, - Depends, - File, - Form, - Header, - HTTPException, - Request, - Response, - UploadFile, - status, -) - -import litellm -from litellm import CreateFileRequest, FileContentRequest -from litellm._logging import verbose_proxy_logger -from litellm.batches.main import FileObject -from litellm.proxy._types import * -from litellm.proxy.auth.user_api_key_auth import user_api_key_auth - -router = APIRouter() - -from litellm.llms.fine_tuning_apis.openai import ( - FineTuningJob, - FineTuningJobCreate, - OpenAIFineTuningAPI, -) - - -@router.post( - "/v1/fine_tuning/jobs", - dependencies=[Depends(user_api_key_auth)], - tags=["fine-tuning"], -) -@router.post( - "/fine_tuning/jobs", - dependencies=[Depends(user_api_key_auth)], - tags=["fine-tuning"], -) -async def create_fine_tuning_job( - request: Request, - fastapi_response: Response, - fine_tuning_job: FineTuningJobCreate, - user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), -): - """ - Creates a fine-tuning job which begins the process of creating a new model from a given dataset. - This is the equivalent of POST https://api.openai.com/v1/fine_tuning/jobs - - Supports Identical Params as: https://platform.openai.com/docs/api-reference/fine-tuning/create - - Example Curl: - ``` - curl http://localhost:4000/v1/fine_tuning/jobs \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer sk-1234" \ - -d '{ - "model": "gpt-3.5-turbo", - "training_file": "file-abc123", - "hyperparameters": { - "n_epochs": 4 - } - }' - ``` - """ - from litellm.proxy.proxy_server import ( - add_litellm_data_to_request, - general_settings, - get_custom_headers, - proxy_config, - proxy_logging_obj, - version, - ) - - try: - # Convert Pydantic model to dict - data = fine_tuning_job.dict(exclude_unset=True) - - # Include original request and headers in the data - data = await add_litellm_data_to_request( - data=data, - request=request, - general_settings=general_settings, - user_api_key_dict=user_api_key_dict, - version=version, - proxy_config=proxy_config, - ) - - # For now, use custom_llm_provider=="openai" -> this will change as LiteLLM adds more providers for fine-tuning - response = await litellm.acreate_fine_tuning_job( - custom_llm_provider="openai", **data - ) - - ### ALERTING ### - asyncio.create_task( - proxy_logging_obj.update_request_status( - litellm_call_id=data.get("litellm_call_id", ""), status="success" - ) - ) - - ### RESPONSE HEADERS ### - hidden_params = getattr(response, "_hidden_params", {}) or {} - model_id = hidden_params.get("model_id", None) or "" - cache_key = hidden_params.get("cache_key", None) or "" - api_base = hidden_params.get("api_base", None) or "" - - fastapi_response.headers.update( - get_custom_headers( - user_api_key_dict=user_api_key_dict, - model_id=model_id, - cache_key=cache_key, - api_base=api_base, - version=version, - model_region=getattr(user_api_key_dict, "allowed_model_region", ""), - ) - ) - - return response - except Exception as e: - await proxy_logging_obj.post_call_failure_hook( - user_api_key_dict=user_api_key_dict, original_exception=e, request_data=data - ) - verbose_proxy_logger.error( - "litellm.proxy.proxy_server.create_fine_tuning_job(): Exception occurred - {}".format( - str(e) - ) - ) - verbose_proxy_logger.debug(traceback.format_exc()) - if isinstance(e, HTTPException): - raise ProxyException( - message=getattr(e, "message", str(e.detail)), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", status.HTTP_400_BAD_REQUEST), - ) - else: - error_msg = f"{str(e)}" - raise ProxyException( - message=getattr(e, "message", error_msg), - type=getattr(e, "type", "None"), - param=getattr(e, "param", "None"), - code=getattr(e, "status_code", 500), - ) diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 396e58e994..3bb59f0053 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -9,7 +9,6 @@ from typing import ( Mapping, Optional, Tuple, - TypedDict, Union, ) @@ -31,7 +30,7 @@ from openai.types.beta.threads.message import Message as OpenAIMessage from openai.types.beta.threads.message_content import MessageContent from openai.types.beta.threads.run import Run from pydantic import BaseModel, Field -from typing_extensions import Dict, Required, override +from typing_extensions import Dict, Required, TypedDict, override FileContent = Union[IO[bytes], bytes, PathLike] @@ -457,15 +456,17 @@ class ChatCompletionUsageBlock(TypedDict): total_tokens: int -class Hyperparameters(TypedDict): - batch_size: Optional[Union[str, int]] # "Number of examples in each batch." - learning_rate_multiplier: Optional[ - Union[str, float] - ] # Scaling factor for the learning rate - n_epochs: Optional[Union[str, int]] # "The number of epochs to train the model for" +class Hyperparameters(BaseModel): + batch_size: Optional[Union[str, int]] = None # "Number of examples in each batch." + learning_rate_multiplier: Optional[Union[str, float]] = ( + None # Scaling factor for the learning rate + ) + n_epochs: Optional[Union[str, int]] = ( + None # "The number of epochs to train the model for" + ) -class FineTuningJobCreate(TypedDict): +class FineTuningJobCreate(BaseModel): """ FineTuningJobCreate - Create a fine-tuning job @@ -489,16 +490,16 @@ class FineTuningJobCreate(TypedDict): model: str # "The name of the model to fine-tune." training_file: str # "The ID of an uploaded file that contains training data." - hyperparameters: Optional[ - Hyperparameters - ] # "The hyperparameters used for the fine-tuning job." - suffix: Optional[ - str - ] # "A string of up to 18 characters that will be added to your fine-tuned model name." - validation_file: Optional[ - str - ] # "The ID of an uploaded file that contains validation data." - integrations: Optional[ - List[str] - ] # "A list of integrations to enable for your fine-tuning job." - seed: Optional[int] # "The seed controls the reproducibility of the job." + hyperparameters: Optional[Hyperparameters] = ( + None # "The hyperparameters used for the fine-tuning job." + ) + suffix: Optional[str] = ( + None # "A string of up to 18 characters that will be added to your fine-tuned model name." + ) + validation_file: Optional[str] = ( + None # "The ID of an uploaded file that contains validation data." + ) + integrations: Optional[List[str]] = ( + None # "A list of integrations to enable for your fine-tuning job." + ) + seed: Optional[int] = None # "The seed controls the reproducibility of the job."