From 92c216b7653e7bafe6b5dfdb27615c78b04c7442 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Wed, 14 Aug 2024 09:06:28 -0700 Subject: [PATCH] feat: add load methods Signed-off-by: Donnie Adams --- README.md | 4 ++-- gptscript/__init__.py | 2 +- gptscript/frame.py | 9 ++++++--- gptscript/gptscript.py | 34 ++++++++++++++++++++++++++----- tests/test_gptscript.py | 45 ++++++++++++++++++++++++++++++++++------- 5 files changed, 76 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f185ad4..0d08cb6 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ from gptscript.gptscript import GPTScript async def parse_tool_example(): gptscript = GPTScript() - tools = await gptscript.parse_tool("Instructions: Say hello!") + tools = await gptscript.parse_content("Instructions: Say hello!") print(tools) gptscript.close() ``` @@ -155,7 +155,7 @@ from gptscript.gptscript import GPTScript async def fmt_example(): gptscript = GPTScript() - tools = await gptscript.parse_tool("Instructions: Say hello!") + tools = await gptscript.parse_content("Instructions: Say hello!") print(tools) contents = gptscript.fmt(tools) diff --git a/gptscript/__init__.py b/gptscript/__init__.py index ef3adbe..9b5de34 100644 --- a/gptscript/__init__.py +++ b/gptscript/__init__.py @@ -1,6 +1,6 @@ from gptscript.gptscript import GPTScript from gptscript.confirm import AuthResponse -from gptscript.frame import RunFrame, CallFrame, PromptFrame +from gptscript.frame import RunFrame, CallFrame, PromptFrame, Program from gptscript.opts import GlobalOptions from gptscript.prompt import PromptResponse from gptscript.run import Run, RunBasicCommand, Options diff --git a/gptscript/frame.py b/gptscript/frame.py index d5d0214..dfdddaa 100644 --- a/gptscript/frame.py +++ b/gptscript/frame.py @@ -47,9 +47,12 @@ def __init__(self, self.name = name self.entryToolId = entryToolId self.toolSet = toolSet - for tool in toolSet: - if isinstance(self.toolSet[tool], dict): - self.toolSet[tool] = Tool(**self.toolSet[tool]) + if self.toolSet is None: + self.toolSet = {} + else: + for tool in toolSet: + if isinstance(self.toolSet[tool], dict): + self.toolSet[tool] = Tool(**self.toolSet[tool]) class RunFrame: diff --git a/gptscript/gptscript.py b/gptscript/gptscript.py index 563bc64..c29a155 100644 --- a/gptscript/gptscript.py +++ b/gptscript/gptscript.py @@ -9,7 +9,7 @@ import requests from gptscript.confirm import AuthResponse -from gptscript.frame import RunFrame, CallFrame, PromptFrame +from gptscript.frame import RunFrame, CallFrame, PromptFrame, Program from gptscript.opts import GlobalOptions from gptscript.prompt import PromptResponse from gptscript.run import Run, RunBasicCommand, Options @@ -90,7 +90,7 @@ def evaluate( opts.merge_global_opts(self.opts), self._server_url, event_handlers=event_handlers, - ).next_chat("" if opts is None else opts.input) + ).next_chat(opts.input) def run( self, tool_path: str, @@ -104,7 +104,31 @@ def run( opts.merge_global_opts(self.opts), self._server_url, event_handlers=event_handlers, - ).next_chat("" if opts is None else opts.input) + ).next_chat(opts.input) + + async def load_file(self, file_path: str, disable_cache: bool = False, sub_tool: str = '') -> Program: + out = await self._run_basic_command( + "load", + {"file": file_path, "disableCache": disable_cache, "subTool": sub_tool}, + ) + parsed_nodes = json.loads(out) + return Program(**parsed_nodes.get("program", {})) + + async def load_content(self, content: str, disable_cache: bool = False, sub_tool: str = '') -> Program: + out = await self._run_basic_command( + "load", + {"content": content, "disableCache": disable_cache, "subTool": sub_tool}, + ) + parsed_nodes = json.loads(out) + return Program(**parsed_nodes.get("program", {})) + + async def load_tools(self, tool_defs: list[ToolDef], disable_cache: bool = False, sub_tool: str = '') -> Program: + out = await self._run_basic_command( + "load", + {"toolDefs": [t.to_json() for t in tool_defs], "disableCache": disable_cache, "subTool": sub_tool}, + ) + parsed_nodes = json.loads(out) + return Program(**parsed_nodes.get("program", {})) async def parse(self, file_path: str, disable_cache: bool = False) -> list[Text | Tool]: out = await self._run_basic_command("parse", {"file": file_path, "disableCache": disable_cache}) @@ -114,8 +138,8 @@ async def parse(self, file_path: str, disable_cache: bool = False) -> list[Text return [Text(**node["textNode"]) if "textNode" in node else Tool(**node.get("toolNode", {}).get("tool", {})) for node in parsed_nodes.get("nodes", [])] - async def parse_tool(self, tool_def: str) -> list[Text | Tool]: - out = await self._run_basic_command("parse", {"content": tool_def}) + async def parse_content(self, content: str) -> list[Text | Tool]: + out = await self._run_basic_command("parse", {"content": content}) parsed_nodes = json.loads(out) if parsed_nodes is None or parsed_nodes.get("nodes", None) is None: return [] diff --git a/tests/test_gptscript.py b/tests/test_gptscript.py index acb2754..3820b29 100644 --- a/tests/test_gptscript.py +++ b/tests/test_gptscript.py @@ -48,10 +48,8 @@ def simple_tool(): @pytest.fixture def complex_tool(): return ToolDef( - tools=["sys.write"], jsonResponse=True, - instructions=""" -Create three short graphic artist descriptions and their muses. + instructions="""Create three short graphic artist descriptions and their muses. These should be descriptive and explain their point of view. Also come up with a made up name, they each should be from different backgrounds and approach art differently. @@ -272,10 +270,43 @@ async def test_eval_with_context(gptscript): ) run = gptscript.evaluate(tool) - assert "Acorn Labs" == await run.text(), "Unexpected output from eval using context" +@pytest.mark.asyncio +async def test_load_simple_file(gptscript): + wd = os.getcwd() + prg = await gptscript.load_file(wd + "/tests/fixtures/test.gpt") + assert prg.toolSet[prg.entryToolId].instructions == "Who was the president of the United States in 1986?", \ + "Unexpected output from parsing simple file" + + +@pytest.mark.asyncio +async def test_load_remote_tool(gptscript): + prg = await gptscript.load_file("github.com/gptscript-ai/context/workspace") + assert prg.entryToolId != "", "Unexpected entry tool id from remote tool" + assert len(prg.toolSet) > 0, "Unexpected number of tools in remote tool" + assert prg.name != "", "Unexpected name from remote tool" + + +@pytest.mark.asyncio +async def test_load_simple_content(gptscript): + wd = os.getcwd() + with open(wd + "/tests/fixtures/test.gpt") as f: + prg = await gptscript.load_content(f.read()) + assert prg.toolSet[prg.entryToolId].instructions == "Who was the president of the United States in 1986?", \ + "Unexpected output from parsing simple file" + + +@pytest.mark.asyncio +async def test_load_tools(gptscript, tool_list): + prg = await gptscript.load_tools(tool_list) + assert prg.entryToolId != "", "Unexpected entry tool id from remote tool" + assert len(prg.toolSet) > 0, "Unexpected number of tools in remote tool" + # Name will be empty in this case. + assert prg.name == "", "Unexpected name from remote tool" + + @pytest.mark.asyncio async def test_parse_simple_file(gptscript): wd = os.getcwd() @@ -295,7 +326,7 @@ async def test_parse_empty_file(gptscript): @pytest.mark.asyncio async def test_parse_empty_str(gptscript): - tools = await gptscript.parse_tool("") + tools = await gptscript.parse_content("") assert len(tools) == 0, "Unexpected number of tools for parsing empty string" @@ -313,7 +344,7 @@ async def test_parse_tool_with_metadata(gptscript): @pytest.mark.asyncio async def test_parse_tool(gptscript): - tools = await gptscript.parse_tool("echo hello") + tools = await gptscript.parse_content("echo hello") assert len(tools) == 1, "Unexpected number of tools for parsing tool" assert isinstance(tools[0], Tool), "Unexpected node type from parsing tool" assert tools[0].instructions == "echo hello", "Unexpected output from parsing tool" @@ -321,7 +352,7 @@ async def test_parse_tool(gptscript): @pytest.mark.asyncio async def test_parse_tool_with_text_node(gptscript): - tools = await gptscript.parse_tool("echo hello\n---\n!markdown\nhello") + tools = await gptscript.parse_content("echo hello\n---\n!markdown\nhello") assert len(tools) == 2, "Unexpected number of tools for parsing tool with text node" assert isinstance(tools[0], Tool), "Unexpected node type for first tool from parsing tool with text node" assert isinstance(tools[1], Text), "Unexpected node type for second tool from parsing tool with text node"