mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
style: format stale source files
This commit is contained in:
@@ -24,9 +24,9 @@ describe("amazon-bedrock-mantle provider plugin", () => {
|
||||
expect(
|
||||
provider.classifyFailoverReason?.({ errorMessage: "some other error" } as never),
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
provider.classifyFailoverReason?.({ errorMessage: "overloaded_error" } as never),
|
||||
).toBe("overloaded");
|
||||
expect(provider.classifyFailoverReason?.({ errorMessage: "overloaded_error" } as never)).toBe(
|
||||
"overloaded",
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a custom stream only for Mantle Anthropic models", async () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createMantleAnthropicStreamFn } from "./mantle-anthropic.runtime.js";
|
||||
import {
|
||||
mergeImplicitMantleProvider,
|
||||
resolveImplicitMantleProvider,
|
||||
resolveMantleBearerToken,
|
||||
resolveMantleRuntimeBearerToken,
|
||||
} from "./discovery.js";
|
||||
import { createMantleAnthropicStreamFn } from "./mantle-anthropic.runtime.js";
|
||||
|
||||
export function registerBedrockMantlePlugin(api: OpenClawPluginApi): void {
|
||||
const providerId = "amazon-bedrock-mantle";
|
||||
|
||||
@@ -132,7 +132,8 @@ describe("bedrock discovery", () => {
|
||||
type: "SYSTEM_DEFINED",
|
||||
models: [
|
||||
{
|
||||
modelArn: "arn:aws:bedrock:ap-northeast-1::foundation-model/example.unknown-text-v1:0",
|
||||
modelArn:
|
||||
"arn:aws:bedrock:ap-northeast-1::foundation-model/example.unknown-text-v1:0",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -395,9 +395,10 @@ function resolveInferenceProfiles(
|
||||
reasoning: baseModel?.reasoning ?? false,
|
||||
input: baseModel?.input ?? ["text"],
|
||||
cost: baseModel?.cost ?? DEFAULT_COST,
|
||||
contextWindow: baseModel?.contextWindow
|
||||
?? resolveKnownContextWindow(baseModelId ?? profile.inferenceProfileId ?? "")
|
||||
?? defaults.contextWindow,
|
||||
contextWindow:
|
||||
baseModel?.contextWindow ??
|
||||
resolveKnownContextWindow(baseModelId ?? profile.inferenceProfileId ?? "") ??
|
||||
defaults.contextWindow,
|
||||
maxTokens: baseModel?.maxTokens ?? defaults.maxTokens,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import type { PluginRuntime } from "../../src/plugins/runtime/types.js";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
import amazonBedrockPlugin from "./index.js";
|
||||
|
||||
type InferenceProfileResult =
|
||||
| { models?: Array<{ modelArn?: string }> }
|
||||
| Error;
|
||||
type InferenceProfileResult = { models?: Array<{ modelArn?: string }> } | Error;
|
||||
|
||||
const inferenceProfileResults: InferenceProfileResult[] = [];
|
||||
const bedrockClientConfigs: Array<Record<string, unknown>> = [];
|
||||
@@ -376,10 +374,11 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
streamFn: spyStreamFn,
|
||||
} as never);
|
||||
|
||||
const result = wrapped?.(modelDescriptor, { messages: [] } as never, options) as unknown as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const result = wrapped?.(
|
||||
modelDescriptor,
|
||||
{ messages: [] } as never,
|
||||
options,
|
||||
) as unknown as Record<string, unknown>;
|
||||
|
||||
if (typeof result?.onPayload === "function") {
|
||||
await (
|
||||
@@ -393,9 +392,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
await callWrappedStreamWithPayload(
|
||||
@@ -410,7 +407,10 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
expect(system).toHaveLength(2);
|
||||
expect(system[1]).toEqual({ cachePoint: { type: "default" } });
|
||||
|
||||
const messages = payload.messages as Array<{ role: string; content: Array<Record<string, unknown>> }>;
|
||||
const messages = payload.messages as Array<{
|
||||
role: string;
|
||||
content: Array<Record<string, unknown>>;
|
||||
}>;
|
||||
const lastUserContent = messages[0].content;
|
||||
expect(lastUserContent).toHaveLength(2);
|
||||
expect(lastUserContent[1]).toEqual({ cachePoint: { type: "default" } });
|
||||
@@ -420,9 +420,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
await callWrappedStreamWithPayload(
|
||||
@@ -441,9 +439,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
await callWrappedStreamWithPayload(
|
||||
@@ -478,7 +474,10 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const system = payload.system as Array<Record<string, unknown>>;
|
||||
expect(system).toHaveLength(2);
|
||||
|
||||
const messages = payload.messages as Array<{ role: string; content: Array<Record<string, unknown>> }>;
|
||||
const messages = payload.messages as Array<{
|
||||
role: string;
|
||||
content: Array<Record<string, unknown>>;
|
||||
}>;
|
||||
expect(messages[0].content).toHaveLength(2);
|
||||
});
|
||||
|
||||
@@ -486,9 +485,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
// Regular model IDs contain "claude" so pi-ai handles caching natively.
|
||||
@@ -517,9 +514,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const oldClaudeModel = "anthropic.claude-3-opus-20240229-v1:0";
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
// Claude 3 Opus is not in pi-ai's supportsPromptCaching list, but it's
|
||||
@@ -546,9 +541,7 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const payload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
messages: [
|
||||
{ role: "user", content: [{ text: "Hello" }] },
|
||||
],
|
||||
messages: [{ role: "user", content: [{ text: "Hello" }] }],
|
||||
};
|
||||
|
||||
await callWrappedStreamWithPayload(
|
||||
@@ -584,7 +577,10 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
payload,
|
||||
);
|
||||
|
||||
const messages = payload.messages as Array<{ role: string; content: Array<Record<string, unknown>> }>;
|
||||
const messages = payload.messages as Array<{
|
||||
role: string;
|
||||
content: Array<Record<string, unknown>>;
|
||||
}>;
|
||||
// First user message should NOT have a cache point
|
||||
expect(messages[0].content).toHaveLength(1);
|
||||
// Assistant message untouched
|
||||
@@ -661,17 +657,14 @@ describe("amazon-bedrock provider plugin", () => {
|
||||
it("retries opaque profile lookup after a transient failure instead of caching the fallback", async () => {
|
||||
const modelId =
|
||||
"arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/z27qyso459dc";
|
||||
inferenceProfileResults.push(
|
||||
new Error("throttled"),
|
||||
{
|
||||
models: [
|
||||
{
|
||||
modelArn:
|
||||
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-20250514-v1:0",
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
inferenceProfileResults.push(new Error("throttled"), {
|
||||
models: [
|
||||
{
|
||||
modelArn:
|
||||
"arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-20250514-v1:0",
|
||||
},
|
||||
],
|
||||
});
|
||||
const provider = await registerWithConfig(undefined);
|
||||
const firstPayload: Record<string, unknown> = {
|
||||
system: [{ text: "You are helpful." }],
|
||||
|
||||
@@ -96,7 +96,8 @@ function piAiWouldInjectCachePoints(modelId: string): boolean {
|
||||
* System-defined profiles (us., eu., global.) and base model IDs always
|
||||
* contain the model name and are handled by pi-ai natively.
|
||||
*/
|
||||
const BEDROCK_APP_INFERENCE_PROFILE_RE = /^arn:aws(-cn|-us-gov)?:bedrock:.*:application-inference-profile\//i;
|
||||
const BEDROCK_APP_INFERENCE_PROFILE_RE =
|
||||
/^arn:aws(-cn|-us-gov)?:bedrock:.*:application-inference-profile\//i;
|
||||
|
||||
function isBedrockAppInferenceProfile(modelId: string): boolean {
|
||||
return BEDROCK_APP_INFERENCE_PROFILE_RE.test(modelId);
|
||||
@@ -172,9 +173,7 @@ async function resolveAppProfileCacheEligible(
|
||||
const models = resp.models ?? [];
|
||||
const eligible =
|
||||
models.length > 0 &&
|
||||
models.every((m: { modelArn?: string }) =>
|
||||
resolvedModelSupportsCaching(m.modelArn ?? ""),
|
||||
);
|
||||
models.every((m: { modelArn?: string }) => resolvedModelSupportsCaching(m.modelArn ?? ""));
|
||||
appProfileCacheEligibleCache.set(modelId, eligible);
|
||||
return eligible;
|
||||
} catch {
|
||||
@@ -374,9 +373,8 @@ export function registerAmazonBedrockPlugin(api: OpenClawPluginApi): void {
|
||||
// to also teach resolveAnthropicCacheRetentionFamily about opaque profiles
|
||||
// (tracked separately). In practice, users with app inference profiles
|
||||
// want caching enabled, so defaulting to "short" is the safer behavior.
|
||||
const cacheRetention = typeof merged.cacheRetention === "string"
|
||||
? merged.cacheRetention
|
||||
: "short";
|
||||
const cacheRetention =
|
||||
typeof merged.cacheRetention === "string" ? merged.cacheRetention : "short";
|
||||
|
||||
if (heuristicMatch) {
|
||||
// Fast path: ARN heuristic already identified this as Claude.
|
||||
|
||||
@@ -382,7 +382,9 @@ describe("openshell fs bridges", () => {
|
||||
|
||||
const { createOpenShellFsBridge } = await import("./fs-bridge.js");
|
||||
const bridge = createOpenShellFsBridge({ sandbox, backend });
|
||||
const readlinkSpy = vi.spyOn(fs, "readlink").mockRejectedValue(new Error("fd path unavailable"));
|
||||
const readlinkSpy = vi
|
||||
.spyOn(fs, "readlink")
|
||||
.mockRejectedValue(new Error("fd path unavailable"));
|
||||
|
||||
try {
|
||||
await expect(bridge.readFile({ filePath: "subdir/secret.txt" })).resolves.toEqual(
|
||||
@@ -401,43 +403,45 @@ describe("openshell fs bridges", () => {
|
||||
it.skipIf(process.platform === "win32")(
|
||||
"rejects fallback reads when path stats report an unknown device id",
|
||||
async () => {
|
||||
const workspaceDir = await makeTempDir("openclaw-openshell-fs-");
|
||||
const targetPath = path.join(workspaceDir, "subdir", "secret.txt");
|
||||
await fs.mkdir(path.join(workspaceDir, "subdir"), { recursive: true });
|
||||
await fs.writeFile(targetPath, "inside", "utf8");
|
||||
const workspaceDir = await makeTempDir("openclaw-openshell-fs-");
|
||||
const targetPath = path.join(workspaceDir, "subdir", "secret.txt");
|
||||
await fs.mkdir(path.join(workspaceDir, "subdir"), { recursive: true });
|
||||
await fs.writeFile(targetPath, "inside", "utf8");
|
||||
|
||||
const backend = createMirrorBackendMock();
|
||||
const sandbox = createSandboxTestContext({
|
||||
overrides: {
|
||||
backendId: "openshell",
|
||||
workspaceDir,
|
||||
agentWorkspaceDir: workspaceDir,
|
||||
containerWorkdir: "/sandbox",
|
||||
},
|
||||
});
|
||||
const backend = createMirrorBackendMock();
|
||||
const sandbox = createSandboxTestContext({
|
||||
overrides: {
|
||||
backendId: "openshell",
|
||||
workspaceDir,
|
||||
agentWorkspaceDir: workspaceDir,
|
||||
containerWorkdir: "/sandbox",
|
||||
},
|
||||
});
|
||||
|
||||
const { createOpenShellFsBridge } = await import("./fs-bridge.js");
|
||||
const bridge = createOpenShellFsBridge({ sandbox, backend });
|
||||
const readlinkSpy = vi.spyOn(fs, "readlink").mockRejectedValue(new Error("fd path unavailable"));
|
||||
const originalStat = fs.stat.bind(fs);
|
||||
const statSpy = vi.spyOn(fs, "stat").mockImplementation(async (...args) => {
|
||||
const stat = await originalStat(...args);
|
||||
if (args[0] === targetPath) {
|
||||
return cloneStatWithDev(stat, 0);
|
||||
const { createOpenShellFsBridge } = await import("./fs-bridge.js");
|
||||
const bridge = createOpenShellFsBridge({ sandbox, backend });
|
||||
const readlinkSpy = vi
|
||||
.spyOn(fs, "readlink")
|
||||
.mockRejectedValue(new Error("fd path unavailable"));
|
||||
const originalStat = fs.stat.bind(fs);
|
||||
const statSpy = vi.spyOn(fs, "stat").mockImplementation(async (...args) => {
|
||||
const stat = await originalStat(...args);
|
||||
if (args[0] === targetPath) {
|
||||
return cloneStatWithDev(stat, 0);
|
||||
}
|
||||
return stat;
|
||||
});
|
||||
|
||||
try {
|
||||
await expect(bridge.readFile({ filePath: "subdir/secret.txt" })).rejects.toThrow(
|
||||
"Sandbox boundary checks failed",
|
||||
);
|
||||
expect(readlinkSpy).toHaveBeenCalled();
|
||||
expect(statSpy).toHaveBeenCalledWith(targetPath);
|
||||
} finally {
|
||||
statSpy.mockRestore();
|
||||
readlinkSpy.mockRestore();
|
||||
}
|
||||
return stat;
|
||||
});
|
||||
|
||||
try {
|
||||
await expect(bridge.readFile({ filePath: "subdir/secret.txt" })).rejects.toThrow(
|
||||
"Sandbox boundary checks failed",
|
||||
);
|
||||
expect(readlinkSpy).toHaveBeenCalled();
|
||||
expect(statSpy).toHaveBeenCalledWith(targetPath);
|
||||
} finally {
|
||||
statSpy.mockRestore();
|
||||
readlinkSpy.mockRestore();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -474,7 +478,9 @@ describe("openshell fs bridges", () => {
|
||||
}) as unknown as typeof fs.open);
|
||||
// Force the fallback verification path even on Linux so the ancestor-walk
|
||||
// guard is exercised directly.
|
||||
const readlinkSpy = vi.spyOn(fs, "readlink").mockRejectedValue(new Error("fd path unavailable"));
|
||||
const readlinkSpy = vi
|
||||
.spyOn(fs, "readlink")
|
||||
.mockRejectedValue(new Error("fd path unavailable"));
|
||||
|
||||
try {
|
||||
await expect(bridge.readFile({ filePath: "subdir/secret.txt" })).rejects.toThrow(
|
||||
@@ -512,12 +518,13 @@ describe("openshell fs bridges", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { createOpenShellFsBridge, setReadOpenFlagsResolverForTest } = await import(
|
||||
"./fs-bridge.js"
|
||||
);
|
||||
const { createOpenShellFsBridge, setReadOpenFlagsResolverForTest } =
|
||||
await import("./fs-bridge.js");
|
||||
const bridge = createOpenShellFsBridge({ sandbox, backend });
|
||||
// Force the fallback path so the leaf-lstat guard is exercised.
|
||||
const readlinkSpy = vi.spyOn(fs, "readlink").mockRejectedValue(new Error("fd path unavailable"));
|
||||
const readlinkSpy = vi
|
||||
.spyOn(fs, "readlink")
|
||||
.mockRejectedValue(new Error("fd path unavailable"));
|
||||
// Simulate a host that lacks `O_NOFOLLOW` (e.g. Windows) without touching
|
||||
// the non-configurable native `fs.constants` data property. The bridge
|
||||
// exposes a test-only seam for exactly this case.
|
||||
|
||||
@@ -89,12 +89,8 @@ export function resolveAnthropicCacheRetentionFamily(params: {
|
||||
// cacheRetention, honor it — the extension's GetInferenceProfile resolution
|
||||
// handles the actual model detection at runtime.
|
||||
if (
|
||||
BEDROCK_APP_INFERENCE_PROFILE_ARN_RE.test(
|
||||
normalizeLowercaseStringOrEmpty(params.modelId),
|
||||
) &&
|
||||
normalizeLowercaseStringOrEmpty(params.modelId).includes(
|
||||
":application-inference-profile/",
|
||||
)
|
||||
BEDROCK_APP_INFERENCE_PROFILE_ARN_RE.test(normalizeLowercaseStringOrEmpty(params.modelId)) &&
|
||||
normalizeLowercaseStringOrEmpty(params.modelId).includes(":application-inference-profile/")
|
||||
) {
|
||||
return "anthropic-bedrock";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { complete, type Api, type Model } from "@mariozechner/pi-ai";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { prepareProviderRuntimeAuth } from "../plugins/provider-runtime.runtime.js";
|
||||
import { resolveAgentDir, resolveAgentEffectiveModelPrimary } from "./agent-scope.js";
|
||||
import { DEFAULT_PROVIDER } from "./defaults.js";
|
||||
import {
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
resolveModelRefFromString,
|
||||
} from "./model-selection.js";
|
||||
import { resolveModel } from "./pi-embedded-runner/model.js";
|
||||
import { prepareProviderRuntimeAuth } from "../plugins/provider-runtime.runtime.js";
|
||||
|
||||
type SimpleCompletionAuthStorage = {
|
||||
setRuntimeApiKey: (provider: string, apiKey: string) => void;
|
||||
|
||||
Reference in New Issue
Block a user