From 9fd567984c14eb3fb4d370eccb6068ea975c9cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Thu, 8 Jan 2026 14:02:28 +0100 Subject: [PATCH] fix(mcp): add back bank list and create_bank tools (#123) * fix(mcp): add back bank list and create_bank tools * fix tests * fix tests --- hindsight-api/hindsight_api/api/mcp.py | 54 ++++++++++++++ hindsight-api/tests/test_mcp_routing.py | 5 +- hindsight-docs/docs/developer/multilingual.md | 70 +++++++++++++------ 3 files changed, 105 insertions(+), 24 deletions(-) diff --git a/hindsight-api/hindsight_api/api/mcp.py b/hindsight-api/hindsight_api/api/mcp.py index f8efad44..a881af09 100644 --- a/hindsight-api/hindsight_api/api/mcp.py +++ b/hindsight-api/hindsight_api/api/mcp.py @@ -187,6 +187,60 @@ def create_mcp_server(memory: MemoryEngine) -> FastMCP: logger.error(f"Error reflecting: {e}", exc_info=True) return f'{{"error": "{e}", "text": ""}}' + @mcp.tool() + async def list_banks() -> str: + """ + List all available memory banks. + + Use this tool to discover what memory banks exist in the system. + Each bank is an isolated memory store (like a separate "brain"). + + Returns: + JSON list of banks with their IDs, names, dispositions, and backgrounds. + """ + try: + banks = await memory.list_banks(request_context=RequestContext()) + return json.dumps({"banks": banks}, indent=2) + except Exception as e: + logger.error(f"Error listing banks: {e}", exc_info=True) + return f'{{"error": "{e}", "banks": []}}' + + @mcp.tool() + async def create_bank(bank_id: str, name: str | None = None, background: str | None = None) -> str: + """ + Create a new memory bank or get an existing one. + + Memory banks are isolated stores - each one is like a separate "brain" for a user/agent. + Banks are auto-created with default settings if they don't exist. + + Args: + bank_id: Unique identifier for the bank (e.g., 'user-123', 'agent-alpha') + name: Optional human-friendly name for the bank + background: Optional background context about the bank's owner/purpose + """ + try: + # get_bank_profile auto-creates bank if it doesn't exist + profile = await memory.get_bank_profile(bank_id, request_context=RequestContext()) + + # Update name/background if provided + if name is not None or background is not None: + await memory.update_bank( + bank_id, + name=name, + background=background, + request_context=RequestContext(), + ) + # Fetch updated profile + profile = await memory.get_bank_profile(bank_id, request_context=RequestContext()) + + # Serialize disposition if it's a Pydantic model + if "disposition" in profile and hasattr(profile["disposition"], "model_dump"): + profile["disposition"] = profile["disposition"].model_dump() + return json.dumps(profile, indent=2) + except Exception as e: + logger.error(f"Error creating bank: {e}", exc_info=True) + return f'{{"error": "{e}"}}' + return mcp diff --git a/hindsight-api/tests/test_mcp_routing.py b/hindsight-api/tests/test_mcp_routing.py index dd7c385f..d54e9f4d 100644 --- a/hindsight-api/tests/test_mcp_routing.py +++ b/hindsight-api/tests/test_mcp_routing.py @@ -9,6 +9,7 @@ def mock_memory(): """Create a mock MemoryEngine.""" memory = MagicMock() memory.retain_batch_async = AsyncMock() + memory.submit_async_retain = AsyncMock(return_value={"operation_id": "test-op-123"}) memory.recall_async = AsyncMock(return_value=MagicMock(results=[])) return memory @@ -44,11 +45,11 @@ async def test_mcp_tools_use_context_bank_id(mock_memory): assert "retain" in tools assert "recall" in tools - # Test retain with bank_id from context + # Test retain with bank_id from context (use async_processing=False for synchronous test) token = _current_bank_id.set("context-bank-id") try: retain_tool = tools["retain"] - result = await retain_tool.fn(content="test content", context="test_context") + result = await retain_tool.fn(content="test content", context="test_context", async_processing=False) assert "successfully" in result.lower() # Verify the memory was called with the context bank_id diff --git a/hindsight-docs/docs/developer/multilingual.md b/hindsight-docs/docs/developer/multilingual.md index 5f6c63cf..3177af0a 100644 --- a/hindsight-docs/docs/developer/multilingual.md +++ b/hindsight-docs/docs/developer/multilingual.md @@ -137,42 +137,68 @@ hindsight.retain( ## Supported Languages -Hindsight supports any language that your configured LLM can understand. This typically includes: +**Hindsight's multilingual support depends entirely on your LLM's language capabilities.** Hindsight instructs the LLM to detect the input language and respond in that same language. If your LLM supports a language, Hindsight will work with it. -| Language | Script | Example | -|----------|--------|---------| -| Chinese (Simplified) | 简体中文 | 张伟是软件工程师 | -| Chinese (Traditional) | 繁體中文 | 張偉是軟體工程師 | -| Japanese | 日本語 | 田中さんはエンジニアです | -| Korean | 한국어 | 김철수는 개발자입니다 | -| Arabic | العربية | أحمد مهندس برمجيات | -| Russian | Русский | Иван - разработчик | -| Spanish | Español | María es ingeniera | -| French | Français | Pierre est développeur | -| German | Deutsch | Hans ist Entwickler | -| And many more... | | | +Most modern LLMs (GPT-4, Claude, Gemini, Llama 3, etc.) support dozens of languages including: -The actual language support depends on your LLM provider's capabilities. +- **East Asian**: Chinese (Simplified/Traditional), Japanese, Korean +- **European**: Spanish, French, German, Italian, Portuguese, Dutch, Polish, Russian +- **Middle Eastern**: Arabic, Hebrew, Turkish +- **South Asian**: Hindi, Bengali, Tamil +- **Southeast Asian**: Thai, Vietnamese, Indonesian + +**To verify support for your target language**, test your LLM directly with content in that language. If the LLM can understand and generate text in the language, Hindsight will preserve it correctly. --- -## Best Practices +## Configuring for Multilingual Use -### 1. Keep Content in One Language Per Retain Call -While mixed content works, keeping each `retain` call in a single language produces more consistent results. +For optimal multilingual performance, you should configure all three components of the pipeline: -### 2. Query in the Same Language as Your Content -For best results, query using the same language as your stored content. Cross-language queries (e.g., English query for Chinese content) may work but results can vary. +### 1. LLM (Required) +Your LLM must support the target languages. Most modern LLMs do, but verify with your specific model. -### 3. Consider Embedding Model Language Support -The default embedding model (`BAAI/bge-small-en-v1.5`) is English-optimized. For better multilingual semantic search, consider using a multilingual embedding model: +### 2. Embedding Model (Recommended) +The default embedding model (`BAAI/bge-small-en-v1.5`) is **English-only**. For multilingual content, use a multilingual embedding model: ```bash # In your .env file HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-m3 ``` -The `bge-m3` model supports 100+ languages with better cross-lingual retrieval. +**Recommended multilingual embedding models:** +| Model | Languages | Notes | +|-------|-----------|-------| +| `BAAI/bge-m3` | 100+ | Best overall multilingual performance | +| `intfloat/multilingual-e5-large` | 100+ | Good alternative | +| `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | 50+ | Lighter weight | + +### 3. Reranker Model (Recommended) +The default reranker (`cross-encoder/ms-marco-MiniLM-L-6-v2`) is **English-only**. For multilingual content, use a multilingual reranker: + +```bash +# In your .env file +HINDSIGHT_API_RERANKER_LOCAL_MODEL=BAAI/bge-reranker-v2-m3 +``` + +**Recommended multilingual reranker models:** +| Model | Languages | Notes | +|-------|-----------|-------| +| `BAAI/bge-reranker-v2-m3` | 100+ | Best multilingual reranking | +| `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1` | 14 | Lighter alternative | + +--- + +## Best Practices + +### 1. Use Multilingual Models for Non-English Content +If you primarily work with non-English content, configure multilingual embedding and reranker models. English-only models will still store your content correctly, but semantic search quality will be degraded. + +### 2. Keep Content in One Language Per Retain Call +While mixed content works, keeping each `retain` call in a single language produces more consistent results. + +### 3. Query in the Same Language as Your Content +For best results, query using the same language as your stored content. Cross-language queries (e.g., English query for Chinese content) may work but results can vary depending on your embedding model. ---