Skip to content
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
3 changes: 3 additions & 0 deletions examples/calendar_negotiator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from openai import OpenAI

from npiai.core import Agent
from npiai.core.hitl import ConsoleHITLHandler
from npiai.app.google import Gmail, Calendar
from npiai.app.human_feedback import ConsoleFeedback

Expand Down Expand Up @@ -53,9 +54,11 @@ def main():
description='Schedule meetings with others using gmail and google calendar',
prompt=PROMPT,
llm=OpenAI(),
hitl_handler=ConsoleHITLHandler(),
)

negotiator.use(Calendar(), Gmail(), ConsoleFeedback())

print('Negotiator: What\'s your task for me?')
task = input('User: ')
print('')
Expand Down
14 changes: 10 additions & 4 deletions examples/github_notifier/main.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
from openai import OpenAI
from npiai.core import Agent
from npiai.core.hitl import ConsoleHITLHandler
from npiai.app.github import GitHub
from npiai.app.google import Gmail
from npiai.app.human_feedback import ConsoleFeedback


def main():
agent = Agent(
agent_name='github_notifier',
prompt='You are a Github Notifier that informs users when a new issue or pull request is open.',
description='Github Notifier that informs users when a new issue or pull request is open',
llm=OpenAI()
llm=OpenAI(),
hitl_handler=ConsoleHITLHandler()
)
agent.use(GitHub(), Gmail(), ConsoleFeedback())

agent.when("idiotWu/npi-test has a new issue, send an email to [email protected] with the body of issue")
agent.use(GitHub(), Gmail())

task = 'idiotWu/npi-test has a new issue, send an email to [email protected] with the body of issue'

print(f'[GitHub Notifier] Task: {task}')

agent.when(task)


if __name__ == '__main__':
Expand Down
3 changes: 3 additions & 0 deletions examples/twitter_discord/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from openai import OpenAI

from npiai.core import Agent
from npiai.core.hitl import ConsoleHITLHandler
from npiai.app.discord import Discord
from npiai.browser_app.twitter import Twitter
from npiai.app.human_feedback import ConsoleFeedback
Expand Down Expand Up @@ -32,9 +33,11 @@ def main():
description='Retrieve data from Twitter and send messages to Discord',
prompt=PROMPT,
llm=OpenAI(),
hitl_handler=ConsoleHITLHandler(),
)

negotiator.use(Twitter(), Discord(), ConsoleFeedback())

print('Twitter Crawler: What\'s your task for me?')
task = input('User: ')
print('')
Expand Down
41 changes: 31 additions & 10 deletions sdk/python/npiai/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ class App:
__app_type: api_pb2.AppType
__npi_endpoint: str
stub: api_pb2_grpc.AppServerStub
hitl_handler: hitl.HITLHandler = None

def __init__(self, app_name: str, app_type: api_pb2.AppType, endpoint: str = "localhost:9140"):
def __init__(
self,
app_name: str,
app_type: api_pb2.AppType,
endpoint: str = "localhost:9140",
hitl_handler: hitl.HITLHandler = None,
):
self.__app_name = app_name
if endpoint is None:
endpoint = "localhost:9140"
self.__npi_endpoint = endpoint
self.__app_type = app_type
channel = grpc.insecure_channel(self.__npi_endpoint)
self.stub = api_pb2_grpc.AppServerStub(channel)
self.hitl_handler = None
self.hitl_handler = hitl_handler

def tool_name(self):
return self.__app_name
Expand Down Expand Up @@ -95,7 +102,12 @@ def hitl(self, handler: hitl.HITLHandler):
self.hitl_handler = handler

def __call_human(self, resp: api_pb2.Response) -> api_pb2.Request:
human_resp = self.hitl_handler.handle(hitl.convert_to_hitl_request(resp.action_response))
human_resp = self.hitl_handler.handle(
hitl.convert_to_hitl_request(
req=resp.action_response,
app_name=self.__app_name
)
)
if human_resp is hitl.ACTION_APPROVED:
result = "approved"
else:
Expand All @@ -119,30 +131,39 @@ class Agent:
__llm: OpenAI
tool_choice: ChatCompletionToolChoiceOptionParam = "auto"
fn_map: dict = {}
hitl_handler: hitl.HITLHandler = None

def __init__(
self,
agent_name: str,
description: str,
prompt: str,
endpoint: str = None,
llm: OpenAI = None
llm: OpenAI = None,
hitl_handler: hitl.HITLHandler = None,
):
self.__agent_name = agent_name
self.__description = description
self.__prompt = prompt
self.__npi_endpoint = endpoint
self.__llm = llm
self.hitl_handler = hitl_handler

def use(self, *tools: App):
for app in tools:
def use(self, *apps: App):
for app in apps:
# inherit HITL handler
if app.hitl_handler is None:
app.hitl_handler = self.hitl_handler
app_name = app.tool_name()
self.fn_map[app_name] = app

def group(self, *tools: 'Agent'):
for app in tools:
app_name = app.__as_app().tool_name()
self.fn_map[app_name] = app
def group(self, *agents: 'Agent'):
for agent in agents:
app_name = agent.__as_app().tool_name()
self.fn_map[app_name] = agent

def hitl(self, handler: hitl.HITLHandler):
self.hitl_handler = handler

def run(self, msg: str) -> str:

Expand Down
28 changes: 20 additions & 8 deletions sdk/python/npiai/core/hitl.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,37 @@ def type(self) -> api_pb2.ActionType:
pass


def convert_to_hitl_request(req: api_pb2.ActionRequiredResponse) -> HITLRequest:
def convert_to_hitl_request(req: api_pb2.ActionRequiredResponse, app_name: str = None) -> HITLRequest:
match req.type:
case api_pb2.ActionType.INFORMATION:
return HITLRequest(
code=ActionRequestCode.INFORMATION,
message=req.message
message=req.message,
app_name=app_name,
)
case api_pb2.ActionType.CONFIRMATION:
return HITLRequest(
code=ActionRequestCode.CONFIRMATION,
message=req.message
message=req.message,
app_name=app_name,
)
case api_pb2.ActionType.SINGLE_SELECTION:
return HITLRequest(
code=ActionRequestCode.SINGLE_SELECTION,
message=req.message
message=req.message,
app_name=app_name,
)
case api_pb2.ActionType.MULTIPLE_SELECTION:
return HITLRequest(
code=ActionRequestCode.MULTIPLE_SELECTION,
message=req.message
message=req.message,
app_name=app_name,
)
return HITLRequest(code=ActionRequestCode.UNKNOWN_ACTION, message="invalid action request")
return HITLRequest(
code=ActionRequestCode.UNKNOWN_ACTION,
message="invalid action request",
app_name=app_name,
)


class ConsoleHITLHandler(HITLHandler):
Expand All @@ -91,8 +99,12 @@ def handle(self, req: HITLRequest) -> HITLResponse:
resp = ACTION_APPROVED
else:
resp = ACTION_DENIED
human_response = input(colored(f'[{req.app_name}]: Do you want give a message back? (typing Enter to '
f'skip) > ', 'magenta'))
human_response = input(
colored(
f'[{req.app_name}]: Do you want give a message back? (typing Enter to '
f'skip) > ', 'magenta'
)
)
if human_response is not None:
resp.message = human_response
return resp
Expand Down