Skip to content

Commit 0e83623

Browse files
authored
Merge pull request #11 from pamelafox/gpt4o
Add gpt 4o support
2 parents c630cbe + fd189c8 commit 0e83623

File tree

6 files changed

+56
-26
lines changed

6 files changed

+56
-26
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.1.4] - May 14, 2024
6+
7+
- Add support and tests for gpt-4o, which has a different tokenizer.
8+
59
## [0.1.3] - May 2, 2024
610

711
- Use openai type annotations for more precise type hints, and add a typing test.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "openai-messages-token-helper"
33
description = "A helper library for estimating tokens used by messages sent through OpenAI Chat Completions API."
4-
version = "0.1.3"
4+
version = "0.1.4"
55
authors = [{name = "Pamela Fox"}]
66
requires-python = ">=3.9"
77
readme = "README.md"

src/openai_messages_token_helper/model_helper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"gpt-4": 8100,
2222
"gpt-4-32k": 32000,
2323
"gpt-4v": 128000,
24+
"gpt-4o": 128000,
2425
}
2526

2627

tests/messages.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"content": "You are a bot.",
55
},
66
"count": 12,
7+
"count_omni": 12,
78
}
89

910
system_message = {
@@ -12,6 +13,7 @@
1213
"content": "You are a helpful, pattern-following assistant that translates corporate jargon into plain English.",
1314
},
1415
"count": 25,
16+
"count_omni": 24,
1517
}
1618

1719
system_message_long = {
@@ -20,6 +22,7 @@
2022
"content": "Assistant helps the company employees with their healthcare plan questions, and questions about the employee handbook. Be brief in your answers.",
2123
},
2224
"count": 31,
25+
"count_omni": 31,
2326
}
2427

2528
system_message_unicode = {
@@ -28,6 +31,7 @@
2831
"content": "á",
2932
},
3033
"count": 8,
34+
"count_omni": 8,
3135
}
3236

3337
system_message_with_name = {
@@ -37,6 +41,7 @@
3741
"content": "New synergies will help drive top-line growth.",
3842
},
3943
"count": 20, # Less tokens in older vision preview models
44+
"count_omni": 20,
4045
}
4146

4247
user_message = {
@@ -45,6 +50,7 @@
4550
"content": "Hello, how are you?",
4651
},
4752
"count": 13,
53+
"count_omni": 13,
4854
}
4955

5056
user_message_unicode = {
@@ -53,6 +59,7 @@
5359
"content": "á",
5460
},
5561
"count": 8,
62+
"count_omni": 8,
5663
}
5764

5865
user_message_perf = {
@@ -61,6 +68,7 @@
6168
"content": "What happens in a performance review?",
6269
},
6370
"count": 14,
71+
"count_omni": 14,
6472
}
6573

6674
assistant_message_perf = {
@@ -69,6 +77,7 @@
6977
"content": "During the performance review at Contoso Electronics, the supervisor will discuss the employee's performance over the past year and provide feedback on areas for improvement. They will also provide an opportunity for the employee to discuss their goals and objectives for the upcoming year. The review is a two-way dialogue between managers and employees, and employees will receive a written summary of their performance review which will include a rating of their performance, feedback, and goals and objectives for the upcoming year [employee_handbook-3.pdf].",
7078
},
7179
"count": 106,
80+
"count_omni": 106,
7281
}
7382

7483
assistant_message_perf_short = {
@@ -77,6 +86,7 @@
7786
"content": "The supervisor will discuss the employee's performance and provide feedback on areas for improvement. They will also provide an opportunity for the employee to discuss their goals and objectives for the upcoming year. The review is a two-way dialogue between managers and employees, and employees will receive a written summary of their performance review which will include a rating of their performance, feedback, and goals for the upcoming year [employee_handbook-3.pdf].",
7887
},
7988
"count": 91,
89+
"count_omni": 91,
8090
}
8191

8292
user_message_dresscode = {
@@ -85,6 +95,7 @@
8595
"content": "Is there a dress code?",
8696
},
8797
"count": 13,
98+
"count_omni": 13,
8899
}
89100

