Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DeprecationBot

OpenAI shuts down the Assistants API on August 26, 2026. Code calling client.beta.assistants or client.beta.threads stops working that day.

DeprecationBot migrates that code to the Responses API, verifies the rewrite preserves what your code sends to the model, and refuses any migration it cannot verify.


What it does

  from openai import OpenAI

  client = OpenAI()

- assistant = client.beta.assistants.create(
-     name="Math Tutor",
-     instructions="You are a personal math tutor. Be concise.",
-     model="gpt-4o",
-     tools=[{"type": "code_interpreter"}],
- )
-
- thread = client.beta.threads.create()
-
- client.beta.threads.messages.create(
-     thread_id=thread.id,
-     role="user",
-     content="Solve 3x + 11 = 14",
- )
-
- run = client.beta.threads.runs.create_and_poll(
-     thread_id=thread.id,
-     assistant_id=assistant.id,
- )
-
- messages = client.beta.threads.messages.list(thread_id=thread.id)
- print(messages)
+ conversation = client.conversations.create()
+
+ response = client.responses.create(
+     model="gpt-4o",
+     instructions="You are a personal math tutor. Be concise.",
+     tools=[{"type": "code_interpreter"}],
+     input=[{"role": "user", "content": "Solve 3x + 11 = 14"}],
+     conversation=conversation.id,
+ )
+
+ print(response.output_text)

Five calls become two. The assistant, the thread, the message creation, and the polling run collapse into one responses.create().

This is not find-and-replace. assistant_id, thread_id, and run_id create data dependencies across call sites, often across functions. DeprecationBot builds a variable-flow graph so related calls migrate together — migrating any one alone produces code that doesn't run.


Why you can trust it

This tool rewrites source code. The design assumes you should not have to take its word for anything.

  • It refuses rather than guesses. Unrecognized patterns, oversized files, and failed verification all produce a refusal with a stated reason. Never a partial migration.
  • It never writes unverified output. Every transformation passes syntax validation and invariant comparison before touching disk.
  • Dry run is the default. Writing requires an explicit --write. There is no setting to change that.

A refusal:

$ deprecationbot migrate src/auth_service.py

DEFERRED  src/auth_service.py (340 lines)
          Exceeds the 250-line validated limit.
          Measured truncation threshold: ~300 lines.
          No LLM call made. No files modified.

That limit was measured, not guessed. See Limitations.


Install

Not yet on PyPI. From source:

git clone https://github.com/TODO/deprecationbot
cd deprecationbot
python -m venv .venv
source .venv/bin/activate
pip install -e .

Python 3.10+.

Transformation calls an LLM with your own key:

export DEEPSEEK_API_KEY=...

scan and verify need no key and make no network calls.


Usage

deprecationbot scan ./src                      # detect, read-only
deprecationbot migrate ./src/assistant.py      # preview, writes nothing
deprecationbot migrate ./src/assistant.py --write

A successful dry run:

$ deprecationbot migrate examples/math_tutor.py

Found 5 deprecated API calls in examples/math_tutor.py
Migrating... done

--- examples/math_tutor.py
+++ examples/math_tutor.py (migrated)
[unified diff]

VERIFIED
  model preserved            gpt-4o
  instructions preserved     exact match (44 chars)
  tool schemas preserved     1 schema, byte-identical
  message order preserved    1 message
  syntax                     valid

dry run — no files modified
Run again with --write to apply.
Command
scan <path> Detect deprecated usage. Read-only.
migrate <path> Migrate. Dry run unless --write.
verify <old> <new> Compare two files for semantic equivalence.
pr <owner/repo> Fork, migrate, verify, open a pull request. All gates apply.

Exit codes: 0 success · 1 verification failed · 2 safety gate tripped · 3 nothing detected. Nothing is written on 1 or 2.


Limitations

Read this before running the tool. These are measured or structural, not a backlog.

File size: 250 lines

The transformer sends a file to an LLM and gets a rewritten file back, so output token limits bound what it can process. The boundary was measured:

Lines Result
~100 Complete output
~200 Complete output
~300 Truncated — top-level definitions missing
~400+ Truncated

Truncation starts around 300. The tool refuses above 250, leaving margin. Raising the ceiling requires localized CST replacement instead of whole-file generation — see Roadmap.

Python only

TypeScript and JavaScript are not supported.

Deferred to human review

