diff --git a/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py b/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py new file mode 100644 index 0000000000..f4f12c9fe1 --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py @@ -0,0 +1,307 @@ +import logging +import time +from enum import Enum +from typing import Optional + +import requests +from pydantic import Field + +from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema +from backend.data.model import BlockSecret, SchemaField, SecretField + + +class AudioTrack(str, Enum): + OBSERVER = ("Observer",) + FUTURISTIC_BEAT = ("Futuristic Beat",) + SCIENCE_DOCUMENTARY = ("Science Documentary",) + HOTLINE = ("Hotline",) + BLADERUNNER_2049 = ("Bladerunner 2049",) + A_FUTURE = ("A Future",) + ELYSIAN_EMBERS = ("Elysian Embers",) + INSPIRING_CINEMATIC = ("Inspiring Cinematic",) + BLADERUNNER_REMIX = ("Bladerunner Remix",) + IZZAMUZZIC = ("Izzamuzzic",) + NAS = ("Nas",) + PARIS_ELSE = ("Paris - Else",) + SNOWFALL = ("Snowfall",) + BURLESQUE = ("Burlesque",) + CORNY_CANDY = ("Corny Candy",) + HIGHWAY_NOCTURNE = ("Highway Nocturne",) + I_DONT_THINK_SO = ("I Don't Think So",) + LOSING_YOUR_MARBLES = ("Losing Your Marbles",) + REFRESHER = ("Refresher",) + TOURIST = ("Tourist",) + TWIN_TYCHES = ("Twin Tyches",) + + @property + def audio_url(self): + audio_urls = { + AudioTrack.OBSERVER: "https://cdn.tfrv.xyz/audio/observer.mp3", + AudioTrack.FUTURISTIC_BEAT: "https://cdn.tfrv.xyz/audio/_futuristic-beat.mp3", + AudioTrack.SCIENCE_DOCUMENTARY: "https://cdn.tfrv.xyz/audio/_science-documentary.mp3", + AudioTrack.HOTLINE: "https://cdn.tfrv.xyz/audio/_hotline.mp3", + AudioTrack.BLADERUNNER_2049: "https://cdn.tfrv.xyz/audio/_bladerunner-2049.mp3", + AudioTrack.A_FUTURE: "https://cdn.tfrv.xyz/audio/a-future.mp3", + AudioTrack.ELYSIAN_EMBERS: "https://cdn.tfrv.xyz/audio/elysian-embers.mp3", + AudioTrack.INSPIRING_CINEMATIC: "https://cdn.tfrv.xyz/audio/inspiring-cinematic-ambient.mp3", + AudioTrack.BLADERUNNER_REMIX: "https://cdn.tfrv.xyz/audio/bladerunner-remix.mp3", + AudioTrack.IZZAMUZZIC: "https://cdn.tfrv.xyz/audio/_izzamuzzic.mp3", + AudioTrack.NAS: "https://cdn.tfrv.xyz/audio/_nas.mp3", + AudioTrack.PARIS_ELSE: "https://cdn.tfrv.xyz/audio/_paris-else.mp3", + AudioTrack.SNOWFALL: "https://cdn.tfrv.xyz/audio/_snowfall.mp3", + AudioTrack.BURLESQUE: "https://cdn.tfrv.xyz/audio/burlesque.mp3", + AudioTrack.CORNY_CANDY: "https://cdn.tfrv.xyz/audio/corny-candy.mp3", + AudioTrack.HIGHWAY_NOCTURNE: "https://cdn.tfrv.xyz/audio/highway-nocturne.mp3", + AudioTrack.I_DONT_THINK_SO: "https://cdn.tfrv.xyz/audio/i-dont-think-so.mp3", + AudioTrack.LOSING_YOUR_MARBLES: "https://cdn.tfrv.xyz/audio/losing-your-marbles.mp3", + AudioTrack.REFRESHER: "https://cdn.tfrv.xyz/audio/refresher.mp3", + AudioTrack.TOURIST: "https://cdn.tfrv.xyz/audio/tourist.mp3", + AudioTrack.TWIN_TYCHES: "https://cdn.tfrv.xyz/audio/twin-tynches.mp3", + } + return audio_urls[self] + + +class GenerationPreset(str, Enum): + LEONARDO = ("Default",) + ANIME = ("Anime",) + REALISM = ("Realist",) + ILLUSTRATION = ("Illustration",) + SKETCH_COLOR = ("Sketch Color",) + SKETCH_BW = ("Sketch B&W",) + PIXAR = ("Pixar",) + INK = ("Japanese Ink",) + RENDER_3D = ("3D Render",) + LEGO = ("Lego",) + SCIFI = ("Sci-Fi",) + RECRO_CARTOON = ("Retro Cartoon",) + PIXEL_ART = ("Pixel Art",) + CREATIVE = ("Creative",) + PHOTOGRAPHY = ("Photography",) + RAYTRACED = ("Raytraced",) + ENVIRONMENT = ("Environment",) + FANTASY = ("Fantasy",) + ANIME_SR = ("Anime Realism",) + MOVIE = ("Movie",) + STYLIZED_ILLUSTRATION = ("Stylized Illustration",) + MANGA = ("Manga",) + + +class Voice(str, Enum): + LILY = "Lily" + DANIEL = "Daniel" + BRIAN = "Brian" + JESSICA = "Jessica" + CHARLOTTE = "Charlotte" + CALLUM = "Callum" + + @property + def voice_id(self): + voice_id_map = { + Voice.LILY: "pFZP5JQG7iQjIQuC4Bku", + Voice.DANIEL: "onwK4e9ZLuTAKqWW03F9", + Voice.BRIAN: "nPczCjzI2devNBz1zQrb", + Voice.JESSICA: "cgSgspJ2msm6clMCkdW9", + Voice.CHARLOTTE: "XB0fDUnXU5powFXDhCwa", + Voice.CALLUM: "N2lVS1w4EtoT3dr4eOWO", + } + return voice_id_map[self] + + def __str__(self): + return self.value + + +class VisualMediaType(str, Enum): + STOCK_VIDEOS = ("stockVideo",) + MOVING_AI_IMAGES = ("movingImage",) + AI_VIDEO = ("aiVideo",) + + +logger = logging.getLogger(__name__) + + +class AIShortformVideoCreatorBlock(Block): + class Input(BlockSchema): + api_key: BlockSecret = SecretField( + key="revid_api_key", + description="Your revid.ai API key", + placeholder="Enter your revid.ai API key", + ) + script: str = SchemaField( + description="""1. Use short and punctuated sentences\n\n2. Use linebreaks to create a new clip\n\n3. Text outside of brackets is spoken by the AI, and [text between brackets] will be used to guide the visual generation. For example, [close-up of a cat] will show a close-up of a cat.""", + placeholder="[close-up of a cat] Meow!", + ) + ratio: str = Field(description="Aspect ratio of the video", default="9 / 16") + resolution: str = Field(description="Resolution of the video", default="720p") + frame_rate: int = Field(description="Frame rate of the video", default=60) + generation_preset: GenerationPreset = SchemaField( + description="Generation preset for visual style - only effects AI generated visuals", + default=GenerationPreset.LEONARDO, + placeholder=GenerationPreset.LEONARDO, + ) + background_music: AudioTrack = SchemaField( + description="Background music track", + default=AudioTrack.HIGHWAY_NOCTURNE, + placeholder=AudioTrack.HIGHWAY_NOCTURNE, + ) + voice: Voice = SchemaField( + description="AI voice to use for narration", + default=Voice.LILY, + placeholder=Voice.LILY, + ) + video_style: VisualMediaType = SchemaField( + description="Type of visual media to use for the video", + default=VisualMediaType.STOCK_VIDEOS, + placeholder=VisualMediaType.STOCK_VIDEOS, + ) + + class Output(BlockSchema): + video_url: str = Field(description="The URL of the created video") + error: Optional[str] = Field(description="Error message if the request failed") + + def __init__(self): + super().__init__( + id="361697fb-0c4f-4feb-aed3-8320c88c771b", + description="Creates a shortform video using revid.ai", + categories={BlockCategory.SOCIAL, BlockCategory.AI}, + input_schema=AIShortformVideoCreatorBlock.Input, + output_schema=AIShortformVideoCreatorBlock.Output, + test_input={ + "api_key": "test_api_key", + "script": "[close-up of a cat] Meow!", + "ratio": "9 / 16", + "resolution": "720p", + "frame_rate": 60, + "generation_preset": GenerationPreset.LEONARDO, + "background_music": AudioTrack.HIGHWAY_NOCTURNE, + "voice": Voice.LILY, + "video_style": VisualMediaType.STOCK_VIDEOS, + }, + test_output=( + "video_url", + "https://example.com/video.mp4", + ), + test_mock={ + "create_webhook": lambda: ( + "test_uuid", + "https://webhook.site/test_uuid", + ), + "create_video": lambda api_key, payload: {"pid": "test_pid"}, + "wait_for_video": lambda api_key, pid, webhook_token, max_wait_time=1000: "https://example.com/video.mp4", + }, + ) + + def create_webhook(self): + url = "https://webhook.site/token" + headers = {"Accept": "application/json", "Content-Type": "application/json"} + response = requests.post(url, headers=headers) + response.raise_for_status() + webhook_data = response.json() + return webhook_data["uuid"], f"https://webhook.site/{webhook_data['uuid']}" + + def create_video(self, api_key: str, payload: dict) -> dict: + url = "https://www.revid.ai/api/public/v2/render" + headers = {"key": api_key} + response = requests.post(url, json=payload, headers=headers) + logger.debug( + f"API Response Status Code: {response.status_code}, Content: {response.text}" + ) + response.raise_for_status() + return response.json() + + def check_video_status(self, api_key: str, pid: str) -> dict: + url = f"https://www.revid.ai/api/public/v2/status?pid={pid}" + headers = {"key": api_key} + response = requests.get(url, headers=headers) + response.raise_for_status() + return response.json() + + def wait_for_video( + self, api_key: str, pid: str, webhook_token: str, max_wait_time: int = 1000 + ) -> str: + start_time = time.time() + while time.time() - start_time < max_wait_time: + status = self.check_video_status(api_key, pid) + logger.debug(f"Video status: {status}") + + if status.get("status") == "ready" and "videoUrl" in status: + return status["videoUrl"] + elif status.get("status") == "error": + error_message = status.get("error", "Unknown error occurred") + logger.error(f"Video creation failed: {error_message}") + raise ValueError(f"Video creation failed: {error_message}") + elif status.get("status") in ["FAILED", "CANCELED"]: + logger.error(f"Video creation failed: {status.get('message')}") + raise ValueError(f"Video creation failed: {status.get('message')}") + + time.sleep(10) + + logger.error("Video creation timed out") + raise TimeoutError("Video creation timed out") + + def run(self, input_data: Input, **kwargs) -> BlockOutput: + try: + # Create a new Webhook.site URL + webhook_token, webhook_url = self.create_webhook() + logger.debug(f"Webhook URL: {webhook_url}") + + audio_url = input_data.background_music.audio_url + + payload = { + "frameRate": input_data.frame_rate, + "resolution": input_data.resolution, + "frameDurationMultiplier": 18, + "webhook": webhook_url, + "creationParams": { + "mediaType": input_data.video_style, + "captionPresetName": "Wrap 1", + "selectedVoice": input_data.voice.voice_id, + "hasEnhancedGeneration": True, + "generationPreset": input_data.generation_preset.name, + "selectedAudio": input_data.background_music, + "origin": "/create", + "inputText": input_data.script, + "flowType": "text-to-video", + "slug": "create-tiktok-video", + "hasToGenerateVoice": True, + "hasToTranscript": False, + "hasToSearchMedia": True, + "hasAvatar": False, + "hasWebsiteRecorder": False, + "hasTextSmallAtBottom": False, + "ratio": input_data.ratio, + "sourceType": "contentScraping", + "selectedStoryStyle": {"value": "custom", "label": "Custom"}, + "hasToGenerateVideos": input_data.video_style + != VisualMediaType.STOCK_VIDEOS, + "audioUrl": audio_url, + }, + } + + logger.debug("Creating video...") + response = self.create_video(input_data.api_key.get_secret_value(), payload) + pid = response.get("pid") + + if not pid: + logger.error( + f"Failed to create video: No project ID returned. API Response: {response}" + ) + yield "error", "Failed to create video: No project ID returned" + else: + logger.debug( + f"Video created with project ID: {pid}. Waiting for completion..." + ) + video_url = self.wait_for_video( + input_data.api_key.get_secret_value(), pid, webhook_token + ) + logger.debug(f"Video ready: {video_url}") + yield "video_url", video_url + + except requests.RequestException as e: + logger.exception("Error creating video") + yield "error", f"Error creating video: {str(e)}" + except ValueError as e: + logger.exception("Error in video creation process") + yield "error", str(e) + except TimeoutError as e: + logger.exception("Video creation timed out") + yield "error", str(e) diff --git a/autogpt_platform/backend/backend/blocks/iteration.py b/autogpt_platform/backend/backend/blocks/iteration.py index f863521c83..05ca5f5ed7 100644 --- a/autogpt_platform/backend/backend/blocks/iteration.py +++ b/autogpt_platform/backend/backend/blocks/iteration.py @@ -1,37 +1,52 @@ -from typing import Any, List, Tuple +from typing import Any from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema from backend.data.model import SchemaField -class ListIteratorBlock(Block): +class StepThroughItemsBlock(Block): class Input(BlockSchema): - items: List[Any] = SchemaField( - description="The list of items to iterate over", - placeholder="[1, 2, 3, 4, 5]", + items: list | dict = SchemaField( + description="The list or dictionary of items to iterate over", + placeholder="[1, 2, 3, 4, 5] or {'key1': 'value1', 'key2': 'value2'}", ) class Output(BlockSchema): - item: Tuple[int, Any] = SchemaField( - description="A tuple with the index and current item in the iteration" + item: Any = SchemaField(description="The current item in the iteration") + key: Any = SchemaField( + description="The key or index of the current item in the iteration", ) def __init__(self): super().__init__( id="f8e7d6c5-b4a3-2c1d-0e9f-8g7h6i5j4k3l", - input_schema=ListIteratorBlock.Input, - output_schema=ListIteratorBlock.Output, - description="Iterates over a list of items and outputs each item with its index.", + input_schema=StepThroughItemsBlock.Input, + output_schema=StepThroughItemsBlock.Output, categories={BlockCategory.LOGIC}, - test_input={"items": [1, "two", {"three": 3}, [4, 5]]}, + description="Iterates over a list or dictionary and outputs each item.", + test_input={"items": [1, 2, 3, {"key1": "value1", "key2": "value2"}]}, test_output=[ - ("item", (0, 1)), - ("item", (1, "two")), - ("item", (2, {"three": 3})), - ("item", (3, [4, 5])), + ("item", 1), + ("key", 0), + ("item", 2), + ("key", 1), + ("item", 3), + ("key", 2), + ("item", {"key1": "value1", "key2": "value2"}), + ("key", 3), ], + test_mock={}, ) def run(self, input_data: Input, **kwargs) -> BlockOutput: - for index, item in enumerate(input_data.items): - yield "item", (index, item) + items = input_data.items + if isinstance(items, dict): + # If items is a dictionary, iterate over its values + for item in items.values(): + yield "item", item + yield "key", item + else: + # If items is a list, iterate over the list + for index, item in enumerate(items): + yield "item", item + yield "key", index diff --git a/autogpt_platform/backend/backend/blocks/llm.py b/autogpt_platform/backend/backend/blocks/llm.py index f510d680f5..868244cd8e 100644 --- a/autogpt_platform/backend/backend/blocks/llm.py +++ b/autogpt_platform/backend/backend/blocks/llm.py @@ -1,3 +1,4 @@ +import ast import logging from enum import Enum from json import JSONDecodeError @@ -209,6 +210,7 @@ class AIStructuredResponseGeneratorBlock(Block): raise ValueError(f"Unsupported LLM provider: {provider}") def run(self, input_data: Input, **kwargs) -> BlockOutput: + logger.debug(f"Calling LLM with input data: {input_data}") prompt = [] def trim_prompt(s: str) -> str: @@ -622,3 +624,232 @@ class AIConversationBlock(Block): yield "response", response except Exception as e: yield "error", f"Error calling LLM: {str(e)}" + + +class AIListGeneratorBlock(Block): + class Input(BlockSchema): + focus: str | None = SchemaField( + description="The focus of the list to generate.", + placeholder="The top 5 most interesting news stories in the data.", + default=None, + advanced=False, + ) + source_data: str | None = SchemaField( + description="The data to generate the list from.", + placeholder="News Today: Humans land on Mars: Today humans landed on mars. -- AI wins Nobel Prize: AI wins Nobel Prize for solving world hunger. -- New AI Model: A new AI model has been released.", + default=None, + advanced=False, + ) + model: LlmModel = SchemaField( + title="LLM Model", + default=LlmModel.GPT4_TURBO, + description="The language model to use for generating the list.", + advanced=True, + ) + api_key: BlockSecret = SecretField(value="") + max_retries: int = SchemaField( + default=3, + description="Maximum number of retries for generating a valid list.", + ge=1, + le=5, + ) + + class Output(BlockSchema): + generated_list: List[str] = SchemaField(description="The generated list.") + list_item: str = SchemaField( + description="Each individual item in the list.", + ) + error: str = SchemaField( + description="Error message if the list generation failed." + ) + + def __init__(self): + super().__init__( + id="9c0b0450-d199-458b-a731-072189dd6593", + description="Generate a Python list based on the given prompt using a Large Language Model (LLM).", + categories={BlockCategory.AI, BlockCategory.TEXT}, + input_schema=AIListGeneratorBlock.Input, + output_schema=AIListGeneratorBlock.Output, + test_input={ + "focus": "planets", + "source_data": ( + "Zylora Prime is a glowing jungle world with bioluminescent plants, " + "while Kharon-9 is a harsh desert planet with underground cities. " + "Vortexia's constant storms power floating cities, and Oceara is a water-covered world home to " + "intelligent marine life. On icy Draknos, ancient ruins lie buried beneath its frozen landscape, " + "drawing explorers to uncover its mysteries. Each planet showcases the limitless possibilities of " + "fictional worlds." + ), + "model": LlmModel.GPT4_TURBO, + "api_key": "test_api_key", + "max_retries": 3, + }, + test_output=[ + ( + "generated_list", + ["Zylora Prime", "Kharon-9", "Vortexia", "Oceara", "Draknos"], + ), + ("list_item", "Zylora Prime"), + ("list_item", "Kharon-9"), + ("list_item", "Vortexia"), + ("list_item", "Oceara"), + ("list_item", "Draknos"), + ], + test_mock={ + "llm_call": lambda input_data: { + "response": "['Zylora Prime', 'Kharon-9', 'Vortexia', 'Oceara', 'Draknos']" + }, + }, + ) + + @staticmethod + def llm_call( + input_data: AIStructuredResponseGeneratorBlock.Input, + ) -> dict[str, str]: + llm_block = AIStructuredResponseGeneratorBlock() + for output_name, output_data in llm_block.run(input_data): + if output_name == "response": + logger.debug(f"Received response from LLM: {output_data}") + return output_data + raise ValueError("Failed to get a response from the LLM.") + + @staticmethod + def string_to_list(string): + """ + Converts a string representation of a list into an actual Python list object. + """ + logger.debug(f"Converting string to list. Input string: {string}") + try: + # Use ast.literal_eval to safely evaluate the string + python_list = ast.literal_eval(string) + if isinstance(python_list, list): + logger.debug(f"Successfully converted string to list: {python_list}") + return python_list + else: + logger.error(f"The provided string '{string}' is not a valid list") + raise ValueError(f"The provided string '{string}' is not a valid list.") + except (SyntaxError, ValueError) as e: + logger.error(f"Failed to convert string to list: {e}") + raise ValueError("Invalid list format. Could not convert to list.") + + def run(self, input_data: Input, **kwargs) -> BlockOutput: + logger.debug(f"Starting AIListGeneratorBlock.run with input data: {input_data}") + + # Check for API key + api_key_check = ( + input_data.api_key.get_secret_value() + or LlmApiKeys[input_data.model.metadata.provider].get_secret_value() + ) + if not api_key_check: + logger.error("No LLM API key provided.") + yield "error", "No LLM API key provided." + return + + # Prepare the system prompt + sys_prompt = """You are a Python list generator. Your task is to generate a Python list based on the user's prompt. + |Respond ONLY with a valid python list. + |The list can contain strings, numbers, or nested lists as appropriate. + |Do not include any explanations or additional text. + + |Valid Example string formats: + + |Example 1: + |``` + |['1', '2', '3', '4'] + |``` + + |Example 2: + |``` + |[['1', '2'], ['3', '4'], ['5', '6']] + |``` + + |Example 3: + |``` + |['1', ['2', '3'], ['4', ['5', '6']]] + |``` + + |Example 4: + |``` + |['a', 'b', 'c'] + |``` + + |Example 5: + |``` + |['1', '2.5', 'string', 'True', ['False', 'None']] + |``` + + |Do not include any explanations or additional text, just respond with the list in the format specified above. + """ + # If a focus is provided, add it to the prompt + if input_data.focus: + prompt = f"Generate a list with the following focus:\n\n\n{input_data.focus}" + else: + # If there's source data + if input_data.source_data: + prompt = "Extract the main focus of the source data to a list.\ni.e if the source data is a news website, the focus would be the news stories rather than the social links in the footer." + else: + # No focus or source data provided, generat a random list + prompt = "Generate a random list." + + # If the source data is provided, add it to the prompt + if input_data.source_data: + prompt += f"\n\nUse the following source data to generate the list from:\n\n\n\n{input_data.source_data}\n\nDo not invent fictional data that is not present in the source data." + # Else, tell the LLM to synthesize the data + else: + prompt += "\n\nInvent the data to generate the list from." + + for attempt in range(input_data.max_retries): + try: + logger.debug("Calling LLM") + llm_response = self.llm_call( + AIStructuredResponseGeneratorBlock.Input( + sys_prompt=sys_prompt, + prompt=prompt, + api_key=input_data.api_key, + model=input_data.model, + expected_format={}, # Do not use structured response + ) + ) + + logger.debug(f"LLM response: {llm_response}") + + # Extract Response string + response_string = llm_response["response"] + logger.debug(f"Response string: {response_string}") + + # Convert the string to a Python list + logger.debug("Converting string to Python list") + parsed_list = self.string_to_list(response_string) + logger.debug(f"Parsed list: {parsed_list}") + + # If we reach here, we have a valid Python list + logger.debug("Successfully generated a valid Python list") + yield "generated_list", parsed_list + + # Yield each item in the list + for item in parsed_list: + yield "list_item", item + return + + except Exception as e: + logger.error(f"Error in attempt {attempt + 1}: {str(e)}") + if attempt == input_data.max_retries - 1: + logger.error( + f"Failed to generate a valid Python list after {input_data.max_retries} attempts" + ) + yield "error", f"Failed to generate a valid Python list after {input_data.max_retries} attempts. Last error: {str(e)}" + else: + # Add a retry prompt + logger.debug("Preparing retry prompt") + prompt = f""" + The previous attempt failed due to `{e}` + Generate a valid Python list based on the original prompt. + Remember to respond ONLY with a valid Python list as per the format specified earlier. + Original prompt: + ```{prompt}``` + + Respond only with the list in the format specified with no commentary or apologies. + """ + logger.debug(f"Retry prompt: {prompt}") + + logger.debug("AIListGeneratorBlock.run completed") diff --git a/autogpt_platform/backend/backend/blocks/search.py b/autogpt_platform/backend/backend/blocks/search.py index 7414ca2f8a..e51ff4013a 100644 --- a/autogpt_platform/backend/backend/blocks/search.py +++ b/autogpt_platform/backend/backend/blocks/search.py @@ -4,7 +4,7 @@ from urllib.parse import quote import requests from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import BlockSecret, SecretField +from backend.data.model import BlockSecret, SchemaField, SecretField class GetRequest: @@ -96,6 +96,12 @@ class SearchTheWebBlock(Block, GetRequest): class ExtractWebsiteContentBlock(Block, GetRequest): class Input(BlockSchema): url: str # The URL to scrape + raw_content: bool = SchemaField( + default=False, + title="Raw Content", + description="Whether to do a raw scrape of the content or use Jina-ai Reader to scrape the content", + advanced=True, + ) class Output(BlockSchema): content: str # The scraped content from the URL @@ -114,21 +120,18 @@ class ExtractWebsiteContentBlock(Block, GetRequest): ) def run(self, input_data: Input, **kwargs) -> BlockOutput: + if input_data.raw_content: + url = input_data.url + else: + url = f"https://r.jina.ai/{input_data.url}" + try: - # Prepend the Jina-ai Reader URL to the input URL - jina_url = f"https://r.jina.ai/{input_data.url}" - - # Make the request to Jina-ai Reader - response = self.get_request(jina_url, json=False) - - # Output the scraped content - yield "content", response - + content = self.get_request(url, json=False) + yield "content", content except requests.exceptions.HTTPError as http_err: yield "error", f"HTTP error occurred: {http_err}" - except requests.RequestException as e: - yield "error", f"Request to Jina-ai Reader failed: {e}" + yield "error", f"Request to URL failed: {e}" class GetWeatherInformationBlock(Block, GetRequest): diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index c76399eba6..c5cfdc6602 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -206,6 +206,7 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings): medium_api_key: str = Field(default="", description="Medium API key") medium_author_id: str = Field(default="", description="Medium author ID") did_api_key: str = Field(default="", description="D-ID API Key") + revid_api_key: str = Field(default="", description="revid.ai API key") discord_bot_token: str = Field(default="", description="Discord bot token")