90101
assistant_message_dresscode = {
@@ -93,13 +104,15 @@
93104
"content": "Yes, there is a dress code at Contoso Electronics. Look sharp! [employee_handbook-1.pdf]",
94105
},
95106
"count": 30,
107+
"count_omni": 30,
96108
}
97109
user_message_pm = {
98110
"message": {
99111
"role": "user",
100112
"content": "What does a Product Manager do?",
101113
},
102114
"count": 14,
115+
"count_omni": 14,
103116
}
104117
text_and_image_message = {
105118
"message": {
@@ -116,6 +129,7 @@
116129
],
117130
},
118131
"count": 266,
132+
"count_omni": 266,
119133
}
120134

121135
MESSAGE_COUNTS = [

tests/test_modelhelper.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def test_get_token_limit():
1212
assert get_token_limit("gpt-3.5-turbo-16k") == 16000
1313
assert get_token_limit("gpt-4") == 8100
1414
assert get_token_limit("gpt-4-32k") == 32000
15+
assert get_token_limit("gpt-4o") == 128000
1516

1617

1718
def test_get_token_limit_error():
@@ -27,15 +28,16 @@ def test_get_token_limit_default(caplog):
2728

2829
# parameterize the model and the expected number of tokens
2930
@pytest.mark.parametrize(
30-
"model",
31+
"model, count_key",
3132
[
32-
"gpt-35-turbo",
33-
"gpt-3.5-turbo",
34-
"gpt-35-turbo-16k",
35-
"gpt-3.5-turbo-16k",
36-
"gpt-4",
37-
"gpt-4-32k",
38-
"gpt-4v",
33+
("gpt-35-turbo", "count"),
34+
("gpt-3.5-turbo", "count"),
35+
("gpt-35-turbo-16k", "count"),
36+
("gpt-3.5-turbo-16k", "count"),
37+
("gpt-4", "count"),
38+
("gpt-4-32k", "count"),
39+
("gpt-4v", "count"),
40+
("gpt-4o", "count_omni"),
3941
],
4042
)
4143
@pytest.mark.parametrize(
@@ -46,13 +48,19 @@ def test_get_token_limit_default(caplog):
4648
system_message_with_name,
4749
],
4850
)
49-
def test_count_tokens_for_message(model: str, message: dict):
50-
assert count_tokens_for_message(model, message["message"]) == message["count"]
51+
def test_count_tokens_for_message(model, count_key, message):
52+
assert count_tokens_for_message(model, message["message"]) == message[count_key]
5153

5254

53-
def test_count_tokens_for_message_list():
54-
model = "gpt-4"
55-
assert count_tokens_for_message(model, text_and_image_message["message"]) == text_and_image_message["count"]
55+
@pytest.mark.parametrize(
56+
"model, count_key",
57+
[
58+
("gpt-4", "count"),
59+
("gpt-4o", "count_omni"),
60+
],
61+
)
62+
def test_count_tokens_for_message_list(model, count_key):
63+
assert count_tokens_for_message(model, text_and_image_message["message"]) == text_and_image_message[count_key]
5664

5765

5866
def test_count_tokens_for_message_error():

tests/verify_openai.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,19 @@
3939

4040
# Test the token count for each message
4141
for message_count_pair in MESSAGE_COUNTS:
42-
response = client.chat.completions.create(
43-
model=MODEL_NAME,
44-
temperature=0.7,
45-
n=1,
46-
messages=[message_count_pair["message"]], # type: ignore[list-item]
47-
)
42+
for model, expected_tokens in [
43+
(MODEL_NAME, message_count_pair["count"]),
44+
("gpt-4o", message_count_pair["count_omni"]),
45+
]:
46+
response = client.chat.completions.create(
47+
model=model,
48+
temperature=0.7,
49+
n=1,
50+
messages=[message_count_pair["message"]], # type: ignore[list-item]
51+
)
4852

49-
print(message_count_pair["message"])
50-
expected_tokens = message_count_pair["count"]
51-
assert response.usage is not None, "Expected usage to be present"
52-
assert (
53-
response.usage.prompt_tokens == expected_tokens
54-
), f"Expected {expected_tokens} tokens, got {response.usage.prompt_tokens}"
53+
print(message_count_pair["message"])
54+
assert response.usage is not None, "Expected usage to be present"
55+
assert (
56+
response.usage.prompt_tokens == expected_tokens
57+
), f"Expected {expected_tokens} tokens, got {response.usage.prompt_tokens} for model {model}"

0 commit comments

Comments
 (0)