Skip to content

Commit

Permalink
Update: Fixed bot management + replaced js2py with quickjs for python…
Browse files Browse the repository at this point in the history
… 3.12+
  • Loading branch information
snowby666 committed Jun 19, 2024
1 parent db1541e commit 568faa6
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 90 deletions.
21 changes: 8 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ Or you can install a proxy-support version of this library for **Python 3.9+**
```ShellSession
pip install -U poe-api-wrapper[proxy]
```
You can also use the Async version:
```ShellSession
pip install -U poe-api-wrapper[async]
```
Quick setup for Async Client:
```py
from poe_api_wrapper import AsyncPoeApi
Expand Down Expand Up @@ -208,8 +204,7 @@ Copy the values of `p-b` and `p-lat` cookies

#### Getting formkey (*optional*)
> [!IMPORTANT]
> **poe-api-wrapper** depends on library **js2py** to get formkey. If you are using `Python 3.12+`, it may not be compatible, so please downgrade your version.
> By default, poe_api_wrapper will automatically retrieve formkey for you. If it doesn't work, please pass this token manually by following these steps:
> By default, **poe-api-wrapper** will automatically retrieve formkey for you. If it doesn't work, please pass this token manually by following these steps:
There are two ways to get formkey:

Expand Down Expand Up @@ -521,30 +516,30 @@ print(client.get_botInfo(handle=bot))
```py
print(client.get_available_creation_models())
>> Output:
['chinchilla', 'mixtral8x7bchat', 'playgroundv25', 'stablediffusionxl', 'dalle3', 'a2', 'claude_2_short', 'gemini_pro', 'a2_2', 'a2_100k', 'beaver', 'llama_2_70b_chat', 'mythomaxl213b', 'claude_2_1_bamboo', 'claude_2_1_cedar', 'claude_3_haiku', 'claude_3_haiku_200k']
['chinchilla', 'claude_3_haiku', 'mythomaxl213b', 'mixtral8x7bchat', 'playgroundv25', 'stablediffusionxl', 'dalle3', 'a2', 'claude_2_short', 'gemini_pro', 'a2_2', 'a2_100k', 'gpt4_o', 'gpt4_o_128k', 'beaver', 'vizcacha', 'gemini_1_5_pro', 'gemini_1_5_pro_128k', 'gemini_1_5_pro_1m', 'gemini_1_5_flash', 'gemini_1_5_flash_128k', 'gemini_1_5_flash_1m', 'llama_2_70b_chat', 'claude_2_1_bamboo', 'claude_2_1_cedar', 'claude_3_haiku_200k', 'claude_3_sonnet_200k', 'claude_3_opus_200k', 'sd3turbo', 'stablediffusion3', 'ideogram']
```
- Creating a new Bot
```py
client.create_bot("BOT_NAME", "PROMPT_HERE", base_model="a2")
client.create_bot(handle="BOT_NAME", prompt="PROMPT_HERE", base_model="a2")

# Using knowledge bases (you can use source_ids from uploaded knowledge bases for your custom bot)
client.create_bot("BOT_NAME", "PROMPT_HERE", base_model="a2", knowledgeSourceIds=source_ids, shouldCiteSources=True)
client.create_bot(handle="BOT_NAME", prompt="PROMPT_HERE", base_model="a2", knowledgeSourceIds=source_ids, shouldCiteSources=True)
```
- Editing a Bot
```py
client.edit_bot("(NEW)BOT_NAME", "PROMPT_HERE", base_model='chinchilla')
client.edit_bot(handle="BOT_NAME", prompt="PROMPT_HERE", new_handle="NEW_BOT_NAME", base_model='chinchilla')

# Adding knowledge bases
client.edit_bot("(NEW)BOT_NAME", "PROMPT_HERE", base_model='chinchilla', knowledgeSourceIdsToAdd=source_ids, shouldCiteSources=True)
client.edit_bot(handle="BOT_NAME", prompt="PROMPT_HERE", new_handle="NEW_BOT_NAME", base_model='chinchilla', knowledgeSourceIdsToAdd=source_ids, shouldCiteSources=True)

# Removing knowledge bases
client.edit_bot("(NEW)BOT_NAME", "PROMPT_HERE", base_model='chinchilla', knowledgeSourceIdsToRemove=source_ids, shouldCiteSources=True)
client.edit_bot(handle="BOT_NAME", prompt="PROMPT_HERE", new_handle="NEW_BOT_NAME", base_model='chinchilla', knowledgeSourceIdsToRemove=source_ids, shouldCiteSources=True)
```
> [!TIP]
> You can also use both `knowledgeSourceIdsToAdd` and `knowledgeSourceIdsToRemove` at the same time.
- Deleting a Bot
```py
client.delete_bot("BOT_NAME")
client.delete_bot(handle="BOT_NAME")
```
- Getting available bots (your bots section)
```py
Expand Down
92 changes: 59 additions & 33 deletions poe_api_wrapper/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from time import sleep, time
from httpx import Client, ReadTimeout, ConnectError
from requests_toolbelt import MultipartEncoder
import os, secrets, string, random, websocket, json, threading, queue, ssl, hashlib
import os, secrets, string, random, websocket, json, threading, queue, ssl, hashlib, re
from loguru import logger
from typing import Generator
from .utils import (
Expand Down Expand Up @@ -1228,9 +1228,13 @@ def get_available_creation_models(self):
return models

def create_bot(self, handle, prompt, display_name=None, base_model="chinchilla", description="", intro_message="",
api_key=None, api_bot=False, api_url=None, prompt_public=True, pfp_url=None, linkification=False,
markdown_rendering=True, suggested_replies=False, private=False, temperature=None, customMessageLimit=None,
messagePriceCc=None, shouldCiteSources=True, knowledgeSourceIds:dict = {}):
api_key=None, api_bot=False, api_url=None, prompt_public=True, pfp_url=None, markdown_rendering=True,
suggested_replies=False, private=False, temperature=None, customMessageLimit=None, messagePriceCc=None,
shouldCiteSources=True, knowledgeSourceIds:dict = {}, allowRelatedBotRecommendations=True):

if not re.match("^[a-zA-Z0-9_.-]{4,20}$", handle):
raise ValueError("Invalid handle. Should be unique and use 4-20 characters, including letters, numbers, dashes, periods and underscores.")

