local-coze 0.0.1__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.
Files changed (58) hide show
  1. local_coze/__init__.py +110 -0
  2. local_coze/cli/__init__.py +3 -0
  3. local_coze/cli/chat.py +126 -0
  4. local_coze/cli/cli.py +34 -0
  5. local_coze/cli/constants.py +7 -0
  6. local_coze/cli/db.py +81 -0
  7. local_coze/cli/embedding.py +193 -0
  8. local_coze/cli/image.py +162 -0
  9. local_coze/cli/knowledge.py +195 -0
  10. local_coze/cli/search.py +198 -0
  11. local_coze/cli/utils.py +41 -0
  12. local_coze/cli/video.py +191 -0
  13. local_coze/cli/video_edit.py +888 -0
  14. local_coze/cli/voice.py +351 -0
  15. local_coze/core/__init__.py +25 -0
  16. local_coze/core/client.py +253 -0
  17. local_coze/core/config.py +58 -0
  18. local_coze/core/exceptions.py +67 -0
  19. local_coze/database/__init__.py +29 -0
  20. local_coze/database/client.py +170 -0
  21. local_coze/database/migration.py +342 -0
  22. local_coze/embedding/__init__.py +31 -0
  23. local_coze/embedding/client.py +350 -0
  24. local_coze/embedding/models.py +130 -0
  25. local_coze/image/__init__.py +19 -0
  26. local_coze/image/client.py +110 -0
  27. local_coze/image/models.py +163 -0
  28. local_coze/knowledge/__init__.py +19 -0
  29. local_coze/knowledge/client.py +148 -0
  30. local_coze/knowledge/models.py +45 -0
  31. local_coze/llm/__init__.py +25 -0
  32. local_coze/llm/client.py +317 -0
  33. local_coze/llm/models.py +48 -0
  34. local_coze/memory/__init__.py +14 -0
  35. local_coze/memory/client.py +176 -0
  36. local_coze/s3/__init__.py +12 -0
  37. local_coze/s3/client.py +580 -0
  38. local_coze/s3/models.py +18 -0
  39. local_coze/search/__init__.py +19 -0
  40. local_coze/search/client.py +183 -0
  41. local_coze/search/models.py +57 -0
  42. local_coze/video/__init__.py +17 -0
  43. local_coze/video/client.py +347 -0
  44. local_coze/video/models.py +39 -0
  45. local_coze/video_edit/__init__.py +23 -0
  46. local_coze/video_edit/examples.py +340 -0
  47. local_coze/video_edit/frame_extractor.py +176 -0
  48. local_coze/video_edit/models.py +362 -0
  49. local_coze/video_edit/video_edit.py +631 -0
  50. local_coze/voice/__init__.py +17 -0
  51. local_coze/voice/asr.py +82 -0
  52. local_coze/voice/models.py +86 -0
  53. local_coze/voice/tts.py +94 -0
  54. local_coze-0.0.1.dist-info/METADATA +636 -0
  55. local_coze-0.0.1.dist-info/RECORD +58 -0
  56. local_coze-0.0.1.dist-info/WHEEL +4 -0
  57. local_coze-0.0.1.dist-info/entry_points.txt +3 -0
  58. local_coze-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,350 @@
