indoxrouter 0.1.18__tar.gz → 0.1.19__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: indoxrouter
3
- Version: 0.1.18
3
+ Version: 0.1.19
4
4
  Summary: A unified client for various AI providers
5
5
  Author-email: indoxRouter Team <ashkan.eskandari.dev@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,159 @@
1
+ """
2
+ Example script demonstrating image generation with Google's Imagen model through indoxRouter.
3
+
4
+ This script shows how to properly format parameters for Google's image generation API,
5
+ including the special handling of aspect ratios and other Google-specific parameters.
6
+
7
+ Requirements:
8
+ - Install indoxRouter: pip install indoxrouter
9
+ - Set INDOX_ROUTER_API_KEY environment variable with your API key
10
+ """
11
+
12
+ import os
13
+ import base64
14
+ from io import BytesIO
15
+ import requests
16
+ from datetime import datetime
17
+
18
+ from indoxrouter import Client
19
+
20
+ # For displaying images in notebooks
21
+ try:
22
+ from IPython.display import Image, display
23
+
24
+ in_notebook = True
25
+ except ImportError:
26
+ in_notebook = False
27
+
28
+ # Initialize client with API key from environment variable
29
+ api_key = os.environ.get("INDOX_ROUTER_API_KEY")
30
+ if not api_key:
31
+ print("Please set the INDOX_ROUTER_API_KEY environment variable")
32
+ exit(1)
33
+
34
+ client = Client(api_key=api_key)
35
+
36
+
37
+ def save_image_from_url(url, filename):
38
+ """Download and save an image from a URL."""
39
+ response = requests.get(url)
40
+ if response.status_code == 200:
41
+ with open(filename, "wb") as f:
42
+ f.write(response.content)
43
+ print(f"Image saved to {filename}")
44
+ else:
45
+ print(f"Failed to download image: {response.status_code}")
46
+
47
+
48
+ def save_image_from_b64(b64_data, filename):
49
+ """Save an image from base64 data."""
50
+ image_data = base64.b64decode(b64_data)
51
+ with open(filename, "wb") as f:
52
+ f.write(image_data)
53
+ print(f"Image saved to {filename}")
54
+
55
+
56
+ def generate_google_image(prompt, aspect_ratio="1:1", **kwargs):
57
+ """
58
+ Generate an image using Google's Imagen model.
59
+
60
+ Args:
61
+ prompt: The text prompt to generate an image from
62
+ aspect_ratio: The aspect ratio of the image (e.g., "1:1", "4:3", "16:9")
63
+ **kwargs: Additional parameters to pass to the API
64
+ """
65
+ print(f"\n=== Generating image with Google Imagen ===")
66
+ print(f"Prompt: {prompt}")
67
+ print(f"Aspect ratio: {aspect_ratio}")
68
+
69
+ try:
70
+ # Generate the image
71
+ # Note: The client will automatically convert "1024x1024" to "1:1" for Google models,
72
+ # but it's more explicit to use the correct format directly
73
+ response = client.images(
74
+ prompt=prompt,
75
+ model="google/imagen-3.0-generate-002",
76
+ size=aspect_ratio, # Google uses aspect ratios instead of pixel dimensions
77
+ **kwargs,
78
+ )
79
+
80
+ print(f"Response received from Google:")
81
+ print(f"- Success: {response['success']}")
82
+ print(f"- Cost: ${response['usage']['cost']:.4f}")
83
+
84
+ # Check if we have image data
85
+ if "data" in response and response["data"]:
86
+ image_data = response["data"][0]
87
+
88
+ # Create a timestamp for unique filenames
89
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
90
+
91
+ if "url" in image_data and image_data["url"]:
92
+ filename = (
93
+ f"google_imagen_{aspect_ratio.replace(':', 'x')}_{timestamp}.png"
94
+ )
95
+ save_image_from_url(image_data["url"], filename)
96
+
97
+ # Display in notebook if possible
98
+ if in_notebook:
99
+ display(Image(url=image_data["url"]))
100
+
101
+ elif "b64_json" in image_data and image_data["b64_json"]:
102
+ filename = (
103
+ f"google_imagen_{aspect_ratio.replace(':', 'x')}_{timestamp}.png"
104
+ )
105
+ save_image_from_b64(image_data["b64_json"], filename)
106
+
107
+ # Display in notebook if possible
108
+ if in_notebook:
109
+ display(Image(data=base64.b64decode(image_data["b64_json"])))
110
+ else:
111
+ print("No image data found in response")
112
+ print("Response:", response)
113
+
114
+ except Exception as e:
115
+ print(f"Error generating image with Google Imagen: {str(e)}")
116
+
117
+
118
+ def main():
119
+ """Main function demonstrating Google Imagen image generation."""
120
+
121
+ # Basic example with 1:1 aspect ratio (square image)
122
+ generate_google_image(
123
+ prompt="A tranquil zen garden with cherry blossoms and a small pond"
124
+ )
125
+
126
+ # Example with 16:9 aspect ratio (widescreen)
127
+ generate_google_image(
128
+ prompt="A wide panoramic view of a futuristic city skyline at sunset with flying vehicles",
129
+ aspect_ratio="16:9",
130
+ )
131
+
132
+ # Example with 9:16 aspect ratio (portrait/mobile)
133
+ generate_google_image(
134
+ prompt="A tall waterfall surrounded by lush greenery in a tropical forest",
135
+ aspect_ratio="9:16",
136
+ )
137
+
138
+ # Example with negative prompt to influence the generation
139
+ generate_google_image(
140
+ prompt="A detailed watercolor painting of a coastal village with boats in the harbor",
141
+ negative_prompt="dark, moody, sketch, black and white, blurry",
142
+ )
143
+
144
+ # Example with more parameters
145
+ generate_google_image(
146
+ prompt="A beautiful tiger resting in a lush jungle environment",
147
+ aspect_ratio="4:3",
148
+ negative_prompt="cartoon, illustration, low quality",
149
+ seed=123456, # Consistent results with the same seed
150
+ guidance_scale=7.5, # Controls how closely the model follows the prompt (usually between 1-20)
151
+ safety_filter_level="block_none", # Less restrictive safety filter
152
+ )
153
+
154
+ # Close the client when done
155
+ client.close()
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()
@@ -0,0 +1,141 @@
1
+ """
2
+ Example script demonstrating image generation with various providers through indoxRouter.
3
+
4
+ This script shows how to generate images using different providers (OpenAI, xAI, Google)
5
+ and how to handle the responses to display or save the generated images.
6
+
7
+ Requirements:
8
+ - Install indoxRouter: pip install indoxrouter
9
+ - Set INDOX_ROUTER_API_KEY environment variable with your API key
10
+ """
11
+
12
+ import os
13
+ import base64
14
+ from io import BytesIO
15
+ import requests
16
+ from datetime import datetime
17
+
18
+ from indoxrouter import Client
19
+
20
+ # For displaying images in notebooks
21
+ try:
22
+ from IPython.display import Image, display
23
+
24
+ in_notebook = True
25
+ except ImportError:
26
+ in_notebook = False
27
+
28
+ # Initialize client with API key from environment variable
29
+ client = Client()
30
+
31
+
32
+ def save_image_from_url(url, filename):
33
+ """Download and save an image from a URL."""
34
+ response = requests.get(url)
35
+ if response.status_code == 200:
36
+ with open(filename, "wb") as f:
37
+ f.write(response.content)
38
+ print(f"Image saved to {filename}")
39
+ else:
40
+ print(f"Failed to download image: {response.status_code}")
41
+
42
+
43
+ def save_image_from_b64(b64_data, filename):
44
+ """Save an image from base64 data."""
45
+ image_data = base64.b64decode(b64_data)
46
+ with open(filename, "wb") as f:
47
+ f.write(image_data)
48
+ print(f"Image saved to {filename}")
49
+
50
+
51
+ def generate_and_save_image(provider, model, prompt):
52
+ """Generate an image and save it to a file."""
53
+ print(f"\n=== Generating image with {provider}/{model} ===")
54
+ print(f"Prompt: {prompt}")
55
+
56
+ try:
57
+ # Generate the image
58
+ response = client.images(
59
+ prompt=prompt,
60
+ model=f"{provider}/{model}",
61
+ size="1024x1024",
62
+ )
63
+
64
+ print(f"Response received from {provider}:")
65
+ print(f"- Success: {response['success']}")
66
+ print(f"- Cost: ${response['usage']['cost']:.4f}")
67
+
68
+ # Check if we have image data
69
+ if "data" in response and response["data"]:
70
+ image_data = response["data"][0]
71
+
72
+ # Create a timestamp for unique filenames
73
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
74
+
75
+ if "url" in image_data and image_data["url"]:
76
+ filename = f"{provider}_{model}_{timestamp}.png"
77
+ save_image_from_url(image_data["url"], filename)
78
+
79
+ # Display in notebook if possible
80
+ if in_notebook:
81
+ display(Image(url=image_data["url"]))
82
+
83
+ elif "b64_json" in image_data and image_data["b64_json"]:
84
+ filename = f"{provider}_{model}_{timestamp}.png"
85
+ save_image_from_b64(image_data["b64_json"], filename)
86
+
87
+ # Display in notebook if possible
88
+ if in_notebook:
89
+ display(Image(data=base64.b64decode(image_data["b64_json"])))
90
+
91
+ # Check for revised prompt (DALL-E 3 often revises prompts)
92
+ if "revised_prompt" in image_data and image_data["revised_prompt"]:
93
+ print(f"Revised prompt: {image_data['revised_prompt']}")
94
+ else:
95
+ print("No image data found in response")
96
+ print("Response:", response)
97
+
98
+ except Exception as e:
99
+ print(f"Error generating image with {provider}/{model}: {str(e)}")
100
+
101
+
102
+ # Example prompts
103
+ prompts = {
104
+ "landscape": "A beautiful mountain landscape with a lake at sunset",
105
+ "animal": "A cute puppy playing with a ball in a garden",
106
+ "abstract": "An abstract digital art piece with vibrant colors and geometric shapes",
107
+ "space": "A realistic view of Earth from space with the moon in the background",
108
+ }
109
+
110
+
111
+ def main():
112
+ # Test OpenAI models
113
+ generate_and_save_image("openai", "dall-e-2", prompts["landscape"])
114
+ generate_and_save_image("openai", "dall-e-3", prompts["animal"])
115
+
116
+ # Test with gpt-image-1 if available
117
+ try:
118
+ generate_and_save_image("openai", "gpt-image-1", prompts["abstract"])
119
+ except Exception as e:
120
+ print(f"gpt-image-1 test skipped: {str(e)}")
121
+
122
+ # Test xAI models
123
+ try:
124
+ generate_and_save_image("xai", "grok-2-image", prompts["space"])
125
+ except Exception as e:
126
+ print(f"xAI test skipped: {str(e)}")
127
+
128
+ # Test Google models
129
+ try:
130
+ generate_and_save_image(
131
+ "google", "imagen-3.0-generate-002", prompts["abstract"]
132
+ )
133
+ except Exception as e:
134
+ print(f"Google test skipped: {str(e)}")
135
+
136
+ # Close the client
137
+ client.close()
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()
@@ -399,6 +399,48 @@ class Client:
399
399
  # is having issues with JSON formatted model strings
400
400
  return model
401
401
 
402
+ def _format_image_size_for_provider(
403
+ self, size: str, provider: str, model: str
404
+ ) -> str:
405
+ """
406
+ Format the image size parameter based on the provider's requirements.
407
+
408
+ Google requires aspect ratios like "1:1", "4:3", etc. while OpenAI uses pixel dimensions
409
+ like "1024x1024", "512x512", etc.
410
+
411
+ Args:
412
+ size: The size parameter (e.g., "1024x1024")
413
+ provider: The provider name (e.g., "google", "openai")
414
+ model: The model name
415
+
416
+ Returns:
417
+ Formatted size parameter appropriate for the provider
418
+ """
419
+ if provider.lower() == "google":
420
+ # Google uses aspect ratios instead of pixel dimensions
421
+ # Convert common pixel dimensions to aspect ratios
422
+ size_to_aspect_ratio = {
423
+ "1024x1024": "1:1",
424
+ "512x512": "1:1",
425
+ "256x256": "1:1",
426
+ "1024x768": "4:3",
427
+ "768x1024": "3:4",
428
+ "1024x1536": "2:3",
429
+ "1536x1024": "3:2",
430
+ "1792x1024": "16:9",
431
+ "1024x1792": "9:16",
432
+ }
433
+
434
+ # Check if size is already in aspect ratio format (contains a colon)
435
+ if ":" in size:
436
+ return size
437
+
438
+ # Convert to aspect ratio if we have a mapping, otherwise use default 1:1
439
+ return size_to_aspect_ratio.get(size, "1:1")
440
+
441
+ # For other providers, return the original size
442
+ return size
443
+
402
444
  def chat(
403
445
  self,
404
446
  messages: List[Dict[str, str]],
@@ -602,6 +644,11 @@ class Client:
602
644
  # Format the model string
603
645
  formatted_model = self._format_model_string(model)
604
646
 
647
+ # Extract provider from model string if present
648
+ provider = "openai" # Default provider
649
+ if "/" in model:
650
+ provider, _ = model.split("/", 1)
651
+
605
652
  # Filter out problematic parameters
606
653
  filtered_kwargs = {}
607
654
  for key, value in kwargs.items():
@@ -617,8 +664,15 @@ class Client:
617
664
  # Add optional parameters only if they are explicitly provided
618
665
  if n is not None:
619
666
  data["n"] = n
667
+
668
+ # Handle size parameter with provider-specific formatting
620
669
  if size is not None:
621
- data["size"] = size
670
+ formatted_size = self._format_image_size_for_provider(size, provider, model)
671
+ data["size"] = formatted_size
672
+ elif provider.lower() == "google":
673
+ # Default size for Google if not provided
674
+ data["size"] = "1:1"
675
+
622
676
  if quality is not None:
623
677
  data["quality"] = quality
624
678
  if style is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: indoxrouter
3
- Version: 0.1.18
3
+ Version: 0.1.19
4
4
  Summary: A unified client for various AI providers
5
5
  Author-email: indoxRouter Team <ashkan.eskandari.dev@gmail.com>
6
6
  License: MIT
@@ -3,6 +3,8 @@ README.md
3
3
  pyproject.toml
4
4
  cookbook/README.md
5
5
  cookbook/indoxRouter_cookbook.ipynb
6
+ examples/google_image_generation.py
7
+ examples/image_generation.py
6
8
  indoxrouter/__init__.py
7
9
  indoxrouter/client.py
8
10
  indoxrouter/constants.py
@@ -11,4 +13,5 @@ indoxrouter.egg-info/PKG-INFO
11
13
  indoxrouter.egg-info/SOURCES.txt
12
14
  indoxrouter.egg-info/dependency_links.txt
13
15
  indoxrouter.egg-info/requires.txt
14
- indoxrouter.egg-info/top_level.txt
16
+ indoxrouter.egg-info/top_level.txt
17
+ tests/test_image.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "indoxrouter"
7
- version = "0.1.18"
7
+ version = "0.1.19"
8
8
  authors = [
9
9
  {name = "indoxRouter Team", email = "ashkan.eskandari.dev@gmail.com"},
10
10
  ]
@@ -0,0 +1,93 @@
1
+ """
2
+ Test the image generation functionality of the IndoxRouter client.
3
+ """
4
+
5
+ import unittest
6
+ from unittest.mock import patch, MagicMock
7
+ import os
8
+ from indoxrouter import Client
9
+
10
+
11
+ class TestImageGeneration(unittest.TestCase):
12
+ """Test the image generation functionality."""
13
+
14
+ def setUp(self):
15
+ """Set up the test case."""
16
+ # Use a mock API key for testing
17
+ self.api_key = "test_api_key"
18
+
19
+ # Create a patched client that doesn't make real API calls
20
+ with patch("indoxrouter.client.requests.Session") as mock_session:
21
+ # Mock successful authentication response
22
+ mock_response = MagicMock()
23
+ mock_response.status_code = 200
24
+ mock_response.json.return_value = {"access_token": "mock_token"}
25
+ mock_session.return_value.post.return_value = mock_response
26
+
27
+ # Create client with mocked session
28
+ self.client = Client(api_key=self.api_key)
29
+ self.client._request = MagicMock() # Replace _request with mock
30
+
31
+ def test_image_generation_response_format(self):
32
+ """Test that the image generation response format is correct."""
33
+ # Mock response data that matches what we expect from the server
34
+ mock_response = {
35
+ "request_id": "test-request-id",
36
+ "created_at": "2025-05-29T11:39:24.621706",
37
+ "duration_ms": 12340.412378311157,
38
+ "provider": "openai",
39
+ "model": "dall-e-2",
40
+ "success": True,
41
+ "message": "",
42
+ "usage": {
43
+ "tokens_prompt": 0,
44
+ "tokens_completion": 0,
45
+ "tokens_total": 0,
46
+ "cost": 0.016,
47
+ "latency": 12.240789651870728,
48
+ "timestamp": "2025-05-29T11:39:24.612377",
49
+ },
50
+ "data": [
51
+ {
52
+ "url": "https://example.com/generated-image.png",
53
+ "revised_prompt": "A beautiful sunset over the ocean with clouds.",
54
+ }
55
+ ],
56
+ }
57
+
58
+ # Set the mock response for the _request method
59
+ self.client._request.return_value = mock_response
60
+
61
+ # Call the images method
62
+ response = self.client.images(
63
+ prompt="A beautiful sunset over the ocean",
64
+ model="openai/dall-e-2",
65
+ size="1024x1024",
66
+ )
67
+
68
+ # Verify the client made the request with the correct parameters
69
+ self.client._request.assert_called_once()
70
+ call_args = self.client._request.call_args[0]
71
+
72
+ # Verify the response format
73
+ self.assertEqual(response["success"], True)
74
+ self.assertEqual(response["provider"], "openai")
75
+ self.assertEqual(response["model"], "dall-e-2")
76
+
77
+ # Verify the data contains the image URL
78
+ self.assertIn("data", response)
79
+ self.assertIsInstance(response["data"], list)
80
+ self.assertEqual(len(response["data"]), 1)
81
+ self.assertIn("url", response["data"][0])
82
+ self.assertEqual(
83
+ response["data"][0]["url"], "https://example.com/generated-image.png"
84
+ )
85
+
86
+ # Verify usage information
87
+ self.assertIn("usage", response)
88
+ self.assertIn("cost", response["usage"])
89
+ self.assertGreater(response["usage"]["cost"], 0)
90
+
91
+
92
+ if __name__ == "__main__":
93
+ unittest.main()
File without changes
File without changes
File without changes