fix(tools): isolate OpenAI middleware clients

This commit is contained in:
abhinav7x94 2026-08-16 07:46:43 +05:30 • committed by Mahesh Sanikommu
parent 8c70ffdc8b
commit 608c380c5a
3 changed files with 62 additions and 17 deletions

View file

@ -288,7 +288,7 @@ The `MemoryPromptData` object provides:
#### OpenAI Middleware with Supermemory
The `withSupermemory` function creates an OpenAI client with SuperMemory middleware automatically injected:
The `withSupermemory` function creates an isolated middleware facade around an OpenAI client. It does not mutate the supplied client, so a shared base client can safely be wrapped with different user or conversation options:
```typescript
import { withSupermemory } from "@supermemory/tools/openai"

View file

@ -23,7 +23,7 @@ import {
* @param options.addMemory - Optional mode for memory addition: "always" (default), "never"
* @param options.apiKey - Optional Supermemory API key; falls back to SUPERMEMORY_API_KEY
*
* @returns An OpenAI client with SuperMemory middleware injected for both Chat Completions and Responses APIs
* @returns A distinct OpenAI middleware facade for Chat Completions and Responses APIs. The supplied client is not mutated.
*
* @example
* ```typescript

View file

@ -19,6 +19,34 @@ import { deduplicateMemoriesForMode } from "../tools-shared"
import { createLogger, type Logger } from "../vercel/logger"
import { convertProfileToMarkdown } from "../vercel/util"
// Keep canonicalization stable across duplicate package copies and hot reloads.
const BASE_CLIENT_SYMBOL = Symbol.for("@supermemory/tools/openai/base-client")
const baseClientByWrapper = new WeakMap<OpenAI, OpenAI>()
const getBaseClient = (client: OpenAI) =>
(Reflect.get(client, BASE_CLIENT_SYMBOL) as OpenAI | undefined) ??
baseClientByWrapper.get(client) ??
client
const cloneWithOverrides = <T extends object>(
source: T,
overrides: Partial<T>,
): T => {
const descriptors = Object.getOwnPropertyDescriptors(source)
for (const key of Reflect.ownKeys(overrides) as Array<keyof T>) {
const current = Object.getOwnPropertyDescriptor(source, key)
Reflect.set(descriptors, key, {
configurable: current?.configurable ?? true,
enumerable: current?.enumerable ?? false,
value: overrides[key],
writable: current && "writable" in current ? current.writable : true,
})
}
return Object.create(Object.getPrototypeOf(source), descriptors) as T
}
const normalizeBaseUrl = (url?: string): string => {
const defaultUrl = "https://api.supermemory.ai"
return url?.trim().replace(/\/+$/, "") || defaultUrl
@ -658,6 +686,11 @@ export function createOpenAIMiddleware(
namespace: string,
options?: OpenAIMiddlewareOptions,
) {
const baseClient = getBaseClient(openaiClient)
const baseChat = baseClient.chat
const baseCompletions = baseChat.completions
const baseResponses = baseClient.responses
const logger = createLogger(options?.verbose ?? false)
const apiKey =
options?.apiKey?.trim() || process.env.SUPERMEMORY_API_KEY?.trim() || ""
@ -676,8 +709,8 @@ export function createOpenAIMiddleware(
const mode = options?.mode ?? "profile"
const addMemory = options?.addMemory ?? "always"
const originalCreate = openaiClient.chat.completions.create
const originalResponsesCreate = openaiClient.responses?.create
const originalCreate = baseCompletions.create
const originalResponsesCreate = baseResponses?.create
/**
* Searches for memories and formats them for injection into API calls.
@ -818,7 +851,7 @@ export function createOpenAIMiddleware(
}
return {
request: originalResponsesCreate.call(
openaiClient.responses,
baseResponses,
cleanedParams,
requestOptions,
),
@ -864,7 +897,7 @@ export function createOpenAIMiddleware(
return {
request: originalResponsesCreate.call(
openaiClient.responses,
baseResponses,
{
...params,
input: cleanedInput,
@ -913,7 +946,7 @@ export function createOpenAIMiddleware(
logger.debug("No textual user message found, skipping memory search")
return {
request: originalCreate.call(
openaiClient.chat.completions,
baseCompletions,
{
...params,
messages: updateChatMemoryContexts(messages),
@ -968,7 +1001,7 @@ export function createOpenAIMiddleware(
return {
request: originalCreate.call(
openaiClient.chat.completions,
baseCompletions,
{
...params,
messages: enhancedMessages,
@ -983,14 +1016,26 @@ export function createOpenAIMiddleware(
requestOptions?: OpenAI.RequestOptions,
) => deferAPIPromise(() => prepareCreateWithMemory(params, requestOptions))
openaiClient.chat.completions.create =
createWithMemory as typeof originalCreate
const wrappedCompletions = cloneWithOverrides(baseCompletions, {
create: createWithMemory as typeof originalCreate,
})
const wrappedChat = cloneWithOverrides(baseChat, {
completions: wrappedCompletions,
})
const wrappedResponses =
baseResponses && originalResponsesCreate
? cloneWithOverrides(baseResponses, {
create: createResponsesWithMemory as typeof originalResponsesCreate,
})
: undefined
const wrappedClient = cloneWithOverrides(baseClient, {
chat: wrappedChat,
...(wrappedResponses ? { responses: wrappedResponses } : {}),
})
// Wrap Responses API if available
if (originalResponsesCreate) {
openaiClient.responses.create =
createResponsesWithMemory as typeof originalResponsesCreate
}
return openaiClient
Object.defineProperty(wrappedClient, BASE_CLIENT_SYMBOL, {
value: baseClient,
})
baseClientByWrapper.set(wrappedClient, baseClient)
return wrappedClient
}