feat: slidespeak slides generation (#10955)

This commit is contained in:
Kalo Chin 2024-11-22 11:30:21 +09:00 committed by GitHub
parent e8868a7fb9
commit 817b85001f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 315 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

View File

@ -0,0 +1,28 @@
from typing import Any
import requests
from yarl import URL
from core.tools.errors import ToolProviderCredentialValidationError
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
class SlideSpeakProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
api_key = credentials.get("slidespeak_api_key")
base_url = credentials.get("base_url")
if not api_key:
raise ToolProviderCredentialValidationError("API key is missing")
if base_url:
base_url = str(URL(base_url) / "v1")
headers = {"Content-Type": "application/json", "X-API-Key": api_key}
test_task_id = "xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
url = f"{base_url or 'https://api.slidespeak.co/api/v1'}/task_status/{test_task_id}"
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise ToolProviderCredentialValidationError("Invalid SlidePeak API key")

View File

@ -0,0 +1,22 @@
identity:
author: Kalo Chin
name: slidespeak
label:
en_US: SlideSpeak
zh_Hans: SlideSpeak
description:
en_US: Generate presentation slides using SlideSpeak API
zh_Hans: 使用 SlideSpeak API 生成演示幻灯片
icon: icon.png
credentials_for_provider:
slidespeak_api_key:
type: secret-input
required: true
label:
en_US: API Key
zh_Hans: API 密钥
placeholder:
en_US: Enter your SlideSpeak API key
zh_Hans: 输入您的 SlideSpeak API 密钥
url: https://app.slidespeak.co/settings/developer

View File

@ -0,0 +1,163 @@
import asyncio
from dataclasses import asdict, dataclass
from enum import Enum
from typing import Any, Optional, Union
import aiohttp
from pydantic import ConfigDict
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.errors import ToolProviderCredentialValidationError
from core.tools.tool.builtin_tool import BuiltinTool
class SlidesGeneratorTool(BuiltinTool):
"""
Tool for generating presentations using the SlideSpeak API.
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
headers: Optional[dict[str, str]] = None
base_url: Optional[str] = None
timeout: Optional[aiohttp.ClientTimeout] = None
poll_interval: Optional[int] = None
class TaskState(Enum):
FAILURE = "FAILURE"
REVOKED = "REVOKED"
SUCCESS = "SUCCESS"
PENDING = "PENDING"
RECEIVED = "RECEIVED"
STARTED = "STARTED"
@dataclass
class PresentationRequest:
plain_text: str
length: Optional[int] = None
theme: Optional[str] = None
async def _generate_presentation(
self,
session: aiohttp.ClientSession,
request: PresentationRequest,
) -> dict[str, Any]:
"""Generate a new presentation asynchronously"""
async with session.post(
f"{self.base_url}/presentation/generate",
headers=self.headers,
json=asdict(request),
timeout=self.timeout,
) as response:
response.raise_for_status()
return await response.json()
async def _get_task_status(
self,
session: aiohttp.ClientSession,
task_id: str,
) -> dict[str, Any]:
"""Get the status of a task asynchronously"""
async with session.get(
f"{self.base_url}/task_status/{task_id}",
headers=self.headers,
timeout=self.timeout,
) as response:
response.raise_for_status()
return await response.json()
async def _wait_for_completion(
self,
session: aiohttp.ClientSession,
task_id: str,
) -> str:
"""Wait for task completion and return download URL"""
while True:
status = await self._get_task_status(session, task_id)
task_status = self.TaskState(status["task_status"])
if task_status == self.TaskState.SUCCESS:
return status["task_result"]["url"]
if task_status in [self.TaskState.FAILURE, self.TaskState.REVOKED]:
raise Exception(f"Task failed with status: {task_status.value}")
await asyncio.sleep(self.poll_interval)
async def _generate_slides(
self,
plain_text: str,
length: Optional[int],
theme: Optional[str],
) -> str:
"""Generate slides and return the download URL"""
async with aiohttp.ClientSession() as session:
request = self.PresentationRequest(
plain_text=plain_text,
length=length,
theme=theme,
)
result = await self._generate_presentation(session, request)
task_id = result["task_id"]
download_url = await self._wait_for_completion(session, task_id)
return download_url
async def _fetch_presentation(
self,
session: aiohttp.ClientSession,
download_url: str,
) -> bytes:
"""Fetch the presentation file from the download URL"""
async with session.get(download_url, timeout=self.timeout) as response:
response.raise_for_status()
return await response.read()
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""Synchronous invoke method that runs asynchronous code"""
async def async_invoke():
# Extract parameters
plain_text = tool_parameters.get("plain_text", "")
length = tool_parameters.get("length")
theme = tool_parameters.get("theme")
# Ensure runtime and credentials
if not self.runtime or not self.runtime.credentials:
raise ToolProviderCredentialValidationError("Tool runtime or credentials are missing")
# Get API key from credentials
api_key = self.runtime.credentials.get("slidespeak_api_key")
if not api_key:
raise ToolProviderCredentialValidationError("SlideSpeak API key is missing")
# Set configuration
self.headers = {
"Content-Type": "application/json",
"X-API-Key": api_key,
}
self.base_url = "https://api.slidespeak.co/api/v1"
self.timeout = aiohttp.ClientTimeout(total=30)
self.poll_interval = 2
# Run the asynchronous slide generation
try:
download_url = await self._generate_slides(plain_text, length, theme)
# Fetch the presentation file
async with aiohttp.ClientSession() as session:
presentation_bytes = await self._fetch_presentation(session, download_url)
return [
self.create_text_message("Presentation generated successfully"),
self.create_blob_message(
blob=presentation_bytes,
meta={"mime_type": "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
),
]
except Exception as e:
return [self.create_text_message(f"An error occurred: {str(e)}")]
# Run the asynchronous code synchronously
result = asyncio.run(async_invoke())
return result

View File

@ -0,0 +1,102 @@
identity:
name: slide_generator
author: Kalo Chin
label:
en_US: Slides Generator
zh_Hans: 幻灯片生成器
description:
human:
en_US: Generate presentation slides from text using SlideSpeak API.
zh_Hans: 使用 SlideSpeak API 从文本生成演示幻灯片。
llm: This tool converts text input into a presentation using the SlideSpeak API service, with options for slide length and theme.
parameters:
- name: plain_text
type: string
required: true
label:
en_US: Topic or Content
zh_Hans: 主题或内容
human_description:
en_US: The topic or content to be converted into presentation slides.
zh_Hans: 需要转换为幻灯片的内容或主题。
llm_description: A string containing the topic or content to be transformed into presentation slides.
form: llm
- name: length
type: number
required: false
label:
en_US: Number of Slides
zh_Hans: 幻灯片数量
human_description:
en_US: The desired number of slides in the presentation (optional).
zh_Hans: 演示文稿中所需的幻灯片数量(可选)。
llm_description: Optional parameter specifying the number of slides to generate.
form: form
- name: theme
type: select
required: false
label:
en_US: Presentation Theme
zh_Hans: 演示主题
human_description:
en_US: The visual theme for the presentation (optional).
zh_Hans: 演示文稿的视觉主题(可选)。
llm_description: Optional parameter specifying the presentation theme.
options:
- label:
en_US: Adam
zh_Hans: Adam
value: adam
- label:
en_US: Aurora
zh_Hans: Aurora
value: aurora
- label:
en_US: Bruno
zh_Hans: Bruno
value: bruno
- label:
en_US: Clyde
zh_Hans: Clyde
value: clyde
- label:
en_US: Daniel
zh_Hans: Daniel
value: daniel
- label:
en_US: Default
zh_Hans: Default
value: default
- label:
en_US: Eddy
zh_Hans: Eddy
value: eddy
- label:
en_US: Felix
zh_Hans: Felix
value: felix
- label:
en_US: Gradient
zh_Hans: Gradient
value: gradient
- label:
en_US: Iris
zh_Hans: Iris
value: iris
- label:
en_US: Lavender
zh_Hans: Lavender
value: lavender
- label:
en_US: Monolith
zh_Hans: Monolith
value: monolith
- label:
en_US: Nebula
zh_Hans: Nebula
value: nebula
- label:
en_US: Nexus
zh_Hans: Nexus
value: nexus
form: form