|
11 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | 12 | # See the License for the specific language governing permissions and |
13 | 13 | # limitations under the License. |
| 14 | + |
14 | 15 | from typing import Dict |
15 | 16 | from google.adk.tools import ToolContext |
16 | 17 | from google.genai import types |
|
23 | 24 | logger = get_logger(__name__) |
24 | 25 |
|
25 | 26 | client = Ark( |
26 | | - api_key=getenv("MODEL_IMAGE_API_KEY"), |
27 | | - base_url=getenv("MODEL_IMAGE_API_BASE"), |
| 27 | + api_key=getenv("MODEL_EDIT_API_KEY"), |
| 28 | + base_url=getenv("MODEL_EDIT_API_BASE"), |
28 | 29 | ) |
29 | 30 |
|
30 | 31 |
|
31 | 32 | async def image_edit( |
32 | | - origin_image: str, |
33 | | - image_name: str, |
34 | | - image_prompt: str, |
35 | | - response_format: str, |
36 | | - guidance_scale: float, |
37 | | - watermark: bool, |
38 | | - seed: int, |
| 33 | + params: list, |
39 | 34 | tool_context: ToolContext, |
40 | 35 | ) -> Dict: |
41 | | - """Edit an image accoding to the prompt. |
| 36 | + """ |
| 37 | + Edit images in batch according to prompts and optional settings. |
| 38 | +
|
| 39 | + Each item in `params` describes a single image-edit request. |
42 | 40 |
|
43 | 41 | Args: |
44 | | - origin_image: The url or the base64 string of the edited image. |
45 | | - image_name: The name of the generated image. |
46 | | - image_prompt: The prompt that describes the image. |
47 | | - response_format: str, b64_json or url, default url. |
48 | | - guidance_scale: default 2.5. |
49 | | - watermark: default True. |
50 | | - seed: default -1. |
| 42 | + params (list[dict]): |
| 43 | + A list of image editing requests. Each item supports: |
| 44 | +
|
| 45 | + Required: |
| 46 | + - origin_image (str): |
| 47 | + The URL or Base64 string of the original image to edit. |
| 48 | + Example: |
| 49 | + * URL: "https://example.com/image.png" |
| 50 | + * Base64: "data:image/png;base64,<BASE64>" |
| 51 | +
|
| 52 | + - prompt (str): |
| 53 | + The textual description/instruction for editing the image. |
| 54 | + Supports English and Chinese. |
| 55 | +
|
| 56 | + Optional: |
| 57 | + - image_name (str): |
| 58 | + Name/identifier for the generated image. |
51 | 59 |
|
| 60 | + - response_format (str): |
| 61 | + Format of the returned image. |
| 62 | + * "url": JPEG link (default) |
| 63 | + * "b64_json": Base64 string in JSON |
| 64 | +
|
| 65 | + - guidance_scale (float): |
| 66 | + How strongly the prompt affects the result. |
| 67 | + Range: [1.0, 10.0], default 2.5. |
| 68 | +
|
| 69 | + - watermark (bool): |
| 70 | + Whether to add watermark. |
| 71 | + Default: True. |
| 72 | +
|
| 73 | + - seed (int): |
| 74 | + Random seed for reproducibility. |
| 75 | + Range: [-1, 2^31-1], default -1 (random). |
| 76 | +
|
| 77 | + Returns: |
| 78 | + Dict: API response containing generated image metadata. |
| 79 | + Example: |
| 80 | + { |
| 81 | + "status": "success", |
| 82 | + "success_list": [{"image_name": ""}], |
| 83 | + "error_list": [{}] |
| 84 | + } |
| 85 | +
|
| 86 | + Notes: |
| 87 | + - Uses SeedEdit 3.0 model. |
| 88 | + - Provide the same `seed` for consistent outputs across runs. |
| 89 | + - A high `guidance_scale` enforces stricter adherence to text prompt. |
52 | 90 | """ |
53 | | - try: |
54 | | - response = client.images.generate( |
55 | | - model=getenv("MODEL_EDIT_NAME"), |
56 | | - image=origin_image, |
57 | | - prompt=image_prompt, |
58 | | - response_format=response_format, |
59 | | - guidance_scale=guidance_scale, |
60 | | - watermark=watermark, |
61 | | - seed=seed, |
62 | | - ) |
63 | | - |
64 | | - if response.data and len(response.data) > 0: |
65 | | - for item in response.data: |
66 | | - if response_format == "url": |
67 | | - image = item.url |
68 | | - tool_context.state["generated_image_url"] = image |
69 | | - |
70 | | - elif response_format == "b64_json": |
71 | | - image = item.b64_json |
72 | | - image_bytes = base64.b64decode(image) |
73 | | - |
74 | | - tool_context.state["generated_image_url"] = ( |
75 | | - f"data:image/jpeg;base64,{image}" |
76 | | - ) |
77 | | - |
78 | | - report_artifact = types.Part.from_bytes( |
79 | | - data=image_bytes, mime_type="image/png" |
80 | | - ) |
81 | | - await tool_context.save_artifact(image_name, report_artifact) |
82 | | - logger.debug(f"Image saved as ADK artifact: {image_name}") |
83 | | - |
84 | | - return {"status": "success", "image_name": image_name, "image": image} |
85 | | - else: |
86 | | - error_details = f"No images returned by Doubao model: {response}" |
| 91 | + success_list = [] |
| 92 | + error_list = [] |
| 93 | + for idx, item in enumerate(params): |
| 94 | + image_name = item.get("image_name", f"generated_image_{idx}") |
| 95 | + prompt = item.get("prompt") |
| 96 | + origin_image = item.get("origin_image") |
| 97 | + response_format = item.get("response_format", "url") |
| 98 | + guidance_scale = item.get("guidance_scale", 2.5) |
| 99 | + watermark = item.get("watermark", True) |
| 100 | + seed = item.get("seed", -1) |
| 101 | + |
| 102 | + try: |
| 103 | + response = client.images.generate( |
| 104 | + model=getenv("MODEL_EDIT_NAME"), |
| 105 | + image=origin_image, |
| 106 | + prompt=prompt, |
| 107 | + response_format=response_format, |
| 108 | + guidance_scale=guidance_scale, |
| 109 | + watermark=watermark, |
| 110 | + seed=seed, |
| 111 | + ) |
| 112 | + |
| 113 | + if response.data and len(response.data) > 0: |
| 114 | + for item in response.data: |
| 115 | + if response_format == "url": |
| 116 | + image = item.url |
| 117 | + tool_context.state[f"{image_name}_url"] = image |
| 118 | + |
| 119 | + elif response_format == "b64_json": |
| 120 | + image = item.b64_json |
| 121 | + image_bytes = base64.b64decode(image) |
| 122 | + |
| 123 | + tool_context.state[f"{image_name}_url"] = ( |
| 124 | + f"data:image/jpeg;base64,{image}" |
| 125 | + ) |
| 126 | + |
| 127 | + report_artifact = types.Part.from_bytes( |
| 128 | + data=image_bytes, mime_type="image/png" |
| 129 | + ) |
| 130 | + await tool_context.save_artifact(image_name, report_artifact) |
| 131 | + logger.debug(f"Image saved as ADK artifact: {image_name}") |
| 132 | + |
| 133 | + success_list.append({image_name: image}) |
| 134 | + else: |
| 135 | + error_details = f"No images returned by Doubao model: {response}" |
| 136 | + logger.error(error_details) |
| 137 | + error_list.append(image_name) |
| 138 | + |
| 139 | + except Exception as e: |
| 140 | + error_details = f"No images returned by Doubao model: {e}" |
87 | 141 | logger.error(error_details) |
88 | | - return {"status": "error", "message": error_details} |
| 142 | + error_list.append(image_name) |
89 | 143 |
|
90 | | - except Exception as e: |
| 144 | + if len(success_list) == 0: |
91 | 145 | return { |
92 | 146 | "status": "error", |
93 | | - "message": f"Doubao image generation failed: {str(e)}", |
| 147 | + "success_list": success_list, |
| 148 | + "error_list": error_list, |
| 149 | + } |
| 150 | + else: |
| 151 | + return { |
| 152 | + "status": "success", |
| 153 | + "success_list": success_list, |
| 154 | + "error_list": error_list, |
94 | 155 | } |
0 commit comments