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.
Files changed (24) hide show
  1. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/PKG-INFO +1 -1
  2. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_format.py +79 -0
  3. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/format.py +3 -3
  4. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/PKG-INFO +1 -1
  5. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/LICENSE +0 -0
  6. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/README.md +0 -0
  7. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/__init__.py +0 -0
  8. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_client.py +0 -0
  9. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_config.py +0 -0
  10. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_integration_complete.py +0 -0
  11. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_langgraph.py +0 -0
  12. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/client_tests/test_memory.py +0 -0
  13. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/pyproject.toml +0 -0
  14. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/setup.cfg +0 -0
  15. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/__init__.py +0 -0
  16. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/client.py +0 -0
  17. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/config.py +0 -0
  18. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/langgraph.py +0 -0
  19. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/memory.py +0 -0
  20. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client/project.py +0 -0
  21. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/SOURCES.txt +0 -0
  22. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/dependency_links.txt +0 -0
  23. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/requires.txt +0 -0
  24. {memmachine_client-0.3.7 → memmachine_client-0.3.8}/src/memmachine_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memmachine-client
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: MemMachine REST Client - A lightweight Python client library for MemMachine memory system
5
5
  Author-email: MemMachine <noreply@memmachine.ai>
6
6
  License-Expression: Apache-2.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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memmachine-client
3
- Version: 0.3.7
3
+ Version: 0.3.8
4
4
  Summary: MemMachine REST Client - A lightweight Python client library for MemMachine memory system
5
5
  Author-email: MemMachine <noreply@memmachine.ai>
6
6
  License-Expression: Apache-2.0