ultimate-gemini-mcp 3.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
src/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """
2
+ Ultimate Gemini MCP Server
3
+
4
+ A unified MCP server that combines the best features from:
5
+ - Gemini 2.5 Flash Image (with prompt enhancement)
6
+ - Imagen 3, 4, and 4-Ultra
7
+ - Advanced features: batch processing, editing, templates, and more
8
+ """
9
+
10
+ __version__ = "3.0.7"
11
+ __author__ = "Ultimate Gemini MCP"
12
+
13
+ from .config import get_settings
14
+ from .server import create_app, main
15
+
16
+ __all__ = ["create_app", "main", "get_settings", "__version__"]
src/config/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """Configuration module for Ultimate Gemini MCP."""
2
+
3
+ from .constants import (
4
+ ALL_MODELS,
5
+ ASPECT_RATIOS,
6
+ DEFAULT_ENHANCEMENT_MODEL,
7
+ DEFAULT_IMAGE_SIZE,
8
+ DEFAULT_MODEL,
9
+ DEFAULT_OUTPUT_DIR,
10
+ DEFAULT_TIMEOUT,
11
+ GEMINI_MODELS,
12
+ IMAGE_FORMATS,
13
+ IMAGE_SIZES,
14
+ MAX_BATCH_SIZE,
15
+ MAX_HUMAN_IMAGES,
16
+ MAX_OBJECT_IMAGES,
17
+ MAX_PROMPT_LENGTH,
18
+ MAX_REFERENCE_IMAGES,
19
+ RESPONSE_MODALITIES,
20
+ )
21
+ from .settings import APIConfig, ServerConfig, Settings, get_settings
22
+
23
+ __all__ = [
24
+ "ALL_MODELS",
25
+ "ASPECT_RATIOS",
26
+ "DEFAULT_ENHANCEMENT_MODEL",
27
+ "DEFAULT_IMAGE_SIZE",
28
+ "DEFAULT_MODEL",
29
+ "DEFAULT_OUTPUT_DIR",
30
+ "DEFAULT_TIMEOUT",
31
+ "GEMINI_MODELS",
32
+ "IMAGE_FORMATS",
33
+ "IMAGE_SIZES",
34
+ "MAX_BATCH_SIZE",
35
+ "MAX_HUMAN_IMAGES",
36
+ "MAX_OBJECT_IMAGES",
37
+ "MAX_PROMPT_LENGTH",
38
+ "MAX_REFERENCE_IMAGES",
39
+ "RESPONSE_MODALITIES",
40
+ "APIConfig",
41
+ "ServerConfig",
42
+ "Settings",
43
+ "get_settings",
44
+ ]
@@ -0,0 +1,68 @@
1
+ """
2
+ Constants and model definitions for Gemini 3 Pro Image API.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ # Gemini Models (using official Google GenAI SDK)
8
+ GEMINI_MODELS = {
9
+ "gemini-3-pro-image-preview": "gemini-3-pro-image-preview",
10
+ "gemini-flash-latest": "gemini-flash-latest", # For prompt enhancement (non-image)
11
+ }
12
+
13
+ # All available models
14
+ ALL_MODELS = GEMINI_MODELS
15
+
16
+ # Default models
17
+ DEFAULT_MODEL = "gemini-3-pro-image-preview"
18
+ DEFAULT_ENHANCEMENT_MODEL = "gemini-flash-latest"
19
+
20
+ # Aspect ratios
21
+ ASPECT_RATIOS = [
22
+ "1:1", # Square
23
+ "2:3", # Portrait
24
+ "3:2", # Landscape
25
+ "3:4", # Portrait
26
+ "4:3", # Standard landscape
27
+ "4:5", # Portrait
28
+ "5:4", # Landscape
29
+ "9:16", # Vertical mobile
30
+ "16:9", # Widescreen
31
+ "21:9", # Ultrawide
32
+ ]
33
+
34
+ # Image formats
35
+ IMAGE_FORMATS = {
36
+ "png": "image/png",
37
+ "jpeg": "image/jpeg",
38
+ "jpg": "image/jpeg",
39
+ "webp": "image/webp",
40
+ }
41
+
42
+ # Image sizes (Gemini 3 Pro Image)
43
+ IMAGE_SIZES = ["1K", "2K", "4K"]
44
+ DEFAULT_IMAGE_SIZE = "2K"
45
+
46
+ # Reference images limits (Gemini 3 Pro Image)
47
+ MAX_REFERENCE_IMAGES = 14
48
+ MAX_OBJECT_IMAGES = 6
49
+ MAX_HUMAN_IMAGES = 5
50
+
51
+ # Response modalities
52
+ RESPONSE_MODALITIES = ["TEXT", "IMAGE"]
53
+
54
+ # Generation limits
55
+ MAX_BATCH_SIZE = 8
56
+ MAX_PROMPT_LENGTH = 8192
57
+
58
+ # File size limits
59
+ MAX_IMAGE_SIZE_MB = 20
60
+ MAX_IMAGE_SIZE_BYTES = MAX_IMAGE_SIZE_MB * 1024 * 1024
61
+
62
+ # Timeout settings (in seconds)
63
+ DEFAULT_TIMEOUT = 60
64
+ ENHANCEMENT_TIMEOUT = 30
65
+ BATCH_TIMEOUT = 120
66
+
67
+ # Output settings
68
+ DEFAULT_OUTPUT_DIR = str(Path.home() / "gemini_images")
src/config/settings.py ADDED
@@ -0,0 +1,143 @@
1
+ """
2
+ Configuration settings for the Ultimate Gemini MCP server.
3
+ """
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from pydantic import Field
10
+ from pydantic_settings import BaseSettings, SettingsConfigDict
11
+
12
+ from .constants import (
13
+ DEFAULT_ENHANCEMENT_MODEL,
14
+ DEFAULT_IMAGE_SIZE,
15
+ DEFAULT_MODEL,
16
+ DEFAULT_OUTPUT_DIR,
17
+ DEFAULT_TIMEOUT,
18
+ MAX_BATCH_SIZE,
19
+ )
20
+
21
+
22
+ class ServerConfig(BaseSettings):
23
+ """Server configuration settings."""
24
+
25
+ model_config = SettingsConfigDict(
26
+ env_file=".env",
27
+ env_file_encoding="utf-8",
28
+ case_sensitive=False,
29
+ extra="ignore",
30
+ )
31
+
32
+ # Server settings
33
+ transport: str = Field(default="stdio", description="Transport mode: stdio or http")
34
+ host: str = Field(default="localhost", description="Host for HTTP transport")
35
+ port: int = Field(default=8000, description="Port for HTTP transport")
36
+
37
+ # Logging settings
38
+ log_level: str = Field(default="INFO", description="Logging level")
39
+ log_format: str = Field(default="standard", description="Log format: standard, json, detailed")
40
+
41
+ # Output settings
42
+ output_dir: str = Field(
43
+ default=DEFAULT_OUTPUT_DIR, description="Directory for generated images"
44
+ )
45
+
46
+ @classmethod
47
+ def from_env(cls) -> "ServerConfig":
48
+ """Load configuration from environment variables."""
49
+ return cls()
50
+
51
+
52
+ class APIConfig(BaseSettings):
53
+ """API configuration for Gemini and Imagen."""
54
+
55
+ model_config = SettingsConfigDict(
56
+ env_file=".env",
57
+ env_file_encoding="utf-8",
58
+ case_sensitive=False,
59
+ extra="ignore",
60
+ )
61
+
62
+ # API Keys
63
+ gemini_api_key: str = Field(
64
+ default="",
65
+ alias="GEMINI_API_KEY",
66
+ description="Gemini API key (also accepts GOOGLE_API_KEY)",
67
+ )
68
+
69
+ # Model settings
70
+ default_model: str = Field(
71
+ default=DEFAULT_MODEL, description="Default Gemini 3 Pro Image model"
72
+ )
73
+ enhancement_model: str = Field(
74
+ default=DEFAULT_ENHANCEMENT_MODEL, description="Model for prompt enhancement"
75
+ )
76
+
77
+ # Feature flags
78
+ enable_prompt_enhancement: bool = Field(
79
+ default=True, description="Enable automatic prompt enhancement"
80
+ )
81
+ enable_batch_processing: bool = Field(default=True, description="Enable batch processing")
82
+
83
+ # Request settings
84
+ request_timeout: int = Field(default=DEFAULT_TIMEOUT, description="API request timeout")
85
+ max_batch_size: int = Field(
86
+ default=MAX_BATCH_SIZE, description="Maximum batch size for parallel requests"
87
+ )
88
+ max_retries: int = Field(default=3, description="Maximum number of retries for failed requests")
89
+
90
+ # Image settings
91
+ default_aspect_ratio: str = Field(default="1:1", description="Default aspect ratio")
92
+ default_image_size: str = Field(
93
+ default=DEFAULT_IMAGE_SIZE, description="Default image size (1K, 2K, 4K)"
94
+ )
95
+ default_output_format: str = Field(default="png", description="Default output format")
96
+ enable_google_search: bool = Field(default=False, description="Enable Google Search grounding")
97
+ response_modalities: list[str] = Field(
98
+ default=["TEXT", "IMAGE"], description="Response modalities"
99
+ )
100
+
101
+ def __init__(self, **kwargs: Any) -> None:
102
+ """Initialize API configuration with fallback for API key."""
103
+ super().__init__(**kwargs)
104
+ # Fallback to GOOGLE_API_KEY if GEMINI_API_KEY not set
105
+ if not self.gemini_api_key:
106
+ self.gemini_api_key = os.getenv("GOOGLE_API_KEY", "")
107
+
108
+ if not self.gemini_api_key:
109
+ raise ValueError("GEMINI_API_KEY or GOOGLE_API_KEY environment variable is required")
110
+
111
+ @classmethod
112
+ def from_env(cls) -> "APIConfig":
113
+ """Load API configuration from environment variables."""
114
+ return cls()
115
+
116
+
117
+ class Settings:
118
+ """Combined settings for the server."""
119
+
120
+ def __init__(self) -> None:
121
+ self.server = ServerConfig.from_env()
122
+ self.api = APIConfig.from_env()
123
+
124
+ # Ensure output directory exists
125
+ output_path = Path(self.server.output_dir)
126
+ output_path.mkdir(parents=True, exist_ok=True)
127
+
128
+ @property
129
+ def output_dir(self) -> Path:
130
+ """Get output directory as Path object."""
131
+ return Path(self.server.output_dir)
132
+
133
+
134
+ # Global settings instance (lazy initialization)
135
+ _settings: Settings | None = None
136
+
137
+
138
+ def get_settings() -> Settings:
139
+ """Get or create global settings instance."""
140
+ global _settings
141
+ if _settings is None:
142
+ _settings = Settings()
143
+ return _settings
src/core/__init__.py ADDED
@@ -0,0 +1,47 @@
1
+ """Core modules for Ultimate Gemini MCP."""
2
+
3
+ from .exceptions import (
4
+ APIError,
5
+ AuthenticationError,
6
+ ConfigurationError,
7
+ ContentPolicyError,
8
+ FileOperationError,
9
+ ImageProcessingError,
10
+ RateLimitError,
11
+ UltimateGeminiError,
12
+ ValidationError,
13
+ )
14
+ from .validation import (
15
+ sanitize_filename,
16
+ validate_aspect_ratio,
17
+ validate_base64_image,
18
+ validate_batch_size,
19
+ validate_file_path,
20
+ validate_image_format,
21
+ validate_model,
22
+ validate_prompt,
23
+ validate_prompts_list,
24
+ )
25
+
26
+ __all__ = [
27
+ # Exceptions
28
+ "UltimateGeminiError",
29
+ "ConfigurationError",
30
+ "ValidationError",
31
+ "APIError",
32
+ "AuthenticationError",
33
+ "RateLimitError",
34
+ "ContentPolicyError",
35
+ "ImageProcessingError",
36
+ "FileOperationError",
37
+ # Validation
38
+ "validate_prompt",
39
+ "validate_model",
40
+ "validate_aspect_ratio",
41
+ "validate_image_format",
42
+ "validate_file_path",
43
+ "validate_base64_image",
44
+ "validate_prompts_list",
45
+ "validate_batch_size",
46
+ "sanitize_filename",
47
+ ]
src/core/exceptions.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ Custom exceptions for the Ultimate Gemini MCP server.
3
+ """
4
+
5
+
6
+ class UltimateGeminiError(Exception):
7
+ """Base exception for all Ultimate Gemini MCP errors."""
8
+
9
+ pass
10
+
11
+
12
+ class ConfigurationError(UltimateGeminiError):
13
+ """Raised when there's a configuration error."""
14
+
15
+ pass
16
+
17
+
18
+ class ValidationError(UltimateGeminiError):
19
+ """Raised when input validation fails."""
20
+
21
+ pass
22
+
23
+
24
+ class APIError(UltimateGeminiError):
25
+ """Raised when an API request fails."""
26
+
27
+ def __init__(
28
+ self, message: str, status_code: int | None = None, response_data: dict | None = None
29
+ ):
30
+ super().__init__(message)
31
+ self.status_code = status_code
32
+ self.response_data = response_data
33
+
34
+
35
+ class AuthenticationError(APIError):
36
+ """Raised when API authentication fails."""
37
+
38
+ pass
39
+
40
+
41
+ class RateLimitError(APIError):
42
+ """Raised when API rate limit is exceeded."""
43
+
44
+ pass
45
+
46
+
47
+ class ContentPolicyError(APIError):
48
+ """Raised when content violates safety policies."""
49
+
50
+ pass
51
+
52
+
53
+ class ImageProcessingError(UltimateGeminiError):
54
+ """Raised when image processing fails."""
55
+
56
+ pass
57
+
58
+
59
+ class FileOperationError(UltimateGeminiError):
60
+ """Raised when file operations fail."""
61
+
62
+ pass
src/core/validation.py ADDED
@@ -0,0 +1,117 @@
1
+ """
2
+ Input validation utilities.
3
+ """
4
+
5
+ import base64
6
+ import re
7
+ from pathlib import Path
8
+
9
+ from ..config.constants import (
10
+ ALL_MODELS,
11
+ ASPECT_RATIOS,
12
+ IMAGE_FORMATS,
13
+ MAX_PROMPT_LENGTH,
14
+ )
15
+ from .exceptions import ValidationError
16
+
17
+
18
+ def validate_prompt(prompt: str) -> None:
19
+ """Validate prompt text."""
20
+ if not prompt or not prompt.strip():
21
+ raise ValidationError("Prompt cannot be empty")
22
+
23
+ if len(prompt) > MAX_PROMPT_LENGTH:
24
+ raise ValidationError(
25
+ f"Prompt too long: {len(prompt)} characters (max {MAX_PROMPT_LENGTH})"
26
+ )
27
+
28
+
29
+ def validate_model(model: str) -> None:
30
+ """Validate model name."""
31
+ if model not in ALL_MODELS:
32
+ available = ", ".join(ALL_MODELS.keys())
33
+ raise ValidationError(f"Invalid model '{model}'. Available models: {available}")
34
+
35
+
36
+ def validate_aspect_ratio(aspect_ratio: str) -> None:
37
+ """Validate aspect ratio."""
38
+ if aspect_ratio not in ASPECT_RATIOS:
39
+ available = ", ".join(ASPECT_RATIOS)
40
+ raise ValidationError(f"Invalid aspect ratio '{aspect_ratio}'. Available: {available}")
41
+
42
+
43
+ def validate_image_format(format_str: str) -> None:
44
+ """Validate image format."""
45
+ if format_str.lower() not in IMAGE_FORMATS:
46
+ available = ", ".join(IMAGE_FORMATS.keys())
47
+ raise ValidationError(f"Invalid image format '{format_str}'. Available: {available}")
48
+
49
+
50
+ def validate_file_path(path: str) -> Path:
51
+ """Validate and return file path."""
52
+ try:
53
+ file_path = Path(path).resolve()
54
+ except Exception as e:
55
+ raise ValidationError(f"Invalid file path '{path}': {e}") from e
56
+
57
+ if not file_path.exists():
58
+ raise ValidationError(f"File does not exist: {file_path}")
59
+
60
+ if not file_path.is_file():
61
+ raise ValidationError(f"Path is not a file: {file_path}")
62
+
63
+ return file_path
64
+
65
+
66
+ def validate_base64_image(data: str) -> None:
67
+ """Validate base64-encoded image data."""
68
+ if not data:
69
+ raise ValidationError("Base64 image data cannot be empty")
70
+
71
+ try:
72
+ # Try to decode to verify it's valid base64
73
+ decoded = base64.b64decode(data, validate=True)
74
+ if len(decoded) == 0:
75
+ raise ValidationError("Decoded image data is empty")
76
+ except Exception as e:
77
+ raise ValidationError(f"Invalid base64 image data: {e}") from e
78
+
79
+
80
+ def validate_prompts_list(prompts: list[str]) -> None:
81
+ """Validate list of prompts for batch processing."""
82
+ if not isinstance(prompts, list):
83
+ raise ValidationError("Prompts must be a list")
84
+
85
+ if not prompts:
86
+ raise ValidationError("Prompts list cannot be empty")
87
+
88
+ for i, prompt in enumerate(prompts):
89
+ if not isinstance(prompt, str):
90
+ raise ValidationError(f"Prompt at index {i} must be a string")
91
+ try:
92
+ validate_prompt(prompt)
93
+ except ValidationError as e:
94
+ raise ValidationError(f"Invalid prompt at index {i}: {e}") from e
95
+
96
+
97
+ def sanitize_filename(filename: str) -> str:
98
+ """Sanitize filename to remove unsafe and special characters."""
99
+ # Replace all non-alphanumeric characters (except hyphens) with underscores
100
+ safe_name = re.sub(r"[^a-zA-Z0-9-]", "_", filename)
101
+ # Remove multiple consecutive underscores
102
+ safe_name = re.sub(r"_+", "_", safe_name)
103
+ # Remove leading/trailing underscores
104
+ safe_name = safe_name.strip("_")
105
+ # Ensure filename is not empty
106
+ if not safe_name:
107
+ safe_name = "image"
108
+ return safe_name
109
+
110
+
111
+ def validate_batch_size(size: int, max_size: int) -> None:
112
+ """Validate batch size."""
113
+ if not isinstance(size, int) or size < 1:
114
+ raise ValidationError(f"Batch size must be at least 1, got {size}")
115
+
116
+ if size > max_size:
117
+ raise ValidationError(f"Batch size exceeds maximum: {size} > {max_size}")
src/server.py ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Ultimate Gemini MCP Server - Main Entry Point
4
+
5
+ Unified MCP server supporting:
6
+ - Gemini 3 Pro Image (with 4K resolution, reasoning, prompt enhancement and editing)
7
+ - Imagen 4, 4-Fast, and 4-Ultra (with advanced controls)
8
+ - Batch processing, prompt templates, and comprehensive features
9
+ """
10
+
11
+ import logging
12
+ import sys
13
+
14
+ from fastmcp import FastMCP
15
+
16
+ from .config import ALL_MODELS, get_settings
17
+ from .tools import register_batch_generate_tool, register_generate_image_tool
18
+
19
+ # Set up logging
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
23
+ stream=sys.stderr, # Important: use stderr for logging in MCP
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def create_app() -> FastMCP:
30
+ """
31
+ Create and configure the Ultimate Gemini MCP application.
32
+
33
+ This is the factory function used by FastMCP CLI.
34
+ """
35
+ logger.info("Initializing Ultimate Gemini MCP Server...")
36
+
37
+ try:
38
+ # Load settings (validates API key)
39
+ settings = get_settings()
40
+
41
+ logger.info(f"Output directory: {settings.output_dir}")
42
+ logger.info(f"Prompt enhancement: {settings.api.enable_prompt_enhancement}")
43
+ logger.info(f"Available models: {', '.join(ALL_MODELS.keys())}")
44
+
45
+ # Create FastMCP server
46
+ mcp = FastMCP(
47
+ "Ultimate Gemini MCP",
48
+ version="1.5.0",
49
+ )
50
+
51
+ # Register tools
52
+ register_generate_image_tool(mcp)
53
+ register_batch_generate_tool(mcp)
54
+
55
+ # Add resources
56
+ @mcp.resource("models://list")
57
+ def list_models() -> str:
58
+ """List all available image generation models."""
59
+ import json
60
+
61
+ models_info = {
62
+ "gemini": {
63
+ "gemini-3-pro-image-preview": {
64
+ "name": "Gemini 3 Pro Image",
65
+ "description": "Advanced image generation with 4K resolution, reasoning, and editing",
66
+ "features": [
67
+ "Native 4K resolution",
68
+ "Advanced reasoning capabilities",
69
+ "Prompt enhancement",
70
+ "Image editing",
71
+ "Character consistency",
72
+ "Multi-image blending",
73
+ "World knowledge integration",
74
+ "Grounded generation with Google Search",
75
+ ],
76
+ "default": True,
77
+ }
78
+ },
79
+ "imagen": {
80
+ "imagen-4": {
81
+ "name": "Imagen 4",
82
+ "description": "High-quality image generation with improved text rendering",
83
+ "features": [
84
+ "Enhanced quality",
85
+ "Better text rendering",
86
+ "Negative prompts",
87
+ "Seed-based reproducibility",
88
+ "Person generation controls",
89
+ "Advanced controls",
90
+ ],
91
+ },
92
+ "imagen-4-fast": {
93
+ "name": "Imagen 4 Fast",
94
+ "description": "Optimized for faster generation while maintaining good quality",
95
+ "features": [
96
+ "Faster generation speed",
97
+ "Good quality output",
98
+ "Negative prompts",
99
+ "Seed-based reproducibility",
100
+ "Person generation controls",
101
+ "Cost-effective",
102
+ ],
103
+ },
104
+ "imagen-4-ultra": {
105
+ "name": "Imagen 4 Ultra",
106
+ "description": "Highest quality with best prompt adherence",
107
+ "features": [
108
+ "Highest quality",
109
+ "Best prompt adherence",
110
+ "Professional results",
111
+ "Enhanced text rendering",
112
+ "Advanced controls",
113
+ ],
114
+ },
115
+ },
116
+ }
117
+
118
+ return json.dumps(models_info, indent=2)
119
+
120
+ @mcp.resource("settings://config")
121
+ def get_config() -> str:
122
+ """Get current server configuration."""
123
+ import json
124
+
125
+ config = {
126
+ "output_directory": str(settings.output_dir),
127
+ "prompt_enhancement_enabled": settings.api.enable_prompt_enhancement,
128
+ "batch_processing_enabled": settings.api.enable_batch_processing,
129
+ "default_model": settings.api.default_model,
130
+ "default_image_size": settings.api.default_image_size,
131
+ "max_batch_size": settings.api.max_batch_size,
132
+ "request_timeout": settings.api.request_timeout,
133
+ "default_aspect_ratio": settings.api.default_aspect_ratio,
134
+ "default_output_format": settings.api.default_output_format,
135
+ }
136
+
137
+ return json.dumps(config, indent=2)
138
+
139
+ logger.info("Ultimate Gemini MCP Server initialized successfully")
140
+ return mcp
141
+
142
+ except Exception as e:
143
+ logger.error(f"Failed to initialize server: {e}", exc_info=True)
144
+ raise
145
+
146
+
147
+ def main() -> None:
148
+ """Main entry point for direct execution."""
149
+ try:
150
+ logger.info("Starting Ultimate Gemini MCP Server...")
151
+
152
+ # Create application
153
+ app = create_app()
154
+
155
+ # Run the server (FastMCP handles stdio transport)
156
+ logger.info("Server is ready and listening for MCP requests")
157
+ app.run()
158
+
159
+ except KeyboardInterrupt:
160
+ logger.info("Server shutdown requested by user")
161
+ sys.exit(0)
162
+ except Exception as e:
163
+ logger.error(f"Server error: {e}", exc_info=True)
164
+ sys.exit(1)
165
+
166
+
167
+ if __name__ == "__main__":
168
+ main()
@@ -0,0 +1,13 @@
1
+ """Services module for Ultimate Gemini MCP."""
2
+
3
+ from .gemini_client import GeminiClient
4
+ from .image_service import ImageResult, ImageService
5
+ from .prompt_enhancer import PromptEnhancer, create_prompt_enhancer
6
+
7
+ __all__ = [
8
+ "GeminiClient",
9
+ "ImageService",
10
+ "ImageResult",
11
+ "PromptEnhancer",
12
+ "create_prompt_enhancer",
13
+ ]