bot_models = self.get_available_creation_models()
if base_model not in bot_models:
raise ValueError(f"Invalid base model {base_model}. Please choose from {bot_models}")
Expand All @@ -1243,6 +1247,7 @@ def create_bot(self, handle, prompt, display_name=None, base_model="chinchilla",
sourceIds = [item for sublist in knowledgeSourceIds.values() for item in sublist]
else:
sourceIds = []

variables = {
"model": base_model,
"displayName": display_name,
Expand All @@ -1255,16 +1260,17 @@ def create_bot(self, handle, prompt, display_name=None, base_model="chinchilla",
"apiUrl": api_url,
"apiKey": api_key,
"isApiBot": api_bot,
"hasLinkification": linkification,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIds": sourceIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources
"shouldCiteSources": shouldCiteSources,
"allowRelatedBotRecommendations": allowRelatedBotRecommendations,
}

result = self.send_request('gql_POST', 'PoeBotCreate', variables)['data']['poeBotCreate']
if result["status"] != "success":
logger.error(f"Poe returned an error while trying to create a bot: {result['status']}")
Expand All @@ -1282,11 +1288,15 @@ def get_botData(self, handle):
f"Fail to get botId from {handle}. Make sure the bot exists and you have access to it."
) from e

def edit_bot(self, handle, prompt, display_name=None, base_model="chinchilla", description="",
intro_message="", api_key=None, api_url=None, private=False,
prompt_public=True, pfp_url=None, linkification=False,
markdown_rendering=True, suggested_replies=False, temperature=None, customMessageLimit=None,
knowledgeSourceIdsToAdd:dict = {}, knowledgeSourceIdsToRemove:dict = {}, messagePriceCc=None, shouldCiteSources=True):
def edit_bot(self, handle, prompt, new_handle=None, display_name=None, base_model="chinchilla", description="",
intro_message="", api_key=None, api_url=None, private=False, prompt_public=True,
pfp_url=None, markdown_rendering=True, suggested_replies=False, temperature=None,
customMessageLimit=None, knowledgeSourceIdsToAdd:dict = {}, knowledgeSourceIdsToRemove:dict = {},
messagePriceCc=None, shouldCiteSources=True, allowRelatedBotRecommendations=True):

if new_handle and not re.match("^[a-zA-Z0-9_.-]{4,20}$", new_handle):
raise ValueError("Invalid handle. Should be unique and use 4-20 characters, including letters, numbers, dashes, periods and underscores.")

bot_models = self.get_available_creation_models()
if base_model not in bot_models:
raise ValueError(f"Invalid base model {base_model}. Please choose from {bot_models}")
Expand All @@ -1299,34 +1309,50 @@ def edit_bot(self, handle, prompt, display_name=None, base_model="chinchilla", d
removeIds = [item for sublist in knowledgeSourceIdsToRemove.values() for item in sublist]
else:
removeIds = []

try:
botId = self.get_botData(handle)['botId']
except Exception as e:
raise ValueError(
f"Fail to get botId from {handle}. Make sure the bot exists and you have access to it."
) from e

variables = {
"baseBot": base_model,
"botId": self.get_botData(handle)['botId'],
"handle": handle,
"displayName": display_name,
"prompt": prompt,
"isPromptPublic": prompt_public,
"introduction": intro_message,
"description": description,
"profilePictureUrl": pfp_url,
"apiUrl": api_url,
"apiKey": api_key,
"hasLinkification": linkification,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIdsToAdd": addIds,
"knowledgeSourceIdsToRemove": removeIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources
"baseBot": base_model,
"botId": botId,
"handle": new_handle if new_handle != None else handle,
"displayName": display_name,
"prompt": prompt,
"isPromptPublic": prompt_public,
"introduction": intro_message,
"description": description,
"profilePictureUrl": pfp_url,
"apiUrl": api_url,
"apiKey": api_key,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIdsToAdd": addIds,
"knowledgeSourceIdsToRemove": removeIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources,
"allowRelatedBotRecommendations": allowRelatedBotRecommendations,
}

result = self.send_request('gql_POST', 'PoeBotEdit', variables)["data"]["poeBotEdit"]
if result["status"] != "success":
logger.error(f"Poe returned an error while trying to edit a bot: {result['status']}")
else:
logger.info(f"Bot edited successfully | {handle}")
if new_handle and handle != new_handle:
if handle in self.current_thread:
self.current_thread[new_handle] = self.current_thread.pop(handle)
logger.info(f"Bot edited successfully | New handle from {handle} to {new_handle}")
else:
logger.info(f"Bot edited successfully | {handle}")

return {"status": result["status"], "botId": botId, "handle": new_handle if new_handle != None else handle}

def delete_bot(self, handle):
isCreator = self.get_botData(handle)['viewerIsCreator']
Expand Down
96 changes: 61 additions & 35 deletions poe_api_wrapper/async_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
ASYNC = True
except ImportError:
ASYNC = False
import asyncio, json, queue, random, ssl, threading, websocket, string, secrets, os, hashlib
import asyncio, json, queue, random, ssl, threading, websocket, string, secrets, os, hashlib, re
from time import time
from typing import AsyncIterator
from loguru import logger
Expand Down Expand Up @@ -1227,9 +1227,13 @@ async def get_available_creation_models(self):
return models

async def create_bot(self, handle, prompt, display_name=None, base_model="chinchilla", description="", intro_message="",
api_key=None, api_bot=False, api_url=None, prompt_public=True, pfp_url=None, linkification=False,
markdown_rendering=True, suggested_replies=False, private=False, temperature=None, customMessageLimit=None,
messagePriceCc=None, shouldCiteSources=True, knowledgeSourceIds:dict = {}):
api_key=None, api_bot=False, api_url=None, prompt_public=True, pfp_url=None, markdown_rendering=True,
suggested_replies=False, private=False, temperature=None, customMessageLimit=None, messagePriceCc=None,
shouldCiteSources=True, knowledgeSourceIds:dict = {}, allowRelatedBotRecommendations=True):

if not re.match("^[a-zA-Z0-9_.-]{4,20}$", handle):
raise ValueError("Invalid handle. Should be unique and use 4-20 characters, including letters, numbers, dashes, periods and underscores.")

bot_models = await self.get_available_creation_models()
if base_model not in bot_models:
raise ValueError(f"Invalid base model {base_model}. Please choose from {bot_models}")
Expand All @@ -1242,6 +1246,7 @@ async def create_bot(self, handle, prompt, display_name=None, base_model="chinch
sourceIds = [item for sublist in knowledgeSourceIds.values() for item in sublist]
else:
sourceIds = []

variables = {
"model": base_model,
"displayName": display_name,
Expand All @@ -1254,16 +1259,17 @@ async def create_bot(self, handle, prompt, display_name=None, base_model="chinch
"apiUrl": api_url,
"apiKey": api_key,
"isApiBot": api_bot,
"hasLinkification": linkification,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIds": sourceIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources
"shouldCiteSources": shouldCiteSources,
"allowRelatedBotRecommendations": allowRelatedBotRecommendations,
}

result = await self.send_request('gql_POST', 'PoeBotCreate', variables)['data']['poeBotCreate']
if result["status"] != "success":
logger.error(f"Poe returned an error while trying to create a bot: {result['status']}")
Expand All @@ -1281,12 +1287,16 @@ async def get_botData(self, handle):
f"Fail to get botId from {handle}. Make sure the bot exists and you have access to it."
) from e

async def edit_bot(self, handle, prompt, display_name=None, base_model="chinchilla", description="",
intro_message="", api_key=None, api_url=None, private=False,
prompt_public=True, pfp_url=None, linkification=False,
markdown_rendering=True, suggested_replies=False, temperature=None, customMessageLimit=None,
knowledgeSourceIdsToAdd:dict = {}, knowledgeSourceIdsToRemove:dict = {}, messagePriceCc=None, shouldCiteSources=True):
bot_models = await self.get_available_creation_models()
async def edit_bot(self, handle, prompt, new_handle=None, display_name=None, base_model="chinchilla", description="",
intro_message="", api_key=None, api_url=None, private=False, prompt_public=True,
pfp_url=None, markdown_rendering=True, suggested_replies=False, temperature=None,
customMessageLimit=None, knowledgeSourceIdsToAdd:dict = {}, knowledgeSourceIdsToRemove:dict = {},
messagePriceCc=None, shouldCiteSources=True, allowRelatedBotRecommendations=True):

if new_handle and not re.match("^[a-zA-Z0-9_.-]{4,20}$", new_handle):
raise ValueError("Invalid handle. Should be unique and use 4-20 characters, including letters, numbers, dashes, periods and underscores.")

bot_models = await self.get_available_creation_models()
if base_model not in bot_models:
raise ValueError(f"Invalid base model {base_model}. Please choose from {bot_models}")

Expand All @@ -1298,35 +1308,51 @@ async def edit_bot(self, handle, prompt, display_name=None, base_model="chinchil
removeIds = [item for sublist in knowledgeSourceIdsToRemove.values() for item in sublist]
else:
removeIds = []

try:
botId = await self.get_botData(handle)['botId']
except Exception as e:
raise ValueError(
f"Fail to get botId from {handle}. Make sure the bot exists and you have access to it."
) from e

variables = {
"baseBot": base_model,
"botId": await self.get_botData(handle)['botId'],
"handle": handle,
"displayName": display_name,
"prompt": prompt,
"isPromptPublic": prompt_public,
"introduction": intro_message,
"description": description,
"profilePictureUrl": pfp_url,
"apiUrl": api_url,
"apiKey": api_key,
"hasLinkification": linkification,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIdsToAdd": addIds,
"knowledgeSourceIdsToRemove": removeIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources
"baseBot": base_model,
"botId": botId,
"handle": new_handle if new_handle != None else handle,
"displayName": display_name,
"prompt": prompt,
"isPromptPublic": prompt_public,
"introduction": intro_message,
"description": description,
"profilePictureUrl": pfp_url,
"apiUrl": api_url,
"apiKey": api_key,
"hasMarkdownRendering": markdown_rendering,
"hasSuggestedReplies": suggested_replies,
"isPrivateBot": private,
"temperature": temperature,
"customMessageLimit": customMessageLimit,
"knowledgeSourceIdsToAdd": addIds,
"knowledgeSourceIdsToRemove": removeIds,
"messagePriceCc": messagePriceCc,
"shouldCiteSources": shouldCiteSources,
"allowRelatedBotRecommendations": allowRelatedBotRecommendations,
}

result = await self.send_request('gql_POST', 'PoeBotEdit', variables)["data"]["poeBotEdit"]
if result["status"] != "success":
logger.error(f"Poe returned an error while trying to edit a bot: {result['status']}")
else:
logger.info(f"Bot edited successfully | {handle}")

if new_handle and handle != new_handle:
if handle in self.current_thread:
self.current_thread[new_handle] = self.current_thread.pop(handle)
logger.info(f"Bot edited successfully | New handle from {handle} to {new_handle}")
else:
logger.info(f"Bot edited successfully | {handle}")

return {"status": result["status"], "botId": botId, "handle": new_handle if new_handle != None else handle}

async def delete_bot(self, handle):
isCreator = await self.get_botData(handle)['viewerIsCreator']
botId = await self.get_botData(handle)['botId']
Expand Down
Loading

0 comments on commit 568faa6

Please sign in to comment.