diff --git a/pyproject.toml b/pyproject.toml index 743aa8e..82a625d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "fastapi_poe" -version = "0.0.47" +version = "0.0.48" authors = [ { name="Lida Li", email="lli@quora.com" }, { name="Jelle Zijlstra", email="jelle@quora.com" }, diff --git a/src/fastapi_poe/base.py b/src/fastapi_poe/base.py index 85ff237..790f726 100644 --- a/src/fastapi_poe/base.py +++ b/src/fastapi_poe/base.py @@ -29,6 +29,7 @@ from starlette.types import Message from typing_extensions import deprecated, overload +from fastapi_poe.client import PROTOCOL_VERSION, sync_bot_settings from fastapi_poe.templates import ( IMAGE_VISION_ATTACHMENT_TEMPLATE, TEXT_ATTACHMENT_TEMPLATE, @@ -132,6 +133,7 @@ class PoeBot: path: str = "/" # Path where this bot will be exposed access_key: Optional[str] = None # Access key for this bot + bot_name: Optional[str] = None # Name of the bot using this PoeBot instance in Poe should_insert_attachment_messages: bool = ( True # Whether to insert attachment messages into the conversation ) @@ -866,6 +868,7 @@ def make_app( bot: Union[PoeBot, Sequence[PoeBot]], access_key: str = "", *, + bot_name: str = "", api_key: str = "", allow_without_key: bool = False, app: Optional[FastAPI] = None, @@ -910,11 +913,18 @@ def make_app( raise ValueError( "Cannot provide api_key if the bot object already has an access key" ) + + if bot.bot_name is None: + bot.bot_name = bot_name + elif bot_name: + raise ValueError( + "Cannot provide bot_name if the bot object already has a bot_name" + ) bots = [bot] else: - if access_key or api_key: + if access_key or api_key or bot_name: raise ValueError( - "When serving multiple bots, the access_key must be set on each bot" + "When serving multiple bots, the access_key/bot_name must be set on each bot" ) bots = bot @@ -933,6 +943,37 @@ def make_app( if bot_obj.access_key is None and not allow_without_key: raise ValueError(f"Missing access key on {bot_obj}") _add_routes_for_bot(app, bot_obj) + if not bot_obj.bot_name or not bot_obj.access_key: + logger.warning("\n************* Warning *************") + logger.warning( + "Bot name or access key is not set for PoeBot.\n" + "Bot settings will NOT be synced automatically on server start/update." + "Please remember to sync bot settings manually.\n\n" + "For more information, see: https://creator.poe.com/docs/server-bots-functional-guides#updating-bot-settings" + ) + logger.warning("\n************* Warning *************") + else: + try: + settings_response = asyncio.run( + bot_obj.get_settings( + SettingsRequest(version=PROTOCOL_VERSION, type="settings") + ) + ) + sync_bot_settings( + bot_name=bot_obj.bot_name, + settings=settings_response.model_dump(), + access_key=bot_obj.access_key, + ) + except Exception as e: + logger.error("\n*********** Error ***********") + logger.error( + f"Bot settings sync failed for {bot_obj.bot_name}: \n{e}\n\n" + ) + logger.error("Please sync bot settings manually.\n\n") + logger.error( + "For more information, see: https://creator.poe.com/docs/server-bots-functional-guides#updating-bot-settings" + ) + logger.error("\n*********** Error ***********") # Uncomment this line to print out request and response # app.add_middleware(LoggingMiddleware) diff --git a/src/fastapi_poe/client.py b/src/fastapi_poe/client.py index 5aeafeb..bbc27ce 100644 --- a/src/fastapi_poe/client.py +++ b/src/fastapi_poe/client.py @@ -617,17 +617,30 @@ async def get_final_response( def sync_bot_settings( bot_name: str, access_key: str = "", - base_url: str = "https://api.poe.com/bot/fetch_settings/", + *, + settings: Optional[Dict[str, Any]] = None, + base_url: str = "https://api.poe.com/bot/", ) -> None: - """Sync bot settings with the Poe server using bot name and its Access Key.""" + """Fetch settings from the running bot server, and then sync them with Poe.""" try: - response = httpx.post(f"{base_url}{bot_name}/{access_key}/{PROTOCOL_VERSION}") + if settings is None: + response = httpx.post( + f"{base_url}fetch_settings/{bot_name}/{access_key}/{PROTOCOL_VERSION}" + ) + else: + headers = {"Content-Type": "application/json"} + response = httpx.post( + f"{base_url}update_settings/{bot_name}/{access_key}/{PROTOCOL_VERSION}", + headers=headers, + json=settings, + ) if response.status_code != 200: raise BotError( - f"Error fetching settings for bot {bot_name}: {response.text}" + f"Error syncing settings for bot {bot_name}: {response.text}" ) except httpx.ReadTimeout as e: - raise BotError( - f"Timeout fetching settings for bot {bot_name}. Try sync manually later." - ) from e + error_message = f"Timeout syncing settings for bot {bot_name}." + if not settings: + error_message += " Check that the bot server is running." + raise BotError(error_message) from e print(response.text)