build(memhub): pin hy-memory 2.0.11 and set the SDK language directly

Core pins and locks the published hy-memory 2.0.11 from PyPI, which adds
MemoryConfig.language and the prompt registry. apply_sdk_language now sets
the language directly; the guard that skipped it on an SDK without the
field (and its debug log) is gone, and the tests use the real field. The
version constant, README, architecture notes, SBOM, notices and their tests
follow. CHANGELOG records 2.0.11 as published.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
alvinfeiandClaude Opus 5.5 committed 2026-11-01 22:22:32 +08:00
1 parent 35128f2c84
commit 7a2ff2f4c4
16 files changed
+35 -88

No files matched your search

+2
View File
@@ -6,6 +6,8 @@ Hy Memory and MemHub are versioned and released independently.
## Hy Memory 2.0.11 — 2026-11-01
- Published
[`hy-memory==2.0.11`](https://pypi.org/project/hy-memory/2.0.11/).
- LLM prompts now ship in 11 languages: `en`, `zh-Hans`,
`zh-Hant`, `ja`, `es`, `de`, `pt`, `fr`, `ko`, `ru` and `id`. They live in
one registry, `hy_memory/prompts/`, with one module per language. Call
+2 -2
View File
@@ -18,7 +18,7 @@ If you run into a problem, please open an Issue with the version, platform,repro
## 发行与平台合同
源码当前固定使用公开 PyPI 正式版 `hy-memory==2.0.10`。MemHub 源码版本为 `1.0.5`
源码当前固定使用公开 PyPI 正式版 `hy-memory==2.0.11`。MemHub 源码版本为 `1.0.5`
(build 36),GitHub 仓库为
[`Tencent-Hunyuan/Hy-Memory`](https://github.com/Tencent-Hunyuan/Hy-Memory),npm 包名为
`memhub-cli`。当前代码架构见
@@ -118,7 +118,7 @@ MemHub 的 SQLite 数据默认位于 `~/.one-memory`,不在 npm 包或 Runtime
## Hy Memory
SDK 依赖固定为公开 PyPI 正式版 `hy-memory==2.0.10`。SDK 配置、权限边界及 search
SDK 依赖固定为公开 PyPI 正式版 `hy-memory==2.0.11`。SDK 配置、权限边界及 search
返回格式见 [`MEMORY.md`](MEMORY.md)。HY search 返回的 `content` 已包含 SDK 统一生成的
逻辑时间和演化上下文;调用方不应再次转换时间戳或重复拼接 `When`。
+1 -1
View File
@@ -2352,7 +2352,7 @@
{
"SPDXID": "SPDXRef-Package-hy-memory",
"name": "hy-memory",
"versionInfo": "2.0.10",
"versionInfo": "2.0.11",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "Apache-2.0",
+1 -1
View File
@@ -9,7 +9,7 @@ Per-package license texts are not reproduced here yet; the license
column is filled in as part of release review, and the SBOM records
`NOASSERTION` until then rather than guessing.
## hy-memory 2.0.10
## hy-memory 2.0.11
License: Apache-2.0
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies = [
"fastapi>=0.141.1",
"filelock>=3.32.4",
# Public SDK version validated against MemHub's permission and recall contracts.
"hy-memory==2.0.10",
"hy-memory==2.0.11",
# chromadb arrives through the HY SDK; locked here so an unrelated upgrade
# cannot move the SDK's store. Built-in embedding consumes numpy directly.
"chromadb==1.5.9",
@@ -14,7 +14,7 @@ from pathlib import Path
from typing import Literal, cast
HY_MEMORY_DISTRIBUTION = "hy-memory"
HY_MEMORY_VERSION = "2.0.10"
HY_MEMORY_VERSION = "2.0.11"
HY_MEMORY_NAMESPACE = "governed-v2"
DiagnosticLogLevel = Literal["error", "warning", "info", "debug"]
@@ -503,7 +503,7 @@ def _ensure_coding_schema(path: Path) -> None:
CodingMemoryStore,
)
# Relies on the pinned SDK's internals (hy-memory==2.0.10 is pinned exactly).
# Relies on the pinned SDK's internals (hy-memory==2.0.11 is pinned exactly).
store = CodingMemoryStore.__new__(CodingMemoryStore)
store._db_path = str(path)
store._conn = None
@@ -9,7 +9,6 @@ from __future__ import annotations
import asyncio
import hashlib
import logging
import os
import threading
from collections.abc import Callable, Coroutine, Iterable, Iterator, Mapping
@@ -58,8 +57,6 @@ from one_memory.model_profiles import (
disable_thinking_params,
)
logger = logging.getLogger(__name__)
_RUNTIME_LOCK = threading.RLock()
BUILTIN_RETRIEVAL_PROFILE_ID = "builtin-retrieval"
_LOCAL_ONNX_PLACEHOLDER_URL = "http://127.0.0.1/local-onnx"
@@ -354,9 +351,6 @@ def apply_sdk_features(
return config
_LANGUAGE_UNSUPPORTED_LOGGED = False
class LanguageKwargs(TypedDict, total=False):
language: str
@@ -371,22 +365,14 @@ def language_kwargs(language: str | None) -> LanguageKwargs:
def apply_sdk_language(config: MemoryConfig, language: str | None) -> MemoryConfig:
"""Set the SDK prompt language, when there is one and the SDK has the field.
"""Set the SDK prompt language (`MemoryConfig.language`), when there is one.
None (zh-Hans, or no choice) sets nothing: the SDK detects zh/en as it
always did. An SDK without `MemoryConfig.language` keeps working without
it; that is logged once at debug level. Like a feature, the language never
touches `vector_store`, so the collection identity never depends on it.
always did. Like a feature, the language never touches `vector_store`, so
the collection identity never depends on it.
"""
global _LANGUAGE_UNSUPPORTED_LOGGED
if language is None:
return config
if not hasattr(config, "language"):
if not _LANGUAGE_UNSUPPORTED_LOGGED:
_LANGUAGE_UNSUPPORTED_LOGGED = True
logger.debug("hy_sdk_language_unsupported language=%s", language)
return config
config.language = language
if language is not None:
config.language = language
return config
@@ -91,7 +91,7 @@ def test_bundle_contains_management_sdk_config_and_verified_manifest(tmp_path):
assert manifest["bundle_id"] == "migration-1"
assert manifest["sdk_namespace"] == "governed-v2"
assert manifest["runtime_build"] == "build-13"
assert manifest["snapshot_sdk_version"] == "2.0.10"
assert manifest["snapshot_sdk_version"] == "2.0.11"
paths = {item["path"]: item for item in manifest["files"]}
assert {
"management/one-memory.sqlite3",
+9 -50
View File
@@ -2,8 +2,7 @@
The owner's rule: zh-Hans, or no explicit choice, passes no language, so the
SDK keeps detecting zh/en exactly as before; every other code (en included)
is passed as `MemoryConfig.language`. The pinned SDK may not have that field
yet: then nothing is passed and that is logged once. A change rebuilds the
is passed as `MemoryConfig.language` (hy-memory 2.0.11). A change rebuilds the
handles the way a mode or feature change does, and the language never touches
the vector collection identity.
"""
@@ -13,7 +12,6 @@ from __future__ import annotations
import asyncio
import dataclasses
import json
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from types import SimpleNamespace
@@ -34,31 +32,10 @@ TOKEN = "ui-language-test"
PASSED = ("en", "zh-Hant", "ja", "es", "de", "pt", "fr", "ko", "ru", "id")
@dataclasses.dataclass
class _MemoryConfigWithLanguage(hy_sdk_runtime.MemoryConfig):
"""The released SDK's `MemoryConfig`, which adds `language`."""
language: str | None = None
@pytest.fixture
def sdk_with_language(monkeypatch):
monkeypatch.setattr(hy_sdk_runtime, "MemoryConfig", _MemoryConfigWithLanguage)
@pytest.fixture
def sdk_without_language(monkeypatch):
"""The pinned SDK, whose `MemoryConfig` has no `language` yet."""
monkeypatch.setattr(hy_sdk_runtime, "_LANGUAGE_UNSUPPORTED_LOGGED", False)
@dataclasses.dataclass
class Pinned(hy_sdk_runtime.MemoryConfig):
def __getattribute__(self, name):
if name == "language":
raise AttributeError(name)
return super().__getattribute__(name)
monkeypatch.setattr(hy_sdk_runtime, "MemoryConfig", Pinned)
@pytest.fixture(autouse=True)
def _clean_sdk_env(monkeypatch):
"""The SDK's own `MEMORY_LANGUAGE` feeds its default; tests start without it."""
monkeypatch.delenv("MEMORY_LANGUAGE", raising=False)
def _config(tmp_path: Path, language: str | None):
@@ -92,19 +69,19 @@ def test_only_zh_hans_and_no_choice_leave_the_sdk_to_detect() -> None:
assert sdk_language(code) == code
def test_zh_hans_and_no_choice_build_the_config_of_before(tmp_path, sdk_with_language) -> None:
def test_zh_hans_and_no_choice_build_the_config_of_before(tmp_path) -> None:
before = repr(dataclasses.asdict(_config(tmp_path, None)))
assert _config(tmp_path, None).language is None
assert repr(dataclasses.asdict(_config(tmp_path, sdk_language("zh-Hans")))) == before
@pytest.mark.parametrize("code", ("en", "ja"))
def test_a_passed_code_reaches_the_memory_config(tmp_path, sdk_with_language, code) -> None:
@pytest.mark.parametrize("code", PASSED)
def test_a_passed_code_reaches_the_memory_config(tmp_path, code) -> None:
config = _config(tmp_path, sdk_language(code))
assert config.language == code
def test_the_language_never_changes_the_collection_identity(tmp_path, sdk_with_language) -> None:
def test_the_language_never_changes_the_collection_identity(tmp_path) -> None:
def identity(config):
return (
dataclasses.asdict(config.vector_store),
@@ -119,24 +96,6 @@ def test_the_language_never_changes_the_collection_identity(tmp_path, sdk_with_l
assert identity(_config(tmp_path, code)) == baseline
def test_an_sdk_without_the_field_is_left_alone_and_logged_once(
tmp_path, sdk_without_language, caplog
) -> None:
caplog.set_level(logging.DEBUG, logger=hy_sdk_runtime.__name__)
before = repr(dataclasses.asdict(_config(tmp_path, None)))
assert repr(dataclasses.asdict(_config(tmp_path, "ja"))) == before
assert repr(dataclasses.asdict(_config(tmp_path, "fr"))) == before
logged = [r for r in caplog.records if "hy_sdk_language_unsupported" in r.getMessage()]
assert len(logged) == 1 and logged[0].levelno == logging.DEBUG
def test_the_pinned_sdk_builds_with_a_language(tmp_path) -> None:
"""Whatever the pinned SDK is, a language never breaks building a config."""
config = _config(tmp_path, "ja")
if hasattr(config, "language"):
assert config.language == "ja"
def test_acquire_client_passes_the_language_only_when_there_is_one(tmp_path, monkeypatch) -> None:
calls: list[dict] = []
monkeypatch.setattr(
+4 -4
View File
@@ -991,7 +991,7 @@ wheels = [
[[package]]
name = "hy-memory"
version = "2.0.10"
version = "2.0.11"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "chromadb" },
@@ -1008,9 +1008,9 @@ dependencies = [
{ name = "scikit-learn" },
{ name = "tiktoken" },
]
sdist = { url = "https://files.pythonhosted.org/packages/86/ed/1e1264d5b8741237c34fdaf17ae68bf5d16d403ca73c054997a502cd06a3/hy_memory-2.0.10.tar.gz", hash = "sha256:0841a441862e2427f75898bb8c958ed22fa083d2c0326298687ee129bcb729a8", size = 881659, upload-time = "2026-11-01T11:33:33.742Z" }
sdist = { url = "https://files.pythonhosted.org/packages/01/93/aec7d243259f6a315f38e8ebc21a9eea153a48fc13648fda149be7672788/hy_memory-2.0.11.tar.gz", hash = "sha256:96d6bd606d61052f127cd274aa6f8de8e7151d6250f1e89e4ef5b90e18ec7cb0", size = 1245738, upload-time = "2026-11-01T14:15:02.474Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/d6/c07a9d2d3af14da5a90c2bc466a46fc77068c24570fafe8f5729e46e2cae/hy_memory-2.0.10-py3-none-any.whl", hash = "sha256:54528d61a1730c7ea0f464f15144de3d1952f5d9aab1875bc841c97a193914b1", size = 947165, upload-time = "2026-11-01T11:33:31.66Z" },
{ url = "https://files.pythonhosted.org/packages/1a/85/1f5e6a13507a0cfcdce26f67c590df00d327a0f7d5c5b5483c3bd0fa2f85/hy_memory-2.0.11-py3-none-any.whl", hash = "sha256:c0173444a83c1197a49acaccb123f8286cea205b4838c1e83d106974f5bad259", size = 1315871, upload-time = "2026-11-01T14:14:58.764Z" },
]
[[package]]
@@ -1837,7 +1837,7 @@ requires-dist = [
{ name = "fastapi", specifier = ">=0.141.1" },
{ name = "filelock", specifier = ">=3.32.4" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "hy-memory", specifier = "==2.0.10" },
{ name = "hy-memory", specifier = "==2.0.11" },
{ name = "keyring", specifier = ">=25.7.0" },
{ name = "numpy", specifier = "==2.5.2" },
{ name = "onnxruntime", specifier = "==1.23.2" },
+1 -1
View File
@@ -282,7 +282,7 @@ Support is declared per immutable GitHub Release manifest:
- Linux x64/arm64 Runtime on native GHA runners.
- macOS arm64/x64 App/Runtime: Developer ID, notarization, native acceptance.
- Windows is not supported.
- `hy-memory==2.0.10` is the current stable SDK dependency; the release
- `hy-memory==2.0.11` is the current stable SDK dependency; the release
lock and [`../MEMORY.md`](../MEMORY.md) are authoritative when the pin changes.
See [`INSTALLATION.md`](INSTALLATION.md).
+1 -1
View File
@@ -195,7 +195,7 @@ node apps/cli/dist/one-memory.cjs settings language system # 跟随系统(
- 可选 11 种:`en`、`zh-Hans`、`zh-Hant`、`ja`、`es`、`de`、`pt`、`fr`、`ko`、`ru`、`id`,以及 `system`;旧代码 `zh-CN` 按 `zh-Hans` 处理。
- App 切换语言时会自动设置(Desktop 启动时也会同步一次已保存的选择)。全局设置,存于 `config.json` 的 `ui_language`;从未选择时不写入。
- `zh-Hans` 和 `system` 不向 SDK 传语言,SDK 照旧自动识别中文或英文;其余语言(包括 `en`)作为 `MemoryConfig.language` 传给 SDK,提取等提示使用该语言。
- 修改会像功能开关一样重建 SDK 客户端;不迁移数据,不改集合名。当前固定的 SDK 版本还没有 `language` 字段时只在 debug 日志记一次,不影响运行。
- 修改会像功能开关一样重建 SDK 客户端;不迁移数据,不改集合名(需要 hy-memory 2.0.11 的 `MemoryConfig.language`)。
- `status --json` 的 `pipeline.ui_language` / `pipeline.sdk_language`(`auto` 或语言代码)。API:`GET`/`PUT /api/v1/settings/ui-language`。
## 8. 召回(recall)
+1 -1
View File
@@ -36,7 +36,7 @@ MEMMY_LICENSE = (
MEMMY_PROVENANCE = MEMMY_LICENSE.with_name("PROVENANCE.md")
MEMMY_PIN = "25acd5f29fc7df73b943713abe61f1b2f9839887"
HY_MEMORY_NAME = "hy-memory"
HY_MEMORY_VERSION = "2.0.10"
HY_MEMORY_VERSION = "2.0.11"
class SbomError(RuntimeError):
+1 -1
View File
@@ -12,7 +12,7 @@ test("installation docs disclose publication and lifecycle boundaries", async ()
"utf8",
);
assert.match(readme, /`hy-memory==2\.0\.10`/);
assert.match(readme, /`hy-memory==2\.0\.11`/);
assert.match(readme, /`v1\.0\.5` 的发行目标是 Linux x64\/arm64 Runtime/);
assert.match(readme, /Linux 制品由 GitHub Actions/);
assert.match(readme, /macOS.*Developer ID 签名、公证/s);
+3 -3
View File
@@ -13,7 +13,7 @@ spec.loader.exec_module(module)
def test_hy_memory_notice_comes_from_the_installed_dist_info(tmp_path: Path) -> None:
dist_info = tmp_path / "hy_memory-2.0.10.dist-info"
dist_info = tmp_path / "hy_memory-2.0.11.dist-info"
licenses = dist_info / "licenses"
licenses.mkdir(parents=True)
(dist_info / "METADATA").write_text(
@@ -21,7 +21,7 @@ def test_hy_memory_notice_comes_from_the_installed_dist_info(tmp_path: Path) ->
(
"Metadata-Version: 2.4",
"Name: hy-memory",
"Version: 2.0.10",
"Version: 2.0.11",
"License-Expression: Apache-2.0",
"",
)
@@ -38,7 +38,7 @@ def test_hy_memory_notice_comes_from_the_installed_dist_info(tmp_path: Path) ->
notice = module.hy_memory_notice(dist_info)
assert notice["name"] == "hy-memory"
assert notice["version"] == "2.0.10"
assert notice["version"] == "2.0.11"
assert notice["license"] == "Apache-2.0"
assert notice["copyright"] == "Copyright (C) 2026 Tencent. All rights reserved."
assert "Licensed under the Apache License" in notice["text"]