Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ahnuf-karim-chowdhury_car-dealership-ai-voice-assistant.json
T
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
31 KiB
JSON

{"repo": "ahnuf-karim-chowdhury/car-dealership-ai-voice-assistant", "files": {"ai/Lib/site-packages/livekit/plugins/openai/llm.py": {"c64bfd78ded7309c2e37311407723a1dcc718404": {"t": 1786068484.640317, "n": 29991, "h": "6b942407773ed29b", "c": "# Copyright 2023 LiveKit, Inc.\n#\n\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom __future__ import annotations\n\nimport asyncio\nimport datetime\nimport os\nfrom dataclasses import dataclass\nfrom typing import Any, Literal, MutableSet, Union\n\nimport aiohttp\nimport httpx\nfrom livekit.agents import (\n APIConnectionError,\n APIStatusError,\n APITimeoutError,\n llm,\n)\nfrom livekit.agents.llm import (\n LLMCapabilities,\n ToolChoice,\n _create_ai_function_info,\n)\nfrom livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS, APIConnectOptions\n\nimport openai\nfrom openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam\nfrom openai.types.chat.chat_completion_chunk import Choice\n\nfrom ._oai_api import build_oai_function_description\nfrom .log import logger\nfrom .models import (\n CerebrasChatModels,\n ChatModels,\n DeepSeekChatModels,\n GroqChatModels,\n OctoChatModels,\n PerplexityChatModels,\n TelnyxChatModels,\n TogetherChatModels,\n VertexModels,\n XAIChatModels,\n)\nfrom .utils import AsyncAzureADTokenProvider, build_oai_message\n\n\n@dataclass\nclass LLMOptions:\n model: str | ChatModels\n user: str | None\n temperature: float | None\n parallel_tool_calls: bool | None\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\"\n store: bool | None = None\n metadata: dict[str, str] | None = None\n\n\nclass LLM(llm.LLM):\n def __init__(\n self,\n *,\n model: str | ChatModels = \"gpt-4o\",\n api_key: str | None = None,\n base_url: str | None = None,\n user: str | None = None,\n client: openai.AsyncClient | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n store: bool | None = None,\n metadata: dict[str, str] | None = None,\n ) -> None:\n \"\"\"\n Create a new instance of OpenAI LLM.\n\n ``api_key`` must be set to your OpenAI API key, either using the argument or by setting the\n ``OPENAI_API_KEY`` environmental variable.\n \"\"\"\n super().__init__(\n capabilities=LLMCapabilities(\n supports_choices_on_int=True,\n requires_persistent_functions=False,\n )\n )\n\n self._opts = LLMOptions(\n model=model,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n store=store,\n metadata=metadata,\n )\n self._client = client or openai.AsyncClient(\n api_key=api_key,\n base_url=base_url,\n max_retries=0,\n http_client=httpx.AsyncClient(\n timeout=httpx.Timeout(connect=15.0, read=5.0, write=5.0, pool=5.0),\n follow_redirects=True,\n limits=httpx.Limits(\n max_connections=50,\n max_keepalive_connections=50,\n keepalive_expiry=120,\n ),\n ),\n )\n self._running_fncs: MutableSet[asyncio.Task[Any]] = set()\n\n @staticmethod\n def with_azure(\n *,\n model: str | ChatModels = \"gpt-4o\",\n azure_endpoint: str | None = None,\n azure_deployment: str | None = None,\n api_version: str | None = None,\n api_key: str | None = None,\n azure_ad_token: str | None = None,\n azure_ad_token_provider: AsyncAzureADTokenProvider | None = None,\n organization: str | None = None,\n project: str | None = None,\n base_url: str | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n This automatically infers the following arguments from their corresponding environment variables if they are not provided:\n - `api_key` from `AZURE_OPENAI_API_KEY`\n - `organization` from `OPENAI_ORG_ID`\n - `project` from `OPENAI_PROJECT_ID`\n - `azure_ad_token` from `AZURE_OPENAI_AD_TOKEN`\n - `api_version` from `OPENAI_API_VERSION`\n - `azure_endpoint` from `AZURE_OPENAI_ENDPOINT`\n \"\"\"\n\n azure_client = openai.AsyncAzureOpenAI(\n max_retries=0,\n azure_endpoint=azure_endpoint,\n azure_deployment=azure_deployment,\n api_version=api_version,\n api_key=api_key,\n azure_ad_token=azure_ad_token,\n azure_ad_token_provider=azure_ad_token_provider,\n organization=organization,\n project=project,\n base_url=base_url,\n ) # type: ignore\n\n return LLM(\n model=model,\n client=azure_client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_cerebras(\n *,\n model: str | CerebrasChatModels = \"llama3.1-8b\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.cerebras.ai/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of Cerebras LLM.\n\n ``api_key`` must be set to your Cerebras API key, either using the argument or by setting\n the ``CEREBRAS_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"CEREBRAS_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_vertex(\n *,\n model: str | VertexModels = \"google/gemini-2.0-flash-exp\",\n project_id: str | None = None,\n location: str = \"us-central1\",\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of VertexAI LLM.\n\n `GOOGLE_APPLICATION_CREDENTIALS` environment variable must be set to the path of the service account key file.\n \"\"\"\n logger.warning(\n \"`openai.LLM.with_vertex()` is deprecated. Use `google.LLM()` instead.\"\n )\n project_id = project_id\n location = location\n _gac = os.environ.get(\"GOOGLE_APPLICATION_CREDENTIALS\")\n if _gac is None:\n logger.warning(\n \"`GOOGLE_APPLICATION_CREDENTIALS` environment variable is not set. please set it to the path of the service account key file. Otherwise, use any of the other Google Cloud auth methods.\"\n )\n\n try:\n from google.auth._default_async import default_async\n from google.auth.transport._aiohttp_requests import Request\n except ImportError:\n raise ImportError(\n \"Google Auth dependencies not found. Please install with: `pip install livekit-plugins-openai[vertex]`\"\n )\n\n class AuthTokenRefresher(openai.AsyncClient):\n def __init__(self, **kwargs: Any) -> None:\n self.creds, self.project = default_async(\n scopes=[\"https://www.googleapis.com/auth/cloud-platform\"]\n )\n project = project_id or self.project\n base_url = f\"https://{location}-aiplatform.googleapis.com/v1beta1/projects/{project}/locations/{location}/endpoints/openapi\"\n kwargs.update({\"base_url\": base_url})\n super().__init__(api_key=\"DUMMY\", **kwargs)\n self.refresh_threshold = 600 # 10 minutes\n\n def _token_needs_refresh(self) -> bool:\n if not self.creds or not self.creds.valid:\n return True\n expiry = self.creds.expiry\n if expiry is None:\n return True\n remaining = (expiry - datetime.datetime.utcnow()).total_seconds()\n return remaining < self.refresh_threshold\n\n async def _refresh_credentials(self) -> None:\n if self.creds and self.creds.valid and not self._token_needs_refresh():\n return\n async with aiohttp.ClientSession(auto_decompress=False) as session:\n auth_req = Request(session=session)\n await self.creds.refresh(auth_req)\n self.api_key = self.creds.token\n\n client = AuthTokenRefresher(\n max_retries=0,\n http_client=httpx.AsyncClient(\n timeout=httpx.Timeout(connect=15.0, read=5.0, write=5.0, pool=5.0),\n follow_redirects=True,\n limits=httpx.Limits(\n max_connections=50,\n max_keepalive_connections=50,\n keepalive_expiry=120,\n ),\n ),\n )\n\n vertex_llm = LLM(\n model=model,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n vertex_llm._capabilities = llm.LLMCapabilities(supports_choices_on_int=False)\n return vertex_llm\n\n @staticmethod\n def with_fireworks(\n *,\n model: str = \"accounts/fireworks/models/llama-v3p3-70b-instruct\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.fireworks.ai/inference/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of Fireworks LLM.\n\n ``api_key`` must be set to your Fireworks API key, either using the argument or by setting\n the ``FIREWORKS_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"FIREWORKS_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_x_ai(\n *,\n model: str | XAIChatModels = \"grok-2-public\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.x.ai/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of XAI LLM.\n\n ``api_key`` must be set to your XAI API key, either using the argument or by setting\n the ``XAI_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"XAI_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_groq(\n *,\n model: str | GroqChatModels = \"llama3-8b-8192\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.groq.com/openai/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of Groq LLM.\n\n ``api_key`` must be set to your Groq API key, either using the argument or by setting\n the ``GROQ_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"GROQ_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_deepseek(\n *,\n model: str | DeepSeekChatModels = \"deepseek-chat\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.deepseek.com/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of DeepSeek LLM.\n\n ``api_key`` must be set to your DeepSeek API key, either using the argument or by setting\n the ``DEEPSEEK_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"DEEPSEEK_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_octo(\n *,\n model: str | OctoChatModels = \"llama-2-13b-chat\",\n api_key: str | None = None,\n base_url: str | None = \"https://text.octoai.run/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of OctoAI LLM.\n\n ``api_key`` must be set to your OctoAI API key, either using the argument or by setting\n the ``OCTOAI_TOKEN`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"OCTOAI_TOKEN\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_ollama(\n *,\n model: str = \"llama3.1\",\n base_url: str | None = \"http://localhost:11434/v1\",\n client: openai.AsyncClient | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of Ollama LLM.\n \"\"\"\n\n return LLM(\n model=model,\n api_key=\"ollama\",\n base_url=base_url,\n client=client,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_perplexity(\n *,\n model: str | PerplexityChatModels = \"llama-3.1-sonar-small-128k-chat\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.perplexity.ai\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of PerplexityAI LLM.\n\n ``api_key`` must be set to your Perplexity API key, either using the argument or by setting\n the ``PERPLEXITY_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"PERPLEXITY_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_together(\n *,\n model: str | TogetherChatModels = \"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.together.xyz/v1\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of TogetherAI LLM.\n\n ``api_key`` must be set to your TogetherAI API key, either using the argument or by setting\n the ``TOGETHER_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"TOGETHER_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def with_telnyx(\n *,\n model: str | TelnyxChatModels = \"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n api_key: str | None = None,\n base_url: str | None = \"https://api.telnyx.com/v2/ai\",\n client: openai.AsyncClient | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n \"\"\"\n Create a new instance of Telnyx LLM.\n\n ``api_key`` must be set to your Telnyx API key, either using the argument or by setting\n the ``TELNYX_API_KEY`` environmental variable.\n \"\"\"\n api_key = _get_api_key(\"TELNYX_API_KEY\", api_key)\n return LLM(\n model=model,\n api_key=api_key,\n base_url=base_url,\n client=client,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n @staticmethod\n def create_azure_client(\n *,\n model: str | ChatModels = \"gpt-4o\",\n azure_endpoint: str | None = None,\n azure_deployment: str | None = None,\n api_version: str | None = None,\n api_key: str | None = None,\n azure_ad_token: str | None = None,\n azure_ad_token_provider: AsyncAzureADTokenProvider | None = None,\n organization: str | None = None,\n project: str | None = None,\n base_url: str | None = None,\n user: str | None = None,\n temperature: float | None = None,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]] = \"auto\",\n ) -> LLM:\n logger.warning(\"This alias is deprecated. Use LLM.with_azure() instead\")\n return LLM.with_azure(\n model=model,\n azure_endpoint=azure_endpoint,\n api_version=api_version,\n api_key=api_key,\n azure_ad_token=azure_ad_token,\n azure_ad_token_provider=azure_ad_token_provider,\n organization=organization,\n project=project,\n base_url=base_url,\n user=user,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n def chat(\n self,\n *,\n chat_ctx: llm.ChatContext,\n conn_options: APIConnectOptions = DEFAULT_API_CONNECT_OPTIONS,\n fnc_ctx: llm.FunctionContext | None = None,\n temperature: float | None = None,\n n: int | None = 1,\n parallel_tool_calls: bool | None = None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]]\n | None = None,\n ) -> \"LLMStream\":\n if parallel_tool_calls is None:\n parallel_tool_calls = self._opts.parallel_tool_calls\n\n if tool_choice is None:\n tool_choice = self._opts.tool_choice\n\n if temperature is None:\n temperature = self._opts.temperature\n\n return LLMStream(\n self,\n client=self._client,\n model=self._opts.model,\n user=self._opts.user,\n chat_ctx=chat_ctx,\n fnc_ctx=fnc_ctx,\n conn_options=conn_options,\n n=n,\n temperature=temperature,\n parallel_tool_calls=parallel_tool_calls,\n tool_choice=tool_choice,\n )\n\n\nclass LLMStream(llm.LLMStream):\n def __init__(\n self,\n llm: LLM,\n *,\n client: openai.AsyncClient,\n model: str | ChatModels,\n user: str | None,\n chat_ctx: llm.ChatContext,\n conn_options: APIConnectOptions,\n fnc_ctx: llm.FunctionContext | None,\n temperature: float | None,\n n: int | None,\n parallel_tool_calls: bool | None,\n tool_choice: Union[ToolChoice, Literal[\"auto\", \"required\", \"none\"]],\n ) -> None:\n super().__init__(\n llm, chat_ctx=chat_ctx, fnc_ctx=fnc_ctx, conn_options=conn_options\n )\n self._client = client\n self._model = model\n self._llm: LLM = llm\n\n self._user = user\n self._temperature = temperature\n self._n = n\n self._parallel_tool_calls = parallel_tool_calls\n self._tool_choice = tool_choice\n\n async def _run(self) -> None:\n if hasattr(self._llm._client, \"_refresh_credentials\"):\n await self._llm._client._refresh_credentials()\n\n # current function call that we're waiting for full completion (args are streamed)\n # (defined inside the _run method to make sure the state is reset for each run/attempt)\n self._oai_stream: openai.AsyncStream[ChatCompletionChunk] | None = None\n self._tool_call_id: str | None = None\n self._fnc_name: str | None = None\n self._fnc_raw_arguments: str | None = None\n self._tool_index: int | None = None\n retryable = True\n\n try:\n if self._fnc_ctx and len(self._fnc_ctx.ai_functions) > 0:\n tools = [\n build_oai_function_description(fnc, self._llm._capabilities)\n for fnc in self._fnc_ctx.ai_functions.values()\n ]\n else:\n tools = None\n\n opts: dict[str, Any] = {\n \"tools\": tools,\n \"parallel_tool_calls\": self._parallel_tool_calls if tools else None,\n \"tool_choice\": (\n {\"type\": \"function\", \"function\": {\"name\": self._tool_choice.name}}\n if isinstance(self._tool_choice, ToolChoice)\n else self._tool_choice\n )\n if tools is not None\n else None,\n \"temperature\": self._temperature,\n \"metadata\": self._llm._opts.metadata,\n \"store\": self._llm._opts.store,\n \"n\": self._n,\n \"stream\": True,\n \"stream_options\": {\"include_usage\": True},\n \"user\": self._user or openai.NOT_GIVEN,\n }\n # remove None values from the options\n opts = _strip_nones(opts)\n\n messages = _build_oai_context(self._chat_ctx, id(self))\n stream = await self._client.chat.completions.create(\n messages=messages,\n model=self._model,\n **opts,\n )\n\n async with stream:\n async for chunk in stream:\n for choice in chunk.choices:\n chat_chunk = self._parse_choice(chunk.id, choice)\n if chat_chunk is not None:\n retryable = False\n self._event_ch.send_nowait(chat_chunk)\n\n if chunk.usage is not None:\n usage = chunk.usage\n self._event_ch.send_nowait(\n llm.ChatChunk(\n request_id=chunk.id,\n usage=llm.CompletionUsage(\n completion_tokens=usage.completion_tokens,\n prompt_tokens=usage.prompt_tokens,\n total_tokens=usage.total_tokens,\n ),\n )\n )\n\n except openai.APITimeoutError:\n raise APITimeoutError(retryable=retryable)\n except openai.APIStatusError as e:\n raise APIStatusError(\n e.message,\n status_code=e.status_code,\n request_id=e.request_id,\n body=e.body,\n )\n except Exception as e:\n raise APIConnectionError(retryable=retryable) from e\n\n def _parse_choice(self, id: str, choice: Choice) -> llm.ChatChunk | None:\n delta = choice.delta\n\n # https://github.com/livekit/agents/issues/688\n # the delta can be None when using Azure OpenAI using content filtering\n if delta is None:\n return None\n\n if delta.tool_calls:\n # check if we have functions to calls\n for tool in delta.tool_calls:\n if not tool.function:\n continue # oai may add other tools in the future\n\n call_chunk = None\n if self._tool_call_id and tool.id and tool.index != self._tool_index:\n call_chunk = self._try_build_function(id, choice)\n\n if tool.function.name:\n self._tool_index = tool.index\n self._tool_call_id = tool.id\n self._fnc_name = tool.function.name\n self._fnc_raw_arguments = tool.function.arguments or \"\"\n elif tool.function.arguments:\n self._fnc_raw_arguments += tool.function.arguments # type: ignore\n\n if call_chunk is not None:\n return call_chunk\n\n if choice.finish_reason in (\"tool_calls\", \"stop\") and self._tool_call_id:\n # we're done with the tool calls, run the last one\n return self._try_build_function(id, choice)\n\n return llm.ChatChunk(\n request_id=id,\n choices=[\n llm.Choice(\n delta=llm.ChoiceDelta(content=delta.content, role=\"assistant\"),\n index=choice.index,\n )\n ],\n )\n\n def _try_build_function(self, id: str, choice: Choice) -> llm.ChatChunk | None:\n if not self._fnc_ctx:\n logger.warning(\"oai stream tried to run function without function context\")\n return None\n\n if self._tool_call_id is None:\n logger.warning(\n \"oai stream tried to run function but tool_call_id is not set\"\n )\n return None\n\n if self._fnc_name is None or self._fnc_raw_arguments is None:\n logger.warning(\n \"oai stream tried to call a function but raw_arguments and fnc_name are not set\"\n )\n return None\n\n fnc_info = _create_ai_function_info(\n self._fnc_ctx, self._tool_call_id, self._fnc_name, self._fnc_raw_arguments\n )\n\n self._tool_call_id = self._fnc_name = self._fnc_raw_arguments = None\n self._function_calls_info.append(fnc_info)\n\n return llm.ChatChunk(\n request_id=id,\n choices=[\n llm.Choice(\n delta=llm.ChoiceDelta(\n role=\"assistant\",\n tool_calls=[fnc_info],\n content=choice.delta.content,\n ),\n index=choice.index,\n )\n ],\n )\n\n\ndef _build_oai_context(\n chat_ctx: llm.ChatContext, cache_key: Any\n) -> list[ChatCompletionMessageParam]:\n return [build_oai_message(msg, cache_key) for msg in chat_ctx.messages] # type: ignore\n\n\ndef _strip_nones(data: dict[str, Any]) -> dict[str, Any]:\n return {k: v for k, v in data.items() if v is not None}\n\n\ndef _get_api_key(env_var: str, key: str | None) -> str:\n key = key or os.environ.get(env_var)\n if not key:\n raise ValueError(\n f\"{env_var} is required, either as argument or set {env_var} environmental variable\"\n )\n return key\n"}}}}