diff --git a/lib/crewai/src/crewai/state/provider/json_provider.py b/lib/crewai/src/crewai/state/provider/json_provider.py index 904526292d..93556f0e3f 100644 --- a/lib/crewai/src/crewai/state/provider/json_provider.py +++ b/lib/crewai/src/crewai/state/provider/json_provider.py @@ -63,7 +63,7 @@ def checkpoint( file_path = _build_path(location, branch, parent_id) file_path.parent.mkdir(parents=True, exist_ok=True) - with open(file_path, "w") as f: + with open(file_path, "w", encoding="utf-8") as f: f.write(data) return str(file_path) @@ -91,7 +91,7 @@ async def acheckpoint( file_path = _build_path(location, branch, parent_id) await aiofiles.os.makedirs(str(file_path.parent), exist_ok=True) - async with aiofiles.open(file_path, "w") as f: + async with aiofiles.open(file_path, "w", encoding="utf-8") as f: await f.write(data) return str(file_path) @@ -129,7 +129,7 @@ def from_checkpoint(self, location: str) -> str: Returns: The raw JSON string. """ - return Path(location).read_text() + return Path(location).read_text(encoding="utf-8") async def afrom_checkpoint(self, location: str) -> str: """Read a JSON checkpoint file asynchronously. @@ -140,7 +140,7 @@ async def afrom_checkpoint(self, location: str) -> str: Returns: The raw JSON string. """ - async with aiofiles.open(location) as f: + async with aiofiles.open(location, encoding="utf-8") as f: return await f.read() diff --git a/lib/crewai/tests/test_checkpoint.py b/lib/crewai/tests/test_checkpoint.py index 4d316afe89..ab49786c58 100644 --- a/lib/crewai/tests/test_checkpoint.py +++ b/lib/crewai/tests/test_checkpoint.py @@ -8,6 +8,7 @@ import sqlite3 import tempfile import time +from pathlib import Path from typing import Any from unittest.mock import MagicMock, patch @@ -378,6 +379,25 @@ def test_checkpoint_writes_to_branch_subdir(self) -> None: assert path.endswith(".json") assert os.path.isfile(path) + def test_checkpoint_uses_utf8_for_non_ascii_json(self) -> None: + provider = JsonProvider() + data = '{"message": "olá niño"}' + with tempfile.TemporaryDirectory() as d: + path = provider.checkpoint(data, d, branch="main") + + assert Path(path).read_bytes() == data.encode("utf-8") + assert provider.from_checkpoint(path) == data + + @pytest.mark.asyncio + async def test_acheckpoint_uses_utf8_for_non_ascii_json(self) -> None: + provider = JsonProvider() + data = '{"message": "olá niño"}' + with tempfile.TemporaryDirectory() as d: + path = await provider.acheckpoint(data, d, branch="main") + + assert Path(path).read_bytes() == data.encode("utf-8") + assert await provider.afrom_checkpoint(path) == data + def test_checkpoint_fork_branch_subdir(self) -> None: provider = JsonProvider() with tempfile.TemporaryDirectory() as d: