tamar-model-client 0.2.3__tar.gz → 0.2.5__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.
Files changed (40) hide show
  1. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/PKG-INFO +106 -3
  2. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/README.md +105 -2
  3. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/setup.py +1 -1
  4. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/request_builder.py +25 -22
  5. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/enums/invoke.py +1 -0
  6. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/schemas/inputs.py +64 -28
  7. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/utils.py +7 -1
  8. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client.egg-info/PKG-INFO +106 -3
  9. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tests/test_google_azure_final.py +325 -57
  10. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/setup.cfg +0 -0
  11. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/__init__.py +0 -0
  12. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/async_client.py +0 -0
  13. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/auth.py +0 -0
  14. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/circuit_breaker.py +0 -0
  15. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/__init__.py +0 -0
  16. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/base_client.py +0 -0
  17. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/http_fallback.py +0 -0
  18. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/logging_setup.py +0 -0
  19. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/request_id_manager.py +0 -0
  20. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/response_handler.py +0 -0
  21. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/core/utils.py +0 -0
  22. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/enums/__init__.py +0 -0
  23. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/enums/channel.py +0 -0
  24. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/enums/providers.py +0 -0
  25. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/error_handler.py +0 -0
  26. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/exceptions.py +0 -0
  27. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/generated/__init__.py +0 -0
  28. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/generated/model_service_pb2.py +0 -0
  29. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/generated/model_service_pb2_grpc.py +0 -0
  30. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/json_formatter.py +0 -0
  31. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/logging_icons.py +0 -0
  32. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/schemas/__init__.py +0 -0
  33. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/schemas/outputs.py +0 -0
  34. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client/sync_client.py +0 -0
  35. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client.egg-info/SOURCES.txt +0 -0
  36. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client.egg-info/dependency_links.txt +0 -0
  37. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client.egg-info/requires.txt +0 -0
  38. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tamar_model_client.egg-info/top_level.txt +0 -0
  39. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tests/__init__.py +0 -0
  40. {tamar_model_client-0.2.3 → tamar_model_client-0.2.5}/tests/test_circuit_breaker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tamar-model-client
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: A Python SDK for interacting with the Model Manager gRPC service
5
5
  Home-page: http://gitlab.tamaredge.top/project-tap/AgentOS/model-manager-client
6
6
  Author: Oscar Ou
@@ -54,7 +54,7 @@ Dynamic: summary
54
54
 
55
55
  ### 🔌 多服务商支持
56
56
  - **OpenAI** (GPT-3.5/4, DALL-E)
57
- - **Google** (Gemini - AI Studio & Vertex AI)
57
+ - **Google** (Gemini - AI Studio & Vertex AI, Imagen 图像生成)
58
58
  - **Azure OpenAI** (企业级部署)
59
59
  - **Anthropic** (Claude)
60
60
  - **DeepSeek** (深度求索)
@@ -114,7 +114,7 @@ tamar_model_client/
114
114
  │ └── outputs.py # 响应模型(ModelResponse, Usage)
115
115
  ├── 📁 enums/ # 枚举定义
116
116
  │ ├── providers.py # AI 服务商(OpenAI, Google, Azure...)
117
- │ ├── invoke.py # 调用类型(generation, images...)
117
+ │ ├── invoke.py # 调用类型(generation, images, image-generation-genai...)
118
118
  │ └── channel.py # 服务通道(openai, vertexai...)
119
119
  ├── 📁 core/ # 核心功能模块
120
120
  │ ├── base_client.py # 客户端基类(熔断、降级、配置)
@@ -288,6 +288,37 @@ else:
288
288
  print(f"响应: {vertex_response.content}")
289
289
  if vertex_response.usage:
290
290
  print(f"Token 使用情况: {vertex_response.usage}")
291
+
292
+ # Google GenAI 图像生成示例
293
+ from google.genai import types
294
+
295
+ genai_image_request = ModelRequest(
296
+ provider=ProviderType.GOOGLE, # 选择 Google 作为提供商
297
+ channel=Channel.AI_STUDIO, # 使用 AI Studio 渠道
298
+ invoke_type=InvokeType.IMAGE_GENERATION_GENAI, # 使用 GenAI 图像生成调用类型
299
+ model="imagen-3.0-generate-001", # 指定图像生成模型
300
+ prompt="一只可爱的小猫在花园里玩耍", # 图像描述提示词
301
+ user_context=UserContext(
302
+ user_id="test_user",
303
+ org_id="test_org",
304
+ client_type="python-sdk"
305
+ ),
306
+ # 使用 Google GenAI 类型构建配置
307
+ config=types.GenerateImagesConfig(
308
+ number_of_images=1,
309
+ aspect_ratio="1:1",
310
+ safety_filter_level="block_some"
311
+ )
312
+ )
313
+
314
+ # 发送图像生成请求并获取响应
315
+ image_response = client.invoke(genai_image_request)
316
+ if image_response.error:
317
+ print(f"错误: {image_response.error}")
318
+ else:
319
+ print(f"图像生成成功: {image_response.content}")
320
+ if image_response.usage:
321
+ print(f"使用情况: {image_response.usage}")
291
322
  ```
292
323
 
293
324
  ### Azure OpenAI 调用示例
@@ -488,6 +519,78 @@ async def batch_example():
488
519
  asyncio.run(batch_example())
489
520
  ```
