camel-ai 0.1.5.4__py3-none-any.whl → 0.1.5.5__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 (43) hide show
  1. camel/__init__.py +1 -1
  2. camel/configs/__init__.py +6 -0
  3. camel/configs/litellm_config.py +8 -18
  4. camel/configs/ollama_config.py +85 -0
  5. camel/configs/zhipuai_config.py +78 -0
  6. camel/embeddings/openai_embedding.py +2 -2
  7. camel/functions/search_functions.py +5 -14
  8. camel/functions/slack_functions.py +5 -7
  9. camel/functions/twitter_function.py +3 -8
  10. camel/functions/weather_functions.py +3 -8
  11. camel/interpreters/__init__.py +2 -0
  12. camel/interpreters/docker_interpreter.py +235 -0
  13. camel/loaders/__init__.py +2 -0
  14. camel/loaders/base_io.py +5 -9
  15. camel/loaders/jina_url_reader.py +99 -0
  16. camel/loaders/unstructured_io.py +4 -6
  17. camel/models/anthropic_model.py +6 -4
  18. camel/models/litellm_model.py +49 -21
  19. camel/models/model_factory.py +1 -0
  20. camel/models/nemotron_model.py +14 -6
  21. camel/models/ollama_model.py +11 -17
  22. camel/models/openai_audio_models.py +10 -2
  23. camel/models/openai_model.py +4 -3
  24. camel/models/zhipuai_model.py +12 -6
  25. camel/retrievers/bm25_retriever.py +3 -8
  26. camel/retrievers/cohere_rerank_retriever.py +3 -5
  27. camel/storages/__init__.py +2 -0
  28. camel/storages/graph_storages/neo4j_graph.py +3 -7
  29. camel/storages/key_value_storages/__init__.py +2 -0
  30. camel/storages/key_value_storages/redis.py +169 -0
  31. camel/storages/vectordb_storages/milvus.py +3 -7
  32. camel/storages/vectordb_storages/qdrant.py +3 -7
  33. camel/toolkits/__init__.py +2 -0
  34. camel/toolkits/code_execution.py +69 -0
  35. camel/toolkits/github_toolkit.py +5 -9
  36. camel/types/enums.py +35 -1
  37. camel/utils/__init__.py +2 -2
  38. camel/utils/async_func.py +42 -0
  39. camel/utils/commons.py +31 -49
  40. camel/utils/token_counting.py +40 -1
  41. {camel_ai-0.1.5.4.dist-info → camel_ai-0.1.5.5.dist-info}/METADATA +11 -3
  42. {camel_ai-0.1.5.4.dist-info → camel_ai-0.1.5.5.dist-info}/RECORD +43 -36
  43. {camel_ai-0.1.5.4.dist-info → camel_ai-0.1.5.5.dist-info}/WHEEL +0 -0
@@ -16,6 +16,7 @@ from hashlib import md5
16
16
  from typing import Any, Dict, List, Optional
17
17
 
18
18
  from camel.storages.graph_storages import BaseGraphStorage, GraphElement
19
+ from camel.utils import dependencies_required
19
20
 
20
21
  logger = logging.getLogger(__name__)
21
22
 
@@ -81,6 +82,7 @@ class Neo4jGraph(BaseGraphStorage):
81
82
  than `LIST_LIMIT` elements from results. Defaults to `False`.
82
83
  """
83
84
 
85
+ @dependencies_required('neo4j')
84
86
  def __init__(
85
87
  self,
86
88
  url: str,
@@ -91,13 +93,7 @@ class Neo4jGraph(BaseGraphStorage):
91
93
  truncate: bool = False,
92
94
  ) -> None:
93
95
  r"""Create a new Neo4j graph instance."""
94
- try:
95
- import neo4j
96
- except ImportError:
97
- raise ValueError(
98
- "Could not import neo4j python package. "
99
- "Please install it with `pip install neo4j`."
100
- )
96
+ import neo4j
101
97
 
102
98
  self.driver = neo4j.GraphDatabase.driver(
103
99
  url, auth=(username, password)
@@ -15,9 +15,11 @@
15
15
  from .base import BaseKeyValueStorage
16
16
  from .in_memory import InMemoryKeyValueStorage
17
17
  from .json import JsonStorage
18
+ from .redis import RedisStorage
18
19
 
19
20
  __all__ = [
20
21
  'BaseKeyValueStorage',
21
22
  'InMemoryKeyValueStorage',
22
23
  'JsonStorage',
24
+ 'RedisStorage',
23
25
  ]
@@ -0,0 +1,169 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+
15
+ import asyncio
16
+ import json
17
+ import logging
18
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
19
+
20
+ from camel.storages.key_value_storages import BaseKeyValueStorage
21
+
22
+ if TYPE_CHECKING:
23
+ from redis.asyncio import Redis
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class RedisStorage(BaseKeyValueStorage):
29
+ r"""A concrete implementation of the :obj:`BaseCacheStorage` using Redis as
30
+ the backend. This is suitable for distributed cache systems that require
31
+ persistence and high availability.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ sid: str,
37
+ url: str = "redis://localhost:6379",
38
+ loop: Optional[asyncio.AbstractEventLoop] = None,
39
+ **kwargs,
40
+ ) -> None:
41
+ r"""Initializes the RedisStorage instance with the provided URL and
42
+ options.
43
+
44
+ Args:
45
+ sid (str): The ID for the storage instance to identify the
46
+ record space.
47
+ url (str): The URL for connecting to the Redis server.
48
+ **kwargs: Additional keyword arguments for Redis client
49
+ configuration.
50
+
51
+ Raises:
52
+ ImportError: If the `redis.asyncio` module is not installed.
53
+ """
54
+ try:
55
+ import redis.asyncio as aredis
56
+ except ImportError as exc:
57
+ logger.error(
58
+ "Please install `redis` first. You can install it by "
59
+ "running `pip install redis`."
60
+ )
61
+ raise exc
62
+
63
+ self._client: Optional[aredis.Redis] = None
64
+ self._url = url
65
+ self._sid = sid
66
+ self._loop = loop or asyncio.get_event_loop()
67
+
68
+ self._create_client(**kwargs)
69
+
70
+ def __enter__(self):
71
+ return self
72
+
73
+ def __exit__(self, exc_type, exc, tb):
74
+ self._run_async(self.close())
75
+
76
+ async def close(self) -> None:
77
+ r"""Closes the Redis client asynchronously."""
78
+ if self._client:
79
+ await self._client.close()
80
+
81
+ def _create_client(self, **kwargs) -> None:
82
+ r"""Creates the Redis client with the provided URL and options.
83
+
84
+ Args:
85
+ **kwargs: Additional keyword arguments for Redis client
86
+ configuration.
87
+ """
88
+ import redis.asyncio as aredis
89
+
90
+ self._client = aredis.from_url(self._url, **kwargs)
91
+
92
+ @property
93
+ def client(self) -> Optional["Redis"]:
94
+ r"""Returns the Redis client instance.
95
+
96
+ Returns:
97
+ redis.asyncio.Redis: The Redis client instance.
98
+ """
99
+ return self._client
100
+
101
+ def save(
102
+ self, records: List[Dict[str, Any]], expire: Optional[int] = None
103
+ ) -> None:
104
+ r"""Saves a batch of records to the key-value storage system."""
105
+ try:
106
+ self._run_async(self._async_save(records, expire))
107
+ except Exception as e:
108
+ logger.error(f"Error in save: {e}")
109
+
110
+ def load(self) -> List[Dict[str, Any]]:
111
+ r"""Loads all stored records from the key-value storage system.
112
+
113
+ Returns:
114
+ List[Dict[str, Any]]: A list of dictionaries, where each dictionary
115
+ represents a stored record.
116
+ """
117
+ try:
118
+ return self._run_async(self._async_load())
119
+ except Exception as e:
120
+ logger.error(f"Error in load: {e}")
121
+ return []
122
+
123
+ def clear(self) -> None:
124
+ r"""Removes all records from the key-value storage system."""
125
+ try:
126
+ self._run_async(self._async_clear())
127
+ except Exception as e:
128
+ logger.error(f"Error in clear: {e}")
129
+
130
+ async def _async_save(
131
+ self, records: List[Dict[str, Any]], expire: Optional[int] = None
132
+ ) -> None:
133
+ if self._client is None:
134
+ raise ValueError("Redis client is not initialized")
135
+ try:
136
+ value = json.dumps(records)
137
+ if expire:
138
+ await self._client.setex(self._sid, expire, value)
139
+ else:
140
+ await self._client.set(self._sid, value)
141
+ except Exception as e:
142
+ logger.error(f"Error saving records: {e}")
143
+
144
+ async def _async_load(self) -> List[Dict[str, Any]]:
145
+ if self._client is None:
146
+ raise ValueError("Redis client is not initialized")
147
+ try:
148
+ value = await self._client.get(self._sid)
149
+ if value:
150
+ return json.loads(value)
151
+ return []
152
+ except Exception as e:
153
+ logger.error(f"Error loading records: {e}")
154
+ return []
155
+
156
+ async def _async_clear(self) -> None:
157
+ if self._client is None:
158
+ raise ValueError("Redis client is not initialized")
159
+ try:
160
+ await self._client.delete(self._sid)
161
+ except Exception as e:
162
+ logger.error(f"Error clearing records: {e}")
163
+
164
+ def _run_async(self, coro):
165
+ if not self._loop.is_running():
166
+ return self._loop.run_until_complete(coro)
167
+ else:
168
+ future = asyncio.run_coroutine_threadsafe(coro, self._loop)
169
+ return future.result()
@@ -23,6 +23,7 @@ from camel.storages.vectordb_storages import (
23
23
  VectorDBStatus,
24
24
  VectorRecord,
25
25
  )
