|
4 | 4 | author_url: https://github.com/owndev/ |
5 | 5 | project_url: https://github.com/owndev/Open-WebUI-Functions |
6 | 6 | funding_url: https://github.com/sponsors/owndev |
7 | | -version: 1.9.2 |
| 7 | +version: 1.10.0 |
8 | 8 | required_open_webui_version: 0.6.26 |
9 | 9 | license: Apache License 2.0 |
10 | 10 | description: Highly optimized Google Gemini pipeline with advanced image generation capabilities, intelligent compression, and streamlined processing workflows. |
|
35 | 35 | - Flexible upload fallback options and optimization controls |
36 | 36 | - Configurable thinking levels (low/high) for Gemini 3 models |
37 | 37 | - Configurable thinking budgets (0-32768 tokens) for Gemini 2.5 models |
| 38 | + - Configurable image generation aspect ratio (1:1, 16:9, etc.) and resolution (1K, 2K, 4K) |
38 | 39 | """ |
39 | 40 |
|
40 | 41 | import os |
@@ -259,6 +260,14 @@ class Valves(BaseModel): |
259 | 260 | default=os.getenv("GOOGLE_IMAGE_HISTORY_FIRST", "true").lower() == "true", |
260 | 261 | description="If true (default), history images precede current message images; if false, current images first.", |
261 | 262 | ) |
| 263 | + IMAGE_GENERATION_ASPECT_RATIO: str = Field( |
| 264 | + default=os.getenv("GOOGLE_IMAGE_GENERATION_ASPECT_RATIO", "1:1"), |
| 265 | + description="Default aspect ratio for image generation. Valid values: '1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9'", |
| 266 | + ) |
| 267 | + IMAGE_GENERATION_RESOLUTION: str = Field( |
| 268 | + default=os.getenv("GOOGLE_IMAGE_GENERATION_RESOLUTION", "2K"), |
| 269 | + description="Default resolution for image generation. Valid values: '1K', '2K', '4K'", |
| 270 | + ) |
262 | 271 |
|
263 | 272 | # ---------------- Internal Helpers ---------------- # |
264 | 273 | async def _gather_history_images( |
@@ -863,6 +872,69 @@ def _validate_thinking_budget(self, budget: int) -> int: |
863 | 872 | ) |
864 | 873 | return -1 |
865 | 874 |
|
| 875 | + def _validate_aspect_ratio(self, aspect_ratio: str) -> Optional[str]: |
| 876 | + """ |
| 877 | + Validate and normalize the aspect ratio value. |
| 878 | +
|
| 879 | + Args: |
| 880 | + aspect_ratio: The aspect ratio string to validate |
| 881 | +
|
| 882 | + Returns: |
| 883 | + Validated aspect ratio string or None if invalid |
| 884 | + """ |
| 885 | + if not aspect_ratio: |
| 886 | + return None |
| 887 | + |
| 888 | + # Valid aspect ratios according to Google's API |
| 889 | + valid_ratios = [ |
| 890 | + "1:1", |
| 891 | + "2:3", |
| 892 | + "3:2", |
| 893 | + "3:4", |
| 894 | + "4:3", |
| 895 | + "4:5", |
| 896 | + "5:4", |
| 897 | + "9:16", |
| 898 | + "16:9", |
| 899 | + "21:9", |
| 900 | + ] |
| 901 | + |
| 902 | + normalized = aspect_ratio.strip() |
| 903 | + if normalized in valid_ratios: |
| 904 | + return normalized |
| 905 | + |
| 906 | + self.log.warning( |
| 907 | + f"Invalid aspect ratio '{aspect_ratio}'. Valid values are: {', '.join(valid_ratios)}. " |
| 908 | + "Using default '1:1'." |
| 909 | + ) |
| 910 | + return "1:1" |
| 911 | + |
| 912 | + def _validate_resolution(self, resolution: str) -> Optional[str]: |
| 913 | + """ |
| 914 | + Validate and normalize the resolution value. |
| 915 | +
|
| 916 | + Args: |
| 917 | + resolution: The resolution string to validate |
| 918 | +
|
| 919 | + Returns: |
| 920 | + Validated resolution string or None if invalid |
| 921 | + """ |
| 922 | + if not resolution: |
| 923 | + return None |
| 924 | + |
| 925 | + # Valid resolutions according to Google's API |
| 926 | + valid_resolutions = ["1K", "2K", "4K"] |
| 927 | + |
| 928 | + normalized = resolution.strip().upper() |
| 929 | + if normalized in valid_resolutions: |
| 930 | + return normalized |
| 931 | + |
| 932 | + self.log.warning( |
| 933 | + f"Invalid resolution '{resolution}'. Valid values are: {', '.join(valid_resolutions)}. " |
| 934 | + "Using default '2K'." |
| 935 | + ) |
| 936 | + return "2K" |
| 937 | + |
866 | 938 | def pipes(self) -> List[Dict[str, str]]: |
867 | 939 | """ |
868 | 940 | Returns a list of available Google Gemini models for the UI. |
@@ -1569,6 +1641,36 @@ def _configure_generation( |
1569 | 1641 | if enable_image_generation: |
1570 | 1642 | gen_config_params["response_modalities"] = ["TEXT", "IMAGE"] |
1571 | 1643 |
|
| 1644 | + # Configure image generation parameters (aspect ratio and resolution) |
| 1645 | + # Body parameters override valve defaults for per-request customization |
| 1646 | + aspect_ratio = body.get( |
| 1647 | + "aspect_ratio", self.valves.IMAGE_GENERATION_ASPECT_RATIO |
| 1648 | + ) |
| 1649 | + resolution = body.get("image_size", self.valves.IMAGE_GENERATION_RESOLUTION) |
| 1650 | + |
| 1651 | + # Validate and normalize the values |
| 1652 | + validated_aspect_ratio = self._validate_aspect_ratio(aspect_ratio) |
| 1653 | + validated_resolution = self._validate_resolution(resolution) |
| 1654 | + |
| 1655 | + # Create image config if we have valid values |
| 1656 | + if validated_aspect_ratio and validated_resolution: |
| 1657 | + try: |
| 1658 | + gen_config_params["image_config"] = types.ImageConfig( |
| 1659 | + aspect_ratio=validated_aspect_ratio, |
| 1660 | + image_size=validated_resolution, |
| 1661 | + ) |
| 1662 | + self.log.debug( |
| 1663 | + f"Image generation config: aspect_ratio={validated_aspect_ratio}, resolution={validated_resolution}" |
| 1664 | + ) |
| 1665 | + except (AttributeError, TypeError) as e: |
| 1666 | + # Fall back if SDK does not support ImageConfig |
| 1667 | + self.log.warning( |
| 1668 | + f"ImageConfig not supported by SDK version: {e}. Image generation will use default settings." |
| 1669 | + ) |
| 1670 | + except Exception as e: |
| 1671 | + # Log unexpected errors but continue without image config |
| 1672 | + self.log.warning(f"Unexpected error configuring ImageConfig: {e}") |
| 1673 | + |
1572 | 1674 | # Configure Gemini thinking/reasoning for models that support it |
1573 | 1675 | # This is independent of include_thoughts - thinking config controls HOW the model reasons, |
1574 | 1676 | # while include_thoughts controls whether the reasoning is shown in the output |
|
0 commit comments