mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(providers): server-side credential hiding for Azure and Bedrock (#3884)
* fix: allow Bedrock provider to use AWS SDK default credential chain Remove hard requirement for explicit AWS credentials in Bedrock provider. When access key and secret key are not provided, the AWS SDK automatically falls back to its default credential chain (env vars, instance profile, ECS task role, EKS IRSA, SSO). Closes #3694 Signed-off-by: majiayu000 <1835304752@qq.com> * fix: add partial credential guard for Bedrock provider Reject configurations where only one of bedrockAccessKeyId or bedrockSecretKey is provided, preventing silent fallback to the default credential chain with a potentially different identity. Add tests covering all credential configuration scenarios. Signed-off-by: majiayu000 <1835304752@qq.com> * fix: clean up bedrock test lint and dead code Remove unused config parameter and dead _lastConfig assignment from mock factory. Break long mockReturnValue chain to satisfy biome line-length rule. Signed-off-by: majiayu000 <1835304752@qq.com> * fix: address greptile review feedback on PR #3708 Use BedrockRuntimeClientConfig from SDK instead of inline type. Add default return value for prepareToolsWithUsageControl mock. Signed-off-by: majiayu000 <1835304752@qq.com> * feat(providers): server-side credential hiding for Azure and Bedrock * fix(providers): revert Bedrock credential fields to required with original placeholders * fix(blocks): add hideWhenEnvSet to getProviderCredentialSubBlocks for Azure and Bedrock * fix(agent): use getProviderCredentialSubBlocks() instead of duplicating credential subblocks * fix(blocks): consolidate Vertex credential into shared factory with basic/advanced mode * fix(types): resolve pre-existing TypeScript errors across auth, secrets, and copilot * lint * improvement(blocks): make Vertex AI project ID a password field * fix(blocks): preserve vertexCredential subblock ID for backwards compatibility * fix(blocks): follow canonicalParamId pattern correctly for vertex credential subblocks * fix(blocks): keep vertexCredential subblock ID stable to preserve saved workflow state * fix(blocks): add canonicalParamId to vertexCredential basic subblock to complete the swap pair * fix types * more types --------- Signed-off-by: majiayu000 <1835304752@qq.com> Co-authored-by: majiayu000 <1835304752@qq.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
This commit is contained in:
@@ -192,7 +192,7 @@ In the block config (`blocks/blocks/{service}.ts`), add `hideWhenHosted: true` t
|
||||
},
|
||||
```
|
||||
|
||||
The visibility is controlled by `isSubBlockHiddenByHostedKey()` in `lib/workflows/subblocks/visibility.ts`, which checks the `isHosted` feature flag.
|
||||
The visibility is controlled by `isSubBlockHidden()` in `lib/workflows/subblocks/visibility.ts`, which checks both the `isHosted` feature flag (`hideWhenHosted`) and optional env var conditions (`hideWhenEnvSet`).
|
||||
|
||||
### Excluding Specific Operations from Hosted Key Support
|
||||
|
||||
|
||||
@@ -29,6 +29,14 @@ API_ENCRYPTION_KEY=your_api_encryption_key # Use `openssl rand -hex 32` to gener
|
||||
# VLLM_BASE_URL=http://localhost:8000 # Base URL for your self-hosted vLLM (OpenAI-compatible)
|
||||
# VLLM_API_KEY= # Optional bearer token if your vLLM instance requires auth
|
||||
# FIREWORKS_API_KEY= # Optional Fireworks AI API key for model listing
|
||||
# NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS=true # Set when using AWS default credential chain (IAM roles, ECS task roles, IRSA). Hides credential fields in Agent block UI.
|
||||
# AZURE_OPENAI_ENDPOINT= # Azure OpenAI endpoint (hides field in UI when set alongside NEXT_PUBLIC_AZURE_CONFIGURED)
|
||||
# AZURE_OPENAI_API_KEY= # Azure OpenAI API key
|
||||
# AZURE_OPENAI_API_VERSION= # Azure OpenAI API version
|
||||
# AZURE_ANTHROPIC_ENDPOINT= # Azure Anthropic endpoint (AI Foundry)
|
||||
# AZURE_ANTHROPIC_API_KEY= # Azure Anthropic API key
|
||||
# AZURE_ANTHROPIC_API_VERSION= # Azure Anthropic API version (e.g., 2023-06-01)
|
||||
# NEXT_PUBLIC_AZURE_CONFIGURED=true # Set when Azure credentials are pre-configured above. Hides endpoint/key/version fields in Agent block UI.
|
||||
|
||||
# Admin API (Optional - for self-hosted GitOps)
|
||||
# ADMIN_API_KEY= # Use `openssl rand -hex 32` to generate. Enables admin API for workflow export/import.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { SecretListEntry, Tag } from '@aws-sdk/client-secrets-manager'
|
||||
import {
|
||||
CreateSecretCommand,
|
||||
DeleteSecretCommand,
|
||||
@@ -61,7 +62,7 @@ export async function listSecrets(
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const secrets = (response.SecretList ?? []).map((secret) => ({
|
||||
const secrets = (response.SecretList ?? []).map((secret: SecretListEntry) => ({
|
||||
name: secret.Name ?? '',
|
||||
arn: secret.ARN ?? '',
|
||||
description: secret.Description ?? null,
|
||||
@@ -69,7 +70,7 @@ export async function listSecrets(
|
||||
lastChangedDate: secret.LastChangedDate?.toISOString() ?? null,
|
||||
lastAccessedDate: secret.LastAccessedDate?.toISOString() ?? null,
|
||||
rotationEnabled: secret.RotationEnabled ?? false,
|
||||
tags: secret.Tags?.map((t) => ({ key: t.Key ?? '', value: t.Value ?? '' })) ?? [],
|
||||
tags: secret.Tags?.map((t: Tag) => ({ key: t.Key ?? '', value: t.Value ?? '' })) ?? [],
|
||||
}))
|
||||
|
||||
return {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
buildCanonicalIndex,
|
||||
evaluateSubBlockCondition,
|
||||
isSubBlockFeatureEnabled,
|
||||
isSubBlockHiddenByHostedKey,
|
||||
isSubBlockHidden,
|
||||
isSubBlockVisibleForMode,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockType } from '@/blocks/types'
|
||||
@@ -109,8 +109,8 @@ export function useEditorSubblockLayout(
|
||||
// Check required feature if specified - declarative feature gating
|
||||
if (!isSubBlockFeatureEnabled(block)) return false
|
||||
|
||||
// Hide tool API key fields when hosted
|
||||
if (isSubBlockHiddenByHostedKey(block)) return false
|
||||
// Hide tool API key fields when hosted or when env var is set
|
||||
if (isSubBlockHidden(block)) return false
|
||||
|
||||
// Special handling for trigger-config type (legacy trigger configuration UI)
|
||||
if (block.type === ('trigger-config' as SubBlockType)) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
evaluateSubBlockCondition,
|
||||
hasAdvancedValues,
|
||||
isSubBlockFeatureEnabled,
|
||||
isSubBlockHiddenByHostedKey,
|
||||
isSubBlockHidden,
|
||||
isSubBlockVisibleForMode,
|
||||
resolveDependencyValue,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
@@ -980,7 +980,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
if (block.hidden) return false
|
||||
if (block.hideFromPreview) return false
|
||||
if (!isSubBlockFeatureEnabled(block)) return false
|
||||
if (isSubBlockHiddenByHostedKey(block)) return false
|
||||
if (isSubBlockHidden(block)) return false
|
||||
|
||||
const isPureTriggerBlock = config?.triggers?.enabled && config.category === 'triggers'
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { getScopesForService } from '@/lib/oauth/utils'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode, IntegrationType } from '@/blocks/types'
|
||||
import { getApiKeyCondition, getModelOptions, RESPONSE_FORMAT_WAND_CONFIG } from '@/blocks/utils'
|
||||
import {
|
||||
getModelOptions,
|
||||
getProviderCredentialSubBlocks,
|
||||
RESPONSE_FORMAT_WAND_CONFIG,
|
||||
} from '@/blocks/utils'
|
||||
import {
|
||||
getBaseModelProviders,
|
||||
getMaxTemperature,
|
||||
@@ -12,7 +15,6 @@ import {
|
||||
getModelsWithReasoningEffort,
|
||||
getModelsWithThinking,
|
||||
getModelsWithVerbosity,
|
||||
getProviderModels,
|
||||
getReasoningEffortValuesForModel,
|
||||
getThinkingLevelsForModel,
|
||||
getVerbosityValuesForModel,
|
||||
@@ -23,9 +25,6 @@ import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('AgentBlock')
|
||||
const VERTEX_MODELS = getProviderModels('vertex')
|
||||
const BEDROCK_MODELS = getProviderModels('bedrock')
|
||||
const AZURE_MODELS = [...getProviderModels('azure-openai'), ...getProviderModels('azure-anthropic')]
|
||||
const MODELS_WITH_REASONING_EFFORT = getModelsWithReasoningEffort()
|
||||
const MODELS_WITH_VERBOSITY = getModelsWithVerbosity()
|
||||
const MODELS_WITH_THINKING = getModelsWithThinking()
|
||||
@@ -134,34 +133,6 @@ Return ONLY the JSON array.`,
|
||||
defaultValue: 'claude-sonnet-4-5',
|
||||
options: getModelOptions,
|
||||
},
|
||||
{
|
||||
id: 'vertexCredential',
|
||||
title: 'Google Cloud Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'vertex-ai',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'basic',
|
||||
requiredScopes: getScopesForService('vertex-ai'),
|
||||
placeholder: 'Select Google Cloud account',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'manualCredential',
|
||||
title: 'Google Cloud Account',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'oauthCredential',
|
||||
mode: 'advanced',
|
||||
placeholder: 'Enter credential ID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'reasoningEffort',
|
||||
title: 'Reasoning Effort',
|
||||
@@ -318,100 +289,7 @@ Return ONLY the JSON array.`,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'azureEndpoint',
|
||||
title: 'Azure Endpoint',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'https://your-resource.services.ai.azure.com',
|
||||
connectionDroppable: false,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: AZURE_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'azureApiVersion',
|
||||
title: 'Azure API Version',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter API version',
|
||||
connectionDroppable: false,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: AZURE_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'vertexProject',
|
||||
title: 'Vertex AI Project',
|
||||
type: 'short-input',
|
||||
placeholder: 'your-gcp-project-id',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'vertexLocation',
|
||||
title: 'Vertex AI Location',
|
||||
type: 'short-input',
|
||||
placeholder: 'us-central1',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bedrockAccessKeyId',
|
||||
title: 'AWS Access Key ID',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your AWS Access Key ID',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: BEDROCK_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bedrockSecretKey',
|
||||
title: 'AWS Secret Access Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your AWS Secret Access Key',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: BEDROCK_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'bedrockRegion',
|
||||
title: 'AWS Region',
|
||||
type: 'short-input',
|
||||
placeholder: 'us-east-1',
|
||||
connectionDroppable: false,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: BEDROCK_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your API key',
|
||||
password: true,
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
condition: getApiKeyCondition(),
|
||||
},
|
||||
...getProviderCredentialSubBlocks(),
|
||||
{
|
||||
id: 'tools',
|
||||
title: 'Tools',
|
||||
@@ -661,7 +539,7 @@ Return ONLY the JSON array.`,
|
||||
apiKey: { type: 'string', description: 'Provider API key' },
|
||||
azureEndpoint: { type: 'string', description: 'Azure endpoint URL' },
|
||||
azureApiVersion: { type: 'string', description: 'Azure API version' },
|
||||
oauthCredential: { type: 'string', description: 'OAuth credential for Vertex AI' },
|
||||
vertexCredential: { type: 'string', description: 'OAuth credential for Vertex AI' },
|
||||
vertexProject: { type: 'string', description: 'Google Cloud project ID for Vertex AI' },
|
||||
vertexLocation: { type: 'string', description: 'Google Cloud location for Vertex AI' },
|
||||
bedrockAccessKeyId: { type: 'string', description: 'AWS Access Key ID for Bedrock' },
|
||||
|
||||
@@ -29,7 +29,7 @@ export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
],
|
||||
placeholder: 'Select language',
|
||||
value: () => CodeLanguage.JavaScript,
|
||||
requiresFeature: 'NEXT_PUBLIC_E2B_ENABLED',
|
||||
showWhenEnvSet: 'NEXT_PUBLIC_E2B_ENABLED',
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
|
||||
@@ -327,8 +327,9 @@ export interface SubBlockConfig {
|
||||
connectionDroppable?: boolean
|
||||
hidden?: boolean
|
||||
hideFromPreview?: boolean // Hide this subblock from the workflow block preview
|
||||
requiresFeature?: string // Environment variable name that must be truthy for this subblock to be visible
|
||||
showWhenEnvSet?: string // Show this subblock only when the named NEXT_PUBLIC_ env var is truthy
|
||||
hideWhenHosted?: boolean // Hide this subblock when running on hosted sim
|
||||
hideWhenEnvSet?: string // Hide this subblock when the named NEXT_PUBLIC_ env var is truthy
|
||||
description?: string
|
||||
tooltip?: string // Tooltip text displayed via info icon next to the title
|
||||
value?: (params: Record<string, any>) => string
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { isAzureConfigured, isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { getScopesForService } from '@/lib/oauth/utils'
|
||||
import type { BlockOutput, OutputFieldDefinition, SubBlockConfig } from '@/blocks/types'
|
||||
import {
|
||||
getHostedModels,
|
||||
@@ -8,9 +9,12 @@ import {
|
||||
} from '@/providers/models'
|
||||
import { useProvidersStore } from '@/stores/providers/store'
|
||||
|
||||
const VERTEX_MODELS = getProviderModels('vertex')
|
||||
const BEDROCK_MODELS = getProviderModels('bedrock')
|
||||
const AZURE_MODELS = [...getProviderModels('azure-openai'), ...getProviderModels('azure-anthropic')]
|
||||
export const VERTEX_MODELS = getProviderModels('vertex')
|
||||
export const BEDROCK_MODELS = getProviderModels('bedrock')
|
||||
export const AZURE_MODELS = [
|
||||
...getProviderModels('azure-openai'),
|
||||
...getProviderModels('azure-anthropic'),
|
||||
]
|
||||
|
||||
/**
|
||||
* Returns model options for combobox subblocks, combining all provider sources.
|
||||
@@ -105,6 +109,16 @@ function shouldRequireApiKeyForModel(model: string): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
isAzureConfigured &&
|
||||
(normalizedModel.startsWith('azure/') ||
|
||||
normalizedModel.startsWith('azure-openai/') ||
|
||||
normalizedModel.startsWith('azure-anthropic/') ||
|
||||
AZURE_MODELS.some((m) => m.toLowerCase() === normalizedModel))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (normalizedModel.startsWith('vllm/')) {
|
||||
return false
|
||||
}
|
||||
@@ -158,7 +172,9 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
title: 'Google Cloud Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'vertex-ai',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/cloud-platform'],
|
||||
canonicalParamId: 'vertexCredential',
|
||||
mode: 'basic',
|
||||
requiredScopes: getScopesForService('vertex-ai'),
|
||||
placeholder: 'Select Google Cloud account',
|
||||
required: true,
|
||||
condition: {
|
||||
@@ -166,6 +182,19 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'vertexManualCredential',
|
||||
title: 'Google Cloud Account',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'vertexCredential',
|
||||
mode: 'advanced',
|
||||
placeholder: 'Enter credential ID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: VERTEX_MODELS,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
@@ -183,6 +212,7 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
password: true,
|
||||
placeholder: 'https://your-resource.services.ai.azure.com',
|
||||
connectionDroppable: false,
|
||||
hideWhenEnvSet: 'NEXT_PUBLIC_AZURE_CONFIGURED',
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: AZURE_MODELS,
|
||||
@@ -194,6 +224,7 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter API version',
|
||||
connectionDroppable: false,
|
||||
hideWhenEnvSet: 'NEXT_PUBLIC_AZURE_CONFIGURED',
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: AZURE_MODELS,
|
||||
@@ -203,6 +234,7 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
id: 'vertexProject',
|
||||
title: 'Vertex AI Project',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'your-gcp-project-id',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
@@ -231,6 +263,7 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
placeholder: 'Enter your AWS Access Key ID',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
hideWhenEnvSet: 'NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS',
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: BEDROCK_MODELS,
|
||||
@@ -244,6 +277,7 @@ export function getProviderCredentialSubBlocks(): SubBlockConfig[] {
|
||||
placeholder: 'Enter your AWS Secret Access Key',
|
||||
connectionDroppable: false,
|
||||
required: true,
|
||||
hideWhenEnvSet: 'NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS',
|
||||
condition: {
|
||||
field: 'model',
|
||||
value: BEDROCK_MODELS,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { decryptSecret } from '@/lib/core/security/encryption'
|
||||
import { getWorkspaceById } from '@/lib/workspaces/permissions/utils'
|
||||
import { getHostedModels } from '@/providers/models'
|
||||
import { PROVIDER_PLACEHOLDER_KEY } from '@/providers/utils'
|
||||
import { useProvidersStore } from '@/stores/providers/store'
|
||||
import type { BYOKProviderId } from '@/tools/types'
|
||||
|
||||
@@ -95,7 +96,15 @@ export async function getApiKeyWithBYOK(
|
||||
|
||||
const isBedrockModel = provider === 'bedrock' || model.startsWith('bedrock/')
|
||||
if (isBedrockModel) {
|
||||
return { apiKey: 'bedrock-uses-own-credentials', isBYOK: false }
|
||||
return { apiKey: PROVIDER_PLACEHOLDER_KEY, isBYOK: false }
|
||||
}
|
||||
|
||||
if (provider === 'azure-openai') {
|
||||
return { apiKey: userProvidedKey || env.AZURE_OPENAI_API_KEY || '', isBYOK: false }
|
||||
}
|
||||
|
||||
if (provider === 'azure-anthropic') {
|
||||
return { apiKey: userProvidedKey || env.AZURE_ANTHROPIC_API_KEY || '', isBYOK: false }
|
||||
}
|
||||
|
||||
const isOpenAIModel = provider === 'openai'
|
||||
|
||||
@@ -559,12 +559,12 @@ export const auth = betterAuth({
|
||||
github: {
|
||||
clientId: env.GITHUB_CLIENT_ID as string,
|
||||
clientSecret: env.GITHUB_CLIENT_SECRET as string,
|
||||
scopes: ['user:email', 'repo'],
|
||||
scope: ['user:email', 'repo'],
|
||||
},
|
||||
google: {
|
||||
clientId: env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET as string,
|
||||
scopes: [
|
||||
scope: [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
],
|
||||
@@ -602,7 +602,6 @@ export const auth = betterAuth({
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: isEmailVerificationEnabled,
|
||||
sendVerificationOnSignUp: isEmailVerificationEnabled, // Auto-send verification OTP on signup when verification is required
|
||||
throwOnMissingCredentials: true,
|
||||
throwOnInvalidCredentials: true,
|
||||
sendResetPassword: async ({ user, url, token }, request) => {
|
||||
|
||||
@@ -954,10 +954,10 @@ async function generateOAuthLink(
|
||||
const { headers: getHeaders } = await import('next/headers')
|
||||
const reqHeaders = await getHeaders()
|
||||
|
||||
const data = (await auth.api.oAuth2LinkAccount({
|
||||
const data = await auth.api.oAuth2LinkAccount({
|
||||
body: { providerId, callbackURL },
|
||||
headers: reqHeaders,
|
||||
})) as { url?: string; redirect?: boolean }
|
||||
})
|
||||
|
||||
if (!data?.url) {
|
||||
throw new Error('oAuth2LinkAccount did not return an authorization URL')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getCopilotToolDescription } from '@/lib/copilot/tool-descriptions'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { isSubBlockHiddenByHostedKey } from '@/lib/workflows/subblocks/visibility'
|
||||
import { isSubBlockHidden } from '@/lib/workflows/subblocks/visibility'
|
||||
import type { BlockConfig, SubBlockConfig } from '@/blocks/types'
|
||||
import { PROVIDER_DEFINITIONS } from '@/providers/models'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
@@ -369,7 +369,7 @@ function serializeSubBlock(sb: SubBlockConfig): Record<string, unknown> {
|
||||
* Serialize a block schema for VFS components/blocks/{type}.json
|
||||
*/
|
||||
export function serializeBlockSchema(block: BlockConfig): string {
|
||||
const hiddenIds = new Set(block.subBlocks.filter(isSubBlockHiddenByHostedKey).map((sb) => sb.id))
|
||||
const hiddenIds = new Set(block.subBlocks.filter(isSubBlockHidden).map((sb) => sb.id))
|
||||
|
||||
const subBlocks = block.subBlocks
|
||||
.filter((sb) => !hiddenIds.has(sb.id))
|
||||
|
||||
@@ -403,6 +403,8 @@ export const env = createEnv({
|
||||
NEXT_PUBLIC_SUPPORT_EMAIL: z.string().email().optional(), // Custom support email
|
||||
|
||||
NEXT_PUBLIC_E2B_ENABLED: z.string().optional(),
|
||||
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: z.string().optional(), // Hide Bedrock credential fields when deployment uses AWS default credential chain (IAM roles, instance profiles, ECS task roles, IRSA)
|
||||
NEXT_PUBLIC_AZURE_CONFIGURED: z.string().optional(), // Hide Azure credential fields when endpoint/key/version are pre-configured server-side
|
||||
NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: z.string().optional(),
|
||||
NEXT_PUBLIC_ENABLE_PLAYGROUND: z.string().optional(), // Enable component playground at /playground
|
||||
NEXT_PUBLIC_DOCUMENTATION_URL: z.string().url().optional(), // Custom documentation URL
|
||||
@@ -461,6 +463,8 @@ export const env = createEnv({
|
||||
NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED: process.env.NEXT_PUBLIC_EMAIL_PASSWORD_SIGNUP_ENABLED,
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
|
||||
NEXT_PUBLIC_E2B_ENABLED: process.env.NEXT_PUBLIC_E2B_ENABLED,
|
||||
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: process.env.NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS,
|
||||
NEXT_PUBLIC_AZURE_CONFIGURED: process.env.NEXT_PUBLIC_AZURE_CONFIGURED,
|
||||
NEXT_PUBLIC_COPILOT_TRAINING_ENABLED: process.env.NEXT_PUBLIC_COPILOT_TRAINING_ENABLED,
|
||||
NEXT_PUBLIC_ENABLE_PLAYGROUND: process.env.NEXT_PUBLIC_ENABLE_PLAYGROUND,
|
||||
NEXT_PUBLIC_POSTHOG_ENABLED: process.env.NEXT_PUBLIC_POSTHOG_ENABLED,
|
||||
|
||||
@@ -122,6 +122,14 @@ export const isInboxEnabled = isTruthy(env.INBOX_ENABLED)
|
||||
*/
|
||||
export const isE2bEnabled = isTruthy(env.E2B_ENABLED)
|
||||
|
||||
/**
|
||||
* Whether Azure OpenAI / Azure Anthropic credentials are pre-configured at the server level
|
||||
* (via AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_ANTHROPIC_ENDPOINT, etc.).
|
||||
* When true, the endpoint, API key, and API version fields are hidden in the Agent block UI.
|
||||
* Set NEXT_PUBLIC_AZURE_CONFIGURED=true in self-hosted deployments on Azure.
|
||||
*/
|
||||
export const isAzureConfigured = isTruthy(getEnv('NEXT_PUBLIC_AZURE_CONFIGURED'))
|
||||
|
||||
/**
|
||||
* Are invitations disabled globally
|
||||
* When true, workspace invitations are disabled for all users
|
||||
|
||||
@@ -285,15 +285,19 @@ export function resolveDependencyValue(
|
||||
* Check if a subblock is gated by a feature flag.
|
||||
*/
|
||||
export function isSubBlockFeatureEnabled(subBlock: SubBlockConfig): boolean {
|
||||
if (!subBlock.requiresFeature) return true
|
||||
return isTruthy(getEnv(subBlock.requiresFeature))
|
||||
if (!subBlock.showWhenEnvSet) return true
|
||||
return isTruthy(getEnv(subBlock.showWhenEnvSet))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a subblock should be hidden because we're running on hosted Sim.
|
||||
* Used for tool API key fields that should be hidden when Sim provides hosted keys.
|
||||
* Check if a subblock should be hidden based on environment conditions.
|
||||
* Covers two cases:
|
||||
* - `hideWhenHosted`: hidden when running on hosted Sim (tool API key fields)
|
||||
* - `hideWhenEnvSet`: hidden when a specific NEXT_PUBLIC_ env var is truthy
|
||||
* (credential fields hidden when the deployment provides them server-side)
|
||||
*/
|
||||
export function isSubBlockHiddenByHostedKey(subBlock: SubBlockConfig): boolean {
|
||||
if (!subBlock.hideWhenHosted) return false
|
||||
return isHosted
|
||||
export function isSubBlockHidden(subBlock: SubBlockConfig): boolean {
|
||||
if (subBlock.hideWhenHosted && isHosted) return true
|
||||
if (subBlock.hideWhenEnvSet && isTruthy(getEnv(subBlock.hideWhenEnvSet))) return true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Anthropic from '@anthropic-ai/sdk'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import type { StreamingExecution } from '@/executor/types'
|
||||
import { executeAnthropicProviderRequest } from '@/providers/anthropic/core'
|
||||
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
|
||||
@@ -18,14 +19,16 @@ export const azureAnthropicProvider: ProviderConfig = {
|
||||
executeRequest: async (
|
||||
request: ProviderRequest
|
||||
): Promise<ProviderResponse | StreamingExecution> => {
|
||||
if (!request.azureEndpoint) {
|
||||
const azureEndpoint = request.azureEndpoint || env.AZURE_ANTHROPIC_ENDPOINT
|
||||
if (!azureEndpoint) {
|
||||
throw new Error(
|
||||
'Azure endpoint is required for Azure Anthropic. Please provide it via the azureEndpoint parameter.'
|
||||
'Azure endpoint is required for Azure Anthropic. Please provide it via the azureEndpoint parameter or AZURE_ANTHROPIC_ENDPOINT environment variable.'
|
||||
)
|
||||
}
|
||||
|
||||
if (!request.apiKey) {
|
||||
throw new Error('API key is required for Azure Anthropic')
|
||||
const apiKey = request.apiKey
|
||||
if (!apiKey) {
|
||||
throw new Error('API key is required for Azure Anthropic.')
|
||||
}
|
||||
|
||||
// Strip the azure-anthropic/ prefix from the model name if present
|
||||
@@ -33,14 +36,16 @@ export const azureAnthropicProvider: ProviderConfig = {
|
||||
|
||||
// Azure AI Foundry hosts Anthropic models at {endpoint}/anthropic
|
||||
// The SDK appends /v1/messages automatically
|
||||
const baseURL = `${request.azureEndpoint.replace(/\/$/, '')}/anthropic`
|
||||
const baseURL = `${azureEndpoint.replace(/\/$/, '')}/anthropic`
|
||||
|
||||
const anthropicVersion = request.azureApiVersion || '2023-06-01'
|
||||
const anthropicVersion =
|
||||
request.azureApiVersion || env.AZURE_ANTHROPIC_API_VERSION || '2023-06-01'
|
||||
|
||||
return executeAnthropicProviderRequest(
|
||||
{
|
||||
...request,
|
||||
model: modelName,
|
||||
apiKey,
|
||||
},
|
||||
{
|
||||
providerId: 'azure-anthropic',
|
||||
|
||||
@@ -65,7 +65,7 @@ async function executeChatCompletionsRequest(
|
||||
})
|
||||
|
||||
const azureOpenAI = new AzureOpenAI({
|
||||
apiKey: request.apiKey,
|
||||
apiKey: request.apiKey!,
|
||||
apiVersion: azureApiVersion,
|
||||
endpoint: azureEndpoint,
|
||||
})
|
||||
@@ -623,8 +623,9 @@ export const azureOpenAIProvider: ProviderConfig = {
|
||||
)
|
||||
}
|
||||
|
||||
if (!request.apiKey) {
|
||||
throw new Error('API key is required for Azure OpenAI')
|
||||
const apiKey = request.apiKey
|
||||
if (!apiKey) {
|
||||
throw new Error('API key is required for Azure OpenAI.')
|
||||
}
|
||||
|
||||
// Check if the endpoint is a full chat completions URL
|
||||
@@ -653,7 +654,12 @@ export const azureOpenAIProvider: ProviderConfig = {
|
||||
apiVersion: azureApiVersion,
|
||||
})
|
||||
|
||||
return executeChatCompletionsRequest(request, baseUrl, azureApiVersion, deploymentName)
|
||||
return executeChatCompletionsRequest(
|
||||
{ ...request, apiKey },
|
||||
baseUrl,
|
||||
azureApiVersion,
|
||||
deploymentName
|
||||
)
|
||||
}
|
||||
|
||||
// Check if the endpoint is already a full responses API URL
|
||||
@@ -663,18 +669,21 @@ export const azureOpenAIProvider: ProviderConfig = {
|
||||
const deploymentName = request.model.replace('azure/', '')
|
||||
|
||||
// Use the URL as-is since it's already complete
|
||||
return executeResponsesProviderRequest(request, {
|
||||
providerId: 'azure-openai',
|
||||
providerLabel: 'Azure OpenAI',
|
||||
modelName: deploymentName,
|
||||
endpoint: azureEndpoint,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'OpenAI-Beta': 'responses=v1',
|
||||
'api-key': request.apiKey,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
return executeResponsesProviderRequest(
|
||||
{ ...request, apiKey },
|
||||
{
|
||||
providerId: 'azure-openai',
|
||||
providerLabel: 'Azure OpenAI',
|
||||
modelName: deploymentName,
|
||||
endpoint: azureEndpoint,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'OpenAI-Beta': 'responses=v1',
|
||||
'api-key': apiKey,
|
||||
},
|
||||
logger,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Default: base URL provided, construct the responses API URL
|
||||
@@ -684,17 +693,20 @@ export const azureOpenAIProvider: ProviderConfig = {
|
||||
const deploymentName = request.model.replace('azure/', '')
|
||||
const apiUrl = `${azureEndpoint.replace(/\/$/, '')}/openai/v1/responses?api-version=${azureApiVersion}`
|
||||
|
||||
return executeResponsesProviderRequest(request, {
|
||||
providerId: 'azure-openai',
|
||||
providerLabel: 'Azure OpenAI',
|
||||
modelName: deploymentName,
|
||||
endpoint: apiUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'OpenAI-Beta': 'responses=v1',
|
||||
'api-key': request.apiKey,
|
||||
},
|
||||
logger,
|
||||
})
|
||||
return executeResponsesProviderRequest(
|
||||
{ ...request, apiKey },
|
||||
{
|
||||
providerId: 'azure-openai',
|
||||
providerLabel: 'Azure OpenAI',
|
||||
modelName: deploymentName,
|
||||
endpoint: apiUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'OpenAI-Beta': 'responses=v1',
|
||||
'api-key': apiKey,
|
||||
},
|
||||
logger,
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
115
apps/sim/providers/bedrock/index.test.ts
Normal file
115
apps/sim/providers/bedrock/index.test.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const mockSend = vi.fn()
|
||||
|
||||
vi.mock('@aws-sdk/client-bedrock-runtime', () => ({
|
||||
BedrockRuntimeClient: vi.fn().mockImplementation(() => {
|
||||
return { send: mockSend }
|
||||
}),
|
||||
ConverseCommand: vi.fn(),
|
||||
ConverseStreamCommand: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/providers/bedrock/utils', () => ({
|
||||
getBedrockInferenceProfileId: vi
|
||||
.fn()
|
||||
.mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'),
|
||||
checkForForcedToolUsage: vi.fn(),
|
||||
createReadableStreamFromBedrockStream: vi.fn(),
|
||||
generateToolUseId: vi.fn().mockReturnValue('tool-1'),
|
||||
}))
|
||||
|
||||
vi.mock('@/providers/models', () => ({
|
||||
getProviderModels: vi.fn().mockReturnValue([]),
|
||||
getProviderDefaultModel: vi.fn().mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'),
|
||||
}))
|
||||
|
||||
vi.mock('@/providers/utils', () => ({
|
||||
calculateCost: vi.fn().mockReturnValue({ input: 0, output: 0, total: 0, pricing: null }),
|
||||
prepareToolExecution: vi.fn(),
|
||||
prepareToolsWithUsageControl: vi.fn().mockReturnValue({
|
||||
tools: [],
|
||||
toolChoice: 'auto',
|
||||
forcedTools: [],
|
||||
}),
|
||||
sumToolCosts: vi.fn().mockReturnValue(0),
|
||||
}))
|
||||
|
||||
vi.mock('@/tools', () => ({
|
||||
executeTool: vi.fn(),
|
||||
}))
|
||||
|
||||
import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime'
|
||||
import { bedrockProvider } from '@/providers/bedrock/index'
|
||||
|
||||
describe('bedrockProvider credential handling', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockSend.mockResolvedValue({
|
||||
output: { message: { content: [{ text: 'response' }] } },
|
||||
usage: { inputTokens: 10, outputTokens: 5 },
|
||||
})
|
||||
})
|
||||
|
||||
const baseRequest = {
|
||||
model: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0',
|
||||
systemPrompt: 'You are helpful.',
|
||||
messages: [{ role: 'user' as const, content: 'Hello' }],
|
||||
}
|
||||
|
||||
it('throws when only bedrockAccessKeyId is provided', async () => {
|
||||
await expect(
|
||||
bedrockProvider.executeRequest({
|
||||
...baseRequest,
|
||||
bedrockAccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
||||
})
|
||||
).rejects.toThrow('Both bedrockAccessKeyId and bedrockSecretKey must be provided together')
|
||||
})
|
||||
|
||||
it('throws when only bedrockSecretKey is provided', async () => {
|
||||
await expect(
|
||||
bedrockProvider.executeRequest({
|
||||
...baseRequest,
|
||||
bedrockSecretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||
})
|
||||
).rejects.toThrow('Both bedrockAccessKeyId and bedrockSecretKey must be provided together')
|
||||
})
|
||||
|
||||
it('creates client with explicit credentials when both are provided', async () => {
|
||||
await bedrockProvider.executeRequest({
|
||||
...baseRequest,
|
||||
bedrockAccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
||||
bedrockSecretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||
})
|
||||
|
||||
expect(BedrockRuntimeClient).toHaveBeenCalledWith({
|
||||
region: 'us-east-1',
|
||||
credentials: {
|
||||
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
|
||||
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('creates client without credentials when neither is provided', async () => {
|
||||
await bedrockProvider.executeRequest(baseRequest)
|
||||
|
||||
expect(BedrockRuntimeClient).toHaveBeenCalledWith({
|
||||
region: 'us-east-1',
|
||||
})
|
||||
})
|
||||
|
||||
it('uses custom region when provided', async () => {
|
||||
await bedrockProvider.executeRequest({
|
||||
...baseRequest,
|
||||
bedrockRegion: 'eu-west-1',
|
||||
})
|
||||
|
||||
expect(BedrockRuntimeClient).toHaveBeenCalledWith({
|
||||
region: 'eu-west-1',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
type Message as BedrockMessage,
|
||||
BedrockRuntimeClient,
|
||||
type BedrockRuntimeClientConfig,
|
||||
type ContentBlock,
|
||||
type ConversationRole,
|
||||
ConverseCommand,
|
||||
@@ -50,14 +51,6 @@ export const bedrockProvider: ProviderConfig = {
|
||||
executeRequest: async (
|
||||
request: ProviderRequest
|
||||
): Promise<ProviderResponse | StreamingExecution> => {
|
||||
if (!request.bedrockAccessKeyId) {
|
||||
throw new Error('AWS Access Key ID is required for Bedrock')
|
||||
}
|
||||
|
||||
if (!request.bedrockSecretKey) {
|
||||
throw new Error('AWS Secret Access Key is required for Bedrock')
|
||||
}
|
||||
|
||||
const region = request.bedrockRegion || 'us-east-1'
|
||||
const bedrockModelId = getBedrockInferenceProfileId(request.model, region)
|
||||
|
||||
@@ -67,13 +60,24 @@ export const bedrockProvider: ProviderConfig = {
|
||||
region,
|
||||
})
|
||||
|
||||
const client = new BedrockRuntimeClient({
|
||||
region,
|
||||
credentials: {
|
||||
accessKeyId: request.bedrockAccessKeyId || '',
|
||||
secretAccessKey: request.bedrockSecretKey || '',
|
||||
},
|
||||
})
|
||||
const hasAccessKey = Boolean(request.bedrockAccessKeyId)
|
||||
const hasSecretKey = Boolean(request.bedrockSecretKey)
|
||||
if (hasAccessKey !== hasSecretKey) {
|
||||
throw new Error(
|
||||
'Both bedrockAccessKeyId and bedrockSecretKey must be provided together. ' +
|
||||
'Provide both for explicit credentials, or omit both to use the AWS default credential chain.'
|
||||
)
|
||||
}
|
||||
|
||||
const clientConfig: BedrockRuntimeClientConfig = { region }
|
||||
if (request.bedrockAccessKeyId && request.bedrockSecretKey) {
|
||||
clientConfig.credentials = {
|
||||
accessKeyId: request.bedrockAccessKeyId,
|
||||
secretAccessKey: request.bedrockSecretKey,
|
||||
}
|
||||
}
|
||||
|
||||
const client = new BedrockRuntimeClient(clientConfig)
|
||||
|
||||
const messages: BedrockMessage[] = []
|
||||
const systemContent: SystemContentBlock[] = []
|
||||
|
||||
@@ -718,6 +718,13 @@ export function shouldBillModelUsage(model: string): boolean {
|
||||
return hostedModels.some((hostedModel) => model.toLowerCase() === hostedModel.toLowerCase())
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder returned for providers that use their own credential mechanism
|
||||
* rather than a user-supplied API key (e.g. AWS Bedrock via IAM/instance profiles).
|
||||
* Must be truthy so upstream key-presence checks don't reject it.
|
||||
*/
|
||||
export const PROVIDER_PLACEHOLDER_KEY = 'provider-uses-own-credentials'
|
||||
|
||||
/**
|
||||
* Get an API key for a specific provider, handling rotation and fallbacks
|
||||
* For use server-side only
|
||||
@@ -740,7 +747,7 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str
|
||||
// Bedrock uses its own credentials (bedrockAccessKeyId/bedrockSecretKey), not apiKey
|
||||
const isBedrockModel = provider === 'bedrock' || model.startsWith('bedrock/')
|
||||
if (isBedrockModel) {
|
||||
return 'bedrock-uses-own-credentials'
|
||||
return PROVIDER_PLACEHOLDER_KEY
|
||||
}
|
||||
|
||||
const isOpenAIModel = provider === 'openai'
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
isCanonicalPair,
|
||||
isNonEmptyValue,
|
||||
isSubBlockFeatureEnabled,
|
||||
isSubBlockHiddenByHostedKey,
|
||||
isSubBlockHidden,
|
||||
resolveCanonicalMode,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
import { getBlock } from '@/blocks'
|
||||
@@ -49,7 +49,7 @@ function shouldSerializeSubBlock(
|
||||
canonicalModeOverrides?: CanonicalModeOverrides
|
||||
): boolean {
|
||||
if (!isSubBlockFeatureEnabled(subBlockConfig)) return false
|
||||
if (isSubBlockHiddenByHostedKey(subBlockConfig)) return false
|
||||
if (isSubBlockHidden(subBlockConfig)) return false
|
||||
|
||||
if (subBlockConfig.mode === 'trigger') {
|
||||
if (!isTriggerContext && !isTriggerCategory) return false
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type CanonicalModeOverrides,
|
||||
evaluateSubBlockCondition,
|
||||
isCanonicalPair,
|
||||
isSubBlockHiddenByHostedKey,
|
||||
isSubBlockHidden,
|
||||
resolveCanonicalMode,
|
||||
type SubBlockCondition,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
@@ -320,7 +320,7 @@ export function getToolParametersConfig(
|
||||
)
|
||||
|
||||
if (subBlock) {
|
||||
if (isSubBlockHiddenByHostedKey(subBlock)) {
|
||||
if (isSubBlockHidden(subBlock)) {
|
||||
toolParam.visibility = 'hidden'
|
||||
}
|
||||
|
||||
@@ -946,8 +946,8 @@ export function getSubBlocksForToolInput(
|
||||
// Skip trigger-mode-only subblocks
|
||||
if (sb.mode === 'trigger') continue
|
||||
|
||||
// Hide tool API key fields when running on hosted Sim
|
||||
if (isSubBlockHiddenByHostedKey(sb)) continue
|
||||
// Hide tool API key fields when running on hosted Sim or when env var is set
|
||||
if (isSubBlockHidden(sb)) continue
|
||||
|
||||
// Determine the effective param ID (canonical or subblock id)
|
||||
const effectiveParamId = sb.canonicalParamId || sb.id
|
||||
|
||||
1
bun.lock
1
bun.lock
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "simstudio",
|
||||
|
||||
@@ -45,6 +45,10 @@ app:
|
||||
NODE_ENV: "production"
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
|
||||
# AWS Bedrock - when using IRSA (see serviceAccount below), the default credential chain
|
||||
# resolves automatically. Setting this hides the credential fields in the Agent block UI.
|
||||
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: "true" # Uncomment if using Bedrock with IRSA
|
||||
|
||||
# AWS S3 Cloud Storage Configuration (RECOMMENDED for production)
|
||||
# Create S3 buckets in your AWS account and configure IAM permissions
|
||||
AWS_REGION: "us-west-2"
|
||||
|
||||
@@ -47,6 +47,17 @@ app:
|
||||
NODE_ENV: "production"
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
|
||||
# Azure OpenAI / Azure Anthropic (Azure AI Foundry) — set to use server-side credentials.
|
||||
# When NEXT_PUBLIC_AZURE_CONFIGURED is true, the endpoint/key/version fields are hidden in
|
||||
# the Agent block UI so users just pick a model and run.
|
||||
AZURE_OPENAI_ENDPOINT: "" # e.g. https://your-resource.openai.azure.com
|
||||
AZURE_OPENAI_API_KEY: "" # Azure OpenAI API key
|
||||
AZURE_OPENAI_API_VERSION: "" # e.g. 2024-07-01-preview
|
||||
AZURE_ANTHROPIC_ENDPOINT: "" # Azure AI Foundry endpoint for Anthropic models
|
||||
AZURE_ANTHROPIC_API_KEY: "" # Azure Anthropic API key
|
||||
AZURE_ANTHROPIC_API_VERSION: "" # Azure Anthropic API version (e.g., 2023-06-01)
|
||||
NEXT_PUBLIC_AZURE_CONFIGURED: "true" # Set to "true" once credentials are configured above
|
||||
|
||||
# Azure Blob Storage Configuration (RECOMMENDED for production)
|
||||
# Create a storage account and containers in your Azure subscription
|
||||
AZURE_ACCOUNT_NAME: "simstudiostorageacct" # Azure storage account name
|
||||
|
||||
@@ -113,13 +113,24 @@ app:
|
||||
# Google Vertex AI Configuration
|
||||
VERTEX_PROJECT: "" # Google Cloud project ID for Vertex AI
|
||||
VERTEX_LOCATION: "us-central1" # Google Cloud region for Vertex AI (e.g., "us-central1")
|
||||
|
||||
|
||||
# Azure OpenAI Configuration (leave empty if not using Azure OpenAI)
|
||||
AZURE_OPENAI_ENDPOINT: "" # Azure OpenAI service endpoint (e.g., https://your-resource.openai.azure.com)
|
||||
AZURE_OPENAI_API_KEY: "" # Azure OpenAI API key
|
||||
AZURE_OPENAI_API_VERSION: "" # Azure OpenAI API version (e.g., 2024-07-01-preview)
|
||||
|
||||
# Azure Anthropic Configuration (leave empty if not using Azure Anthropic via AI Foundry)
|
||||
AZURE_ANTHROPIC_ENDPOINT: "" # Azure AI Foundry endpoint for Anthropic models
|
||||
AZURE_ANTHROPIC_API_KEY: "" # Azure Anthropic API key
|
||||
AZURE_ANTHROPIC_API_VERSION: "" # Azure Anthropic API version (e.g., 2023-06-01)
|
||||
|
||||
# AI Provider API Keys (leave empty if not using)
|
||||
OPENAI_API_KEY: "" # Primary OpenAI API key
|
||||
OPENAI_API_KEY_1: "" # Additional OpenAI API key for load balancing
|
||||
OPENAI_API_KEY_2: "" # Additional OpenAI API key for load balancing
|
||||
OPENAI_API_KEY_3: "" # Additional OpenAI API key for load balancing
|
||||
MISTRAL_API_KEY: "" # Mistral AI API key
|
||||
FIREWORKS_API_KEY: "" # Fireworks AI API key (for hosted model access)
|
||||
ANTHROPIC_API_KEY_1: "" # Primary Anthropic Claude API key
|
||||
ANTHROPIC_API_KEY_2: "" # Additional Anthropic API key for load balancing
|
||||
ANTHROPIC_API_KEY_3: "" # Additional Anthropic API key for load balancing
|
||||
@@ -223,6 +234,18 @@ app:
|
||||
SSO_ENABLED: "" # Enable SSO authentication ("true" to enable)
|
||||
NEXT_PUBLIC_SSO_ENABLED: "" # Show SSO login button in UI ("true" to enable)
|
||||
|
||||
# AWS Bedrock Credential Mode
|
||||
# Set to "true" when the deployment uses AWS default credential chain (IAM roles, instance
|
||||
# profiles, ECS task roles, IRSA, etc.) instead of explicit access key/secret per workflow.
|
||||
# When enabled, the AWS Access Key ID and Secret fields are hidden in the Agent block UI.
|
||||
NEXT_PUBLIC_BEDROCK_DEFAULT_CREDENTIALS: "" # Set to "true" to hide Bedrock credential fields
|
||||
|
||||
# Azure Provider Credential Mode
|
||||
# Set to "true" when AZURE_OPENAI_ENDPOINT/API_KEY (and/or AZURE_ANTHROPIC_*) are configured
|
||||
# server-side. When enabled, the Azure endpoint, API key, and API version fields are hidden
|
||||
# in the Agent block UI — users just pick an Azure model and run.
|
||||
NEXT_PUBLIC_AZURE_CONFIGURED: "" # Set to "true" to hide Azure credential fields
|
||||
|
||||
# AWS S3 Cloud Storage Configuration (optional - for file storage)
|
||||
# If configured, files will be stored in S3 instead of local storage
|
||||
AWS_REGION: "" # AWS region (e.g., "us-east-1")
|
||||
|
||||
Reference in New Issue
Block a user