26
+ from camel.utils import dependencies_required
26
27
 
27
28
  logger = logging.getLogger(__name__)
28
29
 
@@ -52,6 +53,7 @@ class MilvusStorage(BaseVectorStorage):
52
53
  ImportError: If `pymilvus` package is not installed.
53
54
  """
54
55
 
56
+ @dependencies_required('pymilvus')
55
57
  def __init__(
56
58
  self,
57
59
  vector_dim: int,
@@ -59,13 +61,7 @@ class MilvusStorage(BaseVectorStorage):
59
61
  collection_name: Optional[str] = None,
60
62
  **kwargs: Any,
61
63
  ) -> None:
62
- try:
63
- from pymilvus import MilvusClient
64
- except ImportError as exc:
65
- raise ImportError(
66
- "Please install `pymilvus` first. You can install it by "
67
- "running `pip install pymilvus`."
68
- ) from exc
64
+ from pymilvus import MilvusClient
69
65
 
70
66
  self._client: MilvusClient
71
67
  self._create_client(url_and_api_key, **kwargs)
@@ -23,6 +23,7 @@ from camel.storages.vectordb_storages import (
23
23
  VectorRecord,
24
24
  )
25
25
  from camel.types import VectorDistance
26
+ from camel.utils import dependencies_required
26
27
 
27
28
  _qdrant_local_client_map: Dict[str, Tuple[Any, int]] = {}
28
29
 
@@ -62,6 +63,7 @@ class QdrantStorage(BaseVectorStorage):
62
63
  be initialized with an in-memory storage (`":memory:"`).
63
64
  """
64
65
 
66
+ @dependencies_required('qdrant_client')
65
67
  def __init__(
66
68
  self,
67
69
  vector_dim: int,
@@ -72,13 +74,7 @@ class QdrantStorage(BaseVectorStorage):
72
74
  delete_collection_on_del: bool = False,
73
75
  **kwargs: Any,
74
76
  ) -> None:
75
- try:
76
- from qdrant_client import QdrantClient
77
- except ImportError as exc:
78
- raise ImportError(
79
- "Please install `qdrant-client` first. You can install it by "
80
- "running `pip install qdrant-client`."
81
- ) from exc
77
+ from qdrant_client import QdrantClient
82
78
 
83
79
  self._client: QdrantClient
84
80
  self._local_path: Optional[str] = None
@@ -13,9 +13,11 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
 
15
15
  from .base import BaseToolkit
16
+ from .code_execution import CodeExecutionToolkit
16
17
  from .github_toolkit import GithubToolkit
17
18
 
18
19
  __all__ = [
19
20
  'BaseToolkit',
20
21
  'GithubToolkit',
22
+ 'CodeExecutionToolkit',
21
23
  ]
@@ -0,0 +1,69 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ from typing import List, Literal
15
+
16
+ from camel.functions import OpenAIFunction
17
+ from camel.interpreters import InternalPythonInterpreter
18
+
19
+ from .base import BaseToolkit
20
+
21
+
22
+ class CodeExecutionToolkit(BaseToolkit):
23
+ r"""A tookit for code execution.
24
+
25
+ Args:
26
+ sandbox (str): the environment type used to execute code.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ sandbox: Literal[
32
+ "internal_python", "jupyter", "docker"
33
+ ] = "internal_python",
34
+ verbose: bool = False,
35
+ ) -> None:
36
+ # TODO: Add support for docker and jupyter.
37
+ self.verbose = verbose
38
+ if sandbox == "internal_python":
39
+ self.interpreter = InternalPythonInterpreter()
40
+ else:
41
+ raise RuntimeError(
42
+ f"The sandbox type `{sandbox}` is not supported."
43
+ )
44
+
45
+ def execute_code(self, code: str) -> str:
46
+ r"""Execute a given code snippet.
47
+
48
+ Args:
49
+ code (str): The input code to the Code Interpreter tool call.
50
+
51
+ Returns:
52
+ str: The text output from the Code Interpreter tool call.
53
+ """
54
+ output = self.interpreter.run(code, "python")
55
+ # ruff: noqa: E501
56
+ content = f"Executed the code below:\n```py\n{code}\n```\n> Executed Results:\n{output}"
57
+ if self.verbose:
58
+ print(content)
59
+ return content
60
+
61
+ def get_tools(self) -> List[OpenAIFunction]:
62
+ r"""Returns a list of OpenAIFunction objects representing the
63
+ functions in the toolkit.
64
+
65
+ Returns:
66
+ List[OpenAIFunction]: A list of OpenAIFunction objects
67
+ representing the functions in the toolkit.
68
+ """
69
+ return [OpenAIFunction(self.execute_code)]
@@ -18,8 +18,8 @@ from datetime import datetime, timedelta
18
18
  from typing import List, Optional
19
19
 
20
20
  from camel.functions import OpenAIFunction
21
-
22
- from .base import BaseToolkit
21
+ from camel.toolkits.base import BaseToolkit
22
+ from camel.utils import dependencies_required
23
23
 
24
24
 
25
25
  @dataclass
@@ -130,6 +130,7 @@ class GithubToolkit(BaseToolkit):
130
130
  `get_github_access_token` method.
131
131
  """
132
132
 
133
+ @dependencies_required('github')
133
134
  def __init__(
134
135
  self, repo_name: str, access_token: Optional[str] = None
135
136
  ) -> None:
@@ -144,13 +145,8 @@ class GithubToolkit(BaseToolkit):
144
145
  if access_token is None:
145
146
  access_token = self.get_github_access_token()
146
147
 
147
- try:
148
- from github import Auth, Github
149
- except ImportError:
150
- raise ImportError(
151
- "Please install `github` first. You can install it by running "
152
- "`pip install pygithub`."
153
- )
148
+ from github import Auth, Github
149
+
154
150
  self.github = Github(auth=Auth.Token(access_token))
155
151
  self.repo = self.github.get_repo(repo_name)
156
152
 
camel/types/enums.py CHANGED
@@ -30,15 +30,19 @@ class ModelType(Enum):
30
30
  GPT_4_TURBO = "gpt-4-turbo"
31
31
  GPT_4O = "gpt-4o"
32
32
  GLM_4 = "glm-4"
33
+ GLM_4_OPEN_SOURCE = "glm-4-open-source"
33
34
  GLM_4V = 'glm-4v'
34
35
  GLM_3_TURBO = "glm-3-turbo"
35
36
 
36
37
  STUB = "stub"
37
38
 
38
39
  LLAMA_2 = "llama-2"
40
+ LLAMA_3 = "llama-3"
39
41
  VICUNA = "vicuna"
40
42
  VICUNA_16K = "vicuna-16k"
41
43
 
44
+ QWEN_2 = "qwen-2"
45
+
42
46
  # Legacy anthropic models
43
47
  # NOTE: anthropic lagecy models only Claude 2.1 has system prompt support
44
48
  CLAUDE_2_1 = "claude-2.1"
@@ -87,6 +91,9 @@ class ModelType(Enum):
87
91
  r"""Returns whether this type of models is open-source."""
88
92
  return self in {
89
93
  ModelType.LLAMA_2,
94
+ ModelType.LLAMA_3,
95
+ ModelType.QWEN_2,
96
+ ModelType.GLM_4_OPEN_SOURCE,
90
97
  ModelType.VICUNA,
91
98
  ModelType.VICUNA_16K,
92
99
  }
@@ -135,7 +142,7 @@ class ModelType(Enum):
135
142
  return 128000
136
143
  elif self is ModelType.GPT_4O:
137
144
  return 128000
