unique-user-memory 2026.34.0.dev4__tar.gz → 2026.34.0.dev6__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.
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/PKG-INFO +3 -3
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/pyproject.toml +3 -3
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/config.py +1 -1
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/tests/test_user_memory.py +43 -1
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/user_memory.py +18 -2
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/README.md +0 -0
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/__init__.py +0 -0
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/user_memory_message_log.py +0 -0
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/user_memory_postprocessor.py +0 -0
- {unique_user_memory-2026.34.0.dev4 → unique_user_memory-2026.34.0.dev6}/unique_user_memory/user_memory_prompts.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-user-memory
|
|
3
|
-
Version: 2026.34.0.
|
|
3
|
+
Version: 2026.34.0.dev6
|
|
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.
|
|
11
|
-
Requires-Dist: unique-toolkit>=2026.34.0.
|
|
10
|
+
Requires-Dist: unique-sdk>=2026.34.0.dev7,<2026.34.0rc0
|
|
11
|
+
Requires-Dist: unique-toolkit>=2026.34.0.dev17,<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.
|
|
3
|
+
version = "2026.34.0.dev6"
|
|
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.
|
|
15
|
-
"unique-toolkit>=2026.34.0.
|
|
14
|
+
"unique-sdk>=2026.34.0.dev7,<2026.34.0rc0",
|
|
15
|
+
"unique-toolkit>=2026.34.0.dev17,<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
|
|
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={
|
|
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(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|