camel-ai 0.1.9__py3-none-any.whl → 0.2.3__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 (102) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +334 -113
  3. camel/agents/knowledge_graph_agent.py +4 -6
  4. camel/bots/__init__.py +34 -0
  5. camel/bots/discord_app.py +138 -0
  6. camel/bots/slack/__init__.py +30 -0
  7. camel/bots/slack/models.py +158 -0
  8. camel/bots/slack/slack_app.py +255 -0
  9. camel/bots/telegram_bot.py +82 -0
  10. camel/configs/__init__.py +1 -2
  11. camel/configs/anthropic_config.py +2 -5
  12. camel/configs/base_config.py +6 -6
  13. camel/configs/gemini_config.py +1 -1
  14. camel/configs/groq_config.py +2 -3
  15. camel/configs/ollama_config.py +1 -2
  16. camel/configs/openai_config.py +2 -23
  17. camel/configs/samba_config.py +2 -2
  18. camel/configs/togetherai_config.py +1 -1
  19. camel/configs/vllm_config.py +1 -1
  20. camel/configs/zhipuai_config.py +2 -3
  21. camel/embeddings/openai_embedding.py +2 -2
  22. camel/loaders/__init__.py +2 -0
  23. camel/loaders/chunkr_reader.py +163 -0
  24. camel/loaders/firecrawl_reader.py +13 -45
  25. camel/loaders/unstructured_io.py +65 -29
  26. camel/messages/__init__.py +1 -0
  27. camel/messages/func_message.py +2 -2
  28. camel/models/__init__.py +2 -4
  29. camel/models/anthropic_model.py +32 -26
  30. camel/models/azure_openai_model.py +39 -36
  31. camel/models/base_model.py +31 -20
  32. camel/models/gemini_model.py +37 -29
  33. camel/models/groq_model.py +29 -23
  34. camel/models/litellm_model.py +44 -61
  35. camel/models/mistral_model.py +33 -30
  36. camel/models/model_factory.py +66 -76
  37. camel/models/nemotron_model.py +33 -23
  38. camel/models/ollama_model.py +42 -47
  39. camel/models/{openai_compatibility_model.py → openai_compatible_model.py} +36 -41
  40. camel/models/openai_model.py +60 -25
  41. camel/models/reka_model.py +30 -28
  42. camel/models/samba_model.py +82 -177
  43. camel/models/stub_model.py +2 -2
  44. camel/models/togetherai_model.py +37 -43
  45. camel/models/vllm_model.py +43 -50
  46. camel/models/zhipuai_model.py +33 -27
  47. camel/retrievers/auto_retriever.py +28 -10
  48. camel/retrievers/vector_retriever.py +72 -44
  49. camel/societies/babyagi_playing.py +6 -3
  50. camel/societies/role_playing.py +17 -3
  51. camel/storages/__init__.py +2 -0
  52. camel/storages/graph_storages/__init__.py +2 -0
  53. camel/storages/graph_storages/graph_element.py +3 -5
  54. camel/storages/graph_storages/nebula_graph.py +547 -0
  55. camel/storages/key_value_storages/json.py +6 -1
  56. camel/tasks/task.py +11 -4
  57. camel/tasks/task_prompt.py +4 -0
  58. camel/toolkits/__init__.py +28 -24
  59. camel/toolkits/arxiv_toolkit.py +155 -0
  60. camel/toolkits/ask_news_toolkit.py +653 -0
  61. camel/toolkits/base.py +2 -3
  62. camel/toolkits/code_execution.py +6 -7
  63. camel/toolkits/dalle_toolkit.py +6 -6
  64. camel/toolkits/{openai_function.py → function_tool.py} +34 -11
  65. camel/toolkits/github_toolkit.py +9 -10
  66. camel/toolkits/google_maps_toolkit.py +7 -14
  67. camel/toolkits/google_scholar_toolkit.py +146 -0
  68. camel/toolkits/linkedin_toolkit.py +7 -10
  69. camel/toolkits/math_toolkit.py +8 -8
  70. camel/toolkits/open_api_toolkit.py +5 -8
  71. camel/toolkits/reddit_toolkit.py +7 -10
  72. camel/toolkits/retrieval_toolkit.py +5 -9
  73. camel/toolkits/search_toolkit.py +9 -9
  74. camel/toolkits/slack_toolkit.py +11 -14
  75. camel/toolkits/twitter_toolkit.py +377 -454
  76. camel/toolkits/weather_toolkit.py +6 -6
  77. camel/toolkits/whatsapp_toolkit.py +177 -0
  78. camel/types/__init__.py +6 -1
  79. camel/types/enums.py +43 -85
  80. camel/types/openai_types.py +3 -0
  81. camel/types/unified_model_type.py +104 -0
  82. camel/utils/__init__.py +0 -2
  83. camel/utils/async_func.py +7 -7
  84. camel/utils/commons.py +40 -4
  85. camel/utils/token_counting.py +38 -214
  86. camel/workforce/__init__.py +6 -6
  87. camel/workforce/base.py +9 -5
  88. camel/workforce/prompts.py +179 -0
  89. camel/workforce/role_playing_worker.py +181 -0
  90. camel/workforce/{single_agent_node.py → single_agent_worker.py} +49 -23
  91. camel/workforce/task_channel.py +7 -8
  92. camel/workforce/utils.py +20 -50
  93. camel/workforce/{worker_node.py → worker.py} +15 -12
  94. camel/workforce/workforce.py +456 -19
  95. camel_ai-0.2.3.dist-info/LICENSE +201 -0
  96. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/METADATA +40 -65
  97. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/RECORD +98 -86
  98. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/WHEEL +1 -1
  99. camel/models/open_source_model.py +0 -170
  100. camel/workforce/manager_node.py +0 -299
  101. camel/workforce/role_playing_node.py +0 -168
  102. camel/workforce/workforce_prompt.py +0 -125
