Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing improper text-completion output in completion() and acompletion() #8532

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
20 changes: 14 additions & 6 deletions litellm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ async def acompletion(
if (
custom_llm_provider == "text-completion-openai"
or custom_llm_provider == "text-completion-codestral"
) and isinstance(response, TextCompletionResponse):
) and isinstance(response, TextCompletionResponse) and not kwargs.get("text_completion", False):
response = litellm.OpenAITextCompletionConfig().convert_to_chat_model_response_object(
response_object=response,
model_response_object=litellm.ModelResponse(),
Expand Down Expand Up @@ -1533,13 +1533,13 @@ def completion( # type: ignore # noqa: PLR0915
logger_fn=logger_fn,
timeout=timeout, # type: ignore
)

if (
optional_params.get("stream", False) is False
and acompletion is False
not optional_params.get("stream", False)
and isinstance(_response, TextCompletionResponse)
and text_completion is False
):
# convert to chat completion response
# convert to ModelResponse unless text_completion is explicitly set to True
_response = litellm.OpenAITextCompletionConfig().convert_to_chat_model_response_object(
response_object=_response, model_response_object=model_response
)
Expand Down Expand Up @@ -2558,7 +2558,15 @@ def completion( # type: ignore # noqa: PLR0915
api_key=api_key,
timeout=timeout,
)

if (
not optional_params.get("stream", False)
and isinstance(_model_response, TextCompletionResponse)
and text_completion is False
):
# convert to ModelResponse unless text_completion is explicitly set to True
_model_response = litellm.OpenAITextCompletionConfig().convert_to_chat_model_response_object(
response_object=_model_response, model_response_object=model_response
)
if (
"stream" in optional_params
and optional_params["stream"] is True
Expand Down
252 changes: 252 additions & 0 deletions tests/local_testing/test_text_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -4285,3 +4285,255 @@ def test_text_completion_with_echo(stream):
print(chunk)
else:
assert isinstance(response, TextCompletionResponse)



@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.new_event_loop()
yield loop
# Gather any pending tasks and wait for them to finish.
pending = asyncio.all_tasks(loop)
if pending:
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
loop.close()
def test_response_model_codestral_completion():
try:
print("INFO: Starting Codestral completion test")
response = litellm.completion(
model="text-completion-codestral/codestral-latest",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
)
print("INFO: Codestral completion test successful")

# Type assertion
assert isinstance(response, litellm.ModelResponse), (
f"Expected litellm.ModelResponse, got {type(response)}"
)

# Content validation
response_content = response.choices[0].message.content.lower()
assert "hello, world" in response_content, (
f"Expected response to contain 'Hello, world', but got: {response_content}"
)

return response
except Exception as e:
print(f"ERROR: Error in Codestral completion test: {str(e)}")
raise


@pytest.mark.asyncio
async def test_response_model_codestral_acompletion():
try:
print("INFO: Starting Codestral async completion test")
response = await litellm.acompletion(
model="text-completion-codestral/codestral-latest",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
)
print("INFO: Codestral async completion test successful")

# Type assertion
assert isinstance(response, litellm.ModelResponse), (
f"Expected litellm.ModelResponse, got {type(response)}"
)

# Content validation
response_content = response.choices[0].message.content.lower()
assert "hello, world" in response_content, (
f"Expected response to contain 'Hello, world', but got: {response_content}"
)

return response
except Exception as e:
print(f"ERROR: Error in Codestral async completion test: {str(e)}")
raise


def test_response_model_openai_completion():
try:
print("INFO: Starting OpenAI completion test")
response = litellm.completion(
model="text-completion-openai/gpt-3.5-turbo-instruct",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
)
print("INFO: OpenAI completion test successful")

# Type assertion
assert isinstance(response, litellm.ModelResponse), (
f"Expected litellm.ModelResponse, got {type(response)}"
)

# Content validation
response_content = response.choices[0].message.content.lower()
assert "hello, world" in response_content, (
f"Expected response to contain 'Hello, world', but got: {response_content}"
)

return response
except Exception as e:
print(f"ERROR: Error in OpenAI completion test: {str(e)}")
raise


@pytest.mark.asyncio
async def test_response_model_openai_acompletion():
try:
print("INFO: Starting OpenAI async completion test")
response = await litellm.acompletion(
model="text-completion-openai/gpt-3.5-turbo-instruct",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
)
print("INFO: OpenAI async completion test successful")

# Type assertion
assert isinstance(response, litellm.ModelResponse), (
f"Expected litellm.ModelResponse, got {type(response)}"
)

# Content validation
response_content = response.choices[0].message.content.lower()
assert "hello, world" in response_content, (
f"Expected response to contain 'Hello, world', but got: {response_content}"
)

return response
except Exception as e:
print(f"ERROR: Error in OpenAI async completion test: {str(e)}")
raise


def test_response_model_codestral_completion_text():
try:
print("INFO: Starting Codestral text completion test")
response = litellm.completion(
model="text-completion-codestral/codestral-latest",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
text_completion=True
)
print("INFO: Codestral text completion test successful")
assert isinstance(response, TextCompletionResponse), (
f"Expected TextCompletionResponse, got {type(response)}"
)
assert "hello, world" in response.choices[0].text.lower(), (
f"Expected response to contain 'Hello, world', but got: {response.choices[0].text}"
)
return response
except Exception as e:
print(f"ERROR: Error in Codestral text completion test: {str(e)}")
raise


@pytest.mark.asyncio
async def test_response_model_codestral_acompletion_text(event_loop):
try:
print("INFO: Starting Codestral async text completion test")
asyncio.set_event_loop(event_loop)
response = await litellm.acompletion(
model="text-completion-codestral/codestral-latest",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
text_completion=True
)
print("INFO: Codestral async text completion test successful")
assert isinstance(response, TextCompletionResponse), (
f"Expected TextCompletionResponse, got {type(response)}"
)
assert "hello, world" in response.choices[0].text.lower(), (
f"Expected response to contain 'Hello, world', but got: {response.choices[0].text}"
)
return response
except Exception as e:
print(f"ERROR: Error in Codestral async text completion test: {str(e)}")
raise


def test_response_model_openai_completion_text():
try:
print("INFO: Starting OpenAI text completion test")
response = litellm.completion(
model="text-completion-openai/gpt-3.5-turbo-instruct",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
text_completion=True
)
print("INFO: OpenAI text completion test successful")
assert isinstance(response, TextCompletionResponse), (
f"Expected TextCompletionResponse, got {type(response)}"
)
assert "hello, world" in response.choices[0].text.lower(), (
f"Expected response to contain 'Hello, world', but got: {response.choices[0].text}"
)
return response
except Exception as e:
print(f"ERROR: Error in OpenAI text completion test: {str(e)}")
raise


@pytest.mark.asyncio
async def test_response_model_openai_acompletion_text(event_loop):
try:
print("INFO: Starting OpenAI async text completion test")
asyncio.set_event_loop(event_loop)
response = await litellm.acompletion(
model="text-completion-openai/gpt-3.5-turbo-instruct",
messages=[
{
"role": "user",
"content": "Repeat this back to me: 'Hello, world!'",
}
],
max_tokens=20,
text_completion=True
)
print("INFO: OpenAI async text completion test successful")
assert isinstance(response, TextCompletionResponse), (
f"Expected TextCompletionResponse, got {type(response)}"
)
assert "hello, world" in response.choices[0].text.lower(), (
f"Expected response to contain 'Hello, world', but got: {response.choices[0].text}"
)
return response
except Exception as e:
print(f"ERROR: Error in OpenAI async text completion test: {str(e)}")
raise