unique-user-memory 2026.34.0.dev5__tar.gz → 2026.34.0.dev7__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.
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-user-memory
3
- Version: 2026.34.0.dev5
3
+ Version: 2026.34.0.dev7
4
4
  Summary:
5
5
  Author: Fabian Schläpfer
6
6
  Author-email: Fabian Schläpfer <fabian@unique.ch>
7
7
  License: Proprietary
8
8
  Requires-Dist: jinja2>=3.1.6
9
9
  Requires-Dist: pydantic>=2.8.2
10
- Requires-Dist: unique-sdk>=2026.34.0.dev7,<2026.34.0rc0
11
- Requires-Dist: unique-toolkit>=2026.34.0.dev16,<2026.34.0rc0
10
+ Requires-Dist: unique-sdk>=2026.34.0.dev10,<2026.34.0rc0
11
+ Requires-Dist: unique-toolkit>=2026.34.0.dev19,<2026.34.0rc0
12
12
  Requires-Python: >=3.12, <4
13
13
  Description-Content-Type: text/markdown
14
14
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_user_memory"
3
- version = "2026.34.0.dev5"
3
+ version = "2026.34.0.dev7"
4
4
  description = ""
5
5
  authors = [
6
6
  { name = "Fabian Schläpfer", email = "fabian@unique.ch" },
@@ -11,8 +11,8 @@ requires-python = ">=3.12,<4"
11
11
  dependencies = [
12
12
  "jinja2>=3.1.6",
13
13
  "pydantic>=2.8.2",
14
- "unique-sdk>=2026.34.0.dev7,<2026.34.0rc0",
15
- "unique-toolkit>=2026.34.0.dev16,<2026.34.0rc0",
14
+ "unique-sdk>=2026.34.0.dev10,<2026.34.0rc0",
15
+ "unique-toolkit>=2026.34.0.dev19,<2026.34.0rc0",
16
16
  ]
17
17
 
18
18
  [dependency-groups]
@@ -13,7 +13,7 @@ from unique_toolkit.chat.schemas import MessageLogStatus
13
13
  from unique_toolkit.language_model.default_language_model import (
14
14
  DEFAULT_LANGUAGE_MODEL,
15
15
  )
16
- from unique_toolkit.language_model.infos import LanguageModelInfo
16
+ from unique_toolkit.language_model.infos import LanguageModelInfo, LanguageModelName
17
17
  from unique_toolkit.language_model.invocation_stats import LanguageModelInvocationStats
18
18
  from unique_toolkit.language_model.schemas import (
19
19
  LanguageModelAssistantMessage,
@@ -25,6 +25,7 @@ from unique_toolkit.language_model.schemas import (
25
25
  from unique_user_memory.config import UserMemoryConfig
26
26
  from unique_user_memory.user_memory import (
27
27
  UserMemoryState,
28
+ _gate_max_tokens,
28
29
  _sanitize_for_xml_context,
29
30
  condense_user_memory,
30
31
  consolidate_user_memory,
@@ -621,6 +622,47 @@ async def test_should_consolidate_memory_returns_false_on_noop(
621
622
  )
622
623
 
623
624
 
625
+ def test_gate_max_tokens_depends_on_reasoning_capability() -> None:
626
+ reasoning_model = LanguageModelInfo.from_name(
627
+ LanguageModelName.AZURE_GPT_5_2025_0807
628
+ )
629
+
630
+ assert _gate_max_tokens(language_model=_TEST_LANGUAGE_MODEL) == 4
631
+ assert _gate_max_tokens(language_model=reasoning_model) == 64
632
+
633
+
634
+ @pytest.mark.asyncio
635
+ async def test_should_consolidate_memory_uses_reasoning_safe_gate_budget(
636
+ monkeypatch: pytest.MonkeyPatch,
637
+ ) -> None:
638
+ response = MagicMock()
639
+ response.choices[0].message.content = "NOOP"
640
+ llm_service = MagicMock()
641
+ llm_service.complete_async = AsyncMock(return_value=response)
642
+ monkeypatch.setattr(
643
+ "unique_user_memory.user_memory.LanguageModelService",
644
+ MagicMock(return_value=llm_service),
645
+ )
646
+ reasoning_model = LanguageModelInfo.from_name(
647
+ LanguageModelName.AZURE_GPT_5_2025_0807
648
+ )
649
+
650
+ result = await should_consolidate_memory(
651
+ current_memory=empty_profile("user_1"),
652
+ user_id="user_1",
653
+ user_message="hello",
654
+ assistant_message="hi",
655
+ language_model=reasoning_model,
656
+ event=MagicMock(),
657
+ logger=MagicMock(),
658
+ )
659
+
660
+ assert result is False
661
+ assert (
662
+ llm_service.complete_async.call_args.kwargs["other_options"]["max_tokens"] == 64
663
+ )
664
+
665
+
624
666
  @pytest.mark.asyncio
625
667
  async def test_should_consolidate_memory_returns_true_on_update(
626
668
  monkeypatch: pytest.MonkeyPatch,
@@ -20,7 +20,10 @@ from unique_toolkit.language_model import (
20
20
  TypeDecoder,
21
21
  TypeEncoder,
22
22
  )
23
- from unique_toolkit.language_model.infos import LanguageModelInfo
23
+ from unique_toolkit.language_model.infos import (
24
+ LanguageModelInfo,
25
+ ModelCapabilities,
26
+ )
24
27
  from unique_toolkit.language_model.invocation_stats import (
25
28
  LanguageModelInvocationStats,
26
29
  )
@@ -54,6 +57,11 @@ async def noop_update_callback() -> None:
54
57
  # The gate only ever replies with the single word UPDATE or NOOP; a tiny
55
58
  # output budget keeps the common (NOOP) path cheap and fast.
56
59
  _GATE_MAX_TOKENS = 4
60
+ # Reasoning models spend `max_completion_tokens` on internal reasoning before
61
+ # emitting the answer; a 4-token budget truncates the reply to empty and the
62
+ # gate silently degrades to always-UPDATE. Give them enough room to reason and
63
+ # still emit the single-word answer.
64
+ _GATE_MAX_TOKENS_REASONING = 64
57
65
  # When condensing an oversized profile, aim below the hard cap so the LLM
58
66
  # output leaves headroom and the hard-cut safety net rarely has to fire.
59
67
  _CONDENSE_TARGET_RATIO = 0.9
@@ -110,6 +118,12 @@ class UserMemoryState:
110
118
  load_invocation_stats: tuple[LanguageModelInvocationStats, ...] = ()
111
119
 
112
120
 
121
+ def _gate_max_tokens(*, language_model: LanguageModelInfo) -> int:
122
+ if ModelCapabilities.REASONING in language_model.capabilities:
123
+ return _GATE_MAX_TOKENS_REASONING
124
+ return _GATE_MAX_TOKENS
125
+
126
+
113
127
  def _get_model_tokenizer(
114
128
  *,
115
129
  language_model: LanguageModelInfo,
@@ -684,7 +698,9 @@ async def should_consolidate_memory(
684
698
  response = await llm_service.complete_async(
685
699
  messages=messages,
686
700
  model_name=language_model.name,
687
- other_options={"max_tokens": _GATE_MAX_TOKENS},
701
+ other_options={
702
+ "max_tokens": _gate_max_tokens(language_model=language_model)
703
+ },
688
704
  )
689
705
  except Exception as exc:
690
706
  logger.warning(