diff --git a/libs/core/kiln_ai/datamodel/basemodel.py b/libs/core/kiln_ai/datamodel/basemodel.py index 183e24b..1d9f413 100644 --- a/libs/core/kiln_ai/datamodel/basemodel.py +++ b/libs/core/kiln_ai/datamodel/basemodel.py @@ -139,7 +139,7 @@ def load_from_file(cls: Type[T], path: Path | str, readonly: bool = False) -> T: cached_model = ModelCache.shared().get_model(path, cls, readonly=readonly) if cached_model is not None: return cached_model - with open(path, "r") as file: + with open(path, "r", encoding="utf-8") as file: # modified time of file for cache invalidation. From file descriptor so it's atomic w read. mtime_ns = os.fstat(file.fileno()).st_mtime_ns file_data = file.read() @@ -198,7 +198,7 @@ def save_to_file(self) -> None: ) path.parent.mkdir(parents=True, exist_ok=True) json_data = self.model_dump_json(indent=2, exclude={"path"}) - with open(path, "w") as file: + with open(path, "w", encoding="utf-8") as file: file.write(json_data) # save the path so even if something like name changes, the file doesn't move self.path = path diff --git a/libs/core/kiln_ai/datamodel/test_models.py b/libs/core/kiln_ai/datamodel/test_models.py index f429443..02107b1 100644 --- a/libs/core/kiln_ai/datamodel/test_models.py +++ b/libs/core/kiln_ai/datamodel/test_models.py @@ -71,6 +71,20 @@ def test_save_to_file(test_project_file): assert data["description"] == "Test Description" +def test_save_to_file_non_ascii(test_project_file): + project = Project( + name="Test Project", description="Chúc mừng!", path=test_project_file + ) + project.save_to_file() + + with open(test_project_file, "r", encoding="utf-8") as file: + data = json.load(file) + + assert data["v"] == 1 + assert data["name"] == "Test Project" + assert data["description"] == "Chúc mừng!" + + def test_task_defaults(): task = Task(name="Test Task", instruction="Test Instruction") assert task.description is None