fix: save and load chat conversations as UTF-8

The interactive proxy chat /save command wrote conversations with
json.dump's default ensure_ascii=True and no file encoding, so non-ASCII
content (any non-English message, emoji) was stored as unreadable \uXXXX
escapes, and /load read the file with the platform default encoding.

Write with ensure_ascii=False and open both /save and /load as UTF-8 so
conversations are stored readably and round-trip on every platform,
matching how the rest of the codebase persists JSON.
This commit is contained in:
SWAPI03 2026-10-15 11:48:36 +05:30
parent 254c2f6b3b
commit c02eb5df2e
2 changed files with 30 additions and 3 deletions

View file

@ -301,8 +301,8 @@ def _save_conversation(console: Console, messages: list[dict[str, str]], command
filename += ".json"
try:
with open(filename, "w") as f:
json.dump(messages, f, indent=2)
with open(filename, "w", encoding="utf-8") as f:
json.dump(messages, f, indent=2, ensure_ascii=False)
console.print(f"[green]Conversation saved to {filename}[/green]")
except Exception as e:
console.print(f"[red]Error saving conversation: {e}[/red]")
@ -320,7 +320,7 @@ def _load_conversation(console: Console, command: str, system: str | None) -> li
filename += ".json"
try:
with open(filename, "r") as f:
with open(filename, "r", encoding="utf-8") as f:
loaded: Final[_MessagesView] = {"messages": json.load(f)}
console.print(f"[green]Conversation loaded from {filename}[/green]")
return loaded["messages"]

View file

@ -0,0 +1,27 @@
from pathlib import Path
from rich.console import Console
from litellm.proxy.client.cli.commands.chat import (
_load_conversation,
_save_conversation,
)
def test_save_and_load_conversation_roundtrips_non_ascii(tmp_path: Path) -> None:
"""/save writes readable UTF-8 (not \\uXXXX escapes) and /load reads it back on any locale."""
console = Console()
messages = [
{"role": "user", "content": "Café ☕ 東京"},
{"role": "assistant", "content": "日本語で返信します"},
]
target = tmp_path / "conversation.json"
_save_conversation(console, messages, f"/save {target}")
saved = target.read_text(encoding="utf-8")
assert "東京" in saved
assert "\\u" not in saved
loaded = _load_conversation(console, f"/load {target}", system=None)
assert loaded == messages