- unsupport_message_handle now checks is_bot_mentioned, so the bot stops
replying "I don't know how to read files or videos" to videos/documents
it wasn't mentioned in within group chats (#454). Private chats are
unaffected (is_bot_mentioned returns True there).
- Bump python-dotenv 1.0.1 -> 1.2.2 to resolve a Dependabot symlink-following
advisory in set_key.
- Bump version to 1.3.1 and document in CHANGELOG.
The per-model if/elif chain only ever set tokens_per_message=3 for
supported models (and 4 for the now-removed gpt-3.5 family). Collapse
it to a single assignment since every current chat model shares the
same overhead.
Existing users may have token usage recorded against models that have
since been removed from models.yml. Skip any model key not present in
the config when building the balance breakdown so /balance no longer
raises a KeyError for them.
generate_images now returns raw image bytes, so wrap each in BytesIO
for reply_photo instead of passing a URL. Reference the gpt-image-1
price entry in /balance and relabel it 'GPT Image'.
Replace DALL-E 3 with OpenAI's gpt-image-1 model. gpt-image-1 returns
base64-encoded images instead of URLs, so decode each result to raw
bytes and return the list of images.
Reference the dalle-3 price entry and label balance details as
DALL·E 3, and count actually returned images (len(image_urls))
instead of the configured request count, since DALL-E 3 always
returns a single image.
512x512 is not a valid DALL-E 3 size. Default to 1024x1024 and update
the example config to list the supported DALL-E 3 sizes and note the
one-image-per-request limit.
bot.py decided vision handling from hardcoded gpt-4o/gpt-4-vision
checks. Use the models.yml 'vision' flag instead: any vision-capable
model (incl. gpt-5.5 and Claude) now handles images directly, and a
photo sent to a non-vision model still falls back to gpt-4o. The
rejection message in the vision handler is updated to match.
send_vision_message and send_vision_message_stream now accept any
model flagged vision: true in models.yml instead of a hardcoded
gpt-4o/gpt-4-vision set, so Claude and gpt-5.5 can process images.
The settings menu put every model button in a single row. Telegram
rejects rows with more than 8 inline buttons, which would break
/settings now that the model list is longer. Chunk the buttons into
rows of two.
Introduce an optional OpenRouter client (OpenAI-compatible) alongside
the OpenAI one. Models can declare 'provider: openrouter' in
models.yml; ChatGPT now selects the matching client per model via
_get_client_for_model and routes chat completions through it. This
unlocks non-OpenAI models (e.g. Claude) through a single SDK.
Adds openrouter_api_key / openrouter_api_base config options.
Replace the hardcoded model-name sets in __init__, send_message and
send_message_stream with checks against config/models.yml: the assert
now accepts any model defined in models['info'], and the chat path is
taken for any model whose type is 'chat_completion'. Adding a new
chat model is now a models.yml-only change. Vision handling is left
on its explicit model set for now.
tiktoken.encoding_for_model and the per-model token table only know
OpenAI models, so any other model (Claude or anything routed via
OpenRouter) raised. Fall back to the o200k_base encoding and a
default tokens_per_message for unknown models so streaming token
estimates work for every chat model.
Remove the legacy completion handling now that the model is gone:
- drop text-davinci-003 from the __init__ assert
- remove the legacy completions.create branches in send_message and
send_message_stream
- delete the now-unused _generate_prompt and _count_tokens_from_prompt
helpers
All remaining paths use the chat completions API.
Keep the ChatGPT class fallback model in sync with the new project
default. Callers always pass an explicit model, so this only affects
the no-argument fallback.
new_dialog_handle pins the model on every /new. Point it at the new
default gpt-4o-mini so starting a new dialog no longer drops users
back onto the outdated gpt-3.5-turbo.
Replace the two bare 'except:' clauses (is_bot_mentioned and the
error handler fallback) with 'except Exception:' so they no longer
swallow KeyboardInterrupt, SystemExit or asyncio.CancelledError,
which must propagate for clean shutdown and task cancellation.
Follow-up to the openai 1.x migration: the legacy
openai.error.InvalidRequestError no longer exists in the 1.x SDK.
Catch openai.BadRequestError when DALL-E rejects a prompt.
logger.error(current_model) fired on every incoming message and
spammed the error log with the model name; the accompanying
'# What is this? ^^^' note was dead. Drop both; the vision
auto-switch logic is unchanged.
Wire gpt-4o-mini through the bot: register it in models.yml
(available list + pricing/scores entry) and add it to the model
sets in openai_utils (chat, streaming, vision and token counting).
gpt-4o-mini is cheaper and smarter than gpt-3.5-turbo and supports
images, so it is a good default-grade option. Addresses #474.
* fix argument of type 'float' is not iterable
fix "argument of type 'float' is not iterable" in show_balance_handle and message_handle
* Update bot.py