camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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 (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -16,9 +16,14 @@ import warnings
16
16
  from typing import Any, Dict, List, Optional, Type, Union
17
17
 
18
18
  from openai import AsyncOpenAI, AsyncStream, OpenAI, Stream
19
+ from openai.lib.streaming.chat import (
20
+ AsyncChatCompletionStreamManager,
21
+ ChatCompletionStreamManager,
22
+ )
19
23
  from pydantic import BaseModel
20
24
 
21
- from camel.configs import OPENAI_API_PARAMS, ChatGPTConfig
25
+ from camel.configs import ChatGPTConfig
26
+ from camel.logger import get_logger
22
27
  from camel.messages import OpenAIMessage
23
28
  from camel.models import BaseModelBackend
24
29
  from camel.types import (
@@ -35,11 +40,18 @@ from camel.utils import (
35
40
  update_langfuse_trace,
36
41
  )
37
42
 
43
+ logger = get_logger(__name__)
44
+
38
45
  if os.environ.get("LANGFUSE_ENABLED", "False").lower() == "true":
39
46
  try:
40
47
  from langfuse.decorators import observe
41
48
  except ImportError:
42
49
  from camel.utils import observe
50
+ elif os.environ.get("TRACEROOT_ENABLED", "False").lower() == "true":
51
+ try:
52
+ from traceroot import trace as observe # type: ignore[import]
53
+ except ImportError:
54
+ from camel.utils import observe
43
55
  else:
44
56
  from camel.utils import observe
45
57
 
@@ -78,9 +90,21 @@ class OpenAIModel(BaseModelBackend):
78
90
  (default: :obj:`None`)
79
91
  max_retries (int, optional): Maximum number of retries for API calls.
80
92
  (default: :obj:`3`)
93
+ client (Optional[Any], optional): A custom synchronous OpenAI client
94
+ instance. If provided, this client will be used instead of
95
+ creating a new one. Useful for RL frameworks like AReaL or rLLM
96
+ that provide OpenAI-compatible clients. The client should
97
+ implement the OpenAI client interface with
98
+ `.chat.completions.create()` and `.beta.chat.completions.parse()`
99
+ methods. (default: :obj:`None`)
100
+ async_client (Optional[Any], optional): A custom asynchronous OpenAI
101
+ client instance. If provided, this client will be used instead of
102
+ creating a new one. The client should implement the AsyncOpenAI
103
+ client interface. (default: :obj:`None`)
81
104
  **kwargs (Any): Additional arguments to pass to the
82
105
  OpenAI client initialization. These can include parameters like
83
106
  'organization', 'default_headers', 'http_client', etc.
107
+ Ignored if custom clients are provided.
84
108
  """
85
109
 
86
110
  @api_keys_required(
@@ -97,6 +121,8 @@ class OpenAIModel(BaseModelBackend):
97
121
  token_counter: Optional[BaseTokenCounter] = None,
98
122
  timeout: Optional[float] = None,
99
123
  max_retries: int = 3,
124
+ client: Optional[Any] = None,
125
+ async_client: Optional[Any] = None,
100
126
  **kwargs: Any,
101
127
  ) -> None:
102
128
  if model_config_dict is None:
@@ -112,42 +138,54 @@ class OpenAIModel(BaseModelBackend):
112
138
  model_type, model_config_dict, api_key, url, token_counter, timeout
113
139
  )
114
140
 
115
- if is_langfuse_available():
116
- from langfuse.openai import AsyncOpenAI as LangfuseAsyncOpenAI
117
- from langfuse.openai import OpenAI as LangfuseOpenAI
118
-
119
- # Create Langfuse client with base parameters and additional
120
- # arguments
121
- self._client = LangfuseOpenAI(
122
- timeout=self._timeout,
123
- max_retries=self._max_retries,
124
- base_url=self._url,
125
- api_key=self._api_key,
126
- **kwargs,
127
- )
128
- self._async_client = LangfuseAsyncOpenAI(
129
- timeout=self._timeout,
130
- max_retries=self._max_retries,
131
- base_url=self._url,
132
- api_key=self._api_key,
133
- **kwargs,
134
- )
141
+ # Use custom clients if provided, otherwise create new ones
142
+ if client is not None:
143
+ # Use the provided custom sync client
144
+ self._client = client
135
145
  else:
136
- # Create client with base parameters and additional arguments
137
- self._client = OpenAI(
138
- timeout=self._timeout,
139
- max_retries=self._max_retries,
140
- base_url=self._url,
141
- api_key=self._api_key,
142
- **kwargs,
143
- )
144
- self._async_client = AsyncOpenAI(
145
- timeout=self._timeout,
146
- max_retries=self._max_retries,
147
- base_url=self._url,
148
- api_key=self._api_key,
149
- **kwargs,
150
- )
146
+ # Create default sync client
147
+ if is_langfuse_available():
148
+ from langfuse.openai import OpenAI as LangfuseOpenAI
149
+
150
+ self._client = LangfuseOpenAI(
151
+ timeout=self._timeout,
152
+ max_retries=self._max_retries,
153
+ base_url=self._url,
154
+ api_key=self._api_key,
155
+ **kwargs,
156
+ )
157
+ else:
158
+ self._client = OpenAI(
159
+ timeout=self._timeout,
160
+ max_retries=self._max_retries,
161
+ base_url=self._url,
162
+ api_key=self._api_key,
163
+ **kwargs,
164
+ )
165
+
166
+ if async_client is not None:
167
+ # Use the provided custom async client
168
+ self._async_client = async_client
169
+ else:
170
+ # Create default async client
171
+ if is_langfuse_available():
172
+ from langfuse.openai import AsyncOpenAI as LangfuseAsyncOpenAI
173
+
174
+ self._async_client = LangfuseAsyncOpenAI(
175
+ timeout=self._timeout,
176
+ max_retries=self._max_retries,
177
+ base_url=self._url,
178
+ api_key=self._api_key,
179
+ **kwargs,
180
+ )
181
+ else:
182
+ self._async_client = AsyncOpenAI(
183
+ timeout=self._timeout,
184
+ max_retries=self._max_retries,
185
+ base_url=self._url,
186
+ api_key=self._api_key,
187
+ **kwargs,
188
+ )
151
189
 
152
190
  def _sanitize_config(self, config_dict: Dict[str, Any]) -> Dict[str, Any]:
153
191
  r"""Sanitize the model configuration for O1 models."""
@@ -238,7 +276,11 @@ class OpenAIModel(BaseModelBackend):
238
276
  messages: List[OpenAIMessage],
239
277
  response_format: Optional[Type[BaseModel]] = None,
240
278
  tools: Optional[List[Dict[str, Any]]] = None,
241
- ) -> Union[ChatCompletion, Stream[ChatCompletionChunk]]:
279
+ ) -> Union[
280
+ ChatCompletion,
281
+ Stream[ChatCompletionChunk],
282
+ ChatCompletionStreamManager[BaseModel],
283
+ ]:
242
284
  r"""Runs inference of OpenAI chat completion.
243
285
 
244
286
  Args:
@@ -250,33 +292,51 @@ class OpenAIModel(BaseModelBackend):
250
292
  use for the request.
251
293
 
252
294
  Returns:
253
- Union[ChatCompletion, Stream[ChatCompletionChunk]]:
254
- `ChatCompletion` in the non-stream mode, or
255
- `Stream[ChatCompletionChunk]` in the stream mode.
295
+ Union[ChatCompletion, Stream[ChatCompletionChunk],
296
+ ChatCompletionStreamManager[BaseModel]]:
297
+ `ChatCompletion` in the non-stream mode,
298
+ `Stream[ChatCompletionChunk]`in the stream mode,
299
+ or `ChatCompletionStreamManager[BaseModel]` for
300
+ structured output streaming.
256
301
  """
257
302
 
258
303
  # Update Langfuse trace with current agent session and metadata
259
304
  agent_session_id = get_current_agent_session_id()
305
+ model_type_str = str(self.model_type)
306
+ if not agent_session_id:
307
+ agent_session_id = "no-session-id"
308
+ metadata = {
309
+ "source": "camel",
310
+ "agent_id": agent_session_id,
311
+ "agent_type": "camel_chat_agent",
312
+ "model_type": model_type_str,
313
+ }
314
+ metadata = {k: str(v) for k, v in metadata.items()}
260
315
  if agent_session_id:
261
316
  update_langfuse_trace(
262
317
  session_id=agent_session_id,
263
- metadata={
264
- "source": "camel",
265
- "agent_id": agent_session_id,
266
- "agent_type": "camel_chat_agent",
267
- "model_type": str(self.model_type),
268
- },
269
- tags=["CAMEL-AI", str(self.model_type)],
318
+ metadata=metadata,
319
+ tags=["CAMEL-AI", model_type_str],
270
320
  )
321
+ logger.info(f"metadata: {metadata}")
271
322
 
272
323
  messages = self._adapt_messages_for_o1_models(messages)
273
324
  response_format = response_format or self.model_config_dict.get(
274
325
  "response_format", None
275
326
  )
327
+
328
+ # Check if streaming is enabled
329
+ is_streaming = self.model_config_dict.get("stream", False)
330
+
276
331
  if response_format:
277
- result: Union[ChatCompletion, Stream[ChatCompletionChunk]] = (
278
- self._request_parse(messages, response_format, tools)
279
- )
332
+ if is_streaming:
333
+ # Use streaming parse for structured output
334
+ return self._request_stream_parse(
335
+ messages, response_format, tools
336
+ )
337
+ else:
338
+ # Use non-streaming parse for structured output
339
+ return self._request_parse(messages, response_format, tools)
280
340
  else:
281
341
  result = self._request_chat_completion(messages, tools)
282
342
 
@@ -288,7 +348,11 @@ class OpenAIModel(BaseModelBackend):
288
348
  messages: List[OpenAIMessage],
289
349
  response_format: Optional[Type[BaseModel]] = None,
290
350
  tools: Optional[List[Dict[str, Any]]] = None,
291
- ) -> Union[ChatCompletion, AsyncStream[ChatCompletionChunk]]:
351
+ ) -> Union[
352
+ ChatCompletion,
353
+ AsyncStream[ChatCompletionChunk],
354
+ AsyncChatCompletionStreamManager[BaseModel],
355
+ ]:
292
356
  r"""Runs inference of OpenAI chat completion in async mode.
293
357
 
294
358
  Args:
@@ -300,33 +364,52 @@ class OpenAIModel(BaseModelBackend):
300
364
  use for the request.
301
365
 
302
366
  Returns:
303
- Union[ChatCompletion, AsyncStream[ChatCompletionChunk]]:
304
- `ChatCompletion` in the non-stream mode, or
305
- `AsyncStream[ChatCompletionChunk]` in the stream mode.
367
+ Union[ChatCompletion, AsyncStream[ChatCompletionChunk],
368
+ AsyncChatCompletionStreamManager[BaseModel]]:
369
+ `ChatCompletion` in the non-stream mode,
370
+ `AsyncStream[ChatCompletionChunk]` in the stream mode, or
371
+ `AsyncChatCompletionStreamManager[BaseModel]` for
372
+ structured output streaming.
306
373
  """
307
374
 
308
375
  # Update Langfuse trace with current agent session and metadata
309
376
  agent_session_id = get_current_agent_session_id()
310
- if agent_session_id:
377
+ model_type_str = str(self.model_type)
378
+ if not agent_session_id:
379
+ agent_session_id = "no-session-id"
380
+ metadata = {
381
+ "source": "camel",
382
+ "agent_id": agent_session_id,
383
+ "agent_type": "camel_chat_agent",
384
+ "model_type": model_type_str,
385
+ }
386
+ metadata = {k: str(v) for k, v in metadata.items()}
311
387
  update_langfuse_trace(
312
388
  session_id=agent_session_id,
313
- metadata={
314
- "source": "camel",
315
- "agent_id": agent_session_id,
316
- "agent_type": "camel_chat_agent",
317
- "model_type": str(self.model_type),
318
- },
319
- tags=["CAMEL-AI", str(self.model_type)],
389
+ metadata=metadata,
390
+ tags=["CAMEL-AI", model_type_str],
320
391
  )
392
+ logger.info(f"metadata: {metadata}")
321
393
 
322
394
  messages = self._adapt_messages_for_o1_models(messages)
323
395
  response_format = response_format or self.model_config_dict.get(
324
396
  "response_format", None
325
397
  )
398
+
399
+ # Check if streaming is enabled
400
+ is_streaming = self.model_config_dict.get("stream", False)
401
+
326
402
  if response_format:
327
- result: Union[
328
- ChatCompletion, AsyncStream[ChatCompletionChunk]
329
- ] = await self._arequest_parse(messages, response_format, tools)
403
+ if is_streaming:
404
+ # Use streaming parse for structured output
405
+ return await self._arequest_stream_parse(
406
+ messages, response_format, tools
407
+ )
408
+ else:
409
+ # Use non-streaming parse for structured output
410
+ return await self._arequest_parse(
411
+ messages, response_format, tools
412
+ )
330
413
  else:
331
414
  result = await self._arequest_chat_completion(messages, tools)
332
415
 
@@ -422,20 +505,65 @@ class OpenAIModel(BaseModelBackend):
422
505
  **request_config,
423
506
  )
424
507
 
425
- def check_model_config(self):
426
- r"""Check whether the model configuration contains any
427
- unexpected arguments to OpenAI API.
508
+ def _request_stream_parse(
509
+ self,
510
+ messages: List[OpenAIMessage],
511
+ response_format: Type[BaseModel],
512
+ tools: Optional[List[Dict[str, Any]]] = None,
513
+ ) -> ChatCompletionStreamManager[BaseModel]:
514
+ r"""Request streaming structured output parsing.
428
515
 
429
- Raises:
430
- ValueError: If the model configuration dictionary contains any
431
- unexpected arguments to OpenAI API.
516
+ Note: This uses OpenAI's beta streaming API for structured outputs.
432
517
  """
433
- for param in self.model_config_dict:
434
- if param not in OPENAI_API_PARAMS:
435
- raise ValueError(
436
- f"Unexpected argument `{param}` is "
437
- "input into OpenAI model backend."
438
- )
518
+ import copy
519
+
520
+ request_config = copy.deepcopy(self.model_config_dict)
521
+
522
+ # Remove stream from config as it's handled by the stream method
523
+ request_config.pop("stream", None)
524
+
525
+ if tools is not None:
526
+ request_config["tools"] = tools
527
+
528
+ request_config = self._sanitize_config(request_config)
529
+
530
+ # Use the beta streaming API for structured outputs
531
+ return self._client.beta.chat.completions.stream(
532
+ messages=messages,
533
+ model=self.model_type,
534
+ response_format=response_format,
535
+ **request_config,
536
+ )
537
+
538
+ async def _arequest_stream_parse(
539
+ self,
540
+ messages: List[OpenAIMessage],
541
+ response_format: Type[BaseModel],
542
+ tools: Optional[List[Dict[str, Any]]] = None,
543
+ ) -> AsyncChatCompletionStreamManager[BaseModel]:
544
+ r"""Request async streaming structured output parsing.
545
+
546
+ Note: This uses OpenAI's beta streaming API for structured outputs.
547
+ """
548
+ import copy
549
+
550
+ request_config = copy.deepcopy(self.model_config_dict)
551
+
552
+ # Remove stream from config as it's handled by the stream method
553
+ request_config.pop("stream", None)
554
+
555
+ if tools is not None:
556
+ request_config["tools"] = tools
557
+
558
+ request_config = self._sanitize_config(request_config)
559
+
560
+ # Use the beta streaming API for structured outputs
561
+ return self._async_client.beta.chat.completions.stream(
562
+ messages=messages,
563
+ model=self.model_type,
564
+ response_format=response_format,
565
+ **request_config,
566
+ )
439
567
 
440
568
  @property
441
569
  def stream(self) -> bool:
@@ -14,7 +14,7 @@
14
14
  import os
15
15
  from typing import Any, Dict, Optional, Union
16
16
 
17
- from camel.configs import OPENROUTER_API_PARAMS, OpenRouterConfig
17
+ from camel.configs import OpenRouterConfig
18
18
  from camel.models.openai_compatible_model import OpenAICompatibleModel
19
19
  from camel.types import ModelType
20
20
  from camel.utils import (
@@ -81,19 +81,3 @@ class OpenRouterModel(OpenAICompatibleModel):
81
81
  max_retries=max_retries,
82
82
  **kwargs,
83
83
  )
84
-
85
- def check_model_config(self):
86
- r"""Check whether the model configuration contains any unexpected
87
- arguments to OpenRouter API. But OpenRouter API does not have any
88
- additional arguments to check.
89
-
90
- Raises:
91
- ValueError: If the model configuration dictionary contains any
92
- unexpected arguments to OpenRouter API.
93
- """
94
- for param in self.model_config_dict:
95
- if param not in OPENROUTER_API_PARAMS:
96
- raise ValueError(
97
- f"Unexpected argument `{param}` is "
98
- "input into OpenRouter model backend."
99
- )
@@ -15,7 +15,7 @@
15
15
  import os
16
16
  from typing import Any, Dict, Optional, Union
17
17
 
18
- from camel.configs import PPIO_API_PARAMS, PPIOConfig
18
+ from camel.configs import PPIOConfig
19
19
  from camel.models.openai_compatible_model import OpenAICompatibleModel
20
20
  from camel.types import ModelType
21
21
  from camel.utils import (
@@ -86,18 +86,3 @@ class PPIOModel(OpenAICompatibleModel):
86
86
  max_retries=max_retries,
87
87
  **kwargs,
88
88
  )
89
-
90
- def check_model_config(self):
91
- r"""Check whether the model configuration contains any
92
- unexpected arguments to PPIO API.
93
-
94
- Raises:
95
- ValueError: If the model configuration dictionary contains any
96
- unexpected arguments to PPIO API.
97
- """
98
- for param in self.model_config_dict:
99
- if param not in PPIO_API_PARAMS:
100
- raise ValueError(
101
- f"Unexpected argument `{param}` is "
102
- "input into PPIO model backend."
103
- )
@@ -15,7 +15,7 @@
15
15
  import os
16
16
  from typing import Any, Dict, Optional, Union
17
17
 
18
- from camel.configs import QIANFAN_API_PARAMS, QianfanConfig
18
+ from camel.configs import QianfanConfig
19
19
  from camel.models.openai_compatible_model import OpenAICompatibleModel
20
20
  from camel.types import ModelType
21
21
  from camel.utils import (
@@ -87,18 +87,3 @@ class QianfanModel(OpenAICompatibleModel):
87
87
  max_retries=max_retries,
88
88
  **kwargs,
89
89
  )
90
-
91
- def check_model_config(self):
92
- r"""Check whether the model configuration contains any
93
- unexpected arguments to Qianfan API.
94
-
95
- Raises:
96
- ValueError: If the model configuration dictionary contains any
97
- unexpected arguments to Qianfan API.
98
- """
99
- for param in self.model_config_dict:
100
- if param not in QIANFAN_API_PARAMS:
101
- raise ValueError(
102
- f"Unexpected argument `{param}` is "
103
- "input into QIANFAN model backend."
104
- )
@@ -18,7 +18,7 @@ from typing import Any, Dict, List, Optional, Union
18
18
 
19
19
  from openai import AsyncStream, Stream
20
20
 
21
- from camel.configs import QWEN_API_PARAMS, QwenConfig
21
+ from camel.configs import QwenConfig
22
22
  from camel.messages import OpenAIMessage
23
23
  from camel.models.openai_compatible_model import OpenAICompatibleModel
24
24
  from camel.types import (
@@ -259,18 +259,3 @@ class QwenModel(OpenAICompatibleModel):
259
259
  **request_config,
260
260
  )
261
261
  return self._post_handle_response(response)
262
-
263
- def check_model_config(self):
264
- r"""Check whether the model configuration contains any
265
- unexpected arguments to Qwen API.
266
-
267
- Raises:
268
- ValueError: If the model configuration dictionary contains any
269
- unexpected arguments to Qwen API.
270
- """
271
- for param in self.model_config_dict:
272
- if param not in QWEN_API_PARAMS:
273
- raise ValueError(
274
- f"Unexpected argument `{param}` is "
275
- "input into Qwen model backend."
276
- )
@@ -16,7 +16,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
16
16
 
17
17
  from pydantic import BaseModel
18
18
 
19
- from camel.configs import REKA_API_PARAMS, RekaConfig
19
+ from camel.configs import RekaConfig
20
20
  from camel.messages import OpenAIMessage
21
21
  from camel.models import BaseModelBackend
22
22
  from camel.types import ChatCompletion, ModelType
@@ -354,21 +354,6 @@ class RekaModel(BaseModelBackend):
354
354
 
355
355
  return openai_response
356
356
 
357
- def check_model_config(self):
358
- r"""Check whether the model configuration contains any
359
- unexpected arguments to Reka API.
360
-
361
- Raises:
362
- ValueError: If the model configuration dictionary contains any
363
- unexpected arguments to Reka API.
364
- """
365
- for param in self.model_config_dict:
366
- if param not in REKA_API_PARAMS:
367
- raise ValueError(
368
- f"Unexpected argument `{param}` is "
369
- "input into Reka model backend."
370
- )
371
-
372
357
  @property
373
358
  def stream(self) -> bool:
374
359
  r"""Returns whether the model is in stream mode, which sends partial
@@ -22,8 +22,6 @@ from openai import AsyncOpenAI, AsyncStream, OpenAI, Stream
22
22
  from pydantic import BaseModel
23
23
 
24
24
  from camel.configs import (
25
- SAMBA_CLOUD_API_PARAMS,
26
- SAMBA_VERSE_API_PARAMS,
27
25
  SambaCloudAPIConfig,
28
26
  )
29
27
  from camel.messages import OpenAIMessage
@@ -90,8 +88,16 @@ class SambaModel(BaseModelBackend):
90
88
  (default: :obj:`None`)
91
89
  max_retries (int, optional): Maximum number of retries for API calls.
92
90
  (default: :obj:`3`)
91
+ client (Optional[Any], optional): A custom synchronous
92
+ OpenAI-compatible client instance. If provided, this client will
93
+ be used instead of creating a new one. Only applicable when using
94
+ SambaNova Cloud API. (default: :obj:`None`)
95
+ async_client (Optional[Any], optional): A custom asynchronous
96
+ OpenAI-compatible client instance. If provided, this client will
97
+ be used instead of creating a new one. Only applicable when using
98
+ SambaNova Cloud API. (default: :obj:`None`)
93
99
  **kwargs (Any): Additional arguments to pass to the client
94
- initialization.
100
+ initialization. Ignored if custom clients are provided.
95
101
  """
96
102
 
97
103
  @api_keys_required(
@@ -108,6 +114,8 @@ class SambaModel(BaseModelBackend):
108
114
  token_counter: Optional[BaseTokenCounter] = None,
109
115
  timeout: Optional[float] = None,
110
116
  max_retries: int = 3,
117
+ client: Optional[Any] = None,
118
+ async_client: Optional[Any] = None,
111
119
  **kwargs: Any,
112
120
  ) -> None:
113
121
  if model_config_dict is None:
@@ -128,21 +136,30 @@ class SambaModel(BaseModelBackend):
128
136
  max_retries,
129
137
  )
130
138
 
139
+ # Only create clients for Cloud API mode
131
140
  if self._url == "https://api.sambanova.ai/v1":
132
- self._client = OpenAI(
133
- timeout=self._timeout,
134
- max_retries=self._max_retries,
135
- base_url=self._url,
136
- api_key=self._api_key,
137
- **kwargs,
138
- )
139
- self._async_client = AsyncOpenAI(
140
- timeout=self._timeout,
141
- max_retries=self._max_retries,
142
- base_url=self._url,
143
- api_key=self._api_key,
144
- **kwargs,
145
- )
141
+ # Use custom clients if provided, otherwise create new ones
142
+ if client is not None:
143
+ self._client = client
144
+ else:
145
+ self._client = OpenAI(
146
+ timeout=self._timeout,
147
+ max_retries=self._max_retries,
148
+ base_url=self._url,
149
+ api_key=self._api_key,
150
+ **kwargs,
151
+ )
152
+
153
+ if async_client is not None:
154
+ self._async_client = async_client
155
+ else:
156
+ self._async_client = AsyncOpenAI(
157
+ timeout=self._timeout,
158
+ max_retries=self._max_retries,
159
+ base_url=self._url,
160
+ api_key=self._api_key,
161
+ **kwargs,
162
+ )
146
163
 
147
164
  @property
148
165
  def token_counter(self) -> BaseTokenCounter:
@@ -156,36 +173,6 @@ class SambaModel(BaseModelBackend):
156
173
  self._token_counter = OpenAITokenCounter(ModelType.GPT_4O_MINI)
157
174
  return self._token_counter
158
175
 
159
- def check_model_config(self):
160
- r"""Check whether the model configuration contains any
161
- unexpected arguments to SambaNova API.
162
-
163
- Raises:
164
- ValueError: If the model configuration dictionary contains any
165
- unexpected arguments to SambaNova API.
166
- """
167
- if self._url == "https://sambaverse.sambanova.ai/api/predict":
168
- for param in self.model_config_dict:
169
- if param not in SAMBA_VERSE_API_PARAMS:
170
- raise ValueError(
171
- f"Unexpected argument `{param}` is "
172
- "input into SambaVerse API."
173
- )
174
-
175
- elif self._url == "https://api.sambanova.ai/v1":
176
- for param in self.model_config_dict:
177
- if param not in SAMBA_CLOUD_API_PARAMS:
178
- raise ValueError(
179
- f"Unexpected argument `{param}` is "
180
- "input into SambaCloud API."
181
- )
182
-
183
- else:
184
- raise ValueError(
185
- f"{self._url} is not supported, please check the url to the"
186
- " SambaNova service"
187
- )
188
-
189
176
  @observe(as_type="generation")
190
177
  async def _arun( # type: ignore[misc]
191
178
  self,