Python SDK
Python SDK
Altough it's possible to rebuild your own SDK by using our PluginLab RESTful API, if you're using Python as a backend technology it will be much simpler to use our Python SDK.
To use our Python SDK, follow the instuctions below or directly go to our Github Repository.
Install
pip install pluginlab_admin
Getting started
Get started by initializing the Pluginlab app:
from pluginlab_admin import App
app = App(
secret_key="<PLUGIN_SECRET>",
plugin_id="<PLUGIN_ID>",
)
# to manage auth-related stuff, mostly members
auth = app.get_auth()
The SDK secret should be generated from your Pluginlab dashboard.
Verify the user token
Everytime ChatGPT will talk to your backend through the PluginLab proxy, it will send the user token in the Authorization header as a bearer token.
You can verify the integrity of such token by using the verify_token method:
auth = app.get_auth()
# usually you'd get the token from the Authorization header of the HTTP request, formatted as `Bearer <token>`
token = "eyJhbGc...dQssw5c"
try:
payload = auth.verify_token(token)
# from that point we know the information in the token hasn't been tampered with
# we could, for instance, check if the user has a given plan and take specific action based on that
premium_plan_id = 'KWsmKyJnHBF2Dz1mETDF'
plan_id = payload.user.plan_id
if plan_id:
if plan_id == premium_plan_id:
do_something_special()
else
do_other_stuff()
print(payload.uid)
except Exception as e:
print(f"An error occurred while verifying the token: {e}")
The verified token payload is represented by the following typings:
class TokenPayloadUser:
id: str
email: str
name: Optional[str] = None
given_name: Optional[str] = None
plan_id: Optional[str] = None
price_id: Optional[str] = None
class TokenPayload:
uid: str
iss: str
aud: str
iat: int
exp: int
user: TokenPayloadUser
Get a member by id or email
Note that the payload sent in the verification token (see above) contains almost all user info you should need for production use. So prefer extracting user informations from the idToken instead of calling the API to fetch the member at every request coming from PluginLab. This would result in a meaningless latency in your backend. :)
By email
member = auth.get_member_by_email('johndoe@example.com')
if member is None:
print('No such member.')
else
print(f'Member with email {member.auth.email} signed up with method "{member.auth.sign_in_method}"')
By id
member = auth.get_member_by_id('mem_ec9fd884f7fc81fc9e5576d213b6e826d3109d26')
if member is None:
print('No such member.')
else
print(f'Member with id {member.id} (email={member.auth.email}) signed up with method "{member.auth.sign_in_method}"')
A member is represented with the following typings:
class SignInMethodId(Enum):
EMAIL_AND_PASSWORD = 'email-and-password'
MAGIC_EMAIL_CODE = 'magic-email-code'
GOOGLE = 'google'
class MemberAuth:
is_verified: bool;
email: str;
has_password: bool;
sign_in_method: SignInMethodId;
class Member:
id: str;
auth: MemberAuth;
name: Optional[str]
given_name: Optional[str]
family_name: Optional[str]
picture_url: Optional[str]
custom_fields: dict[str, str];
metadata: dict[str, str];
created_at_ms: int;
updated_at_ms: int;
Get member identities
Once a user logs in to your PluginLab portal using a third-party authentication provider, they get assigned an identity for that specific provider.
An identity might contain what you need to interact with the service on behalf of your user such as an access token or a refresh token.
To retrieve all the identities for a given user, use the auth.get_member_identities method:
identities = auth.get_member_identities("mem_66c72702dfd9c9a34e80cae434a4bf7e6d3d37df")
if identities.google is not None:
print("Google identity found, access token is ", identities.google.access_token)
if identities.github is not None:
# do your stuff
# This applies to every third-party auth provider supported by PluginLab although each might give or not give some information.
Here is the class used to represent an identity:
@dataclass
class IdentityProviderData:
provider: str;
provider_user_id: str;
access_token_expires_at_ms: int | None;
refresh_token_expires_at_ms: int | None;
last_used_at_ms: int | None;
access_token: str | None;
refresh_token: str | None;
Refresh identity access token
For OAuth providers such as Google or Gitlab the access token has a limited lifetime. PluginLab provides an endpoint in case your token needs to be refreshed.
The following endpoint will refresh the token
refreshed_identity = auth.refresh_member_identity_token("mem_66c72702dfd9c9a34e80cae434a4bf7e6d3d37df", "google")
print("Google refreshed access token is ", refreshed_identity.access_token)
The provider id can be either google or gitlab at this moment.
This endpoint will return a IdentityProviderData.
Note this endpoint currently works only with google and gitlab. Github does not provide any refresh token at this moment. If you need support for more providers feel free to reach out.
Handling refresh errors
Sometimes it's possible that the refresh token is revoked or expired. In that case PluginLab will not be able to refresh the token anymore.
In that situation, PluginLab will return the following error with a HTTP CODE 422:
{
"code": "refresh-token-failed",
"message": "The refresh token request has been rejected by the provider. It most likely means the refresh token is expired. Please ask your user to sign-in again using your auth-portal url."
}
List members
Basic
paginated_result = auth.get_members()
for member in paginated_result.items:
print(member.id)
With adjustable limit and pagination
You can request up to 100 members at a time and get a specific page by providing a cursor obtained from a previous call.
paginated_result1 = auth.get_members(50)
cursor = paginated_result1.next_page_cursor
if not cursor is None:
paginated_result2 = auth.get_members(50, cursor)
Create a member
created_member = auth.create_member(
email='johndoe@example.com',
password='12345',
is_verified=True,
metadata={
'origin': 'backend-sdk'
}
)
print(created_member)
As the typings show, both is_verified and metadata fields are optional to create a user.
Update a member
auth.update_member(
id='mem_ec9fd884f7fc81fc9e5576d213b6e826d3109d26',
given_name="John the king of examples",
family_name="Doe",
name="John Doe",
picture_url="https://example.com/john-doe.png",
metadata={}
)
Except the id field that is used to identify the user to update, all the other updatable fields are optional in this function call.
Delete a member
auth.delete_member(
id='mem_ec9fd884f7fc81fc9e5576d213b6e826d3109d26',
)
Note that the member ID is derived from the email the user used to sign up. Therefore recreating an account with the same email will NOT reset usage quotas.
Dealing with webhooks
Pluginlab signs each webhook payload it sends you using a secret key you can retrieve from your webhooks settings on the Pluginlab dashboard.
This package provides facilities to easiliy verifiy the payload of a webhook.
Here is how, using a flask handler as an example:
import os
from pluginlab_admin import WebhookHeaders, Webhook
webhook_secret = os.getenv('WEBHOOK_SECRET')
webhook = Webhook(webhook_secret)
@app.route('/webhook', methods=['POST'])
def api():
sig = request.headers.get(WebhookHeaders.Signature.value)
raw_body = request.get_data(as_text=True)
if not sig:
return jsonify({'message': 'No signature was found.'})
if not webhook.is_signature_valid(raw_body, sig):
return jsonify({'message': 'Invalid signature.'})
// process the payload as you like
Make sure you are providing the is_signature_valid method with the raw body of the request, as it was originally sent.
Events
On PluginLab, an event is used as a representaiton of a plugin request and is traditionally generated on each request made by ChatGPT.
The SDK offers limited but useful utilities to deal with events.
To get started, you need to leverage the event service by calling the app.get_event method.
// assuming app was previously initialized as showed above
event = app.get_event();
Create a custom event
If you want to create custom events that will appear in your PluginLab dashboard and count as part of your users' quota, you can rely on the create_custom method.
This is especially useful if your service is accessible from other means that ChatGPT but you still want to account for usage using PluginLab.
from pluginlab_admin import EventLocation
location = EventLocation(
country_code="US",
subdivision_code="CA", # optional
)
event.create_custom(
# case insensitive, max. 10 chars
event_source="WEB_UI",
# optional, quota ignored if not present
member_id="mem_fd9a1ba5e385e3d97412cdfbd7b8284c4f0c8e18",
# optional
location=location,
# optional, defaults to true
is_in_quota=True
)
Assuming the provided member_id refers to a valid plugin member then a new event will get created once the above code runs. It will appear in your PluginLab dashboard's event section.
Feeling lost?
Feel free to contact us using the chatbox on the bottom right corner and we will be more than happy to help!
Updated on: 17/08/2023
Thank you!