memmachine-client 0.3.7__tar.gz → 0.3.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/PKG-INFO +1 -1
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_format.py +79 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/format.py +3 -3
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/PKG-INFO +1 -1
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/LICENSE +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/README.md +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/__init__.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_client.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_config.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_integration_complete.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_langgraph.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_memory.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/pyproject.toml +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/setup.cfg +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/__init__.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/client.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/config.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/langgraph.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/memory.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/project.py +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/SOURCES.txt +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/dependency_links.txt +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/requires.txt +0 -0
- {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/top_level.txt +0 -0
|
@@ -99,6 +99,59 @@ class TestFormatEpisodes:
|
|
|
99
99
|
result = format_episodes([ep])
|
|
100
100
|
assert json.dumps('She said "hello"') in result
|
|
101
101
|
|
|
102
|
+
def test_non_ascii_content_preserved_literally(self):
|
|
103
|
+
"""Non-ASCII characters must appear literally in the LLM context, not
|
|
104
|
+
as ``\\uXXXX`` escapes — escaping bloats token counts and degrades
|
|
105
|
+
recall on multilingual content."""
|
|
106
|
+
ep = EpisodeResponse(
|
|
107
|
+
uid="1",
|
|
108
|
+
content="寿司 café 🍕 naïve résumé Привет",
|
|
109
|
+
producer_id="user_1",
|
|
110
|
+
producer_role="user",
|
|
111
|
+
created_at=datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc),
|
|
112
|
+
)
|
|
113
|
+
result = format_episodes([ep])
|
|
114
|
+
assert "寿司" in result
|
|
115
|
+
assert "café" in result
|
|
116
|
+
assert "🍕" in result
|
|
117
|
+
assert "naïve" in result
|
|
118
|
+
assert "résumé" in result
|
|
119
|
+
assert "Привет" in result
|
|
120
|
+
# Sanity: no escaped CJK / cyrillic / accented sequences.
|
|
121
|
+
assert "\\u" not in result
|
|
122
|
+
|
|
123
|
+
def test_non_ascii_content_lossless_roundtrip(self):
|
|
124
|
+
"""The JSON-encoded portion must round-trip back to the original
|
|
125
|
+
string so downstream LLM consumers (and any client-side
|
|
126
|
+
post-processing) still see correct text."""
|
|
127
|
+
original = '日本語 — "quoted" + emoji 🎉'
|
|
128
|
+
ep = EpisodeResponse(
|
|
129
|
+
uid="1",
|
|
130
|
+
content=original,
|
|
131
|
+
producer_id="user_1",
|
|
132
|
+
producer_role="user",
|
|
133
|
+
created_at=None,
|
|
134
|
+
)
|
|
135
|
+
result = format_episodes([ep])
|
|
136
|
+
json_part = result.removeprefix("user_1: ").rstrip("\n")
|
|
137
|
+
assert json.loads(json_part) == original
|
|
138
|
+
|
|
139
|
+
def test_output_is_utf8_encodable(self):
|
|
140
|
+
"""The LLM-visible string must be safe to send over UTF-8 transports
|
|
141
|
+
(HTTP body, logging sinks). ``ensure_ascii=False`` produces
|
|
142
|
+
unescaped surrogates only for malformed inputs; clean Unicode must
|
|
143
|
+
encode without error."""
|
|
144
|
+
ep = EpisodeResponse(
|
|
145
|
+
uid="1",
|
|
146
|
+
content="Mixed: ASCII + 中文 + 🚀",
|
|
147
|
+
producer_id="user_1",
|
|
148
|
+
producer_role="user",
|
|
149
|
+
created_at=datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc),
|
|
150
|
+
)
|
|
151
|
+
result = format_episodes([ep])
|
|
152
|
+
encoded = result.encode("utf-8")
|
|
153
|
+
assert encoded.decode("utf-8") == result
|
|
154
|
+
|
|
102
155
|
|
|
103
156
|
class TestFormatSemanticMemories:
|
|
104
157
|
"""Tests for format_semantic_memories."""
|
|
@@ -146,6 +199,32 @@ class TestFormatSemanticMemories:
|
|
|
146
199
|
"background": {"role": "engineer"},
|
|
147
200
|
}
|
|
148
201
|
|
|
202
|
+
def test_non_ascii_value_preserved_literally(self):
|
|
203
|
+
feature = SemanticFeature(
|
|
204
|
+
category="profile",
|
|
205
|
+
tag="prefs",
|
|
206
|
+
feature_name="favorite_food",
|
|
207
|
+
value="寿司 🍣",
|
|
208
|
+
)
|
|
209
|
+
result = format_semantic_memories([feature])
|
|
210
|
+
assert "寿司" in result
|
|
211
|
+
assert "🍣" in result
|
|
212
|
+
assert "\\u" not in result
|
|
213
|
+
# And the JSON is still valid.
|
|
214
|
+
assert json.loads(result) == {"prefs": {"favorite_food": "寿司 🍣"}}
|
|
215
|
+
|
|
216
|
+
def test_non_ascii_tag_and_feature_name_preserved(self):
|
|
217
|
+
feature = SemanticFeature(
|
|
218
|
+
category="profile",
|
|
219
|
+
tag="préférences",
|
|
220
|
+
feature_name="種類",
|
|
221
|
+
value="ramen",
|
|
222
|
+
)
|
|
223
|
+
result = format_semantic_memories([feature])
|
|
224
|
+
assert "préférences" in result
|
|
225
|
+
assert "種類" in result
|
|
226
|
+
assert json.loads(result) == {"préférences": {"種類": "ramen"}}
|
|
227
|
+
|
|
149
228
|
def test_metadata_excluded(self):
|
|
150
229
|
feature = SemanticFeature(
|
|
151
230
|
set_id="set_1",
|
|
@@ -43,9 +43,9 @@ def format_episodes(episodes: Iterable[EpisodeResponse | Episode]) -> str:
|
|
|
43
43
|
if episode.created_at is not None:
|
|
44
44
|
date_str = episode.created_at.strftime("%A, %B %d, %Y")
|
|
45
45
|
time_str = episode.created_at.strftime("%I:%M %p")
|
|
46
|
-
result += f"[{date_str} at {time_str}] {episode.producer_id}: {json.dumps(episode.content)}\n"
|
|
46
|
+
result += f"[{date_str} at {time_str}] {episode.producer_id}: {json.dumps(episode.content, ensure_ascii=False)}\n"
|
|
47
47
|
else:
|
|
48
|
-
result += f"{episode.producer_id}: {json.dumps(episode.content)}\n"
|
|
48
|
+
result += f"{episode.producer_id}: {json.dumps(episode.content, ensure_ascii=False)}\n"
|
|
49
49
|
return result
|
|
50
50
|
|
|
51
51
|
|
|
@@ -66,7 +66,7 @@ def format_semantic_memories(features: Iterable[SemanticFeature]) -> str:
|
|
|
66
66
|
structured: dict[str, dict[str, str]] = {}
|
|
67
67
|
for feature in features:
|
|
68
68
|
structured.setdefault(feature.tag, {})[feature.feature_name] = feature.value
|
|
69
|
-
return json.dumps(structured)
|
|
69
|
+
return json.dumps(structured, ensure_ascii=False)
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def format_search_result(result: SearchResult) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_integration_complete.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/requires.txt
RENAMED
|
File without changes
|
{memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/top_level.txt
RENAMED
|
File without changes
|