mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
refactor: dedupe provider and channel string helpers
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
||||
import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeAllowFrom } from "./bot-access.js";
|
||||
import { resolveLineGroupConfigEntry, resolveLineGroupHistoryKey } from "./group-keys.js";
|
||||
import { resolveLineGroupConfigEntry } from "./group-keys.js";
|
||||
import type { LineGroupConfig, ResolvedLineAccount } from "./types.js";
|
||||
|
||||
type EventSource = webhook.Source | undefined;
|
||||
@@ -77,10 +77,9 @@ function buildPeerId(source: EventSource): string {
|
||||
if (!source) {
|
||||
return "unknown";
|
||||
}
|
||||
const groupKey = resolveLineGroupHistoryKey({
|
||||
groupId: source.type === "group" ? source.groupId : undefined,
|
||||
roomId: source.type === "room" ? source.roomId : undefined,
|
||||
});
|
||||
const groupKey =
|
||||
normalizeOptionalString(source.type === "group" ? source.groupId : undefined) ??
|
||||
normalizeOptionalString(source.type === "room" ? source.roomId : undefined);
|
||||
if (groupKey) {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
resolveExactLineGroupConfigKey,
|
||||
resolveLineGroupConfigEntry,
|
||||
resolveLineGroupHistoryKey,
|
||||
resolveLineGroupLookupIds,
|
||||
resolveLineGroupsConfig,
|
||||
} from "./group-keys.js";
|
||||
@@ -39,14 +38,6 @@ describe("resolveLineGroupConfigEntry", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveLineGroupHistoryKey", () => {
|
||||
it("uses the raw group or room id as the shared LINE peer key", () => {
|
||||
expect(resolveLineGroupHistoryKey({ groupId: "g1" })).toBe("g1");
|
||||
expect(resolveLineGroupHistoryKey({ roomId: "r1" })).toBe("r1");
|
||||
expect(resolveLineGroupHistoryKey({})).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("account-scoped LINE groups", () => {
|
||||
it("resolves the effective account-scoped groups map", () => {
|
||||
const cfg = {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/account-resolution";
|
||||
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { LineConfig, LineGroupConfig } from "./types.js";
|
||||
|
||||
export function resolveLineGroupLookupIds(groupId?: string | null): string[] {
|
||||
@@ -64,10 +63,3 @@ export function resolveExactLineGroupConfigKey(params: {
|
||||
Object.hasOwn(groups, candidate),
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveLineGroupHistoryKey(params: {
|
||||
groupId?: string | null;
|
||||
roomId?: string | null;
|
||||
}): string | undefined {
|
||||
return normalizeOptionalString(params.groupId) ?? normalizeOptionalString(params.roomId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { OpenClawConfig, HumanDelayConfig, IdentityConfig } from "../config/config.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import { resolveAgentConfig } from "./agent-scope.js";
|
||||
|
||||
const DEFAULT_ACK_REACTION = "👀";
|
||||
@@ -57,11 +56,6 @@ export function resolveIdentityNamePrefix(
|
||||
return `[${name}]`;
|
||||
}
|
||||
|
||||
/** Returns just the identity name (without brackets) for template context. */
|
||||
export function resolveIdentityName(cfg: OpenClawConfig, agentId: string): string | undefined {
|
||||
return normalizeOptionalString(resolveAgentIdentity(cfg, agentId)?.name);
|
||||
}
|
||||
|
||||
export function resolveMessagePrefix(
|
||||
cfg: OpenClawConfig,
|
||||
agentId: string,
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
ACP_SESSIONS_USAGE,
|
||||
formatAcpCapabilitiesText,
|
||||
resolveAcpInstallCommandHint,
|
||||
resolveConfiguredAcpBackendId,
|
||||
stopWithText,
|
||||
} from "./shared.js";
|
||||
import { resolveBoundAcpThreadSessionKey } from "./targets.js";
|
||||
@@ -28,7 +27,7 @@ export async function handleAcpDoctorAction(
|
||||
return stopWithText(`⚠️ ${ACP_DOCTOR_USAGE}`);
|
||||
}
|
||||
|
||||
const backendId = resolveConfiguredAcpBackendId(params.cfg);
|
||||
const backendId = normalizeOptionalString(params.cfg.acp?.backend) ?? "acpx";
|
||||
const installHint = resolveAcpInstallCommandHint(params.cfg);
|
||||
const registeredBackend = getAcpRuntimeBackend(backendId);
|
||||
const managerSnapshot = getAcpSessionManager().getObservabilitySnapshot(params.cfg);
|
||||
@@ -116,7 +115,7 @@ export function handleAcpInstallAction(
|
||||
if (restTokens.length > 0) {
|
||||
return stopWithText(`⚠️ ${ACP_INSTALL_USAGE}`);
|
||||
}
|
||||
const backendId = resolveConfiguredAcpBackendId(params.cfg);
|
||||
const backendId = normalizeOptionalString(params.cfg.acp?.backend) ?? "acpx";
|
||||
const installHint = resolveAcpInstallCommandHint(params.cfg);
|
||||
const lines = [
|
||||
"ACP install:",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../../config/config.js";
|
||||
import { resolveAcpInstallCommandHint, resolveConfiguredAcpBackendId } from "./install-hints.js";
|
||||
import { resolveAcpInstallCommandHint } from "./install-hints.js";
|
||||
|
||||
function withAcpConfig(acp: OpenClawConfig["acp"]): OpenClawConfig {
|
||||
return { acp } as OpenClawConfig;
|
||||
@@ -35,7 +35,6 @@ describe("ACP install hints", () => {
|
||||
|
||||
it("returns generic plugin hint for non-acpx backend", () => {
|
||||
const cfg = withAcpConfig({ backend: "custom-backend" });
|
||||
expect(resolveConfiguredAcpBackendId(cfg)).toBe("custom-backend");
|
||||
expect(resolveAcpInstallCommandHint(cfg)).toContain('ACP backend "custom-backend"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,17 +5,13 @@ import { resolveBundledPluginWorkspaceSourcePath } from "../../../plugins/bundle
|
||||
import { resolveBundledPluginInstallCommandHint } from "../../../plugins/bundled-sources.js";
|
||||
import { normalizeOptionalString } from "../../../shared/string-coerce.js";
|
||||
|
||||
export function resolveConfiguredAcpBackendId(cfg: OpenClawConfig): string {
|
||||
return normalizeOptionalString(cfg.acp?.backend) || "acpx";
|
||||
}
|
||||
|
||||
export function resolveAcpInstallCommandHint(cfg: OpenClawConfig): string {
|
||||
const configured = normalizeOptionalString(cfg.acp?.runtime?.installCommand);
|
||||
if (configured) {
|
||||
return configured;
|
||||
}
|
||||
const workspaceDir = process.cwd();
|
||||
const backendId = resolveConfiguredAcpBackendId(cfg).toLowerCase();
|
||||
const backendId = normalizeOptionalString(cfg.acp?.backend)?.toLowerCase() ?? "acpx";
|
||||
if (backendId === "acpx") {
|
||||
const workspaceLocalPath = resolveBundledPluginWorkspaceSourcePath({
|
||||
rootDir: workspaceDir,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { normalizeAgentId } from "../../../routing/session-key.js";
|
||||
import { normalizeOptionalString } from "../../../shared/string-coerce.js";
|
||||
import type { CommandHandlerResult, HandleCommandsParams } from "../commands-types.js";
|
||||
import { resolveAcpCommandChannel, resolveAcpCommandThreadId } from "./context.js";
|
||||
export { resolveAcpInstallCommandHint, resolveConfiguredAcpBackendId } from "./install-hints.js";
|
||||
export { resolveAcpInstallCommandHint } from "./install-hints.js";
|
||||
|
||||
export const COMMAND = "/acp";
|
||||
export const ACP_SPAWN_USAGE =
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js";
|
||||
import { resolveAgentIdentity, resolveEffectiveMessagesConfig } from "../agents/identity.js";
|
||||
import {
|
||||
extractShortModelName,
|
||||
type ResponsePrefixContext,
|
||||
} from "../auto-reply/reply/response-prefix-template.js";
|
||||
import type { GetReplyOptions } from "../auto-reply/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
|
||||
type ModelSelectionContext = Parameters<NonNullable<GetReplyOptions["onModelSelected"]>>[0];
|
||||
|
||||
@@ -28,7 +29,7 @@ export function createReplyPrefixContext(params: {
|
||||
}): ReplyPrefixContextBundle {
|
||||
const { cfg, agentId } = params;
|
||||
const prefixContext: ResponsePrefixContext = {
|
||||
identityName: resolveIdentityName(cfg, agentId),
|
||||
identityName: normalizeOptionalString(resolveAgentIdentity(cfg, agentId)?.name),
|
||||
};
|
||||
|
||||
const onModelSelected = (ctx: ModelSelectionContext) => {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildCapabilityProviderMaps,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "./provider-registry-shared.js";
|
||||
import { buildCapabilityProviderMaps } from "./provider-registry-shared.js";
|
||||
|
||||
describe("provider registry shared", () => {
|
||||
it("normalizes provider ids case-insensitively", () => {
|
||||
expect(normalizeCapabilityProviderId(" OpenAI ")).toBe("openai");
|
||||
expect(normalizeCapabilityProviderId(" ")).toBeUndefined();
|
||||
const { canonical } = buildCapabilityProviderMaps([{ id: " OpenAI " }, { id: " " }]);
|
||||
expect([...canonical.keys()]).toEqual(["openai"]);
|
||||
});
|
||||
|
||||
it("indexes providers by id and alias", () => {
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
|
||||
export function normalizeCapabilityProviderId(providerId: string | undefined): string | undefined {
|
||||
return normalizeOptionalString(providerId)?.toLowerCase();
|
||||
}
|
||||
|
||||
export function buildCapabilityProviderMaps<T extends { id: string; aliases?: readonly string[] }>(
|
||||
providers: readonly T[],
|
||||
normalizeId: (
|
||||
providerId: string | undefined,
|
||||
) => string | undefined = normalizeCapabilityProviderId,
|
||||
normalizeId: (providerId: string | undefined) => string | undefined = (providerId) =>
|
||||
normalizeOptionalString(providerId)?.toLowerCase(),
|
||||
): {
|
||||
canonical: Map<string, T>;
|
||||
aliases: Map<string, T>;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js";
|
||||
import {
|
||||
buildCapabilityProviderMaps,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "../plugins/provider-registry-shared.js";
|
||||
import { buildCapabilityProviderMaps } from "../plugins/provider-registry-shared.js";
|
||||
import type { RealtimeVoiceProviderPlugin } from "../plugins/types.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import type { RealtimeVoiceProviderId } from "./provider-types.js";
|
||||
|
||||
export function normalizeRealtimeVoiceProviderId(
|
||||
providerId: string | undefined,
|
||||
): RealtimeVoiceProviderId | undefined {
|
||||
return normalizeCapabilityProviderId(providerId);
|
||||
return normalizeOptionalString(providerId)?.toLowerCase();
|
||||
}
|
||||
|
||||
function resolveRealtimeVoiceProviderEntries(cfg?: OpenClawConfig): RealtimeVoiceProviderPlugin[] {
|
||||
|
||||
@@ -523,8 +523,8 @@ function taskRunScopeKey(
|
||||
return [
|
||||
task.runtime,
|
||||
task.scopeKind,
|
||||
normalizeComparableText(task.ownerKey),
|
||||
normalizeComparableText(task.childSessionKey),
|
||||
normalizeOptionalString(task.ownerKey) ?? "",
|
||||
normalizeOptionalString(task.childSessionKey) ?? "",
|
||||
].join("\u0000");
|
||||
}
|
||||
|
||||
@@ -562,9 +562,10 @@ function getPeerTasksForDelivery(task: TaskRecord): TaskRecord[] {
|
||||
(candidate) =>
|
||||
candidate.runtime === task.runtime &&
|
||||
candidate.scopeKind === task.scopeKind &&
|
||||
normalizeComparableText(candidate.ownerKey) === normalizeComparableText(task.ownerKey) &&
|
||||
normalizeComparableText(candidate.childSessionKey) ===
|
||||
normalizeComparableText(task.childSessionKey),
|
||||
(normalizeOptionalString(candidate.ownerKey) ?? "") ===
|
||||
(normalizeOptionalString(task.ownerKey) ?? "") &&
|
||||
(normalizeOptionalString(candidate.childSessionKey) ?? "") ===
|
||||
(normalizeOptionalString(task.childSessionKey) ?? ""),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -583,10 +584,6 @@ function pickPreferredRunIdTask(matches: TaskRecord[]): TaskRecord | undefined {
|
||||
})[0];
|
||||
}
|
||||
|
||||
function normalizeComparableText(value: string | undefined): string {
|
||||
return normalizeOptionalString(value) ?? "";
|
||||
}
|
||||
|
||||
function compareTasksNewestFirst(
|
||||
left: Pick<TaskRecord, "createdAt"> & { insertionIndex?: number },
|
||||
right: Pick<TaskRecord, "createdAt"> & { insertionIndex?: number },
|
||||
@@ -614,18 +611,21 @@ function findExistingTaskForCreate(params: {
|
||||
(task) =>
|
||||
task.runtime === params.runtime &&
|
||||
task.scopeKind === params.scopeKind &&
|
||||
normalizeComparableText(task.ownerKey) === normalizeComparableText(params.ownerKey) &&
|
||||
normalizeComparableText(task.childSessionKey) ===
|
||||
normalizeComparableText(params.childSessionKey) &&
|
||||
normalizeComparableText(task.parentFlowId) ===
|
||||
normalizeComparableText(params.parentFlowId),
|
||||
(normalizeOptionalString(task.ownerKey) ?? "") ===
|
||||
(normalizeOptionalString(params.ownerKey) ?? "") &&
|
||||
(normalizeOptionalString(task.childSessionKey) ?? "") ===
|
||||
(normalizeOptionalString(params.childSessionKey) ?? "") &&
|
||||
(normalizeOptionalString(task.parentFlowId) ?? "") ===
|
||||
(normalizeOptionalString(params.parentFlowId) ?? ""),
|
||||
)
|
||||
: [];
|
||||
const exact = runId
|
||||
? runScopeMatches.find(
|
||||
(task) =>
|
||||
normalizeComparableText(task.label) === normalizeComparableText(params.label) &&
|
||||
normalizeComparableText(task.task) === normalizeComparableText(params.task),
|
||||
(normalizeOptionalString(task.label) ?? "") ===
|
||||
(normalizeOptionalString(params.label) ?? "") &&
|
||||
(normalizeOptionalString(task.task) ?? "") ===
|
||||
(normalizeOptionalString(params.task) ?? ""),
|
||||
)
|
||||
: undefined;
|
||||
if (exact) {
|
||||
@@ -688,11 +688,11 @@ function mergeExistingTaskForCreate(
|
||||
}
|
||||
const nextLabel = params.label?.trim();
|
||||
if (params.preferMetadata) {
|
||||
if (nextLabel && normalizeComparableText(existing.label) !== nextLabel) {
|
||||
if (nextLabel && (normalizeOptionalString(existing.label) ?? "") !== nextLabel) {
|
||||
patch.label = nextLabel;
|
||||
}
|
||||
const nextTask = params.task.trim();
|
||||
if (nextTask && normalizeComparableText(existing.task) !== nextTask) {
|
||||
if (nextTask && (normalizeOptionalString(existing.task) ?? "") !== nextTask) {
|
||||
patch.task = nextTask;
|
||||
}
|
||||
} else if (nextLabel && !existing.label?.trim()) {
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js";
|
||||
import {
|
||||
buildCapabilityProviderMaps,
|
||||
normalizeCapabilityProviderId,
|
||||
} from "../plugins/provider-registry-shared.js";
|
||||
import { buildCapabilityProviderMaps } from "../plugins/provider-registry-shared.js";
|
||||
import type { SpeechProviderPlugin } from "../plugins/types.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import type { SpeechProviderId } from "./provider-types.js";
|
||||
|
||||
export function normalizeSpeechProviderId(
|
||||
providerId: string | undefined,
|
||||
): SpeechProviderId | undefined {
|
||||
return normalizeCapabilityProviderId(providerId);
|
||||
return normalizeOptionalString(providerId)?.toLowerCase();
|
||||
}
|
||||
|
||||
function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {
|
||||
|
||||
Reference in New Issue
Block a user