Skip to content

Commit

Permalink
move exec to skills
Browse files Browse the repository at this point in the history
  • Loading branch information
eavae committed Nov 18, 2024
1 parent 8e3e053 commit 8931df0
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 62 deletions.
44 changes: 4 additions & 40 deletions GeneralAgent/interpreter/python_interpreter.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import re, io, os, sys, ast
import re, io, os, sys
import pickle
import logging
from jinja2 import Template
from functools import partial
from codyer import skills
from .interpreter import Interpreter


Expand Down Expand Up @@ -145,7 +146,7 @@ def _remove_unpickleable(self):
for key in keys:
try:
pickle.dumps(save_globals[key])
except Exception as e:
except Exception:
save_globals.__delitem__(key)
return save_globals

Expand Down Expand Up @@ -180,7 +181,7 @@ def run_code(self, code):
else:
name = fun.__name__
self.globals[name] = fun
result = exec_and_get_last_expression(self.globals, code)
result = skills._exec(code, self.globals)
self.run_wrong_count = 0
stop = True
# 出现了自我调用,则判断一下层级,如果层级为1,则停止
Expand Down Expand Up @@ -217,40 +218,3 @@ def get_variable(self, name):

def set_variable(self, name, value):
self.globals[name] = value


def exec_and_get_last_expression(globals_vars, code):
tree = ast.parse(code)

try:
last_node = tree.body[-1]
code_body = tree.body[0:-1]
last_expr = ast.unparse(last_node)

if isinstance(last_node, ast.Assign):
code_body = tree.body
expr_left = last_node.targets[-1]
if isinstance(expr_left, ast.Tuple):
last_expr = f"({', '.join([x.id for x in expr_left.elts])})"
else:
last_expr = expr_left.id

elif isinstance(last_node, ast.AugAssign) or isinstance(
last_node, ast.AnnAssign
):
code_body = tree.body
last_expr = last_node.target.id

if len(code_body):
main_code = compile(ast.unparse(code_body), "<string>", "exec")
exec(main_code, globals_vars)
except SyntaxError:
return None

try:
return eval(
compile(last_expr, "<string>", "eval"),
globals_vars,
)
except SyntaxError:
return None
90 changes: 73 additions & 17 deletions GeneralAgent/skills/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,26 @@
import os
from codyer import skills


def default_output_callback(token):
if token is not None:
print(token, end='', flush=True)
print(token, end="", flush=True)
else:
print('\n', end='', flush=True)
print("\n", end="", flush=True)


def default_check(check_content=None):
show = '确认 | 继续 (回车, yes, y, 是, ok) 或者 直接输入你的想法\n'
show = "确认 | 继续 (回车, yes, y, 是, ok) 或者 直接输入你的想法\n"
if check_content is not None:
show = f'{check_content}\n\n{show}'
show = f"{check_content}\n\n{show}"
response = input(show)
if response.lower() in ['', 'yes', 'y', '是', 'ok']:
if response.lower() in ["", "yes", "y", "是", "ok"]:
return None
else:
return response

def load_functions_with_path(python_code_path) -> (list, str):