@@ -17,10 +17,14 @@ from typing import Any, Dict, List, Optional, Union
17
17
 
18
18
  from openai import OpenAI, Stream
19
19
 
20
- from camel.configs import ZHIPUAI_API_PARAMS
20
+ from camel.configs import ZHIPUAI_API_PARAMS, ZhipuAIConfig
21
21
  from camel.messages import OpenAIMessage
22
22
  from camel.models import BaseModelBackend
23
- from camel.types import ChatCompletion, ChatCompletionChunk, ModelType
23
+ from camel.types import (
24
+ ChatCompletion,
25
+ ChatCompletionChunk,
26
+ ModelType,
27
+ )
24
28
  from camel.utils import (
25
29
  BaseTokenCounter,
26
30
  OpenAITokenCounter,
@@ -29,40 +33,42 @@ from camel.utils import (
29
33
 
30
34
 
31
35
  class ZhipuAIModel(BaseModelBackend):
32
- r"""ZhipuAI API in a unified BaseModelBackend interface."""
36
+ r"""ZhipuAI API in a unified BaseModelBackend interface.
37
+
38
+ Args:
39
+ model_type (Union[ModelType, str]): Model for which a backend is
40
+ created, one of GLM_* series.
41
+ model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
42
+ that will be fed into:obj:`openai.ChatCompletion.create()`. If
43
+ :obj:`None`, :obj:`ZhipuAIConfig().as_dict()` will be used.
44
+ (default: :obj:`None`)
45
+ api_key (Optional[str], optional): The API key for authenticating with
46
+ the ZhipuAI service. (default: :obj:`None`)
47
+ url (Optional[str], optional): The url to the ZhipuAI service.
48
+ (default: :obj:`https://open.bigmodel.cn/api/paas/v4/`)
49
+ token_counter (Optional[BaseTokenCounter], optional): Token counter to
50
+ use for the model. If not provided, :obj:`OpenAITokenCounter(
51
+ ModelType.GPT_4O_MINI)` will be used.
52
+ (default: :obj:`None`)
53
+ """
33
54
 
34
55
  def __init__(
35
56
  self,
36
- model_type: ModelType,
37
- model_config_dict: Dict[str, Any],
57
+ model_type: Union[ModelType, str],
58
+ model_config_dict: Optional[Dict[str, Any]] = None,
38
59
  api_key: Optional[str] = None,
39
60
  url: Optional[str] = None,
40
61
  token_counter: Optional[BaseTokenCounter] = None,
41
62
  ) -> None:
42
- r"""Constructor for ZhipuAI backend.
43
-
44
- Args:
45
- model_type (ModelType): Model for which a backend is created,
46
- such as GLM_* series.
47
- model_config_dict (Dict[str, Any]): A dictionary that will
48
- be fed into openai.ChatCompletion.create().
49
- api_key (Optional[str]): The API key for authenticating with the
50
- ZhipuAI service. (default: :obj:`None`)
51
- url (Optional[str]): The url to the ZhipuAI service. (default:
52
- :obj:`None`)
53
- token_counter (Optional[BaseTokenCounter]): Token counter to use
54
- for the model. If not provided, `OpenAITokenCounter(ModelType.
55
- GPT_4O_MINI)` will be used.
56
- """
63
+ if model_config_dict is None:
64
+ model_config_dict = ZhipuAIConfig().as_dict()
65
+ api_key = api_key or os.environ.get("ZHIPUAI_API_KEY")
66
+ url = url or os.environ.get(
67
+ "ZHIPUAI_API_BASE_URL", "https://open.bigmodel.cn/api/paas/v4/"
68
+ )
57
69
  super().__init__(
58
70
  model_type, model_config_dict, api_key, url, token_counter
59
71
  )
60
- self._url = url or os.environ.get("ZHIPUAI_API_BASE_URL")
61
- self._api_key = api_key or os.environ.get("ZHIPUAI_API_KEY")
62
- if not self._url or not self._api_key:
63
- raise ValueError(
64
- "ZHIPUAI_API_BASE_URL and ZHIPUAI_API_KEY should be set."
65
- )
66
72
  self._client = OpenAI(
67
73
  timeout=60,
68
74
  max_retries=3,
@@ -90,7 +96,7 @@ class ZhipuAIModel(BaseModelBackend):
90
96
  # Reference: https://open.bigmodel.cn/dev/api#openai_sdk
91
97
  response = self._client.chat.completions.create(
92
98
  messages=messages,
93
- model=self.model_type.value,
99
+ model=self.model_type,
94
100
  **self.model_config_dict,
95
101
  )
96
102
  return response
@@ -14,7 +14,16 @@
14
14
  import datetime
15
15
  import os
16
16
  import re
17
- from typing import Collection, List, Optional, Sequence, Tuple, Union
17
+ import uuid
18
+ from typing import (
19
+ TYPE_CHECKING,
20
+ Collection,
21
+ List,
22
+ Optional,
23
+ Sequence,
24
+ Tuple,
25
+ Union,
26
+ )
18
27
 
19
28
  from camel.embeddings import BaseEmbedding, OpenAIEmbedding
20
29
  from camel.retrievers.vector_retriever import VectorRetriever
@@ -27,10 +36,8 @@ from camel.storages import (
27
36
  from camel.types import StorageType
28
37
  from camel.utils import Constants
29
38
 
30
- try:
39
+ if TYPE_CHECKING:
31
40
  from unstructured.documents.elements import Element
32
- except ImportError:
33
- Element = None
34
41
 
35
42
 
36
43
  class AutoRetriever:
@@ -98,7 +105,9 @@ class AutoRetriever:
98
105
  f"Unsupported vector storage type: {self.storage_type}"
99
106
  )
100
107
 
101
- def _collection_name_generator(self, content: Union[str, Element]) -> str:
108
+ def _collection_name_generator(
109
+ self, content: Union[str, "Element"]
110
+ ) -> str:
102
111
  r"""Generates a valid collection name from a given file path or URL.
103
112
 
104
113
  Args:
@@ -108,9 +117,10 @@ class AutoRetriever:
108
117
  Returns:
109
118
  str: A sanitized, valid collection name suitable for use.
110
119
  """
120
+ from unstructured.documents.elements import Element
111
121
 
112
122
  if isinstance(content, Element):
113
- content = content.metadata.file_directory
123
+ content = content.metadata.file_directory or str(uuid.uuid4())
114
124
 
115
125
  collection_name = re.sub(r'[^a-zA-Z0-9]', '', content)[:20]
116
126
 
@@ -175,7 +185,7 @@ class AutoRetriever:
175
185
  def run_vector_retriever(
176
186
  self,
177
187
  query: str,
178
- contents: Union[str, List[str], Element, List[Element]],
188
+ contents: Union[str, List[str], "Element", List["Element"]],
179
189
  top_k: int = Constants.DEFAULT_TOP_K_RESULTS,
180
190
  similarity_threshold: float = Constants.DEFAULT_SIMILARITY_THRESHOLD,
181
191
  return_detailed_info: bool = False,
@@ -212,12 +222,20 @@ class AutoRetriever:
212
222
  `contents` is empty.
213
223
  RuntimeError: If any errors occur during the retrieve process.
214
224
  """
225
+ from unstructured.documents.elements import Element
226
+
215
227
  if not contents:
216
228
  raise ValueError("content cannot be empty.")
217
229
 
218
- contents = (
219
- [contents] if isinstance(contents, (str, Element)) else contents
220
- )
230
+ # Normalize contents to a list
231
+ if isinstance(contents, str):
232
+ contents = [contents]
233
+ elif isinstance(contents, Element):
234
+ contents = [contents]
235
+ elif not isinstance(contents, list):
236
+ raise ValueError(
237
+ "contents must be a string, Element, or a list of them."
238
+ )
221
239
 
222
240
  all_retrieved_info = []
223
241
  for content in contents:
@@ -13,7 +13,8 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
  import os
15
15
  import warnings
16
- from typing import Any, Dict, List, Optional, Union
16
+ from io import IOBase
17
+ from typing import IO, TYPE_CHECKING, Any, Dict, List, Optional, Union
17
18
  from urllib.parse import urlparse
18
19
 
19
20
  from camel.embeddings import BaseEmbedding, OpenAIEmbedding
@@ -27,10 +28,8 @@ from camel.storages import (
27
28
  )
28
29
  from camel.utils import Constants
29
30
 
30
- try:
31
+ if TYPE_CHECKING:
31
32
  from unstructured.documents.elements import Element
32
- except ImportError:
33
- Element = None
34
33
 
35
34
 
36
35
  class VectorRetriever(BaseRetriever):
@@ -72,75 +71,97 @@ class VectorRetriever(BaseRetriever):
72
71
 
73
72
  def process(
74
73
  self,
75
- content: Union[str, Element],
74
+ content: Union[str, "Element", IO[bytes]],
76
75
  chunk_type: str = "chunk_by_title",
77
76
  max_characters: int = 500,
77
+ embed_batch: int = 50,
78
+ should_chunk: bool = True,
78
79
  **kwargs: Any,
79
80
  ) -> None:
80
- r"""Processes content from a file or URL, divides it into chunks by
81
- using `Unstructured IO`, and stores their embeddings in the specified
82
- vector storage.
81
+ r"""Processes content from local file path, remote URL, string
82
+ content, Element object, or a binary file object, divides it into
83
+ chunks by using `Unstructured IO`, and stores their embeddings in the
84
+ specified vector storage.
83
85
 
84
86
  Args:
85
- content (Union[str, Element]): Local file path, remote URL,
86
- string content or Element object.
87
+ content (Union[str, Element, IO[bytes]]): Local file path, remote
88
+ URL, string content, Element object, or a binary file object.
87
89
  chunk_type (str): Type of chunking going to apply. Defaults to
88
90
  "chunk_by_title".
89
91
  max_characters (int): Max number of characters in each chunk.
90
92
  Defaults to `500`.
93
+ embed_batch (int): Size of batch for embeddings. Defaults to `50`.
94
+ should_chunk (bool): If True, divide the content into chunks,
95
+ otherwise skip chunking. Defaults to True.
91
96
  **kwargs (Any): Additional keyword arguments for content parsing.
92
97
  """
98
+ from unstructured.documents.elements import Element
99
+
93
100
  if isinstance(content, Element):
94
101
  elements = [content]
95
- else:
102
+ elif isinstance(content, IOBase):
103
+ elements = self.uio.parse_bytes(file=content, **kwargs) or []
104
+ elif isinstance(content, str):
96
105
  # Check if the content is URL
97
106
  parsed_url = urlparse(content)
98
107
  is_url = all([parsed_url.scheme, parsed_url.netloc])
99
108
  if is_url or os.path.exists(content):
100
- elements = self.uio.parse_file_or_url(content, **kwargs) or []
109
+ elements = (
110
+ self.uio.parse_file_or_url(input_path=content, **kwargs)
111
+ or []
112
+ )
101
113
  else:
102
114
  elements = [self.uio.create_element_from_text(text=content)]
103
- if elements:
104
- chunks = self.uio.chunk_elements(
105
- chunk_type=chunk_type,
106
- elements=elements,
107
- max_characters=max_characters,
108
- )
115
+
109
116
  if not elements:
110
117
  warnings.warn(
111
118
  f"No elements were extracted from the content: {content}"
112
119
  )
113
- return
114
- # Iterate to process and store embeddings, set batch of 50
115
- for i in range(0, len(chunks), 50):
116
- batch_chunks = chunks[i : i + 50]
117
- batch_vectors = self.embedding_model.embed_list(
118
- objs=[str(chunk) for chunk in batch_chunks]
120
+ else:
121
+ # Chunk the content if required
122
+ chunks = (
123
+ self.uio.chunk_elements(
124
+ chunk_type=chunk_type,
125
+ elements=elements,
126
+ max_characters=max_characters,
127
+ )
128
+ if should_chunk
129
+ else elements
119
130
  )
120
131
 
121
- records = []
122
- # Prepare the payload for each vector record, includes the content
123
- # path, chunk metadata, and chunk text
124
- for vector, chunk in zip(batch_vectors, batch_chunks):
125
- if isinstance(content, str):
126
- content_path_info = {"content path": content}
127
- elif isinstance(content, Element):
128
- content_path_info = {
129
- "content path": content.metadata.file_directory
132
+ # Process chunks in batches and store embeddings
133
+ for i in range(0, len(chunks), embed_batch):
134
+ batch_chunks = chunks[i : i + embed_batch]
135
+ batch_vectors = self.embedding_model.embed_list(
136
+ objs=[str(chunk) for chunk in batch_chunks]
137
+ )
138
+
139
+ records = []
140
+ # Prepare the payload for each vector record, includes the
141
+ # content path, chunk metadata, and chunk text
142
+ for vector, chunk in zip(batch_vectors, batch_chunks):
143
+ if isinstance(content, str):
144
+ content_path_info = {"content path": content}
145
+ elif isinstance(content, IOBase):
146
+ content_path_info = {"content path": "From file bytes"}
147
+ elif isinstance(content, Element):
148
+ content_path_info = {
149
+ "content path": content.metadata.file_directory
150
+ or ""
151
+ }
152
+ chunk_metadata = {"metadata": chunk.metadata.to_dict()}
153
+ chunk_text = {"text": str(chunk)}
154
+ combined_dict = {
155
+ **content_path_info,
156
+ **chunk_metadata,
157
+ **chunk_text,
130
158
  }
131
- chunk_metadata = {"metadata": chunk.metadata.to_dict()}
132
- chunk_text = {"text": str(chunk)}
133
- combined_dict = {
134
- **content_path_info,
135
- **chunk_metadata,
136
- **chunk_text,
137
- }
138
159
 
139
- records.append(
140
- VectorRecord(vector=vector, payload=combined_dict)
141
- )
160
+ records.append(
161
+ VectorRecord(vector=vector, payload=combined_dict)
162
+ )
142
163
 
143
- self.storage.add(records=records)
164
+ self.storage.add(records=records)
144
165
 
145
166
  def query(
146
167
  self,
@@ -178,6 +199,13 @@ class VectorRetriever(BaseRetriever):
178
199
  db_query = VectorDBQuery(query_vector=query_vector, top_k=top_k)
179
200
  query_results = self.storage.query(query=db_query)
180
201
 
202
+ # If no results found, raise an error
203
+ if not query_results:
204
+ raise ValueError(
205
+ "Query result is empty, please check if "
206
+ "the vector storage is empty."
207
+ )
208
+
181
209
  if query_results[0].record.payload is None:
182
210
  raise ValueError(
183
211
  "Payload of vector storage is None, please check the "
@@ -106,7 +106,7 @@ class BabyAGI:
106
106
  )
107
107
 
108
108
  self.assistant_agent: ChatAgent
109
- self.assistant_sys_msg: BaseMessage
109
+ self.assistant_sys_msg: Optional[BaseMessage]
110
110
  self.task_creation_agent: TaskCreationAgent
111
111
  self.task_prioritization_agent: TaskPrioritizationAgent
112
112
  self.init_agents(
@@ -202,7 +202,8 @@ class BabyAGI:
202
202
 
203
203
  self.task_creation_agent = TaskCreationAgent(
204
204
  objective=self.specified_task_prompt,
205
- role_name=self.assistant_sys_msg.role_name,
205
+ role_name=getattr(self.assistant_sys_msg, 'role_name', None)
206
+ or "assistant",
206
207
  output_language=output_language,
207
208
  message_window_size=message_window_size,
208
209
  **(task_creation_agent_kwargs or {}),
@@ -238,7 +239,9 @@ class BabyAGI:
238
239
 
239
240
  task_name = self.subtasks.popleft()
240
241
  assistant_msg_msg = BaseMessage.make_user_message(
241
- role_name=self.assistant_sys_msg.role_name, content=f"{task_name}"
242
+ role_name=getattr(self.assistant_sys_msg, 'role_name', None)
243
+ or "assistant",
244
+ content=f"{task_name}",
242
245
  )
243
246
 
244
247
  assistant_response = self.assistant_agent.step(assistant_msg_msg)
@@ -11,6 +11,7 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ import logging
14
15
  from typing import Dict, List, Optional, Sequence, Tuple, Union
15
16
 
16
17
  from camel.agents import (
@@ -27,6 +28,9 @@ from camel.prompts import TextPrompt
27
28
  from camel.responses import ChatAgentResponse
28
29
  from camel.types import RoleType, TaskType
29
30
 
31
+ logger = logging.getLogger(__name__)
32
+ logger.setLevel(logging.WARNING)
33
+
30
34
 
31
35
  class RolePlaying:
32
36
  r"""Role playing between two agents.
@@ -97,6 +101,14 @@ class RolePlaying:
97
101
  extend_task_specify_meta_dict: Optional[Dict] = None,
98
102
  output_language: Optional[str] = None,
99
103
  ) -> None:
104
+ if model is not None:
105
+ logger.warning(
106
+ "The provided model will override the model settings in "
107
+ "all agents, including any configurations passed "
108
+ "through assistant_agent_kwargs, user_agent_kwargs, and "
109
+ "other agent-specific kwargs."
110
+ )
111
+
100
112
  self.with_task_specify = with_task_specify
101
113
  self.with_task_planner = with_task_planner
102
114
  self.with_critic_in_the_loop = with_critic_in_the_loop
@@ -137,8 +149,8 @@ class RolePlaying:
137
149
 
138
150
  self.assistant_agent: ChatAgent
139
151
  self.user_agent: ChatAgent
140
- self.assistant_sys_msg: BaseMessage
141
- self.user_sys_msg: BaseMessage
152
+ self.assistant_sys_msg: Optional[BaseMessage]
153
+ self.user_sys_msg: Optional[BaseMessage]
142
154
  self._init_agents(
143
155
  init_assistant_sys_msg,
144
156
  init_user_sys_msg,
@@ -442,9 +454,11 @@ class RolePlaying:
442
454
  )
443
455
  if init_msg_content is None:
444
456
  init_msg_content = default_init_msg_content
457
+
445
458
  # Initialize a message sent by the assistant
446
459
  init_msg = BaseMessage.make_assistant_message(
447
- role_name=self.assistant_sys_msg.role_name,
460
+ role_name=getattr(self.assistant_sys_msg, 'role_name', None)
461
+ or "assistant",
448
462
  content=init_msg_content,
449
463
  )
450
464
 
@@ -13,6 +13,7 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
 
15
15
  from .graph_storages.base import BaseGraphStorage
16
+ from .graph_storages.nebula_graph import NebulaGraph
16
17
  from .graph_storages.neo4j_graph import Neo4jGraph
17
18
  from .key_value_storages.base import BaseKeyValueStorage
18
19
  from .key_value_storages.in_memory import InMemoryKeyValueStorage
@@ -40,4 +41,5 @@ __all__ = [
40
41
  'MilvusStorage',
41
42
  'BaseGraphStorage',
42
43
  'Neo4jGraph',
44
+ 'NebulaGraph',
43
45
  ]
@@ -14,10 +14,12 @@
14
14
 
15
15
  from .base import BaseGraphStorage
16
16
  from .graph_element import GraphElement
17
+ from .nebula_graph import NebulaGraph
17
18
  from .neo4j_graph import Neo4jGraph
18
19
 
19
20
  __all__ = [
20
21
  'BaseGraphStorage',
21
22
  'GraphElement',
22
23
  'Neo4jGraph',
24
+ 'NebulaGraph',
23
25
  ]
@@ -13,14 +13,12 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import List, Union
16
+ from typing import TYPE_CHECKING, List, Union
17
17
 
18
18
  from pydantic import BaseModel, ConfigDict, Field
19
19
 
20
- try:
20
+ if TYPE_CHECKING:
21
21
  from unstructured.documents.elements import Element
22
- except ImportError:
23
- Element = None
24
22
 
25
23
 
26
24
  class Node(BaseModel):
@@ -73,6 +71,6 @@ class GraphElement(BaseModel):
73
71
  source: Element
74
72
 
75
73
  def __post_init__(self):
76
- if Element is None:
74
+ if "Element" not in globals():
77
75
  raise ImportError("""The 'unstructured' package is required to use
78
76
  the 'source' attribute.""")