| @@ -0,0 +1 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><rect x="32" y="48" width="192" height="160" rx="8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="156" cy="100" r="12"/><path d="M147.31,164,173,138.34a8,8,0,0,1,11.31,0L224,178.06" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M32,168.69l54.34-54.35a8,8,0,0,1,11.32,0L191.31,208" fill="none" stroke="#1553ed" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg> | |||
| @@ -0,0 +1,22 @@ | |||
| from core.tools.errors import ToolProviderCredentialValidationError | |||
| from core.tools.provider.builtin.getimgai.tools.text2image import Text2ImageTool | |||
| from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController | |||
| class GetImgAIProvider(BuiltinToolProviderController): | |||
| def _validate_credentials(self, credentials: dict) -> None: | |||
| try: | |||
| # Example validation using the text2image tool | |||
| Text2ImageTool().fork_tool_runtime( | |||
| runtime={"credentials": credentials} | |||
| ).invoke( | |||
| user_id='', | |||
| tool_parameters={ | |||
| "prompt": "A fire egg", | |||
| "response_format": "url", | |||
| "style": "photorealism", | |||
| } | |||
| ) | |||
| except Exception as e: | |||
| raise ToolProviderCredentialValidationError(str(e)) | |||
| @@ -0,0 +1,29 @@ | |||
| identity: | |||
| author: Matri Qi | |||
| name: getimgai | |||
| label: | |||
| en_US: getimg.ai | |||
| zh_CN: getimg.ai | |||
| description: | |||
| en_US: GetImg API integration for image generation and scraping. | |||
| icon: icon.svg | |||
| tags: | |||
| - image | |||
| credentials_for_provider: | |||
| getimg_api_key: | |||
| type: secret-input | |||
| required: true | |||
| label: | |||
| en_US: getimg.ai API Key | |||
| placeholder: | |||
| en_US: Please input your getimg.ai API key | |||
| help: | |||
| en_US: Get your getimg.ai API key from your getimg.ai account settings. If you are using a self-hosted version, you may enter any key at your convenience. | |||
| url: https://dashboard.getimg.ai/api-keys | |||
| base_url: | |||
| type: text-input | |||
| required: false | |||
| label: | |||
| en_US: getimg.ai server's Base URL | |||
| placeholder: | |||
| en_US: https://api.getimg.ai/v1 | |||
| @@ -0,0 +1,59 @@ | |||
| import logging | |||
| import time | |||
| from collections.abc import Mapping | |||
| from typing import Any | |||
| import requests | |||
| from requests.exceptions import HTTPError | |||
| logger = logging.getLogger(__name__) | |||
| class GetImgAIApp: | |||
| def __init__(self, api_key: str | None = None, base_url: str | None = None): | |||
| self.api_key = api_key | |||
| self.base_url = base_url or 'https://api.getimg.ai/v1' | |||
| if not self.api_key: | |||
| raise ValueError("API key is required") | |||
| def _prepare_headers(self): | |||
| headers = { | |||
| 'Content-Type': 'application/json', | |||
| 'Authorization': f'Bearer {self.api_key}' | |||
| } | |||
| return headers | |||
| def _request( | |||
| self, | |||
| method: str, | |||
| url: str, | |||
| data: Mapping[str, Any] | None = None, | |||
| headers: Mapping[str, str] | None = None, | |||
| retries: int = 3, | |||
| backoff_factor: float = 0.3, | |||
| ) -> Mapping[str, Any] | None: | |||
| for i in range(retries): | |||
| try: | |||
| response = requests.request(method, url, json=data, headers=headers) | |||
| response.raise_for_status() | |||
| return response.json() | |||
| except requests.exceptions.RequestException as e: | |||
| if i < retries - 1 and isinstance(e, HTTPError) and e.response.status_code >= 500: | |||
| time.sleep(backoff_factor * (2 ** i)) | |||
| else: | |||
| raise | |||
| return None | |||
| def text2image( | |||
| self, mode: str, **kwargs | |||
| ): | |||
| data = kwargs['params'] | |||
| if not data.get('prompt'): | |||
| raise ValueError("Prompt is required") | |||
| endpoint = f'{self.base_url}/{mode}/text-to-image' | |||
| headers = self._prepare_headers() | |||
| logger.debug(f"Send request to {endpoint=} body={data}") | |||
| response = self._request('POST', endpoint, data, headers) | |||
| if response is None: | |||
| raise HTTPError("Failed to initiate getimg.ai after multiple retries") | |||
| return response | |||
| @@ -0,0 +1,39 @@ | |||
| import json | |||
| from typing import Any, Union | |||
| from core.tools.entities.tool_entities import ToolInvokeMessage | |||
| from core.tools.provider.builtin.getimgai.getimgai_appx import GetImgAIApp | |||
| from core.tools.tool.builtin_tool import BuiltinTool | |||
| class Text2ImageTool(BuiltinTool): | |||
| def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: | |||
| app = GetImgAIApp(api_key=self.runtime.credentials['getimg_api_key'], base_url=self.runtime.credentials['base_url']) | |||
| options = { | |||
| 'style': tool_parameters.get('style'), | |||
| 'prompt': tool_parameters.get('prompt'), | |||
| 'aspect_ratio': tool_parameters.get('aspect_ratio'), | |||
| 'output_format': tool_parameters.get('output_format', 'jpeg'), | |||
| 'response_format': tool_parameters.get('response_format', 'url'), | |||
| 'width': tool_parameters.get('width'), | |||
| 'height': tool_parameters.get('height'), | |||
| 'steps': tool_parameters.get('steps'), | |||
| 'negative_prompt': tool_parameters.get('negative_prompt'), | |||
| 'prompt_2': tool_parameters.get('prompt_2'), | |||
| } | |||
| options = {k: v for k, v in options.items() if v} | |||
| text2image_result = app.text2image( | |||
| mode=tool_parameters.get('mode', 'essential-v2'), | |||
| params=options, | |||
| wait=True | |||
| ) | |||
| if not isinstance(text2image_result, str): | |||
| text2image_result = json.dumps(text2image_result, ensure_ascii=False, indent=4) | |||
| if not text2image_result: | |||
| return self.create_text_message("getimg.ai request failed.") | |||
| return self.create_text_message(text2image_result) | |||
| @@ -0,0 +1,167 @@ | |||
| identity: | |||
| name: text2image | |||
| author: Matri Qi | |||
| label: | |||
| en_US: text2image | |||
| icon: icon.svg | |||
| description: | |||
| human: | |||
| en_US: Generate image via getimg.ai. | |||
| llm: This tool is used to generate image from prompt or image via https://getimg.ai. | |||
| parameters: | |||
| - name: prompt | |||
| type: string | |||
| required: true | |||
| label: | |||
| en_US: prompt | |||
| human_description: | |||
| en_US: The text prompt used to generate the image. The getimg.aier will generate an image based on this prompt. | |||
| llm_description: this prompt text will be used to generate image. | |||
| form: llm | |||
| - name: mode | |||
| type: select | |||
| required: false | |||
| label: | |||
| en_US: mode | |||
| human_description: | |||
| en_US: The getimg.ai mode to use. The mode determines the endpoint used to generate the image. | |||
| form: form | |||
| options: | |||
| - value: "essential-v2" | |||
| label: | |||
| en_US: essential-v2 | |||
| - value: stable-diffusion-xl | |||
| label: | |||
| en_US: stable-diffusion-xl | |||
| - value: stable-diffusion | |||
| label: | |||
| en_US: stable-diffusion | |||
| - value: latent-consistency | |||
| label: | |||
| en_US: latent-consistency | |||
| - name: style | |||
| type: select | |||
| required: false | |||
| label: | |||
| en_US: style | |||
| human_description: | |||
| en_US: The style preset to use. The style preset guides the generation towards a particular style. It's just efficient for `Essential V2` mode. | |||
| form: form | |||
| options: | |||
| - value: photorealism | |||
| label: | |||
| en_US: photorealism | |||
| - value: anime | |||
| label: | |||
| en_US: anime | |||
| - value: art | |||
| label: | |||
| en_US: art | |||
| - name: aspect_ratio | |||
| type: select | |||
| required: false | |||
| label: | |||
| en_US: "aspect ratio" | |||
| human_description: | |||
| en_US: The aspect ratio of the generated image. It's just efficient for `Essential V2` mode. | |||
| form: form | |||
| options: | |||
| - value: "1:1" | |||
| label: | |||
| en_US: "1:1" | |||
| - value: "4:5" | |||
| label: | |||
| en_US: "4:5" | |||
| - value: "5:4" | |||
| label: | |||
| en_US: "5:4" | |||
| - value: "2:3" | |||
| label: | |||
| en_US: "2:3" | |||
| - value: "3:2" | |||
| label: | |||
| en_US: "3:2" | |||
| - value: "4:7" | |||
| label: | |||
| en_US: "4:7" | |||
| - value: "7:4" | |||
| label: | |||
| en_US: "7:4" | |||
| - name: output_format | |||
| type: select | |||
| required: false | |||
| label: | |||
| en_US: "output format" | |||
| human_description: | |||
| en_US: The file format of the generated image. | |||
| form: form | |||
| options: | |||
| - value: jpeg | |||
| label: | |||
| en_US: jpeg | |||
| - value: png | |||
| label: | |||
| en_US: png | |||
| - name: response_format | |||
| type: select | |||
| required: false | |||
| label: | |||
| en_US: "response format" | |||
| human_description: | |||
| en_US: The format in which the generated images are returned. Must be one of url or b64. URLs are only valid for 1 hour after the image has been generated. | |||
| form: form | |||
| options: | |||
| - value: url | |||
| label: | |||
| en_US: url | |||
| - value: b64 | |||
| label: | |||
| en_US: b64 | |||
| - name: model | |||
| type: string | |||
| required: false | |||
| label: | |||
| en_US: model | |||
| human_description: | |||
| en_US: Model ID supported by this pipeline and family. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode. | |||
| form: form | |||
| - name: negative_prompt | |||
| type: string | |||
| required: false | |||
| label: | |||
| en_US: negative prompt | |||
| human_description: | |||
| en_US: Text input that will not guide the image generation. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode. | |||
| form: form | |||
| - name: prompt_2 | |||
| type: string | |||
| required: false | |||
| label: | |||
| en_US: prompt2 | |||
| human_description: | |||
| en_US: Prompt sent to second tokenizer and text encoder. If not defined, prompt is used in both text-encoders. It's just efficient for `Stable Diffusion XL` mode. | |||
| form: form | |||
| - name: width | |||
| type: number | |||
| required: false | |||
| label: | |||
| en_US: width | |||
| human_description: | |||
| en_US: he width of the generated image in pixels. Width needs to be multiple of 64. | |||
| form: form | |||
| - name: height | |||
| type: number | |||
| required: false | |||
| label: | |||
| en_US: height | |||
| human_description: | |||
| en_US: he height of the generated image in pixels. Height needs to be multiple of 64. | |||
| form: form | |||
| - name: steps | |||
| type: number | |||
| required: false | |||
| label: | |||
| en_US: steps | |||
| human_description: | |||
| en_US: The number of denoising steps. More steps usually can produce higher quality images, but take more time to generate. It's just efficient for `Stable Diffusion XL`, `Stable Diffusion`, `Latent Consistency` mode. | |||
| form: form | |||