mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-08 22:21:07 +00:00
Merge fa8ba27b10 into 9652478093
This commit is contained in:
commit
abc47aa740
10 changed files with 114 additions and 28 deletions
|
|
@ -10,7 +10,9 @@ const openai = new OpenAI({
|
|||
})
|
||||
|
||||
// Wrap OpenAI client with supermemory
|
||||
const openaiWithSupermemory = withSupermemory(openai, "test_user_123", {
|
||||
const openaiWithSupermemory = withSupermemory(openai, {
|
||||
containerTag: "test_user_123",
|
||||
customId: "test_user_123_chat",
|
||||
verbose: true, // Enable logging to see what's happening
|
||||
mode: "full", // Search both profile and query memories
|
||||
addMemory: "always", // Auto-save conversations as memories
|
||||
|
|
|
|||
|
|
@ -5,9 +5,33 @@
|
|||
*/
|
||||
|
||||
import Anthropic from "@anthropic-ai/sdk"
|
||||
import { createClaudeMemoryTool } from "./claude-memory"
|
||||
import {
|
||||
createClaudeMemoryTool,
|
||||
type MemoryCommand,
|
||||
} from "../src/claude-memory"
|
||||
import "dotenv/config"
|
||||
|
||||
const MEMORY_COMMANDS: readonly string[] = [
|
||||
"view",
|
||||
"create",
|
||||
"str_replace",
|
||||
"insert",
|
||||
"delete",
|
||||
"rename",
|
||||
]
|
||||
|
||||
function isMemoryCommand(input: unknown): input is MemoryCommand {
|
||||
return (
|
||||
typeof input === "object" &&
|
||||
input !== null &&
|
||||
"command" in input &&
|
||||
"path" in input &&
|
||||
typeof input.command === "string" &&
|
||||
MEMORY_COMMANDS.includes(input.command) &&
|
||||
typeof input.path === "string"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Claude's memory tool calls using the Anthropic SDK
|
||||
*/
|
||||
|
|
@ -37,7 +61,7 @@ async function chatWithMemoryTool() {
|
|||
})
|
||||
|
||||
// Conversation messages
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
const messages: Anthropic.Beta.Messages.BetaMessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content:
|
||||
|
|
@ -45,7 +69,7 @@ async function chatWithMemoryTool() {
|
|||
},
|
||||
]
|
||||
|
||||
console.log("💬 User:", messages[0].content)
|
||||
console.log("💬 User:", messages[0]?.content)
|
||||
console.log("\n🔄 Sending to Claude with memory tool...")
|
||||
|
||||
try {
|
||||
|
|
@ -66,20 +90,25 @@ async function chatWithMemoryTool() {
|
|||
console.log("📥 Claude responded:")
|
||||
|
||||
// Process the response
|
||||
const toolResults: Anthropic.Messages.ToolResultBlockParam[] = []
|
||||
const toolResults: Anthropic.Beta.Messages.BetaToolResultBlockParam[] = []
|
||||
|
||||
for (const block of response.content) {
|
||||
if (block.type === "text") {
|
||||
console.log("💭", block.text)
|
||||
} else if (block.type === "tool_use" && block.name === "memory") {
|
||||
const command = block.input
|
||||
if (!isMemoryCommand(command)) {
|
||||
console.log("Skipping unrecognized memory tool input:", command)
|
||||
continue
|
||||
}
|
||||
console.log("🔧 Claude is using memory tool:")
|
||||
console.log(" Command:", block.input.command)
|
||||
console.log(" Path:", block.input.path)
|
||||
console.log(" Command:", command.command)
|
||||
console.log(" Path:", command.path)
|
||||
|
||||
// Handle the memory tool call
|
||||
const memoryResult = await memoryTool.handleCommand(block.input as any)
|
||||
const memoryResult = await memoryTool.handleCommand(command)
|
||||
|
||||
const toolResult: Anthropic.Messages.ToolResultBlockParam = {
|
||||
const toolResult: Anthropic.Beta.Messages.BetaToolResultBlockParam = {
|
||||
type: "tool_result",
|
||||
tool_use_id: block.id,
|
||||
content: memoryResult.success
|
||||
|
|
@ -138,14 +167,17 @@ async function chatWithMemoryTool() {
|
|||
if (block.type === "text") {
|
||||
console.log("💭", block.text)
|
||||
} else if (block.type === "tool_use" && block.name === "memory") {
|
||||
const command = block.input
|
||||
if (!isMemoryCommand(command)) {
|
||||
console.log("Skipping unrecognized memory tool input:", command)
|
||||
continue
|
||||
}
|
||||
console.log("🔧 Claude is using memory tool again:")
|
||||
console.log(" Command:", block.input.command)
|
||||
console.log(" Path:", block.input.path)
|
||||
console.log(" Command:", command.command)
|
||||
console.log(" Path:", command.path)
|
||||
|
||||
// Handle additional memory tool calls
|
||||
const memoryResult = await memoryTool.handleCommand(
|
||||
block.input as any,
|
||||
)
|
||||
const memoryResult = await memoryTool.handleCommand(command)
|
||||
console.log(
|
||||
"📊 Memory operation result:",
|
||||
memoryResult.success ? "✅ Success" : "❌ Failed",
|
||||
|
|
@ -239,7 +271,7 @@ async function testMemoryOperations() {
|
|||
command: {
|
||||
command: "view" as const,
|
||||
path: "/memories/project-notes.txt",
|
||||
view_range: [4, 8],
|
||||
view_range: [4, 8] as [number, number],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@
|
|||
* 2. Anthropic SDK integration
|
||||
*/
|
||||
|
||||
import { createClaudeMemoryTool, type MemoryCommand } from "./claude-memory"
|
||||
import {
|
||||
createClaudeMemoryTool,
|
||||
type MemoryCommand,
|
||||
} from "../src/claude-memory"
|
||||
|
||||
// =====================================================
|
||||
// Example 1: Direct TypeScript/fetch Integration
|
||||
|
|
@ -67,8 +70,7 @@ export async function directFetchExample() {
|
|||
]
|
||||
|
||||
// Execute each command
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const command = commands[i]
|
||||
for (const [i, command] of commands.entries()) {
|
||||
console.log(
|
||||
`\n📝 Step ${i + 1}: ${command.command.toUpperCase()} ${command.path}`,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
* This shows actual tool call handling based on real Claude API responses
|
||||
*/
|
||||
|
||||
import { createClaudeMemoryTool, type MemoryCommand } from "./claude-memory"
|
||||
import {
|
||||
createClaudeMemoryTool,
|
||||
type MemoryCommand,
|
||||
} from "../src/claude-memory"
|
||||
|
||||
// =====================================================
|
||||
// Real Claude API Integration
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { describe, it, expect, beforeEach } from "vitest"
|
||||
import { createClaudeMemoryTool, type MemoryCommand } from "./claude-memory"
|
||||
import {
|
||||
createClaudeMemoryTool,
|
||||
type MemoryCommand,
|
||||
} from "../src/claude-memory"
|
||||
import "dotenv/config"
|
||||
|
||||
// Test configuration
|
||||
|
|
@ -10,6 +13,10 @@ const TEST_CONFIG = {
|
|||
memoryContainerTag: "claude_memory_test",
|
||||
}
|
||||
|
||||
// Same gate the other integration suites use: these hit the live API, so they
|
||||
// only run when a key is present. Without one every request comes back 401.
|
||||
const shouldRunIntegration = !!process.env.SUPERMEMORY_API_KEY
|
||||
|
||||
describe("Claude Memory Tool", () => {
|
||||
let memoryTool: ReturnType<typeof createClaudeMemoryTool>
|
||||
|
||||
|
|
@ -62,7 +69,7 @@ describe("Claude Memory Tool", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("File operations", () => {
|
||||
describe.skipIf(!shouldRunIntegration)("File operations", () => {
|
||||
const testFilePath = "/memories/test-file.txt"
|
||||
const testContent = "Hello, World!\nThis is a test file.\nLine 3 here."
|
||||
|
||||
|
|
@ -219,7 +226,7 @@ describe("Claude Memory Tool", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("Directory operations", () => {
|
||||
describe.skipIf(!shouldRunIntegration)("Directory operations", () => {
|
||||
it("should list empty directory", async () => {
|
||||
const result = await memoryTool.handleCommand({
|
||||
command: "view",
|
||||
|
|
@ -263,7 +270,7 @@ describe("Claude Memory Tool", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("Error handling", () => {
|
||||
describe.skipIf(!shouldRunIntegration)("Error handling", () => {
|
||||
it("should handle missing file", async () => {
|
||||
const result = await memoryTool.handleCommand({
|
||||
command: "view",
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -152,6 +153,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -191,6 +193,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -233,6 +236,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args1)
|
||||
|
|
@ -247,6 +251,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args2)
|
||||
|
|
@ -282,6 +287,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -313,6 +319,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -346,6 +353,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -383,6 +391,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
requestContext,
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +426,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await input.processInput(inputArgs)
|
||||
|
|
@ -430,6 +440,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await output.processOutputResult(outputArgs)
|
||||
|
|
@ -470,6 +481,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await inputProcessor.processInput(args)
|
||||
|
|
@ -534,6 +546,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -558,6 +571,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -592,6 +606,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
const result = await processor.processInput(args)
|
||||
|
|
@ -616,6 +631,7 @@ describe.skipIf(!shouldRunIntegration)(
|
|||
messageList: createIntegrationMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await expect(processor.processOutputResult(args)).resolves.toBeDefined()
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -223,6 +224,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args1)
|
||||
|
|
@ -234,6 +236,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args2)
|
||||
|
|
@ -266,6 +269,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args1)
|
||||
|
|
@ -277,6 +281,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args2)
|
||||
|
|
@ -298,6 +303,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
const result = await processor.processInput(args)
|
||||
|
|
@ -329,6 +335,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
const result = await processor.processInput(args)
|
||||
|
|
@ -356,6 +363,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -385,6 +393,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
requestContext,
|
||||
}
|
||||
|
||||
|
|
@ -428,6 +437,7 @@ describe("SupermemoryInputProcessor", () => {
|
|||
messageList,
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processInput(args)
|
||||
|
|
@ -495,6 +505,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -535,6 +546,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -563,6 +575,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -598,6 +611,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
requestContext,
|
||||
}
|
||||
|
||||
|
|
@ -632,6 +646,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -668,6 +683,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -723,6 +739,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
@ -756,6 +773,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
// Should not throw
|
||||
|
|
@ -775,6 +793,7 @@ describe("SupermemoryOutputProcessor", () => {
|
|||
messageList: createMockMessageList(),
|
||||
abort: vi.fn() as never,
|
||||
retryCount: 0,
|
||||
state: {},
|
||||
}
|
||||
|
||||
await processor.processOutputResult(args)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
* Run with: bun run src/test-memory-tool.ts
|
||||
*/
|
||||
|
||||
import { createClaudeMemoryTool, type MemoryCommand } from "./claude-memory"
|
||||
import {
|
||||
createClaudeMemoryTool,
|
||||
type MemoryCommand,
|
||||
} from "../src/claude-memory"
|
||||
import "dotenv/config"
|
||||
|
||||
async function testMemoryTool() {
|
||||
|
|
@ -140,8 +143,7 @@ async function testMemoryTool() {
|
|||
let passed = 0
|
||||
let failed = 0
|
||||
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
const testCase = testCases[i]
|
||||
for (const [i, testCase] of testCases.entries()) {
|
||||
console.log(`\\n🔄 Test ${i + 1}/${testCases.length}: ${testCase.name}`)
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -446,8 +446,8 @@ describe("Unit: withSupermemory", () => {
|
|||
usage: {
|
||||
inputTokens: 1,
|
||||
outputTokens: 1,
|
||||
totalTokens: 2,
|
||||
},
|
||||
rawCall: { rawPrompt: [], rawSettings: {} },
|
||||
warnings: [],
|
||||
})
|
||||
|
||||
|
|
@ -511,8 +511,8 @@ describe("Unit: withSupermemory", () => {
|
|||
usage: {
|
||||
inputTokens: 1,
|
||||
outputTokens: 1,
|
||||
totalTokens: 2,
|
||||
},
|
||||
rawCall: { rawPrompt: [], rawSettings: {} },
|
||||
warnings: [],
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"extends": "@total-typescript/tsconfig/bundler/dom/library-monorepo",
|
||||
// test/chatapp is a standalone Next.js demo with its own package.json,
|
||||
// lockfile and tsconfig; it is not part of this package's program.
|
||||
"exclude": ["node_modules", "test/chatapp"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue