From ea780b3019e44fb4d17693b60d4c4cd32cd99bf8 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Mon, 9 Mar 2026 10:17:21 +0100 Subject: [PATCH 01/11] feat: add prompt caching support for different model platforms --- backend/app/agent/agent_model.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index 1b1fe860b..ac5486144 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -112,6 +112,15 @@ def agent_model( else: model_config[k] = v + # Auto-inject prompt caching based on model platform + model_platform_enum = ModelPlatformType( + effective_config["model_platform"].lower() + ) + if model_platform_enum == ModelPlatformType.ANTHROPIC: + model_config.setdefault("cache_control", "1h") + elif model_platform_enum == ModelPlatformType.OPENAI: + model_config.setdefault("prompt_cache_key", str(options.project_id)) + if agent_name == Agents.task_agent: model_config["stream"] = True if agent_name == Agents.browser_agent: From 9dfbf80eb69d1292de9b07e083aa90214e458ab4 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Tue, 10 Mar 2026 23:51:08 +0100 Subject: [PATCH 02/11] feat: implement support for AWS Bedrock Converse region and update related components --- backend/app/agent/agent_model.py | 32 +++++++--- backend/app/component/model_validation.py | 8 +++ backend/app/model/model_platform.py | 3 + backend/pyproject.toml | 2 +- .../app/component/test_model_validation.py | 19 ++++++ backend/uv.lock | 60 ++++++++++++++++--- src/lib/llm.ts | 2 +- src/pages/Agents/Models.tsx | 6 +- src/store/authStore.ts | 2 +- src/store/chatStore.ts | 2 +- 10 files changed, 115 insertions(+), 21 deletions(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index ac5486144..b1e3d376f 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 BEDROCK_CONVERSE_REGION from app.service.task import ActionCreateAgentData, Agents, get_task_lock from app.utils.event_loop_utils import _schedule_async_task @@ -80,6 +81,11 @@ def agent_model( for attr in config_attrs: effective_config[attr] = getattr(options, attr) extra_params = options.extra_params or {} + # Frontend does not provide Bedrock Converse region yet, so default it here. + if effective_config.get("model_platform") == "aws-bedrock-converse": + extra_params = dict(extra_params) + extra_params["region_name"] = BEDROCK_CONVERSE_REGION + effective_config["api_url"] = effective_config["api_url"] + "/bedrock" init_param_keys = { "api_version", "azure_ad_token", @@ -89,6 +95,7 @@ def agent_model( "client", "async_client", "azure_deployment_name", + "region_name", } init_params = {} @@ -113,13 +120,24 @@ def agent_model( model_config[k] = v # Auto-inject prompt caching based on model platform - model_platform_enum = ModelPlatformType( - effective_config["model_platform"].lower() - ) - if model_platform_enum == ModelPlatformType.ANTHROPIC: - model_config.setdefault("cache_control", "1h") - elif model_platform_enum == ModelPlatformType.OPENAI: - model_config.setdefault("prompt_cache_key", str(options.project_id)) + 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", "1h") + 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 diff --git a/backend/app/component/model_validation.py b/backend/app/component/model_validation.py index c8da48be8..223e320af 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 @@ -223,6 +225,9 @@ def create_agent( """ platform = model_platform mtype = model_type + # Keep validation aligned with runtime Bedrock Converse initialization. + if platform == "aws-bedrock-converse": + kwargs["region_name"] = BEDROCK_CONVERSE_REGION if mtype is None: raise ValueError(f"Invalid model_type: {model_type}") if platform is None: @@ -322,6 +327,9 @@ def validate_model_with_details( # Stage 2: Model Creation result.validation_stages[ValidationStage.MODEL_CREATION] = False try: + # Validation should use the same default region as normal agent startup. + if model_platform == "aws-bedrock-converse": + kwargs["region_name"] = BEDROCK_CONVERSE_REGION logger.debug( "Creating model", extra={"platform": model_platform, "model_type": model_type}, diff --git a/backend/app/model/model_platform.py b/backend/app/model/model_platform.py index 707d2b30c..824da19c9 100644 --- a/backend/app/model/model_platform.py +++ b/backend/app/model/model_platform.py @@ -23,6 +23,9 @@ "llama.cpp": "openai-compatible-model", } +# Bedrock Converse requires a region during model initialization. +BEDROCK_CONVERSE_REGION: Final[str] = "us-west-2" + def normalize_model_platform(platform: str) -> str: """Normalize provider aliases to supported model platform names.""" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 3d928b63c..e14cc710e 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.90a5", + "camel-ai[eigent] @ git+https://github.com/camel-ai/camel.git@async-support-for-aws-bedwork-converse-api", "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 6abf74d37..e9ad76c3d 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.90a5" }, + { name = "camel-ai", extras = ["eigent"], git = "https://github.com/camel-ai/camel.git?rev=async-support-for-aws-bedwork-converse-api" }, { name = "debugpy", specifier = ">=1.8.17" }, { name = "fastapi", specifier = ">=0.115.12" }, { name = "fastapi-babel", specifier = ">=1.0.0" }, @@ -283,10 +283,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] +[[package]] +name = "boto3" +version = "1.42.65" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/c9/8ff8a901cf62374f1289cf36391f855e1702c70f545c28d1b57608a84ff2/boto3-1.42.65.tar.gz", hash = "sha256:c740af6bdaebcc1a00f3827a5729050bf6fc820ee148bf7d06f28db11c80e2a1", size = 112805, upload-time = "2026-03-10T19:44:58.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/bb/ace5921655df51e3c9b787b3f0bd6aa25548e5cf1dabae02e53fa88f2d98/boto3-1.42.65-py3-none-any.whl", hash = "sha256:cc7f2e0aec6c68ee5b10232cf3e01326acf6100bc785a770385b61a0474b31f4", size = 140556, upload-time = "2026-03-10T19:44:55.433Z" }, +] + +[[package]] +name = "botocore" +version = "1.42.65" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/81/2c832e2117d24da4fe800861e8ddd19bbaa308623b1198eb2c2cc6fcd3d4/botocore-1.42.65.tar.gz", hash = "sha256:7d52c148df07f70c375eeda58f99b439c7c7836c25df74cccfba3bb6e12444d2", size = 14970239, upload-time = "2026-03-10T19:44:43.686Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/9e/2ca03a55408c0820d7f0a04ae52bc6dfc7e4fff1f007a90135a68e056c93/botocore-1.42.65-py3-none-any.whl", hash = "sha256:0283c332ce00cbd1b894e86b7bed89dd624a5ca3a4ee62ec4db3898d16652e98", size = 14644794, upload-time = "2026-03-10T19:44:37.442Z" }, +] + [[package]] name = "camel-ai" version = "0.2.90a5" -source = { registry = "https://pypi.org/simple" } +source = { git = "https://github.com/camel-ai/camel.git?rev=async-support-for-aws-bedwork-converse-api#3a0d46bd61ebb12dea3cf721c12407c7d87fa64b" } dependencies = [ { name = "astor" }, { name = "colorama" }, @@ -303,15 +331,12 @@ dependencies = [ { name = "tiktoken" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/ea/9daf0e01e4b88962bbcfbfaf8585452172c50c147b9d447096f77de87308/camel_ai-0.2.90a5.tar.gz", hash = "sha256:39284ce2b25f5cc43f5e2d3844b8e32136c3175de8e8b0fe68f2e52613a18af9", size = 1162881, upload-time = "2026-03-03T11:07:20.562Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/b7/84040f24ce8267d7b949b77a86ae12ce2e4373c9e6a8b1ef42af3f0f3823/camel_ai-0.2.90a5-py3-none-any.whl", hash = "sha256:2757ca62cdc4f9a9466bf1e78a360837954c721be21eb0c23d368a143f64ba3d", size = 1641236, upload-time = "2026-03-03T11:07:16.756Z" }, -] [package.optional-dependencies] eigent = [ { name = "anthropic" }, { name = "av" }, + { name = "boto3" }, { name = "datasets" }, { name = "docx" }, { name = "exa-py" }, @@ -1189,6 +1214,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212, upload-time = "2025-11-09T20:49:15.643Z" }, ] +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + [[package]] name = "jsonschema" version = "4.26.0" @@ -2375,6 +2409,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, ] +[[package]] +name = "s3transfer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, +] + [[package]] name = "scenedetect" version = "0.6.7.1" diff --git a/src/lib/llm.ts b/src/lib/llm.ts index 6072a5d95..362ad50a3 100644 --- a/src/lib/llm.ts +++ b/src/lib/llm.ts @@ -133,7 +133,7 @@ export const INIT_PROVODERS: Provider[] = [ model_type: '', }, { - id: 'aws-bedrock', + id: 'aws-bedrock-converse', name: 'AWS Bedrock', apiKey: '', apiHost: '', diff --git a/src/pages/Agents/Models.tsx b/src/pages/Agents/Models.tsx index d3fa52184..d5e4c79c0 100644 --- a/src/pages/Agents/Models.tsx +++ b/src/pages/Agents/Models.tsx @@ -494,7 +494,7 @@ export default function SettingModels() { { id: 'gpt-5.2', name: 'GPT-5.2' }, { id: 'gpt-5.4', name: 'GPT-5.4' }, { id: 'gpt-5-mini', name: 'GPT-5 Mini' }, - { id: 'claude-sonnet-4-5', name: 'Claude Sonnet 4-5' }, + { id: 'claude-sonnet-4-5-bedrock', name: 'Claude Sonnet 4-5' }, { id: 'minimax_m2_5', name: 'Minimax M2.5' }, ]; @@ -1062,7 +1062,7 @@ export default function SettingModels() { 'samba-nova': PROVIDER_AVATAR_URLS['samba-nova'], 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 @@ -1274,7 +1274,7 @@ export default function SettingModels() { {t('setting.gpt-5-mini-name')} - + {t('setting.claude-sonnet-4-5-name')} diff --git a/src/store/authStore.ts b/src/store/authStore.ts index 96f96fb76..8f1bfc3d7 100644 --- a/src/store/authStore.ts +++ b/src/store/authStore.ts @@ -25,7 +25,7 @@ type CloudModelType = | 'gemini-3-flash-preview' | 'gpt-4.1-mini' | 'gpt-4.1' - | 'claude-sonnet-4-5' + | 'claude-sonnet-4-5-bedrock' | 'gpt-5' | 'gpt-5.1' | 'gpt-5.2' diff --git a/src/store/chatStore.ts b/src/store/chatStore.ts index c059e94ce..09f96c079 100644 --- a/src/store/chatStore.ts +++ b/src/store/chatStore.ts @@ -652,7 +652,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', From 8b78266c9b45ef9bfc1b15610a149d777782fb7e Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Tue, 10 Mar 2026 23:51:28 +0100 Subject: [PATCH 03/11] update --- backend/app/agent/agent_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index b1e3d376f..9cd59fa2a 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -126,7 +126,7 @@ def agent_model( ) if model_platform_enum in { ModelPlatformType.ANTHROPIC, - ModelPlatformType.AWS_BEDROCK_CONVERSE + ModelPlatformType.AWS_BEDROCK_CONVERSE, }: model_config.setdefault("cache_control", "1h") elif model_platform_enum == ModelPlatformType.OPENAI: From 281b3a465b8155837d49e81297781817cc5fb72e Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Wed, 11 Mar 2026 00:16:09 +0100 Subject: [PATCH 04/11] feat: enhance AWS Bedrock Converse support with region handling and URL adjustments --- backend/app/agent/factory/mcp.py | 19 +++++++++++++------ backend/app/component/model_validation.py | 4 ++++ backend/app/utils/single_agent_worker.py | 23 +++++++++++++++++++++-- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/backend/app/agent/factory/mcp.py b/backend/app/agent/factory/mcp.py index 4f1dc86ab..6d4123130 100644 --- a/backend/app/agent/factory/mcp.py +++ b/backend/app/agent/factory/mcp.py @@ -21,6 +21,7 @@ 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 BEDROCK_CONVERSE_REGION from app.service.task import ActionCreateAgentData, Agents, get_task_lock @@ -73,6 +74,16 @@ 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": + extra_params["region_name"] = BEDROCK_CONVERSE_REGION + api_url = api_url + "/bedrock" + return ListenChatAgent( options.project_id, Agents.mcp_agent, @@ -81,7 +92,7 @@ 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, + url=api_url, model_config_dict=( { "user": str(options.project_id), @@ -90,11 +101,7 @@ async def mcp_agent(options: Chat): else 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 223e320af..4d0fc7c86 100644 --- a/backend/app/component/model_validation.py +++ b/backend/app/component/model_validation.py @@ -228,6 +228,8 @@ def create_agent( # Keep validation aligned with runtime Bedrock Converse initialization. if platform == "aws-bedrock-converse": kwargs["region_name"] = BEDROCK_CONVERSE_REGION + if url: + url = url + "/bedrock" if mtype is None: raise ValueError(f"Invalid model_type: {model_type}") if platform is None: @@ -330,6 +332,8 @@ def validate_model_with_details( # Validation should use the same default region as normal agent startup. if model_platform == "aws-bedrock-converse": kwargs["region_name"] = BEDROCK_CONVERSE_REGION + if url: + url = url + "/bedrock" logger.debug( "Creating model", extra={"platform": model_platform, "model_type": model_type}, diff --git a/backend/app/utils/single_agent_worker.py b/backend/app/utils/single_agent_worker.py index dd96b832b..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] + 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 From 7ab75f4bf9d2c1b24d5173e97caee9a66859d2f4 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Thu, 12 Mar 2026 09:39:47 +0100 Subject: [PATCH 05/11] feat: update Claude Sonnet model identifier by removing 'bedrock' suffix --- src/pages/Agents/Models.tsx | 4 ++-- src/store/authStore.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/Agents/Models.tsx b/src/pages/Agents/Models.tsx index d5e4c79c0..6b86db767 100644 --- a/src/pages/Agents/Models.tsx +++ b/src/pages/Agents/Models.tsx @@ -494,7 +494,7 @@ export default function SettingModels() { { id: 'gpt-5.2', name: 'GPT-5.2' }, { id: 'gpt-5.4', name: 'GPT-5.4' }, { id: 'gpt-5-mini', name: 'GPT-5 Mini' }, - { id: 'claude-sonnet-4-5-bedrock', name: 'Claude Sonnet 4-5' }, + { id: 'claude-sonnet-4-5', name: 'Claude Sonnet 4-5' }, { id: 'minimax_m2_5', name: 'Minimax M2.5' }, ]; @@ -1274,7 +1274,7 @@ export default function SettingModels() { {t('setting.gpt-5-mini-name')} - + {t('setting.claude-sonnet-4-5-name')} diff --git a/src/store/authStore.ts b/src/store/authStore.ts index 8f1bfc3d7..96f96fb76 100644 --- a/src/store/authStore.ts +++ b/src/store/authStore.ts @@ -25,7 +25,7 @@ type CloudModelType = | 'gemini-3-flash-preview' | 'gpt-4.1-mini' | 'gpt-4.1' - | 'claude-sonnet-4-5-bedrock' + | 'claude-sonnet-4-5' | 'gpt-5' | 'gpt-5.1' | 'gpt-5.2' From e8acc6ceb04efd4872453bf26a1c1ea2ad4384bc Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Wed, 25 Mar 2026 13:24:04 +0100 Subject: [PATCH 06/11] feat: implement cloud configuration for AWS Bedrock Converse models --- backend/app/agent/agent_model.py | 20 +++++++++---- backend/app/agent/factory/mcp.py | 9 +++--- backend/app/component/model_validation.py | 11 ------- backend/app/model/model_platform.py | 13 +++++++++ src/lib/llm.ts | 35 ++++++++++++++++++++++- src/pages/Agents/Models.tsx | 4 ++- src/types/index.ts | 1 + 7 files changed, 70 insertions(+), 23 deletions(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index 9cd59fa2a..46a830228 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -24,7 +24,7 @@ from app.agent.listen_chat_agent import ListenChatAgent, logger from app.model.chat import AgentModelConfig, Chat -from app.model.model_platform import BEDROCK_CONVERSE_REGION +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 @@ -81,11 +81,16 @@ def agent_model( for attr in config_attrs: effective_config[attr] = getattr(options, attr) extra_params = options.extra_params or {} - # Frontend does not provide Bedrock Converse region yet, so default it here. - if effective_config.get("model_platform") == "aws-bedrock-converse": - extra_params = dict(extra_params) - extra_params["region_name"] = BEDROCK_CONVERSE_REGION - effective_config["api_url"] = effective_config["api_url"] + "/bedrock" + # 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", @@ -96,6 +101,9 @@ def agent_model( "async_client", "azure_deployment_name", "region_name", + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token", } init_params = {} diff --git a/backend/app/agent/factory/mcp.py b/backend/app/agent/factory/mcp.py index 6d4123130..9b92c90df 100644 --- a/backend/app/agent/factory/mcp.py +++ b/backend/app/agent/factory/mcp.py @@ -21,7 +21,7 @@ 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 BEDROCK_CONVERSE_REGION +from app.model.model_platform import patch_bedrock_cloud_config from app.service.task import ActionCreateAgentData, Agents, get_task_lock @@ -80,9 +80,10 @@ async def mcp_agent(options: Chat): if k not in ["model_platform", "model_type", "api_key", "url"] } api_url = options.api_url - if options.model_platform == "aws-bedrock-converse": - extra_params["region_name"] = BEDROCK_CONVERSE_REGION - api_url = api_url + "/bedrock" + if options.model_platform == "aws-bedrock-converse" and options.is_cloud(): + api_url, extra_params = patch_bedrock_cloud_config( + api_url, extra_params + ) return ListenChatAgent( options.project_id, diff --git a/backend/app/component/model_validation.py b/backend/app/component/model_validation.py index 4d0fc7c86..963e0ffea 100644 --- a/backend/app/component/model_validation.py +++ b/backend/app/component/model_validation.py @@ -19,7 +19,6 @@ 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") @@ -225,11 +224,6 @@ def create_agent( """ platform = model_platform mtype = model_type - # Keep validation aligned with runtime Bedrock Converse initialization. - if platform == "aws-bedrock-converse": - kwargs["region_name"] = BEDROCK_CONVERSE_REGION - if url: - url = url + "/bedrock" if mtype is None: raise ValueError(f"Invalid model_type: {model_type}") if platform is None: @@ -329,11 +323,6 @@ def validate_model_with_details( # Stage 2: Model Creation result.validation_stages[ValidationStage.MODEL_CREATION] = False try: - # Validation should use the same default region as normal agent startup. - if model_platform == "aws-bedrock-converse": - kwargs["region_name"] = BEDROCK_CONVERSE_REGION - if url: - url = url + "/bedrock" logger.debug( "Creating model", extra={"platform": model_platform, "model_type": model_type}, diff --git a/backend/app/model/model_platform.py b/backend/app/model/model_platform.py index b536adf62..b9a42fc97 100644 --- a/backend/app/model/model_platform.py +++ b/backend/app/model/model_platform.py @@ -28,6 +28,19 @@ 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) + return api_url + "/bedrock", extra_params + + def normalize_model_platform(platform: str) -> str: """Normalize provider aliases to supported model platform names.""" return PLATFORM_ALIAS_MAPPING.get(platform, platform) diff --git a/src/lib/llm.ts b/src/lib/llm.ts index 6809820c5..a6908aaf7 100644 --- a/src/lib/llm.ts +++ b/src/lib/llm.ts @@ -133,7 +133,7 @@ export const INIT_PROVODERS: Provider[] = [ model_type: '', }, { - id: 'aws-bedrock-converse', + id: 'aws-bedrock', name: 'AWS Bedrock', apiKey: '', apiHost: '', @@ -142,6 +142,39 @@ export const INIT_PROVODERS: Provider[] = [ is_valid: false, model_type: '', }, + { + id: 'aws-bedrock-converse', + name: 'AWS Bedrock Converse', + apiKey: '', + apiHost: '', + description: + 'AWS Bedrock Converse model configuration. Auth: API Key (Bearer Token), or Access Key ID + Secret Access Key (+Session Token).', + hostPlaceHolder: 'e.g. https://bedrock-runtime.{{region}}.amazonaws.com', + externalConfig: [ + { + key: 'region_name', + name: 'Region', + value: '', + }, + { + key: 'aws_access_key_id', + name: 'Access Key ID', + value: '', + }, + { + key: 'aws_secret_access_key', + name: 'Secret Access Key', + value: '', + }, + { + key: 'aws_session_token', + name: 'Session Token (Optional)', + value: '', + }, + ], + is_valid: false, + model_type: '', + }, { id: 'azure', name: 'Azure', diff --git a/src/pages/Agents/Models.tsx b/src/pages/Agents/Models.tsx index e979819e5..b6943069b 100644 --- a/src/pages/Agents/Models.tsx +++ b/src/pages/Agents/Models.tsx @@ -507,7 +507,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; @@ -1067,6 +1067,7 @@ export default function SettingModels() { 'samba-nova': PROVIDER_AVATAR_URLS['samba-nova'], 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 @@ -1474,6 +1475,7 @@ export default function SettingModels() { Date: Wed, 25 Mar 2026 13:24:23 +0100 Subject: [PATCH 07/11] feat: implement cloud configuration for AWS Bedrock Converse models --- backend/app/agent/agent_model.py | 6 ++---- backend/app/component/model_validation.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index 46a830228..00b49d0b5 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -86,10 +86,8 @@ def agent_model( 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 - ) + effective_config["api_url"], extra_params = patch_bedrock_cloud_config( + effective_config["api_url"], extra_params ) init_param_keys = { "api_version", diff --git a/backend/app/component/model_validation.py b/backend/app/component/model_validation.py index 963e0ffea..c8da48be8 100644 --- a/backend/app/component/model_validation.py +++ b/backend/app/component/model_validation.py @@ -19,7 +19,6 @@ from camel.agents import ChatAgent from camel.models import ModelFactory, ModelProcessingError - logger = logging.getLogger("model_validation") # Expected result from tool execution for validation From 77b19a4324cf86ec944ed238e01e17ebfd931789 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Thu, 26 Mar 2026 11:01:32 +0100 Subject: [PATCH 08/11] feat: update AWS Bedrock configurations and enhance validation message handling --- src/lib/llm.ts | 11 +++++----- src/pages/Agents/Models.tsx | 41 ++++++++++++++++++++++++++++++------- src/types/index.ts | 1 + 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/lib/llm.ts b/src/lib/llm.ts index a6908aaf7..e06105bb0 100644 --- a/src/lib/llm.ts +++ b/src/lib/llm.ts @@ -136,9 +136,8 @@ 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: '', }, @@ -146,30 +145,32 @@ export const INIT_PROVODERS: Provider[] = [ id: 'aws-bedrock-converse', name: 'AWS Bedrock Converse', apiKey: '', - apiHost: '', + 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).', - hostPlaceHolder: 'e.g. https://bedrock-runtime.{{region}}.amazonaws.com', externalConfig: [ { key: 'region_name', name: 'Region', - value: '', + 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, diff --git a/src/pages/Agents/Models.tsx b/src/pages/Agents/Models.tsx index b6943069b..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< { @@ -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, }); @@ -1475,9 +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/types/index.ts b/src/types/index.ts index c0ac53c63..82eeea590 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -17,6 +17,7 @@ type externalConfig = { name: string; value: string; placeholder?: string; + secret?: boolean; options?: { label: string; value: string; From 892d1058c45f520113c1127b87d2c86154572742 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Thu, 26 Mar 2026 12:56:53 +0100 Subject: [PATCH 09/11] fix: update cloud API URL check to use 'eigent-proxy' --- backend/app/model/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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( From 38364c5a1c06914ad3e9c94d2b4746fcbf6425b2 Mon Sep 17 00:00:00 2001 From: Xiangyu Shi Date: Mon, 13 Apr 2026 19:13:24 +0800 Subject: [PATCH 10/11] fix(agent_model): update cache control duration for specific model platforms --- backend/app/agent/agent_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index 08e805064..f1f8ce634 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -134,7 +134,7 @@ def agent_model( ModelPlatformType.ANTHROPIC, ModelPlatformType.AWS_BEDROCK_CONVERSE, }: - model_config.setdefault("cache_control", "1h") + model_config.setdefault("cache_control", "5m") elif model_platform_enum == ModelPlatformType.OPENAI: model_config.setdefault( "prompt_cache_key", str(options.project_id) From 42f491ebd6b35cd7f3b39919ef99e0257a184f60 Mon Sep 17 00:00:00 2001 From: Wendong-Fan Date: Mon, 13 Apr 2026 20:40:04 +0800 Subject: [PATCH 11/11] minor enhance --- backend/app/agent/agent_model.py | 4 +-- backend/app/agent/factory/mcp.py | 31 ++++++++++++++++++----- backend/app/component/model_validation.py | 6 +++++ backend/app/model/model_platform.py | 4 ++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/backend/app/agent/agent_model.py b/backend/app/agent/agent_model.py index f1f8ce634..b7078a146 100644 --- a/backend/app/agent/agent_model.py +++ b/backend/app/agent/agent_model.py @@ -169,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 9b92c90df..f31cefd8e 100644 --- a/backend/app/agent/factory/mcp.py +++ b/backend/app/agent/factory/mcp.py @@ -12,9 +12,11 @@ # 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 @@ -85,6 +87,27 @@ async def mcp_agent(options: Chat): 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, @@ -94,13 +117,7 @@ async def mcp_agent(options: Chat): model_type=options.model_type, api_key=options.api_key, url=api_url, - model_config_dict=( - { - "user": str(options.project_id), - } - if options.is_cloud() - else None - ), + model_config_dict=model_config_dict or None, timeout=600, # 10 minutes **extra_params, ), 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/model_platform.py b/backend/app/model/model_platform.py index b9a42fc97..94ca0d918 100644 --- a/backend/app/model/model_platform.py +++ b/backend/app/model/model_platform.py @@ -38,7 +38,9 @@ def patch_bedrock_cloud_config( """ extra_params = dict(extra_params) extra_params.setdefault("region_name", BEDROCK_CONVERSE_REGION) - return api_url + "/bedrock", extra_params + if not api_url.rstrip("/").endswith("/bedrock"): + api_url = api_url + "/bedrock" + return api_url, extra_params def normalize_model_platform(platform: str) -> str: