소스 검색

feat: slidespeak slides generation (#10955)

tags/0.12.0
Kalo Chin 11 달 전
부모
커밋
817b85001f
No account linked to committer's email address

BIN
api/core/tools/provider/builtin/slidespeak/_assets/icon.png 파일 보기


+ 28
- 0
api/core/tools/provider/builtin/slidespeak/slidespeak.py 파일 보기

@@ -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")

+ 22
- 0
api/core/tools/provider/builtin/slidespeak/slidespeak.yaml 파일 보기

@@ -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

+ 163
- 0
api/core/tools/provider/builtin/slidespeak/tools/slides_generator.py 파일 보기

@@ -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

+ 102
- 0
api/core/tools/provider/builtin/slidespeak/tools/slides_generator.yaml 파일 보기

@@ -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

Loading…
취소
저장