Skip to content

Commit 526983a

Browse files
authored
Merge pull request #17 from pamelafox/gpt-4o-mini
Add gpt4o mini support
2 parents d79be12 + be1ad07 commit 526983a

File tree

12 files changed

+110
-23
lines changed

12 files changed

+110
-23
lines changed

.github/workflows/python.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
python-version: ${{ matrix.python_version }}
2323
- name: Install dependencies
2424
run: |
25-
python -m pip install --upgrade pip
25+
python3 -m pip install --upgrade pip
2626
python3 -m pip install -e '.[dev]'
2727
- name: Lint with ruff
2828
run: ruff check .
@@ -32,4 +32,4 @@ jobs:
3232
run: |
3333
python3 -m pytest -s -vv --cov --cov-fail-under=97
3434
- name: Run type checks
35-
run: mypy .
35+
run: python3 -m mypy .

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.9] - Aug 7, 2024
6+
7+
- Add gpt-4o-mini support, by adding a 33.3x multiplier to the token cost.
8+
59
## [0.1.8] - Aug 3, 2024
610

711
- Fix the type for the tool_choice param to be inclusive of "auto" and other options.

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.8"
4+
version = "0.1.9"
55
authors = [{name = "Pamela Fox"}]
66
requires-python = ">=3.9"
77
readme = "README.md"

src/openai_messages_token_helper/images_helper.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import base64
22
import math
33
import re
4+
from fractions import Fraction
45
from io import BytesIO
6+
from typing import Optional
57

68
from PIL import Image
79

@@ -16,20 +18,23 @@ def get_image_dims(image_uri: str) -> tuple[int, int]:
1618
raise ValueError("Image must be a base64 string.")
1719

1820

19-
def count_tokens_for_image(image_uri: str, detail: str = "auto") -> int:
21+
def count_tokens_for_image(image_uri: str, detail: str = "auto", model: Optional[str] = None) -> int:
2022
# From https://github.com/openai/openai-cookbook/pull/881/files
2123
# Based on https://platform.openai.com/docs/guides/vision
22-
LOW_DETAIL_COST = 85
23-
HIGH_DETAIL_COST_PER_TILE = 170
24-
ADDITIONAL_COST = 85
24+
multiplier = Fraction(1, 1)
25+
if model == "gpt-4o-mini":
26+
multiplier = Fraction(100, 3)
27+
COST_PER_TILE = 85 * multiplier
28+
LOW_DETAIL_COST = COST_PER_TILE
29+
HIGH_DETAIL_COST_PER_TILE = COST_PER_TILE * 2
2530

2631
if detail == "auto":
2732
# assume high detail for now
2833
detail = "high"
2934

3035
if detail == "low":
3136
# Low detail images have a fixed cost
32-
return LOW_DETAIL_COST
37+
return int(LOW_DETAIL_COST)
3338
elif detail == "high":
3439
# Calculate token cost for high detail images
3540
width, height = get_image_dims(image_uri)
@@ -47,8 +52,8 @@ def count_tokens_for_image(image_uri: str, detail: str = "auto") -> int:
4752
# Calculate the number of 512px squares
4853
num_squares = math.ceil(width / 512) * math.ceil(height / 512)
4954
# Calculate the total token cost
50-
total_cost = num_squares * HIGH_DETAIL_COST_PER_TILE + ADDITIONAL_COST
51-
return total_cost
55+
total_cost = num_squares * HIGH_DETAIL_COST_PER_TILE + COST_PER_TILE
56+
return math.ceil(total_cost)
5257
else:
5358
# Invalid detail_option
5459
raise ValueError("Invalid value for detail parameter. Use 'low' or 'high'.")

src/openai_messages_token_helper/message_builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def build_messages(
126126
if tool_calls is not None and not isinstance(tool_calls, Iterable):
127127
raise ValueError("tool_calls must be a list of tool calls")
128128
message_builder.insert_message(
129-
shot["role"], shot.get("content"), tool_calls=tool_calls, tool_call_id=tool_call_id
129+
shot["role"], shot.get("content"), tool_calls=tool_calls, tool_call_id=tool_call_id # type: ignore[arg-type]
130130
)
131131

132132
append_index = len(few_shots)
@@ -149,6 +149,6 @@ def build_messages(
149149

150150
if message["role"] is None or message["content"] is None:
151151
raise ValueError("Few-shot messages must have both role and content")
152-
message_builder.insert_message(message["role"], message["content"], index=append_index)
152+
message_builder.insert_message(message["role"], message["content"], index=append_index) # type: ignore[arg-type]
153153
total_token_count += potential_message_count
154154
return message_builder.all_messages

src/openai_messages_token_helper/model_helper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"gpt-4-32k": 32000,
2323
"gpt-4v": 128000,
2424
"gpt-4o": 128000,
25+
"gpt-4o-mini": 128000,
2526
}
2627

2728

@@ -106,7 +107,7 @@ def count_tokens_for_message(model: str, message: ChatCompletionMessageParam, de
106107
if item["type"] == "text":
107108
num_tokens += len(encoding.encode(item["text"]))
108109
elif item["type"] == "image_url":
109-
num_tokens += count_tokens_for_image(item["image_url"]["url"], item["image_url"]["detail"])
110+
num_tokens += count_tokens_for_image(item["image_url"]["url"], item["image_url"]["detail"], model)
110111
elif isinstance(value, str):
111112
num_tokens += len(encoding.encode(value))
112113
else:

tests/image_messages.py

Lines changed: 55 additions & 0 deletions
Large diffs are not rendered by default.

tests/messages.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,4 @@
146146
assistant_message_perf,
147147
assistant_message_perf_short,
148148
assistant_message_dresscode,
149-
text_and_image_message,
150149
]

tests/test_imageshelper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def large_image():
1818

1919
def test_count_tokens_for_image(small_image, large_image):
2020
assert count_tokens_for_image(small_image, "low") == 85
21+
assert count_tokens_for_image(small_image, "low", "gpt-4o-mini") == 2833
2122
assert count_tokens_for_image(small_image, "high") == 255
2223
assert count_tokens_for_image(small_image) == 255
2324
assert count_tokens_for_image(large_image, "low") == 85

tests/test_messagebuilder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
from openai_messages_token_helper import build_messages, count_tokens_for_message
1010

1111
from .functions import search_sources_toolchoice_auto
12+
from .image_messages import text_and_tiny_image_message
1213
from .messages import (
1314
assistant_message_dresscode,
1415
assistant_message_perf,
1516
assistant_message_perf_short,
1617
system_message_long,
1718
system_message_short,
1819
system_message_unicode,
19-
text_and_image_message,
2020
user_message,
2121
user_message_dresscode,
2222
user_message_perf,
@@ -35,9 +35,9 @@ def test_messagebuilder_imagemessage():
3535
messages = build_messages(
3636
"gpt-35-turbo",
3737
system_message_short["message"]["content"],
38-
new_user_content=text_and_image_message["message"]["content"],
38+
new_user_content=text_and_tiny_image_message["message"]["content"],
3939
)
40-
assert messages == [system_message_short["message"], text_and_image_message["message"]]
40+
assert messages == [system_message_short["message"], text_and_tiny_image_message["message"]]
4141

4242

4343
def test_messagebuilder_append():

0 commit comments

Comments
 (0)