Skip to content

Commit e7e1ffa

Browse files
RestryBradGroux
authored andcommitted
feat(memory-lancedb): add native Azure OpenAI support
1 parent c2ffe1f commit e7e1ffa

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Docs: https://docs.openclaw.ai
183183

184184
### Fixes
185185

186+
- Memory/LanceDB: configure Azure OpenAI embedding endpoints with the `api-key` header and API version query so Azure-hosted embeddings work without an intermediate proxy. (#47285) Thanks @Restry.
186187
- fix(discord): gate user allowlist name resolution [AI]. (#79002) Thanks @pgondhi987.
187188
- fix(msteams): gate startup user allowlist resolution [AI]. (#79003) Thanks @pgondhi987.
188189
- Harden macOS shell wrapper allowlist parsing [AI]. (#78518) Thanks @pgondhi987.

extensions/memory-lancedb/index.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,6 +1871,102 @@ describe("memory plugin e2e", () => {
18711871
}
18721872
});
18731873

1874+
test("configures Azure OpenAI embedding clients with api-key header and api-version", async () => {
1875+
const embeddingsCreate = vi.fn(async () => ({
1876+
data: [{ embedding: [0.1, 0.2, 0.3] }],
1877+
}));
1878+
const openAiCtorArgs: unknown[] = [];
1879+
const toArray = vi.fn(async () => []);
1880+
const limit = vi.fn(() => ({ toArray }));
1881+
const vectorSearch = vi.fn(() => ({ limit }));
1882+
const loadLanceDbModule = vi.fn(async () => ({
1883+
connect: vi.fn(async () => ({
1884+
tableNames: vi.fn(async () => ["memories"]),
1885+
openTable: vi.fn(async () => ({
1886+
vectorSearch,
1887+
countRows: vi.fn(async () => 0),
1888+
add: vi.fn(async () => undefined),
1889+
delete: vi.fn(async () => undefined),
1890+
})),
1891+
})),
1892+
}));
1893+
1894+
vi.resetModules();
1895+
vi.doMock("openai", () => ({
1896+
default: class MockOpenAI {
1897+
constructor(opts: unknown) {
1898+
openAiCtorArgs.push(opts);
1899+
}
1900+
1901+
post = vi.fn((_path: string, opts: { body?: unknown }) =>
1902+
invokeEmbeddingCreate(embeddingsCreate, opts.body),
1903+
);
1904+
},
1905+
}));
1906+
vi.doMock("./lancedb-runtime.js", () => ({
1907+
loadLanceDbModule,
1908+
}));
1909+
1910+
try {
1911+
const { default: memoryPlugin } = await import("./index.js");
1912+
const registeredTools: any[] = [];
1913+
const mockApi = {
1914+
id: "memory-lancedb",
1915+
name: "Memory (LanceDB)",
1916+
source: "test",
1917+
config: {},
1918+
pluginConfig: {
1919+
embedding: {
1920+
apiKey: "azure-key",
1921+
model: "text-embedding-3-small",
1922+
baseUrl: "https://example.openai.azure.com/openai/deployments/embed",
1923+
},
1924+
dbPath: getDbPath(),
1925+
autoCapture: false,
1926+
autoRecall: false,
1927+
},
1928+
runtime: {},
1929+
logger: {
1930+
info: vi.fn(),
1931+
warn: vi.fn(),
1932+
error: vi.fn(),
1933+
debug: vi.fn(),
1934+
},
1935+
registerTool: (tool: any, opts: any) => {
1936+
registeredTools.push({ tool, opts });
1937+
},
1938+
registerCli: vi.fn(),
1939+
registerService: vi.fn(),
1940+
on: vi.fn(),
1941+
resolvePath: (p: string) => p,
1942+
};
1943+
1944+
memoryPlugin.register(mockApi as any);
1945+
const recallTool = registeredTools.find((t) => t.opts?.name === "memory_recall")?.tool;
1946+
if (!recallTool) {
1947+
throw new Error("memory_recall tool was not registered");
1948+
}
1949+
await recallTool.execute("test-call-azure", { query: "hello azure" });
1950+
1951+
expect(openAiCtorArgs).toEqual([
1952+
expect.objectContaining({
1953+
apiKey: "azure-key",
1954+
baseURL: "https://example.openai.azure.com/openai/deployments/embed",
1955+
defaultHeaders: { "api-key": "azure-key" },
1956+
defaultQuery: { "api-version": "2024-02-01" },
1957+
}),
1958+
]);
1959+
expect(embeddingsCreate).toHaveBeenCalledWith({
1960+
model: "text-embedding-3-small",
1961+
input: "hello azure",
1962+
});
1963+
} finally {
1964+
vi.doUnmock("openai");
1965+
vi.doUnmock("./lancedb-runtime.js");
1966+
vi.resetModules();
1967+
}
1968+
});
1969+
18741970
test("clears failed database initialization so later tool calls can retry", async () => {
18751971
const embeddingsCreate = vi.fn(async () => ({
18761972
data: [{ embedding: [0.1, 0.2, 0.3] }],

extensions/memory-lancedb/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,19 @@ class OpenAiCompatibleEmbeddings implements Embeddings {
351351
baseUrl?: string,
352352
private dimensions?: number,
353353
) {
354+
const isAzure = baseUrl?.includes(".openai.azure.com");
354355
this.clientPromise = loadOpenAiModule().then(
355-
({ default: OpenAI }) => new OpenAI({ apiKey, baseURL: baseUrl }) as OpenAiEmbeddingClient,
356+
({ default: OpenAI }) =>
357+
new OpenAI({
358+
apiKey,
359+
baseURL: baseUrl,
360+
...(isAzure
361+
? {
362+
defaultHeaders: { "api-key": apiKey },
363+
defaultQuery: { "api-version": "2024-02-01" },
364+
}
365+
: {}),
366+
}) as OpenAiEmbeddingClient,
356367
);
357368
}
358369

0 commit comments

Comments
 (0)