Skip to content

fix(tools): route Responses API memory saves through /v4/conversations - #1286

Open
abhay-codes07 wants to merge 3 commits into
supermemoryai:mainfrom
abhay-codes07:fix/openai-responses-conversation-save
Open

fix(tools): route Responses API memory saves through /v4/conversations#1286
abhay-codes07 wants to merge 3 commits into
supermemoryai:mainfrom
abhay-codes07:fix/openai-responses-conversation-save

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

What

Responses API memory saves silently failed whenever a customId was configured.

The chat-completions path hands addMemoryTool the messages plus API credentials, so a configured customId routes the save through /v4/conversations (with the internal conversation: routing prefix stripped back off). The Responses path built the same prefixed memoryCustomId but passed neither messages nor credentials:

operations.push(
    addMemoryTool(client, containerTag, content, memoryCustomId, logger),  // no messages, no apiKey, no baseUrl
)

So the conversation branch never ran and the save fell through to client.add() with conversation:<id> as the stored customId — a value customId validation rejects (alphanumeric/hyphen/underscore only). Since save errors are swallowed by design (memory persistence must never break the user's API call), every responses.create call with a customId saved nothing, invisibly.

Fix

  • The Responses path hands the input over as a single user message together with the credentials, mirroring the chat path — conversation grouping now works for the Responses API
  • The client.add fallback strips the internal conversation: prefix so the routing marker can never leak into a stored customId on any path (the chat path had the same leak whenever SUPERMEMORY_API_KEY was unset)

Testing

  • New mocked middleware tests (src/openai/middleware.test.ts): a customId save routes through addConversation with the un-prefixed conversation id and never touches client.add; the fallback stores the user's configured id, not the prefixed marker; the no-customId path is unchanged — two of the three tests fail against the previous implementation
  • vitest run — 3 pass
  • tsc --noEmit error count unchanged vs main (148 pre-existing), biome check clean

cc @MaheshtheDev

When a customId is configured, the chat-completions path hands
addMemoryTool the messages plus API credentials, so the save routes
through /v4/conversations with the internal "conversation:" prefix
stripped back off. The Responses path built the same prefixed
memoryCustomId but passed neither messages nor credentials, so the
conversation branch never ran and the save fell through to
client.add() with "conversation:<id>" as the stored customId — a value
customId validation rejects (alphanumeric/hyphen/underscore only). The
error is swallowed by design (memory saves must not break the user's
API call), which means every Responses API call with a customId
silently saved nothing.

Hand the input over as a single user message together with the
credentials, mirroring the chat path, so conversation grouping works
for the Responses API too. Also strip the internal prefix in the
client.add fallback so the routing marker can never leak into a stored
customId on any path (previously the chat path had the same leak
whenever SUPERMEMORY_API_KEY was unset).

Covered with mocked-SDK middleware tests: a customId save routes
through addConversation with the un-prefixed conversation id and never
touches client.add; the fallback stores the user's configured id, not
the prefixed marker; and the no-customId path is unchanged. Two of the
three tests fail against the previous implementation.
Copilot AI review requested due to automatic review settings July 13, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ved015 ved015 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @abhay-codes07 thanks for the PR.

The routing idea is valid, but I think it requires some changes

  1. Our current backedn accepts conversation:, so previous saves may already exist under that ID. Switching to unprefixed would create a second document and split existing conversation history.

  2. OpenAI Responses also supports structured input like

input: [{
  role: "user",
  content: [
    { type: "input_text", text: "Hi" },
    { type: "input_image", image_url: "..." }
  ]
}]

and this PR does this
const input = typeof params.input === "string" ? params.input : ""

so basically this implementation converts non-string input to "", so those requests still skip memory search and saving.

…ds stable

Addresses review feedback on the Responses API memory routing:

- The wrapper collapsed any non-string `input` to "" (`typeof input ===
  "string" ? input : ""`), so structured or multi-modal Responses requests
  skipped both memory search and saving. Add extractResponsesInput to flatten
  string or structured array input into a text query plus role-tagged messages,
  and route the reconstructed messages through /v4/conversations.

- Stop rewriting the stored customId in the client.add fallback. The backend
  already stores conversation memories under the "conversation:" id, so
  stripping the prefix would orphan a conversation's earlier turns.

Unit tests cover string, empty, structured multi-part, multi-turn, role
normalization, and text-less items.
@abhay-codes07

Copy link
Copy Markdown
Contributor Author

Thanks @ved015, both are fair. Pushed a follow-up:

  1. conversation id stability - reverted the customId rewrite in the client.add fallback. It now passes customId through untouched, so existing conversation:<id> documents keep their identity and earlier turns are not orphaned. The /v4/conversations path already normalizes the id the same way the chat-completions path does, so the two paths stay consistent.

  2. structured input - replaced typeof input === "string" ? input : "" with a new extractResponsesInput helper. It flattens both plain-string and structured/multi-part input (input_text parts, multi-turn arrays) into a text query for search and rebuilds role-tagged messages, which then route through /v4/conversations. So structured and multi-modal Responses requests are no longer dropped from search and saving.

Added unit tests covering string, empty, structured multi-part, multi-turn, role normalization, and text-less items (vitest run test/with-supermemory/responses-input.test.ts, 7 passing). Let me know if you'd prefer the fallback to store the unprefixed id instead and I'll adjust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants