From 186b8e44dce274161433cfebc692fee82720c1ae Mon Sep 17 00:00:00 2001 From: Shakker Date: Fri, 1 May 2026 18:51:21 +0100 Subject: [PATCH] perf: reuse run auth store for media tool availability --- CHANGELOG.md | 1 + src/agents/openclaw-tools.ts | 8 +++++ src/agents/pi-embedded-runner/run.ts | 1 + src/agents/pi-embedded-runner/run/attempt.ts | 1 + src/agents/pi-embedded-runner/run/types.ts | 3 ++ src/agents/pi-tools.ts | 4 +++ src/agents/tools/image-generate-tool.ts | 7 ++++ src/agents/tools/image-tool.ts | 5 +++ src/agents/tools/media-tool-shared.test.ts | 19 +++++++++++ src/agents/tools/media-tool-shared.ts | 30 +++++++++++++++-- src/agents/tools/model-config.helpers.ts | 17 ++++++++-- src/agents/tools/music-generate-tool.ts | 6 ++++ src/agents/tools/pdf-tool.model-config.ts | 34 +++++++++++++++++--- src/agents/tools/pdf-tool.ts | 8 ++++- src/agents/tools/video-generate-tool.ts | 6 ++++ 15 files changed, 140 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eab4056f29c..b549d80d5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -159,6 +159,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Agents/tools: reuse the auth profile store already loaded for the active run when deciding media and generation tool availability, avoiding repeated provider-auth runtime discovery during reply startup. Thanks @shakkernerd. - Agents/tools: keep image, video, and music generation tool registration on manifest/auth control-plane checks instead of loading runtime provider registries during reply startup, reducing live-path tool-prep blocking while leaving provider runtime resolution for execution and list actions. Thanks @shakkernerd. - fix: block workspace CLOUDSDK_PYTHON override and always set trusted interpreter for gcloud. (#74492) Thanks @pgondhi987. - Providers/Z.AI: move the bundled GLM catalog and auth env metadata into the plugin manifest, so `models list --all --provider zai` shows the full known catalog without duplicated runtime seed data. Thanks @shakkernerd. diff --git a/src/agents/openclaw-tools.ts b/src/agents/openclaw-tools.ts index 76448003a04..2aac92df86b 100644 --- a/src/agents/openclaw-tools.ts +++ b/src/agents/openclaw-tools.ts @@ -5,6 +5,7 @@ import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime.js"; import { normalizeDeliveryContext } from "../utils/delivery-context.js"; import type { GatewayMessageChannel } from "../utils/message-channel.js"; import { resolveAgentWorkspaceDir, resolveSessionAgentIds } from "./agent-scope.js"; +import type { AuthProfileStore } from "./auth-profiles/types.js"; import { resolveOpenClawPluginToolsForOptions } from "./openclaw-plugin-tools.js"; import { applyNodesToolWorkspaceGuard } from "./openclaw-tools.nodes-workspace-guard.js"; import { @@ -104,6 +105,8 @@ export function createOpenClawTools( recordToolPrepStage?: (name: string) => void; /** Trusted sender id from inbound context (not tool args). */ requesterSenderId?: string | null; + /** Auth profiles already loaded for this run; used for prompt-time tool availability. */ + authProfileStore?: AuthProfileStore; /** Whether the requesting sender is an owner. */ senderIsOwner?: boolean; /** Ephemeral session UUID — regenerated on /new and /reset. */ @@ -153,6 +156,7 @@ export function createOpenClawTools( ? createImageTool({ config: options?.config, agentDir: options.agentDir, + authProfileStore: options?.authProfileStore, workspaceDir, sandbox, fsPolicy: options?.fsPolicy, @@ -163,6 +167,7 @@ export function createOpenClawTools( const imageGenerateTool = createImageGenerateTool({ config: options?.config, agentDir: options?.agentDir, + authProfileStore: options?.authProfileStore, workspaceDir, sandbox, fsPolicy: options?.fsPolicy, @@ -171,6 +176,7 @@ export function createOpenClawTools( const videoGenerateTool = createVideoGenerateTool({ config: options?.config, agentDir: options?.agentDir, + authProfileStore: options?.authProfileStore, agentSessionKey: options?.agentSessionKey, requesterOrigin: deliveryContext ?? undefined, workspaceDir, @@ -181,6 +187,7 @@ export function createOpenClawTools( const musicGenerateTool = createMusicGenerateTool({ config: options?.config, agentDir: options?.agentDir, + authProfileStore: options?.authProfileStore, agentSessionKey: options?.agentSessionKey, requesterOrigin: deliveryContext ?? undefined, workspaceDir, @@ -192,6 +199,7 @@ export function createOpenClawTools( ? createPdfTool({ config: options?.config, agentDir: options.agentDir, + authProfileStore: options?.authProfileStore, workspaceDir, sandbox, fsPolicy: options?.fsPolicy, diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index cc9dff465f2..b1c8a279c72 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -1078,6 +1078,7 @@ export async function runEmbeddedPiAgent( authProfileIdSource: lockedProfileId ? "user" : "auto", initialReplayState: accumulatedReplayState, authStorage, + authProfileStore: authStore, modelRegistry, agentId: workspaceResolution.agentId, legacyBeforeAgentStartResult, diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 79a77320507..7f56c0b12c9 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -918,6 +918,7 @@ export async function runEmbeddedAttempt( params.requireExplicitMessageTarget ?? isSubagentSessionKey(params.sessionKey), disableMessageTool: params.disableMessageTool, forceMessageTool: params.forceMessageTool, + authProfileStore: params.authProfileStore, recordToolPrepStage: (name) => corePluginToolStages.mark(name), onYield: (message) => { yieldDetected = true; diff --git a/src/agents/pi-embedded-runner/run/types.ts b/src/agents/pi-embedded-runner/run/types.ts index 9f6e80bb530..c69dd499ebd 100644 --- a/src/agents/pi-embedded-runner/run/types.ts +++ b/src/agents/pi-embedded-runner/run/types.ts @@ -7,6 +7,7 @@ import type { SessionSystemPromptReport } from "../../../config/sessions/types.j import type { ContextEngine, ContextEnginePromptCacheInfo } from "../../../context-engine/types.js"; import type { DiagnosticTraceContext } from "../../../infra/diagnostic-trace-context.js"; import type { PluginHookBeforeAgentStartResult } from "../../../plugins/hook-before-agent-start.types.js"; +import type { AuthProfileStore } from "../../auth-profiles/types.js"; import type { MessagingToolSend } from "../../pi-embedded-messaging.types.js"; import type { AgentRuntimePlan } from "../../runtime-plan/types.js"; import type { ToolErrorSummary } from "../../tool-error-summary.js"; @@ -41,6 +42,8 @@ export type EmbeddedRunAttemptParams = EmbeddedRunAttemptBase & { runtimePlan?: AgentRuntimePlan; model: Model; authStorage: AuthStorage; + /** Auth profile store already resolved during startup for this attempt. */ + authProfileStore: AuthProfileStore; modelRegistry: ModelRegistry; thinkLevel: ThinkLevel; legacyBeforeAgentStartResult?: PluginHookBeforeAgentStartResult; diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index c77f86ae2f6..0369a453944 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -3,6 +3,7 @@ import { HEARTBEAT_RESPONSE_TOOL_NAME } from "../auto-reply/heartbeat-tool-respo import type { ModelCompatConfig } from "../config/types.models.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { ToolLoopDetectionConfig } from "../config/types.tools.js"; +import type { AuthProfileStore } from "./auth-profiles/types.js"; import type { DiagnosticTraceContext } from "../infra/diagnostic-trace-context.js"; import { resolveMergedSafeBinProfileFixtures } from "../infra/exec-safe-bin-runtime-policy.js"; import { logWarn } from "../logger.js"; @@ -362,6 +363,8 @@ export function createOpenClawCodingTools(options?: { * Keep this narrowly scoped; it is not a replacement for sender ownership. */ ownerOnlyToolAllowlist?: string[]; + /** Auth profiles already loaded for this run; used for prompt-time tool availability. */ + authProfileStore?: AuthProfileStore; /** Callback invoked when sessions_yield tool is called. */ onYield?: (message: string) => Promise | void; }): AnyAgentTool[] { @@ -711,6 +714,7 @@ export function createOpenClawCodingTools(options?: { ...(cronSelfRemoveOnlyJobId ? { cronSelfRemoveOnlyJobId } : {}), requesterAgentIdOverride: agentId, requesterSenderId: options?.senderId, + authProfileStore: options?.authProfileStore, senderIsOwner: options?.senderIsOwner, sessionId: options?.sessionId, onYield: options?.onYield, diff --git a/src/agents/tools/image-generate-tool.ts b/src/agents/tools/image-generate-tool.ts index d4d552c5742..8ac453b349e 100644 --- a/src/agents/tools/image-generate-tool.ts +++ b/src/agents/tools/image-generate-tool.ts @@ -33,6 +33,7 @@ import { saveMediaBuffer } from "../../media/store.js"; import { loadWebMedia } from "../../media/web-media.js"; import { getProviderEnvVars } from "../../secrets/provider-env-vars.js"; import { resolveUserPath } from "../../utils.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { optionalStringEnum } from "../schema/string-enum.js"; import { ToolInputError, readNumberParam, readStringParam } from "./common.js"; import { decodeDataUrl } from "./image-tool.helpers.js"; @@ -195,10 +196,12 @@ function formatImageGenerationAuthHint(provider: { export function resolveImageGenerationModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; }): ToolModelConfig | null { return resolveCapabilityModelConfigForTool({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, modelConfig: params.cfg?.agents?.defaults?.imageGenerationModel, providers: listRuntimeImageGenerationProviders({ config: params.cfg }), }); @@ -563,6 +566,7 @@ async function inferResolutionFromInputImages( export function createImageGenerateTool(options?: { config?: OpenClawConfig; agentDir?: string; + authProfileStore?: AuthProfileStore; workspaceDir?: string; sandbox?: ImageGenerateSandboxConfig; fsPolicy?: ToolFsPolicy; @@ -572,6 +576,7 @@ export function createImageGenerateTool(options?: { !hasGenerationToolAvailability({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, modelConfig: cfg.agents?.defaults?.imageGenerationModel, providerKey: "imageGenerationProviders", }) @@ -610,6 +615,7 @@ export function createImageGenerateTool(options?: { provider, cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, }), authEnvVars: getImageGenerationProviderAuthEnvVars(provider.id), capabilities: provider.capabilities, @@ -661,6 +667,7 @@ export function createImageGenerateTool(options?: { const imageGenerationModelConfig = resolveImageGenerationModelConfigForTool({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, }); if (!imageGenerationModelConfig) { throw new ToolInputError("No image-generation model configured."); diff --git a/src/agents/tools/image-tool.ts b/src/agents/tools/image-tool.ts index 8699c5a0e69..ba49ace7862 100644 --- a/src/agents/tools/image-tool.ts +++ b/src/agents/tools/image-tool.ts @@ -23,6 +23,7 @@ import { type MediaUnderstandingProvider, } from "../../plugin-sdk/media-understanding.js"; import { resolveUserPath } from "../../utils.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { isMinimaxVlmProvider } from "../minimax-vlm.js"; import { coerceImageAssistantText, @@ -117,6 +118,7 @@ function resolveImageToolMaxTokens(modelMaxTokens: number | undefined, requested export function resolveImageModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir: string; + authStore?: AuthProfileStore; }): ImageModelConfig | null { // Note: We intentionally do NOT gate based on primarySupportsImages here. // Even when the primary model supports images, we keep the tool available @@ -171,6 +173,7 @@ export function resolveImageModelConfigForTool(params: { return buildToolModelConfigFromCandidates({ explicit, agentDir: params.agentDir, + authStore: params.authStore, candidates: [...primaryCandidates, ...autoCandidates], }); } @@ -366,6 +369,7 @@ async function runImagePrompt(params: { export function createImageTool(options?: { config?: OpenClawConfig; agentDir?: string; + authProfileStore?: AuthProfileStore; workspaceDir?: string; sandbox?: ImageSandboxConfig; fsPolicy?: ToolFsPolicy; @@ -383,6 +387,7 @@ export function createImageTool(options?: { const imageModelConfig = resolveImageModelConfigForTool({ cfg: options?.config, agentDir, + authStore: options?.authProfileStore, }); if (!imageModelConfig) { return null; diff --git a/src/agents/tools/media-tool-shared.test.ts b/src/agents/tools/media-tool-shared.test.ts index 6297eee9048..a88427f83dd 100644 --- a/src/agents/tools/media-tool-shared.test.ts +++ b/src/agents/tools/media-tool-shared.test.ts @@ -146,4 +146,23 @@ describe("hasGenerationToolAvailability", () => { ).toBe(true); expect(loadProviders).not.toHaveBeenCalled(); }); + + it("checks configured runtime providers against the supplied auth store", () => { + expect( + hasGenerationToolAvailability({ + providerKey: "imageGenerationProviders", + authStore: { + version: 1, + profiles: { + "local-image:default": { + provider: "local-image", + type: "api_key", + key: "test", + }, + }, + }, + providers: [{ id: "local-image", defaultModel: "workflow" }], + }), + ).toBe(true); + }); }); diff --git a/src/agents/tools/media-tool-shared.ts b/src/agents/tools/media-tool-shared.ts index 9fa414e0432..2abf4c5a31d 100644 --- a/src/agents/tools/media-tool-shared.ts +++ b/src/agents/tools/media-tool-shared.ts @@ -9,6 +9,7 @@ import { normalizeOptionalLowercaseString, normalizeOptionalString, } from "../../shared/string-coerce.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { normalizeModelRef } from "../model-selection.js"; import { normalizeProviderId } from "../provider-id.js"; import { @@ -155,6 +156,7 @@ export function isCapabilityProviderConfigured(par providerId?: string; cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; }): boolean { const provider = params.provider ?? @@ -164,7 +166,11 @@ export function isCapabilityProviderConfigured(par }); if (!provider) { return params.providerId - ? hasAuthForProvider({ provider: params.providerId, agentDir: params.agentDir }) + ? hasAuthForProvider({ + provider: params.providerId, + agentDir: params.agentDir, + authStore: params.authStore, + }) : false; } if (provider.isConfigured) { @@ -173,7 +179,11 @@ export function isCapabilityProviderConfigured(par agentDir: params.agentDir, }); } - return hasAuthForProvider({ provider: provider.id, agentDir: params.agentDir }); + return hasAuthForProvider({ + provider: provider.id, + agentDir: params.agentDir, + authStore: params.authStore, + }); } export function resolveSelectedCapabilityProvider(params: { @@ -196,6 +206,7 @@ export function resolveSelectedCapabilityProvider( export function resolveCapabilityModelCandidatesForTool(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; providers: CapabilityProvider[]; }): string[] { const providerDefaults = new Map(); @@ -211,6 +222,7 @@ export function resolveCapabilityModelCandidatesForTool(params: { provider, cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, }) ) { continue; @@ -252,6 +264,7 @@ export function resolveCapabilityModelCandidatesForTool(params: { export function resolveCapabilityModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; modelConfig?: AgentModelConfig; providers: CapabilityProvider[]; }): ToolModelConfig | null { @@ -262,9 +275,11 @@ export function resolveCapabilityModelConfigForTool(params: { return buildToolModelConfigFromCandidates({ explicit, agentDir: params.agentDir, + authStore: params.authStore, candidates: resolveCapabilityModelCandidatesForTool({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, providers: params.providers, }), isProviderConfigured: (providerId) => @@ -273,6 +288,7 @@ export function resolveCapabilityModelConfigForTool(params: { providerId, cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, }), }); } @@ -280,6 +296,7 @@ export function resolveCapabilityModelConfigForTool(params: { export function hasGenerationToolAvailability(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; modelConfig?: AgentModelConfig; providers?: CapabilityProvider[] | (() => CapabilityProvider[]); providerKey: GenerationCapabilityProviderKey; @@ -295,13 +312,20 @@ export function hasGenerationToolAvailability(params: { provider, cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, }), ); } return resolveBundledCapabilityProviderIds({ key: params.providerKey, cfg: params.cfg, - }).some((providerId) => hasAuthForProvider({ provider: providerId, agentDir: params.agentDir })); + }).some((providerId) => + hasAuthForProvider({ + provider: providerId, + agentDir: params.agentDir, + authStore: params.authStore, + }), + ); } function formatQuotedList(values: readonly string[]): string { diff --git a/src/agents/tools/model-config.helpers.ts b/src/agents/tools/model-config.helpers.ts index 5b963bfa76d..2ed99af6c94 100644 --- a/src/agents/tools/model-config.helpers.ts +++ b/src/agents/tools/model-config.helpers.ts @@ -11,6 +11,7 @@ import { hasAnyAuthProfileStoreSource, listProfilesForProvider, } from "../auth-profiles.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js"; import { resolveEnvApiKey } from "../model-auth.js"; import { resolveConfiguredModelRef } from "../model-selection.js"; @@ -35,10 +36,17 @@ export function resolveDefaultModelRef(cfg?: OpenClawConfig): { provider: string return { provider: DEFAULT_PROVIDER, model: DEFAULT_MODEL }; } -export function hasAuthForProvider(params: { provider: string; agentDir?: string }): boolean { +export function hasAuthForProvider(params: { + provider: string; + agentDir?: string; + authStore?: AuthProfileStore; +}): boolean { if (resolveEnvApiKey(params.provider)?.apiKey) { return true; } + if (params.authStore) { + return listProfilesForProvider(params.authStore, params.provider).length > 0; + } const agentDir = params.agentDir?.trim(); if (!agentDir) { return false; @@ -66,6 +74,7 @@ export function coerceToolModelConfig(model?: AgentModelConfig): ToolModelConfig export function buildToolModelConfigFromCandidates(params: { explicit: ToolModelConfig; agentDir?: string; + authStore?: AuthProfileStore; candidates: Array; isProviderConfigured?: (provider: string) => boolean; }): ToolModelConfig | null { @@ -82,7 +91,11 @@ export function buildToolModelConfigFromCandidates(params: { const provider = trimmed.slice(0, trimmed.indexOf("/")).trim(); const providerConfigured = params.isProviderConfigured?.(provider) ?? - hasAuthForProvider({ provider, agentDir: params.agentDir }); + hasAuthForProvider({ + provider, + agentDir: params.agentDir, + authStore: params.authStore, + }); if (!provider || !providerConfigured) { continue; } diff --git a/src/agents/tools/music-generate-tool.ts b/src/agents/tools/music-generate-tool.ts index 603f90f250e..e05eab9dedb 100644 --- a/src/agents/tools/music-generate-tool.ts +++ b/src/agents/tools/music-generate-tool.ts @@ -26,6 +26,7 @@ import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js" import { resolveUserPath } from "../../utils.js"; import type { DeliveryContext } from "../../utils/delivery-context.js"; import { buildTimeoutAbortSignal } from "../../utils/fetch-timeout.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { ToolInputError, readNumberParam, readStringParam } from "./common.js"; import { decodeDataUrl } from "./image-tool.helpers.js"; import { withMediaGenerationTaskKeepalive } from "./media-generate-background-shared.js"; @@ -131,10 +132,12 @@ const MusicGenerateToolSchema = Type.Object({ export function resolveMusicGenerationModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; }): ToolModelConfig | null { return resolveCapabilityModelConfigForTool({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, modelConfig: params.cfg?.agents?.defaults?.musicGenerationModel, providers: listRuntimeMusicGenerationProviders({ config: params.cfg }), }); @@ -488,6 +491,7 @@ async function executeMusicGenerationJob(params: { export function createMusicGenerateTool(options?: { config?: OpenClawConfig; agentDir?: string; + authProfileStore?: AuthProfileStore; agentSessionKey?: string; requesterOrigin?: DeliveryContext; workspaceDir?: string; @@ -500,6 +504,7 @@ export function createMusicGenerateTool(options?: { !hasGenerationToolAvailability({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, modelConfig: cfg.agents?.defaults?.musicGenerationModel, providerKey: "musicGenerationProviders", }) @@ -539,6 +544,7 @@ export function createMusicGenerateTool(options?: { const musicGenerationModelConfig = resolveMusicGenerationModelConfigForTool({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, }); if (!musicGenerationModelConfig) { throw new ToolInputError("No music-generation model configured."); diff --git a/src/agents/tools/pdf-tool.model-config.ts b/src/agents/tools/pdf-tool.model-config.ts index 272301d6a3a..398031f55d2 100644 --- a/src/agents/tools/pdf-tool.model-config.ts +++ b/src/agents/tools/pdf-tool.model-config.ts @@ -4,6 +4,7 @@ import { resolveAutoMediaKeyProviders, resolveDefaultMediaModel, } from "../../media-understanding/defaults.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { coerceImageModelConfig, type ImageModelConfig, @@ -16,11 +17,18 @@ import { coercePdfModelConfig } from "./pdf-tool.helpers.js"; function resolveImageCandidateRefs(params: { cfg?: OpenClawConfig; agentDir: string; + authStore?: AuthProfileStore; filter?: (providerId: string) => boolean; }): string[] { return resolveAutoMediaKeyProviders({ capability: "image", cfg: params.cfg }) .filter((providerId) => !params.filter || params.filter(providerId)) - .filter((providerId) => hasAuthForProvider({ provider: providerId, agentDir: params.agentDir })) + .filter((providerId) => + hasAuthForProvider({ + provider: providerId, + agentDir: params.agentDir, + authStore: params.authStore, + }), + ) .map((providerId) => { const modelId = resolveProviderVisionModelFromConfig({ @@ -40,6 +48,7 @@ function resolveImageCandidateRefs(params: { export function resolvePdfModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir: string; + authStore?: AuthProfileStore; }): ImageModelConfig | null { const explicitPdf = coercePdfModelConfig(params.cfg); if (explicitPdf.primary?.trim() || (explicitPdf.fallbacks?.length ?? 0) > 0) { @@ -58,7 +67,11 @@ export function resolvePdfModelConfigForTool(params: { } const primary = resolveDefaultModelRef(params.cfg); - const googleOk = hasAuthForProvider({ provider: "google", agentDir: params.agentDir }); + const googleOk = hasAuthForProvider({ + provider: "google", + agentDir: params.agentDir, + authStore: params.authStore, + }); const fallbacks: string[] = []; const addFallback = (ref: string) => { @@ -70,7 +83,11 @@ export function resolvePdfModelConfigForTool(params: { let preferred: string | null = null; - const providerOk = hasAuthForProvider({ provider: primary.provider, agentDir: params.agentDir }); + const providerOk = hasAuthForProvider({ + provider: primary.provider, + agentDir: params.agentDir, + authStore: params.authStore, + }); const providerVision = resolveProviderVisionModelFromConfig({ cfg: params.cfg, provider: primary.provider, @@ -89,17 +106,26 @@ export function resolvePdfModelConfigForTool(params: { const nativePdfCandidates = resolveImageCandidateRefs({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, filter: (providerId) => providerSupportsNativePdfDocument({ cfg: params.cfg, providerId }), }); const genericImageCandidates = resolveImageCandidateRefs({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, }); if (params.cfg?.models?.providers && typeof params.cfg.models.providers === "object") { for (const [providerKey, providerCfg] of Object.entries(params.cfg.models.providers)) { const providerId = providerKey.trim(); - if (!providerId || !hasAuthForProvider({ provider: providerId, agentDir: params.agentDir })) { + if ( + !providerId || + !hasAuthForProvider({ + provider: providerId, + agentDir: params.agentDir, + authStore: params.authStore, + }) + ) { continue; } const models = providerCfg?.models ?? []; diff --git a/src/agents/tools/pdf-tool.ts b/src/agents/tools/pdf-tool.ts index 88461446103..261cb32773a 100644 --- a/src/agents/tools/pdf-tool.ts +++ b/src/agents/tools/pdf-tool.ts @@ -12,6 +12,7 @@ import { normalizeOptionalString, } from "../../shared/string-coerce.js"; import { resolveUserPath } from "../../utils.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { type ImageModelConfig } from "./image-tool.helpers.js"; import { applyImageModelConfigDefaults, @@ -244,6 +245,7 @@ async function runPdfPrompt(params: { export function createPdfTool(options?: { config?: OpenClawConfig; agentDir?: string; + authProfileStore?: AuthProfileStore; workspaceDir?: string; sandbox?: PdfSandboxConfig; fsPolicy?: ToolFsPolicy; @@ -257,7 +259,11 @@ export function createPdfTool(options?: { return null; } - const pdfModelConfig = resolvePdfModelConfigForTool({ cfg: options?.config, agentDir }); + const pdfModelConfig = resolvePdfModelConfigForTool({ + cfg: options?.config, + agentDir, + authStore: options?.authProfileStore, + }); if (!pdfModelConfig) { return null; } diff --git a/src/agents/tools/video-generate-tool.ts b/src/agents/tools/video-generate-tool.ts index f59f8558d8e..9d9612311ec 100644 --- a/src/agents/tools/video-generate-tool.ts +++ b/src/agents/tools/video-generate-tool.ts @@ -29,6 +29,7 @@ import type { VideoGenerationResolution, VideoGenerationSourceAsset, } from "../../video-generation/types.js"; +import type { AuthProfileStore } from "../auth-profiles/types.js"; import { ToolInputError, readNumberParam, readStringParam } from "./common.js"; import { decodeDataUrl } from "./image-tool.helpers.js"; import { withMediaGenerationTaskKeepalive } from "./media-generate-background-shared.js"; @@ -225,10 +226,12 @@ const VideoGenerateToolSchema = Type.Object({ export function resolveVideoGenerationModelConfigForTool(params: { cfg?: OpenClawConfig; agentDir?: string; + authStore?: AuthProfileStore; }): ToolModelConfig | null { return resolveCapabilityModelConfigForTool({ cfg: params.cfg, agentDir: params.agentDir, + authStore: params.authStore, modelConfig: params.cfg?.agents?.defaults?.videoGenerationModel, providers: listRuntimeVideoGenerationProviders({ config: params.cfg }), }); @@ -795,6 +798,7 @@ async function executeVideoGenerationJob(params: { export function createVideoGenerateTool(options?: { config?: OpenClawConfig; agentDir?: string; + authProfileStore?: AuthProfileStore; agentSessionKey?: string; requesterOrigin?: DeliveryContext; workspaceDir?: string; @@ -807,6 +811,7 @@ export function createVideoGenerateTool(options?: { !hasGenerationToolAvailability({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, modelConfig: cfg.agents?.defaults?.videoGenerationModel, providerKey: "videoGenerationProviders", }) @@ -846,6 +851,7 @@ export function createVideoGenerateTool(options?: { const videoGenerationModelConfig = resolveVideoGenerationModelConfigForTool({ cfg, agentDir: options?.agentDir, + authStore: options?.authProfileStore, }); if (!videoGenerationModelConfig) { throw new ToolInputError("No video-generation model configured.");