490
521
 
522
+ ### 图像生成调用示例
523
+
524
+ 支持 OpenAI DALL-E、Google Vertex AI 和 Google GenAI 图像生成:
525
+
526
+ ```python
527
+ from tamar_model_client import TamarModelClient
528
+ from tamar_model_client.schemas import ModelRequest, UserContext
529
+ from tamar_model_client.enums import ProviderType, InvokeType, Channel
530
+
531
+ client = TamarModelClient()
532
+
533
+ # OpenAI DALL-E 图像生成
534
+ openai_image_request = ModelRequest(
535
+ provider=ProviderType.OPENAI,
536
+ channel=Channel.OPENAI,
537
+ invoke_type=InvokeType.IMAGE_GENERATION,
538
+ model="dall-e-3",
539
+ prompt="一只穿着西装的猫在办公室里工作",
540
+ user_context=UserContext(
541
+ user_id="test_user",
542
+ org_id="test_org",
543
+ client_type="python-sdk"
544
+ ),
545
+ size="1024x1024",
546
+ quality="hd",
547
+ n=1
548
+ )
549
+
550
+ # Google Vertex AI 图像生成
551
+ vertex_image_request = ModelRequest(
552
+ provider=ProviderType.GOOGLE,
553
+ channel=Channel.VERTEXAI,
554
+ invoke_type=InvokeType.IMAGE_GENERATION,
555
+ model="imagegeneration@006",
556
+ prompt="一座美丽的山峰在日出时分",
557
+ user_context=UserContext(
558
+ user_id="test_user",
559
+ org_id="test_org",
560
+ client_type="python-sdk"
561
+ ),
562
+ number_of_images=1,
563
+ aspect_ratio="1:1",
564
+ safety_filter_level="block_some"
565
+ )
566
+
567
+ # Google GenAI 图像生成(新增功能)
568
+ genai_image_request = ModelRequest(
569
+ provider=ProviderType.GOOGLE,
570
+ channel=Channel.AI_STUDIO,
571
+ invoke_type=InvokeType.IMAGE_GENERATION_GENAI, # 新增的调用类型
572
+ model="imagen-3.0-generate-001",
573
+ prompt="科幻风格的城市夜景,霓虹灯闪烁",
574
+ user_context=UserContext(
575
+ user_id="test_user",
576
+ org_id="test_org",
577
+ client_type="python-sdk"
578
+ ),
579
+ config=types.GenerateImagesConfig(
580
+ number_of_images=1,
581
+ aspect_ratio="16:9"
582
+ )
583
+ )
584
+
585
+ # 发送请求
586
+ for request in [openai_image_request, vertex_image_request, genai_image_request]:
587
+ response = client.invoke(request)
588
+ if response.error:
589
+ print(f"图像生成失败: {response.error}")
590
+ else:
591
+ print(f"图像生成成功: {response.content}")
592
+ ```
593
+
491
594
  ### 文件输入示例
492
595
 
493
596
  支持处理图像等文件输入(需使用支持多模态的模型,如 gemini-2.0-flash):
@@ -22,7 +22,7 @@
22
22
 
23
23
  ### 🔌 多服务商支持
24
24
  - **OpenAI** (GPT-3.5/4, DALL-E)
25
- - **Google** (Gemini - AI Studio & Vertex AI)
25
+ - **Google** (Gemini - AI Studio & Vertex AI, Imagen 图像生成)
26
26
  - **Azure OpenAI** (企业级部署)
27
27
  - **Anthropic** (Claude)
28
28
  - **DeepSeek** (深度求索)
@@ -82,7 +82,7 @@ tamar_model_client/
82
82
  │ └── outputs.py # 响应模型(ModelResponse, Usage)
83
83
  ├── 📁 enums/ # 枚举定义
84
84
  │ ├── providers.py # AI 服务商(OpenAI, Google, Azure...)
85
- │ ├── invoke.py # 调用类型(generation, images...)
85
+ │ ├── invoke.py # 调用类型(generation, images, image-generation-genai...)
86
86
  │ └── channel.py # 服务通道(openai, vertexai...)
