Repository navigation
fix: 修复无法使用自定义镜像以及自定义镜像无法校验的问题 #866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 27 commits
3b9f692
0a8526f
ed8d9f0
123705f
1c6babd
12e5b6e
5a85836
4744710
0d283a5
b3deb29
0c66656
a137ef1
5a5bd72
0141f64
3cf43c0
dd1d11a
5f3baaa
ff1b9ac
1cf7b3b
7df10a1
d96baae
ca93caf
9c6dbef
4ed6a32
cc84293
6f481f7
ca54d3a
d08b604
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -310,6 +310,15 @@ def _execute_in_existing_container( | |
| context=f"existing container: {executor_name}", | ||
| ) | ||
|
|
||
| def _should_keep_failed_validation_container(self, task: Dict[str, Any]) -> bool: | ||
| """Whether to keep failed validation containers for debugging.""" | ||
| is_validation_task = get_metadata_field(task, "type") == "validation" | ||
| if not is_validation_task: | ||
| return False | ||
|
|
||
| keep_flag = os.getenv("VALIDATION_KEEP_FAILED_CONTAINER", "false").lower() | ||
| return keep_flag in ("1", "true", "yes", "on") | ||
|
|
||
| def _get_container_port( | ||
| self, executor_name: str | ||
| ) -> tuple[Optional[int], Optional[str]]: | ||
|
|
@@ -375,13 +384,20 @@ def _create_new_container( | |
| self.dispatch_task_to_instance(task, executor_name, ready_info) | ||
| except Exception: | ||
| # Avoid leaking an idle container when initial dispatch fails. | ||
| try: | ||
| delete_container(executor_name) | ||
| except Exception as cleanup_error: | ||
| logger.warning( | ||
| f"Failed to cleanup container {executor_name} after " | ||
| f"initial dispatch failure: {cleanup_error}" | ||
| if self._should_keep_failed_validation_container(task): | ||
| logger.info( | ||
| "Keeping failed validation container %s for debugging " | ||
| "(VALIDATION_KEEP_FAILED_CONTAINER=true)", | ||
| executor_name, | ||
| ) | ||
| else: | ||
| try: | ||
| delete_container(executor_name) | ||
| except Exception as cleanup_error: | ||
| logger.warning( | ||
| f"Failed to cleanup container {executor_name} after " | ||
| f"initial dispatch failure: {cleanup_error}" | ||
| ) | ||
| raise | ||
|
|
||
| def create_instance( | ||
|
|
@@ -588,18 +604,35 @@ def _dispatch_initial_task_to_new_container( | |
| Raises: | ||
| RuntimeError: If request dispatch fails after retries | ||
| """ | ||
| max_retries = max( | ||
| int(os.getenv("EXECUTOR_INITIAL_DISPATCH_MAX_RETRIES", "3")), | ||
| 1, | ||
| ) | ||
| retry_interval = max( | ||
| float(os.getenv("EXECUTOR_INITIAL_DISPATCH_RETRY_INTERVAL", "1")), | ||
| 0, | ||
| ) | ||
| request_timeout = max( | ||
| float(os.getenv("EXECUTOR_INITIAL_DISPATCH_TIMEOUT", "10")), | ||
| 0.1, | ||
| ) | ||
| is_validation_task = get_metadata_field(task, "type") == "validation" | ||
| if is_validation_task: | ||
| # Validation tasks may spend longer in start callback path. | ||
| # Use dedicated limits to avoid false timeout and duplicate dispatches. | ||
| max_retries = max( | ||
| int(os.getenv("VALIDATION_INITIAL_DISPATCH_MAX_RETRIES", "1")), | ||
| 1, | ||
| ) | ||
| retry_interval = max( | ||
| float(os.getenv("VALIDATION_INITIAL_DISPATCH_RETRY_INTERVAL", "1")), | ||
| 0, | ||
| ) | ||
| request_timeout = max( | ||
| float(os.getenv("VALIDATION_INITIAL_DISPATCH_TIMEOUT", "60")), | ||
| 0.1, | ||
| ) | ||
| else: | ||
| max_retries = max( | ||
| int(os.getenv("EXECUTOR_INITIAL_DISPATCH_MAX_RETRIES", "3")), | ||
| 1, | ||
| ) | ||
| retry_interval = max( | ||
| float(os.getenv("EXECUTOR_INITIAL_DISPATCH_RETRY_INTERVAL", "1")), | ||
| 0, | ||
| ) | ||
| request_timeout = max( | ||
| float(os.getenv("EXECUTOR_INITIAL_DISPATCH_TIMEOUT", "10")), | ||
| 0.1, | ||
| ) | ||
| last_error = "unknown error" | ||
|
|
||
| for attempt in range(1, max_retries + 1): | ||
|
|
@@ -727,14 +760,21 @@ def _check_container_health( | |
| ) | ||
|
|
||
| # Clean up the failed container | ||
| try: | ||
| self.subprocess.run( | ||
| ["docker", "rm", "-f", executor_name], | ||
| capture_output=True, | ||
| timeout=10, | ||
| if self._should_keep_failed_validation_container(task): | ||
| logger.info( | ||
| "Keeping exited validation container %s for debugging " | ||
| "(VALIDATION_KEEP_FAILED_CONTAINER=true)", | ||
| executor_name, | ||
| ) | ||
| except Exception: | ||
| pass | ||
| else: | ||
| try: | ||
| self.subprocess.run( | ||
| ["docker", "rm", "-f", executor_name], | ||
| capture_output=True, | ||
| timeout=10, | ||
| ) | ||
| except Exception: | ||
| pass | ||
|
Comment on lines
+770
to
+777
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log failed cleanup of exited validation containers.
🧰 Tools🪛 Ruff (0.15.7)[error] 776-777: (S110) [warning] 776-776: Do not catch blind exception: (BLE001) 🤖 Prompt for AI Agents |
||
|
|
||
| # Raise exception to mark task as failed | ||
| raise RuntimeError(f"Container exited immediately: {error_msg}") | ||
|
|
@@ -1005,9 +1045,16 @@ def _add_heartbeat_env_vars(self, cmd: List[str], task: Dict[str, Any]) -> None: | |
| cmd: Docker command list to extend | ||
| task: Task dictionary containing task info and sandbox_metadata | ||
| """ | ||
| # Skip validation tasks - they are short-lived and don't need heartbeat | ||
| task_type = get_metadata_field(task, "type", "online") | ||
| if task_type == "validation": | ||
| is_validation = task_type == "validation" | ||
|
|
||
| # For validation tasks, only set EXECUTOR_MANAGER_HEARTBEAT_BASE_URL for callback | ||
| # Skip other heartbeat settings as validation tasks are short-lived | ||
| if is_validation: | ||
| callback_url = build_callback_url(task) | ||
| if callback_url and "/callback" in callback_url: | ||
| base_url = callback_url.replace("/callback", "") | ||
| cmd.extend(["-e", f"EXECUTOR_MANAGER_HEARTBEAT_BASE_URL={base_url}"]) | ||
| return | ||
|
|
||
| is_sandbox = task_type == "sandbox" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 2093
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 2748
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 1108
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 603
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 42
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 42
🏁 Script executed:
# Check imports at top of file to understand what's available head -n 50 backend/app/api/endpoints/adapter/shells.pyRepository: wecode-ai/Wegent
Length of output: 1529
🏁 Script executed:
Repository: wecode-ai/Wegent
Length of output: 2112
Track the delayed cleanup task instead of dropping it.
This coroutine can spend time inside
asyncio.sleep()with no strong reference. If the task is garbage collected or loses its reference, the cleanup will never execute and validation containers will remain on the executor. Keep pending cleanup tasks in a set or another lifecycle-managed runner, similar to patterns used elsewhere in the codebase likeself._refresh_task = asyncio.create_task(...).Also applies to lines 1022-1028.
🧰 Tools
🪛 Ruff (0.15.7)
[warning] 997-997: Store a reference to the return value of
asyncio.create_task(RUF006)
🤖 Prompt for AI Agents