Skip to content

Commit

Permalink
feat(python): create require approval decorator (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinkys authored Nov 25, 2024
2 parents 2f9bace + b3a74d8 commit 8c93c2e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 31 deletions.
17 changes: 4 additions & 13 deletions clients/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,14 @@ adding an approval workflow to your function:

```py
from phantasmpy import Phantasm
phantasm = Phantasm()

# Replace with your own function and parameters.
parameters = {...}
# Replace with your own function.
@phantasm.require_approval()
def schedule_meeting(...):
pass

phantasm = Phantasm()
response = phantasm.get_approval(
name="schedule_meeting",
parameters=parameters
)

if response.approved:
# Do this only if you trust the approvers.
schedule_meeting(**response.parameters)
else:
fallback()
schedule_meeting(...)
```

## Advanced Usage With LangChain
Expand Down
72 changes: 54 additions & 18 deletions clients/python/phantasmpy/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import grpc
import json
from typing import Any, Dict
from typing import Any, Callable
from functools import wraps
from google.protobuf.empty_pb2 import Empty
from .stubs import receiver_pb2 as protos
from .stubs.receiver_pb2_grpc import ReceiverStub
Expand Down Expand Up @@ -73,23 +74,58 @@ def get_approval(
parameters=response.parameters or "",
)

def require_approval(self, with_parameters: bool = True) -> Callable:
"""Decorator to request approval before executing a function.
def test_get_approval():
params = {
"x": 5,
"y": 10,
}
By default, the decorator will use the parameters provided by the
approvers to call the function. If you want to use the original
parameters, set `with_parameters` to false.
This decorator raises exceptions if:
- The approval request is rejected.
- Error when requesting approval from the server.
Example:
```py
phantasm = Phantasm()
@phantasm.require_approval()
def double(x: int) -> int:
return x * 2
double(x=5)
```
"""

def decorator(function: Callable) -> Callable:
@wraps(function)
def wrapper(**kwargs) -> Callable:
name = function.__name__
docs = function.__doc__ or ""
response = self.get_approval(
name=name,
parameters=kwargs,
context=docs,
)

if response.approved:
if with_parameters:
kwargs = response.parameters
return function(**kwargs)

raise PermissionError("The approval request is not approved.")

return wrapper

return decorator


def test_get_approval():
phantasm = Phantasm()
response = phantasm.get_approval(
name="multiply",
parameters=params,
context="Multiply two numbers: x and y.",
)

if response.approved:
result = response.parameters["x"] * response.parameters["y"]
print("Request Approved")
print(f"Result: {result}")
else:
print("Request Rejected")

@phantasm.require_approval()
def multiply(x: int, y: int) -> int:
"""Multiply two numbers, x and y."""
return x * y

print(multiply(x=5, y=10))

0 comments on commit 8c93c2e

Please sign in to comment.