How to Build a Local Backup to Save Agent Sessions Even When the OpenAI Assistants API Goes Down
Running a service with an AI agent alone can become tricky if the vendor API suddenly shuts down out of nowhere. If OpenAI completely shuts down the Assistants API v1 beta endpoint on August 26, 2026, the conversation threads and execution histories stored on their servers will instantly vanish. A structure that leaves all session states entirely to the vendor's server is no different from working with a ticking time bomb.
To avoid being dragged around by platforms, you need to bring conversation states into your own computer's file system. It is much safer to use LLMs as interchangeable computing tools while directly holding and controlling conversation contexts and prompt assets locally.
The Price Paid When Trapped in Cloud Agent Sessions
When you leave sessions on servers of providers like OpenAI or Anthropic, there is no way to know how conversation states are compressed and encrypted internally. When problems occur, auditing the internals becomes completely impossible. Because the entire context is reprocessed every time conversation turns accumulate, token costs increase quadratically. Code Interpreter costs of $0.03 per session or File Search storage costs of $0.10 per GB per month quietly drain your bank account.
| Evaluation Criteria |
Cloud-Managed Sessions (OpenAI Assistants) |
Local File System Standalone Harness |
| Data Ownership |
Isolated on vendor servers (cannot export) |
Stored as JSON files in a local directory |
| Service Continuity |
Thread loss during API shutdown on August 26, 2026 |
Immediate switch to another model even if vendor servers crash |
| Context Cost |
Token waste due to full thread reprocessing per turn |
Token reduction by selectively injecting DiffMem changes |
| Debugging Transparency |
Unverifiable due to server-side opaque compression |
Direct verification of inference processes via local middleware logs |
As Microsoft CEO Satya Nadella mentioned, the structure of leaving session management to model provider infrastructure is risky. This is because exclusively owned conversation contexts, not the models themselves, are the real assets. The "behavioral dependency" pointed out by 76% of developers in a Docker survey also ultimately arises from losing this session control.
Taking Back Session Control with a Local File System
Combining the LangGraph framework with a Custom Checkpointer allows you to securely fix conversation states into local JSON files. When writing data to files, use the atomic write method utilizing os.replace. Even if the power goes out or the process is forcibly killed during an operation, the data will not get corrupted.
`python
import json
import os
from datetime import datetime
from typing import Any, Dict, List
from dataclasses import dataclass, asdict
@dataclass
class LocalSessionState:
session_id: str
model_provider: str
model_name: str
system_prompt: str
messages: List[Dict[str, Any]]
metadata: Dict[str, Any]
updated_at: str
class LocalFileCheckpointer:
def init(self, base_dir: str = "./agent_workspace/sessions"):
self.base_dir = base_dir
os.makedirs(self.base_dir, exist_ok=True)
def _get_file_path(self, session_id: str) -> str:
return os.path.join(self.base_dir, f"{session_id}.json")
def save_state(self, state: LocalSessionState) -> str:
file_path = self._get_file_path(state.session_id)
temp_path = f"{file_path}.tmp"
state.updated_at = datetime.utcnow().isoformat()
data = asdict(state)
with open(temp_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
os.replace(temp_path, file_path)
return file_path
`
You can set up your session backup environment like this:
- Create
agent_workspace inside your project folder, and place prompts/, sessions/, and logs/ directories underneath it respectively. Organize operation rules in AGENTS.md.
- Import
LocalFileCheckpointer from the Python code above and connect it so that a snapshot is overwritten to a JSON file every time a conversation turn is updated.
- Attach the
GitSessionVersioner below to your Git post-hook pipeline to automatically commit changes to Git whenever session files change.
`python
import subprocess
class GitSessionVersioner:
def init(self, repo_dir: str = "./agent_workspace"):
self.repo_dir = repo_dir
self._ensure_git_repo()
def _ensure_git_repo(self):
if not os.path.exists(os.path.join(self.repo_dir, ".git")):
subprocess.run(["git", "init"], cwd=self.repo_dir, check=True)
def commit_session(self, session_id: str, commit_message: str):
file_name = f"sessions/{session_id}.json"
subprocess.run(["git", "add", file_name], cwd=self.repo_dir, check=True)
status = subprocess.run(["git", "status", "--porcelain", file_name], cwd=self.repo_dir, capture_output=True, text=True)
if status.stdout.strip():
subprocess.run(["git", "commit", "-m", f"session({session_id}): {commit_message}"], cwd=self.repo_dir, check=True)
`
Once this structure is established, even if conversation threads grow long, there is no need to forcefully shove the entire history into the context window. Since only DiffMem changes are selectively queried, unnecessary token expenses are prevented, and session restoration working time is reduced by over 2 hours.
Collecting Hidden Inference Logs via Local Middleware
Cloud APIs often completely conceal what agents are thinking and which tools they are calling. To eliminate debugging frustration, place LiteLLM Proxy or a middleware interceptor in the middle and directly log incoming and outgoing data to files. Spinning up open-source tracing tools like Langfuse or Weights & Biases Weave in local Docker lets you cleanly view differences such as OpenAI's type: function or Anthropic's input_schema all in one place.
`python
import json
import logging
from typing import Dict, Any, Optional
class AgentLoggingMiddleware:
def init(self, log_file_path: str):
self.logger = logging.getLogger("AgentLogger")
self.logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(log_file_path, encoding="utf-8")
handler.setFormatter(logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s'))
self.logger.addHandler(handler)
def on_pre_tool_execution(self, tool_name: str, tool_args: Dict[str, Any], tool_call_id: str):
log_entry = {"event": "PreToolUse", "tool_call_id": tool_call_id, "tool_name": tool_name, "arguments": tool_args}
self.logger.info(f"TOOL_CALL_INIT: {json.dumps(log_entry, ensure_ascii=False)}")
def on_post_tool_execution(self, tool_call_id: str, result: Any, error: Optional[str] = None):
log_entry = {"event": "PostToolUse", "tool_call_id": tool_call_id, "result": result, "error": error}
if error:
self.logger.error(f"TOOL_CALL_FAILED: {json.dumps(log_entry, ensure_ascii=False)}")
else:
self.logger.info(f"TOOL_CALL_SUCCESS: {json.dumps(log_entry, ensure_ascii=False)}")
`
Execution log collection is completed in three steps:
- Insert
AgentLoggingMiddleware into the PreToolUse and PostToolUse points of the agent execution loop.
- Configure argument values passed during tool calls and raw text return values to be immediately recorded in file logs.
- Run Langfuse in a local Docker environment to track infinite retries or incorrect argument parsing during API communication on a single screen.
Even just properly accumulating logs allows you to find the root causes of errors exploding in tool arguments all at once. API token costs wasted running in pointless loops noticeably decrease.
How to Convert Schemas and Move from OpenAI to Claude
OpenAI APIs send and receive tool arguments as serialized JSON strings and have a separate tool role. On the other hand, the Anthropic Messages API uses parsed dictionary objects and a tool_result block inside user messages. Because the specifications differ, throwing backed-up JSON data as-is to Anthropic results in an HTTP 400 error. An adapter that aligns specifications in the middle is required.
`python
import json
from typing import List, Dict, Any, Tuple
class CrossModelSessionAdapter:
@staticmethod
def openai_to_anthropic_format(system_prompt: str, openai_messages: List[Dict[str, Any]]) -> Tuple[str, List[Dict[str, Any]]]:
anthropic_messages = []
i = 0
while i < len(openai_messages):
msg = openai_messages[i]
role = msg.get("role")
if role == "system":
system_prompt = msg.get("content", system_prompt)
i += 1
elif role == "user":
anthropic_messages.append({"role": "user", "content": msg.get("content")})
i += 1
elif role == "assistant":
content_blocks = []
if msg.get("content"):
content_blocks.append({"type": "text", "text": msg.get("content")})
if "tool_calls" in msg and msg["tool_calls"]:
for tc in msg["tool_calls"]:
args = tc["function"]["arguments"]
parsed_args = json.loads(args) if isinstance(args, str) else args
content_blocks.append({"type": "tool_use", "id": tc["id"], "name": tc["function"]["name"], "input": parsed_args})
anthropic_messages.append({"role": "assistant", "content": content_blocks})
i += 1
elif role == "tool":
tool_results = []
while i < len(openai_messages) and openai_messages[i].get("role") == "tool":
t_msg = openai_messages[i]
tool_results.append({"type": "tool_result", "tool_use_id": t_msg.get("tool_call_id"), "content": t_msg.get("content")})
i += 1
anthropic_messages.append({"role": "user", "content": tool_results})
return system_prompt, anthropic_messages
`
Migration work is also simple:
- Read conversation history and tool execution data from the local
sessions/{session_id}.json file.
- Run
CrossModelSessionAdapter to change the OpenAI format into the Anthropic specification.
- Put a context block specifying that this is continued work from the previous session at the top of the new model system prompt and send the request.
`text
[System Context Injection]
This conversation is continued work migrated from a previous session (ID: sess_99812).
Tool execution results and conversation context with the previous model are included in the message history.
Based on the presented previous tool call results (tool_result), resume work from the point where it was interrupted.
`
It doesn't matter if a specific vendor server breaks down or suddenly changes service policies. You can just switch to another LLM with a few lines of conversion code while maintaining conversation flow 100%. Business continuity for solo developers is finally secured when you physically hold session data in your own directory.