Docker image, code exe, callstack

- Docker image update - Code exex tool standalone result variables in python and nodejs returned - Agent chain callstack fix after chat loading from file

frdel committed Oct 15, 2024 at 23:25 UTC 763ec9e228194a5d9d55260fa75a54c2a1d6a8fc
9 files changed +115 -30
agent.py
+35 -12
@@ -23,9 +23,14 @@ class AgentContext:
23 _counter: int = 0
24
25 def __init__(
26 - self, config: "AgentConfig", id: str | None = None, name: str | None = None, agent0: "Agent|None" = None,
26 + self,
27 + config: "AgentConfig",
28 + id: str | None = None,
29 + name: str | None = None,
30 + agent0: "Agent|None" = None,
31 log: Log.Log | None = None,
28 - paused: bool = False, streaming_agent: "Agent|None" = None,
32 + paused: bool = False,
33 + streaming_agent: "Agent|None" = None,
34 ):
35 # build context
36 self.id = id or str(uuid.uuid4())
@@ -38,7 +43,7 @@ class AgentContext:
43 self.process: DeferredTask | None = None
44 AgentContext._counter += 1
45 self.no = AgentContext._counter
41 -
46 +
47 self._contexts[self.id] = self
48
49 @staticmethod
@@ -69,12 +74,12 @@ class AgentContext:
74 def communicate(self, msg: str, broadcast_level: int = 1):
75 self.paused = False # unpause if paused
76
72 - if self.process and self.process.is_alive():
73 - if self.streaming_agent:
74 - current_agent = self.streaming_agent
75 - else:
76 - current_agent = self.agent0
77 + if self.streaming_agent:
78 + current_agent = self.streaming_agent
79 + else:
80 + current_agent = self.agent0
81
82 + if self.process and self.process.is_alive():
83 # set intervention messages to agent(s):
84 intervention_agent = current_agent
85 while intervention_agent and broadcast_level != 0:
@@ -82,10 +87,30 @@ class AgentContext:
87 broadcast_level -= 1
88 intervention_agent = intervention_agent.data.get("superior", None)
89 else:
85 - self.process = DeferredTask(self.agent0.monologue, msg)
90 +
91 + # self.process = DeferredTask(current_agent.monologue, msg)
92 + self.process = DeferredTask(self._process_chain, current_agent, msg)
93
94 return self.process
95
96 + # this wrapper ensures that superior agents are called back if the chat was loaded from file and original callstack is gone
97 + async def _process_chain(self, agent: 'Agent', msg: str, user=True):
98 + try:
99 + msg_template = (
100 + agent.read_prompt("fw.user_message.md", message=msg)
101 + if user
102 + else agent.read_prompt(
103 + "fw.tool_response.md",
104 + tool_name="call_subordinate",
105 + tool_response=msg,
106 + )
107 + )
108 + response = await agent.monologue(msg_template)
109 + superior = agent.data.get("superior", None)
110 + if superior:
111 + await self._process_chain(superior, response, False)
112 + except Exception as e:
113 + agent.handle_critical_exception(e)
114
115 @dataclass
116 class AgentConfig:
@@ -220,9 +245,7 @@ class Agent:
245 await self.call_extensions("monologue_start", loop_data=loop_data)
246
247 printer = PrintStyle(italic=True, font_color="#b3ffd9", padding=False)
223 - user_message = self.read_prompt(
224 - "fw.user_message.md", message=loop_data.message
225 - )
248 + user_message = loop_data.message
249 await self.append_message(user_message, human=True)
250
251 # let the agent run message loop until he stops it with a response tool
docker/exe/Dockerfile
+14 -4
@@ -13,7 +13,12 @@ RUN apt-get update && apt-get install -y \
13 npm \
14 openssh-server \
15 sudo \
16 - && rm -rf /var/lib/apt/lists/*
16 + curl \
17 + wget \
18 + git
19 +
20 +# Cleanup package list
21 +RUN rm -rf /var/lib/apt/lists/*
22
23 # Set up SSH
24 RUN mkdir /var/run/sshd && \
@@ -24,16 +29,21 @@ RUN mkdir /var/run/sshd && \
29 ENV VIRTUAL_ENV=/opt/venv
30 RUN python3 -m venv $VIRTUAL_ENV
31
27 -# Copy initial .bashrc with virtual environment activation to a temporary location
28 -COPY .bashrc /etc/skel/.bashrc
29 -
32 # Copy the script to ensure .bashrc is in the root directory
33 COPY initialize.sh /usr/local/bin/initialize.sh
34 RUN chmod +x /usr/local/bin/initialize.sh
35
36 +# Copy contents of filesystem directory to /
37 +COPY ./fs/ /
38 +
39 # Ensure the virtual environment and pip setup
40 RUN $VIRTUAL_ENV/bin/pip install --upgrade pip
41
42 +# Install additional python packages
43 +RUN $VIRTUAL_ENV/bin/pip install \
44 + ipython \
45 + requests
46 +
47 # Expose SSH port
48 EXPOSE 22
49
docker/exe/build.txt
+1
@@ -1 +1,2 @@
1 +docker login
2 docker buildx build --platform linux/amd64,linux/arm64 -t frdel/agent-zero-exe:latest --push .
\ No newline at end of file
docker/exe/fs/exe/node_eval.js new
+41
@@ -0,0 +1,41 @@
1 +#!/usr/bin/env node
2 +
3 +const vm = require('vm');
4 +const path = require('path');
5 +
6 +// Create a comprehensive context with all important global objects
7 +const context = vm.createContext({
8 + ...global,
9 + require: require,
10 + __filename: path.join(process.cwd(), 'eval.js'),
11 + __dirname: process.cwd(),
12 + module: { exports: {} },
13 + exports: module.exports,
14 + console: console,
15 + process: process,
16 + Buffer: Buffer,
17 + setTimeout: setTimeout,
18 + setInterval: setInterval,
19 + setImmediate: setImmediate,
20 + clearTimeout: clearTimeout,
21 + clearInterval: clearInterval,
22 + clearImmediate: clearImmediate
23 +});
24 +
25 +const code = process.argv[2];
26 +const wrappedCode = `
27 + (async function() {
28 + try {
29 + const __result__ = await eval(${JSON.stringify(code)});
30 + if (__result__ !== undefined) console.log('Out[1]:', __result__);
31 + } catch (error) {
32 + console.error(error);
33 + }
34 + })();
35 +`;
36 +
37 +vm.runInContext(wrappedCode, context, {
38 + filename: 'eval.js',
39 + lineOffset: -2,
40 + columnOffset: 0,
41 +}).catch(console.error);
\ No newline at end of file
docker/exe/fs/root/.bashrc renamed
docker/exe/fs/root/.profile new
+9
@@ -0,0 +1,9 @@
1 +# .bashrc
2 +
3 +# Source global definitions
4 +if [ -f /etc/bashrc ]; then
5 + . /etc/bashrc
6 +fi
7 +
8 +# Activate the virtual environment
9 +source /opt/venv/bin/activate
docker/exe/initialize.sh
+11 -11
@@ -1,18 +1,18 @@
1 #!/bin/bash
2
3 -# Ensure .bashrc is in the root directory
4 -if [ ! -f /root/.bashrc ]; then
5 - cp /etc/skel/.bashrc /root/.bashrc
6 - chmod 444 /root/.bashrc
7 -fi
3 +# # Ensure .bashrc is in the root directory
4 +# if [ ! -f /root/.bashrc ]; then
5 +# cp /etc/skel/.bashrc /root/.bashrc
6 +# chmod 444 /root/.bashrc
7 +# fi
8
9 -# Ensure .profile is in the root directory
10 -if [ ! -f /root/.profile ]; then
11 - cp /etc/skel/.bashrc /root/.profile
12 - chmod 444 /root/.profile
13 -fi
9 +# # Ensure .profile is in the root directory
10 +# if [ ! -f /root/.profile ]; then
11 +# cp /etc/skel/.bashrc /root/.profile
12 +# chmod 444 /root/.profile
13 +# fi
14
15 apt-get update
16
17 # Start SSH service
18 -exec /usr/sbin/sshd -D
18 +exec /usr/sbin/sshd -D
\ No newline at end of file
python/helpers/persist_chat.py
+2 -1
@@ -102,7 +102,7 @@ def _deserialize_context(data):
102
103 context = AgentContext(
104 config=config,
105 - id=data.get("id", None),
105 + # id=data.get("id", None), #get new id
106 name=data.get("name", None),
107 log=log,
108 paused=False,
@@ -143,6 +143,7 @@ def _deserialize_agents(
143 if prev:
144 prev.set_data("subordinate", current)
145 current.set_data("superior", prev)
146 + prev = current
147
148 return zero or Agent(0, config, context)
149
python/tools/code_execution_tool.py
+2 -2
@@ -112,12 +112,12 @@ class CodeExecution(Tool):
112
113 async def execute_python_code(self, code: str, reset: bool = False):
114 escaped_code = shlex.quote(code)
115 - command = f"python3 -c {escaped_code}"
115 + command = f"ipython -c {escaped_code}"
116 return await self.terminal_session(command, reset)
117
118 async def execute_nodejs_code(self, code: str, reset: bool = False):
119 escaped_code = shlex.quote(code)
120 - command = f"node -e {escaped_code}"
120 + command = f"node /exe/node_eval.js {escaped_code}"
121 return await self.terminal_session(command, reset)
122
123 async def execute_terminal_command(self, command: str, reset: bool = False):