Deprecate memory-specific embedding provider registration (#85072)

Merged via squash.

Prepared head SHA: 661eb99066
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
Mariano
2026-05-27 15:24:17 +02:00
committed by GitHub
parent 4a8d89f8b5
commit c9d4f7e35c
15 changed files with 250 additions and 28 deletions

View File

@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
### Changes
- Memory: add a core OpenAI-compatible embedding provider for local and hosted OpenAI-style endpoints, with config, doctor, and docs support. (#85269) Thanks @dutifulbob.
- Plugin SDK: mark memory-specific embedding provider registration as deprecated compatibility and surface non-bundled usage in plugin compatibility diagnostics. (#85072) Thanks @mbelinky.
### Fixes

View File

@@ -122,9 +122,9 @@ future feature plugins can consume embeddings without depending on the memory
engine.
Memory search can consume generic `embeddingProviders`. The older
`memoryEmbeddingProviders` contract remains for compatibility while existing
memory-specific providers migrate, but new reusable embedding providers should
use `embeddingProviders`.
`memoryEmbeddingProviders` contract is deprecated compatibility while existing
memory-specific providers migrate; new reusable embedding providers should use
`embeddingProviders`.
## Review checklist

View File

@@ -130,6 +130,9 @@ Current compatibility records include:
`api.runtime.config.loadConfig()` / `api.runtime.config.writeConfigFile(...)`
- legacy memory-plugin split registration while memory plugins move to
`registerMemoryCapability`
- legacy memory-specific embedding provider registration while embedding
providers move to `api.registerEmbeddingProvider(...)` and
`contracts.embeddingProviders`
- legacy channel SDK helpers for native message schemas, mention gating,
inbound envelope formatting, and approval capability nesting
- legacy channel route key and comparable-target helper aliases while plugins

View File

@@ -647,7 +647,7 @@ Each list is optional:
| `speechProviders` | `string[]` | Speech provider ids this plugin owns. |
| `realtimeTranscriptionProviders` | `string[]` | Realtime-transcription provider ids this plugin owns. |
| `realtimeVoiceProviders` | `string[]` | Realtime-voice provider ids this plugin owns. |
| `memoryEmbeddingProviders` | `string[]` | Legacy memory embedding provider ids this plugin owns. |
| `memoryEmbeddingProviders` | `string[]` | Deprecated memory-specific embedding provider ids this plugin owns. |
| `mediaUnderstandingProviders` | `string[]` | Media-understanding provider ids this plugin owns. |
| `transcriptSourceProviders` | `string[]` | Transcript source provider ids this plugin owns. |
| `imageGenerationProviders` | `string[]` | Image-generation provider ids this plugin owns. |
@@ -677,9 +677,9 @@ will be removed after the migration window.
General embedding providers should declare `contracts.embeddingProviders` for
each adapter registered with `api.registerEmbeddingProvider(...)`. Use the
general contract for reusable vector generation, including providers consumed by
memory search. `contracts.memoryEmbeddingProviders` is the older
memory-specific compatibility contract and remains while existing providers
migrate to the generic embedding provider seam.
memory search. `contracts.memoryEmbeddingProviders` is deprecated
memory-specific compatibility and remains only while existing providers migrate
to the generic embedding provider seam.
`contracts.gatewayMethodDispatch` currently accepts
`"authenticated-request"`. It is an API hygiene gate for native plugin HTTP

View File

@@ -868,9 +868,23 @@ canonical replacement.
**New**: one call on the memory-state API -
`registerMemoryCapability(pluginId, { promptBuilder, flushPlanResolver, runtime })`.
Same slots, single registration call. Additive memory helpers
(`registerMemoryPromptSupplement`, `registerMemoryCorpusSupplement`,
`registerMemoryEmbeddingProvider`) are not affected.
Same slots, single registration call. Additive prompt and corpus helpers
(`registerMemoryPromptSupplement`, `registerMemoryCorpusSupplement`) are
not affected.
</Accordion>
<Accordion title="Memory embedding provider API">
**Old**: `api.registerMemoryEmbeddingProvider(...)` plus
`contracts.memoryEmbeddingProviders`.
**New**: `api.registerEmbeddingProvider(...)` plus
`contracts.embeddingProviders`.
The generic embedding provider contract is reusable outside memory and is
the supported path for new providers. The memory-specific registration API
remains wired as deprecated compatibility while existing providers migrate.
Plugin inspection reports non-bundled usage as compatibility debt.
</Accordion>

View File

@@ -111,7 +111,7 @@ also be listed in `contracts.embeddingProviders` in the plugin manifest. This
is the generic embedding surface for reusable vector generation. Memory search
can consume this generic provider surface. The older
`api.registerMemoryEmbeddingProvider(...)` and
`contracts.memoryEmbeddingProviders` seam remains as compatibility while
`contracts.memoryEmbeddingProviders` seam is deprecated compatibility while
existing memory-specific providers migrate.
### Tools and commands
@@ -378,7 +378,7 @@ For an end-to-end authoring guide, see
| `api.registerMemoryFlushPlan(resolver)` | Memory flush plan resolver |
| `api.registerMemoryRuntime(runtime)` | Memory runtime adapter |
### Memory embedding adapters
### Deprecated memory embedding adapters
| Method | What it registers |
| ---------------------------------------------- | ---------------------------------------------- |
@@ -394,12 +394,12 @@ For an end-to-end authoring guide, see
- `MemoryFlushPlan.model` can pin the flush turn to an exact `provider/model`
reference, such as `ollama/qwen3:8b`, without inheriting the active fallback
chain.
- `registerMemoryEmbeddingProvider` lets the active memory plugin register one
or more embedding adapter ids (for example `openai`, `gemini`, or a custom
plugin-defined id).
- User config such as `agents.defaults.memorySearch.provider` and
`agents.defaults.memorySearch.fallback` resolves against those registered
adapter ids.
- `registerMemoryEmbeddingProvider` is deprecated. New embedding providers
should use `api.registerEmbeddingProvider(...)` and
`contracts.embeddingProviders`.
- Existing memory-specific providers continue to work during the migration
window, but plugin inspection reports this as compatibility debt for
non-bundled plugins.
### Events and lifecycle

View File

@@ -685,9 +685,9 @@ API key auth, and dynamic model resolution.
```
Declare the same id in `contracts.embeddingProviders`. This is the
general embedding contract for reusable vector generation. Use
`registerMemoryEmbeddingProvider(...)` only for memory-engine-specific
adapters.
general embedding contract for reusable vector generation, including
memory search. `registerMemoryEmbeddingProvider(...)` is deprecated
compatibility for existing memory-specific adapters.
</Tab>
<Tab title="Image and video generation">
Video capabilities use a **mode-aware** shape: `generate`,

View File

@@ -342,7 +342,7 @@ and pairing-path families.
| `plugin-sdk/memory-core` | Bundled memory-core helper surface for manager/config/file/CLI helpers |
| `plugin-sdk/memory-core-engine-runtime` | Memory index/search runtime facade |
| `plugin-sdk/memory-core-host-engine-foundation` | Memory host foundation engine exports |
| `plugin-sdk/memory-core-host-engine-embeddings` | Memory host embedding contracts, registry access, local provider, and generic batch/remote helpers |
| `plugin-sdk/memory-core-host-engine-embeddings` | Memory host embedding contracts, registry access, local provider, and generic batch/remote helpers. `registerMemoryEmbeddingProvider` on this surface is deprecated; use the generic embedding provider API for new providers. |
| `plugin-sdk/memory-core-host-engine-qmd` | Memory host QMD engine exports |
| `plugin-sdk/memory-core-host-engine-storage` | Memory host storage engine exports |
| `plugin-sdk/memory-core-host-multimodal` | Memory host multimodal helpers |

View File

@@ -57,10 +57,13 @@ export {
listRegisteredMemoryEmbeddingProviderAdapters,
listRegisteredMemoryEmbeddingProviders,
} from "../plugins/memory-embedding-provider-runtime.js";
export {
clearMemoryEmbeddingProviders,
registerMemoryEmbeddingProvider,
} from "../plugins/memory-embedding-providers.js";
export { clearMemoryEmbeddingProviders } from "../plugins/memory-embedding-providers.js";
/**
* @deprecated New embedding providers should use `api.registerEmbeddingProvider(...)`
* and `contracts.embeddingProviders`. This memory-specific registrar remains
* available only for compatibility while existing providers migrate.
*/
export { registerMemoryEmbeddingProvider } from "../plugins/memory-embedding-providers.js";
export type {
MemoryEmbeddingBatchChunk,
MemoryEmbeddingBatchOptions,

View File

@@ -157,6 +157,11 @@ const knownDeprecatedSurfaceMarkers = [
file: "src/plugins/hook-types.ts",
marker: "@deprecated Use gateway_stop",
},
{
code: "deprecated-memory-embedding-provider-api",
file: "src/plugins/types.ts",
marker: "registerMemoryEmbeddingProvider",
},
{
code: "channel-route-key-aliases",
file: "src/plugin-sdk/channel-route.ts",

View File

@@ -50,6 +50,31 @@ export const PLUGIN_COMPAT_RECORDS = [
diagnostics: ["plugin compatibility notice"],
tests: ["src/plugins/status.test.ts", "src/plugins/contracts/shape.contract.test.ts"],
},
{
code: "deprecated-memory-embedding-provider-api",
status: "deprecated",
owner: "sdk",
introduced: "2026-05-21",
deprecated: "2026-05-21",
warningStarts: "2026-05-21",
removeAfter: "2026-08-21",
replacement: "`api.registerEmbeddingProvider(...)` and `contracts.embeddingProviders`",
docsPath: "/plugins/sdk-migration#memory-embedding-provider-api",
surfaces: [
"api.registerMemoryEmbeddingProvider(...)",
"contracts.memoryEmbeddingProviders",
"openclaw/plugin-sdk/memory-core-host-engine-embeddings registerMemoryEmbeddingProvider",
"plugins inspect compatibility notices",
],
diagnostics: ["plugin compatibility notice", "plugin SDK package guardrail"],
tests: [
"src/plugins/status.test.ts",
"src/plugins/compat/registry.test.ts",
"src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts",
],
releaseNote:
"Memory-specific embedding provider registration remains wired as a deprecated compatibility path while providers migrate to the generic embedding provider contract.",
},
{
code: "legacy-root-sdk-import",
status: "deprecated",

View File

@@ -54,6 +54,30 @@ const DEPRECATED_TEST_BARREL_ALLOWED_REFERENCE_FILES = new Set([
"src/plugins/contracts/plugin-entry-guardrails.test.ts",
"src/plugins/contracts/plugin-sdk-package-contract-guardrails.test.ts",
]);
const LEGACY_MEMORY_EMBEDDING_PROVIDER_API_FILES = new Set([
"extensions/amazon-bedrock/register.sync.runtime.ts",
"extensions/deepinfra/index.ts",
"extensions/github-copilot/index.ts",
"extensions/google/index.ts",
"extensions/lmstudio/index.ts",
"extensions/memory-core/src/memory/provider-adapters.ts",
"extensions/mistral/index.ts",
"extensions/ollama/index.ts",
"extensions/openai/index.ts",
"extensions/voyage/index.ts",
]);
const LEGACY_MEMORY_EMBEDDING_PROVIDER_MANIFEST_FILES = new Set([
"extensions/amazon-bedrock/openclaw.plugin.json",
"extensions/deepinfra/openclaw.plugin.json",
"extensions/github-copilot/openclaw.plugin.json",
"extensions/google/openclaw.plugin.json",
"extensions/lmstudio/openclaw.plugin.json",
"extensions/memory-core/openclaw.plugin.json",
"extensions/mistral/openclaw.plugin.json",
"extensions/ollama/openclaw.plugin.json",
"extensions/openai/openclaw.plugin.json",
"extensions/voyage/openclaw.plugin.json",
]);
const MATRIX_RUNTIME_DEPS = [
"@matrix-org/matrix-sdk-crypto-wasm",
"@matrix-org/matrix-sdk-crypto-nodejs",
@@ -396,6 +420,43 @@ function collectDeprecatedExtensionSdkImports(): Array<{ file: string; specifier
return leaks;
}
function collectNewDeprecatedMemoryEmbeddingProviderApiFiles(): string[] {
const files: string[] = [];
for (const file of collectExtensionFiles(resolve(REPO_ROOT, "extensions"))) {
const repoRelativePath = toRepoRelativePath(file);
if (isExtensionTestOrSupportPath(repoRelativePath)) {
continue;
}
const source = fs.readFileSync(file, "utf8");
if (
/\b(?:[A-Za-z_$][\w$]*\.)?registerMemoryEmbeddingProvider\s*\(/u.test(source) &&
!LEGACY_MEMORY_EMBEDDING_PROVIDER_API_FILES.has(repoRelativePath)
) {
files.push(repoRelativePath);
}
}
return files.toSorted();
}
function collectNewDeprecatedMemoryEmbeddingProviderManifestFiles(): string[] {
const files: string[] = [];
const manifestFiles =
listGitTrackedFiles({
repoRoot: REPO_ROOT,
pathspecs: "extensions/**/openclaw.plugin.json",
}) ?? [];
for (const repoRelativePath of manifestFiles) {
const source = fs.readFileSync(resolve(REPO_ROOT, repoRelativePath), "utf8");
if (
/"memoryEmbeddingProviders"\s*:/u.test(source) &&
!LEGACY_MEMORY_EMBEDDING_PROVIDER_MANIFEST_FILES.has(repoRelativePath)
) {
files.push(repoRelativePath);
}
}
return files.toSorted();
}
function collectCodeFiles(dir: string): string[] {
const trackedFiles = listTrackedCodeFiles(dir);
if (trackedFiles) {
@@ -867,6 +928,16 @@ describe("plugin-sdk package contract guardrails", () => {
expect(collectDeprecatedExtensionSdkImports()).toStrictEqual([]);
});
it("keeps new bundled plugins off deprecated memory embedding provider registration", () => {
expect({
apiFiles: collectNewDeprecatedMemoryEmbeddingProviderApiFiles(),
manifestFiles: collectNewDeprecatedMemoryEmbeddingProviderManifestFiles(),
}).toStrictEqual({
apiFiles: [],
manifestFiles: [],
});
});
it("keeps real tests off deprecated plugin-sdk testing barrels", () => {
expect(collectDeprecatedTestBarrelImports()).toStrictEqual([]);
});

View File

@@ -7,6 +7,8 @@ export const LEGACY_BEFORE_AGENT_START_MESSAGE =
"still uses legacy before_agent_start; keep regression coverage on this plugin, and prefer before_model_resolve/before_prompt_build for new work.";
export const HOOK_ONLY_MESSAGE =
"is hook-only. This remains a supported compatibility path, but it has not migrated to explicit capability registration yet.";
export const DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE =
"uses deprecated memory-specific embedding provider API; use api.registerEmbeddingProvider and contracts.embeddingProviders for new embedding providers.";
export function createCompatibilityNotice(
params: Pick<PluginCompatibilityNotice, "pluginId" | "code">,
@@ -28,6 +30,14 @@ export function createCompatibilityNotice(
severity: "info",
message: HOOK_ONLY_MESSAGE,
};
case "deprecated-memory-embedding-provider-api":
return {
pluginId: params.pluginId,
code: params.code,
compatCode: "deprecated-memory-embedding-provider-api",
severity: "warn",
message: DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE,
};
}
const unsupportedCode: never = params.code;
void unsupportedCode;

View File

@@ -1,10 +1,12 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginMemoryEmbeddingProviderRegistration } from "./registry-types.js";
import {
createCompatibilityNotice,
createCustomHook,
createPluginLoadResult,
createPluginRecord,
createTypedHook,
DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE,
HOOK_ONLY_MESSAGE,
LEGACY_BEFORE_AGENT_START_MESSAGE,
} from "./status.test-helpers.js";
@@ -838,6 +840,74 @@ describe("plugin status reports", () => {
});
});
it("warns external plugins off deprecated memory embedding provider registration", () => {
setSinglePluginLoadResult(
createPluginRecord({
id: "legacy-memory-provider",
name: "Legacy Memory Provider",
memoryEmbeddingProviderIds: ["legacy-memory-provider"],
contracts: { memoryEmbeddingProviders: ["legacy-memory-provider"] },
}),
);
expectCompatibilityOutput({
notices: [
createCompatibilityNotice({
pluginId: "legacy-memory-provider",
code: "deprecated-memory-embedding-provider-api",
}),
],
warnings: [`legacy-memory-provider ${DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE}`],
});
});
it("warns when external plugins register memory embedding providers at runtime only", () => {
const runtimeProviderRegistration: PluginMemoryEmbeddingProviderRegistration = {
pluginId: "runtime-only-legacy-memory-provider",
pluginName: "Runtime Only Legacy Memory Provider",
provider: {
id: "runtime-only-legacy-memory-provider",
create: async () => ({ provider: null }),
},
source: "/tmp/runtime-only-legacy-memory-provider/index.ts",
};
setPluginLoadResult({
plugins: [
createPluginRecord({
id: "runtime-only-legacy-memory-provider",
name: "Runtime Only Legacy Memory Provider",
}),
],
memoryEmbeddingProviders: [runtimeProviderRegistration],
});
expectCompatibilityOutput({
notices: [
createCompatibilityNotice({
pluginId: "runtime-only-legacy-memory-provider",
code: "deprecated-memory-embedding-provider-api",
}),
],
warnings: [
`runtime-only-legacy-memory-provider ${DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE}`,
],
});
});
it("does not surface bundled memory embedding migration debt as user warnings", () => {
setSinglePluginLoadResult(
createPluginRecord({
id: "bundled-memory-provider",
name: "Bundled Memory Provider",
origin: "bundled",
memoryEmbeddingProviderIds: ["bundled-memory-provider"],
contracts: { memoryEmbeddingProviders: ["bundled-memory-provider"] },
}),
);
expectNoCompatibilityWarnings();
});
it("builds structured compatibility notices with deterministic ordering", () => {
setPluginLoadResult({
plugins: [

View File

@@ -53,7 +53,7 @@ export type { PluginCapabilityKind, PluginInspectShape } from "./inspect-shape.j
export type PluginCompatibilityNotice = {
pluginId: string;
code: "legacy-before-agent-start" | "hook-only";
code: "legacy-before-agent-start" | "hook-only" | "deprecated-memory-embedding-provider-api";
compatCode: PluginCompatCode;
severity: "warn" | "info";
message: string;
@@ -113,7 +113,9 @@ export type PluginInspectReport = {
};
function buildCompatibilityNoticesForInspect(
inspect: Pick<PluginInspectReport, "plugin" | "shape" | "usesLegacyBeforeAgentStart">,
inspect: Pick<PluginInspectReport, "plugin" | "shape" | "usesLegacyBeforeAgentStart"> & {
hasRuntimeMemoryEmbeddingProviderRegistration: boolean;
},
): PluginCompatibilityNotice[] {
const warnings: PluginCompatibilityNotice[] = [];
if (inspect.usesLegacyBeforeAgentStart) {
@@ -136,6 +138,20 @@ function buildCompatibilityNoticesForInspect(
"is hook-only. This remains a supported compatibility path, but it has not migrated to explicit capability registration yet.",
});
}
const usesMemoryEmbeddingProviderApi =
inspect.plugin.memoryEmbeddingProviderIds.length > 0 ||
(inspect.plugin.contracts?.memoryEmbeddingProviders?.length ?? 0) > 0 ||
inspect.hasRuntimeMemoryEmbeddingProviderRegistration;
if (usesMemoryEmbeddingProviderApi && inspect.plugin.origin !== "bundled") {
warnings.push({
pluginId: inspect.plugin.id,
code: "deprecated-memory-embedding-provider-api",
compatCode: "deprecated-memory-embedding-provider-api",
severity: "warn",
message:
"uses deprecated memory-specific embedding provider API; use api.registerEmbeddingProvider and contracts.embeddingProviders for new embedding providers.",
});
}
return warnings;
}
@@ -495,10 +511,14 @@ export function buildPluginInspectReport(params: {
}
const usesLegacyBeforeAgentStart = shapeSummary.usesLegacyBeforeAgentStart;
const hasRuntimeMemoryEmbeddingProviderRegistration = report.memoryEmbeddingProviders.some(
(entry) => entry.pluginId === plugin.id,
);
const compatibility = buildCompatibilityNoticesForInspect({
plugin,
shape,
usesLegacyBeforeAgentStart,
hasRuntimeMemoryEmbeddingProviderRegistration,
});
return {
workspaceDir: report.workspaceDir,