From 20b228cb0fc99d99220404c00d4f97acb8247c71 Mon Sep 17 00:00:00 2001 From: Fang Zhiyuan <300441457+olong75@users.noreply.github.com> Date: Mon, 31 Aug 2026 02:30:49 +0800 Subject: [PATCH] fix: add encoding="utf-8" to state_store.py read_text/write_text calls --- backend/app/faros/runtime/state_store.py | 12 ++-- .../tests/test_pr_b21_state_store_encoding.py | 59 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 backend/tests/test_pr_b21_state_store_encoding.py diff --git a/backend/app/faros/runtime/state_store.py b/backend/app/faros/runtime/state_store.py index 6d01e300..6549c05a 100644 --- a/backend/app/faros/runtime/state_store.py +++ b/backend/app/faros/runtime/state_store.py @@ -82,7 +82,7 @@ def list_runs(self) -> List[Dict[str, Any]]: runs = [] for path in sorted(self.root.glob("*/run.json"), reverse=True): try: - runs.append(json.loads(path.read_text())) + runs.append(json.loads(path.read_text(encoding="utf-8"))) except Exception: continue return runs @@ -91,7 +91,7 @@ def get_run(self, run_id: str) -> Optional[Dict[str, Any]]: path = self._run_path(run_id) if not path.is_file(): return None - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) def update_run(self, run_id: str, updates: Dict[str, Any]) -> Dict[str, Any]: record = self.get_run(run_id) @@ -120,7 +120,7 @@ def list_events(self, run_id: str) -> List[Dict[str, Any]]: path = self._events_path(run_id) if not path.is_file(): return [] - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) def append_event(self, run_id: str, event: Dict[str, Any]) -> None: events = self.list_events(run_id) @@ -131,7 +131,7 @@ def list_artifacts(self, run_id: str) -> List[Dict[str, Any]]: path = self._artifacts_path(run_id) if not path.is_file(): return [] - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) def append_artifacts(self, run_id: str, artifacts: List[Dict[str, Any]]) -> None: existing: List[Dict[str, Any]] = [] @@ -158,7 +158,7 @@ def get_memory(self, run_id: str) -> Dict[str, Any]: path = self._memory_path(run_id) if not path.is_file(): return {} - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) def save_memory(self, run_id: str, memory: Dict[str, Any]) -> None: self._save_json(self._memory_path(run_id), memory) @@ -181,4 +181,4 @@ def _validate_step_updates(self, current_step: Dict[str, Any], updates: Dict[str def _save_json(self, path: Path, payload: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(payload, indent=2, default=str)) + path.write_text(json.dumps(payload, indent=2, default=str), encoding="utf-8") diff --git a/backend/tests/test_pr_b21_state_store_encoding.py b/backend/tests/test_pr_b21_state_store_encoding.py new file mode 100644 index 00000000..95d0d7fc --- /dev/null +++ b/backend/tests/test_pr_b21_state_store_encoding.py @@ -0,0 +1,59 @@ +"""Regression: FAROS state store must persist non-ASCII run data on any locale. + +read_text()/write_text() in state_store omitted encoding="utf-8", so run +records containing Chinese paper titles or Unicode agent outputs raised +UnicodeEncodeError/UnicodeDecodeError on hosts whose default locale is +not UTF-8 (Windows cp1252, minimal C/POSIX containers). +""" + +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from app.faros.runtime.state_store import FarosStateStore + + +def _make_run(store): + return store.create_run( + blueprint_id="bp_test", + profile_id="pf_test", + execution_mode="sequential", + inputs={"topic": "基于检索增强的科学写作框架研究"}, + steps=[], + ) + + +def test_run_roundtrip_preserves_chinese_inputs(tmp_path): + store = FarosStateStore(root=tmp_path) + run = _make_run(store) + + loaded = store.get_run(run["id"]) + + assert loaded is not None + assert loaded["inputs"]["topic"] == "基于检索增强的科学写作框架研究" + + +def test_list_runs_survives_nonascii_records(tmp_path): + store = FarosStateStore(root=tmp_path) + _make_run(store) + + runs = store.list_runs() + + assert len(runs) == 1 + assert runs[0]["inputs"]["topic"] == "基于检索增强的科学写作框架研究" + + +def test_memory_and_events_preserve_unicode(tmp_path): + store = FarosStateStore(root=tmp_path) + run = _make_run(store) + + store.save_memory(run["id"], {"summary": "实验结果表明方法有效 ✓"}) + store.append_event(run["id"], {"type": "log", "message": "步骤完成:数据预处理"}) + store.append_artifacts(run["id"], [{"name": "图表", "path": "/tmp/图表.png"}]) + + assert store.get_memory(run["id"])["summary"] == "实验结果表明方法有效 ✓" + assert store.list_events(run["id"])[0]["message"] == "步骤完成:数据预处理" + assert store.list_artifacts(run["id"])[0]["name"] == "图表" + + print("PASS: state store non-ASCII round-trips")