-
Notifications
You must be signed in to change notification settings - Fork 188
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
Human In The Loop #1809
Labels
docs
Related to documentation.
Comments
Adding a relatively simple method to def publish_and_wait(self, event: BaseEvent, response_event_type: type[T]) -> T:
future = Future()
def on_event(event: BaseEvent) -> None:
future.set_result(event)
EventBus.add_event_listener(EventListener(on_event=on_event, event_types=[response_event_type]))
EventBus.publish_event(event)
return future.result() import logging
import random
from attrs import define
from griptape.artifacts import InfoArtifact
from griptape.events import BaseEvent, EventBus, EventListener
from griptape.structures import Agent
from griptape.tasks import PromptTask
from griptape.tools import BaseTool
from griptape.utils import Chat
from griptape.utils.decorators import activity
from schema import Schema
@define()
class NeedConfirmationEvent(BaseEvent):
hotel_name: str
@define()
class ProvideConfirmationEvent(BaseEvent):
confirmed: bool
@define()
class HotelBookingTool(BaseTool):
@activity(
{
"description": "Can be used to book a hotel.",
"schema": Schema({"hotel_name": str}),
}
)
def book_hotel(self, hotel_name: str) -> InfoArtifact:
available = self._is_hotel_available(hotel_name)
if available:
confirmed = self._confirm_hotel_booking(hotel_name)
if confirmed:
result = f"Hotel {hotel_name} has been booked."
else:
result = f"Hotel {hotel_name} booking has been canceled."
else:
result = f"Hotel {hotel_name} is not available."
return InfoArtifact(result)
def _confirm_hotel_booking(self, hotel_name: str) -> bool:
provide_confirmation_event = EventBus.publish_and_wait(
NeedConfirmationEvent(hotel_name),
response_event_type=ProvideConfirmationEvent,
)
return provide_confirmation_event.confirmed
def _is_hotel_available(self, _: str) -> bool:
return random.random() > 0.1
def human_confirmation(event: NeedConfirmationEvent) -> None:
response = input(f"Yay or nay {event.hotel_name}: ")
EventBus.publish_event(
ProvideConfirmationEvent(confirmed=response.strip().lower() == "yay")
)
EventBus.add_event_listener(
EventListener(on_event=human_confirmation, event_types=[NeedConfirmationEvent])
)
agent = Agent(tasks=[PromptTask(tools=[HotelBookingTool()])])
Chat(agent, logger_level=logging.INFO).start() |
Same pattern applied to a decorator for blocking Tool uses. import logging
import random
from collections.abc import Callable
from functools import wraps
from typing import Any
from attrs import define
from griptape.artifacts import InfoArtifact
from griptape.events import BaseEvent, EventBus, EventListener
from griptape.structures import Agent
from griptape.tasks import PromptTask
from griptape.tools import BaseTool
from griptape.utils import Chat
from griptape.utils.decorators import activity
from schema import Schema
@define()
class ToolRequest(BaseEvent):
tool_name: str
@define()
class ToolResponse(BaseEvent):
approved: bool = False
feedback: str = ""
def requires_hitl(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
response = EventBus.publish_and_wait(
ToolRequest(func.__name__),
response_event_type=ToolResponse,
)
if response.approved:
return func(*args, **kwargs)
else:
return InfoArtifact(response.feedback)
return wrapper
@define()
class HotelBookingTool(BaseTool):
@activity(
{
"description": "Can be used to book a hotel.",
"schema": Schema({"hotel_name": str}),
}
)
@requires_hitl
def book_hotel(self, hotel_name: str) -> InfoArtifact:
available = self._is_hotel_available(hotel_name)
if available:
result = f"Hotel {hotel_name} is available."
else:
result = f"Hotel {hotel_name} is not available."
return InfoArtifact(result)
def _is_hotel_available(self, _: str) -> bool:
return random.random() > 0.1
def human_confirmation(event: ToolRequest) -> None:
response = input(f"Yay or nay {event.tool_name}: ")
parts = [part.strip().lower() for part in response.split("/")]
approved = parts[0] == "yay"
feedback = parts[1] if len(parts) > 1 else ""
EventBus.publish_event(ToolResponse(approved=approved, feedback=feedback))
EventBus.add_event_listener(
EventListener(on_event=human_confirmation, event_types=[ToolRequest])
)
agent = Agent(tasks=[PromptTask(tools=[HotelBookingTool()])])
Chat(agent, logger_level=logging.INFO).start()
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Griptape needs a clear story for how to implement real-world human in the loop. Many of the demos online rely on either:
input()
in a Tool call.Option 1 is not realistic for real-world usage, option 2 is complex and requires significant code changes. Can we provide a middle ground example that uses what we've got today?
The text was updated successfully, but these errors were encountered: