Skip to content

Commit 7b0baea

Browse files
authored
add support for responses api in openai typescript sdk (supermemoryai#549)
1 parent af12864 commit 7b0baea

3 files changed

Lines changed: 200 additions & 68 deletions

File tree

packages/tools/src/openai/index.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import {
66

77
/**
88
* Wraps an OpenAI client with SuperMemory middleware to automatically inject relevant memories
9-
* into the system prompt based on the user's message content.
9+
* into both Chat Completions and Responses APIs based on the user's input content.
1010
*
11-
* This middleware searches the supermemory API for relevant memories using the container tag
12-
* and user message, then either appends memories to an existing system prompt or creates
13-
* a new system prompt with the memories.
11+
* For Chat Completions API: Searches for memories using the user message content and injects
12+
* them into the system prompt (appends to existing or creates new system prompt).
13+
*
14+
* For Responses API: Searches for memories using the input parameter and injects them into
15+
* the instructions parameter (appends to existing or creates new instructions).
1416
*
1517
* @param openaiClient - The OpenAI client to wrap with SuperMemory middleware
1618
* @param containerTag - The container tag/identifier for memory search (e.g., user ID, project ID)
@@ -20,7 +22,7 @@ import {
2022
* @param options.mode - Optional mode for memory search: "profile" (default), "query", or "full"
2123
* @param options.addMemory - Optional mode for memory addition: "always", "never" (default)
2224
*
23-
* @returns An OpenAI client with SuperMemory middleware injected
25+
* @returns An OpenAI client with SuperMemory middleware injected for both Chat Completions and Responses APIs
2426
*
2527
* @example
2628
* ```typescript
@@ -37,13 +39,20 @@ import {
3739
* addMemory: "always"
3840
* })
3941
*
40-
* // Use normally - memories will be automatically injected
41-
* const response = await openaiWithSupermemory.chat.completions.create({
42+
* // Use with Chat Completions API - memories injected into system prompt
43+
* const chatResponse = await openaiWithSupermemory.chat.completions.create({
4244
* model: "gpt-4",
4345
* messages: [
4446
* { role: "user", content: "What's my favorite programming language?" }
4547
* ]
4648
* })
49+
*
50+
* // Use with Responses API - memories injected into instructions
51+
* const response = await openaiWithSupermemory.responses.create({
52+
* model: "gpt-4o",
53+
* instructions: "You are a helpful coding assistant",
54+
* input: "What's my favorite programming language?"
55+
* })
4756
* ```
4857
*
4958
* @throws {Error} When SUPERMEMORY_API_KEY environment variable is not set

packages/tools/src/openai/middleware.ts

Lines changed: 163 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -152,54 +152,13 @@ const addSystemPrompt = async (
152152

153153
const queryText = mode !== "profile" ? getLastUserMessage(messages) : ""
154154

155-
const memoriesResponse = await supermemoryProfileSearch(
156-
containerTag,
155+
const memories = await searchAndFormatMemories(
157156
queryText,
158-
)
159-
160-
const memoryCountStatic = memoriesResponse.profile.static?.length || 0
161-
const memoryCountDynamic = memoriesResponse.profile.dynamic?.length || 0
162-
163-
logger.info("Memory search completed", {
164157
containerTag,
165-
memoryCountStatic,
166-
memoryCountDynamic,
167-
queryText:
168-
queryText.substring(0, 100) + (queryText.length > 100 ? "..." : ""),
158+
logger,
169159
mode,
170-
})
171-
172-
const profileData =
173-
mode !== "query"
174-
? convertProfileToMarkdown({
175-
profile: {
176-
static: memoriesResponse.profile.static?.map((item) => item.memory),
177-
dynamic: memoriesResponse.profile.dynamic?.map(
178-
(item) => item.memory,
179-
),
180-
},
181-
searchResults: {
182-
results: memoriesResponse.searchResults.results.map((item) => ({
183-
memory: item.memory,
184-
})) as [{ memory: string }],
185-
},
186-
})
187-
: ""
188-
const searchResultsMemories =
189-
mode !== "profile"
190-
? `Search results for user's recent message: \n${memoriesResponse.searchResults.results
191-
.map((result) => `- ${result.memory}`)
192-
.join("\n")}`
193-
: ""
194-
195-
const memories = `${profileData}\n${searchResultsMemories}`.trim()
196-
197-
if (memories) {
198-
logger.debug("Memory content preview", {
199-
content: memories,
200-
fullLength: memories.length,
201-
})
202-
}
160+
"chat",
161+
)
203162

204163
if (systemPromptExists) {
205164
logger.debug("Added memories to existing system prompt")
@@ -338,27 +297,145 @@ export function createOpenAIMiddleware(
338297
apiKey: process.env.SUPERMEMORY_API_KEY,
339298
})
340299

300+
const conversationId = options?.conversationId
301+
const mode = options?.mode ?? "profile"
302+
const addMemory = options?.addMemory ?? "never"
303+
341304
const originalCreate = openaiClient.chat.completions.create
305+
const originalResponsesCreate = openaiClient.responses?.create
306+
307+
/**
308+
* Searches for memories and formats them for injection into API calls.
309+
*
310+
* This shared function handles memory search and formatting for both Chat Completions
311+
* and Responses APIs, reducing code duplication.
312+
*
313+
* @param queryText - The text to search for (empty string for profile-only mode)
314+
* @param containerTag - The container tag for memory search
315+
* @param logger - Logger instance
316+
* @param mode - Memory search mode
317+
* @param context - API context for logging differentiation
318+
* @returns Formatted memories string
319+
*/
320+
const searchAndFormatMemories = async (
321+
queryText: string,
322+
containerTag: string,
323+
logger: Logger,
324+
mode: "profile" | "query" | "full",
325+
context: "chat" | "responses",
326+
) => {
327+
const memoriesResponse = await supermemoryProfileSearch(
328+
containerTag,
329+
queryText,
330+
)
342331

343-
const createWithMemory = async (
344-
params: OpenAI.Chat.Completions.ChatCompletionCreateParams,
332+
const memoryCountStatic = memoriesResponse.profile.static?.length || 0
333+
const memoryCountDynamic = memoriesResponse.profile.dynamic?.length || 0
334+
335+
logger.info(`Memory search completed for ${context} API`, {
336+
containerTag,
337+
memoryCountStatic,
338+
memoryCountDynamic,
339+
queryText:
340+
queryText.substring(0, 100) + (queryText.length > 100 ? "..." : ""),
341+
mode,
342+
})
343+
344+
const profileData =
345+
mode !== "query"
346+
? convertProfileToMarkdown({
347+
profile: {
348+
static: memoriesResponse.profile.static?.map((item) => item.memory),
349+
dynamic: memoriesResponse.profile.dynamic?.map(
350+
(item) => item.memory,
351+
),
352+
},
353+
searchResults: {
354+
results: memoriesResponse.searchResults.results.map((item) => ({
355+
memory: item.memory,
356+
})) as [{ memory: string }],
357+
},
358+
})
359+
: ""
360+
const searchResultsMemories =
361+
mode !== "profile"
362+
? `Search results for user's ${context === "chat" ? "recent message" : "input"}: \n${memoriesResponse.searchResults.results
363+
.map((result) => `- ${result.memory}`)
364+
.join("\n")}`
365+
: ""
366+
367+
const memories = `${profileData}\n${searchResultsMemories}`.trim()
368+
369+
if (memories) {
370+
logger.debug(`Memory content preview for ${context} API`, {
371+
content: memories,
372+
fullLength: memories.length,
373+
})
374+
}
375+
376+
return memories
377+
}
378+
379+
const createResponsesWithMemory = async (
380+
params: Parameters<typeof originalResponsesCreate>[0],
345381
) => {
346-
const messages = Array.isArray(params.messages) ? params.messages : []
382+
if (!originalResponsesCreate) {
383+
throw new Error("Responses API is not available in this OpenAI client version")
384+
}
347385

348-
if (addMemory === "always") {
349-
const userMessage = getLastUserMessage(messages)
350-
if (userMessage?.trim()) {
351-
const content = conversationId
352-
? getConversationContent(messages)
353-
: userMessage
354-
const customId = conversationId
355-
? `conversation:${conversationId}`
356-
: undefined
386+
const input = typeof params.input === "string" ? params.input : ""
357387

358-
addMemoryTool(client, containerTag, content, customId, logger)
359-
}
388+
if (mode !== "profile" && !input) {
389+
logger.debug("No input found for Responses API, skipping memory search")
390+
return originalResponsesCreate.call(openaiClient.responses, params)
360391
}
361392

393+
logger.info("Starting memory search for Responses API", {
394+
containerTag,
395+
conversationId,
396+
mode,
397+
})
398+
399+
const operations: Promise<any>[] = []
400+
401+
if (addMemory === "always" && input?.trim()) {
402+
const content = conversationId
403+
? `Input: ${input}`
404+
: input
405+
const customId = conversationId
406+
? `conversation:${conversationId}`
407+
: undefined
408+
409+
operations.push(addMemoryTool(client, containerTag, content, customId, logger))
410+
}
411+
412+
const queryText = mode !== "profile" ? input : ""
413+
operations.push(searchAndFormatMemories(
414+
queryText,
415+
containerTag,
416+
logger,
417+
mode,
418+
"responses",
419+
))
420+
421+
const results = await Promise.all(operations)
422+
const memories = results[results.length - 1] // Memory search result is always last
423+
424+
const enhancedInstructions = memories
425+
? `${params.instructions || ""}\n\n${memories}`.trim()
426+
: params.instructions
427+
428+
return originalResponsesCreate.call(openaiClient.responses, {
429+
...params,
430+
instructions: enhancedInstructions,
431+
})
432+
}
433+
434+
const createWithMemory = async (
435+
params: OpenAI.Chat.Completions.ChatCompletionCreateParams,
436+
) => {
437+
const messages = Array.isArray(params.messages) ? params.messages : []
438+
362439
if (mode !== "profile") {
363440
const userMessage = getLastUserMessage(messages)
364441
if (!userMessage) {
@@ -373,12 +450,31 @@ export function createOpenAIMiddleware(
373450
mode,
374451
})
375452

376-
const enhancedMessages = await addSystemPrompt(
453+
const operations: Promise<any>[] = []
454+
455+
if (addMemory === "always") {
456+
const userMessage = getLastUserMessage(messages)
457+
if (userMessage?.trim()) {
458+
const content = conversationId
459+
? getConversationContent(messages)
460+
: userMessage
461+
const customId = conversationId
462+
? `conversation:${conversationId}`
463+
: undefined
464+
465+
operations.push(addMemoryTool(client, containerTag, content, customId, logger))
466+
}
467+
}
468+
469+
operations.push(addSystemPrompt(
377470
messages,
378471
containerTag,
379472
logger,
380473
mode,
381-
)
474+
))
475+
476+
const results = await Promise.all(operations)
477+
const enhancedMessages = results[results.length - 1] // Enhanced messages result is always last
382478

383479
return originalCreate.call(openaiClient.chat.completions, {
384480
...params,
@@ -389,5 +485,11 @@ export function createOpenAIMiddleware(
389485
openaiClient.chat.completions.create =
390486
createWithMemory as typeof originalCreate
391487

488+
// Wrap Responses API if available
489+
if (originalResponsesCreate) {
490+
openaiClient.responses.create =
491+
createResponsesWithMemory as typeof originalResponsesCreate
492+
}
493+
392494
return openaiClient
393495
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { OpenAI } from "openai"
2+
import { withSupermemory } from "../src/openai"
3+
4+
const openai = new OpenAI({
5+
apiKey: process.env.OPENAI_API_KEY,
6+
})
7+
8+
const openaiWithSupermemory = withSupermemory(openai, "user_id_life", {
9+
verbose: true,
10+
mode: "full",
11+
addMemory: "always",
12+
})
13+
14+
const response = await openaiWithSupermemory.responses.create({
15+
model: "gpt-4o",
16+
instructions: "you are ai girlfriend",
17+
input: "Where do i live?",
18+
})
19+
20+
console.log("Response output:", JSON.stringify(response.output[0], null, 2))
21+
console.log("Usage:", response.usage)

0 commit comments

Comments
 (0)