diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index eb5c27bab..b7078a146 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -24,6 +24,7 @@ from app.agent.listen_chat_agent import ListenChatAgent, logger from app.model.chat import AgentModelConfig, Chat +from app.model.model_platform import patch_bedrock_cloud_config from app.service.task import ActionCreateAgentData, Agents, get_task_lock from app.utils.event_loop_utils import _schedule_async_task @@ -80,7 +81,14 @@ def agent_model( for attr in config_attrs: effective_config[attr] = getattr(options, attr) extra_params = options.extra_params or {} - + # Cloud mode: inject default Bedrock region and adjust URL for proxy. + if ( + effective_config.get("model_platform") == "aws-bedrock-converse" + and options.is_cloud() + ): + effective_config["api_url"], extra_params = patch_bedrock_cloud_config( + effective_config["api_url"], extra_params + ) init_param_keys = { "api_version", "azure_ad_token", @@ -90,6 +98,10 @@ def agent_model( "client", "async_client", "azure_deployment_name", + "region_name", + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token", } init_params = {} @@ -113,6 +125,26 @@ def agent_model( else: model_config[k] = v + # Auto-inject prompt caching based on model platform + try: + model_platform_enum = ModelPlatformType( + effective_config["model_platform"].lower() + ) + if model_platform_enum in { + ModelPlatformType.ANTHROPIC, + ModelPlatformType.AWS_BEDROCK_CONVERSE, + }: + model_config.setdefault("cache_control", "5m") + elif model_platform_enum == ModelPlatformType.OPENAI: + model_config.setdefault( + "prompt_cache_key", str(options.project_id) + ) + except (ValueError, AttributeError): + logging.error( + f"Invalid model platform: {effective_config['model_platform']}", + exc_info=True, + ) + if agent_name == Agents.task_agent: model_config["stream"] = True if agent_name == Agents.browser_agent: @@ -137,10 +169,8 @@ def agent_model( model_platform_enum = None if effective_config["model_platform"].lower() == "anthropic": - if model_config.get("cache_control") is None: - model_config["cache_control"] = "5m" if model_config.get("max_tokens") is None: - model_config["max_tokens"] = 64000 + model_config["max_tokens"] = 128000 model = ModelFactory.create( model_platform=effective_config["model_platform"], diff --git a/backend/app/agent/factory/mcp.py b/backend/app/agent/factory/mcp.py index 4f1dc86ab..f31cefd8e 100644 --- a/backend/app/agent/factory/mcp.py +++ b/backend/app/agent/factory/mcp.py @@ -12,15 +12,18 @@ # limitations under the License. # ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= import asyncio +import logging import uuid from camel.models import ModelFactory +from camel.types import ModelPlatformType from app.agent.listen_chat_agent import ListenChatAgent, logger from app.agent.prompt import MCP_SYS_PROMPT from app.agent.toolkit.mcp_search_toolkit import McpSearchToolkit from app.agent.tools import get_mcp_tools from app.model.chat import Chat +from app.model.model_platform import patch_bedrock_cloud_config from app.service.task import ActionCreateAgentData, Agents, get_task_lock @@ -73,6 +76,38 @@ async def mcp_agent(options: Chat): ) ) ) + extra_params = { + k: v + for k, v in (options.extra_params or {}).items() + if k not in ["model_platform", "model_type", "api_key", "url"] + } + api_url = options.api_url + if options.model_platform == "aws-bedrock-converse" and options.is_cloud(): + api_url, extra_params = patch_bedrock_cloud_config( + api_url, extra_params + ) + + # Build model_config_dict with prompt caching + model_config_dict = {} + if options.is_cloud(): + model_config_dict["user"] = str(options.project_id) + try: + platform_enum = ModelPlatformType(options.model_platform.lower()) + if platform_enum in { + ModelPlatformType.ANTHROPIC, + ModelPlatformType.AWS_BEDROCK_CONVERSE, + }: + model_config_dict.setdefault("cache_control", "5m") + elif platform_enum == ModelPlatformType.OPENAI: + model_config_dict.setdefault( + "prompt_cache_key", str(options.project_id) + ) + except (ValueError, AttributeError): + logging.error( + f"Invalid model platform: {options.model_platform}", + exc_info=True, + ) + return ListenChatAgent( options.project_id, Agents.mcp_agent, @@ -81,20 +116,10 @@ async def mcp_agent(options: Chat): model_platform=options.model_platform, model_type=options.model_type, api_key=options.api_key, - url=options.api_url, - model_config_dict=( - { - "user": str(options.project_id), - } - if options.is_cloud() - else None - ), + url=api_url, + model_config_dict=model_config_dict or None, timeout=600, # 10 minutes - **{ - k: v - for k, v in (options.extra_params or {}).items() - if k not in ["model_platform", "model_type", "api_key", "url"] - }, + **extra_params, ), # output_language=options.language, tools=tools, diff --git a/backend/app/component/model_validation.py b/backend/app/component/model_validation.py index bad0ad486..1ab4c3373 100644 --- a/backend/app/component/model_validation.py +++ b/backend/app/component/model_validation.py @@ -19,6 +19,8 @@ from camel.agents import ChatAgent from camel.models import ModelFactory, ModelProcessingError +from app.model.model_platform import BEDROCK_CONVERSE_REGION + logger = logging.getLogger("model_validation") # Expected result from tool execution for validation @@ -231,6 +233,8 @@ def create_agent( model_config_dict = dict(model_config_dict or {}) if model_config_dict.get("max_tokens") is None: model_config_dict["max_tokens"] = 4096 + if str(platform).lower() == "aws-bedrock-converse": + kwargs.setdefault("region_name", BEDROCK_CONVERSE_REGION) model = ModelFactory.create( model_platform=platform, model_type=mtype, @@ -334,6 +338,8 @@ def validate_model_with_details( model_config_dict = dict(model_config_dict or {}) if model_config_dict.get("max_tokens") is None: model_config_dict["max_tokens"] = 4096 + if str(model_platform).lower() == "aws-bedrock-converse": + kwargs.setdefault("region_name", BEDROCK_CONVERSE_REGION) model = ModelFactory.create( model_platform=model_platform, model_type=model_type, diff --git a/backend/app/model/chat.py b/backend/app/model/chat.py index 8f13a5aa4..1b9b25a8c 100644 --- a/backend/app/model/chat.py +++ b/backend/app/model/chat.py @@ -116,7 +116,7 @@ def get_uvx_env(self) -> dict[str, str]: ) def is_cloud(self): - return self.api_url is not None and "44.247.171.124" in self.api_url + return self.api_url is not None and "eigent-proxy" in self.api_url def file_save_path(self, path: str | None = None): email = re.sub(r'[\\/*?:"<>|\s]', "_", self.email.split("@")[0]).strip( diff --git a/backend/app/model/model_platform.py b/backend/app/model/model_platform.py index 3ee760c71..94ca0d918 100644 --- a/backend/app/model/model_platform.py +++ b/backend/app/model/model_platform.py @@ -24,6 +24,24 @@ "llama.cpp": "openai-compatible-model", } +# Bedrock Converse requires a region during model initialization. +BEDROCK_CONVERSE_REGION: Final[str] = "us-west-2" + + +def patch_bedrock_cloud_config( + api_url: str, extra_params: dict +) -> tuple[str, dict]: + """Patch API URL and extra_params for Bedrock Converse in cloud mode. + + Appends '/bedrock' to the proxy URL and defaults the region. + Returns the updated (api_url, extra_params). + """ + extra_params = dict(extra_params) + extra_params.setdefault("region_name", BEDROCK_CONVERSE_REGION) + if not api_url.rstrip("/").endswith("/bedrock"): + api_url = api_url + "/bedrock" + return api_url, extra_params + def normalize_model_platform(platform: str) -> str: """Normalize provider aliases to supported model platform names.""" diff --git a/backend/app/utils/single_agent_worker.py b/backend/app/utils/single_agent_worker.py index acd4b6ba4..d276326fd 100644 --- a/backend/app/utils/single_agent_worker.py +++ b/backend/app/utils/single_agent_worker.py @@ -12,10 +12,15 @@ # limitations under the License. # ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= +import asyncio import datetime import logging +from collections.abc import Awaitable, Callable -from camel.agents.chat_agent import AsyncStreamingChatAgentResponse +from camel.agents.chat_agent import ( + AsyncStreamingChatAgentResponse, + ChatAgentResponse, +) from camel.societies.workforce.prompts import PROCESS_TASK_PROMPT from camel.societies.workforce.single_agent_worker import ( SingleAgentWorker as BaseSingleAgentWorker, @@ -67,7 +72,13 @@ def __init__( self.worker = worker # change type hint async def _process_task( - self, task: Task, dependencies: list[Task], stream_callback=None + self, + task: Task, + dependencies: list[Task], + stream_callback: Callable[ + ["ChatAgentResponse"], Awaitable[None] | None + ] + | None = None, ) -> TaskState: r"""Processes a task with its dependencies using an efficient agent management system. @@ -146,6 +157,10 @@ async def _process_task( async for chunk in response: chunk_count += 1 last_chunk = chunk + if stream_callback: + maybe = stream_callback(chunk) + if asyncio.iscoroutine(maybe): + await maybe if chunk.msg and chunk.msg.content: accumulated_content += chunk.msg.content logger.info( @@ -186,6 +201,10 @@ async def _process_task( last_chunk = None async for chunk in response: last_chunk = chunk + if stream_callback: + maybe = stream_callback(chunk) + if asyncio.iscoroutine(maybe): + await maybe if chunk.msg: if chunk.msg.content: accumulated_content += chunk.msg.content diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 5a0724ace..948a46977 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.11,<3.12" dependencies = [ "pip>=23.0", - "camel-ai[eigent]==0.2.90a6", + "camel-ai[eigent]==0.2.90", "fastapi>=0.115.12", "fastapi-babel>=1.0.0", "uvicorn[standard]>=0.34.2", diff --git a/backend/tests/app/component/test_model_validation.py b/backend/tests/app/component/test_model_validation.py index a8c65d5f8..4dfa1de57 100644 --- a/backend/tests/app/component/test_model_validation.py +++ b/backend/tests/app/component/test_model_validation.py @@ -215,6 +215,25 @@ def test_create_agent_invalid_model_platform(): create_agent(model_platform=None, model_type="GPT_4O_MINI") +@pytest.mark.unit +@patch("app.component.model_validation.ModelFactory.create") +@patch("app.component.model_validation.ChatAgent") +def test_create_agent_hardcodes_bedrock_converse_region( + mock_chat_agent, mock_model_factory +): + """Test Bedrock Converse validation always uses the hardcoded region.""" + mock_model_factory.return_value = MagicMock() + mock_chat_agent.return_value = MagicMock() + + create_agent( + model_platform="aws-bedrock-converse", + model_type="anthropic.claude-3-5-sonnet", + api_key="test_key", + ) + + assert mock_model_factory.call_args.kwargs["region_name"] == "us-west-2" + + @pytest.mark.unit def test_validation_missing_model_type(): """Test validation with missing model type.""" diff --git a/backend/uv.lock b/backend/uv.lock index 8fbada652..7feab5347 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = "==3.11.*" resolution-markers = [ "sys_platform == 'win32'", @@ -242,7 +242,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiofiles", specifier = ">=24.1.0" }, - { name = "camel-ai", extras = ["eigent"], specifier = "==0.2.90a6" }, + { name = "camel-ai", extras = ["eigent"], specifier = "==0.2.90" }, { name = "debugpy", specifier = ">=1.8.17" }, { name = "fastapi", specifier = ">=0.115.12" }, { name = "fastapi-babel", specifier = ">=1.0.0" }, @@ -313,7 +313,7 @@ wheels = [ [[package]] name = "camel-ai" -version = "0.2.90a6" +version = "0.2.90" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astor" }, @@ -331,9 +331,9 @@ dependencies = [ { name = "tiktoken" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/52/5113d146328de8cb538f0616d0e7c65395dc52c6147a7b74d901978108da/camel_ai-0.2.90a6.tar.gz", hash = "sha256:9f1f537e49de690b03b4c110ace2ac17afa41663b108f65a0c0e609ef3b4eb27", size = 1206365, upload-time = "2026-03-13T14:49:21.5Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/b3/da958646c69b42cfafd3fc081b77bb19bb625773766da6ffa2d77c9c66e0/camel_ai-0.2.90.tar.gz", hash = "sha256:43f11673390cc8d4451d6b1bb2913ddef6131f411f47da02d16a3989c8096d02", size = 1212904, upload-time = "2026-03-22T08:37:04.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a2/fa855f44589640b8ec1a3c21767d4fdf23ab5918a9aa2a73f21d1f7c2e3b/camel_ai-0.2.90a6-py3-none-any.whl", hash = "sha256:5ccc9af05559af51e421eb9ac1f48b30950117c061dd62dd67cb3e5b13f42d89", size = 1690382, upload-time = "2026-03-13T14:49:18.718Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e2/7005080797edcc760dcf7695ece29f97e18189ea2c56cd7514592801f0c9/camel_ai-0.2.90-py3-none-any.whl", hash = "sha256:9998c434779a1a847d9ccddce1c069f22fb9667b19ba06a2452c479882169082", size = 1700705, upload-time = "2026-03-22T08:37:02.259Z" }, ] [package.optional-dependencies] diff --git a/src/components/WorkFlow/node.tsx b/src/components/WorkFlow/node.tsx index cd3208952..337914506 100644 --- a/src/components/WorkFlow/node.tsx +++ b/src/components/WorkFlow/node.tsx @@ -425,17 +425,17 @@ export function Node({ id, data }: NodeProps) { : 'w-[342px]' } ${ data.isEditMode ? 'h-full' : 'max-h-[calc(100vh-200px)]' - } rounded-xl border-worker-border-default bg-worker-surface-primary flex overflow-hidden border border-solid ${ + } flex overflow-hidden rounded-xl border border-solid border-worker-border-default bg-worker-surface-primary ${ getCurrentTask()?.activeAgent === id ? `${agentMap[data.type]?.borderColor} z-50` - : 'border-worker-border-default z-10' - } ease-in-out transition-all duration-300 ${ + : 'z-10 border-worker-border-default' + } transition-all duration-300 ease-in-out ${ (data.agent?.tasks?.length ?? 0) === 0 && 'opacity-30' }`} > -
-
-
+
+
+
-
+
@@ -460,7 +460,7 @@ export function Node({ id, data }: NodeProps) { - +
{ e.stopPropagation(); const newWorkerList = workerList.filter( @@ -496,7 +496,7 @@ export function Node({ id, data }: NodeProps) {
{/* {JSON.stringify(data.agent)} */} {toolkitLabels.map((toolkit, index) => ( @@ -506,7 +506,7 @@ export function Node({ id, data }: NodeProps) { ))}
{ chatStore.setActiveWorkspace( chatStore.activeTaskId as string, @@ -518,15 +518,15 @@ export function Node({ id, data }: NodeProps) { > {browserImages.length > 0 && (
{browserImages.map((img, index) => (
{data.type} @@ -536,7 +536,7 @@ export function Node({ id, data }: NodeProps) { (_, index) => (
) )} @@ -545,8 +545,8 @@ export function Node({ id, data }: NodeProps) { {data.type === 'document_agent' && data?.agent?.tasks && data.agent.tasks.length > 0 && ( -
-
+
+
@@ -554,14 +554,14 @@ export function Node({ id, data }: NodeProps) { {data.type === 'developer_agent' && terminalTasks.length > 0 && (
{terminalTasks.map((task) => (
-
+
@@ -570,7 +570,7 @@ export function Node({ id, data }: NodeProps) { (_, index) => (
) )} @@ -578,7 +578,7 @@ export function Node({ id, data }: NodeProps) { )}
{data.agent?.tasks && data.agent?.tasks.length > 0 && ( -
+
{/*
Subtasks
*/}
{ e.stopPropagation(); }} - className="scrollbar scrollbar-always-visible gap-2 px-3 pb-2 ease-out animate-in fade-in-0 slide-in-from-bottom-4 flex flex-col overflow-y-auto duration-500" + className="scrollbar scrollbar-always-visible flex flex-col gap-2 overflow-y-auto px-3 pb-2 duration-500 ease-out animate-in fade-in-0 slide-in-from-bottom-4" style={{ maxHeight: data.img && data.img.length > 0 @@ -662,7 +662,7 @@ export function Node({ id, data }: NodeProps) { } }} key={`taskList-${task.id}-${task.failure_count}`} - className={`gap-2 rounded-xl px-sm py-sm ease-in-out animate-in fade-in-0 slide-in-from-left-2 flex transition-all duration-300 ${ + className={`flex gap-2 rounded-xl px-sm py-sm transition-all duration-300 ease-in-out animate-in fade-in-0 slide-in-from-left-2 ${ task.reAssignTo ? 'bg-task-fill-warning' : task.status === TaskStatus.COMPLETED @@ -755,14 +755,14 @@ export function Node({ id, data }: NodeProps) { : task.status === TaskStatus.BLOCKED ? 'text-text-body' : 'text-text-primary' - } text-xs font-medium leading-13 pointer-events-auto text-wrap break-all whitespace-pre-line select-text`} + } pointer-events-auto select-text whitespace-pre-line text-wrap break-all text-xs font-medium leading-13`} > -
+
No. {getTaskId(task.id)}
{task.reAssignTo ? ( -
+
Reassigned to {task.reAssignTo}
) : ( @@ -784,11 +784,11 @@ export function Node({ id, data }: NodeProps) {
{task.content}
{task?.status === TaskStatus.RUNNING && ( -
+
{/* active toolkit */} {lastActiveToolkit?.toolkitStatus === AgentStatusValue.RUNNING && ( -
+
{getToolkitIcon( lastActiveToolkit.toolkitName ?? '' )} @@ -799,11 +799,11 @@ export function Node({ id, data }: NodeProps) { ].activeWorkspace ? '!w-[100px]' : '!w-[500px]' - } min-w-0 pt-1 text-xs leading-17 text-text-primary flex-shrink-0 flex-grow-0 overflow-hidden text-ellipsis whitespace-nowrap`} + } min-w-0 flex-shrink-0 flex-grow-0 overflow-hidden text-ellipsis whitespace-nowrap pt-1 text-xs leading-17 text-text-primary`} >
@@ -824,14 +824,14 @@ export function Node({ id, data }: NodeProps) { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: 24 }} transition={{ duration: 0.3, ease: 'easeIn' }} - className="gap-sm rounded-r-xl bg-worker-surface-secondary py-2 pl-sm flex w-[342px] shrink-0 flex-col overflow-hidden" + className="flex w-[342px] shrink-0 flex-col gap-sm overflow-hidden rounded-r-xl bg-worker-surface-secondary py-2 pl-sm" >
{ e.stopPropagation(); }} - className="scrollbar scrollbar-always-visible pr-sm max-h-[calc(100vh-200px)] overflow-y-scroll" + className="scrollbar scrollbar-always-visible max-h-[calc(100vh-200px)] overflow-y-scroll pr-sm" > {selectedTask && ( @@ -841,7 +841,7 @@ export function Node({ id, data }: NodeProps) { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -16 }} transition={{ duration: 0.25, ease: 'easeIn' }} - className="gap-sm flex w-full flex-col" + className="flex w-full flex-col gap-sm" > {selectedTask.toolkits && selectedTask.toolkits.length > 0 && @@ -851,7 +851,7 @@ export function Node({ id, data }: NodeProps) { {toolkit.toolkitName === 'notice' ? (
{/* first row: icon + toolkit name */} -
+
{toolkit.toolkitStatus === AgentStatusValue.RUNNING ? ( + {toolkit.toolkitName}
{/* second row: method + message */} -
-
+
+
{toolkit.toolkitMethods ? toolkit.toolkitMethods .charAt(0) @@ -923,10 +923,10 @@ export function Node({ id, data }: NodeProps) { : ''}
{toolkit.message} @@ -937,7 +937,7 @@ export function Node({ id, data }: NodeProps) { {toolkit.message && ( @@ -960,9 +960,9 @@ export function Node({ id, data }: NodeProps) { onWheel={(e) => { e.stopPropagation(); }} - className="group my-2 rounded-lg bg-surface-primary relative flex w-full flex-col" + className="group relative my-2 flex w-full flex-col rounded-lg bg-surface-primary" > -
+
Completion Report
diff --git a/src/lib/llm.ts b/src/lib/llm.ts index 65f4340e6..e06105bb0 100644 --- a/src/lib/llm.ts +++ b/src/lib/llm.ts @@ -136,9 +136,43 @@ export const INIT_PROVODERS: Provider[] = [ id: 'aws-bedrock', name: 'AWS Bedrock', apiKey: '', - apiHost: '', + apiHost: 'https://bedrock-mantle.us-east-1.api.aws/v1', description: 'AWS Bedrock model configuration.', - hostPlaceHolder: 'e.g. https://bedrock-runtime.{{region}}.amazonaws.com', + is_valid: false, + model_type: '', + }, + { + id: 'aws-bedrock-converse', + name: 'AWS Bedrock Converse', + apiKey: '', + apiHost: 'https://bedrock-runtime.us-east-1.amazonaws.com', + description: + 'AWS Bedrock Converse model configuration. Auth: API Key (Bearer Token), or Access Key ID + Secret Access Key (+Session Token).', + externalConfig: [ + { + key: 'region_name', + name: 'Region', + value: 'us-east-1', + }, + { + key: 'aws_access_key_id', + name: 'Access Key ID', + value: '', + secret: true, + }, + { + key: 'aws_secret_access_key', + name: 'Secret Access Key', + value: '', + secret: true, + }, + { + key: 'aws_session_token', + name: 'Session Token (Optional)', + value: '', + secret: true, + }, + ], is_valid: false, model_type: '', }, diff --git a/src/pages/Agents/Models.tsx b/src/pages/Agents/Models.tsx index 31a2dbacc..8e2c12859 100644 --- a/src/pages/Agents/Models.tsx +++ b/src/pages/Agents/Models.tsx @@ -119,12 +119,15 @@ export default function SettingModels() { } = useAuthStore(); const _navigate = useNavigate(); const { t } = useTranslation(); - const getValidateMessage = (res: any) => - res?.message ?? - res?.detail?.message ?? - res?.detail?.error?.message ?? - res?.error?.message ?? - t('setting.validate-failed'); + const getValidateMessage = (res: any): string => { + const msg = + res?.message ?? + res?.detail?.message ?? + res?.detail?.error?.message ?? + res?.error?.message ?? + t('setting.validate-failed'); + return typeof msg === 'string' ? msg : JSON.stringify(msg); + }; const [items, _setItems] = useState( INIT_PROVODERS.filter((p) => p.id !== 'local') ); @@ -144,6 +147,7 @@ export default function SettingModels() { const [showApiKey, setShowApiKey] = useState(() => INIT_PROVODERS.filter((p) => p.id !== 'local').map(() => false) ); + const [showSecret, setShowSecret] = useState>({}); const [loading, setLoading] = useState(null); const [errors, setErrors] = useState< { @@ -507,7 +511,7 @@ export default function SettingModels() { form[idx]; let hasError = false; const newErrors = [...errors]; - if (items[idx].id !== 'local') { + if (items[idx].id !== 'local' && items[idx].id !== 'aws-bedrock-converse') { if (!apiKey || apiKey.trim() === '') { newErrors[idx].apiKey = t('setting.api-key-can-not-be-empty'); hasError = true; @@ -544,7 +548,7 @@ export default function SettingModels() { const res = await fetchPost('/model/validate', { model_platform: item.id, model_type: form[idx].model_type, - api_key: form[idx].apiKey, + api_key: form[idx].apiKey || null, url: form[idx].apiHost, extra_params: external, }); @@ -1068,6 +1072,7 @@ export default function SettingModels() { grok: PROVIDER_AVATAR_URLS.grok, mistral: PROVIDER_AVATAR_URLS.mistral, 'aws-bedrock': bedrockImage, + 'aws-bedrock-converse': bedrockImage, azure: azureImage, 'openai-compatible-model': openaiImage, // Use OpenAI icon as fallback // Local models @@ -1474,8 +1479,32 @@ export default function SettingModels() { + ) : ( + + ) + ) : undefined + } + onBackIconClick={ + ec.secret + ? () => + setShowSecret((prev) => ({ + ...prev, + [`${idx}-${ecIdx}`]: !prev[`${idx}-${ecIdx}`], + })) + : undefined + } value={ec.value} onChange={(e) => { const v = e.target.value; diff --git a/src/store/chatStore.ts b/src/store/chatStore.ts index 919cbc9db..b00dc3d4b 100644 --- a/src/store/chatStore.ts +++ b/src/store/chatStore.ts @@ -811,7 +811,7 @@ const chatStore = (initial?: Partial) => model_platform: cloud_model_type.includes('gpt') ? 'openai' : cloud_model_type.includes('claude') - ? 'aws-bedrock' + ? 'aws-bedrock-converse' : cloud_model_type.includes('gemini') ? 'gemini' : 'openai-compatible-model', diff --git a/src/types/index.ts b/src/types/index.ts index feda06ebd..82eeea590 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -16,6 +16,8 @@ type externalConfig = { key: string; name: string; value: string; + placeholder?: string; + secret?: boolean; options?: { label: string; value: string;