mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
feat(memory): support asymmetric embedding input types
This commit is contained in:
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Providers: add Cerebras as a bundled plugin with onboarding, static model catalog, docs, and manifest-owned endpoint metadata. Thanks @codex.
|
||||
- Memory/OpenAI-compatible: add optional `memorySearch.inputType`, `queryInputType`, and `documentInputType` config for asymmetric embedding endpoints, including direct query embeddings and provider batch indexing. Carries forward #63313 and #60727. Thanks @HOYALIM and @prospect1314521.
|
||||
- Ollama/memory: add model-specific retrieval query prefixes for `nomic-embed-text`, `qwen3-embedding`, and `mxbai-embed-large` memory-search queries while leaving document batches unchanged. Carries forward #45013. Thanks @laolin5564.
|
||||
- Plugins/providers: move pre-runtime model-id normalization, provider endpoint host metadata, and OpenAI-compatible request-family hints into plugin manifests so core no longer carries bundled-provider routing tables. Thanks @codex.
|
||||
- Plugins/install: allow `OPENCLAW_PLUGIN_STAGE_DIR` to contain layered runtime-dependency roots, resolving read-only preinstalled deps before installing missing deps into the final writable root. Fixes #72396. Thanks @liorb-mountapps.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
454c34daa3f5f66a97f6a701968756a77a110fe611e013b0245fe6a9ef274997 config-baseline.json
|
||||
56edd542252c0ec8b3005dcddcf083a568d5e7700f7675c509c2963e36a4597c config-baseline.core.json
|
||||
6bd751bdfd55d644e8a83c575c4db8f67efae00df772c71553b7e7673c5ccc13 config-baseline.json
|
||||
2bf9795740688c55b8ac50ecbfe954c3d3b0dce0a17b08e89a9385d56e197bee config-baseline.core.json
|
||||
07963db49502132f26db396c56b36e018b110e6c55a68b3cb012d3ec96f43901 config-baseline.channel.json
|
||||
f14d1d609ce93893f3bbd6c533251d30328f4deed5cf06da7cb2c9208147dc7a config-baseline.plugin.json
|
||||
ed65cefbef96f034ce2b73069d9d5bacc341a43489ff9b20a34d40956b877f79 config-baseline.plugin.json
|
||||
|
||||
@@ -32,6 +32,11 @@ explicitly:
|
||||
For local embeddings with no API key, install the optional `node-llama-cpp`
|
||||
runtime package next to OpenClaw and use `provider: "local"`.
|
||||
|
||||
Some OpenAI-compatible embedding endpoints require asymmetric labels such as
|
||||
`input_type: "query"` for searches and `input_type: "document"` or `"passage"`
|
||||
for indexed chunks. Configure those with `memorySearch.queryInputType` and
|
||||
`memorySearch.documentInputType`; see the [Memory configuration reference](/reference/memory-config#provider-specific-config).
|
||||
|
||||
## Supported providers
|
||||
|
||||
| Provider | ID | Needs API key | Notes |
|
||||
|
||||
@@ -86,6 +86,30 @@ through PI, `openclaw doctor` warns and leaves the route unchanged.
|
||||
| Realtime voice | Voice Call `realtime.provider: "openai"` / Control UI Talk | Yes |
|
||||
| Embeddings | memory embedding provider | Yes |
|
||||
|
||||
## Memory embeddings
|
||||
|
||||
OpenClaw can use OpenAI, or an OpenAI-compatible embedding endpoint, for
|
||||
`memory_search` indexing and query embeddings:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For OpenAI-compatible endpoints that require asymmetric embedding labels, set
|
||||
`queryInputType` and `documentInputType` under `memorySearch`. OpenClaw forwards
|
||||
those as provider-specific `input_type` request fields: query embeddings use
|
||||
`queryInputType`; indexed memory chunks and batch indexing use
|
||||
`documentInputType`. See the [Memory configuration reference](/reference/memory-config#provider-specific-config) for the full example.
|
||||
|
||||
## Getting started
|
||||
|
||||
Choose your preferred auth method and follow the setup steps.
|
||||
|
||||
@@ -149,6 +149,37 @@ For custom OpenAI-compatible endpoints or overriding provider defaults:
|
||||
Changing model or `outputDimensionality` triggers an automatic full reindex.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="OpenAI-compatible input types">
|
||||
OpenAI-compatible embedding endpoints can opt into provider-specific `input_type` request fields. This is useful for asymmetric embedding models that require different labels for query and document embeddings.
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------- | -------- | ------- | ------------------------------------------------------- |
|
||||
| `inputType` | `string` | unset | Shared `input_type` for query and document embeddings |
|
||||
| `queryInputType` | `string` | unset | Query-time `input_type`; overrides `inputType` |
|
||||
| `documentInputType` | `string` | unset | Index/document `input_type`; overrides `inputType` |
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
remote: {
|
||||
baseUrl: "https://embeddings.example/v1",
|
||||
apiKey: "env:EMBEDDINGS_API_KEY",
|
||||
},
|
||||
model: "asymmetric-embedder",
|
||||
queryInputType: "query",
|
||||
documentInputType: "passage",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Changing these values affects embedding cache identity for provider batch indexing and should be followed by a memory reindex when the upstream model treats the labels differently.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Bedrock">
|
||||
Bedrock uses the AWS SDK default credential chain — no API keys needed. If OpenClaw runs on EC2 with a Bedrock-enabled instance role, just set the provider and model:
|
||||
|
||||
@@ -23,6 +23,9 @@ export function resolveMemoryPrimaryProviderRequest(params: {
|
||||
provider: string;
|
||||
model: string;
|
||||
remote: ResolvedMemorySearchConfig["remote"];
|
||||
inputType: ResolvedMemorySearchConfig["inputType"];
|
||||
queryInputType: ResolvedMemorySearchConfig["queryInputType"];
|
||||
documentInputType: ResolvedMemorySearchConfig["documentInputType"];
|
||||
outputDimensionality: ResolvedMemorySearchConfig["outputDimensionality"];
|
||||
fallback: ResolvedMemorySearchConfig["fallback"];
|
||||
local: ResolvedMemorySearchConfig["local"];
|
||||
@@ -31,6 +34,9 @@ export function resolveMemoryPrimaryProviderRequest(params: {
|
||||
provider: params.settings.provider,
|
||||
model: params.settings.model,
|
||||
remote: params.settings.remote,
|
||||
inputType: params.settings.inputType,
|
||||
queryInputType: params.settings.queryInputType,
|
||||
documentInputType: params.settings.documentInputType,
|
||||
outputDimensionality: params.settings.outputDimensionality,
|
||||
fallback: params.settings.fallback,
|
||||
local: params.settings.local,
|
||||
@@ -75,6 +81,9 @@ export function resolveMemoryFallbackProviderRequest(params: {
|
||||
provider: string;
|
||||
model: string;
|
||||
remote: ResolvedMemorySearchConfig["remote"];
|
||||
inputType: ResolvedMemorySearchConfig["inputType"];
|
||||
queryInputType: ResolvedMemorySearchConfig["queryInputType"];
|
||||
documentInputType: ResolvedMemorySearchConfig["documentInputType"];
|
||||
outputDimensionality: ResolvedMemorySearchConfig["outputDimensionality"];
|
||||
fallback: "none";
|
||||
local: ResolvedMemorySearchConfig["local"];
|
||||
@@ -92,6 +101,9 @@ export function resolveMemoryFallbackProviderRequest(params: {
|
||||
provider: fallback,
|
||||
model: resolveEmbeddingProviderFallbackModel(fallback, params.settings.model, params.cfg),
|
||||
remote: params.settings.remote,
|
||||
inputType: params.settings.inputType,
|
||||
queryInputType: params.settings.queryInputType,
|
||||
documentInputType: params.settings.documentInputType,
|
||||
outputDimensionality: params.settings.outputDimensionality,
|
||||
fallback: "none",
|
||||
local: params.settings.local,
|
||||
|
||||
@@ -136,6 +136,21 @@ describe("memory manager mistral provider wiring", () => {
|
||||
expect(request.outputDimensionality).toBe(1536);
|
||||
});
|
||||
|
||||
it("includes memory input_type fields in the primary provider request", () => {
|
||||
const request = resolveMemoryPrimaryProviderRequest({
|
||||
settings: {
|
||||
...createSettings({ provider: "openai" }),
|
||||
inputType: "passage",
|
||||
queryInputType: "query",
|
||||
documentInputType: "document",
|
||||
} as ResolvedMemorySearchConfig,
|
||||
});
|
||||
|
||||
expect(request.inputType).toBe("passage");
|
||||
expect(request.queryInputType).toBe("query");
|
||||
expect(request.documentInputType).toBe("document");
|
||||
});
|
||||
|
||||
it("uses default lmstudio model when activating lmstudio fallback", async () => {
|
||||
const request = resolveMemoryFallbackProviderRequest({
|
||||
cfg: {} as OpenClawConfig,
|
||||
|
||||
88
extensions/openai/embedding-provider.test.ts
Normal file
88
extensions/openai/embedding-provider.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { MemoryEmbeddingProviderCreateOptions } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
fetchRemoteEmbeddingVectors: vi.fn(async () => [[1, 0]]),
|
||||
resolveRemoteEmbeddingClient: vi.fn(async () => ({
|
||||
baseUrl: "https://embeddings.example/v1",
|
||||
headers: { Authorization: "Bearer test" },
|
||||
model: "text-embedding-3-small",
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/memory-core-host-engine-embeddings", () => ({
|
||||
fetchRemoteEmbeddingVectors: mocks.fetchRemoteEmbeddingVectors,
|
||||
resolveRemoteEmbeddingClient: mocks.resolveRemoteEmbeddingClient,
|
||||
}));
|
||||
|
||||
import { createOpenAiEmbeddingProvider } from "./embedding-provider.js";
|
||||
|
||||
function createOptions(
|
||||
overrides: Partial<MemoryEmbeddingProviderCreateOptions> = {},
|
||||
): MemoryEmbeddingProviderCreateOptions {
|
||||
return {
|
||||
config: {} as MemoryEmbeddingProviderCreateOptions["config"],
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
fallback: "none",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("OpenAI embedding provider", () => {
|
||||
beforeEach(() => {
|
||||
mocks.fetchRemoteEmbeddingVectors.mockClear();
|
||||
mocks.resolveRemoteEmbeddingClient.mockClear();
|
||||
});
|
||||
|
||||
it("sends queryInputType on query embeddings", async () => {
|
||||
const { provider } = await createOpenAiEmbeddingProvider(
|
||||
createOptions({ inputType: "passage", queryInputType: "query" }),
|
||||
);
|
||||
|
||||
await provider.embedQuery("hello");
|
||||
|
||||
expect(mocks.fetchRemoteEmbeddingVectors).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
model: "text-embedding-3-small",
|
||||
input: ["hello"],
|
||||
input_type: "query",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("sends documentInputType on document batch embeddings", async () => {
|
||||
const { provider } = await createOpenAiEmbeddingProvider(
|
||||
createOptions({ inputType: "query", documentInputType: "document" }),
|
||||
);
|
||||
|
||||
await provider.embedBatch(["doc one", "doc two"]);
|
||||
|
||||
expect(mocks.fetchRemoteEmbeddingVectors).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
model: "text-embedding-3-small",
|
||||
input: ["doc one", "doc two"],
|
||||
input_type: "document",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("omits input_type unless configured", async () => {
|
||||
const { provider } = await createOpenAiEmbeddingProvider(createOptions());
|
||||
|
||||
await provider.embedBatch(["doc"]);
|
||||
|
||||
expect(mocks.fetchRemoteEmbeddingVectors).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
model: "text-embedding-3-small",
|
||||
input: ["doc"],
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
createRemoteEmbeddingProvider,
|
||||
fetchRemoteEmbeddingVectors,
|
||||
resolveRemoteEmbeddingClient,
|
||||
type MemoryEmbeddingProvider,
|
||||
type MemoryEmbeddingProviderCreateOptions,
|
||||
@@ -13,6 +13,9 @@ export type OpenAiEmbeddingClient = {
|
||||
ssrfPolicy?: SsrFPolicy;
|
||||
fetchImpl?: typeof fetch;
|
||||
model: string;
|
||||
inputType?: string;
|
||||
queryInputType?: string;
|
||||
documentInputType?: string;
|
||||
};
|
||||
|
||||
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
||||
@@ -35,14 +38,46 @@ export async function createOpenAiEmbeddingProvider(
|
||||
options: MemoryEmbeddingProviderCreateOptions,
|
||||
): Promise<{ provider: MemoryEmbeddingProvider; client: OpenAiEmbeddingClient }> {
|
||||
const client = await resolveOpenAiEmbeddingClient(options);
|
||||
const url = `${client.baseUrl.replace(/\/$/, "")}/embeddings`;
|
||||
|
||||
const resolveInputType = (kind: "query" | "document"): string | undefined => {
|
||||
const explicit = kind === "query" ? client.queryInputType : client.documentInputType;
|
||||
const value = explicit ?? client.inputType;
|
||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
||||
};
|
||||
|
||||
const embed = async (input: string[], kind: "query" | "document"): Promise<number[][]> => {
|
||||
if (input.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const inputType = resolveInputType(kind);
|
||||
return await fetchRemoteEmbeddingVectors({
|
||||
url,
|
||||
headers: client.headers,
|
||||
ssrfPolicy: client.ssrfPolicy,
|
||||
fetchImpl: client.fetchImpl,
|
||||
body: {
|
||||
model: client.model,
|
||||
input,
|
||||
...(inputType ? { input_type: inputType } : {}),
|
||||
},
|
||||
errorPrefix: "openai embeddings failed",
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
provider: createRemoteEmbeddingProvider({
|
||||
provider: {
|
||||
id: "openai",
|
||||
client,
|
||||
errorPrefix: "openai embeddings failed",
|
||||
maxInputTokens: OPENAI_MAX_INPUT_TOKENS[client.model],
|
||||
}),
|
||||
model: client.model,
|
||||
...(typeof OPENAI_MAX_INPUT_TOKENS[client.model] === "number"
|
||||
? { maxInputTokens: OPENAI_MAX_INPUT_TOKENS[client.model] }
|
||||
: {}),
|
||||
embedQuery: async (text) => {
|
||||
const [vec] = await embed([text], "query");
|
||||
return vec ?? [];
|
||||
},
|
||||
embedBatch: async (texts) => await embed(texts, "document"),
|
||||
},
|
||||
client,
|
||||
};
|
||||
}
|
||||
@@ -50,10 +85,16 @@ export async function createOpenAiEmbeddingProvider(
|
||||
export async function resolveOpenAiEmbeddingClient(
|
||||
options: MemoryEmbeddingProviderCreateOptions,
|
||||
): Promise<OpenAiEmbeddingClient> {
|
||||
return await resolveRemoteEmbeddingClient({
|
||||
const client = await resolveRemoteEmbeddingClient({
|
||||
provider: "openai",
|
||||
options,
|
||||
defaultBaseUrl: DEFAULT_OPENAI_BASE_URL,
|
||||
normalizeModel: normalizeOpenAiModel,
|
||||
});
|
||||
return {
|
||||
...client,
|
||||
inputType: options.inputType,
|
||||
queryInputType: options.queryInputType,
|
||||
documentInputType: options.documentInputType,
|
||||
};
|
||||
}
|
||||
|
||||
76
extensions/openai/memory-embedding-adapter.test.ts
Normal file
76
extensions/openai/memory-embedding-adapter.test.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { MemoryEmbeddingProvider } from "openclaw/plugin-sdk/memory-core-host-engine-embeddings";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
createOpenAiEmbeddingProvider: vi.fn(),
|
||||
runOpenAiEmbeddingBatches: vi.fn(async () => new Map([["0", [1, 0]]])),
|
||||
}));
|
||||
|
||||
vi.mock("./embedding-provider.js", () => ({
|
||||
DEFAULT_OPENAI_EMBEDDING_MODEL: "text-embedding-3-small",
|
||||
createOpenAiEmbeddingProvider: mocks.createOpenAiEmbeddingProvider,
|
||||
}));
|
||||
|
||||
vi.mock("./embedding-batch.js", () => ({
|
||||
OPENAI_BATCH_ENDPOINT: "/v1/embeddings",
|
||||
runOpenAiEmbeddingBatches: mocks.runOpenAiEmbeddingBatches,
|
||||
}));
|
||||
|
||||
import { openAiMemoryEmbeddingProviderAdapter } from "./memory-embedding-adapter.js";
|
||||
|
||||
const provider: MemoryEmbeddingProvider = {
|
||||
id: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
embedQuery: async () => [1, 0],
|
||||
embedBatch: async (texts) => texts.map(() => [1, 0]),
|
||||
};
|
||||
|
||||
describe("OpenAI memory embedding adapter", () => {
|
||||
beforeEach(() => {
|
||||
mocks.createOpenAiEmbeddingProvider.mockReset();
|
||||
mocks.runOpenAiEmbeddingBatches.mockClear();
|
||||
mocks.createOpenAiEmbeddingProvider.mockResolvedValue({
|
||||
provider,
|
||||
client: {
|
||||
baseUrl: "https://embeddings.example/v1",
|
||||
headers: {},
|
||||
model: "text-embedding-3-small",
|
||||
inputType: "passage",
|
||||
documentInputType: "document",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("sends document input_type in OpenAI batch embedding requests", async () => {
|
||||
const result = await openAiMemoryEmbeddingProviderAdapter.create({
|
||||
config: {} as never,
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
fallback: "none",
|
||||
});
|
||||
|
||||
await result.runtime?.batchEmbed?.({
|
||||
agentId: "main",
|
||||
chunks: [{ text: "doc one" }],
|
||||
wait: true,
|
||||
concurrency: 1,
|
||||
pollIntervalMs: 1000,
|
||||
timeoutMs: 60_000,
|
||||
debug: () => {},
|
||||
});
|
||||
|
||||
expect(mocks.runOpenAiEmbeddingBatches).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
requests: [
|
||||
expect.objectContaining({
|
||||
body: {
|
||||
model: "text-embedding-3-small",
|
||||
input: "doc one",
|
||||
input_type: "document",
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -32,9 +32,11 @@ export const openAiMemoryEmbeddingProviderAdapter: MemoryEmbeddingProviderAdapte
|
||||
provider: "openai",
|
||||
baseUrl: client.baseUrl,
|
||||
model: client.model,
|
||||
documentInputType: client.documentInputType ?? client.inputType,
|
||||
headers: sanitizeEmbeddingCacheHeaders(client.headers, ["authorization"]),
|
||||
},
|
||||
batchEmbed: async (batch) => {
|
||||
const inputType = client.documentInputType ?? client.inputType;
|
||||
const byCustomId = await runOpenAiEmbeddingBatches({
|
||||
openAi: client,
|
||||
agentId: batch.agentId,
|
||||
@@ -45,6 +47,7 @@ export const openAiMemoryEmbeddingProviderAdapter: MemoryEmbeddingProviderAdapte
|
||||
body: {
|
||||
model: client.model,
|
||||
input: chunk.text,
|
||||
...(inputType ? { input_type: inputType } : {}),
|
||||
},
|
||||
})),
|
||||
wait: batch.wait,
|
||||
|
||||
@@ -480,6 +480,33 @@ describe("memory search config", () => {
|
||||
expect(resolved?.model).toBe("nomic-embed-text");
|
||||
});
|
||||
|
||||
it("merges memory search input_type overrides", () => {
|
||||
const cfg = asConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
inputType: "passage",
|
||||
queryInputType: "query",
|
||||
},
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
default: true,
|
||||
memorySearch: {
|
||||
documentInputType: "document",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const resolved = resolveMemorySearchConfig(cfg, "main");
|
||||
expect(resolved?.inputType).toBe("passage");
|
||||
expect(resolved?.queryInputType).toBe("query");
|
||||
expect(resolved?.documentInputType).toBe("document");
|
||||
});
|
||||
|
||||
it("defaults session delta thresholds", () => {
|
||||
const cfg = asConfig({
|
||||
agents: {
|
||||
|
||||
@@ -35,6 +35,9 @@ export type ResolvedMemorySearchConfig = {
|
||||
};
|
||||
fallback: string;
|
||||
model: string;
|
||||
inputType?: string;
|
||||
queryInputType?: string;
|
||||
documentInputType?: string;
|
||||
outputDimensionality?: number;
|
||||
local: {
|
||||
modelPath?: string;
|
||||
@@ -193,6 +196,11 @@ function mergeConfig(
|
||||
: undefined;
|
||||
const modelDefault = provider === "auto" ? undefined : primaryAdapter?.defaultModel;
|
||||
const model = overrides?.model ?? defaults?.model ?? modelDefault ?? "";
|
||||
const inputType = overrides?.inputType?.trim() || defaults?.inputType?.trim() || undefined;
|
||||
const queryInputType =
|
||||
overrides?.queryInputType?.trim() || defaults?.queryInputType?.trim() || undefined;
|
||||
const documentInputType =
|
||||
overrides?.documentInputType?.trim() || defaults?.documentInputType?.trim() || undefined;
|
||||
const outputDimensionality = overrides?.outputDimensionality ?? defaults?.outputDimensionality;
|
||||
const local = {
|
||||
modelPath: overrides?.local?.modelPath ?? defaults?.local?.modelPath,
|
||||
@@ -306,6 +314,9 @@ function mergeConfig(
|
||||
},
|
||||
fallback,
|
||||
model,
|
||||
inputType,
|
||||
queryInputType,
|
||||
documentInputType,
|
||||
outputDimensionality,
|
||||
local,
|
||||
store,
|
||||
|
||||
@@ -4415,6 +4415,27 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
description:
|
||||
"Embedding model override used by the selected memory provider when a non-default model is required. Set this only when you need explicit recall quality/cost tuning beyond provider defaults.",
|
||||
},
|
||||
inputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
title: "Memory Search Input Type",
|
||||
description:
|
||||
"Optional provider-specific `input_type` value forwarded to compatible embedding requests when the same label should apply to both query and document embeddings. For asymmetric providers, prefer queryInputType and documentInputType.",
|
||||
},
|
||||
queryInputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
title: "Memory Search Query Input Type",
|
||||
description:
|
||||
"Optional provider-specific `input_type` value for query-time memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a query label.",
|
||||
},
|
||||
documentInputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
title: "Memory Search Document Input Type",
|
||||
description:
|
||||
"Optional provider-specific `input_type` value for document and indexing memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a passage or document label.",
|
||||
},
|
||||
outputDimensionality: {
|
||||
type: "integer",
|
||||
exclusiveMinimum: 0,
|
||||
@@ -6363,6 +6384,18 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
model: {
|
||||
type: "string",
|
||||
},
|
||||
inputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
},
|
||||
queryInputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
},
|
||||
documentInputType: {
|
||||
type: "string",
|
||||
minLength: 1,
|
||||
},
|
||||
outputDimensionality: {
|
||||
type: "integer",
|
||||
exclusiveMinimum: 0,
|
||||
@@ -26056,6 +26089,21 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
help: "Embedding model override used by the selected memory provider when a non-default model is required. Set this only when you need explicit recall quality/cost tuning beyond provider defaults.",
|
||||
tags: ["models"],
|
||||
},
|
||||
"agents.defaults.memorySearch.inputType": {
|
||||
label: "Memory Search Input Type",
|
||||
help: "Optional provider-specific `input_type` value forwarded to compatible embedding requests when the same label should apply to both query and document embeddings. For asymmetric providers, prefer queryInputType and documentInputType.",
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.memorySearch.queryInputType": {
|
||||
label: "Memory Search Query Input Type",
|
||||
help: "Optional provider-specific `input_type` value for query-time memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a query label.",
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.memorySearch.documentInputType": {
|
||||
label: "Memory Search Document Input Type",
|
||||
help: "Optional provider-specific `input_type` value for document and indexing memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a passage or document label.",
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.memorySearch.outputDimensionality": {
|
||||
label: "Memory Search Output Dimensionality",
|
||||
help: "Provider-specific output vector size override for memory embeddings. Gemini embedding-2 supports 768, 1536, or 3072; Bedrock families such as Titan V2, Cohere V4, and Nova expose their own allowed sizes. Expect a full reindex when you change it because stored vector dimensions must stay consistent.",
|
||||
|
||||
@@ -93,6 +93,9 @@ const TARGET_KEYS = [
|
||||
"agents.defaults.memorySearch.remote.batch.timeoutMinutes",
|
||||
"agents.defaults.memorySearch.local.modelPath",
|
||||
"agents.defaults.memorySearch.store.path",
|
||||
"agents.defaults.memorySearch.inputType",
|
||||
"agents.defaults.memorySearch.queryInputType",
|
||||
"agents.defaults.memorySearch.documentInputType",
|
||||
"agents.defaults.memorySearch.outputDimensionality",
|
||||
"agents.defaults.memorySearch.store.vector.enabled",
|
||||
"agents.defaults.memorySearch.store.vector.extensionPath",
|
||||
|
||||
@@ -1000,6 +1000,12 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
'Selects the embedding backend used to build/query memory vectors: "openai", "gemini", "voyage", "mistral", "bedrock", "lmstudio", "ollama", or "local". Keep your most reliable provider here and configure fallback for resilience.',
|
||||
"agents.defaults.memorySearch.model":
|
||||
"Embedding model override used by the selected memory provider when a non-default model is required. Set this only when you need explicit recall quality/cost tuning beyond provider defaults.",
|
||||
"agents.defaults.memorySearch.inputType":
|
||||
"Optional provider-specific `input_type` value forwarded to compatible embedding requests when the same label should apply to both query and document embeddings. For asymmetric providers, prefer queryInputType and documentInputType.",
|
||||
"agents.defaults.memorySearch.queryInputType":
|
||||
"Optional provider-specific `input_type` value for query-time memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a query label.",
|
||||
"agents.defaults.memorySearch.documentInputType":
|
||||
"Optional provider-specific `input_type` value for document and indexing memory embeddings. Use this with OpenAI-compatible asymmetric embedding endpoints that require a passage or document label.",
|
||||
"agents.defaults.memorySearch.outputDimensionality":
|
||||
"Provider-specific output vector size override for memory embeddings. Gemini embedding-2 supports 768, 1536, or 3072; Bedrock families such as Titan V2, Cohere V4, and Nova expose their own allowed sizes. Expect a full reindex when you change it because stored vector dimensions must stay consistent.",
|
||||
"agents.defaults.memorySearch.remote.baseUrl":
|
||||
|
||||
@@ -423,6 +423,9 @@ export const FIELD_LABELS: Record<string, string> = {
|
||||
"agents.defaults.memorySearch.remote.batch.pollIntervalMs": "Remote Batch Poll Interval (ms)",
|
||||
"agents.defaults.memorySearch.remote.batch.timeoutMinutes": "Remote Batch Timeout (min)",
|
||||
"agents.defaults.memorySearch.model": "Memory Search Model",
|
||||
"agents.defaults.memorySearch.inputType": "Memory Search Input Type",
|
||||
"agents.defaults.memorySearch.queryInputType": "Memory Search Query Input Type",
|
||||
"agents.defaults.memorySearch.documentInputType": "Memory Search Document Input Type",
|
||||
"agents.defaults.memorySearch.outputDimensionality": "Memory Search Output Dimensionality",
|
||||
"agents.defaults.memorySearch.fallback": "Memory Search Fallback",
|
||||
"agents.defaults.memorySearch.local.modelPath": "Local Embedding Model Path",
|
||||
|
||||
@@ -382,6 +382,12 @@ export type MemorySearchConfig = {
|
||||
fallback?: string;
|
||||
/** Embedding model id (remote) or alias (local). */
|
||||
model?: string;
|
||||
/** Optional provider-specific embedding input_type for query and document requests. */
|
||||
inputType?: string;
|
||||
/** Optional provider-specific embedding input_type for query-time memory search. */
|
||||
queryInputType?: string;
|
||||
/** Optional provider-specific embedding input_type for document/index embeddings. */
|
||||
documentInputType?: string;
|
||||
/**
|
||||
* Gemini embedding-2 models only: output vector dimensions.
|
||||
* Supported values today are 768, 1536, and 3072.
|
||||
|
||||
@@ -680,6 +680,9 @@ export const MemorySearchSchema = z
|
||||
.optional(),
|
||||
fallback: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
inputType: z.string().min(1).optional(),
|
||||
queryInputType: z.string().min(1).optional(),
|
||||
documentInputType: z.string().min(1).optional(),
|
||||
outputDimensionality: z.number().int().positive().optional(),
|
||||
local: z
|
||||
.object({
|
||||
|
||||
@@ -34,6 +34,9 @@ export type EmbeddingProviderOptions = {
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
model: string;
|
||||
inputType?: string;
|
||||
queryInputType?: string;
|
||||
documentInputType?: string;
|
||||
fallback?: EmbeddingProviderFallback;
|
||||
local?: {
|
||||
modelPath?: string;
|
||||
|
||||
@@ -45,6 +45,9 @@ export type MemoryEmbeddingProviderCreateOptions = {
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
model: string;
|
||||
inputType?: string;
|
||||
queryInputType?: string;
|
||||
documentInputType?: string;
|
||||
local?: {
|
||||
modelPath?: string;
|
||||
modelCacheDir?: string;
|
||||
|
||||
@@ -913,6 +913,9 @@ export type ProviderCreateEmbeddingProviderContext = {
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
providerApiKey?: string;
|
||||
inputType?: string;
|
||||
queryInputType?: string;
|
||||
documentInputType?: string;
|
||||
outputDimensionality?: number;
|
||||
taskType?: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user