mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-10 03:28:14 +00:00
Merge 38b00bd904 into ac21804982
This commit is contained in:
commit
fd22ee45b7
3 changed files with 62 additions and 17 deletions
|
|
@ -263,7 +263,7 @@ The `MemoryPromptData` object provides:
|
||||||
|
|
||||||
#### OpenAI Middleware with Supermemory
|
#### 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
|
```typescript
|
||||||
import { withSupermemory } from "@supermemory/tools/openai"
|
import { withSupermemory } from "@supermemory/tools/openai"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
* @param options.addMemory - Optional mode for memory addition: "always" (default), "never"
|
* @param options.addMemory - Optional mode for memory addition: "always" (default), "never"
|
||||||
* @param options.apiKey - Optional Supermemory API key; falls back to SUPERMEMORY_API_KEY
|
* @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
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,34 @@ import { deduplicateMemoriesForMode } from "../tools-shared"
|
||||||
import { createLogger, type Logger } from "../vercel/logger"
|
import { createLogger, type Logger } from "../vercel/logger"
|
||||||
import { convertProfileToMarkdown } from "../vercel/util"
|
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 normalizeBaseUrl = (url?: string): string => {
|
||||||
const defaultUrl = "https://api.supermemory.ai"
|
const defaultUrl = "https://api.supermemory.ai"
|
||||||
return url?.trim().replace(/\/+$/, "") || defaultUrl
|
return url?.trim().replace(/\/+$/, "") || defaultUrl
|
||||||
|
|
@ -761,6 +789,11 @@ export function createOpenAIMiddleware(
|
||||||
containerTag: string,
|
containerTag: string,
|
||||||
options?: OpenAIMiddlewareOptions,
|
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 logger = createLogger(options?.verbose ?? false)
|
||||||
const apiKey =
|
const apiKey =
|
||||||
options?.apiKey?.trim() || process.env.SUPERMEMORY_API_KEY?.trim() || ""
|
options?.apiKey?.trim() || process.env.SUPERMEMORY_API_KEY?.trim() || ""
|
||||||
|
|
@ -779,8 +812,8 @@ export function createOpenAIMiddleware(
|
||||||
const mode = options?.mode ?? "profile"
|
const mode = options?.mode ?? "profile"
|
||||||
const addMemory = options?.addMemory ?? "always"
|
const addMemory = options?.addMemory ?? "always"
|
||||||
|
|
||||||
const originalCreate = openaiClient.chat.completions.create
|
const originalCreate = baseCompletions.create
|
||||||
const originalResponsesCreate = openaiClient.responses?.create
|
const originalResponsesCreate = baseResponses?.create
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for memories and formats them for injection into API calls.
|
* Searches for memories and formats them for injection into API calls.
|
||||||
|
|
@ -920,7 +953,7 @@ export function createOpenAIMiddleware(
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
request: originalResponsesCreate.call(
|
request: originalResponsesCreate.call(
|
||||||
openaiClient.responses,
|
baseResponses,
|
||||||
cleanedParams,
|
cleanedParams,
|
||||||
requestOptions,
|
requestOptions,
|
||||||
),
|
),
|
||||||
|
|
@ -972,7 +1005,7 @@ export function createOpenAIMiddleware(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
request: originalResponsesCreate.call(
|
request: originalResponsesCreate.call(
|
||||||
openaiClient.responses,
|
baseResponses,
|
||||||
{
|
{
|
||||||
...params,
|
...params,
|
||||||
input: cleanedInput,
|
input: cleanedInput,
|
||||||
|
|
@ -1023,7 +1056,7 @@ export function createOpenAIMiddleware(
|
||||||
logger.debug("No textual user message found, skipping memory search")
|
logger.debug("No textual user message found, skipping memory search")
|
||||||
return {
|
return {
|
||||||
request: originalCreate.call(
|
request: originalCreate.call(
|
||||||
openaiClient.chat.completions,
|
baseCompletions,
|
||||||
{
|
{
|
||||||
...params,
|
...params,
|
||||||
messages: updateChatMemoryContexts(messages),
|
messages: updateChatMemoryContexts(messages),
|
||||||
|
|
@ -1078,7 +1111,7 @@ export function createOpenAIMiddleware(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
request: originalCreate.call(
|
request: originalCreate.call(
|
||||||
openaiClient.chat.completions,
|
baseCompletions,
|
||||||
{
|
{
|
||||||
...params,
|
...params,
|
||||||
messages: enhancedMessages,
|
messages: enhancedMessages,
|
||||||
|
|
@ -1093,14 +1126,26 @@ export function createOpenAIMiddleware(
|
||||||
requestOptions?: OpenAI.RequestOptions,
|
requestOptions?: OpenAI.RequestOptions,
|
||||||
) => deferAPIPromise(() => prepareCreateWithMemory(params, requestOptions))
|
) => deferAPIPromise(() => prepareCreateWithMemory(params, requestOptions))
|
||||||
|
|
||||||
openaiClient.chat.completions.create =
|
const wrappedCompletions = cloneWithOverrides(baseCompletions, {
|
||||||
createWithMemory as typeof originalCreate
|
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
|
Object.defineProperty(wrappedClient, BASE_CLIENT_SYMBOL, {
|
||||||
if (originalResponsesCreate) {
|
value: baseClient,
|
||||||
openaiClient.responses.create =
|
})
|
||||||
createResponsesWithMemory as typeof originalResponsesCreate
|
baseClientByWrapper.set(wrappedClient, baseClient)
|
||||||
}
|
return wrappedClient
|
||||||
|
|
||||||
return openaiClient
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue