Bläddra i källkod

更新代码架构 文件修改、CodeActAgent

mrh 11 månader sedan
förälder
incheckning
fe64adf77c
2 ändrade filer med 282 tillägg och 35 borttagningar
  1. 9 0
      dev.md
  2. 273 35
      代码架构.md

+ 9 - 0
dev.md

@@ -1,4 +1,13 @@
 # 代码开发
+## aider
+```shell
+conda activate aider
+export OPENAI_API_BASE="https://aiapi.magong.site/v1"
+export OPENAI_API_KEY="sk-NscqaCD1PfVm7soEF3C3E6297bE14d7fB595Be8f17F39aFf"
+aider  --model openai/deepseek-chat
+aider  --model openai/qwen-plus
+```
+
 ```shell
 version: '3.8'
 

+ 273 - 35
代码架构.md

@@ -26,54 +26,292 @@ uvicorn openhands.server.listen:app --host 0.0.0.0 --port 3000
 
 尝试连接到之前的容器,如果之前保留了会话,可根据容器名连接: `async def connect(self):` , `await call_sync_from_async(self._attach_to_container)`
 如果容器不存在,则初始化一个新容器: `await call_sync_from_async(self._init_container)`
-容器名来自前缀+sid `self.container_name = CONTAINER_NAME_PREFIX + sid` ,sid 来自 `listen.py` 会话或者 jwt_token `sid = get_sid_from_token(jwt_token, config.jwt_secret)` `sid = str(uuid.uuid4())`
-> ./openhands/runtime/impl/docker/docker_runtime.py
-> ./openhands/server/listen.py
+容器名来自前缀+sid `self.container_name = CONTAINER_NAME_PREFIX + sid` ,sid 来自 `listen.py` 会话或者 jwt_token 
+```python
+async def connect(connection_id: str, environ, auth)
+
+event_stream = await session_manager.join_conversation
+
+async def maybe_start_agent_loop
 
-在 runtime 内部,`python -u -m openhands.runtime.action_execution_server` 用 api server 来操作文件
-添加所有插件 `plugins_to_load.append(ALL_PLUGINS[plugin]())` 
-加载插件 ` (self._init_plugin(plugin) for plugin in self.plugins_to_load),` 
-初始化插件 `await plugin.initialize(self.username)`
-> ./openhands/runtime/action_execution_server.py
+asyncio.create_task(session.initialize_agent(settings))
+
+await self.agent_session.start
+
+await self._create_runtime
+
+runtime_cls = get_runtime_cls
+
+class DockerRuntime(ActionExecutionClient)
+```
+> ./openhands/runtime/impl/docker/docker_runtime.py
+> ./openhands/server/listen_socket.py
 
-在 runtime 内部,初始化 vscode 插件。 `f'exec /openhands/.openvscode-server/bin/openvscode-server --host 0.0.0.0 --connection-token {self.vscode_connection_token} --port {self.vscode_port}\n'`
-port 地址是 port_mapping 在文件 `eventstream_runtime.py`中 `self.container = self.docker_client.containers.run` 时传参数
-> ./openhands/runtime/plugins/vscode/__init__.py
 
 ### runtime 内部的初始化
 `self.container = self.docker_client.containers.run`
 > OpenHands/openhands/runtime/action_execution_server.py
 
 
-### 文件操作
+### 文件修改
+
+```python
+# 行号:16
+await session_manager.send_to_event_stream
+await session.dispatch(data)
+```
+> OpenHands/openhands/server/listen_socket.py
+**添加事件**
+openhands/server/session/session.py
+testm/logs/openhands_2025-01-03.log:36129
+```python
+# 行号:18
+self.agent_session.event_stream.add_event(event, EventSource.USER)
+```
+
+**回调事件**:
+OpenHands/openhands/events/stream.py
+```python
+async def _process_queue(self)
+```
+
+**回调事件是如何被订阅的**
+参考 [###启动 runtime](###启动 runtime) 
+
+OpenHands/openhands/server/listen_socket.py
+```python
+async def connect(connection_id: str, environ, auth)
+
+event_stream = await session_manager.join_conversation
+
+return await self.maybe_start_agent_loop(sid, settings)
+
+session = Session(
+                sid=sid, file_store=self.file_store, config=self.config, sio=self.sio
+            )
+self.agent_session = AgentSession(
+            sid, file_store, status_callback=self.queue_status_message
+        )     
+# AgentSession event_stream
+self.event_stream = EventStream(sid, file_store)
+
+
+asyncio.create_task(session.initialize_agent(settings))
+
+await self.agent_session.start
+
+await self._create_runtime(
+
+runtime_cls = get_runtime_cls(runtime_name)
+# AgentSession event_stream
+self.runtime = runtime_cls(
+            config=config,
+            event_stream=self.event_stream,
+            sid=self.sid,
+            plugins=agent.sandbox_plugins,
+            status_callback=self._status_callback,
+            headless_mode=False,
+        )
+
+class DockerRuntime(ActionExecutionClient)
+
+class Runtime(FileEditRuntimeMixin)
+self.event_stream.subscribe(
+            EventStreamSubscriber.RUNTIME, self.on_event, self.sid
+        )
+
+async def _handle_action(self, event: Action)
+
+observation = getattr(self, action_type)(action)
+
+
+# 因为 DockerRuntime 继承了 
+class DockerRuntime(ActionExecutionClient):
+
+OpenHands/openhands/runtime/impl/action_execution/action_execution_client.py
+class ActionExecutionClient(Runtime)
+return self.send_action_for_execution(action)
 
-1. 前端通过 WebSocket 发送文件更新请求到 `listen_socket.py`
-   - 请求通过 `websocket_endpoint` 接收
-   - 消息格式包含操作类型(创建/修改/删除)和文件内容
-   - 消息被路由到对应的 session handler
+with send_request(
+         self.session,
+         'POST',
+         f'{self._get_action_execution_server_host()}/execute_action',
+         json={'action': event_to_dict(action)},
+         # wait a few more seconds to get the timeout error from client side
+         timeout=action.timeout + 5,
+      ) as response:
+         output = response.json()
+         obs = observation_from_dict(output)
+         obs._cause = action.id  # type: ignore[attr-defined]
 
-2. 在 `session_manager.py` 中处理请求
-   - `init_or_join_session` 获取或创建对应会话
-   - `handle_message` 方法解析消息类型
-   - 文件操作请求被转发到 `agent_session`
 
-3. `agent_session.py` 处理文件操作
-   - `process_file_operation` 方法处理具体操作
-   - 创建/修改操作通过 `write_file` 方法
-   - 删除操作通过 `delete_file` 方法
-   - 所有操作通过 runtime 的 API 执行
+OpenHands/openhands/runtime/action_execution_server.py
+async def execute_action(action_request: ActionRequest):
 
-4. 文件操作通过 runtime 执行
-   - 通过 `action_execution_server.py` 的 API 接口
-   - 使用 `FileOperationsPlugin` 处理具体文件操作
-   - 操作在容器内的 workspace 目录执行
-   - 结果通过 WebSocket 返回给前端
+observation = await getattr(self, action_type)(action)
 
-5. 文件同步机制
-   - 使用 inotify 监控文件变化
-   - 变化通过 `FileWatcherPlugin` 处理
-   - 重要变化会通知前端更新
-   - 双向同步确保一致性
+```
+
+### CodeActAgent
+
+
+```python
+Agent.register('CodeActAgent', CodeActAgent)
+# CodeActAgent 默认使用插件
+class CodeActAgent(Agent):
+   sandbox_plugins: list[PluginRequirement] = [
+         # NOTE: AgentSkillsRequirement need to go before JupyterRequirement, since
+         # AgentSkillsRequirement provides a lot of Python functions,
+         # and it needs to be initialized before Jupyter for Jupyter to use those functions.
+         AgentSkillsRequirement(),
+         JupyterRequirement(),
+      ]
+
+agent = Agent.get_cls(agent_cls)(llm, agent_config)
+await self._create_runtime(
+   self.runtime = runtime_cls
+   self.controller = self._create_controller(
+
+controller = AgentController(
+
+async def _step(self) -> None:
+   
+   action = self.agent.step(self.state)
+```
+
+OpenHands/openhands/events/stream.py
+```python
+def subscribe
+```
+
+event_stream 如何被订阅,**首先要了解初始化 event_stream 的过程**
+OpenHands/openhands/server/session/agent_session.py
+```python
+self.event_stream = EventStream(sid, file_store)
+```
+
+OpenHands/openhands/server/listen_socket.py
+```python
+async def connect(connection_id: str, environ, auth)
+
+   event_stream = await session_manager.join_conversation
+```
+OpenHands/openhands/server/session/manager.py
+```python
+return await self.maybe_start_agent_loop(sid, settings)
+
+session = Session(
+```
+**订阅事件 SERVER**
+OpenHands/openhands/server/session/session.py
+```python
+self.agent_session = AgentSession
+
+self.agent_session.event_stream.subscribe(
+   EventStreamSubscriber.SERVER, self.on_event, self.sid
+)
+```
+
+
+OpenHands/openhands/server/session/agent_session.py
+```python
+self.event_stream = EventStream(sid, file_store)
+```
+
+
+OpenHands/openhands/server/session/session.py
+```python
+self.event_stream.subscribe(
+   EventStreamSubscriber.AGENT_CONTROLLER, self.on_event, self.id
+)
+
+处理各类事件
+
+await self._handle_action(event)
+
+async def _handle_message_action(self, action: MessageAction)
+```
+
+
+1. **运行时创建 (`agent_session.py`)**:
+   - 根据指定的运行时名称初始化运行时环境。
+   - 连接到运行时并设置插件。
+   - 创建代理控制器以管理运行时中的任务执行。
+   > OpenHands/openhands/server/session/agent_session.py
+   
+   ```python
+   # 行号:21
+   self._start_thread,
+   # 行号:22
+   await self._create_runtime
+   ```
+
+2. **Docker 运行时实现 (`docker_runtime.py`)**:
+   - 初始化和管理 Docker 容器,其中任务被执行。
+   - 处理启动或连接到现有 Docker 容器。
+   - 设置端口映射和环境变量。
+   - 确保容器准备好后再继续执行任务。
+   - 提供 VSCode URL 用于远程开发。
+   > OpenHands/openhands/runtime/impl/docker/docker_runtime.py
+   
+   ```python
+   # 行号:29
+   self.container_name = CONTAINER_NAME_PREFIX + sid
+   # 行号:30
+   sid = get_sid_from_token(jwt_token, config.jwt_secret)
+   # 行号:31
+   sid = str(uuid.uuid4())
+   ```
+
+通过以上步骤,OpenHands 项目能够从前端接收到信息并启动 Docker 运行时,从而执行各种任务。这个流程确保了系统的灵活性和可扩展性,同时提供了强大的开发和调试工具支持。
+
+### 构建自己的 Runtime 镜像
+   - 启动新的会话或重用现有的会话。
+
+3. **代理会话初始化 (`session.py`)**:
+   - 初始化代理会话,设置必要的配置。
+   - 设置事件流订阅者以处理来自代理的事件。
+   - 启动运行时容器,在其中执行任务。
+
+4. **运行时创建 (`agent_session.py`)**:
+   - 根据指定的运行时名称初始化运行时环境。
+   - 连接到运行时并设置插件。
+   - 创建代理控制器以管理运行时中的任务执行。
+
+5. **Docker 运行时实现 (`docker_runtime.py`)**:
+   - 初始化和管理 Docker 容器,其中任务被执行。
+   - 处理启动或连接到现有 Docker 容器。
+   - 设置端口映射和环境变量。
+   - 确保容器准备好后再继续执行任务。
+   - 提供 VSCode URL 用于远程开发。
+
+##### 详细流程说明
+
+1. **前端通信 (`listen_socket.py`)**:
+   - 当前端通过 WebSocket 发送请求时,`listen_socket.py` 处理这些请求并验证用户身份。
+   - 如果验证通过,它会加入相应的会话(conversation),并通过事件流与客户端进行通信。
+
+2. **会话管理 (`manager.py`)**:
+   - `manager.py` 负责管理所有活动和已分离的会话。
+   - 它确保会话在多个服务器节点之间正确同步,并且可以重用现有的会话或启动新的会话。
+   - 例如,当一个新的 WebSocket 连接到来时,`manager.py` 会检查是否已经存在对应的会话,如果存在则重用,否则创建新的会话。
+
+3. **代理会话初始化 (`session.py`)**:
+   - `session.py` 初始化代理会话,设置必要的配置,如代理类型、最大迭代次数等。
+   - 它还设置了事件流订阅者,以便处理来自代理的事件。
+   - 最后,它调用 `agent_session.py` 中的方法来启动运行时容器。
+
+4. **运行时创建 (`agent_session.py`)**:
+   - `agent_session.py` 根据指定的运行时名称初始化运行时环境。
+   - 它尝试连接到现有的运行时容器,如果不存在则初始化一个新的容器。
+   - 它还会创建代理控制器,用于管理运行时中的任务执行。
+
+5. **Docker 运行时实现 (`docker_runtime.py`)**:
+   - `docker_runtime.py` 实现了基于 Docker 的运行时环境。
+   - 它负责启动或连接到 Docker 容器,并设置必要的端口映射和环境变量。
+   - 它还确保容器准备好后再继续执行任务,并提供 VSCode URL 用于远程开发。
+
+通过以上步骤,OpenHands 项目能够从前端接收到信息并启动 Docker 运行时,从而执行各种任务。这个流程确保了系统的灵活性和可扩展性,同时提供了强大的开发和调试工具支持。
 
 
 ## vscode