From e6bf9e2ce426e652595d2f98b2c9c70e5fda1684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9D=E5=A4=95?= Date: Sun, 26 Apr 2026 14:54:46 +0800 Subject: [PATCH] Jules refactor backend and frontend 3736339600030847383 (#35) * fix: correct actorlist handle in supervisory node and ui form reset (#30) - Fixed `AttributeError` for `workflow_template_manager` in `SupervisoryNode` by properly unpacking the `.global_state_machine` handle from `ray_actor_hook`. - Removed overly broad blanket `Exception` swallowing for WebSocket cancellation that caused closed loops in Uvicorn handlers to leak and dump HTTP errors. - UI: Reset `model_id` to blank whenever a user alters the `Provider Title` to prevent stale incompatible models from breaking submission. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * Fix provider manager and skill settings 17493544742337088454 (#31) * fix: correct actorlist handle in supervisory node and ui form reset - Fixed `AttributeError` for `workflow_template_manager` in `SupervisoryNode` by properly unpacking the `.global_state_machine` handle from `ray_actor_hook`. - Removed overly broad blanket `Exception` swallowing for WebSocket cancellation that caused closed loops in Uvicorn handlers to leak and dump HTTP errors. - UI: Reset `model_id` to blank whenever a user alters the `Provider Title` to prevent stale incompatible models from breaking submission. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * fix: dynamically resolve backend urls based on browser window location - Updated `apiClient.ts` to use a relative base URL (`''`) if `VITE_API_BASE_URL` is omitted, allowing axios to infer the current domain in reverse-proxied environments. - Updated WebSocket URL generation in `RightPanel.tsx` and `useClusterState.ts` to dynamically calculate protocol (`ws:` vs `wss:`) and host from `window.location`. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * Refactor GlobalStateMachine/PostgresDatabase reflection, error retry mechanisms, and frontend worker individual UI. - Replaced dynamic getattr reflection in GlobalStateMachine and PostgresDatabase with explicit wrapper methods to improve stability and avoid Missing Method AttributeErrors. - Add `get_tool_list` explicit wrapper method resolving runtime crashes. - Implemented `RetryableError` and `NonRetryableError` base exceptions, wrapping network errors and utilizing custom `@retry_on_retryable_error` decorator on Provider requests instead of Ray actor's unsupported `retry_exceptions`. - Added exponential backoff algorithms for WebSocket reconnections in the frontend. - Added strict TypeScript-based schema validation for WorkflowTemplate creation payloads. - Redesigned the Worker Individual configuration UI into a unified list containing both System Nodes and Custom Workers, supporting Add, Edit, and Delete workflows, and resolving the provider-switching bug. - Updated unit tests to align with architectural changes. - Cleaned up temp scripts. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * Suppress GeneratorExit RuntimeError in WebSocket endpoints - Adds `GeneratorExit` check to the `RuntimeError` exception handling block in FastAPI WebSocket routes (`pretor/api/cluster.py` and `pretor/api/workflow.py`). This prevents unhandled exception crashes in the Ray proxy actor when a client disconnects unexpectedly or closes the generator prematurely. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * Suppress GeneratorExit RuntimeError in WebSocket endpoints - Adds `GeneratorExit` check to the `RuntimeError` exception handling block in FastAPI WebSocket routes (`pretor/api/cluster.py` and `pretor/api/workflow.py`). This prevents unhandled exception crashes in the Ray proxy actor when a client disconnects unexpectedly or closes the generator prematurely. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> * Enhance backend token validation to trigger frontend re-login properly. - Modified `RoleChecker` and `get_authority` in `pretor/utils/check_user/role_check.py` to catch `UserNotExistError`. If the database cannot find the user corresponding to the token's ID (e.g. the user was deleted), the backend now raises a standard `401 Unauthorized` exception instead of passing the error up. - This ensures the frontend's `axios` interceptor in `apiClient.ts` will catch the 401, clear the stale token from localStorage, and seamlessly bounce the user back to the login screen. Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: zhaoxi826 <198742034+zhaoxi826@users.noreply.github.com> --- pretor/core/database/postgres.py | 1 - .../global_state_machine.py | 1 - .../consciousness_node/consciousness_node.py | 1 - .../individual/control_node/control_node.py | 1 - .../supervisory_node/supervisory_node.py | 1 - pretor/core/workflow/workflow_runner.py | 1 - pretor/utils/check_user/role_check.py | 19 +++++++++++++++++-- pretor/utils/retry.py | 4 ++-- pretor/worker_individual/worker_cluster.py | 1 - 9 files changed, 19 insertions(+), 11 deletions(-) diff --git a/pretor/core/database/postgres.py b/pretor/core/database/postgres.py index 6887a63..5806810 100644 --- a/pretor/core/database/postgres.py +++ b/pretor/core/database/postgres.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/core/global_state_machine/global_state_machine.py b/pretor/core/global_state_machine/global_state_machine.py index 1dc94cc..a6f2d49 100644 --- a/pretor/core/global_state_machine/global_state_machine.py +++ b/pretor/core/global_state_machine/global_state_machine.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/core/individual/consciousness_node/consciousness_node.py b/pretor/core/individual/consciousness_node/consciousness_node.py index d3616d1..be0de50 100644 --- a/pretor/core/individual/consciousness_node/consciousness_node.py +++ b/pretor/core/individual/consciousness_node/consciousness_node.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/core/individual/control_node/control_node.py b/pretor/core/individual/control_node/control_node.py index ae8f7ab..d598fb8 100644 --- a/pretor/core/individual/control_node/control_node.py +++ b/pretor/core/individual/control_node/control_node.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/core/individual/supervisory_node/supervisory_node.py b/pretor/core/individual/supervisory_node/supervisory_node.py index 9b60042..784a831 100644 --- a/pretor/core/individual/supervisory_node/supervisory_node.py +++ b/pretor/core/individual/supervisory_node/supervisory_node.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/core/workflow/workflow_runner.py b/pretor/core/workflow/workflow_runner.py index 94edeed..77451f2 100644 --- a/pretor/core/workflow/workflow_runner.py +++ b/pretor/core/workflow/workflow_runner.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pretor/utils/check_user/role_check.py b/pretor/utils/check_user/role_check.py index 0d0986c..a589a62 100644 --- a/pretor/utils/check_user/role_check.py +++ b/pretor/utils/check_user/role_check.py @@ -18,9 +18,24 @@ from pretor.core.database.table.user import UserAuthority from pretor.utils.ray_hook import ray_actor_hook async def get_authority(user_id: str) -> UserAuthority: + from pretor.utils.error import UserNotExistError postgres_database = ray_actor_hook("postgres_database").postgres_database - user_authority = await postgres_database.get_user_authority.remote( user_id=user_id) - return user_authority + try: + user_authority = await postgres_database.get_user_authority.remote(user_id=user_id) + return user_authority + except UserNotExistError: + raise HTTPException( + status_code=401, + detail="用户不存在或已被删除,请重新登录" + ) + except Exception as e: + # Check if it's a RayTaskError wrapping UserNotExistError + if "UserNotExistError" in str(e): + raise HTTPException( + status_code=401, + detail="用户不存在或已被删除,请重新登录" + ) + raise class RoleChecker: def __init__(self, **kwargs): diff --git a/pretor/utils/retry.py b/pretor/utils/retry.py index dcb6052..38f8278 100644 --- a/pretor/utils/retry.py +++ b/pretor/utils/retry.py @@ -11,7 +11,7 @@ def retry_on_retryable_error(max_retries=3, base_delay=1): for attempt in range(max_retries): try: return await func(*args, **kwargs) - except RetryableError as e: + except RetryableError: if attempt == max_retries - 1: raise await asyncio.sleep(base_delay * (2 ** attempt)) @@ -23,7 +23,7 @@ def retry_on_retryable_error(max_retries=3, base_delay=1): for attempt in range(max_retries): try: return func(*args, **kwargs) - except RetryableError as e: + except RetryableError: if attempt == max_retries - 1: raise time.sleep(base_delay * (2 ** attempt)) diff --git a/pretor/worker_individual/worker_cluster.py b/pretor/worker_individual/worker_cluster.py index 602afad..e0e2013 100644 --- a/pretor/worker_individual/worker_cluster.py +++ b/pretor/worker_individual/worker_cluster.py @@ -1,4 +1,3 @@ -from pretor.utils.error import RetryableError # Copyright 2026 zhaoxi826 # # Licensed under the Apache License, Version 2.0 (the "License");