From b88de1e3b2643c2e8193e418374c1ddae6b344aa Mon Sep 17 00:00:00 2001 From: David Myriel Date: Tue, 26 May 2026 15:25:31 +0200 Subject: [PATCH 1/3] docs: clarify region requirement for S3-compatible endpoints (#851) --- apps/docs/connectors/s3.mdx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/docs/connectors/s3.mdx b/apps/docs/connectors/s3.mdx index fde1bed70..438c5d44f 100644 --- a/apps/docs/connectors/s3.mdx +++ b/apps/docs/connectors/s3.mdx @@ -4,7 +4,7 @@ description: "Connect Amazon S3 or S3-compatible storage to sync files into your icon: "aws" --- -Connect Amazon S3 buckets or S3-compatible storage services (MinIO, DigitalOcean Spaces, Cloudflare R2) to sync files into your Supermemory knowledge base. +Connect Amazon S3 buckets or S3-compatible storage services (MinIO, DigitalOcean Spaces, Cloudflare R2, Tigris) to sync files into your Supermemory knowledge base. The S3 connector requires a **Scale Plan** or higher. You can also create S3 connections directly from the [Supermemory Console](https://console.supermemory.ai). @@ -78,7 +78,7 @@ For S3, provider-specific connection fields are passed inside the top-level `met | `accessKeyId` | `metadata.accessKeyId` | Yes | AWS access key ID or S3-compatible service key | | `secretAccessKey` | `metadata.secretAccessKey` | Yes | AWS secret access key | | `bucket` | `metadata.bucket` | Yes | S3 bucket name | -| `region` | `metadata.region` | Yes | AWS region (e.g., `us-east-1`). Use `auto` for Cloudflare R2. | +| `region` | `metadata.region` | Yes | AWS region (e.g., `us-east-1`). Use `auto` for S3-compatible providers that don't expose AWS-style regions (MinIO, R2, Tigris). | | `endpoint` | `metadata.endpoint` | No | Custom endpoint for S3-compatible services | | `prefix` | `metadata.prefix` | No | Key prefix filter (e.g., `documents/`) | | `containerTagRegex` | `metadata.containerTagRegex` | No | Regex to extract container tags from file paths | @@ -91,7 +91,7 @@ In the Python SDK, use `container_tags` for the top-level option, but keep S3 me ## S3-Compatible Services -Use `metadata.endpoint` to connect to S3-compatible storage: +Use `metadata.endpoint` to connect to S3-compatible storage. These services don't use AWS-style regions, so set `metadata.region` to `auto` — the value is still required for request signing but the service ignores it. ```typescript // MinIO @@ -100,7 +100,7 @@ const connection = await client.connections.create('s3', { accessKeyId: 'minio-key', secretAccessKey: 'minio-secret', bucket: 'my-bucket', - region: 'us-east-1', + region: 'auto', endpoint: 'https://minio.example.com' }, containerTags: ['minio-sync'] @@ -113,6 +113,7 @@ Common S3-compatible endpoint values: |---------|----------------------|-------------------| | DigitalOcean Spaces | `https://nyc3.digitaloceanspaces.com` | `nyc3` | | Cloudflare R2 | `https://.r2.cloudflarestorage.com` | `auto` | +| Tigris | `https://t3.storage.dev` | `auto` | Cloudflare R2 example: From fbefac75ebd93f70a229a6a4d378204cc8333643 Mon Sep 17 00:00:00 2001 From: vimzh Date: Tue, 26 May 2026 22:16:25 +0530 Subject: [PATCH 2/3] fix(onboarding): fail fast when EXA_API_KEY is missing (#995) --- .../app/api/onboarding/extract-content/route.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/web/app/api/onboarding/extract-content/route.ts b/apps/web/app/api/onboarding/extract-content/route.ts index 6cdb40d5c..9322f3242 100644 --- a/apps/web/app/api/onboarding/extract-content/route.ts +++ b/apps/web/app/api/onboarding/extract-content/route.ts @@ -9,8 +9,22 @@ interface ExaApiResponse { results: ExaContentResult[] } +const exaApiKey = process.env.EXA_API_KEY +if (!exaApiKey) { + console.error( + "EXA_API_KEY is not configured; /api/onboarding/extract-content will return 503", + ) +} + export async function POST(request: Request) { try { + if (!exaApiKey) { + return Response.json( + { error: "Content extraction is unavailable" }, + { status: 503 }, + ) + } + const { urls } = await request.json() if (!Array.isArray(urls) || urls.length === 0) { @@ -30,7 +44,7 @@ export async function POST(request: Request) { const response = await fetch("https://api.exa.ai/contents", { method: "POST", headers: { - "x-api-key": process.env.EXA_API_KEY ?? "", + "x-api-key": exaApiKey, "Content-Type": "application/json", }, body: JSON.stringify({ From 55445bff1d09bf17bf3c526ea291831e33e5d6fc Mon Sep 17 00:00:00 2001 From: devteamaegis Date: Tue, 26 May 2026 12:58:35 -0400 Subject: [PATCH 3/3] fix(openai-sdk): don't inject empty system prompt when no memories are found (#999) Co-authored-by: Ishaan Samantray --- .../src/supermemory_openai/middleware.py | 3 + .../tests/test_middleware.py | 59 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/packages/openai-sdk-python/src/supermemory_openai/middleware.py b/packages/openai-sdk-python/src/supermemory_openai/middleware.py index db289cc89..4d8762e17 100644 --- a/packages/openai-sdk-python/src/supermemory_openai/middleware.py +++ b/packages/openai-sdk-python/src/supermemory_openai/middleware.py @@ -193,6 +193,9 @@ async def add_system_prompt( }, ) + if not memories: + return messages + if system_prompt_exists: logger.debug("Added memories to existing system prompt") return [ diff --git a/packages/openai-sdk-python/tests/test_middleware.py b/packages/openai-sdk-python/tests/test_middleware.py index b1dd80726..de4004ac4 100644 --- a/packages/openai-sdk-python/tests/test_middleware.py +++ b/packages/openai-sdk-python/tests/test_middleware.py @@ -318,6 +318,65 @@ async def test_existing_system_prompt_enhancement( assert "User prefers Python" in system_message["content"] + @pytest.mark.asyncio + async def test_empty_memories_do_not_modify_messages( + self, mock_async_openai_client, mock_openai_response + ): + """No system prompt should be injected when the memory lookup returns nothing. + + Previously, ``add_system_prompt`` would still modify messages even when + ``memories`` resolved to an empty string: it appended `` \\n `` (whitespace) to + any existing system prompt, or prepended a new ``{"role": "system", "content": ""}`` + message when none existed. Both outcomes pollute the conversation context with + meaningless tokens and can confuse the downstream model. + """ + original_create = AsyncMock(return_value=mock_openai_response) + mock_async_openai_client.chat.completions.create = original_create + + empty_response = { + "profile": {"static": [], "dynamic": []}, + "searchResults": {"results": []}, + } + + with patch.dict(os.environ, {"SUPERMEMORY_API_KEY": "test-key"}): + with patch("supermemory_openai.middleware.supermemory_profile_search") as mock_search: + mock_search.return_value = Mock() + mock_search.return_value.profile = empty_response["profile"] + mock_search.return_value.search_results = empty_response["searchResults"] + + wrapped_client = with_supermemory( + mock_async_openai_client, + OpenAIMiddlewareOptions( + container_tag="user-123", custom_id="test-conv", mode="full" + ), + ) + + # Case 1: no pre-existing system prompt — no system message should be prepended + user_only_messages = [{"role": "user", "content": "Hello"}] + await wrapped_client.chat.completions.create( + model="gpt-4", messages=user_only_messages + ) + sent_messages = original_create.call_args[1]["messages"] + assert sent_messages == user_only_messages, ( + "An empty-memory response must not prepend a blank system message" + ) + + original_create.reset_mock() + + # Case 2: existing system prompt — it must not be modified + with_system = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + ] + await wrapped_client.chat.completions.create( + model="gpt-4", messages=with_system + ) + sent_messages = original_create.call_args[1]["messages"] + assert sent_messages[0]["content"] == "You are helpful.", ( + "An empty-memory response must not append whitespace to the existing system prompt" + ) + + class TestMemoryStorage: """Test memory storage functionality."""