Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/plugin headers #34

Merged
merged 2 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions pypi-core/openplugincore/openplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@


class OpenPlugin:
def __init__(self, plugin_name: str = None, openai_api_key: str = None, root_url: str = None, verbose: bool = False):
def __init__(self, plugin_name: str = None, openai_api_key: str = None, root_url: str = None, manifest = None, verbose: bool = False):
self.name: str = plugin_name
self.root_url: str = root_url
self.description: str = None
self.manifest: Any = None
self.manifest: Any = manifest
self.functions: List[Dict[str, Any]] = None
self.call_api_fn: Callable = None
self.verbose: bool = verbose
Expand Down Expand Up @@ -64,7 +64,8 @@ def init(self, plugin_name: str = None) -> None:
except KeyError:
# throw error
raise KeyError("Plugin not found")
self.manifest = self.fetch_manifest(self.root_url)
if not self.manifest:
self.manifest = self.fetch_manifest(self.root_url)
self.functions, self.call_api_fn = self.openapi_to_functions_and_call_api_fn(self.manifest)

def fetch_manifest(self, root_url: str) -> Any:
Expand Down Expand Up @@ -128,7 +129,7 @@ def _convert_openai_messages_to_langchain_messages(self, openai_messages: List[A

return langchain_messages

def fetch_plugin(self, messages: list[dict], plugin_headers: dict = None, truncate: Union[bool, int] = False, truncate_offset: int = 0, **chatgpt_args) -> ChatgptFunctionMessage:
def fetch_plugin(self, messages: list[dict], plugin_headers: dict = None, truncate: Union[bool, int] = False, truncate_offset: int = 0, return_assistant_message: bool = False, **chatgpt_args) -> ChatgptFunctionMessage:
model = chatgpt_args.get("model", None)
if model not in ["gpt-3.5-turbo-0613", "gpt-4-0613"]:
raise ValueError("Model must be either gpt-3.5-turbo-0613 or gpt-4-0613")
Expand Down Expand Up @@ -221,10 +222,26 @@ def request_chain(name,arguments,headers):
if self.verbose:
print(f"\"{self.name}\" json_response: ", json.dumps(json_response, indent=2))
try:
return ChatgptFunctionMessage(
# arguments should be stringified json
assistant_message = ChatgptAssistantMessage(
role="assistant",
content=None,
function_call= {
"name": llm_chain_out["name"],
"arguments": json.dumps(llm_chain_out["arguments"])
}
)
function_message = ChatgptFunctionMessage(
role="function",
name=llm_chain_out["name"],
content=json.dumps(json_response)
)
if return_assistant_message:
return {
"assistant_message": assistant_message,
"function_message": function_message
}
else:
return function_message
except json.decoder.JSONDecodeError:
raise json.decoder.JSONDecodeError(f"API call failed, API returned the following non-JSON response:\n{response.content}")
3 changes: 2 additions & 1 deletion pypi-core/openplugincore/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class AssistantRole(Enum):

class ChatgptAssistantMessage(TypedDict):
role: AssistantRole
content: str
content: Any
function_call: Any

class FunctionRole(Enum):
FUNCTION = "function"
Expand Down
2 changes: 1 addition & 1 deletion pypi-core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
setup(
# the name must match the folder name 'verysimplemodule'
name='openplugincore',
version="0.6.5",
version="0.7.0",
author="Sebastian Sosa",
author_email="[email protected]",
description='Seamlessly integrate with OpenAI ChatGPT plugins via API (or client), offering the same powerful functionality as the ChatGPT api + plugins!',
Expand Down
19 changes: 18 additions & 1 deletion pypi-core/tests/mock_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,24 @@
"properties": {}
}
}
]
],
"manifest": {
"schema_version": "v1",
"name_for_human": "TODO List MODIFIED",
"name_for_model": "todo",
"description_for_human": "Manage your TODO list. You can add, remove and view your TODOs.",
"description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "http://localhost:3333/openapi.yaml"
},
"logo_url": "http://localhost:3333/logo.png",
"contact_email": "[email protected]",
"legal_info_url": "http://www.example.com/legal"
}
}

chat_completion_of_random_function = {
Expand Down
16 changes: 16 additions & 0 deletions pypi-core/tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ def test_initiate_and_fetch_todo():
# fetch after chatgpt response
response = plugin.fetch_plugin(
messages=todo_plugin["messages"],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]
assert response is not None
assert response["role"] == "function"
assert response["name"] == "addTodo"
Expand All @@ -49,9 +51,11 @@ def test_initiate_and_fetch_LGTM():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -74,9 +78,11 @@ def test_initiate_and_fetch_yt_caption_retriever():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -98,9 +104,11 @@ def test_initiate_and_fetch_twtData():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -123,9 +131,11 @@ def test_initiate_and_fetch_surge_ai_trends():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -147,9 +157,11 @@ def test_initiate_and_fetch_speedy_marketing():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -172,9 +184,11 @@ def test_initiate_and_fetch_scholarai():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand All @@ -196,9 +210,11 @@ def test_initiate_and_fetch_rephrase():
"content": chatgpt_prompt
}
],
return_assistant_message=True,
model="gpt-3.5-turbo-0613",
temperature=0,
)
response = response["function_message"]

assert response is not None
assert response["role"] == "function"
Expand Down
23 changes: 20 additions & 3 deletions pypi-core/tests/test_openplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ def test_initiate_todo_with_url():
assert plugin.manifest is not None
assert plugin.manifest.get("name_for_model") == "todo"

def test_initiate_todo_with_manifest():
plugin = OpenPlugin(root_url="http://localhost:3333", manifest=todo_plugin["manifest"])
assert plugin.manifest is not None
assert plugin.manifest.get("name_for_human") == todo_plugin["manifest"]["name_for_human"]

@pytest.fixture
def todo_openplugin():
plugin = OpenPlugin("__testing__")
Expand Down Expand Up @@ -55,13 +60,25 @@ def test_openapi_to_functions_and_call_api_fn(todo_openplugin):
def test_fetch_plugin(todo_openplugin):
response = todo_openplugin.fetch_plugin(
messages=todo_plugin["messages"],
return_assistant_message=True,
plugin_headers=todo_plugin["plugin_headers"], # tests for service_auth headers
model="gpt-3.5-turbo-0613"
)
assert response is not None
assert response["role"] == "function"
assert response["name"] == "addTodo"
json_content = json.loads(response["content"])

print("response", response)

assistant_message = response["assistant_message"]
assert assistant_message["role"] == "assistant"
assert not assistant_message["content"]
assert assistant_message["function_call"]["name"] == "addTodo"
assert type(assistant_message["function_call"]["arguments"]) == str
assert json.loads(assistant_message["function_call"]["arguments"])["json"]["todo"] == "buy milk" # "json" object needed due to oplangchain

function_message = response["function_message"]
assert function_message["role"] == "function"
assert function_message["name"] == "addTodo"
json_content = json.loads(function_message["content"])
assert json_content["todo"] == "buy milk"

def test_truncated_response():
Expand Down
Loading