refactor: establish Brain-centered architecture and frontend/backend separation foundations#1536
refactor: establish Brain-centered architecture and frontend/backend separation foundations#1536
Conversation
bytecii
left a comment
There was a problem hiding this comment.
In general the design is ok. But IMO we should refactor and put things to different PRs, otherwise it's hard to review.
| has_browser: bool = False | ||
| """browser hand: can control CDP browser""" | ||
|
|
||
| filesystem_scope: str = "full" |
There was a problem hiding this comment.
Maybe make full etc as the strenum types?
| """workspace root path""" | ||
|
|
||
| deployment_type: str = "local" | ||
| """deployment type (for logging): local | cloud_vm | sandbox | docker""" |
There was a problem hiding this comment.
what's the cloud vm? just curious
There was a problem hiding this comment.
I think she meant a full virtual machine remotely; not just in docker.
| if in_docker: | ||
| logger.info("Brain running in Docker, using limited capabilities") | ||
| deployment = "docker" | ||
| caps = BrainCapabilities( |
There was a problem hiding this comment.
QQ should this be the HandCapabilities instead? for example browser is hand's capability?
| from app.hands.interface import IHands | ||
|
|
||
| # Terminal command allowlist for sandbox | ||
| SANDBOX_TERMINAL_ALLOWLIST = frozenset( |
There was a problem hiding this comment.
Seems that this is unused?
| @abstractmethod | ||
| async def acquire( | ||
| self, | ||
| resource_type: str, |
There was a problem hiding this comment.
Make resource type to string enum?
| from app.hands.interface import IHands | ||
|
|
||
|
|
||
| class RemoteHands(IHands): |
There was a problem hiding this comment.
Maybe we can put all Hands to a hands subfolder and import it to the hands/init.py
|
|
||
|
|
||
| class IHardwareBridge(ABC): | ||
| """Hardware capability bridge. Only Desktop has implementation, others use NullBridge""" |
There was a problem hiding this comment.
So should we implement this for the desktop?
| from app.hands.capabilities import detect_capabilities | ||
| from app.hands.environment_hands import EnvironmentHands | ||
|
|
||
| logger = logging.getLogger("hands.resolver") |
There was a problem hiding this comment.
Let's use the following
| logger = logging.getLogger("hands.resolver") | |
| logger = logging.getLogger(__name__) |
| # TODO(multi-tenant): os.environ is global – concurrent sessions overwrite | ||
| # each other's API keys, file paths, and browser ports. Pass these values | ||
| # through Chat / request context instead of mutating the process environment. | ||
| os.environ["file_save_path"] = data.file_save_path() |
There was a problem hiding this comment.
to my understanding as the brain in the server, will it has some problems if we set env as previously just in desktop?
There was a problem hiding this comment.
Yes, It need redesign if we deploy the brain in the server, can use S3 instead of it.
| if hands is not None: | ||
| if ( | ||
| item in TERMINAL_DEPENDENT_TOOLKITS | ||
| and not hands.can_execute_terminal() | ||
| ): | ||
| logger.info( | ||
| f"Skipping {item} for {agent_name}: no terminal hand" | ||
| ) | ||
| continue | ||
| if ( | ||
| item in BROWSER_DEPENDENT_TOOLKITS | ||
| and not hands.can_use_browser() | ||
| ): | ||
| logger.info( | ||
| f"Skipping {item} for {agent_name}: no browser hand" | ||
| ) | ||
| continue |
There was a problem hiding this comment.
I think we can simplify this?
| workspace_root: str = "~/.eigent/workspace" | ||
| """workspace root path""" |
There was a problem hiding this comment.
Can we convert this to PATH type instead? Resolving the path might cause issues down the line.
| @router.get("/files") | ||
| async def list_project_files( | ||
| project_id: str = Query(..., description="Project ID"), | ||
| email: str = Query(..., description="User email"), | ||
| task_id: str | None = Query( | ||
| None, description="Optional task ID to scope listing" | ||
| ), | ||
| ) -> list[dict]: | ||
| """ |
There was a problem hiding this comment.
Not sure if its me; my task was to write "hello world" file. The file is generated, but the route returned
/files/stream?path=task_1776131785382-755%5Chello_world.txt&project_id=1776121925475-837&email=ahmed%40gmail%20com
- Thus if you try to click the file title in Eigent, it doesn't open the file. The file rendering is not there too.
- Lastly just a small comment, previously we were fetching "All project files", thus it appeared having flat hierarchy. But now they are nested ; just a small comment.
| self.workspace_root = Path(workspace_root).expanduser() | ||
|
|
||
| def read_file(self, path: str) -> str: | ||
| resolved = self._ensure_in_workspace(path) |
There was a problem hiding this comment.
Actually, a neat idea (if we don't mind the bandwidth) is to just send base64 strings as attachments. Previously the /chat sends attaches[] with local file paths. Now it is:
1. upload file separately
2. get file_id
3. send the message with file references
We can instead attach a base64 string, then decode it back then save it in whatever the backend is hosted (vm, docker or others) or upload it in the background. This way we don't need to have separate steps; for <10mb resources.
ref: https://github.com/a7m-1st/medigent/blob/main/backend/app/utils/file_utils.py
| def list_dir(self, path: str) -> list[str]: | ||
| return [p.name for p in Path(path).iterdir()] | ||
|
|
There was a problem hiding this comment.
Rereading from disk (recursively) using python is expensive. Especially while SSE running, the python thread gets blocked thus waiting for some time before response. We mitigated this issue by migrating to websockets: ref: https://github.com/a7m-1st/medigent/blob/main/backend/app/controller/session_controller.py
Also Consider caching the get all files response unless there is a change.
ref: https://github.com/a7m-1st/medigent/blob/c32f3cf8cad1daa4fccb80085a9668286628f2f8/backend/app/controller/chat_controller.py#L453
There was a problem hiding this comment.
For the first issue; there should be a better solution.. websockets just supressed the problem it didn't fix it.
Related Issue
Closes #
Description
This PR delivers the current Brain + Web separation milestone.
It continues the ongoing refactor toward a Brain-centered architecture, where runtime capabilities are determined by the Brain environment rather than by client type. The goal of this phase is to preserve the existing Desktop experience while making
Web + Local Braina supported path built on shared, Brain-facing abstractions instead of Electron-specific assumptions.Within that scope, this PR has three primary outcomes:
Concretely, this PR moves the codebase further toward the target architecture described in the refactor work:
Notable user-facing improvements included in this PR:
Browser > CDP Browser Connectionnow works indev:webby using Brain APIs in web modeThis PR should be reviewed as a refactor milestone rather than as a collection of isolated bug fixes. The fixes included here are intentionally integrated into the Brain/Web separation work so the resulting behavior stays aligned with the target architecture.
Out of scope for this PR:
Testing Evidence (REQUIRED)
What is the purpose of this pull request?
Contribution Guidelines Acknowledgement