87
87
  ├── 📁 core/ # 核心功能模块
88
88
  │ ├── base_client.py # 客户端基类(熔断、降级、配置)
@@ -256,6 +256,37 @@ else:
256
256
  print(f"响应: {vertex_response.content}")
257
257
  if vertex_response.usage:
258
258
  print(f"Token 使用情况: {vertex_response.usage}")
259
+
260
+ # Google GenAI 图像生成示例
261
+ from google.genai import types
262
+
263
+ genai_image_request = ModelRequest(
264
+ provider=ProviderType.GOOGLE, # 选择 Google 作为提供商
265
+ channel=Channel.AI_STUDIO, # 使用 AI Studio 渠道
266
+ invoke_type=InvokeType.IMAGE_GENERATION_GENAI, # 使用 GenAI 图像生成调用类型
267
+ model="imagen-3.0-generate-001", # 指定图像生成模型
268
+ prompt="一只可爱的小猫在花园里玩耍", # 图像描述提示词
269
+ user_context=UserContext(
270
+ user_id="test_user",
271
+ org_id="test_org",
272
+ client_type="python-sdk"
273
+ ),
274
+ # 使用 Google GenAI 类型构建配置
275
+ config=types.GenerateImagesConfig(
276
+ number_of_images=1,
277
+ aspect_ratio="1:1",
278
+ safety_filter_level="block_some"
279
+ )
280
+ )
281
+
282
+ # 发送图像生成请求并获取响应
283
+ image_response = client.invoke(genai_image_request)
284
+ if image_response.error:
285
+ print(f"错误: {image_response.error}")
286
+ else:
287
+ print(f"图像生成成功: {image_response.content}")
288
+ if image_response.usage:
289
+ print(f"使用情况: {image_response.usage}")
259
290
  ```
260
291
 
261
292
  ### Azure OpenAI 调用示例
@@ -456,6 +487,78 @@ async def batch_example():
456
487
  asyncio.run(batch_example())
457
488
  ```
458
489
 
490
+ ### 图像生成调用示例
491
+
492
+ 支持 OpenAI DALL-E、Google Vertex AI 和 Google GenAI 图像生成:
493
+
494
+ ```python
495
+ from tamar_model_client import TamarModelClient
496
+ from tamar_model_client.schemas import ModelRequest, UserContext
497
+ from tamar_model_client.enums import ProviderType, InvokeType, Channel
498
+
499
+ client = TamarModelClient()
500
+
501
+ # OpenAI DALL-E 图像生成
502
+ openai_image_request = ModelRequest(
503
+ provider=ProviderType.OPENAI,
504
+ channel=Channel.OPENAI,
505
+ invoke_type=InvokeType.IMAGE_GENERATION,
506
+ model="dall-e-3",
507
+ prompt="一只穿着西装的猫在办公室里工作",
508
+ user_context=UserContext(
509
+ user_id="test_user",
510
+ org_id="test_org",
511
+ client_type="python-sdk"
512
+ ),
513
+ size="1024x1024",
514
+ quality="hd",
515
+ n=1
516
+ )
517
+
518
+ # Google Vertex AI 图像生成
519
+ vertex_image_request = ModelRequest(
520
+ provider=ProviderType.GOOGLE,
521
+ channel=Channel.VERTEXAI,
522
+ invoke_type=InvokeType.IMAGE_GENERATION,
523
+ model="imagegeneration@006",
524
+ prompt="一座美丽的山峰在日出时分",
525
+ user_context=UserContext(
526
+ user_id="test_user",
527
+ org_id="test_org",
528
+ client_type="python-sdk"
529
+ ),
530
+ number_of_images=1,
531
+ aspect_ratio="1:1",
532
+ safety_filter_level="block_some"
533
+ )
534
+
535
+ # Google GenAI 图像生成(新增功能)
536
+ genai_image_request = ModelRequest(
537
+ provider=ProviderType.GOOGLE,
538
+ channel=Channel.AI_STUDIO,
539
+ invoke_type=InvokeType.IMAGE_GENERATION_GENAI, # 新增的调用类型
540
+ model="imagen-3.0-generate-001",
541
+ prompt="科幻风格的城市夜景,霓虹灯闪烁",
542
+ user_context=UserContext(
543
+ user_id="test_user",
544
+ org_id="test_org",
545
+ client_type="python-sdk"
546
+ ),
547
+ config=types.GenerateImagesConfig(
548
+ number_of_images=1,
549
+ aspect_ratio="16:9"
550
+ )
551
+ )
552
+
553
+ # 发送请求
554
+ for request in [openai_image_request, vertex_image_request, genai_image_request]:
555
+ response = client.invoke(request)
556
+ if response.error:
557
+ print(f"图像生成失败: {response.error}")
558
+ else:
559
+ print(f"图像生成成功: {response.content}")
560
+ ```
561
+
459
562
  ### 文件输入示例
460
563
 
461
564
  支持处理图像等文件输入(需使用支持多模态的模型,如 gemini-2.0-flash):
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="tamar-model-client",
5
- version="0.2.3",
5
+ version="0.2.5",
6
6
  description="A Python SDK for interacting with the Model Manager gRPC service",
7
7
  author="Oscar Ou",
8
8
  author_email="oscar.ou@tamaredge.ai",
@@ -17,6 +17,7 @@ from ..schemas.inputs import (
17
17
  UserContext,
18
18
  GoogleGenAiInput,
19
19
  GoogleVertexAIImagesInput,
20
+ GoogleGenAIImagesInput,
20
21
  OpenAIResponsesInput,
21
22
  OpenAIChatCompletionsInput,
22
23
  OpenAIImagesInput,
@@ -32,7 +33,7 @@ class RequestBuilder:
32
33
  负责将高级的 ModelRequest 对象转换为 gRPC 协议所需的请求对象,
33
34
  包括参数验证、序列化和提供商特定的字段处理。
34
35
  """
35
-
36
+
36
37
  @staticmethod
37
38
  def get_allowed_fields(provider: ProviderType, invoke_type: InvokeType) -> Set[str]:
38
39
  """
@@ -61,11 +62,13 @@ class RequestBuilder:
61
62
  return set(OpenAIImagesInput.model_fields.keys())
62
63
  case ((ProviderType.OPENAI | ProviderType.AZURE), InvokeType.IMAGE_EDIT_GENERATION):
63
64
  return set(OpenAIImagesEditInput.model_fields.keys())
65
+ case (ProviderType.GOOGLE, InvokeType.IMAGE_GENERATION_GENAI):
66
+ return set(GoogleGenAIImagesInput.model_fields.keys())
64
67
  case _:
65
68
  raise ValueError(
66
69
  f"Unsupported provider/invoke_type combination: {provider} + {invoke_type}"
67
70
  )
68
-
71
+
69
72
  @staticmethod
70
73
  def build_grpc_extra_fields(model_request: ModelRequest) -> Dict[str, Any]:
71
74
  """
@@ -88,31 +91,31 @@ class RequestBuilder:
88
91
  model_request.provider,
89
92
  model_request.invoke_type
90
93
  )
91
-
94
+
92
95
  # 将 ModelRequest 转换为字典,只包含已设置的字段
93
96
  model_request_dict = model_request.model_dump(exclude_unset=True)
94
-
97
+
95
98
  # 构建 gRPC 请求参数
96
99
  grpc_request_kwargs = {}
97
100
  for field in allowed_fields:
98
101
  if field in model_request_dict:
99
102
  value = model_request_dict[field]
100
-
103
+
101
104
  # 跳过无效的值
102
105
  if not is_effective_value(value):
103
106
  continue
104
-
107
+
105
108
  # 序列化不支持的类型
106
109
  grpc_request_kwargs[field] = serialize_value(value)
107
-
110
+
108
111
  # 清理序列化后的参数中的 None 值
109
112
  grpc_request_kwargs = remove_none_from_dict(grpc_request_kwargs)
110
-
113
+
111
114
  return grpc_request_kwargs
112
-
115
+
113
116
  except Exception as e:
114
117
  raise ValueError(f"构建请求失败: {str(e)}") from e
115
-
118
+
116
119
  @staticmethod
117
120
  def build_single_request(model_request: ModelRequest) -> model_service_pb2.ModelRequestItem:
118
121
  """
@@ -129,7 +132,7 @@ class RequestBuilder:
129
132
  """
130
133
  # 构建额外字段
131
134
  extra_fields = RequestBuilder.build_grpc_extra_fields(model_request)
132
-
135
+
133
136
  # 创建 gRPC 请求对象
134
137
  return model_service_pb2.ModelRequestItem(
135
138
  provider=model_request.provider.value,
@@ -141,11 +144,11 @@ class RequestBuilder:
141
144
  client_type=model_request.user_context.client_type or "",
142
145
  extra=extra_fields
143
146
  )
144
-
147
+
145
148
  @staticmethod
146
149
  def build_batch_request_item(
147
- batch_item: "BatchModelRequestItem",
148
- user_context: "UserContext"
150
+ batch_item: "BatchModelRequestItem",
151
+ user_context: "UserContext"
149
152
  ) -> model_service_pb2.ModelRequestItem:
150
153
  """
151
154
  构建批量请求中的单个项目
@@ -159,7 +162,7 @@ class RequestBuilder:
159
162
  """
160
163
  # 构建额外字段
161
164
  extra_fields = RequestBuilder.build_grpc_extra_fields(batch_item)
162
-
165
+
163
166
  # 添加 custom_id 如果存在
164
167
  if hasattr(batch_item, 'custom_id') and batch_item.custom_id:
165
168
  request_item = model_service_pb2.ModelRequestItem(
@@ -184,13 +187,13 @@ class RequestBuilder:
184
187
  client_type=user_context.client_type or "",
185
188
  extra=extra_fields
186
189
  )
187
-
190
+
188
191
  # 添加 priority 如果存在
189
192
  if hasattr(batch_item, 'priority') and batch_item.priority is not None:
190
193
  request_item.priority = batch_item.priority
191
-
194
+
192
195
  return request_item
193
-
196
+
194
197
  @staticmethod
195
198
  def build_batch_request(batch_request: BatchModelRequest) -> model_service_pb2.ModelRequest:
196
199
  """
@@ -206,16 +209,16 @@ class RequestBuilder:
206
209
  ValueError: 当构建请求失败时
207
210
  """
208
211
  items = []
209
-
212
+
210
213
  for batch_item in batch_request.items:
211
214
  # 为每个请求项构建 gRPC 对象,传入 user_context
212
215
  request_item = RequestBuilder.build_batch_request_item(
213
- batch_item,
216
+ batch_item,
214
217
  batch_request.user_context
215
218
  )
216
219
  items.append(request_item)
217
-
220
+
218
221
  # 创建批量请求对象
219
222
  return model_service_pb2.ModelRequest(
220
223
  items=items
221
- )
224
+ )
@@ -9,3 +9,4 @@ class InvokeType(str, Enum):
9
9
  GENERATION = "generation" # 生成类,默认的值
10
10
  IMAGE_GENERATION = "image-generation"
11
11
  IMAGE_EDIT_GENERATION = "image-edit-generation"
12
+ IMAGE_GENERATION_GENAI = "image-generation-genai" # GenAI SDK图像生成
@@ -11,8 +11,9 @@ from openai.types.chat import ChatCompletionMessageParam, ChatCompletionAudioPar
11
11
  ChatCompletionToolParam
12
12
  from openai.types.responses import ResponseInputParam, ResponseIncludable, ResponseTextConfigParam, \
13
13
  response_create_params, ToolParam
14
+ from openai.types.responses.response_prompt_param import ResponsePromptParam
14
15
  from pydantic import BaseModel, model_validator, field_validator
15
- from typing import List, Optional, Union, Iterable, Dict, Literal, IO
16
+ from typing import List, Optional, Union, Iterable, Dict, Literal
16
17
 
17
18
  from tamar_model_client.enums import ProviderType, InvokeType
18
19
  from tamar_model_client.enums.channel import Channel
@@ -58,25 +59,44 @@ class GoogleVertexAIImagesInput(BaseModel):
58
59
  }
59
60
 
60
61
 
62
+ class GoogleGenAIImagesInput(BaseModel):
63
+ model: str
64
+ prompt: str
65
+ config: Optional[types.GenerateImagesConfigOrDict] = None
66
+
67
+ model_config = {
68
+ "arbitrary_types_allowed": True
69
+ }
70
+
71
+
61
72
  class OpenAIResponsesInput(BaseModel):
62
- input: Union[str, ResponseInputParam]
63
- model: ResponsesModel
73
+ background: Optional[bool] | NotGiven = NOT_GIVEN
64
74
  include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN
75
+ input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN
65
76
  instructions: Optional[str] | NotGiven = NOT_GIVEN
66
77
  max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN
78
+ max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN
67
79
  metadata: Optional[Metadata] | NotGiven = NOT_GIVEN
80
+ model: ResponsesModel | NotGiven = NOT_GIVEN
68
81
  parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN
69
82
  previous_response_id: Optional[str] | NotGiven = NOT_GIVEN
83
+ prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN
84
+ prompt_cache_key: str | NotGiven = NOT_GIVEN
70
85
  reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN
86
+ safety_identifier: str | NotGiven = NOT_GIVEN
87
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN
71
88
  store: Optional[bool] | NotGiven = NOT_GIVEN
72
89
  stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN
90
+ stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN
73
91
  temperature: Optional[float] | NotGiven = NOT_GIVEN
74
92
  text: ResponseTextConfigParam | NotGiven = NOT_GIVEN
75
93
  tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN
76
94
  tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN
95
+ top_logprobs: Optional[int] | NotGiven = NOT_GIVEN
77
96
  top_p: Optional[float] | NotGiven = NOT_GIVEN
78
97
  truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN
79
98
  user: str | NotGiven = NOT_GIVEN
99
+ verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN
80
100
  extra_headers: Headers | None = None
81
101
  extra_query: Query | None = None
82
102
  extra_body: Body | None = None
@@ -104,10 +124,12 @@ class OpenAIChatCompletionsInput(BaseModel):
104
124
  parallel_tool_calls: bool | NotGiven = NOT_GIVEN
105
125
  prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN
106
126
  presence_penalty: Optional[float] | NotGiven = NOT_GIVEN
127
+ prompt_cache_key: str | NotGiven = NOT_GIVEN
107
128
  reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN
108
129
  response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN
130
+ safety_identifier: str | NotGiven = NOT_GIVEN
109
131
  seed: Optional[int] | NotGiven = NOT_GIVEN
110
- service_tier: Optional[Literal["auto", "default"]] | NotGiven = NOT_GIVEN
132
+ service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN
111
133
  stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN
112
134
  store: Optional[bool] | NotGiven = NOT_GIVEN
113
135
  stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN
@@ -118,6 +140,7 @@ class OpenAIChatCompletionsInput(BaseModel):
118
140
  top_logprobs: Optional[int] | NotGiven = NOT_GIVEN
119
141
  top_p: Optional[float] | NotGiven = NOT_GIVEN
120
142
  user: str | NotGiven = NOT_GIVEN
143
+ verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN
121
144
  web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN
122
145
  extra_headers: Headers | None = None
123
146
  extra_query: Query | None = None
@@ -166,7 +189,7 @@ class OpenAIImagesEditInput(BaseModel):
166
189
  output_compression: Optional[int] | NotGiven = NOT_GIVEN
167
190
  output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN
168
191
  partial_images: Optional[int] | NotGiven = NOT_GIVEN
169
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN
192
+ quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN
170
193
  response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN
171
194
  size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | NotGiven = NOT_GIVEN
172
195
  stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN
@@ -191,33 +214,44 @@ class ModelRequestInput(BaseRequest):
191
214
  # 合并 model 字段
192
215
  model: Optional[Union[str, ResponsesModel, ChatModel, ImageModel]] = None
193
216
 
194
- # OpenAI Responses Input
217
+ # OpenAI Responses Input(合并)
195
218
  input: Optional[Union[str, ResponseInputParam]] = None
196
219
  include: Optional[Union[List[ResponseIncludable], NotGiven]] = NOT_GIVEN
197
220
  instructions: Optional[Union[str, NotGiven]] = NOT_GIVEN
198
221
  max_output_tokens: Optional[Union[int, NotGiven]] = NOT_GIVEN
222
+ max_tool_calls: Optional[Union[int, NotGiven]] = NOT_GIVEN
199
223
  metadata: Optional[Union[Metadata, NotGiven]] = NOT_GIVEN
200
224
  parallel_tool_calls: Optional[Union[bool, NotGiven]] = NOT_GIVEN
201
225
  previous_response_id: Optional[Union[str, NotGiven]] = NOT_GIVEN
226
+ # prompt 同名字段合并:Responses 的 ResponsePromptParam + 图片生成的 str
227
+ prompt: Optional[Union[str, ResponsePromptParam, NotGiven]] = NOT_GIVEN
228
+ prompt_cache_key: Optional[Union[str, NotGiven]] = NOT_GIVEN
202
229
  reasoning: Optional[Union[Reasoning, NotGiven]] = NOT_GIVEN
230
+ safety_identifier: Optional[Union[str, NotGiven]] = NOT_GIVEN
231
+ service_tier: Optional[Union[Literal["auto", "default", "flex", "scale", "priority"], NotGiven]] = NOT_GIVEN
203
232
  store: Optional[Union[bool, NotGiven]] = NOT_GIVEN
204
233
  stream: Optional[Union[Literal[False], Literal[True], NotGiven]] = NOT_GIVEN
234
+ # 合并两套 stream_options 类型
235
+ stream_options: Optional[
236
+ Union[response_create_params.StreamOptions, ChatCompletionStreamOptionsParam, NotGiven]] = NOT_GIVEN
205
237
  temperature: Optional[Union[float, NotGiven]] = NOT_GIVEN
206
238
  text: Optional[Union[ResponseTextConfigParam, NotGiven]] = NOT_GIVEN
207
239
  tool_choice: Optional[
208
240
  Union[response_create_params.ToolChoice, ChatCompletionToolChoiceOptionParam, NotGiven]
209
241
  ] = NOT_GIVEN
210
242
  tools: Optional[Union[Iterable[ToolParam], Iterable[ChatCompletionToolParam], NotGiven]] = NOT_GIVEN
243
+ top_logprobs: Optional[Union[int, NotGiven]] = NOT_GIVEN
211
244
  top_p: Optional[Union[float, NotGiven]] = NOT_GIVEN
212
245
  truncation: Optional[Union[Literal["auto", "disabled"], NotGiven]] = NOT_GIVEN
213
246
  user: Optional[Union[str, NotGiven]] = NOT_GIVEN
247
+ verbosity: Optional[Union[Literal["low", "medium", "high"], NotGiven]] = NOT_GIVEN
214
248
 
215
249
  extra_headers: Optional[Union[Headers, None]] = None
216
250
  extra_query: Optional[Union[Query, None]] = None
217
251
  extra_body: Optional[Union[Body, None]] = None
218
252
  timeout: Optional[Union[float, httpx.Timeout, None, NotGiven]] = NOT_GIVEN
219
253
 
220
- # OpenAI Chat Completions Input
254
+ # OpenAI Chat Completions Input(合并)
221
255
  messages: Optional[Iterable[ChatCompletionMessageParam]] = None
222
256
  audio: Optional[Union[ChatCompletionAudioParam, NotGiven]] = NOT_GIVEN
223
257
  frequency_penalty: Optional[Union[float, NotGiven]] = NOT_GIVEN
@@ -226,34 +260,32 @@ class ModelRequestInput(BaseRequest):
226
260
  logit_bias: Optional[Union[Dict[str, int], NotGiven]] = NOT_GIVEN
227
261
  logprobs: Optional[Union[bool, NotGiven]] = NOT_GIVEN
228
262
  max_completion_tokens: Optional[Union[int, NotGiven]] = NOT_GIVEN
263
+ max_tokens: Optional[Union[int, NotGiven]] = NOT_GIVEN
229
264
  modalities: Optional[Union[List[Literal["text", "audio"]], NotGiven]] = NOT_GIVEN
230
- n: Optional[Union[int, NotGiven]] = NOT_GIVEN
265
+ n: Optional[Union[int, NotGiven]] = NOT_GIVEN # 复用给 Chat 和 Images
231
266
  prediction: Optional[Union[ChatCompletionPredictionContentParam, NotGiven]] = NOT_GIVEN
232
267
  presence_penalty: Optional[Union[float, NotGiven]] = NOT_GIVEN
233
268
  reasoning_effort: Optional[Union[ReasoningEffort, NotGiven]] = NOT_GIVEN
234
269
  response_format: Optional[
235
- Union[Literal["url", "b64_json"], completion_create_params.ResponseFormat, NotGiven]] = NOT_GIVEN
236
- seed: Optional[Union[int, NotGiven]] = NOT_GIVEN
237
- service_tier: Optional[Union[Literal["auto", "default"], NotGiven]] = NOT_GIVEN
238
- stop: Optional[Union[Optional[str], List[str], None, NotGiven]] = NOT_GIVEN
239
- top_logprobs: Optional[Union[int, NotGiven]] = NOT_GIVEN
270
+ Union[completion_create_params.ResponseFormat, Literal["url", "b64_json"], NotGiven]
271
+ ] = NOT_GIVEN
272
+ seed: Optional[Union[int, NotGiven]] = NOT_GIVEN # Chat/Vertex Images 共用
240
273
  web_search_options: Optional[Union[completion_create_params.WebSearchOptions, NotGiven]] = NOT_GIVEN
241
- stream_options: Optional[Union[ChatCompletionStreamOptionsParam, NotGiven]] = NOT_GIVEN
242
274
 
243
275
  # Google GenAI Input
244
276
  contents: Optional[Union[types.ContentListUnion, types.ContentListUnionDict]] = None
245
- config: Optional[types.GenerateContentConfigOrDict] = None
277
+ config: Optional[Union[types.GenerateContentConfigOrDict, types.GenerateImagesConfigOrDict]] = None
246
278
 
247
- # OpenAIImagesInput + OpenAIImagesEditInput + GoogleVertexAIImagesInput 合并字段
279
+ # Images(OpenAI Images / Images Edit / Google Vertex Images 合并)
248
280
  image: Optional[Union[FileTypes, List[FileTypes]]] = None
249
- prompt: Optional[str] = None
250
- background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN
251
- moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN
252
- input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN
253
- output_compression: Optional[int] | NotGiven = NOT_GIVEN
254
- output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN
255
- partial_images: Optional[int] | NotGiven = NOT_GIVEN
256
- mask: FileTypes | NotGiven = NOT_GIVEN
281
+ # background 同名字段合并:Responses 的 bool(后台任务)+ Images 的透明度枚举
282
+ background: Optional[Union[bool, Literal["transparent", "opaque", "auto"], NotGiven]] = NOT_GIVEN
283
+ moderation: Optional[Union[Literal["low", "auto"], NotGiven]] = NOT_GIVEN
284
+ input_fidelity: Optional[Union[Literal["high", "low"], NotGiven]] = NOT_GIVEN
285
+ output_compression: Optional[Union[int, NotGiven]] = NOT_GIVEN
286
+ output_format: Optional[Union[Literal["png", "jpeg", "webp"], NotGiven]] = NOT_GIVEN
287
+ partial_images: Optional[Union[int, NotGiven]] = NOT_GIVEN
288
+ mask: Union[FileTypes, NotGiven] = NOT_GIVEN
257
289
  negative_prompt: Optional[str] = None
258
290
  aspect_ratio: Optional[Literal["1:1", "9:16", "16:9", "4:3", "3:4"]] = None
259
291
  guidance_scale: Optional[float] = None
@@ -262,10 +294,14 @@ class ModelRequestInput(BaseRequest):
262
294
  add_watermark: Optional[bool] = None
263
295
  safety_filter_level: Optional[Literal["block_most", "block_some", "block_few", "block_fewest"]] = None
264
296
  person_generation: Optional[Literal["dont_allow", "allow_adult", "allow_all"]] = None
265
- quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN
266
- size: Optional[Literal[
267
- "auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN
268
- style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN
297
+ quality: Optional[Union[Literal["standard", "hd", "low", "medium", "high", "auto"], NotGiven]] = NOT_GIVEN
298
+ size: Optional[
299
+ Union[
300
+ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"],
301
+ NotGiven,
302
+ ]
303
+ ] = NOT_GIVEN
304
+ style: Optional[Union[Literal["vivid", "natural"], NotGiven]] = NOT_GIVEN
269
305
  number_of_images: Optional[int] = None # Google 用法
270
306
 
271
307
  model_config = {
@@ -3,6 +3,7 @@ from pydantic import BaseModel
3
3
  from typing import Any
4
4
  import os, mimetypes
5
5
 
6
+
6
7
  def convert_file_field(value: Any) -> Any:
7
8
  def is_file_like(obj):
8
9
  return hasattr(obj, "read") and callable(obj.read)
@@ -55,7 +56,7 @@ def validate_fields_by_provider_and_invoke_type(
55
56
  """
56
57
  from tamar_model_client.enums import ProviderType, InvokeType
57
58
  from tamar_model_client.schemas.inputs import GoogleGenAiInput, OpenAIResponsesInput, OpenAIChatCompletionsInput, \
58
- OpenAIImagesInput, OpenAIImagesEditInput, GoogleVertexAIImagesInput
59
+ OpenAIImagesInput, OpenAIImagesEditInput, GoogleVertexAIImagesInput, GoogleGenAIImagesInput
59
60
 
60
61
  google_allowed = extra_allowed_fields | set(GoogleGenAiInput.model_fields)
61
62
  openai_responses_allowed = extra_allowed_fields | set(OpenAIResponsesInput.model_fields)
@@ -63,9 +64,11 @@ def validate_fields_by_provider_and_invoke_type(
63
64
  openai_images_allowed = extra_allowed_fields | set(OpenAIImagesInput.model_fields)
64
65
  openai_images_edit_allowed = extra_allowed_fields | set(OpenAIImagesEditInput.model_fields)
65
66
  google_vertexai_images_allowed = extra_allowed_fields | set(GoogleVertexAIImagesInput.model_fields)
67
+ google_genai_images_allowed = extra_allowed_fields | set(GoogleGenAIImagesInput.model_fields)
66
68
 
67
69
  google_required = {"model", "contents"}
68
70
  google_vertex_required = {"model", "prompt"}
71
+ google_genai_images_required = {"model", "prompt"}
69
72
  openai_resp_required = {"input", "model"}
70
73
  openai_chat_required = {"messages", "model"}
71
74
  openai_img_required = {"prompt"}
@@ -90,6 +93,9 @@ def validate_fields_by_provider_and_invoke_type(
90
93
  case ((ProviderType.OPENAI | ProviderType.AZURE), InvokeType.IMAGE_EDIT_GENERATION):
91
94
  allowed = openai_images_edit_allowed
92
95
  required = openai_edit_required
96
+ case (ProviderType.GOOGLE, InvokeType.IMAGE_GENERATION_GENAI):
97
+ allowed = google_genai_images_allowed
98
+ required = google_genai_images_required
93
99
  case _:
94
100
  raise ValueError(f"Unsupported provider/invoke_type: {instance.provider} + {instance.invoke_type}")
95
101