138
- elif self == ModelType.GLM_4:
145
+ elif self == ModelType.GLM_4_OPEN_SOURCE:
139
146
  return 8192
140
147
  elif self == ModelType.GLM_3_TURBO:
141
148
  return 8192
@@ -145,6 +152,12 @@ class ModelType(Enum):
145
152
  return 4096
146
153
  elif self is ModelType.LLAMA_2:
147
154
  return 4096
155
+ elif self is ModelType.LLAMA_3:
156
+ return 8192
157
+ elif self is ModelType.QWEN_2:
158
+ return 128000
159
+ elif self is ModelType.GLM_4:
160
+ return 8192
148
161
  elif self is ModelType.VICUNA:
149
162
  # reference: https://lmsys.org/blog/2023-03-30-vicuna/
150
163
  return 2048
@@ -184,6 +197,20 @@ class ModelType(Enum):
184
197
  self.value in model_name.lower()
185
198
  or "llama2" in model_name.lower()
186
199
  )
200
+ elif self is ModelType.LLAMA_3:
201
+ return (
202
+ self.value in model_name.lower()
203
+ or "llama3" in model_name.lower()
204
+ )
205
+ elif self is ModelType.QWEN_2:
206
+ return (
207
+ self.value in model_name.lower()
208
+ or "qwen2" in model_name.lower()
209
+ )
210
+ elif self is ModelType.GLM_4_OPEN_SOURCE:
211
+ return (
212
+ 'glm-4' in model_name.lower() or "glm4" in model_name.lower()
213
+ )
187
214
  else:
188
215
  return self.value in model_name.lower()
189
216
 
@@ -374,3 +401,10 @@ class VoiceType(Enum):
374
401
  VoiceType.NOVA,
375
402
  VoiceType.SHIMMER,
376
403
  }
