chore(forge): Upgrade OpenAI client lib and LiteLLM from v0 to v1

* Update `openai` dependency from `^0.27.8` to `^1.7.2`
* Update `litellm` dependency from `^0.1.821` to `^1.17.9`
* Migrate llm.py from OpenAI module-level client to client instance
* Update return types in llm.py for new OpenAI and LiteLLM versions
   * Also remove `Exception` as a return type because they are raised, not returned
   * Update tutorials/003_crafting_agent_logic.md accordingly

Note: this changes the output types of the functions in `forge.llm`: `chat_completion_request`, `create_embedding_request`, `transcribe_audio`
This commit is contained in:
Reinier van der Leer
2024-01-16 16:14:52 +01:00
parent 0a4185a919
commit f0ede64ded
4 changed files with 1345 additions and 797 deletions

View File

@@ -1,18 +1,18 @@
import typing
from pathlib import Path
import openai
from litellm import AuthenticationError, InvalidRequestError, ModelResponse, acompletion
from openai import OpenAI
from openai.types import CreateEmbeddingResponse
from openai.types.audio import Transcription
from tenacity import retry, stop_after_attempt, wait_random_exponential
from .sdk.forge_log import ForgeLogger
from litellm import completion, acompletion, AuthenticationError, InvalidRequestError
LOG = ForgeLogger(__name__)
@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))
async def chat_completion_request(
model, messages, **kwargs
) -> typing.Union[typing.Dict[str, typing.Any], Exception]:
async def chat_completion_request(model, messages, **kwargs) -> ModelResponse:
"""Generate a response to a list of messages using OpenAI's API"""
try:
kwargs["model"] = model
@@ -22,8 +22,10 @@ async def chat_completion_request(
return resp
except AuthenticationError as e:
LOG.exception("Authentication Error")
raise
except InvalidRequestError as e:
LOG.exception("Invalid Request Error")
raise
except Exception as e:
LOG.error("Unable to generate ChatCompletion response")
LOG.error(f"Exception: {e}")
@@ -33,12 +35,12 @@ async def chat_completion_request(
@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))
async def create_embedding_request(
messages, model="text-embedding-ada-002"
) -> typing.Union[typing.Dict[str, typing.Any], Exception]:
) -> CreateEmbeddingResponse:
"""Generate an embedding for a list of messages using OpenAI's API"""
try:
return await openai.Embedding.acreate(
return OpenAI().embeddings.create(
input=[f"{m['role']}: {m['content']}" for m in messages],
engine=model,
model=model,
)
except Exception as e:
LOG.error("Unable to generate ChatCompletion response")
@@ -47,12 +49,12 @@ async def create_embedding_request(
@retry(wait=wait_random_exponential(min=1, max=40), stop=stop_after_attempt(3))
async def transcribe_audio(
audio_file: str,
) -> typing.Union[typing.Dict[str, typing.Any], Exception]:
async def transcribe_audio(audio_file: Path) -> Transcription:
"""Transcribe an audio file using OpenAI's API"""
try:
return await openai.Audio.transcribe(model="whisper-1", file=audio_file)
return OpenAI().audio.transcriptions.create(
model="whisper-1", file=audio_file.open(mode="rb")
)
except Exception as e:
LOG.error("Unable to generate ChatCompletion response")
LOG.error(f"Exception: {e}")

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ packages = [{ include = "forge" }]
[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
openai = "^0.27.8"
openai = "^1.7.2"
tenacity = "^8.2.2"
sqlalchemy = "^2.0.19"
aiohttp = "^3.8.5"
@@ -20,7 +20,7 @@ python-multipart = "^0.0.6"
toml = "^0.10.2"
jinja2 = "^3.1.2"
uvicorn = "^0.23.2"
litellm = "^0.1.821"
litellm = "^1.17.9"
duckduckgo-search = "^4.0.0"
selenium = "^4.13.0"
bs4 = "^0.0.1"

View File

@@ -242,7 +242,7 @@ try:
}
# Get the LLM's response and interpret it
chat_response = await chat_completion_request(**chat_completion_kwargs)
answer = json.loads(chat_response["choices"][0]["message"]["content"])
answer = json.loads(chat_response.choices[0].message.content)
# Log the answer for reference
LOG.info(pprint.pformat(answer))
@@ -403,7 +403,7 @@ async def execute_step(self, task_id: str, step_request: StepRequestBody) -> Ste
}
# Make the chat completion request and parse the response
chat_response = await chat_completion_request(**chat_completion_kwargs)
answer = json.loads(chat_response["choices"][0]["message"]["content"])
answer = json.loads(chat_response.choices[0].message.content)
# Log the answer for debugging purposes
LOG.info(pprint.pformat(answer))