1
+ import asyncio
2
+ from typing import Optional, List, Dict
3
+ from cozeloop.decorator import observe
4
+ from coze_coding_utils.runtime_ctx.context import Context
5
+
6
+ from ..core.client import BaseClient
7
+ from ..core.config import Config
8
+ from ..core.exceptions import APIError, ValidationError
9
+ from .models import (
10
+ EmbeddingConfig,
11
+ EmbeddingInputItem,
12
+ EmbeddingInputImageURL,
13
+ EmbeddingInputVideoURL,
14
+ EmbeddingRequest,
15
+ EmbeddingResponse,
16
+ EmbeddingData,
17
+ EmbeddingUsage,
18
+ MultiEmbeddingConfig,
19
+ SparseEmbeddingConfig,
20
+ SparseEmbeddingItem,
21
+ )
22
+
23
+
24
+ class EmbeddingClient(BaseClient):
25
+ def __init__(
26
+ self,
27
+ config: Optional[Config] = None,
28
+ ctx: Optional[Context] = None,
29
+ custom_headers: Optional[Dict[str, str]] = None,
30
+ verbose: bool = False,
31
+ ):
32
+ super().__init__(config, ctx, custom_headers, verbose)
33
+ self.base_url = self.config.base_url
34
+ self.model = EmbeddingConfig.DEFAULT_MODEL
35
+
36
+ def _validate_inputs(
37
+ self,
38
+ texts: Optional[List[str]],
39
+ image_urls: Optional[List[str]],
40
+ video_urls: Optional[List[str]] = None
41
+ ) -> None:
42
+ has_texts = texts is not None and len(texts) > 0
43
+ has_images = image_urls is not None and len(image_urls) > 0
44
+ has_videos = video_urls is not None and len(video_urls) > 0
45
+
46
+ if not has_texts and not has_images and not has_videos:
47
+ raise ValidationError(
48
+ "At least one of texts, image_urls, or video_urls must be provided",
49
+ field="input"
50
+ )
51
+
52
+ total_inputs = 0
53
+ if texts:
54
+ total_inputs += len(texts)
55
+ if image_urls:
56
+ total_inputs += len(image_urls)
57
+ if video_urls:
58
+ total_inputs += len(video_urls)
59
+
60
+ if total_inputs > EmbeddingConfig.MAX_BATCH_SIZE:
61
+ raise ValidationError(
62
+ f"Total inputs exceed maximum batch size of {EmbeddingConfig.MAX_BATCH_SIZE}",
63
+ field="input",
64
+ value=total_inputs
65
+ )
66
+
67
+ def _build_input_items(
68
+ self,
69
+ texts: Optional[List[str]],
70
+ image_urls: Optional[List[str]],
71
+ video_urls: Optional[List[str]] = None
72
+ ) -> List[EmbeddingInputItem]:
73
+ items = []
74
+ if texts:
75
+ for text in texts:
76
+ if text and text.strip():
77
+ items.append(EmbeddingInputItem(type="text", text=text))
78
+ if image_urls:
79
+ for url in image_urls:
80
+ if url and url.strip():
81
+ items.append(EmbeddingInputItem(
82
+ type="image_url",
83
+ image_url=EmbeddingInputImageURL(url=url)
84
+ ))
85
+ if video_urls:
86
+ for url in video_urls:
87
+ if url and url.strip():
88
+ items.append(EmbeddingInputItem(
89
+ type="video_url",
90
+ video_url=EmbeddingInputVideoURL(url=url)
91
+ ))
92
+ return items
93
+
94
+ @observe
95
+ def embed(
96
+ self,
97
+ texts: Optional[List[str]] = None,
98
+ image_urls: Optional[List[str]] = None,
99
+ video_urls: Optional[List[str]] = None,
100
+ model: Optional[str] = None,
101
+ dimensions: Optional[int] = None,
102
+ encoding_format: Optional[str] = None,
103
+ instructions: Optional[str] = None,
104
+ multi_embedding: bool = False,
105
+ sparse_embedding: bool = False
106
+ ) -> EmbeddingResponse:
107
+ self._validate_inputs(texts, image_urls, video_urls)
108
+
109
+ input_items = self._build_input_items(texts, image_urls, video_urls)
110
+ if not input_items:
111
+ raise ValidationError("No valid input provided", field="input")
112
+
113
+ request = EmbeddingRequest(
114
+ model=model or self.model,
115
+ input=input_items,
116
+ dimensions=dimensions,
117
+ encoding_format=encoding_format,
118
+ instructions=instructions,
119
+ multi_embedding=MultiEmbeddingConfig(type="enabled") if multi_embedding else None,
120
+ sparse_embedding=SparseEmbeddingConfig(type="enabled") if sparse_embedding else None
121
+ )
122
+
123
+ data = self._request(
124
+ method="POST",
125
+ url=f"{self.base_url}/api/v3/embeddings/multimodal",
126
+ json=request.to_api_request()
127
+ )
128
+
129
+ if "error" in data and data["error"]:
130
+ raise APIError(
131
+ f"API returned error: {data['error'].get('message', 'Unknown error')}",
132
+ code=data['error'].get('code')
133
+ )
134
+
135
+ return EmbeddingResponse(**data)
136
+
137
+ @observe
138
+ def embed_text(
139
+ self,
140
+ text: str,
141
+ model: Optional[str] = None,
142
+ dimensions: Optional[int] = None,
143
+ instructions: Optional[str] = None
144
+ ) -> List[float]:
145
+ response = self.embed(
146
+ texts=[text],
147
+ model=model,
148
+ dimensions=dimensions,
149
+ instructions=instructions
150
+ )
151
+ if response.embedding:
152
+ return response.embedding
153
+ raise APIError("No embedding returned")
154
+
155
+ @observe
156
+ def embed_texts(
157
+ self,
158
+ texts: List[str],
159
+ model: Optional[str] = None,
160
+ dimensions: Optional[int] = None,
161
+ instructions: Optional[str] = None
162
+ ) -> List[float]:
163
+ response = self.embed(
164
+ texts=texts,
165
+ model=model,
166
+ dimensions=dimensions,
167
+ instructions=instructions
168
+ )
169
+ if response.embedding:
170
+ return response.embedding
171
+ raise APIError("No embedding returned")
172
+
173
+ @observe
174
+ def embed_image(
175
+ self,
176
+ image_url: str,
177
+ model: Optional[str] = None,
178
+ dimensions: Optional[int] = None
179
+ ) -> List[float]:
180
+ response = self.embed(
181
+ image_urls=[image_url],
182
+ model=model,
183
+ dimensions=dimensions
184
+ )
185
+ if response.embedding:
186
+ return response.embedding
187
+ raise APIError("No embedding returned")
188
+
189
+ @observe
190
+ def embed_images(
191
+ self,
192
+ image_urls: List[str],
193
+ model: Optional[str] = None,
194
+ dimensions: Optional[int] = None
195
+ ) -> List[float]:
196
+ response = self.embed(
197
+ image_urls=image_urls,
198
+ model=model,
199
+ dimensions=dimensions
200
+ )
201
+ if response.embedding:
202
+ return response.embedding
203
+ raise APIError("No embedding returned")
204
+
205
+ @observe
206
+ def embed_video(
207
+ self,
208
+ video_url: str,
209
+ model: Optional[str] = None,
210
+ dimensions: Optional[int] = None
211
+ ) -> List[float]:
212
+ response = self.embed(
213
+ video_urls=[video_url],
214
+ model=model,
215
+ dimensions=dimensions
216
+ )
217
+ if response.embedding:
218
+ return response.embedding
219
+ raise APIError("No embedding returned")
220
+
221
+ @observe
222
+ def embed_videos(
223
+ self,
224
+ video_urls: List[str],
225
+ model: Optional[str] = None,
226
+ dimensions: Optional[int] = None
227
+ ) -> List[float]:
228
+ response = self.embed(
229
+ video_urls=video_urls,
230
+ model=model,
231
+ dimensions=dimensions
232
+ )
233
+ if response.embedding:
234
+ return response.embedding
235
+ raise APIError("No embedding returned")
236
+
237
+ @observe
238
+ def embed_multimodal(
239
+ self,
240
+ texts: Optional[List[str]] = None,
241
+ image_urls: Optional[List[str]] = None,
242
+ video_urls: Optional[List[str]] = None,
243
+ model: Optional[str] = None,
244
+ dimensions: Optional[int] = None,
245
+ instructions: Optional[str] = None,
246
+ multi_embedding: bool = False,
247
+ sparse_embedding: bool = False
248
+ ) -> EmbeddingResponse:
249
+ return self.embed(
250
+ texts=texts,
251
+ image_urls=image_urls,
252
+ video_urls=video_urls,
253
+ model=model,
254
+ dimensions=dimensions,
255
+ instructions=instructions,
256
+ multi_embedding=multi_embedding,
257
+ sparse_embedding=sparse_embedding
258
+ )
259
+
260
+ @observe
261
+ def embed_with_multi_vectors(
262
+ self,
263
+ texts: Optional[List[str]] = None,
264
+ image_urls: Optional[List[str]] = None,
265
+ video_urls: Optional[List[str]] = None,
266
+ model: Optional[str] = None,
267
+ dimensions: Optional[int] = None,
268
+ instructions: Optional[str] = None
269
+ ) -> Optional[List[List[float]]]:
270
+ response = self.embed(
271
+ texts=texts,
272
+ image_urls=image_urls,
273
+ video_urls=video_urls,
274
+ model=model,
275
+ dimensions=dimensions,
276
+ instructions=instructions,
277
+ multi_embedding=True
278
+ )
279
+ return response.multi_embeddings
280
+
281
+ @observe
282
+ def embed_with_sparse(
283
+ self,
284
+ texts: Optional[List[str]] = None,
285
+ image_urls: Optional[List[str]] = None,
286
+ video_urls: Optional[List[str]] = None,
287
+ model: Optional[str] = None,
288
+ dimensions: Optional[int] = None,
289
+ instructions: Optional[str] = None
290
+ ) -> Optional[List[SparseEmbeddingItem]]:
291
+ response = self.embed(
292
+ texts=texts,
293
+ image_urls=image_urls,
294
+ video_urls=video_urls,
295
+ model=model,
296
+ dimensions=dimensions,
297
+ instructions=instructions,
298
+ sparse_embedding=True
299
+ )
300
+ return response.sparse_embeddings
301
+
302
+ async def embed_async(
303
+ self,
304
+ texts: Optional[List[str]] = None,
305
+ image_urls: Optional[List[str]] = None,
306
+ video_urls: Optional[List[str]] = None,
307
+ model: Optional[str] = None,
308
+ dimensions: Optional[int] = None,
309
+ encoding_format: Optional[str] = None,
310
+ instructions: Optional[str] = None,
311
+ multi_embedding: bool = False,
312
+ sparse_embedding: bool = False
313
+ ) -> EmbeddingResponse:
314
+ loop = asyncio.get_event_loop()
315
+ return await loop.run_in_executor(
316
+ None,
317
+ lambda: self.embed(
318
+ texts=texts,
319
+ image_urls=image_urls,
320
+ video_urls=video_urls,
321
+ model=model,
322
+ dimensions=dimensions,
323
+ encoding_format=encoding_format,
324
+ instructions=instructions,
325
+ multi_embedding=multi_embedding,
326
+ sparse_embedding=sparse_embedding
327
+ )
328
+ )
329
+
330
+ async def batch_embed(
331
+ self,
332
+ text_batches: List[List[str]],
333
+ model: Optional[str] = None,
334
+ dimensions: Optional[int] = None,
335
+ instructions: Optional[str] = None,
336
+ max_concurrent: int = 5
337
+ ) -> List[EmbeddingResponse]:
338
+ semaphore = asyncio.Semaphore(max_concurrent)
339
+
340
+ async def _embed_with_semaphore(texts: List[str]) -> EmbeddingResponse:
341
+ async with semaphore:
342
+ return await self.embed_async(
343
+ texts=texts,
344
+ model=model,
345
+ dimensions=dimensions,
346
+ instructions=instructions
347
+ )
348
+
349
+ tasks = [_embed_with_semaphore(batch) for batch in text_batches]
350
+ return await asyncio.gather(*tasks, return_exceptions=False)
@@ -0,0 +1,130 @@
1
+ from typing import Optional, List, Literal
2
+ from pydantic import BaseModel, Field
3
+
4
+
5
+ class EmbeddingConfig:
6
+ DEFAULT_MODEL = "doubao-embedding-vision-251215"
7
+ DEFAULT_ENCODING_FORMAT = "float"
8
+ MAX_BATCH_SIZE = 100
9
+
10
+
11
+ class EmbeddingInputImageURL(BaseModel):
12
+ url: str = Field(..., description="Image URL")
13
+
14
+
15
+ class EmbeddingInputVideoURL(BaseModel):
16
+ url: str = Field(..., description="Video URL")
17
+
18
+
19
+ class EmbeddingInputItem(BaseModel):
20
+ type: Literal["text", "image_url", "video_url"] = Field(..., description="Input type")
21
+ text: Optional[str] = Field(default=None, description="Text content for embedding")
22
+ image_url: Optional[EmbeddingInputImageURL] = Field(default=None, description="Image URL for embedding")
23
+ video_url: Optional[EmbeddingInputVideoURL] = Field(default=None, description="Video URL for embedding")
24
+
25
+
26
+ class MultiEmbeddingConfig(BaseModel):
27
+ type: Literal["enabled", "disabled"] = Field(default="disabled", description="Multi embedding mode")
28
+
29
+
30
+ class SparseEmbeddingConfig(BaseModel):
31
+ type: Literal["enabled", "disabled"] = Field(default="disabled", description="Sparse embedding mode")
32
+
33
+
34
+ class SparseEmbeddingItem(BaseModel):
35
+ index: int = Field(..., description="Token index")
36
+ value: float = Field(..., description="Token weight value")
37
+
38
+
39
+ class PromptTokensDetails(BaseModel):
40
+ image_tokens: int = Field(default=0, description="Number of image tokens")
41
+ text_tokens: int = Field(default=0, description="Number of text tokens")
42
+
43
+
44
+ class EmbeddingUsage(BaseModel):
45
+ prompt_tokens: int = Field(default=0, description="Number of prompt tokens")
46
+ prompt_tokens_details: Optional[PromptTokensDetails] = Field(default=None, description="Token details breakdown")
47
+ total_tokens: int = Field(default=0, description="Total number of tokens")
48
+
49
+
50
+ class EmbeddingData(BaseModel):
51
+ object: str = Field(default="embedding", description="Object type")
52
+ embedding: Optional[List[float]] = Field(default=None, description="Embedding vector")
53
+ multi_embedding: Optional[List[List[float]]] = Field(default=None, description="Multi embedding vectors")
54
+ sparse_embedding: Optional[List[SparseEmbeddingItem]] = Field(default=None, description="Sparse embedding")
55
+ index: int = Field(default=0, description="Index of the embedding in the batch")
56
+
57
+
58
+ class EmbeddingRequest(BaseModel):
59
+ model: str = Field(default=EmbeddingConfig.DEFAULT_MODEL, description="Model ID for embedding")
60
+ input: List[EmbeddingInputItem] = Field(..., description="List of inputs to embed")
61
+ dimensions: Optional[int] = Field(default=None, description="Output embedding dimensions")
62
+ encoding_format: Optional[Literal["float", "base64"]] = Field(default=None, description="Encoding format")
63
+ instructions: Optional[str] = Field(default=None, description="Instructions for embedding")
64
+ multi_embedding: Optional[MultiEmbeddingConfig] = Field(default=None, description="Multi embedding config")
65
+ sparse_embedding: Optional[SparseEmbeddingConfig] = Field(default=None, description="Sparse embedding config")
66
+
67
+ def to_api_request(self) -> dict:
68
+ request_data = {
69
+ "model": self.model,
70
+ "input": []
71
+ }
72
+ for item in self.input:
73
+ item_dict = {"type": item.type}
74
+ if item.text is not None:
75
+ item_dict["text"] = item.text
76
+ if item.image_url is not None:
77
+ item_dict["image_url"] = {"url": item.image_url.url}
78
+ if item.video_url is not None:
79
+ item_dict["video_url"] = {"url": item.video_url.url}
80
+ request_data["input"].append(item_dict)
81
+
82
+ if self.dimensions is not None:
83
+ request_data["dimensions"] = self.dimensions
84
+ if self.encoding_format is not None:
85
+ request_data["encoding_format"] = self.encoding_format
86
+ if self.instructions is not None:
87
+ request_data["instructions"] = self.instructions
88
+ if self.multi_embedding is not None:
89
+ request_data["multi_embedding"] = {"type": self.multi_embedding.type}
90
+ if self.sparse_embedding is not None:
91
+ request_data["sparse_embedding"] = {"type": self.sparse_embedding.type}
92
+ return request_data
93
+
94
+
95
+ class EmbeddingResponse(BaseModel):
96
+ object: str = Field(default="list", description="Response object type")
97
+ data: Optional[EmbeddingData] = Field(default=None, description="Embedding result")
98
+ model: str = Field(default="", description="Model used for embedding")
99
+ usage: Optional[EmbeddingUsage] = Field(default=None, description="Token usage information")
100
+ id: Optional[str] = Field(default=None, description="Response ID")
101
+ created: Optional[int] = Field(default=None, description="Creation timestamp")
102
+ error: Optional[dict] = Field(default=None, description="Error information if any")
103
+
104
+ @property
105
+ def success(self) -> bool:
106
+ return self.error is None and self.data is not None
107
+
108
+ @property
109
+ def embedding(self) -> Optional[List[float]]:
110
+ if self.data and self.data.embedding:
111
+ return self.data.embedding
112
+ return None
113
+
114
+ @property
115
+ def multi_embeddings(self) -> Optional[List[List[float]]]:
116
+ if self.data and self.data.multi_embedding:
117
+ return self.data.multi_embedding
118
+ return None
119
+
120
+ @property
121
+ def sparse_embeddings(self) -> Optional[List[SparseEmbeddingItem]]:
122
+ if self.data and self.data.sparse_embedding:
123
+ return self.data.sparse_embedding
124
+ return None
125
+
126
+ @property
127
+ def error_message(self) -> Optional[str]:
128
+ if self.error:
129
+ return self.error.get("message", "Unknown error")
130
+ return None
@@ -0,0 +1,19 @@
1
+ from .client import ImageGenerationClient
2
+ from .models import (
3
+ ImageConfig,
4
+ ImageSize,
5
+ ImageGenerationRequest,
6
+ ImageGenerationResponse,
7
+ ImageData,
8
+ UsageInfo
9
+ )
10
+
11
+ __all__ = [
12
+ "ImageGenerationClient",
13
+ "ImageConfig",
14
+ "ImageSize",
15
+ "ImageGenerationRequest",
16
+ "ImageGenerationResponse",
17
+ "ImageData",
18
+ "UsageInfo",
19
+ ]
@@ -0,0 +1,110 @@
1
+ import asyncio
2
+ from typing import Any, Dict, List, Optional, Union
3
+
4
+ from coze_coding_utils.runtime_ctx.context import Context
5
+ from cozeloop.decorator import observe
6
+
7
+ from ..core.client import BaseClient
8
+ from ..core.config import Config
9
+ from ..core.exceptions import APIError
10
+ from .models import ImageConfig, ImageGenerationRequest, ImageGenerationResponse
11
+
12
+
13
+ class ImageGenerationClient(BaseClient):
14
+ def __init__(
15
+ self,
16
+ config: Optional[Config] = None,
17
+ ctx: Optional[Context] = None,
18
+ custom_headers: Optional[Dict[str, str]] = None,
19
+ verbose: bool = False,
20
+ ):
21
+ super().__init__(config, ctx, custom_headers, verbose)
22
+ self.base_url = self.config.base_url
23
+ self.model = ImageConfig.DEFAULT_MODEL
24
+
25
+ def extract_urls(self, response: ImageGenerationResponse) -> List[str]:
26
+ urls = []
27
+ for item in response.data:
28
+ if item.error:
29
+ raise APIError(
30
+ f"图片生成失败: {item.error.get('message', 'Unknown error')}",
31
+ code=item.error.get("code"),
32
+ )
33
+ if item.url:
34
+ urls.append(item.url)
35
+ elif item.b64_json:
36
+ urls.append(f"data:image/png;base64,{item.b64_json}")
37
+ return urls
38
+
39
+ @observe
40
+ def generate(
41
+ self,
42
+ prompt: str,
43
+ size: Optional[str] = None,
44
+ watermark: Optional[bool] = None,
45
+ image: Optional[Union[str, List[str]]] = None,
46
+ response_format: Optional[str] = None,
47
+ optimize_prompt_mode: Optional[str] = None,
48
+ sequential_image_generation: Optional[str] = None,
49
+ sequential_image_generation_max_images: Optional[int] = None,
50
+ ) -> ImageGenerationResponse:
51
+ request_params = {"prompt": prompt}
52
+ if size is not None:
53
+ request_params["size"] = size
54
+ if watermark is not None:
55
+ request_params["watermark"] = watermark
56
+ if image is not None:
57
+ request_params["image"] = image
58
+ if response_format is not None:
59
+ request_params["response_format"] = response_format
60
+ if optimize_prompt_mode is not None:
61
+ request_params["optimize_prompt_mode"] = optimize_prompt_mode
62
+ if sequential_image_generation is not None:
63
+ request_params["sequential_image_generation"] = sequential_image_generation
64
+ if sequential_image_generation_max_images is not None:
65
+ request_params["sequential_image_generation_max_images"] = (
66
+ sequential_image_generation_max_images
67
+ )
68
+
69
+ request = ImageGenerationRequest(**request_params)
70
+
71
+ data = self._request(
72
+ method="POST",
73
+ url=f"{self.base_url}/api/v3/images/generations",
74
+ json=request.to_api_request(self.model),
75
+ )
76
+
77
+ if "error" in data and data["error"]:
78
+ raise APIError(
79
+ f"API 返回错误: {data['error'].get('message', 'Unknown error')}",
80
+ code=data["error"].get("code"),
81
+ )
82
+
83
+ parsed_response = ImageGenerationResponse(**data)
84
+
85
+ return parsed_response
86
+
87
+ async def generate_async(
88
+ self,
89
+ prompt: str,
90
+ size: Optional[str] = None,
91
+ watermark: Optional[bool] = None,
92
+ image: Optional[Union[str, List[str]]] = None,
93
+ response_format: Optional[str] = None,
94
+ optimize_prompt_mode: Optional[str] = None,
95
+ sequential_image_generation: Optional[str] = None,
96
+ sequential_image_generation_max_images: Optional[int] = None,
97
+ ) -> ImageGenerationResponse:
98
+ loop = asyncio.get_event_loop()
99
+ return await loop.run_in_executor(
100
+ None,
101
+ self.generate,
102
+ prompt,
103
+ size,
104
+ watermark,
105
+ image,
106
+ response_format,
107
+ optimize_prompt_mode,
108
+ sequential_image_generation,
109
+ sequential_image_generation_max_images,
110
+ )