404
+
405
+
406
+ class JinaReturnFormat(Enum):
407
+ DEFAULT = None
408
+ MARKDOWN = "markdown"
409
+ HTML = "html"
410
+ TEXT = "text"
camel/utils/__init__.py CHANGED
@@ -23,7 +23,7 @@ from .commons import (
23
23
  get_prompt_template_key_words,
24
24
  get_system_information,
25
25
  get_task_list,
26
- model_api_key_required,
26
+ is_docker_running,
27
27
  print_text_animated,
28
28
  text_extract_from_web,
29
29
  to_pascal,
@@ -39,7 +39,6 @@ from .token_counting import (
39
39
  )
40
40
 
41
41
  __all__ = [
42
- 'model_api_key_required',
43
42
  'print_text_animated',
44
43
  'get_prompt_template_key_words',
45
44
  'get_first_int',
@@ -60,4 +59,5 @@ __all__ = [
60
59
  'create_chunks',
61
60
  'dependencies_required',
62
61
  'api_keys_required',
62
+ 'is_docker_running',
63
63
  ]
@@ -0,0 +1,42 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ import asyncio
15
+ from copy import deepcopy
16
+
17
+ from camel.functions.openai_function import OpenAIFunction
18
+
19
+
20
+ def sync_funcs_to_async(funcs: list[OpenAIFunction]) -> list[OpenAIFunction]:
21
+ r"""Convert a list of Python synchronous functions to Python
22
+ asynchronous functions.
23
+
24
+ Args:
25
+ funcs (list[OpenAIFunction]): List of Python synchronous
26
+ functions in the :obj:`OpenAIFunction` format.
27
+
28
+ Returns:
29
+ list[OpenAIFunction]: List of Python asynchronous functions
30
+ in the :obj:`OpenAIFunction` format.
31
+ """
32
+ async_funcs = []
33
+ for func in funcs:
34
+ sync_func = func.func
35
+
36
+ def async_callable(*args, **kwargs):
37
+ return asyncio.to_thread(sync_func, *args, **kwargs) # noqa: B023
38
+
39
+ async_funcs.append(
40
+ OpenAIFunction(async_callable, deepcopy(func.openai_tool_schema))
41
+ )
42
+ return async_funcs
camel/utils/commons.py CHANGED
@@ -16,6 +16,7 @@ import os
16
16
  import platform
17
17
  import re
18
18
  import socket
19
+ import subprocess
19
20
  import time
20
21
  import zipfile
21
22
  from functools import wraps
@@ -30,48 +31,6 @@ from camel.types import TaskType
30
31
  F = TypeVar('F', bound=Callable[..., Any])
31
32
 
32
33
 
33
- def model_api_key_required(func: F) -> F:
34
- r"""Decorator that checks if the API key is available either as an
35
- environment variable or passed directly for a model.
36
-
37
- Args:
38
- func (callable): The function to be wrapped.
39
-
40
- Returns:
41
- callable: The decorated function.
42
-
43
- Raises:
44
- ValueError: If the API key is not found, either as an environment
45
- variable or directly passed.
46
-
47
- Note:
48
- Supported model type: `OpenAI` and `Anthropic`.
49
- """
50
-
51
- @wraps(func)
52
- def wrapper(self, *args, **kwargs):
53
- if self.model_type.is_openai:
54
- if not self._api_key and 'OPENAI_API_KEY' not in os.environ:
55
- raise ValueError('OpenAI API key not found.')
56
- return func(self, *args, **kwargs)
57
- elif self.model_type.is_zhipuai:
58
- if 'ZHIPUAI_API_KEY' not in os.environ:
59
- raise ValueError('ZhiPuAI API key not found.')
60
- return func(self, *args, **kwargs)
61
- elif self.model_type.is_anthropic:
62
- if not self._api_key and 'ANTHROPIC_API_KEY' not in os.environ:
63
- raise ValueError('Anthropic API key not found.')
64
- return func(self, *args, **kwargs)
65
- elif self.model_type.is_nvidia:
66
- if not self._api_key and 'NVIDIA_API_KEY' not in os.environ:
67
- raise ValueError('NVIDIA API key not found.')
68
- return func(self, *args, **kwargs)
69
- else:
70
- raise ValueError('Unsupported model type.')
71
-
72
- return cast(F, wrapper)
73
-
74
-
75
34
  def print_text_animated(text, delay: float = 0.02, end: str = ""):
76
35
  r"""Prints the given text with an animated effect.
77
36
 
@@ -260,7 +219,7 @@ def is_module_available(module_name: str) -> bool:
260
219
 
261
220
  def api_keys_required(*required_keys: str) -> Callable[[F], F]:
262
221
  r"""A decorator to check if the required API keys are
263
- present in the environment variables.
222
+ presented in the environment variables or as an instance attribute.
264
223
 
265
224
  Args:
266
225
  required_keys (str): The required API keys to be checked.
@@ -271,7 +230,7 @@ def api_keys_required(*required_keys: str) -> Callable[[F], F]:
271
230
 
272
231
  Raises:
273
232
  ValueError: If any of the required API keys are missing in the
274
- environment variables.
233
+ environment variables and the instance attribute.
275
234
 
276
235
  Example:
277
236
  ::
@@ -283,13 +242,18 @@ def api_keys_required(*required_keys: str) -> Callable[[F], F]:
283
242
 
284
243
  def decorator(func: F) -> F:
285
244
  @wraps(func)
286
- def wrapper(*args: Any, **kwargs: Any) -> Any:
287
- missing_keys = [k for k in required_keys if k not in os.environ]
288
- if missing_keys:
245
+ def wrapper(self, *args: Any, **kwargs: Any) -> Any:
246
+ missing_environment_keys = [
247
+ k for k in required_keys if k not in os.environ
248
+ ]
249
+ if (
250
+ not getattr(self, '_api_key', None)
251
+ and missing_environment_keys
252
+ ):
289
253
  raise ValueError(
290
- f"Missing API keys: {', '.join(missing_keys)}"
254
+ f"Missing API keys: {', '.join(missing_environment_keys)}"
291
255
  )
292
- return func(*args, **kwargs)
256
+ return func(self, *args, **kwargs)
293
257
 
294
258
  return cast(F, wrapper)
295
259
 
@@ -400,3 +364,21 @@ def create_chunks(text: str, n: int) -> List[str]:
400
364
  chunks.append(text[i:j])
401
365
  i = j
402
366
  return chunks
367
+
368
+
369
+ def is_docker_running() -> bool:
370
+ r"""Check if the Docker daemon is running.
371
+
372
+ Returns:
373
+ bool: True if the Docker daemon is running, False otherwise.
374
+ """
375
+ try:
376
+ result = subprocess.run(
377
+ ["docker", "info"],
378
+ check=True,
379
+ stdout=subprocess.PIPE,
380
+ stderr=subprocess.PIPE,
381
+ )
382
+ return result.returncode == 0
383
+ except (subprocess.CalledProcessError, FileNotFoundError):
384
+ return False