Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

Before running the sample:

pip install azure-ai-projects>=2.0.0b1 python-dotenv azure-mgmt-cognitiveservices
pip install azure-ai-projects>=2.0.0b1 python-dotenv azure-mgmt-cognitiveservices httpx

Set these environment variables with your own values:
1) AZURE_AI_PROJECT_ENDPOINT - Required. The Azure AI Project endpoint, as found in the overview page of your
Expand All @@ -33,6 +33,7 @@

import os
import time
import httpx
from dotenv import load_dotenv
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
Expand All @@ -53,8 +54,8 @@

# For Deployment and inferencing on model
subscription_id = os.environ["AZURE_AI_PROJECTS_AZURE_SUBSCRIPTION_ID"]
resource_group = os.environ["AZURE_AI_PROJECTS_AZURE_RESOURCE_GROUP"]
account_name = os.environ["AZURE_AI_PROJECTS_AZURE_AOAI_ACCOUNT"]
resource_group = os.environ.get("AZURE_AI_PROJECTS_AZURE_RESOURCE_GROUP", "")
account_name = os.environ.get("AZURE_AI_PROJECTS_AZURE_AOAI_ACCOUNT", "")


def pause_job(openai_client, job_id):
Expand Down Expand Up @@ -160,6 +161,155 @@ def retrieve_job(openai_client, job_id):
print(retrieved_job)


def wait_for_file_processing(credential, endpoint, file_id, poll_interval_seconds=2, max_wait_seconds=300):
"""Wait for an imported file to be processed.

This is a custom implementation for files imported via the Azure-specific endpoint.

Args:
credential: Azure credential for authentication.
endpoint: The Azure AI Project endpoint.
file_id: The file ID to check.
poll_interval_seconds: How often to poll for status (default 2 seconds).
max_wait_seconds: Maximum time to wait (default 300 seconds).

Returns:
The file status when processed or error.
"""
import_url = f"{endpoint}/openai/files/{file_id}?api-version=2025-11-15-preview"
token = credential.get_token("https://ai.azure.com/.default")

start_time = time.time()
while True:
with httpx.Client() as http_client:
response = http_client.get(
import_url,
headers={"Authorization": f"Bearer {token.token}"},
)
if response.status_code == 200:
result = response.json()
status = result.get("status", "unknown")
print(f"File {file_id} status: {status}")

if status == "processed":
return result
elif status == "error":
raise Exception(f"File processing failed: {result}")

if time.time() - start_time > max_wait_seconds:
raise TimeoutError(f"Timeout waiting for file {file_id} to process")

time.sleep(poll_interval_seconds)


def import_file_from_url(credential, endpoint, filename, content_url):
"""Import a file from a URL (Azure Blob with SAS token or public URL) for fine-tuning.

This uses the Azure-specific /openai/files/import endpoint.
Useful when files are stored in Azure Blob Storage or publicly accessible locations.

Args:
credential: Azure credential for authentication.
endpoint: The Azure AI Project endpoint.
filename: Name for the imported file.
content_url: URL to import from (Azure Blob URL with SAS token or public URL).

Returns:
The file ID of the imported file.
"""
import_url = f"{endpoint}/openai/files/import?api-version=2025-11-15-preview"

import_request = {"filename": filename, "purpose": "fine-tune", "content_url": content_url}

token = credential.get_token("https://ai.azure.com/.default")

with httpx.Client(timeout=60.0) as http_client:
response = http_client.post(
import_url,
json=import_request,
headers={"Authorization": f"Bearer {token.token}", "Content-Type": "application/json"},
)
response.raise_for_status()

result = response.json()
file_id = result["id"]
print(f"Imported file {filename}: {file_id}")
return file_id


def import_files_from_url_and_create_job(openai_client, credential, training_file_url, validation_file_url):
"""Import files from URLs and create a fine-tuning job.

This demonstrates importing training and validation files from URLs
(e.g., Azure Blob Storage with SAS tokens or public URLs like GitHub raw files).
"""
print("Importing training file from URL...")
train_file_id = import_file_from_url(credential, endpoint, "imported_training_set.jsonl", training_file_url)

