docstr: Separate method to account for both legacy and new endpoints.
  Currently, `OpenAIHandler` uses the legacy endpoint.
  Others, like `AnyscaleEndpointsHandler`, use the new endpoint.
docstr_tok: ["Separate","method","to","account","for","both","legacy","and","new","endpoints",".","Currently",",","`","OpenAIHandler","`","uses","the","legacy","endpoint",".","Others",",","like","`","AnyscaleEndpointsHandler","`",",","use","the","new","endpoint","."]
code: def _ft_call(self, ft_params, client, hour_budget):
      """
      Separate method to account for both legacy and new endpoints.
      Currently, `OpenAIHandler` uses the legacy endpoint.
      Others, like `AnyscaleEndpointsHandler`, use the new endpoint.
      """
      ft_result = client.fine_tuning.jobs.create(
          **{k: v for k, v in ft_params.items() if v is not None}
      )
  
      @retry_with_exponential_backoff(
          hour_budget=hour_budget,
      )
      def _check_ft_status(model_id):
          ft_retrieved = client.fine_tuning.jobs.retrieve(
              fine_tuning_job_id=model_id
          )
          if ft_retrieved.status in (
              "succeeded",
              "failed",
              "cancelled",
          ):
              return ft_retrieved
          else:
              raise PendingFT("Fine-tuning still pending!")
  
      ft_stats = _check_ft_status(ft_result.id)
  
      if ft_stats.status != "succeeded":
          raise Exception(
              f"Fine-tuning did not complete successfully (status: {ft_stats.status}). Error message: {ft_stats.events[-1].message}"
          )  # noqa
  
      result_file_id = client.fine_tuning.jobs.retrieve(
          fine_tuning_job_id=ft_result.id
      ).result_files[0]
      if hasattr(result_file_id, "id"):
          result_file_id = result_file_id.id  # legacy endpoint
  
      return ft_stats, result_file_id
  
imports: import os
  import math
  import json
  import shutil
  import tempfile
  import datetime
  import textwrap
  import subprocess
  import concurrent.futures
  from typing import Optional, Dict
  import openai
  from openai import OpenAI
  import numpy
  import pandas
  from mindsdb.utilities.hooks import before_openai_query, after_openai_query
  from mindsdb.utilities import log
  from mindsdb.integrations.libs.base import BaseMLEngine
  from mindsdb.integrations.handlers.openai_handler.helpers import retry_with_exponential_backoff, truncate_msgs_for_token_limit, get_available_models, PendingFT
  from mindsdb.integrations.handlers.openai_handler.constants import CHAT_MODELS, IMAGE_MODELS, FINETUNING_MODELS, OPENAI_API_BASE
  from mindsdb.integrations.utilities.handler_utils import get_api_key
  from mindsdb.integrations.libs.llm_utils import get_completed_prompts
label_desc: Use image node_id 12 for calling the OpenAIHandler obj's underlying member method code with example usage: obj._ft_call(ft_params, client, hour_budget) and returns: ft_stats,  result_file_id, ft_retrieved