def load_functions_with_path(python_code_path) -> tuple[list, str]:
"""
Load functions from python file
@param python_code_path: the path of python file
Expand All @@ -30,7 +32,7 @@ def load_functions_with_path(python_code_path) -> (list, str):
import inspect

# 指定要加载的文件路径和文件名
module_name = 'skills'
module_name = "skills"
module_file = python_code_path

# 使用importlib加载文件
Expand All @@ -42,15 +44,16 @@ def load_functions_with_path(python_code_path) -> (list, str):
functions = inspect.getmembers(module, inspect.isfunction)

# 过滤functions中以下划线开头的函数
functions = filter(lambda f: not f[0].startswith('_'), functions)
functions = filter(lambda f: not f[0].startswith("_"), functions)

return [f[1] for f in functions], None
except Exception as e:
# 代码可能有错误,加载不起来
import logging

logging.exception(e)
return [], str(e)


def load_functions_with_directory(python_code_dir) -> list:
"""
Expand All @@ -59,23 +62,76 @@ def load_functions_with_directory(python_code_dir) -> list:
@return: a list of functions
"""
import os

total_funs = []
for file in os.listdir(python_code_dir):
# if file is directory
if os.path.isdir(os.path.join(python_code_dir, file)):
total_funs += load_functions_with_directory(os.path.join(python_code_dir, file))
total_funs += load_functions_with_directory(
os.path.join(python_code_dir, file)
)
else:
# if file is file
if file.endswith('.py') and (not file.startswith('__init__') and not file.startswith('_') and not file == 'main.py'):
funcs, error = load_functions_with_path(os.path.join(python_code_dir, file))
if file.endswith(".py") and (
not file.startswith("__init__")
and not file.startswith("_")
and not file == "main.py"
):
funcs, error = load_functions_with_path(
os.path.join(python_code_dir, file)
)
total_funs += funcs
return total_funs


def _exec(code, globals_vars={}):
"""
Execute code and return the last expression
"""
import ast

tree = ast.parse(code)

try:
last_node = tree.body[-1]
code_body = tree.body[0:-1]
last_expr = ast.unparse(last_node)

if isinstance(last_node, ast.Assign):
code_body = tree.body
expr_left = last_node.targets[-1]
if isinstance(expr_left, ast.Tuple):
last_expr = f"({', '.join([x.id for x in expr_left.elts])})"
else:
last_expr = expr_left.id

elif isinstance(last_node, ast.AugAssign) or isinstance(
last_node, ast.AnnAssign
):
code_body = tree.body
last_expr = last_node.target.id

if len(code_body):
main_code = compile(ast.unparse(code_body), "<string>", "exec")
exec(main_code, globals_vars)
except SyntaxError:
return None

try:
return eval(
compile(last_expr, "<string>", "eval"),
globals_vars,
)
except SyntaxError:
return None


if len(skills._functions) == 0:
skills._add_function('input', input)
skills._add_function('check', default_check)
skills._add_function('print', default_output_callback)
skills._add_function('output', default_output_callback)
skills._add_function("input", input)
skills._add_function("check", default_check)
skills._add_function("print", default_output_callback)
skills._add_function("output", default_output_callback)
skills._add_function("_exec", _exec)
funcs = load_functions_with_directory(os.path.dirname(__file__))
for fun in funcs:
skills._add_function(fun.__name__, fun)
skills._add_function(fun.__name__, fun)
19 changes: 14 additions & 5 deletions examples/1_function_call.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# 函数调用
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s', handlers=[logging.StreamHandler()])

logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s",
handlers=[logging.StreamHandler()],
)
from GeneralAgent import Agent
from dotenv import load_dotenv

load_dotenv()


# 函数: 获取天气信息
def get_weather(city: str) -> str:
"""
Expand All @@ -13,12 +20,14 @@ def get_weather(city: str) -> str:
@return: str, weather information
"""
# return f"{city} weather: sunny"
print(f"{city} weather: sunny")
weather = "sunny"
print(f"{city} weather: {weather}")
return weather


# agent = Agent('你是一个天气小助手', functions=[get_weather], model='deepseek-chat')
agent = Agent('你是一个天气小助手', functions=[get_weather])
agent.user_input('成都天气怎么样?')
agent = Agent("你是一个天气小助手", functions=[get_weather])
agent.user_input("成都天气怎么样?")

# 输出
# ```python
Expand All @@ -27,4 +36,4 @@ def get_weather(city: str) -> str:
# weather_info
# ```
# 成都的天气是晴天。
# 请问还有什么我可以帮忙的吗?
# 请问还有什么我可以帮忙的吗?

0 comments on commit 8931df0

Please sign in to comment.