mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
feat(memory-wiki): allow per-call search corpus overrides
This commit is contained in:
@@ -3,8 +3,13 @@ import type { Command } from "commander";
|
||||
import type { OpenClawConfig } from "../api.js";
|
||||
import { applyMemoryWikiMutation } from "./apply.js";
|
||||
import { compileMemoryWikiVault } from "./compile.js";
|
||||
import type { MemoryWikiPluginConfig, ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import { resolveMemoryWikiConfig } from "./config.js";
|
||||
import {
|
||||
resolveMemoryWikiConfig,
|
||||
WIKI_SEARCH_BACKENDS,
|
||||
WIKI_SEARCH_CORPORA,
|
||||
type MemoryWikiPluginConfig,
|
||||
type ResolvedMemoryWikiConfig,
|
||||
} from "./config.js";
|
||||
import { ingestMemoryWikiSource } from "./ingest.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import {
|
||||
@@ -52,12 +57,16 @@ type WikiIngestCommandOptions = {
|
||||
type WikiSearchCommandOptions = {
|
||||
json?: boolean;
|
||||
maxResults?: number;
|
||||
backend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
corpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
};
|
||||
|
||||
type WikiGetCommandOptions = {
|
||||
json?: boolean;
|
||||
from?: number;
|
||||
lines?: number;
|
||||
backend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
corpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
};
|
||||
|
||||
type WikiApplySynthesisCommandOptions = {
|
||||
@@ -133,6 +142,17 @@ function normalizeCliStringList(values?: string[]): string[] | undefined {
|
||||
return normalized.length > 0 ? normalized : undefined;
|
||||
}
|
||||
|
||||
function parseWikiSearchEnumOption<T extends string>(
|
||||
value: string,
|
||||
allowed: readonly T[],
|
||||
label: string,
|
||||
): T {
|
||||
if ((allowed as readonly string[]).includes(value)) {
|
||||
return value as T;
|
||||
}
|
||||
throw new Error(`Invalid ${label}: ${value}. Expected one of: ${allowed.join(", ")}`);
|
||||
}
|
||||
|
||||
async function resolveWikiApplyBody(params: { body?: string; bodyFile?: string }): Promise<string> {
|
||||
if (params.body?.trim()) {
|
||||
return params.body;
|
||||
@@ -243,6 +263,8 @@ export async function runWikiSearch(params: {
|
||||
appConfig?: OpenClawConfig;
|
||||
query: string;
|
||||
maxResults?: number;
|
||||
searchBackend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
searchCorpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
json?: boolean;
|
||||
stdout?: Pick<NodeJS.WriteStream, "write">;
|
||||
}) {
|
||||
@@ -252,6 +274,8 @@ export async function runWikiSearch(params: {
|
||||
appConfig: params.appConfig,
|
||||
query: params.query,
|
||||
maxResults: params.maxResults,
|
||||
searchBackend: params.searchBackend,
|
||||
searchCorpus: params.searchCorpus,
|
||||
});
|
||||
const summary = params.json
|
||||
? JSON.stringify(results, null, 2)
|
||||
@@ -273,6 +297,8 @@ export async function runWikiGet(params: {
|
||||
lookup: string;
|
||||
fromLine?: number;
|
||||
lineCount?: number;
|
||||
searchBackend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
searchCorpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
json?: boolean;
|
||||
stdout?: Pick<NodeJS.WriteStream, "write">;
|
||||
}) {
|
||||
@@ -283,6 +309,8 @@ export async function runWikiGet(params: {
|
||||
lookup: params.lookup,
|
||||
fromLine: params.fromLine,
|
||||
lineCount: params.lineCount,
|
||||
searchBackend: params.searchBackend,
|
||||
searchCorpus: params.searchCorpus,
|
||||
});
|
||||
const summary = params.json
|
||||
? JSON.stringify(result, null, 2)
|
||||
@@ -545,6 +573,16 @@ export function registerWikiCli(
|
||||
.description("Search wiki pages and, when configured, the active memory corpus")
|
||||
.argument("<query>", "Search query")
|
||||
.option("--max-results <n>", "Maximum results", (value: string) => Number(value))
|
||||
.option(
|
||||
"--backend <backend>",
|
||||
`Search backend (${WIKI_SEARCH_BACKENDS.join(", ")})`,
|
||||
(value: string) => parseWikiSearchEnumOption(value, WIKI_SEARCH_BACKENDS, "backend"),
|
||||
)
|
||||
.option(
|
||||
"--corpus <corpus>",
|
||||
`Search corpus (${WIKI_SEARCH_CORPORA.join(", ")})`,
|
||||
(value: string) => parseWikiSearchEnumOption(value, WIKI_SEARCH_CORPORA, "corpus"),
|
||||
)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (query: string, opts: WikiSearchCommandOptions) => {
|
||||
await runWikiSearch({
|
||||
@@ -552,6 +590,8 @@ export function registerWikiCli(
|
||||
appConfig,
|
||||
query,
|
||||
maxResults: opts.maxResults,
|
||||
searchBackend: opts.backend,
|
||||
searchCorpus: opts.corpus,
|
||||
json: opts.json,
|
||||
});
|
||||
});
|
||||
@@ -562,6 +602,16 @@ export function registerWikiCli(
|
||||
.argument("<lookup>", "Relative path or page id")
|
||||
.option("--from <n>", "Start line", (value: string) => Number(value))
|
||||
.option("--lines <n>", "Number of lines", (value: string) => Number(value))
|
||||
.option(
|
||||
"--backend <backend>",
|
||||
`Search backend (${WIKI_SEARCH_BACKENDS.join(", ")})`,
|
||||
(value: string) => parseWikiSearchEnumOption(value, WIKI_SEARCH_BACKENDS, "backend"),
|
||||
)
|
||||
.option(
|
||||
"--corpus <corpus>",
|
||||
`Search corpus (${WIKI_SEARCH_CORPORA.join(", ")})`,
|
||||
(value: string) => parseWikiSearchEnumOption(value, WIKI_SEARCH_CORPORA, "corpus"),
|
||||
)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (lookup: string, opts: WikiGetCommandOptions) => {
|
||||
await runWikiGet({
|
||||
@@ -570,6 +620,8 @@ export function registerWikiCli(
|
||||
lookup,
|
||||
fromLine: opts.from,
|
||||
lineCount: opts.lines,
|
||||
searchBackend: opts.backend,
|
||||
searchCorpus: opts.corpus,
|
||||
json: opts.json,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { OpenClawConfig, OpenClawPluginApi } from "../api.js";
|
||||
import { applyMemoryWikiMutation, normalizeMemoryWikiMutationInput } from "./apply.js";
|
||||
import { compileMemoryWikiVault } from "./compile.js";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import {
|
||||
WIKI_SEARCH_BACKENDS,
|
||||
WIKI_SEARCH_CORPORA,
|
||||
type ResolvedMemoryWikiConfig,
|
||||
} from "./config.js";
|
||||
import { ingestMemoryWikiSource } from "./ingest.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import {
|
||||
@@ -48,6 +52,21 @@ function readNumberParam(params: Record<string, unknown>, key: string): number |
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function readEnumParam<T extends string>(
|
||||
params: Record<string, unknown>,
|
||||
key: string,
|
||||
allowed: readonly T[],
|
||||
): T | undefined {
|
||||
const value = readStringParam(params, key);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
if ((allowed as readonly string[]).includes(value)) {
|
||||
return value as T;
|
||||
}
|
||||
throw new Error(`${key} must be one of: ${allowed.join(", ")}.`);
|
||||
}
|
||||
|
||||
function respondError(
|
||||
respond: Parameters<OpenClawPluginApi["registerGatewayMethod"]>[1] extends (
|
||||
ctx: infer T,
|
||||
@@ -203,6 +222,8 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
const query = readStringParam(requestParams, "query", { required: true });
|
||||
const maxResults = readNumberParam(requestParams, "maxResults");
|
||||
const searchBackend = readEnumParam(requestParams, "backend", WIKI_SEARCH_BACKENDS);
|
||||
const searchCorpus = readEnumParam(requestParams, "corpus", WIKI_SEARCH_CORPORA);
|
||||
respond(
|
||||
true,
|
||||
await searchMemoryWiki({
|
||||
@@ -210,6 +231,8 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
appConfig,
|
||||
query,
|
||||
maxResults,
|
||||
searchBackend,
|
||||
searchCorpus,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -246,6 +269,8 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
const lookup = readStringParam(requestParams, "lookup", { required: true });
|
||||
const fromLine = readNumberParam(requestParams, "fromLine");
|
||||
const lineCount = readNumberParam(requestParams, "lineCount");
|
||||
const searchBackend = readEnumParam(requestParams, "backend", WIKI_SEARCH_BACKENDS);
|
||||
const searchCorpus = readEnumParam(requestParams, "corpus", WIKI_SEARCH_CORPORA);
|
||||
respond(
|
||||
true,
|
||||
await getMemoryWikiPage({
|
||||
@@ -254,6 +279,8 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
lookup,
|
||||
fromLine,
|
||||
lineCount,
|
||||
searchBackend,
|
||||
searchCorpus,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -173,6 +173,51 @@ describe("searchMemoryWiki", () => {
|
||||
expect(manager.search).toHaveBeenCalledWith("alpha", { maxResults: 5 });
|
||||
});
|
||||
|
||||
it("allows per-call corpus overrides without changing config defaults", async () => {
|
||||
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
|
||||
tempDirs.push(rootDir);
|
||||
const config = resolveMemoryWikiConfig(
|
||||
{
|
||||
vault: { path: rootDir },
|
||||
search: { backend: "shared", corpus: "wiki" },
|
||||
},
|
||||
{ homedir: "/Users/tester" },
|
||||
);
|
||||
await initializeMemoryWikiVault(config);
|
||||
await fs.writeFile(
|
||||
path.join(rootDir, "sources", "alpha.md"),
|
||||
renderWikiMarkdown({
|
||||
frontmatter: { pageType: "source", id: "source.alpha", title: "Alpha Source" },
|
||||
body: "# Alpha Source\n\nalpha body text\n",
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const manager = createMemoryManager({
|
||||
searchResults: [
|
||||
{
|
||||
path: "MEMORY.md",
|
||||
startLine: 10,
|
||||
endLine: 12,
|
||||
score: 99,
|
||||
snippet: "memory-only alpha",
|
||||
source: "memory",
|
||||
},
|
||||
],
|
||||
});
|
||||
getActiveMemorySearchManagerMock.mockResolvedValue({ manager });
|
||||
|
||||
const memoryOnly = await searchMemoryWiki({
|
||||
config,
|
||||
appConfig: createAppConfig(),
|
||||
query: "alpha",
|
||||
searchCorpus: "memory",
|
||||
});
|
||||
|
||||
expect(memoryOnly).toHaveLength(1);
|
||||
expect(memoryOnly[0]?.corpus).toBe("memory");
|
||||
expect(manager.search).toHaveBeenCalledWith("alpha", { maxResults: 10 });
|
||||
});
|
||||
|
||||
it("keeps memory search disabled when the backend is local", async () => {
|
||||
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
|
||||
tempDirs.push(rootDir);
|
||||
@@ -335,4 +380,43 @@ describe("getMemoryWikiPage", () => {
|
||||
lines: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it("allows per-call get overrides to bypass wiki and force memory fallback", async () => {
|
||||
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
|
||||
tempDirs.push(rootDir);
|
||||
const config = resolveMemoryWikiConfig(
|
||||
{
|
||||
vault: { path: rootDir },
|
||||
search: { backend: "shared", corpus: "wiki" },
|
||||
},
|
||||
{ homedir: "/Users/tester" },
|
||||
);
|
||||
await initializeMemoryWikiVault(config);
|
||||
await fs.writeFile(
|
||||
path.join(rootDir, "sources", "MEMORY.md"),
|
||||
renderWikiMarkdown({
|
||||
frontmatter: { pageType: "source", id: "source.memory.shadow", title: "Shadow Memory" },
|
||||
body: "# Shadow Memory\n\nwiki copy\n",
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
const manager = createMemoryManager({
|
||||
readResult: {
|
||||
path: "MEMORY.md",
|
||||
text: "forced memory read",
|
||||
},
|
||||
});
|
||||
getActiveMemorySearchManagerMock.mockResolvedValue({ manager });
|
||||
|
||||
const result = await getMemoryWikiPage({
|
||||
config,
|
||||
appConfig: createAppConfig(),
|
||||
lookup: "MEMORY.md",
|
||||
searchCorpus: "memory",
|
||||
});
|
||||
|
||||
expect(result?.corpus).toBe("memory");
|
||||
expect(result?.content).toBe("forced memory read");
|
||||
expect(manager.readFile).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { resolveDefaultAgentId } from "openclaw/plugin-sdk/config-runtime";
|
||||
import type { MemorySearchResult } from "openclaw/plugin-sdk/memory-host-files";
|
||||
import { getActiveMemorySearchManager } from "openclaw/plugin-sdk/memory-host-search";
|
||||
import type { OpenClawConfig } from "../api.js";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import type { ResolvedMemoryWikiConfig, WikiSearchBackend, WikiSearchCorpus } from "./config.js";
|
||||
import { parseWikiMarkdown, toWikiPageSummary, type WikiPageSummary } from "./markdown.js";
|
||||
import { initializeMemoryWikiVault } from "./vault.js";
|
||||
|
||||
@@ -49,6 +49,11 @@ export type QueryableWikiPage = WikiPageSummary & {
|
||||
raw: string;
|
||||
};
|
||||
|
||||
type QuerySearchOverrides = {
|
||||
searchBackend?: WikiSearchBackend;
|
||||
searchCorpus?: WikiSearchCorpus;
|
||||
};
|
||||
|
||||
async function listWikiMarkdownFiles(rootDir: string): Promise<string[]> {
|
||||
const files = (
|
||||
await Promise.all(
|
||||
@@ -174,6 +179,22 @@ function buildMemorySearchTitle(resultPath: string): string {
|
||||
return basename.length > 0 ? basename : resultPath;
|
||||
}
|
||||
|
||||
function applySearchOverrides(
|
||||
config: ResolvedMemoryWikiConfig,
|
||||
overrides?: QuerySearchOverrides,
|
||||
): ResolvedMemoryWikiConfig {
|
||||
if (!overrides?.searchBackend && !overrides?.searchCorpus) {
|
||||
return config;
|
||||
}
|
||||
return {
|
||||
...config,
|
||||
search: {
|
||||
backend: overrides.searchBackend ?? config.search.backend,
|
||||
corpus: overrides.searchCorpus ?? config.search.corpus,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildWikiProvenanceLabel(
|
||||
page: Pick<
|
||||
WikiPageSummary,
|
||||
@@ -249,17 +270,20 @@ export async function searchMemoryWiki(params: {
|
||||
appConfig?: OpenClawConfig;
|
||||
query: string;
|
||||
maxResults?: number;
|
||||
searchBackend?: WikiSearchBackend;
|
||||
searchCorpus?: WikiSearchCorpus;
|
||||
}): Promise<WikiSearchResult[]> {
|
||||
await initializeMemoryWikiVault(params.config);
|
||||
const effectiveConfig = applySearchOverrides(params.config, params);
|
||||
await initializeMemoryWikiVault(effectiveConfig);
|
||||
const maxResults = Math.max(1, params.maxResults ?? 10);
|
||||
|
||||
const wikiResults = shouldSearchWiki(params.config)
|
||||
? (await readQueryableWikiPages(params.config.vault.path))
|
||||
const wikiResults = shouldSearchWiki(effectiveConfig)
|
||||
? (await readQueryableWikiPages(effectiveConfig.vault.path))
|
||||
.map((page) => toWikiSearchResult(page, params.query))
|
||||
.filter((page) => page.score > 0)
|
||||
: [];
|
||||
|
||||
const sharedMemoryManager = shouldSearchSharedMemory(params.config, params.appConfig)
|
||||
const sharedMemoryManager = shouldSearchSharedMemory(effectiveConfig, params.appConfig)
|
||||
? await resolveActiveMemoryManager(params.appConfig)
|
||||
: null;
|
||||
const memoryResults = sharedMemoryManager
|
||||
@@ -284,13 +308,16 @@ export async function getMemoryWikiPage(params: {
|
||||
lookup: string;
|
||||
fromLine?: number;
|
||||
lineCount?: number;
|
||||
searchBackend?: WikiSearchBackend;
|
||||
searchCorpus?: WikiSearchCorpus;
|
||||
}): Promise<WikiGetResult | null> {
|
||||
await initializeMemoryWikiVault(params.config);
|
||||
const effectiveConfig = applySearchOverrides(params.config, params);
|
||||
await initializeMemoryWikiVault(effectiveConfig);
|
||||
const fromLine = Math.max(1, params.fromLine ?? 1);
|
||||
const lineCount = Math.max(1, params.lineCount ?? 200);
|
||||
|
||||
if (shouldSearchWiki(params.config)) {
|
||||
const pages = await readQueryableWikiPages(params.config.vault.path);
|
||||
if (shouldSearchWiki(effectiveConfig)) {
|
||||
const pages = await readQueryableWikiPages(effectiveConfig.vault.path);
|
||||
const page = resolveQueryableWikiPageByLookup(pages, params.lookup);
|
||||
if (page) {
|
||||
const parsed = parseWikiMarkdown(page.raw);
|
||||
@@ -317,7 +344,7 @@ export async function getMemoryWikiPage(params: {
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldSearchSharedMemory(params.config, params.appConfig)) {
|
||||
if (!shouldSearchSharedMemory(effectiveConfig, params.appConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import type { AnyAgentTool, OpenClawConfig } from "../api.js";
|
||||
import { applyMemoryWikiMutation, normalizeMemoryWikiMutationInput } from "./apply.js";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import {
|
||||
WIKI_SEARCH_BACKENDS,
|
||||
WIKI_SEARCH_CORPORA,
|
||||
type ResolvedMemoryWikiConfig,
|
||||
} from "./config.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import { getMemoryWikiPage, searchMemoryWiki } from "./query.js";
|
||||
import { syncMemoryWikiImportedSources } from "./source-sync.js";
|
||||
@@ -9,10 +13,16 @@ import { renderMemoryWikiStatus, resolveMemoryWikiStatus } from "./status.js";
|
||||
|
||||
const WikiStatusSchema = Type.Object({}, { additionalProperties: false });
|
||||
const WikiLintSchema = Type.Object({}, { additionalProperties: false });
|
||||
const WikiSearchBackendSchema = Type.Union(
|
||||
WIKI_SEARCH_BACKENDS.map((value) => Type.Literal(value)),
|
||||
);
|
||||
const WikiSearchCorpusSchema = Type.Union(WIKI_SEARCH_CORPORA.map((value) => Type.Literal(value)));
|
||||
const WikiSearchSchema = Type.Object(
|
||||
{
|
||||
query: Type.String({ minLength: 1 }),
|
||||
maxResults: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
backend: Type.Optional(WikiSearchBackendSchema),
|
||||
corpus: Type.Optional(WikiSearchCorpusSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
@@ -21,6 +31,8 @@ const WikiGetSchema = Type.Object(
|
||||
lookup: Type.String({ minLength: 1 }),
|
||||
fromLine: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
lineCount: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
backend: Type.Optional(WikiSearchBackendSchema),
|
||||
corpus: Type.Optional(WikiSearchCorpusSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
@@ -78,13 +90,20 @@ export function createWikiSearchTool(
|
||||
"Search wiki pages and, when shared search is enabled, the active memory corpus by title, path, id, or body text.",
|
||||
parameters: WikiSearchSchema,
|
||||
execute: async (_toolCallId, rawParams) => {
|
||||
const params = rawParams as { query: string; maxResults?: number };
|
||||
const params = rawParams as {
|
||||
query: string;
|
||||
maxResults?: number;
|
||||
backend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
corpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
};
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
const results = await searchMemoryWiki({
|
||||
config,
|
||||
appConfig,
|
||||
query: params.query,
|
||||
maxResults: params.maxResults,
|
||||
...(params.backend ? { searchBackend: params.backend } : {}),
|
||||
...(params.corpus ? { searchCorpus: params.corpus } : {}),
|
||||
});
|
||||
const text =
|
||||
results.length === 0
|
||||
@@ -182,7 +201,13 @@ export function createWikiGetTool(
|
||||
"Read a wiki page by id or relative path, or fall back to the active memory corpus when shared search is enabled.",
|
||||
parameters: WikiGetSchema,
|
||||
execute: async (_toolCallId, rawParams) => {
|
||||
const params = rawParams as { lookup: string; fromLine?: number; lineCount?: number };
|
||||
const params = rawParams as {
|
||||
lookup: string;
|
||||
fromLine?: number;
|
||||
lineCount?: number;
|
||||
backend?: ResolvedMemoryWikiConfig["search"]["backend"];
|
||||
corpus?: ResolvedMemoryWikiConfig["search"]["corpus"];
|
||||
};
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
const result = await getMemoryWikiPage({
|
||||
config,
|
||||
@@ -190,6 +215,8 @@ export function createWikiGetTool(
|
||||
lookup: params.lookup,
|
||||
fromLine: params.fromLine,
|
||||
lineCount: params.lineCount,
|
||||
...(params.backend ? { searchBackend: params.backend } : {}),
|
||||
...(params.corpus ? { searchCorpus: params.corpus } : {}),
|
||||
});
|
||||
if (!result) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user