print("Importing validation file from URL...")
validation_file_id = import_file_from_url(credential, endpoint, "imported_validation_set.jsonl", validation_file_url)

print("Waiting for imported files to be processed...")
# Now that we use the local SDK with correct base_url, standard OpenAI methods should work
openai_client.files.wait_for_processing(train_file_id)
openai_client.files.wait_for_processing(validation_file_id)

print("Creating supervised fine-tuning job with imported files...")
fine_tuning_job = openai_client.fine_tuning.jobs.create(
training_file=train_file_id,
validation_file=validation_file_id,
model=model_name,
method={
"type": "supervised",
"supervised": {"hyperparameters": {"n_epochs": 3, "batch_size": 1, "learning_rate_multiplier": 1.0}},
},
extra_body={"trainingType": "Standard"},
)
print(f"Created fine-tuning job with imported files: {fine_tuning_job.id}")
print(f"Status: {fine_tuning_job.status}")
return fine_tuning_job


def create_job_with_auto_deployment(openai_client, train_file_id, validation_file_id, deployment_sku=0):
"""Create a fine-tuning job with auto-deployment enabled.

When the job completes successfully, Azure OpenAI will automatically deploy the fine-tuned model.

Args:
openai_client: The OpenAI client.
train_file_id: The training file ID.
validation_file_id: The validation file ID.
deployment_sku: 0 = Developer tier, 2 = GlobalStandard tier.

Returns:
The fine-tuning job object.
"""
sku_name = "Developer" if deployment_sku == 0 else "GlobalStandard"
print(f"Creating fine-tuning job with auto-deployment ({sku_name} tier)...")

fine_tuning_job = openai_client.fine_tuning.jobs.create(
training_file=train_file_id,
validation_file=validation_file_id,
model=model_name,
method={
"type": "supervised",
"supervised": {"hyperparameters": {"n_epochs": 3, "batch_size": 1, "learning_rate_multiplier": 1.0}},
},
extra_body={
"trainingType": "Standard",
# Azure-specific: Enable auto-deployment when training completes
"inference_configs": {
"auto_inference_enabled": True,
"inference_sku": deployment_sku, # 0 = Developer, 2 = GlobalStandard
},
},
)
print(f"Created fine-tuning job with auto-deployment: {fine_tuning_job.id}")
print(f"Status: {fine_tuning_job.status}")
print("Model will be automatically deployed when training completes.")
return fine_tuning_job


def main() -> None:
with (
DefaultAzureCredential() as credential,
Expand Down Expand Up @@ -214,6 +364,25 @@ def main() -> None:

# infer(openai_client, deployment_name)

# === Import files from URL examples ===
# Import from Azure Blob Storage with SAS token:
# training_url = os.environ.get("FT_TRAINING_FILE_URL") # Azure Blob URL with SAS token
# validation_url = os.environ.get("FT_VALIDATION_FILE_URL") # Azure Blob URL with SAS token
# import_files_from_url_and_create_job(openai_client, credential, training_url, validation_url)

# Import from public URL (e.g., GitHub raw files):
# base_url = "https://raw.githubusercontent.com/azure-ai-foundry/fine-tuning/refs/heads/main/Sample_Datasets/Supervised_Fine_Tuning/Tool-Calling"
# training_url = f"{base_url}/stock_toolcall_training.jsonl"
# validation_url = f"{base_url}/stock_toolcall_validation.jsonl"
# import_files_from_url_and_create_job(openai_client, credential, training_url, validation_url)

# === Auto-deployment examples ===
# Create job with auto-deployment (Developer tier - lower cost, suitable for testing):
# create_job_with_auto_deployment(openai_client, train_file.id, validation_file.id, deployment_sku=0)

# Create job with auto-deployment (GlobalStandard tier - highest priority):
# create_job_with_auto_deployment(openai_client, train_file.id, validation_file.id, deployment_sku=2)


if __name__ == "__main__":
main()
Loading