diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ce7c4..e48a90b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/memhub/README.md b/memhub/README.md index bfffc98..356954c 100644 --- a/memhub/README.md +++ b/memhub/README.md @@ -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`。 diff --git a/memhub/SBOM.spdx.json b/memhub/SBOM.spdx.json index 587d80a..5afc7e3 100644 --- a/memhub/SBOM.spdx.json +++ b/memhub/SBOM.spdx.json @@ -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", diff --git a/memhub/THIRD_PARTY_NOTICES.md b/memhub/THIRD_PARTY_NOTICES.md index 5f64b49..13a4389 100644 --- a/memhub/THIRD_PARTY_NOTICES.md +++ b/memhub/THIRD_PARTY_NOTICES.md @@ -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 diff --git a/memhub/apps/core/pyproject.toml b/memhub/apps/core/pyproject.toml index 46323d4..10f9e49 100644 --- a/memhub/apps/core/pyproject.toml +++ b/memhub/apps/core/pyproject.toml @@ -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", diff --git a/memhub/apps/core/src/one_memory/hy_runtime_config.py b/memhub/apps/core/src/one_memory/hy_runtime_config.py index dea7dc1..b0782eb 100644 --- a/memhub/apps/core/src/one_memory/hy_runtime_config.py +++ b/memhub/apps/core/src/one_memory/hy_runtime_config.py @@ -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"] diff --git a/memhub/apps/core/src/one_memory/hy_sdk_migration.py b/memhub/apps/core/src/one_memory/hy_sdk_migration.py index cd51967..f061256 100644 --- a/memhub/apps/core/src/one_memory/hy_sdk_migration.py +++ b/memhub/apps/core/src/one_memory/hy_sdk_migration.py @@ -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 diff --git a/memhub/apps/core/src/one_memory/hy_sdk_runtime.py b/memhub/apps/core/src/one_memory/hy_sdk_runtime.py index acb1db6..56b6b25 100644 --- a/memhub/apps/core/src/one_memory/hy_sdk_runtime.py +++ b/memhub/apps/core/src/one_memory/hy_sdk_runtime.py @@ -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 diff --git a/memhub/apps/core/tests/test_sdk_migration_bundle.py b/memhub/apps/core/tests/test_sdk_migration_bundle.py index b1c8f12..3c22f4c 100644 --- a/memhub/apps/core/tests/test_sdk_migration_bundle.py +++ b/memhub/apps/core/tests/test_sdk_migration_bundle.py @@ -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", diff --git a/memhub/apps/core/tests/test_ui_language.py b/memhub/apps/core/tests/test_ui_language.py index cf751f8..09ea8fb 100644 --- a/memhub/apps/core/tests/test_ui_language.py +++ b/memhub/apps/core/tests/test_ui_language.py @@ -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( diff --git a/memhub/apps/core/uv.lock b/memhub/apps/core/uv.lock index 0649259..37e4408 100644 --- a/memhub/apps/core/uv.lock +++ b/memhub/apps/core/uv.lock @@ -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" }, diff --git a/memhub/docs/ARCHITECTURE.md b/memhub/docs/ARCHITECTURE.md index 8aae810..6993784 100644 --- a/memhub/docs/ARCHITECTURE.md +++ b/memhub/docs/ARCHITECTURE.md @@ -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). diff --git a/memhub/docs/cli/EXPERIENCE.md b/memhub/docs/cli/EXPERIENCE.md index 437cbe8..b5d4fb9 100644 --- a/memhub/docs/cli/EXPERIENCE.md +++ b/memhub/docs/cli/EXPERIENCE.md @@ -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) diff --git a/memhub/scripts/generate_sbom.py b/memhub/scripts/generate_sbom.py index 8e495ad..55f6125 100644 --- a/memhub/scripts/generate_sbom.py +++ b/memhub/scripts/generate_sbom.py @@ -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): diff --git a/memhub/scripts/install-docs.test.mjs b/memhub/scripts/install-docs.test.mjs index 23f9833..4eedd29 100644 --- a/memhub/scripts/install-docs.test.mjs +++ b/memhub/scripts/install-docs.test.mjs @@ -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); diff --git a/memhub/scripts/test_generate_sbom.py b/memhub/scripts/test_generate_sbom.py index db2ab25..78e6ec4 100644 --- a/memhub/scripts/test_generate_sbom.py +++ b/memhub/scripts/test_generate_sbom.py @@ -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"]