mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
feat(fine-tuning): address greptile review feedback (greploop iteration 4)
- Add cancel/retrieve overrides in AzureOpenAIFineTuningAPI to normalize responses - Expand _AZURE_STATUS_MAP to handle all known Azure statuses - Add "pending" to OpenAIFileObject.status allowed values - Fix async test mock to return awaitable LiteLLMFineTuningJob - Add test_openai_file_object_accepts_pending_status Made-with: Cursor
This commit is contained in:
parent
e937507637
commit
30af125ef2
5 changed files with 142 additions and 6 deletions
|
|
@ -42,6 +42,26 @@ class AzureOpenAIFineTuningAPI(OpenAIFineTuningAPI, BaseAzureLLM):
|
|||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
async def acancel_fine_tuning_job(
|
||||
self,
|
||||
fine_tuning_job_id: str,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
) -> LiteLLMFineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.cancel(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
async def aretrieve_fine_tuning_job(
|
||||
self,
|
||||
fine_tuning_job_id: str,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
) -> LiteLLMFineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.retrieve(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
def create_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
|
|
@ -93,6 +113,98 @@ class AzureOpenAIFineTuningAPI(OpenAIFineTuningAPI, BaseAzureLLM):
|
|||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
def cancel_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
fine_tuning_job_id: str,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
) -> Union[LiteLLMFineTuningJob, Coroutine[Any, Any, LiteLLMFineTuningJob]]:
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"Azure OpenAI client is not initialized. Make sure api_key is passed or AZURE_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, (AsyncOpenAI, AsyncAzureOpenAI)):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
return self.acancel_fine_tuning_job(
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
|
||||
response = cast(OpenAI, openai_client).fine_tuning.jobs.cancel(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
def retrieve_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
fine_tuning_job_id: str,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
) -> Union[LiteLLMFineTuningJob, Coroutine[Any, Any, LiteLLMFineTuningJob]]:
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"Azure OpenAI client is not initialized. Make sure api_key is passed or AZURE_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, (AsyncOpenAI, AsyncAzureOpenAI)):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
return self.aretrieve_fine_tuning_job(
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
|
||||
response = cast(OpenAI, openai_client).fine_tuning.jobs.retrieve(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return _litellm_fine_tuning_job_from_response(response, is_azure=True)
|
||||
|
||||
def get_openai_client(
|
||||
self,
|
||||
api_key: Optional[str],
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ from litellm.types.utils import LiteLLMFineTuningJob
|
|||
|
||||
_AZURE_STATUS_MAP = {
|
||||
"pending": "queued",
|
||||
"notRunning": "queued",
|
||||
"running": "running",
|
||||
"succeeded": "succeeded",
|
||||
"failed": "failed",
|
||||
"canceled": "cancelled",
|
||||
"canceling": "cancelled",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -315,11 +315,11 @@ class OpenAIFileObject(BaseModel):
|
|||
`fine-tune`, `fine-tune-results`, `vision`, and `user_data`.
|
||||
"""
|
||||
|
||||
status: Optional[Literal["uploaded", "processed", "error"]] = None
|
||||
status: Optional[Literal["uploaded", "processed", "error", "pending"]] = None
|
||||
"""Deprecated.
|
||||
|
||||
The current status of the file, which can be either `uploaded`, `processed`, or
|
||||
`error`.
|
||||
The current status of the file, which can be either `uploaded`, `processed`,
|
||||
`error`, or `pending` (Azure may return `pending` immediately after upload).
|
||||
"""
|
||||
|
||||
expires_at: Optional[int] = None
|
||||
|
|
|
|||
|
|
@ -616,11 +616,11 @@ async def test_mock_openai_retrieve_fine_tune_job():
|
|||
@pytest.mark.asyncio
|
||||
async def test_mock_azure_create_fine_tune_job_with_azure_specific_params():
|
||||
"""Test that Azure-specific parameters are passed through extra_body"""
|
||||
from openai import AsyncAzureOpenAI
|
||||
from openai.types.fine_tuning.fine_tuning_job import FineTuningJob
|
||||
from openai.types.fine_tuning.fine_tuning_job import Hyperparameters as OAIHyperparameters
|
||||
from litellm.types.utils import LiteLLMFineTuningJob
|
||||
|
||||
mock_response = FineTuningJob(
|
||||
mock_response = LiteLLMFineTuningJob(
|
||||
id="ft-azure-123",
|
||||
model="gpt-4.1-mini-2025-04-14",
|
||||
created_at=1677610602,
|
||||
|
|
@ -634,8 +634,11 @@ async def test_mock_azure_create_fine_tune_job_with_azure_specific_params():
|
|||
result_files=[],
|
||||
)
|
||||
|
||||
async def mock_async_create(*args, **kwargs):
|
||||
return mock_response
|
||||
|
||||
with patch("litellm.llms.azure.fine_tuning.handler.AzureOpenAIFineTuningAPI.create_fine_tuning_job") as mock_create:
|
||||
mock_create.return_value = mock_response
|
||||
mock_create.return_value = mock_async_create()
|
||||
|
||||
response = await litellm.acreate_fine_tuning_job(
|
||||
model="gpt-4.1-mini-2025-04-14",
|
||||
|
|
|
|||
|
|
@ -438,3 +438,18 @@ def test_normalize_fine_tuning_job_dict_openai_unchanged():
|
|||
data = {"organization_id": None, "result_files": None, "status": "pending"}
|
||||
out = _normalize_fine_tuning_job_dict(data, is_azure=False)
|
||||
assert out is data
|
||||
|
||||
|
||||
def test_openai_file_object_accepts_pending_status():
|
||||
from litellm.types.llms.openai import OpenAIFileObject
|
||||
|
||||
file_obj = OpenAIFileObject(
|
||||
id="file-123",
|
||||
bytes=1024,
|
||||
created_at=1677610602,
|
||||
filename="train.jsonl",
|
||||
object="file",
|
||||
purpose="fine-tune",
|
||||
status="pending",
|
||||
)
|
||||
assert file_obj.status == "pending"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue