camel-ai 0.2.3__py3-none-any.whl → 0.2.3a0__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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (87) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +69 -93
  3. camel/agents/knowledge_graph_agent.py +6 -4
  4. camel/bots/__init__.py +2 -16
  5. camel/bots/discord_bot.py +206 -0
  6. camel/configs/__init__.py +2 -1
  7. camel/configs/anthropic_config.py +5 -2
  8. camel/configs/base_config.py +6 -6
  9. camel/configs/groq_config.py +3 -2
  10. camel/configs/ollama_config.py +2 -1
  11. camel/configs/openai_config.py +23 -2
  12. camel/configs/samba_config.py +2 -2
  13. camel/configs/togetherai_config.py +1 -1
  14. camel/configs/vllm_config.py +1 -1
  15. camel/configs/zhipuai_config.py +3 -2
  16. camel/embeddings/openai_embedding.py +2 -2
  17. camel/loaders/__init__.py +0 -2
  18. camel/loaders/firecrawl_reader.py +3 -3
  19. camel/loaders/unstructured_io.py +33 -35
  20. camel/messages/__init__.py +0 -1
  21. camel/models/__init__.py +4 -2
  22. camel/models/anthropic_model.py +26 -32
  23. camel/models/azure_openai_model.py +36 -39
  24. camel/models/base_model.py +20 -31
  25. camel/models/gemini_model.py +29 -37
  26. camel/models/groq_model.py +23 -29
  27. camel/models/litellm_model.py +61 -44
  28. camel/models/mistral_model.py +29 -32
  29. camel/models/model_factory.py +76 -66
  30. camel/models/nemotron_model.py +23 -33
  31. camel/models/ollama_model.py +47 -42
  32. camel/models/open_source_model.py +170 -0
  33. camel/models/{openai_compatible_model.py → openai_compatibility_model.py} +49 -31
  34. camel/models/openai_model.py +29 -48
  35. camel/models/reka_model.py +28 -30
  36. camel/models/samba_model.py +177 -82
  37. camel/models/stub_model.py +2 -2
  38. camel/models/togetherai_model.py +43 -37
  39. camel/models/vllm_model.py +50 -43
  40. camel/models/zhipuai_model.py +27 -33
  41. camel/retrievers/auto_retriever.py +10 -28
  42. camel/retrievers/vector_retriever.py +47 -58
  43. camel/societies/babyagi_playing.py +3 -6
  44. camel/societies/role_playing.py +3 -5
  45. camel/storages/graph_storages/graph_element.py +5 -3
  46. camel/storages/key_value_storages/json.py +1 -6
  47. camel/toolkits/__init__.py +7 -20
  48. camel/toolkits/base.py +3 -2
  49. camel/toolkits/code_execution.py +7 -6
  50. camel/toolkits/dalle_toolkit.py +6 -6
  51. camel/toolkits/github_toolkit.py +10 -9
  52. camel/toolkits/google_maps_toolkit.py +7 -7
  53. camel/toolkits/linkedin_toolkit.py +7 -7
  54. camel/toolkits/math_toolkit.py +8 -8
  55. camel/toolkits/open_api_toolkit.py +5 -5
  56. camel/toolkits/{function_tool.py → openai_function.py} +11 -34
  57. camel/toolkits/reddit_toolkit.py +7 -7
  58. camel/toolkits/retrieval_toolkit.py +5 -5
  59. camel/toolkits/search_toolkit.py +9 -9
  60. camel/toolkits/slack_toolkit.py +11 -11
  61. camel/toolkits/twitter_toolkit.py +452 -378
  62. camel/toolkits/weather_toolkit.py +6 -6
  63. camel/types/__init__.py +1 -6
  64. camel/types/enums.py +85 -40
  65. camel/types/openai_types.py +0 -3
  66. camel/utils/__init__.py +2 -0
  67. camel/utils/async_func.py +7 -7
  68. camel/utils/commons.py +3 -32
  69. camel/utils/token_counting.py +212 -30
  70. camel/workforce/role_playing_worker.py +1 -1
  71. camel/workforce/single_agent_worker.py +1 -1
  72. camel/workforce/task_channel.py +3 -4
  73. camel/workforce/workforce.py +4 -4
  74. {camel_ai-0.2.3.dist-info → camel_ai-0.2.3a0.dist-info}/METADATA +56 -27
  75. {camel_ai-0.2.3.dist-info → camel_ai-0.2.3a0.dist-info}/RECORD +76 -85
  76. {camel_ai-0.2.3.dist-info → camel_ai-0.2.3a0.dist-info}/WHEEL +1 -1
  77. camel/bots/discord_app.py +0 -138
  78. camel/bots/slack/__init__.py +0 -30
  79. camel/bots/slack/models.py +0 -158
  80. camel/bots/slack/slack_app.py +0 -255
  81. camel/loaders/chunkr_reader.py +0 -163
  82. camel/toolkits/arxiv_toolkit.py +0 -155
  83. camel/toolkits/ask_news_toolkit.py +0 -653
  84. camel/toolkits/google_scholar_toolkit.py +0 -146
  85. camel/toolkits/whatsapp_toolkit.py +0 -177
  86. camel/types/unified_model_type.py +0 -104
  87. camel_ai-0.2.3.dist-info/LICENSE +0 -201
@@ -22,11 +22,10 @@ from openai import OpenAI, Stream
22
22
 
23
23
  from camel.configs import (
24
24
  SAMBA_CLOUD_API_PARAMS,
25
+ SAMBA_FAST_API_PARAMS,
25
26
  SAMBA_VERSE_API_PARAMS,
26
- SambaCloudAPIConfig,
27
27
  )
28
28
  from camel.messages import OpenAIMessage
29
- from camel.models import BaseModelBackend
30
29
  from camel.types import (
31
30
  ChatCompletion,
32
31
  ChatCompletionChunk,
@@ -39,59 +38,48 @@ from camel.utils import (
39
38
  api_keys_required,
40
39
  )
41
40
 
42
- try:
43
- if os.getenv("AGENTOPS_API_KEY") is not None:
44
- from agentops import LLMEvent, record
45
- else:
46
- raise ImportError
47
- except (ImportError, AttributeError):
48
- LLMEvent = None
49
-
50
-
51
- class SambaModel(BaseModelBackend):
52
- r"""SambaNova service interface.
53
-
54
- Args:
55
- model_type (Union[ModelType, str]): Model for which a SambaNova backend
56
- is created. Supported models via SambaNova Cloud:
57
- `https://community.sambanova.ai/t/supported-models/193`.
58
- Supported models via SambaVerse API is listed in
59
- `https://sambaverse.sambanova.ai/models`.
60
- model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
61
- that will be fed into:obj:`openai.ChatCompletion.create()`. If
62
- :obj:`None`, :obj:`SambaCloudAPIConfig().as_dict()` will be used.
63
- (default: :obj:`None`)
64
- api_key (Optional[str], optional): The API key for authenticating
65
- with the SambaNova service. (default: :obj:`None`)
66
- url (Optional[str], optional): The url to the SambaNova service.
67
- Current support SambaVerse API:
68
- :obj:`"https://sambaverse.sambanova.ai/api/predict"` and
69
- SambaNova Cloud:
70
- :obj:`"https://api.sambanova.ai/v1"` (default: :obj:`https://api.
71
- sambanova.ai/v1`)
72
- token_counter (Optional[BaseTokenCounter], optional): Token counter to
73
- use for the model. If not provided, :obj:`OpenAITokenCounter(
74
- ModelType.GPT_4O_MINI)` will be used.
75
- """
41
+
42
+ class SambaModel:
43
+ r"""SambaNova service interface."""
76
44
 
77
45
  def __init__(
78
46
  self,
79
- model_type: Union[ModelType, str],
80
- model_config_dict: Optional[Dict[str, Any]] = None,
47
+ model_type: str,
48
+ model_config_dict: Dict[str, Any],
81
49
  api_key: Optional[str] = None,
82
50
  url: Optional[str] = None,
83
51
  token_counter: Optional[BaseTokenCounter] = None,
84
52
  ) -> None:
85
- if model_config_dict is None:
86
- model_config_dict = SambaCloudAPIConfig().as_dict()
87
- api_key = api_key or os.environ.get("SAMBA_API_KEY")
88
- url = url or os.environ.get(
53
+ r"""Constructor for SambaNova backend.
54
+
55
+ Args:
56
+ model_type (str): Model for which a SambaNova backend is
57
+ created. Supported models via Fast API: `https://sambanova.ai/
58
+ fast-api?api_ref=128521`. Supported models via SambaVerse API
59
+ is listed in `https://sambaverse.sambanova.ai/models`.
60
+ model_config_dict (Dict[str, Any]): A dictionary that will
61
+ be fed into API request.
62
+ api_key (Optional[str]): The API key for authenticating with the
63
+ SambaNova service. (default: :obj:`None`)
64
+ url (Optional[str]): The url to the SambaNova service. Current
65
+ support SambaNova Fast API: :obj:`"https://fast-api.snova.ai/
66
+ v1/chat/ completions"`, SambaVerse API: :obj:`"https://
67
+ sambaverse.sambanova.ai/api/predict"` and SambaNova Cloud:
68
+ :obj:`"https://api.sambanova.ai/v1"`
69
+ (default::obj:`"https://fast-api.snova.ai/v1/chat/completions"`)
70
+ token_counter (Optional[BaseTokenCounter]): Token counter to use
71
+ for the model. If not provided, `OpenAITokenCounter(ModelType.
72
+ GPT_4O_MINI)` will be used.
73
+ """
74
+ self.model_type = model_type
75
+ self._api_key = api_key or os.environ.get("SAMBA_API_KEY")
76
+ self._url = url or os.environ.get(
89
77
  "SAMBA_API_BASE_URL",
90
- "https://api.sambanova.ai/v1",
91
- )
92
- super().__init__(
93
- model_type, model_config_dict, api_key, url, token_counter
78
+ "https://fast-api.snova.ai/v1/chat/completions",
94
79
  )
80
+ self._token_counter = token_counter
81
+ self.model_config_dict = model_config_dict
82
+ self.check_model_config()
95
83
 
96
84
  if self._url == "https://api.sambanova.ai/v1":
97
85
  self._client = OpenAI(
@@ -121,7 +109,14 @@ class SambaModel(BaseModelBackend):
121
109
  ValueError: If the model configuration dictionary contains any
122
110
  unexpected arguments to SambaNova API.
123
111
  """
124
- if self._url == "https://sambaverse.sambanova.ai/api/predict":
112
+ if self._url == "https://fast-api.snova.ai/v1/chat/completions":
113
+ for param in self.model_config_dict:
114
+ if param not in SAMBA_FAST_API_PARAMS:
115
+ raise ValueError(
116
+ f"Unexpected argument `{param}` is "
117
+ "input into SambaNova Fast API."
118
+ )
119
+ elif self._url == "https://sambaverse.sambanova.ai/api/predict":
125
120
  for param in self.model_config_dict:
126
121
  if param not in SAMBA_VERSE_API_PARAMS:
127
122
  raise ValueError(
@@ -164,7 +159,7 @@ class SambaModel(BaseModelBackend):
164
159
  else:
165
160
  return self._run_non_streaming(messages)
166
161
 
167
- def _run_streaming(
162
+ def _run_streaming( # type: ignore[misc]
168
163
  self, messages: List[OpenAIMessage]
169
164
  ) -> Stream[ChatCompletionChunk]:
170
165
  r"""Handles streaming inference with SambaNova's API.
@@ -180,30 +175,48 @@ class SambaModel(BaseModelBackend):
180
175
 
181
176
  Raises:
182
177
  RuntimeError: If the HTTP request fails.
183
- ValueError: If the API doesn't support stream mode.
184
178
  """
179
+
180
+ # Handle SambaNova's Fast API
181
+ if self._url == "https://fast-api.snova.ai/v1/chat/completions":
182
+ headers = {
183
+ "Authorization": f"Basic {self._api_key}",
184
+ "Content-Type": "application/json",
185
+ }
186
+
187
+ data = {
188
+ "messages": messages,
189
+ "max_tokens": self.token_limit,
190
+ "stop": self.model_config_dict.get("stop"),
191
+ "model": self.model_type,
192
+ "stream": True,
193
+ "stream_options": self.model_config_dict.get("stream_options"),
194
+ }
195
+
196
+ try:
197
+ with httpx.stream(
198
+ "POST",
199
+ self._url,
200
+ headers=headers,
201
+ json=data,
202
+ ) as api_response:
203
+ stream = Stream[ChatCompletionChunk](
204
+ cast_to=ChatCompletionChunk,
205
+ response=api_response,
206
+ client=OpenAI(api_key="required_but_not_used"),
207
+ )
208
+ for chunk in stream:
209
+ yield chunk
210
+ except httpx.HTTPError as e:
211
+ raise RuntimeError(f"HTTP request failed: {e!s}")
212
+
185
213
  # Handle SambaNova's Cloud API
186
- if self._url == "https://api.sambanova.ai/v1":
214
+ elif self._url == "https://api.sambanova.ai/v1":
187
215
  response = self._client.chat.completions.create(
188
216
  messages=messages,
189
217
  model=self.model_type,
190
218
  **self.model_config_dict,
191
219
  )
192
-
193
- # Add AgentOps LLM Event tracking
194
- if LLMEvent:
195
- llm_event = LLMEvent(
196
- thread_id=response.id,
197
- prompt=" ".join(
198
- [message.get("content") for message in messages] # type: ignore[misc]
199
- ),
200
- prompt_tokens=response.usage.prompt_tokens, # type: ignore[union-attr]
201
- completion=response.choices[0].message.content,
202
- completion_tokens=response.usage.completion_tokens, # type: ignore[union-attr]
203
- model=self.model_type,
204
- )
205
- record(llm_event)
206
-
207
220
  return response
208
221
 
209
222
  elif self._url == "https://sambaverse.sambanova.ai/api/predict":
@@ -211,7 +224,6 @@ class SambaModel(BaseModelBackend):
211
224
  "https://sambaverse.sambanova.ai/api/predict doesn't support"
212
225
  " stream mode"
213
226
  )
214
- raise RuntimeError(f"Unknown URL: {self._url}")
215
227
 
216
228
  def _run_non_streaming(
217
229
  self, messages: List[OpenAIMessage]
@@ -231,28 +243,51 @@ class SambaModel(BaseModelBackend):
231
243
  ValueError: If the JSON response cannot be decoded or is missing
232
244
  expected data.
233
245
  """
246
+
247
+ # Handle SambaNova's Fast API
248
+ if self._url == "https://fast-api.snova.ai/v1/chat/completions":
249
+ headers = {
250
+ "Authorization": f"Basic {self._api_key}",
251
+ "Content-Type": "application/json",
252
+ }
253
+
254
+ data = {
255
+ "messages": messages,
256
+ "max_tokens": self.token_limit,
257
+ "stop": self.model_config_dict.get("stop"),
258
+ "model": self.model_type,
259
+ "stream": True,
260
+ "stream_options": self.model_config_dict.get("stream_options"),
261
+ }
262
+
263
+ try:
264
+ with httpx.stream(
265
+ "POST",
266
+ self._url,
267
+ headers=headers,
268
+ json=data,
269
+ ) as api_response:
270
+ samba_response = []
271
+ for chunk in api_response.iter_text():
272
+ if chunk.startswith('data: '):
273
+ chunk = chunk[6:]
274
+ if '[DONE]' in chunk:
275
+ break
276
+ json_data = json.loads(chunk)
277
+ samba_response.append(json_data)
278
+ return self._fastapi_to_openai_response(samba_response)
279
+ except httpx.HTTPError as e:
280
+ raise RuntimeError(f"HTTP request failed: {e!s}")
281
+ except json.JSONDecodeError as e:
282
+ raise ValueError(f"Failed to decode JSON response: {e!s}")
283
+
234
284
  # Handle SambaNova's Cloud API
235
- if self._url == "https://api.sambanova.ai/v1":
285
+ elif self._url == "https://api.sambanova.ai/v1":
236
286
  response = self._client.chat.completions.create(
237
287
  messages=messages,
238
288
  model=self.model_type,
239
289
  **self.model_config_dict,
240
290
  )
241
-
242
- # Add AgentOps LLM Event tracking
243
- if LLMEvent:
244
- llm_event = LLMEvent(
245
- thread_id=response.id,
246
- prompt=" ".join(
247
- [message.get("content") for message in messages] # type: ignore[misc]
248
- ),
249
- prompt_tokens=response.usage.prompt_tokens, # type: ignore[union-attr]
250
- completion=response.choices[0].message.content,
251
- completion_tokens=response.usage.completion_tokens, # type: ignore[union-attr]
252
- model=self.model_type,
253
- )
254
- record(llm_event)
255
-
256
291
  return response
257
292
 
258
293
  # Handle SambaNova's Sambaverse API
@@ -335,6 +370,56 @@ class SambaModel(BaseModelBackend):
335
370
  except httpx.HTTPStatusError:
336
371
  raise RuntimeError(f"HTTP request failed: {raw_text}")
337
372
 
373
+ def _fastapi_to_openai_response(
374
+ self, samba_response: List[Dict[str, Any]]
375
+ ) -> ChatCompletion:
376
+ r"""Converts SambaNova Fast API response chunks into an
377
+ OpenAI-compatible response.
378
+
379
+ Args:
380
+ samba_response (List[Dict[str, Any]]): A list of dictionaries
381
+ representing partial responses from the SambaNova Fast API.
382
+
383
+ Returns:
384
+ ChatCompletion: A `ChatCompletion` object constructed from the
385
+ aggregated response data.
386
+ """
387
+
388
+ # Step 1: Combine the content from each chunk
389
+ full_content = ""
390
+ for chunk in samba_response:
391
+ if chunk['choices']:
392
+ for choice in chunk['choices']:
393
+ delta_content = choice['delta'].get('content', '')
394
+ full_content += delta_content
395
+
396
+ # Step 2: Create the ChatCompletion object
397
+ # Extract relevant information from the first chunk
398
+ first_chunk = samba_response[0]
399
+
400
+ choices = [
401
+ dict(
402
+ index=0, # type: ignore[index]
403
+ message={
404
+ "role": 'assistant',
405
+ "content": full_content.strip(),
406
+ },
407
+ finish_reason=samba_response[-1]['choices'][0]['finish_reason']
408
+ or None,
409
+ )
410
+ ]
411
+
412
+ obj = ChatCompletion.construct(
413
+ id=first_chunk['id'],
414
+ choices=choices,
415
+ created=first_chunk['created'],
416
+ model=first_chunk['model'],
417
+ object="chat.completion",
418
+ usage=None,
419
+ )
420
+
421
+ return obj
422
+
338
423
  def _sambaverse_to_openai_response(
339
424
  self, samba_response: Dict[str, Any]
340
425
  ) -> ChatCompletion:
@@ -384,6 +469,16 @@ class SambaModel(BaseModelBackend):
384
469
 
385
470
  return obj
386
471
 
472
+ @property
473
+ def token_limit(self) -> int:
474
+ r"""Returns the maximum token limit for the given model.
475
+
476
+ Returns:
477
+ int: The maximum token limit for the given model.
478
+ """
479
+ max_tokens = self.model_config_dict["max_tokens"]
480
+ return max_tokens
481
+
387
482
  @property
388
483
  def stream(self) -> bool:
389
484
  r"""Returns whether the model is in stream mode, which sends partial
@@ -51,8 +51,8 @@ class StubModel(BaseModelBackend):
51
51
 
52
52
  def __init__(
53
53
  self,
54
- model_type: Union[ModelType, str],
55
- model_config_dict: Optional[Dict[str, Any]] = None,
54
+ model_type: ModelType,
55
+ model_config_dict: Dict[str, Any],
56
56
  api_key: Optional[str] = None,
57
57
  url: Optional[str] = None,
58
58
  token_counter: Optional[BaseTokenCounter] = None,
@@ -17,14 +17,9 @@ from typing import Any, Dict, List, Optional, Union
17
17
 
18
18
  from openai import OpenAI, Stream
19
19
 
20
- from camel.configs import TOGETHERAI_API_PARAMS, TogetherAIConfig
20
+ from camel.configs import TOGETHERAI_API_PARAMS
21
21
  from camel.messages import OpenAIMessage
22
- from camel.models import BaseModelBackend
23
- from camel.types import (
24
- ChatCompletion,
25
- ChatCompletionChunk,
26
- ModelType,
27
- )
22
+ from camel.types import ChatCompletion, ChatCompletionChunk, ModelType
28
23
  from camel.utils import (
29
24
  BaseTokenCounter,
30
25
  OpenAITokenCounter,
@@ -32,50 +27,45 @@ from camel.utils import (
32
27
  )
33
28
 
34
29
 
35
- class TogetherAIModel(BaseModelBackend):
30
+ class TogetherAIModel:
36
31
  r"""Constructor for Together AI backend with OpenAI compatibility.
37
-
38
- Args:
39
- model_type (Union[ModelType, str]): Model for which a backend is
40
- created, supported model can be found here:
41
- https://docs.together.ai/docs/chat-models
42
- model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
43
- that will be fed into:obj:`openai.ChatCompletion.create()`. If
44
- :obj:`None`, :obj:`TogetherAIConfig().as_dict()` will be used.
45
- (default: :obj:`None`)
46
- api_key (Optional[str], optional): The API key for authenticating with
47
- the Together service. (default: :obj:`None`)
48
- url (Optional[str], optional): The url to the Together AI service.
49
- If not provided, "https://api.together.xyz/v1" will be used.
50
- (default: :obj:`None`)
51
- token_counter (Optional[BaseTokenCounter], optional): Token counter to
52
- use for the model. If not provided, :obj:`OpenAITokenCounter(
53
- ModelType.GPT_4O_MINI)` will be used.
32
+ TODO: Add function calling support
54
33
  """
55
34
 
56
35
  def __init__(
57
36
  self,
58
- model_type: Union[ModelType, str],
59
- model_config_dict: Optional[Dict[str, Any]] = None,
37
+ model_type: str,
38
+ model_config_dict: Dict[str, Any],
60
39
  api_key: Optional[str] = None,
61
40
  url: Optional[str] = None,
62
41
  token_counter: Optional[BaseTokenCounter] = None,
63
42
  ) -> None:
64
- if model_config_dict is None:
65
- model_config_dict = TogetherAIConfig().as_dict()
66
- api_key = api_key or os.environ.get("TOGETHER_API_KEY")
67
- url = url or os.environ.get(
68
- "TOGETHER_API_BASE_URL", "https://api.together.xyz/v1"
69
- )
70
- super().__init__(
71
- model_type, model_config_dict, api_key, url, token_counter
72
- )
43
+ r"""Constructor for TogetherAI backend.
44
+
45
+ Args:
46
+ model_type (str): Model for which a backend is created, supported
47
+ model can be found here: https://docs.together.ai/docs/chat-models
48
+ model_config_dict (Dict[str, Any]): A dictionary that will
49
+ be fed into openai.ChatCompletion.create().
50
+ api_key (Optional[str]): The API key for authenticating with the
51
+ Together service. (default: :obj:`None`)
52
+ url (Optional[str]): The url to the Together AI service. (default:
53
+ :obj:`"https://api.together.xyz/v1"`)
54
+ token_counter (Optional[BaseTokenCounter]): Token counter to use
55
+ for the model. If not provided, `OpenAITokenCounter(ModelType.
56
+ GPT_4O_MINI)` will be used.
57
+ """
58
+ self.model_type = model_type
59
+ self.model_config_dict = model_config_dict
60
+ self._token_counter = token_counter
61
+ self._api_key = api_key or os.environ.get("TOGETHER_API_KEY")
62
+ self._url = url or os.environ.get("TOGETHER_API_BASE_URL")
73
63
 
74
64
  self._client = OpenAI(
75
65
  timeout=60,
76
66
  max_retries=3,
77
67
  api_key=self._api_key,
78
- base_url=self._url,
68
+ base_url=self._url or "https://api.together.xyz/v1",
79
69
  )
80
70
 
81
71
  @api_keys_required("TOGETHER_API_KEY")
@@ -140,3 +130,19 @@ class TogetherAIModel(BaseModelBackend):
140
130
  bool: Whether the model is in stream mode.
141
131
  """
142
132
  return self.model_config_dict.get('stream', False)
133
+
134
+ @property
135
+ def token_limit(self) -> int:
136
+ r"""Returns the maximum token limit for the given model.
137
+
138
+ Returns:
139
+ int: The maximum token limit for the given model.
140
+ """
141
+ max_tokens = self.model_config_dict.get("max_tokens")
142
+ if isinstance(max_tokens, int):
143
+ return max_tokens
144
+ print(
145
+ "Must set `max_tokens` as an integer in `model_config_dict` when"
146
+ " setting up the model. Using 4096 as default value."
147
+ )
148
+ return 4096
@@ -17,66 +17,58 @@ from typing import Any, Dict, List, Optional, Union
17
17
 
18
18
  from openai import OpenAI, Stream
19
19
 
20
- from camel.configs import VLLM_API_PARAMS, VLLMConfig
20
+ from camel.configs import VLLM_API_PARAMS
21
21
  from camel.messages import OpenAIMessage
22
- from camel.models import BaseModelBackend
23
- from camel.types import (
24
- ChatCompletion,
25
- ChatCompletionChunk,
26
- ModelType,
27
- )
22
+ from camel.types import ChatCompletion, ChatCompletionChunk, ModelType
28
23
  from camel.utils import BaseTokenCounter, OpenAITokenCounter
29
24
 
30
25
 
31
26
  # flake8: noqa: E501
32
- class VLLMModel(BaseModelBackend):
33
- r"""vLLM service interface.
34
-
35
- Args:
36
- model_type (Union[ModelType, str]): Model for which a backend is
37
- created.
38
- model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
39
- that will be fed into:obj:`openai.ChatCompletion.create()`. If
40
- :obj:`None`, :obj:`VLLMConfig().as_dict()` will be used.
41
- (default: :obj:`None`)
42
- api_key (Optional[str], optional): The API key for authenticating with
43
- the model service. vLLM doesn't need API key, it would be ignored
44
- if set. (default: :obj:`None`)
45
- url (Optional[str], optional): The url to the model service. If not
46
- provided, :obj:`"http://localhost:8000/v1"` will be used.
47
- (default: :obj:`None`)
48
- token_counter (Optional[BaseTokenCounter], optional): Token counter to
49
- use for the model. If not provided, :obj:`OpenAITokenCounter(
50
- ModelType.GPT_4O_MINI)` will be used.
51
- (default: :obj:`None`)
52
-
53
- References:
54
- https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html
55
- """
27
+ class VLLMModel:
28
+ r"""vLLM service interface."""
56
29
 
57
30
  def __init__(
58
31
  self,
59
- model_type: Union[ModelType, str],
60
- model_config_dict: Optional[Dict[str, Any]] = None,
61
- api_key: Optional[str] = None,
32
+ model_type: str,
33
+ model_config_dict: Dict[str, Any],
62
34
  url: Optional[str] = None,
35
+ api_key: Optional[str] = None,
63
36
  token_counter: Optional[BaseTokenCounter] = None,
64
37
  ) -> None:
65
- if model_config_dict is None:
66
- model_config_dict = VLLMConfig().as_dict()
67
- url = url or os.environ.get("VLLM_BASE_URL")
68
- super().__init__(
69
- model_type, model_config_dict, api_key, url, token_counter
38
+ r"""Constructor for vLLM backend with OpenAI compatibility.
39
+
40
+ # Reference: https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html
41
+
42
+ Args:
43
+ model_type (str): Model for which a backend is created.
44
+ model_config_dict (Dict[str, Any]): A dictionary that will
45
+ be fed into openai.ChatCompletion.create().
46
+ url (Optional[str]): The url to the model service. (default:
47
+ :obj:`"http://localhost:8000/v1"`)
48
+ api_key (Optional[str]): The API key for authenticating with the
49
+ model service.
50
+ token_counter (Optional[BaseTokenCounter]): Token counter to use
51
+ for the model. If not provided, `OpenAITokenCounter(ModelType.
52
+ GPT_4O_MINI)` will be used.
53
+ """
54
+ self.model_type = model_type
55
+ self.model_config_dict = model_config_dict
56
+ self._url = (
57
+ url
58
+ or os.environ.get("VLLM_BASE_URL")
59
+ or "http://localhost:8000/v1"
70
60
  )
71
- if not self._url:
61
+ if not url and not os.environ.get("VLLM_BASE_URL"):
72
62
  self._start_server()
73
63
  # Use OpenAI cilent as interface call vLLM
74
64
  self._client = OpenAI(
75
65
  timeout=60,
76
66
  max_retries=3,
77
- api_key="Set-but-ignored", # required but ignored
78
67
  base_url=self._url,
68
+ api_key=api_key,
79
69
  )
70
+ self._token_counter = token_counter
71
+ self.check_model_config()
80
72
 
81
73
  def _start_server(self) -> None:
82
74
  r"""Starts the vllm server in a subprocess."""
@@ -86,9 +78,8 @@ class VLLMModel(BaseModelBackend):
86
78
  stdout=subprocess.PIPE,
87
79
  stderr=subprocess.PIPE,
88
80
  )
89
- self._url = "http://localhost:8000/v1"
90
81
  print(
91
- f"vllm server started on {self._url} "
82
+ f"vllm server started on http://localhost:8000/v1 "
92
83
  f"for {self.model_type} model."
93
84
  )
94
85
  except Exception as e:
@@ -144,6 +135,22 @@ class VLLMModel(BaseModelBackend):
144
135
  )
145
136
  return response
146
137
 
138
+ @property
139
+ def token_limit(self) -> int:
140
+ r"""Returns the maximum token limit for the given model.
141
+
142
+ Returns:
143
+ int: The maximum token limit for the given model.
144
+ """
145
+ max_tokens = self.model_config_dict.get("max_tokens")
146
+ if isinstance(max_tokens, int):
147
+ return max_tokens
148
+ print(
149
+ "Must set `max_tokens` as an integer in `model_config_dict` when"
150
+ " setting up the model. Using 4096 as default value."
151
+ )
152
+ return 4096
153
+
147
154
  @property
148
155
  def stream(self) -> bool:
149
156
  r"""Returns whether the model is in stream mode, which sends partial