Skip to content

Commit bfa2494

Browse files
mbelinkyvincentkoc
authored andcommitted
test(memory-core): prove generic provider bridge
1 parent 4babfc0 commit bfa2494

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
2+
import type {
3+
EmbeddingInput,
4+
EmbeddingProviderCallOptions,
5+
} from "openclaw/plugin-sdk/embedding-providers";
6+
import {
7+
createPluginRegistryFixture,
8+
registerVirtualTestPlugin,
9+
} from "openclaw/plugin-sdk/plugin-test-contracts";
10+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
11+
import {
12+
clearEmbeddingProviders,
13+
getRegisteredEmbeddingProvider,
14+
listRegisteredEmbeddingProviders,
15+
type RegisteredEmbeddingProvider,
16+
restoreRegisteredEmbeddingProviders,
17+
} from "../../../../src/plugins/embedding-providers.js";
18+
import {
19+
clearMemoryEmbeddingProviders,
20+
listRegisteredMemoryEmbeddingProviders,
21+
type RegisteredMemoryEmbeddingProvider,
22+
restoreRegisteredMemoryEmbeddingProviders,
23+
} from "../../../../src/plugins/memory-embedding-providers.js";
24+
import { createEmbeddingProvider } from "./embeddings.js";
25+
26+
type CapturedCall = {
27+
kind: "embed" | "embedBatch";
28+
input: EmbeddingInput | EmbeddingInput[];
29+
options: EmbeddingProviderCallOptions | undefined;
30+
};
31+
32+
let embeddingProvidersSnapshot: RegisteredEmbeddingProvider[];
33+
let memoryEmbeddingProvidersSnapshot: RegisteredMemoryEmbeddingProvider[];
34+
35+
function createOptions(config: OpenClawConfig) {
36+
return {
37+
config,
38+
agentDir: "/tmp/openclaw-agent",
39+
provider: "virtual-generic",
40+
fallback: "none",
41+
model: "virtual-model",
42+
outputDimensionality: 7,
43+
};
44+
}
45+
46+
beforeEach(() => {
47+
embeddingProvidersSnapshot = listRegisteredEmbeddingProviders();
48+
memoryEmbeddingProvidersSnapshot = listRegisteredMemoryEmbeddingProviders();
49+
clearEmbeddingProviders();
50+
clearMemoryEmbeddingProviders();
51+
});
52+
53+
afterEach(() => {
54+
restoreRegisteredEmbeddingProviders(embeddingProvidersSnapshot);
55+
restoreRegisteredMemoryEmbeddingProviders(memoryEmbeddingProvidersSnapshot);
56+
});
57+
58+
describe("memory-core generic embedding provider bridge", () => {
59+
it("adapts a contract-declared generic embedding plugin into explicit memory requests", async () => {
60+
const calls: CapturedCall[] = [];
61+
const { config, registry } = createPluginRegistryFixture({
62+
plugins: {
63+
enabled: false,
64+
},
65+
} as OpenClawConfig);
66+
67+
registerVirtualTestPlugin({
68+
registry,
69+
config,
70+
id: "virtual-generic-plugin",
71+
name: "Virtual Generic Embeddings",
72+
contracts: {
73+
embeddingProviders: ["virtual-generic"],
74+
},
75+
register(api) {
76+
api.registerEmbeddingProvider({
77+
id: "virtual-generic",
78+
transport: "remote",
79+
defaultModel: "virtual-default",
80+
create: async (options) => {
81+
expect(options.model).toBe("virtual-model");
82+
expect(options.dimensions).toBe(7);
83+
expect(options.config).toBe(config);
84+
return {
85+
provider: {
86+
id: "virtual-generic",
87+
model: options.model,
88+
dimensions: options.dimensions,
89+
maxInputTokens: 2048,
90+
embed: async (input, callOptions) => {
91+
calls.push({ kind: "embed", input, options: callOptions });
92+
return callOptions?.inputType === "query" ? [1, 2, 3] : [0];
93+
},
94+
embedBatch: async (inputs, callOptions) => {
95+
calls.push({ kind: "embedBatch", input: inputs, options: callOptions });
96+
return inputs.map((_input, index) =>
97+
callOptions?.inputType === "document" ? [index, 7] : [0],
98+
);
99+
},
100+
},
101+
runtime: {
102+
id: "virtual-generic",
103+
inlineQueryTimeoutMs: 1234,
104+
inlineBatchTimeoutMs: 5678,
105+
cacheKeyData: {
106+
provider: "virtual-generic",
107+
model: options.model,
108+
dimensions: options.dimensions,
109+
},
110+
},
111+
};
112+
},
113+
});
114+
},
115+
});
116+
117+
expect(getRegisteredEmbeddingProvider("virtual-generic")?.ownerPluginId).toBe(
118+
"virtual-generic-plugin",
119+
);
120+
expect(registry.registry.embeddingProviders.map((entry) => entry.provider.id)).toEqual([
121+
"virtual-generic",
122+
]);
123+
expect(listRegisteredMemoryEmbeddingProviders()).toEqual([]);
124+
125+
const result = await createEmbeddingProvider(createOptions(config));
126+
127+
expect(result.requestedProvider).toBe("virtual-generic");
128+
expect(result.provider).toMatchObject({
129+
id: "virtual-generic",
130+
model: "virtual-model",
131+
maxInputTokens: 2048,
132+
});
133+
expect(result.runtime).toEqual({
134+
id: "virtual-generic",
135+
inlineQueryTimeoutMs: 1234,
136+
inlineBatchTimeoutMs: 5678,
137+
cacheKeyData: {
138+
provider: "virtual-generic",
139+
model: "virtual-model",
140+
dimensions: 7,
141+
},
142+
});
143+
144+
await expect(result.provider?.embedQuery("query")).resolves.toEqual([1, 2, 3]);
145+
await expect(result.provider?.embedBatch(["doc-a", "doc-b"])).resolves.toEqual([
146+
[0, 7],
147+
[1, 7],
148+
]);
149+
await expect(
150+
result.provider?.embedBatchInputs?.([
151+
{
152+
text: "ignored",
153+
parts: [
154+
{ type: "text", text: "doc-" },
155+
{ type: "text", text: "c" },
156+
],
157+
},
158+
]),
159+
).resolves.toEqual([[0, 7]]);
160+
161+
expect(calls).toEqual([
162+
{
163+
kind: "embed",
164+
input: "query",
165+
options: { inputType: "query" },
166+
},
167+
{
168+
kind: "embedBatch",
169+
input: ["doc-a", "doc-b"],
170+
options: { inputType: "document" },
171+
},
172+
{
173+
kind: "embedBatch",
174+
input: [
175+
{
176+
text: "ignored",
177+
parts: [
178+
{ type: "text", text: "doc-" },
179+
{ type: "text", text: "c" },
180+
],
181+
},
182+
],
183+
options: { inputType: "document" },
184+
},
185+
]);
186+
});
187+
});

0 commit comments

Comments
 (0)