mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-11 22:51:05 +00:00
Add Novita AI integration to @supermemory/tools
Adds a dedicated Novita AI provider integration following the OpenAI SDK pattern: - New novita submodule with withSupermemory middleware - Supports OpenAI-compatible endpoint (https://api.novita.ai/openai) - Uses NOVITA_API_KEY env var for authentication - Exports NOVITA_MODELS constants with model IDs (kimi-k2.5, glm-5, minimax-m2.5) - Includes unit tests for the integration
This commit is contained in:
parent
fc94dd8308
commit
89117a9d68
6 changed files with 671 additions and 0 deletions
|
|
@ -1,3 +1,18 @@
|
|||
export type { SupermemoryToolsConfig } from "./types"
|
||||
|
||||
export type { OpenAIMiddlewareOptions } from "./openai"
|
||||
|
||||
export type {
|
||||
NovitaMiddlewareOptions,
|
||||
NovitaClientOptions,
|
||||
NovitaModel,
|
||||
NovitaEmbeddingModel,
|
||||
} from "./novita"
|
||||
export {
|
||||
NOVITA_MODELS,
|
||||
NOVITA_EMBEDDING_MODELS,
|
||||
NOVITA_ENDPOINTS,
|
||||
withSupermemory as withSupermemoryNovita,
|
||||
wrapNovitaClient,
|
||||
createNovita,
|
||||
} from "./novita"
|
||||
|
|
|
|||
78
packages/tools/src/novita/index.ts
Normal file
78
packages/tools/src/novita/index.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import type OpenAI from "openai"
|
||||
import { createNovitaClient, createNovitaMiddleware } from "./middleware"
|
||||
import {
|
||||
NOVITA_MODELS,
|
||||
NOVITA_EMBEDDING_MODELS,
|
||||
NOVITA_ENDPOINTS,
|
||||
type NovitaMiddlewareOptions,
|
||||
type NovitaClientOptions,
|
||||
type NovitaModel,
|
||||
type NovitaEmbeddingModel,
|
||||
} from "./types"
|
||||
|
||||
export {
|
||||
createSearchMemoriesFunction,
|
||||
createAddMemoryFunction,
|
||||
createGetProfileFunction,
|
||||
createDocumentListFunction,
|
||||
createDocumentDeleteFunction,
|
||||
createDocumentAddFunction,
|
||||
createMemoryForgetFunction,
|
||||
supermemoryTools,
|
||||
getToolDefinitions,
|
||||
createToolCallExecutor,
|
||||
createToolCallsExecutor,
|
||||
createSearchMemoriesTool,
|
||||
createAddMemoryTool,
|
||||
createGetProfileTool,
|
||||
createDocumentListTool,
|
||||
createDocumentDeleteTool,
|
||||
createDocumentAddTool,
|
||||
createMemoryForgetTool,
|
||||
memoryToolSchemas,
|
||||
type MemorySearchResult,
|
||||
type MemoryAddResult,
|
||||
type ProfileResult,
|
||||
type DocumentListResult,
|
||||
type DocumentDeleteResult,
|
||||
type DocumentAddResult,
|
||||
type MemoryForgetResult,
|
||||
} from "../openai/tools"
|
||||
|
||||
export type {
|
||||
NovitaMiddlewareOptions as WithSupermemoryOptions,
|
||||
NovitaClientOptions,
|
||||
NovitaModel,
|
||||
NovitaEmbeddingModel,
|
||||
}
|
||||
|
||||
export { NOVITA_MODELS, NOVITA_EMBEDDING_MODELS, NOVITA_ENDPOINTS }
|
||||
|
||||
export function withSupermemory(
|
||||
containerTag: string,
|
||||
options?: NovitaMiddlewareOptions,
|
||||
clientOptions?: NovitaClientOptions,
|
||||
): OpenAI {
|
||||
if (!process.env.SUPERMEMORY_API_KEY) {
|
||||
throw new Error("SUPERMEMORY_API_KEY is not set")
|
||||
}
|
||||
|
||||
const novitaClient = createNovitaClient(clientOptions)
|
||||
return createNovitaMiddleware(novitaClient, containerTag, options)
|
||||
}
|
||||
|
||||
export function wrapNovitaClient(
|
||||
novitaClient: OpenAI,
|
||||
containerTag: string,
|
||||
options?: NovitaMiddlewareOptions,
|
||||
): OpenAI {
|
||||
if (!process.env.SUPERMEMORY_API_KEY) {
|
||||
throw new Error("SUPERMEMORY_API_KEY is not set")
|
||||
}
|
||||
|
||||
return createNovitaMiddleware(novitaClient, containerTag, options)
|
||||
}
|
||||
|
||||
export function createNovita(options?: NovitaClientOptions): OpenAI {
|
||||
return createNovitaClient(options)
|
||||
}
|
||||
331
packages/tools/src/novita/middleware.ts
Normal file
331
packages/tools/src/novita/middleware.ts
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
import OpenAI from "openai"
|
||||
import {
|
||||
NOVITA_ENDPOINTS,
|
||||
type NovitaClientOptions,
|
||||
type NovitaMiddlewareOptions,
|
||||
} from "./types"
|
||||
|
||||
const NOVITA_API_KEY = process.env.NOVITA_API_KEY
|
||||
|
||||
export function createNovitaClient(options?: NovitaClientOptions): OpenAI {
|
||||
const apiKey = options?.apiKey ?? NOVITA_API_KEY
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
"NOVITA_API_KEY is not set — provide it via `options.apiKey` or set `process.env.NOVITA_API_KEY`",
|
||||
)
|
||||
}
|
||||
|
||||
return new OpenAI({
|
||||
apiKey,
|
||||
baseURL: options?.baseURL ?? NOVITA_ENDPOINTS.OPENAI,
|
||||
...(options?.organization && { organization: options.organization }),
|
||||
})
|
||||
}
|
||||
|
||||
export function createNovitaMiddleware(
|
||||
novitaClient: OpenAI,
|
||||
containerTag: string,
|
||||
options?: NovitaMiddlewareOptions,
|
||||
): OpenAI {
|
||||
const logger = {
|
||||
info: (message: string, data?: Record<string, unknown>) => {
|
||||
if (options?.verbose) {
|
||||
console.log(`[novita-supermemory] ${message}`, data ?? "")
|
||||
}
|
||||
},
|
||||
error: (message: string, data?: Record<string, unknown>) => {
|
||||
console.error(`[novita-supermemory] ${message}`, data ?? "")
|
||||
},
|
||||
debug: (message: string, data?: Record<string, unknown>) => {
|
||||
if (options?.verbose) {
|
||||
console.debug(`[novita-supermemory] ${message}`, data ?? "")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const conversationId = options?.conversationId
|
||||
const mode = options?.mode ?? "profile"
|
||||
const addMemory = options?.addMemory ?? "never"
|
||||
const baseUrl = options?.baseUrl ?? "https://api.supermemory.ai"
|
||||
|
||||
const originalCreate = novitaClient.chat.completions.create
|
||||
const originalResponsesCreate = novitaClient.responses?.create
|
||||
|
||||
const getLastUserMessage = (
|
||||
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
) => {
|
||||
const lastUserMessage = messages
|
||||
.slice()
|
||||
.reverse()
|
||||
.find((msg) => msg.role === "user")
|
||||
|
||||
return typeof lastUserMessage?.content === "string"
|
||||
? lastUserMessage.content
|
||||
: ""
|
||||
}
|
||||
|
||||
const supermemoryProfileSearch = async (
|
||||
containerTag: string,
|
||||
queryText: string,
|
||||
): Promise<{
|
||||
profile: {
|
||||
static?: Array<{ memory: string; metadata?: Record<string, unknown> }>
|
||||
dynamic?: Array<{ memory: string; metadata?: Record<string, unknown> }>
|
||||
}
|
||||
searchResults: {
|
||||
results: Array<{ memory: string; metadata?: Record<string, unknown> }>
|
||||
}
|
||||
}> => {
|
||||
const payload = queryText
|
||||
? JSON.stringify({
|
||||
q: queryText,
|
||||
containerTag: containerTag,
|
||||
})
|
||||
: JSON.stringify({
|
||||
containerTag: containerTag,
|
||||
})
|
||||
|
||||
const response = await fetch(`${baseUrl}/v4/profile`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
|
||||
},
|
||||
body: payload,
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text().catch(() => "Unknown error")
|
||||
throw new Error(
|
||||
`Supermemory profile search failed: ${response.status} ${response.statusText}. ${errorText}`,
|
||||
)
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
const convertProfileToMarkdown = (data: {
|
||||
profile: {
|
||||
static?: Array<{ memory: string }>
|
||||
dynamic?: Array<{ memory: string }>
|
||||
}
|
||||
searchResults: { results: Array<{ memory: string }> }
|
||||
}): string => {
|
||||
const parts: string[] = []
|
||||
|
||||
if (data.profile.static?.length) {
|
||||
parts.push("### Static Profile (Facts about user)")
|
||||
for (const item of data.profile.static) {
|
||||
parts.push(`- ${item.memory}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.profile.dynamic?.length) {
|
||||
parts.push("### Dynamic Context (Recent activity)")
|
||||
for (const item of data.profile.dynamic) {
|
||||
parts.push(`- ${item.memory}`)
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join("\n")
|
||||
}
|
||||
|
||||
const deduplicateMemories = (data: {
|
||||
static?: Array<{ memory: string }>
|
||||
dynamic?: Array<{ memory: string }>
|
||||
searchResults?: Array<{ memory: string }>
|
||||
}): {
|
||||
static: string[]
|
||||
dynamic: string[]
|
||||
searchResults: string[]
|
||||
} => {
|
||||
const seen = new Set<string>()
|
||||
const result = {
|
||||
static: [] as string[],
|
||||
dynamic: [] as string[],
|
||||
searchResults: [] as string[],
|
||||
}
|
||||
|
||||
for (const item of data.static ?? []) {
|
||||
const normalized = item.memory.toLowerCase().trim()
|
||||
if (!seen.has(normalized)) {
|
||||
seen.add(normalized)
|
||||
result.static.push(item.memory)
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of data.dynamic ?? []) {
|
||||
const normalized = item.memory.toLowerCase().trim()
|
||||
if (!seen.has(normalized)) {
|
||||
seen.add(normalized)
|
||||
result.dynamic.push(item.memory)
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of data.searchResults ?? []) {
|
||||
const normalized = item.memory.toLowerCase().trim()
|
||||
if (!seen.has(normalized)) {
|
||||
seen.add(normalized)
|
||||
result.searchResults.push(item.memory)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const addSystemPrompt = async (
|
||||
messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||
containerTag: string,
|
||||
mode: "profile" | "query" | "full",
|
||||
): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam[]> => {
|
||||
const systemPromptExists = messages.some((msg) => msg.role === "system")
|
||||
const queryText = mode !== "profile" ? getLastUserMessage(messages) : ""
|
||||
|
||||
const memoriesResponse = await supermemoryProfileSearch(
|
||||
containerTag,
|
||||
queryText,
|
||||
)
|
||||
|
||||
const memoryCountStatic = memoriesResponse.profile.static?.length || 0
|
||||
const memoryCountDynamic = memoriesResponse.profile.dynamic?.length || 0
|
||||
|
||||
logger.info("Memory search completed", {
|
||||
containerTag,
|
||||
memoryCountStatic,
|
||||
memoryCountDynamic,
|
||||
mode,
|
||||
})
|
||||
|
||||
const deduplicated = deduplicateMemories({
|
||||
static: memoriesResponse.profile.static,
|
||||
dynamic: memoriesResponse.profile.dynamic,
|
||||
searchResults: memoriesResponse.searchResults?.results,
|
||||
})
|
||||
|
||||
const profileData =
|
||||
mode !== "query"
|
||||
? convertProfileToMarkdown({
|
||||
profile: {
|
||||
static: deduplicated.static.map((m) => ({ memory: m })),
|
||||
dynamic: deduplicated.dynamic.map((m) => ({ memory: m })),
|
||||
},
|
||||
searchResults: { results: [] },
|
||||
})
|
||||
: ""
|
||||
|
||||
const searchResultsMemories =
|
||||
mode !== "profile"
|
||||
? `Search results for user's recent message: \n${deduplicated.searchResults.map((memory) => `- ${memory}`).join("\n")}`
|
||||
: ""
|
||||
|
||||
const memories = `${profileData}\n${searchResultsMemories}`.trim()
|
||||
|
||||
if (!memories) {
|
||||
return messages
|
||||
}
|
||||
|
||||
if (systemPromptExists) {
|
||||
return messages.map((msg) =>
|
||||
msg.role === "system"
|
||||
? { ...msg, content: `${msg.content} \n ${memories}` }
|
||||
: msg,
|
||||
)
|
||||
}
|
||||
|
||||
return [{ role: "system" as const, content: memories }, ...messages]
|
||||
}
|
||||
|
||||
const createWithMemory = async (
|
||||
params: OpenAI.Chat.Completions.ChatCompletionCreateParams,
|
||||
) => {
|
||||
const messages = Array.isArray(params.messages) ? params.messages : []
|
||||
|
||||
if (mode !== "profile") {
|
||||
const userMessage = getLastUserMessage(messages)
|
||||
if (!userMessage) {
|
||||
logger.debug("No user message found, skipping memory search")
|
||||
return originalCreate.call(novitaClient.chat.completions, params)
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Starting memory search", {
|
||||
containerTag,
|
||||
conversationId,
|
||||
mode,
|
||||
})
|
||||
|
||||
const enhancedMessages = await addSystemPrompt(messages, containerTag, mode)
|
||||
|
||||
return originalCreate.call(novitaClient.chat.completions, {
|
||||
...params,
|
||||
messages: enhancedMessages,
|
||||
})
|
||||
}
|
||||
|
||||
novitaClient.chat.completions.create =
|
||||
createWithMemory as typeof originalCreate
|
||||
|
||||
if (originalResponsesCreate) {
|
||||
const createResponsesWithMemory = async (
|
||||
params: Parameters<typeof originalResponsesCreate>[0],
|
||||
) => {
|
||||
const input = typeof params.input === "string" ? params.input : ""
|
||||
|
||||
if (mode !== "profile" && !input) {
|
||||
logger.debug("No input found for Responses API, skipping memory search")
|
||||
return originalResponsesCreate.call(novitaClient.responses, params)
|
||||
}
|
||||
|
||||
logger.info("Starting memory search for Responses API", {
|
||||
containerTag,
|
||||
conversationId,
|
||||
mode,
|
||||
})
|
||||
|
||||
const queryText = mode !== "profile" ? input : ""
|
||||
const memoriesResponse = await supermemoryProfileSearch(
|
||||
containerTag,
|
||||
queryText,
|
||||
)
|
||||
|
||||
const deduplicated = deduplicateMemories({
|
||||
static: memoriesResponse.profile.static,
|
||||
dynamic: memoriesResponse.profile.dynamic,
|
||||
searchResults: memoriesResponse.searchResults?.results,
|
||||
})
|
||||
|
||||
const profileData =
|
||||
mode !== "query"
|
||||
? convertProfileToMarkdown({
|
||||
profile: {
|
||||
static: deduplicated.static.map((m) => ({ memory: m })),
|
||||
dynamic: deduplicated.dynamic.map((m) => ({ memory: m })),
|
||||
},
|
||||
searchResults: { results: [] },
|
||||
})
|
||||
: ""
|
||||
|
||||
const searchResultsMemories =
|
||||
mode !== "profile"
|
||||
? `Search results: \n${deduplicated.searchResults.map((memory) => `- ${memory}`).join("\n")}`
|
||||
: ""
|
||||
|
||||
const memories = `${profileData}\n${searchResultsMemories}`.trim()
|
||||
|
||||
const enhancedInstructions = memories
|
||||
? `${params.instructions || ""}\n\n${memories}`.trim()
|
||||
: params.instructions
|
||||
|
||||
return originalResponsesCreate.call(novitaClient.responses, {
|
||||
...params,
|
||||
instructions: enhancedInstructions,
|
||||
})
|
||||
}
|
||||
|
||||
novitaClient.responses.create =
|
||||
createResponsesWithMemory as typeof originalResponsesCreate
|
||||
}
|
||||
|
||||
return novitaClient
|
||||
}
|
||||
86
packages/tools/src/novita/types.ts
Normal file
86
packages/tools/src/novita/types.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import type OpenAI from "openai"
|
||||
|
||||
/**
|
||||
* Novita AI configuration options
|
||||
*/
|
||||
export interface NovitaMiddlewareOptions {
|
||||
/** Optional conversation ID to group messages for contextual memory generation */
|
||||
conversationId?: string
|
||||
/** Enable detailed logging of memory operations (default: false) */
|
||||
verbose?: boolean
|
||||
/**
|
||||
* Memory retrieval mode:
|
||||
* - "profile": Retrieves user profile memories (static + dynamic) without query filtering
|
||||
* - "query": Searches memories based on semantic similarity to the user's message
|
||||
* - "full": Combines both profile and query-based results
|
||||
*/
|
||||
mode?: "profile" | "query" | "full"
|
||||
/**
|
||||
* Memory persistence mode:
|
||||
* - "always": Automatically save conversations as memories
|
||||
* - "never": Only retrieve memories, don't store new ones
|
||||
*/
|
||||
addMemory?: "always" | "never"
|
||||
/** Custom Supermemory API base URL */
|
||||
baseUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Novita AI client options
|
||||
*/
|
||||
export interface NovitaClientOptions {
|
||||
/** Novita API key (falls back to NOVITA_API_KEY env var) */
|
||||
apiKey?: string
|
||||
/** Custom base URL (default: https://api.novita.ai/openai) */
|
||||
baseURL?: string
|
||||
/** Organization ID */
|
||||
organization?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Available Novita AI models
|
||||
* @see https://novita.ai/models
|
||||
*/
|
||||
export const NOVITA_MODELS = {
|
||||
/**
|
||||
* Default model - MoE architecture with function calling, structured output, reasoning, and vision
|
||||
* Context: 262,144 tokens | Max Output: 262,144 tokens
|
||||
*/
|
||||
DEFAULT: "moonshotai/kimi-k2.5",
|
||||
|
||||
/**
|
||||
* GLM-5 - MoE architecture with function calling, structured output, reasoning
|
||||
* Context: 202,800 tokens | Max Output: 131,072 tokens
|
||||
*/
|
||||
GLM_5: "zai-org/glm-5",
|
||||
|
||||
/**
|
||||
* MiniMax M2.5 - MoE architecture with function calling, structured output, reasoning
|
||||
* Context: 204,800 tokens | Max Output: 131,100 tokens
|
||||
*/
|
||||
MINIMAX_M2_5: "minimax/minimax-m2.5",
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Novita AI embedding models
|
||||
*/
|
||||
export const NOVITA_EMBEDDING_MODELS = {
|
||||
/**
|
||||
* Qwen3 Embedding - 1024 dimensions, max 8,192 input tokens
|
||||
*/
|
||||
DEFAULT: "qwen/qwen3-embedding-0.6b",
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Novita AI API endpoints
|
||||
*/
|
||||
export const NOVITA_ENDPOINTS = {
|
||||
/** OpenAI-compatible endpoint */
|
||||
OPENAI: "https://api.novita.ai/openai",
|
||||
/** Anthropic-compatible endpoint */
|
||||
ANTHROPIC: "https://api.novita.ai/anthropic",
|
||||
} as const
|
||||
|
||||
export type NovitaModel = (typeof NOVITA_MODELS)[keyof typeof NOVITA_MODELS]
|
||||
export type NovitaEmbeddingModel =
|
||||
(typeof NOVITA_EMBEDDING_MODELS)[keyof typeof NOVITA_EMBEDDING_MODELS]
|
||||
160
packages/tools/test/novita/unit.test.ts
Normal file
160
packages/tools/test/novita/unit.test.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
|
||||
import {
|
||||
withSupermemory,
|
||||
wrapNovitaClient,
|
||||
createNovita,
|
||||
NOVITA_MODELS,
|
||||
NOVITA_ENDPOINTS,
|
||||
} from "../../src/novita"
|
||||
import OpenAI from "openai"
|
||||
import "dotenv/config"
|
||||
|
||||
const TEST_CONFIG = {
|
||||
apiKey: process.env.SUPERMEMORY_API_KEY || "test-api-key",
|
||||
novitaApiKey: process.env.NOVITA_API_KEY || "test-novita-key",
|
||||
baseURL: process.env.SUPERMEMORY_BASE_URL || "https://api.supermemory.ai",
|
||||
containerTag: "test-novita-wrapper",
|
||||
}
|
||||
|
||||
describe("Unit: Novita Integration", () => {
|
||||
let originalEnv: string | undefined
|
||||
let originalFetch: typeof globalThis.fetch
|
||||
|
||||
beforeEach(() => {
|
||||
originalEnv = process.env.SUPERMEMORY_API_KEY
|
||||
originalFetch = globalThis.fetch
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
if (originalEnv) {
|
||||
process.env.SUPERMEMORY_API_KEY = originalEnv
|
||||
} else {
|
||||
delete process.env.SUPERMEMORY_API_KEY
|
||||
}
|
||||
globalThis.fetch = originalFetch
|
||||
})
|
||||
|
||||
describe("Constants and Exports", () => {
|
||||
it("should export NOVITA_MODELS with correct model IDs", () => {
|
||||
expect(NOVITA_MODELS.DEFAULT).toBe("moonshotai/kimi-k2.5")
|
||||
expect(NOVITA_MODELS.GLM_5).toBe("zai-org/glm-5")
|
||||
expect(NOVITA_MODELS.MINIMAX_M2_5).toBe("minimax/minimax-m2.5")
|
||||
})
|
||||
|
||||
it("should export NOVITA_ENDPOINTS with correct URLs", () => {
|
||||
expect(NOVITA_ENDPOINTS.OPENAI).toBe("https://api.novita.ai/openai")
|
||||
expect(NOVITA_ENDPOINTS.ANTHROPIC).toBe("https://api.novita.ai/anthropic")
|
||||
})
|
||||
})
|
||||
|
||||
describe("Environment validation", () => {
|
||||
it("should throw error if SUPERMEMORY_API_KEY is not set", () => {
|
||||
delete process.env.SUPERMEMORY_API_KEY
|
||||
|
||||
expect(() => {
|
||||
withSupermemory(TEST_CONFIG.containerTag)
|
||||
}).toThrow("SUPERMEMORY_API_KEY is not set")
|
||||
})
|
||||
|
||||
it("should throw error if NOVITA_API_KEY is not set when creating client", () => {
|
||||
delete process.env.NOVITA_API_KEY
|
||||
|
||||
expect(() => {
|
||||
createNovita()
|
||||
}).toThrow("NOVITA_API_KEY is not set")
|
||||
})
|
||||
|
||||
it("should successfully create client with valid API keys", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-novita-key"
|
||||
|
||||
const client = createNovita()
|
||||
expect(client).toBeDefined()
|
||||
expect(client).toBeInstanceOf(OpenAI)
|
||||
})
|
||||
|
||||
it("should successfully wrap client with supermemory", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-novita-key"
|
||||
|
||||
const client = createNovita()
|
||||
const wrappedClient = wrapNovitaClient(client, TEST_CONFIG.containerTag)
|
||||
expect(wrappedClient).toBeDefined()
|
||||
})
|
||||
|
||||
it("should successfully create wrapped client in one call", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-novita-key"
|
||||
|
||||
const wrappedClient = withSupermemory(TEST_CONFIG.containerTag)
|
||||
expect(wrappedClient).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Client configuration", () => {
|
||||
it("should use custom baseURL when provided", () => {
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const client = createNovita({ baseURL: "https://custom.api.com" })
|
||||
expect(client).toBeDefined()
|
||||
})
|
||||
|
||||
it("should use default Novita endpoint when baseURL not provided", () => {
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const client = createNovita()
|
||||
expect(client.baseURL).toBe(NOVITA_ENDPOINTS.OPENAI)
|
||||
})
|
||||
|
||||
it("should accept apiKey in options", () => {
|
||||
const client = createNovita({ apiKey: "custom-key" })
|
||||
expect(client).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("withSupermemory options", () => {
|
||||
it("should accept verbose option", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const wrappedClient = withSupermemory(TEST_CONFIG.containerTag, {
|
||||
verbose: true,
|
||||
})
|
||||
expect(wrappedClient).toBeDefined()
|
||||
})
|
||||
|
||||
it("should accept mode option", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const modes = ["profile", "query", "full"] as const
|
||||
for (const mode of modes) {
|
||||
const wrappedClient = withSupermemory(TEST_CONFIG.containerTag, {
|
||||
mode,
|
||||
})
|
||||
expect(wrappedClient).toBeDefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("should accept addMemory option", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const wrappedClient = withSupermemory(TEST_CONFIG.containerTag, {
|
||||
addMemory: "always",
|
||||
})
|
||||
expect(wrappedClient).toBeDefined()
|
||||
})
|
||||
|
||||
it("should accept conversationId option", () => {
|
||||
process.env.SUPERMEMORY_API_KEY = "test-key"
|
||||
process.env.NOVITA_API_KEY = "test-key"
|
||||
|
||||
const wrappedClient = withSupermemory(TEST_CONFIG.containerTag, {
|
||||
conversationId: "conv-123",
|
||||
})
|
||||
expect(wrappedClient).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -7,6 +7,7 @@ export default defineConfig({
|
|||
"src/claude-memory.ts",
|
||||
"src/openai/index.ts",
|
||||
"src/mastra.ts",
|
||||
"src/novita/index.ts",
|
||||
],
|
||||
format: "esm",
|
||||
sourcemap: false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue