Skip to content

Commit 0d503ee

Browse files
jwmuellerelisno
andauthored
better form response str (#98)
Co-authored-by: Elías Snorrason <eliassno@gmail.com>
1 parent 7e9c12e commit 0d503ee

File tree

5 files changed

+130
-8
lines changed

5 files changed

+130
-8
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.1.20] - 2025-07-28
11+
12+
### Changed
13+
14+
- Updated `TLMChatCompletion.score()` to use `form_response_string_chat_completions` instead of `form_response_string_chat_completions_api`
15+
1016
## [1.1.19] - 2025-07-25
1117

1218
### Added
@@ -273,7 +279,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
273279
- Release of the Cleanlab TLM Python client.
274280

275281

276-
[Unreleased]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.19...HEAD
282+
[Unreleased]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.20...HEAD
283+
[1.1.20]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.19...v1.1.20
277284
[1.1.19]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.18...v1.1.19
278285
[1.1.18]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.17...v1.1.18
279286
[1.1.17]: https://github.com/cleanlab/cleanlab-tlm/compare/v1.1.16...v1.1.17

src/cleanlab_tlm/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# SPDX-License-Identifier: MIT
2-
__version__ = "1.1.19"
2+
__version__ = "1.1.20"

src/cleanlab_tlm/utils/chat.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
1010

1111
if TYPE_CHECKING:
12-
from openai.types.chat import ChatCompletionMessage, ChatCompletionMessageParam
12+
from openai.types.chat import ChatCompletion, ChatCompletionMessage, ChatCompletionMessageParam
1313

1414
# Define message prefixes
1515
_SYSTEM_PREFIX = "System: "
@@ -443,11 +443,36 @@ def form_prompt_string(
443443
)
444444

445445

446+
def form_response_string_chat_completions(response: "ChatCompletion") -> str:
447+
"""Form a single string representing the response, out of the raw response object returned by OpenAI's Chat Completions API.
448+
449+
This function extracts the assistant's response message from a ChatCompletion object
450+
and formats it into a single string representation using the Chat Completions API format.
451+
It handles both text content and tool calls, formatting them consistently with the
452+
format used in other functions in this module.
453+
454+
Args:
455+
response (ChatCompletion): A ChatCompletion object returned by OpenAI's
456+
chat.completions.create(). The function uses the first choice
457+
from the response (response.choices[0].message).
458+
459+
Returns:
460+
str: A formatted string containing the response content and any tool calls.
461+
Tool calls are formatted as XML tags containing JSON with function
462+
name and arguments, consistent with the format used in form_prompt_string.
463+
464+
See also:
465+
[form_response_string_chat_completions_api](#function-form_response_string_chat_completions_api)
466+
"""
467+
response_msg = response.choices[0].message
468+
return form_response_string_chat_completions_api(response_msg)
469+
470+
446471
def form_response_string_chat_completions_api(response: Union[dict[str, Any], "ChatCompletionMessage"]) -> str:
447472
"""
448-
Format an assistant response message dictionary from the Chat Completions API into a single string.
473+
Form a single string representing the response, out of an assistant response message dictionary in Chat Completions API format.
449474
450-
Given a ChatCompletion object `response` from `chat.completions.create()`,
475+
Given a ChatCompletion object `response` from OpenAI's `chat.completions.create()`,
451476
this function can take either a ChatCompletionMessage object from `response.choices[0].message`
452477
or a dictionary from `response.choices[0].message.to_dict()`.
453478

src/cleanlab_tlm/utils/chat_completions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
)
1717
from cleanlab_tlm.internal.types import TLMQualityPreset
1818
from cleanlab_tlm.tlm import TLM, TLMOptions, TLMScore
19-
from cleanlab_tlm.utils.chat import _form_prompt_chat_completions_api, form_response_string_chat_completions_api
19+
from cleanlab_tlm.utils.chat import _form_prompt_chat_completions_api, form_response_string_chat_completions
2020

2121
if TYPE_CHECKING:
2222
from openai.types.chat import ChatCompletion, ChatCompletionMessage
@@ -114,7 +114,7 @@ def score(
114114
tools = openai_kwargs.get("tools", None)
115115

116116
prompt_text = _form_prompt_chat_completions_api(messages, tools)
117-
response_text = form_response_string_chat_completions_api(response=self._get_response_message(response))
117+
response_text = form_response_string_chat_completions(response=response)
118118

119119
return cast(TLMScore, self._tlm.get_trustworthiness_score(prompt_text, response_text))
120120

tests/test_chat.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
from typing import TYPE_CHECKING, Any, cast
22

33
import pytest
4-
from openai.types.chat import ChatCompletionMessage
4+
from openai.types.chat import ChatCompletion, ChatCompletionMessage
5+
from openai.types.chat.chat_completion import Choice
56
from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall, Function
67

78
from cleanlab_tlm.utils.chat import (
89
_form_prompt_chat_completions_api,
910
_form_prompt_responses_api,
1011
form_prompt_string,
12+
form_response_string_chat_completions,
1113
form_response_string_chat_completions_api,
1214
)
1315

@@ -1589,3 +1591,91 @@ def test_form_response_string_chat_completions_api_chatcompletion_message_none_c
15891591
expected = ""
15901592
result = form_response_string_chat_completions_api(message)
15911593
assert result == expected
1594+
1595+
1596+
def test_form_response_string_chat_completions_just_content() -> None:
1597+
"""Test form_response_string_chat_completions with ChatCompletion containing just content."""
1598+
1599+
content = "Hello, how can I help you today?"
1600+
1601+
message = ChatCompletionMessage(role="assistant", content=content)
1602+
response = ChatCompletion(
1603+
id="test",
1604+
choices=[
1605+
Choice(
1606+
index=0,
1607+
message=message,
1608+
finish_reason="stop",
1609+
)
1610+
],
1611+
created=1234567890,
1612+
model="test-model",
1613+
object="chat.completion",
1614+
)
1615+
1616+
result = form_response_string_chat_completions(response)
1617+
assert result == content
1618+
1619+
assert result == form_response_string_chat_completions_api(message)
1620+
1621+
1622+
def test_form_response_string_chat_completions_multiple_choices() -> None:
1623+
"""Test form_response_string_chat_completions with ChatCompletion containing multiple choices."""
1624+
1625+
content_first = "Hello, how can I help you today?"
1626+
content_second = "Hi there! What can I do for you?"
1627+
1628+
message_first = ChatCompletionMessage(role="assistant", content=content_first)
1629+
message_second = ChatCompletionMessage(role="assistant", content=content_second)
1630+
response = ChatCompletion(
1631+
id="test",
1632+
choices=[
1633+
Choice(
1634+
index=0,
1635+
message=message_first,
1636+
finish_reason="stop",
1637+
),
1638+
Choice(
1639+
index=1,
1640+
message=message_second,
1641+
finish_reason="stop",
1642+
),
1643+
],
1644+
created=1234567890,
1645+
model="test-model",
1646+
object="chat.completion",
1647+
)
1648+
1649+
result = form_response_string_chat_completions(response)
1650+
assert result == content_first
1651+
1652+
assert result == form_response_string_chat_completions_api(message_first)
1653+
1654+
1655+
def test_form_response_string_chat_completions_uses_api_function() -> None:
1656+
"""Test that form_response_string_chat_completions calls form_response_string_chat_completions_api."""
1657+
from unittest.mock import patch
1658+
1659+
message = ChatCompletionMessage(role="assistant", content="Test response")
1660+
response = ChatCompletion(
1661+
id="test",
1662+
choices=[
1663+
Choice(
1664+
index=0,
1665+
message=message,
1666+
finish_reason="stop",
1667+
)
1668+
],
1669+
created=1234567890,
1670+
model="test-model",
1671+
object="chat.completion",
1672+
)
1673+
1674+
# Mock the api function and test that it's called
1675+
with patch("cleanlab_tlm.utils.chat.form_response_string_chat_completions_api") as mock_api_func:
1676+
mock_api_func.return_value = "Mocked response"
1677+
1678+
result = form_response_string_chat_completions(response)
1679+
1680+
mock_api_func.assert_called_once_with(message)
1681+
assert result == "Mocked response"

0 commit comments

Comments
 (0)