Detected and reported, not migrated, because the correct result isn't mechanically determined:

  • Streaming (stream=True) — event semantics differ between the APIs
  • file_search — vector store wiring differs
  • submit_tool_outputs — the tool-call loop becomes explicit
  • Runtime-loaded assistant IDs (os.getenv("ASSISTANT_ID"), DB lookups) — config is invisible to static analysis
  • Externally persisted thread IDs — existing thread_ identifiers need backfill

Blind spots

  • Indirect clients. get_client().beta.threads.create() is missed; the client can't be resolved statically.
  • Cross-file units. Flagged but not migrated. An assistant created in one module and used in another is deferred.

What "verified" means

DeprecationBot compares semantic invariants extracted from the syntax tree of the original and migrated code:

Invariant Meaning
Model The model identifier is unchanged
Instructions System instruction text is unchanged, character for character
Tool schemas Tool definitions are byte-identical after key normalization
Messages User message content and ordering are preserved
Syntax Both the transformed block and the whole file parse and compile

Any failure rejects the migration and nothing is written.

Values that aren't literals — variables, f-strings, environment lookups, function calls — can't be compared statically. They are reported as unverified, never as passed, and every report states the split:

VERIFIED (partial)
  model                      unverified — value is a variable (MODEL_NAME)
  instructions preserved     exact match (44 chars)
  tool schemas preserved     1 schema, byte-identical

There is no runtime verification. The tool does not call the OpenAI API, execute your code, or compare live responses. It proves the values reaching the API are preserved. It does not prove your application behaves identically end to end — that would require running your code against a live API, which this tool won't do.

Review every diff before merging it. Verification is evidence, not a guarantee.


What leaves your machine

No servers, no account, no hosted component.

  • To your LLM provider: contents of files being migrated, using your own key under your own agreement. Only during migrate and pr.
  • To GitHub: only during pr, through the gh CLI with your existing auth.
  • To the authors: nothing. No telemetry, no analytics, no crash reporting — enabled or otherwise.
  • Locally: a response cache, and a rate-limit state file if you use pr.

Why this keeps happening

The Assistants shutdown isn't isolated. Checked against vendor deprecation pages on 2026-07-27:

Date Change Provider
2026-07-23 25+ model IDs shut down (first wave) OpenAI
2026-08-05 Claude Opus 4.1 retired Anthropic
2026-08-26 Assistants API shutdown OpenAI
2026-10-23 Model shutdown, second wave OpenAI
2026-10-31 Evals platform read-only OpenAI
2026-11-30 Evals, Agent Builder, /v1/prompts shut down OpenAI
2026-12-01 Legacy GPT Image models removed OpenAI

Two details matter more than the length of that list. The breakage has outgrown model identifiers: from Claude Opus 4.7 onward, setting temperature, top_p, or top_k to a non-default value returns a 400 error — a code change, not a string swap. And schedules diverge by platform: Claude Sonnet 4 retired on the Anthropic API on 2026-06-15, while the same model on Amazon Bedrock carries an EOL of 2026-10-14.

Verify current dates against OpenAI and Anthropic.

On OpenAI's recommended path

OpenAI's migration guide says to convert each Assistant into a Prompt object. DeprecationBot doesn't, because Prompt objects can only be created in the dashboard — there's no API, so the step can't be automated — and reusable prompt objects were themselves deprecated on 2026-06-03 with shutdown on 2026-11-30.

Following the official guidance moves you onto a surface that closes three months later. DeprecationBot inlines model, instructions, and tools directly into responses.create() instead.


Recipes

Migrations are declarative YAML mappings.

Recipe Provider Sunset Status
assistants_to_responses OpenAI 2026-08-26 Implemented, in validation

Roadmap

Only work that removes a known limitation.

  • Localized CST replacement — statement-range rewriting instead of whole-file generation. Removes the 250-line ceiling, produces smaller diffs.
  • Additional recipes — Anthropic model retirements; OpenAI's 2026-11-30 shutdowns.
  • Deprecation monitoring — ingest vendor announcements so recipes exist before shutdown dates arrive.

Contributing

Useful reports, in order:

  1. A migration that verified but was wrong. The most valuable thing you can file. Minimal reproduction, please.
  2. Deprecated usage the detector misses.
  3. Patterns that defer but shouldn't.
git clone https://github.com/TODO/deprecationbot
cd deprecationbot
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest

License

Apache-2.0. See LICENSE.