camel-ai 0.2.42__py3-none-any.whl → 0.2.44__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 +3 -0
  3. camel/configs/anthropic_config.py +2 -24
  4. camel/configs/ppio_config.py +102 -0
  5. camel/configs/reka_config.py +1 -7
  6. camel/configs/samba_config.py +1 -7
  7. camel/configs/togetherai_config.py +1 -7
  8. camel/datasets/few_shot_generator.py +1 -0
  9. camel/embeddings/__init__.py +4 -0
  10. camel/embeddings/azure_embedding.py +119 -0
  11. camel/embeddings/together_embedding.py +136 -0
  12. camel/environments/__init__.py +3 -0
  13. camel/environments/multi_step.py +12 -10
  14. camel/environments/single_step.py +14 -2
  15. camel/environments/tic_tac_toe.py +518 -0
  16. camel/extractors/python_strategies.py +14 -5
  17. camel/loaders/__init__.py +2 -0
  18. camel/loaders/crawl4ai_reader.py +230 -0
  19. camel/models/__init__.py +2 -0
  20. camel/models/azure_openai_model.py +10 -2
  21. camel/models/base_model.py +111 -28
  22. camel/models/cohere_model.py +5 -1
  23. camel/models/deepseek_model.py +4 -0
  24. camel/models/gemini_model.py +8 -2
  25. camel/models/model_factory.py +3 -0
  26. camel/models/ollama_model.py +8 -2
  27. camel/models/openai_compatible_model.py +8 -2
  28. camel/models/openai_model.py +16 -4
  29. camel/models/ppio_model.py +184 -0
  30. camel/models/togetherai_model.py +106 -31
  31. camel/models/vllm_model.py +140 -57
  32. camel/societies/workforce/workforce.py +26 -3
  33. camel/toolkits/__init__.py +2 -0
  34. camel/toolkits/browser_toolkit.py +11 -3
  35. camel/toolkits/google_calendar_toolkit.py +432 -0
  36. camel/toolkits/search_toolkit.py +119 -1
  37. camel/types/enums.py +74 -3
  38. camel/types/unified_model_type.py +5 -0
  39. camel/verifiers/python_verifier.py +93 -9
  40. {camel_ai-0.2.42.dist-info → camel_ai-0.2.44.dist-info}/METADATA +21 -2
  41. {camel_ai-0.2.42.dist-info → camel_ai-0.2.44.dist-info}/RECORD +43 -36
  42. {camel_ai-0.2.42.dist-info → camel_ai-0.2.44.dist-info}/WHEEL +0 -0
  43. {camel_ai-0.2.42.dist-info → camel_ai-0.2.44.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  from camel.logger import disable_logging, enable_logging, set_log_level
16
16
 
17
- __version__ = '0.2.42'
17
+ __version__ = '0.2.44'
18
18
 
19
19
  __all__ = [
20
20
  '__version__',
camel/configs/__init__.py CHANGED
@@ -27,6 +27,7 @@ from .nvidia_config import NVIDIA_API_PARAMS, NvidiaConfig
27
27
  from .ollama_config import OLLAMA_API_PARAMS, OllamaConfig
28
28
  from .openai_config import OPENAI_API_PARAMS, ChatGPTConfig
29
29
  from .openrouter_config import OPENROUTER_API_PARAMS, OpenRouterConfig
30
+ from .ppio_config import PPIO_API_PARAMS, PPIOConfig
30
31
  from .qwen_config import QWEN_API_PARAMS, QwenConfig
31
32
  from .reka_config import REKA_API_PARAMS, RekaConfig
32
33
  from .samba_config import (
@@ -82,6 +83,8 @@ __all__ = [
82
83
  'QWEN_API_PARAMS',
83
84
  'DeepSeekConfig',
84
85
  'DEEPSEEK_API_PARAMS',
86
+ 'PPIOConfig',
87
+ 'PPIO_API_PARAMS',
85
88
  'InternLMConfig',
86
89
  'INTERNLM_API_PARAMS',
87
90
  'MoonshotConfig',
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Any, List, Optional
16
+ from typing import List, Optional
17
17
 
18
18
  from camel.configs.base_config import BaseConfig
19
19
 
@@ -23,6 +23,7 @@ class AnthropicConfig(BaseConfig):
23
23
  Anthropic API.
24
24
 
25
25
  See: https://docs.anthropic.com/en/api/messages
26
+
26
27
  Args:
27
28
  max_tokens (int, optional): The maximum number of tokens to
28
29
  generate before stopping. Note that Anthropic models may stop
@@ -75,28 +76,5 @@ class AnthropicConfig(BaseConfig):
75
76
  thinking: Optional[dict] = None
76
77
  tool_choice: Optional[dict] = None
77
78
 
78
- def as_dict(self) -> dict[str, Any]:
79
- config_dict = super().as_dict()
80
- # Create a list of keys to remove to avoid modifying dict
81
- keys_to_remove = [
82
- key for key, value in config_dict.items() if value is None
83
- ]
84
-
85
- for key in keys_to_remove:
86
- del config_dict[key]
87
-
88
- # remove some keys if thinking is enabled
89
- thinking_enabled = (
90
- self.thinking is not None
91
- and self.thinking.get("type") == "enabled"
92
- )
93
- if thinking_enabled:
94
- # `top_p`, `top_k`, `temperature` must be unset when thinking is
95
- # enabled.
96
- config_dict.pop("top_k", None)
97
- config_dict.pop("top_p", None)
98
- config_dict.pop("temperature", None)
99
- return config_dict
100
-
101
79
 
102
80
  ANTHROPIC_API_PARAMS = {param for param in AnthropicConfig.model_fields.keys()}
@@ -0,0 +1,102 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ from __future__ import annotations
15
+
16
+ from typing import Dict, Optional, Sequence, Type, Union
17
+
18
+ from pydantic import BaseModel
19
+
20
+ from camel.configs.base_config import BaseConfig
21
+
22
+
23
+ class PPIOConfig(BaseConfig):
24
+ r"""Defines the parameters for generating chat completions using the
25
+ OpenAI API.
26
+
27
+ Args:
28
+ temperature (float, optional): Sampling temperature to use, between
29
+ :obj:`0` and :obj:`2`. Higher values make the output more random,
30
+ while lower values make it more focused and deterministic.
31
+ (default: :obj:`None`)
32
+ top_p (float, optional): An alternative to sampling with temperature,
33
+ called nucleus sampling, where the model considers the results of
34
+ the tokens with top_p probability mass. So :obj:`0.1` means only
35
+ the tokens comprising the top 10% probability mass are considered.
36
+ (default: :obj:`None`)
37
+ n (int, optional): How many chat completion choices to generate for
38
+ each input message. (default: :obj:`None`)
39
+ response_format (object, optional): An object specifying the format
40
+ that the model must output. Compatible with GPT-4 Turbo and all
41
+ GPT-3.5 Turbo models newer than gpt-3.5-turbo-1106. Setting to
42
+ {"type": "json_object"} enables JSON mode, which guarantees the
43
+ message the model generates is valid JSON. Important: when using
44
+ JSON mode, you must also instruct the model to produce JSON
45
+ yourself via a system or user message. Without this, the model
46
+ may generate an unending stream of whitespace until the generation
47
+ reaches the token limit, resulting in a long-running and seemingly
48
+ "stuck" request. Also note that the message content may be
49
+ partially cut off if finish_reason="length", which indicates the
50
+ generation exceeded max_tokens or the conversation exceeded the
51
+ max context length.
52
+ stream (bool, optional): If True, partial message deltas will be sent
53
+ as data-only server-sent events as they become available.
54
+ (default: :obj:`None`)
55
+ stop (str or list, optional): Up to :obj:`4` sequences where the API
56
+ will stop generating further tokens. (default: :obj:`None`)
57
+ max_tokens (int, optional): The maximum number of tokens to generate
58
+ in the chat completion. The total length of input tokens and
59
+ generated tokens is limited by the model's context length.
60
+ (default: :obj:`None`)
61
+ presence_penalty (float, optional): Number between :obj:`-2.0` and
62
+ :obj:`2.0`. Positive values penalize new tokens based on whether
63
+ they appear in the text so far, increasing the model's likelihood
64
+ to talk about new topics. See more information about frequency and
65
+ presence penalties. (default: :obj:`None`)
66
+ frequency_penalty (float, optional): Number between :obj:`-2.0` and
67
+ :obj:`2.0`. Positive values penalize new tokens based on their
68
+ existing frequency in the text so far, decreasing the model's
69
+ likelihood to repeat the same line verbatim. See more information
70
+ about frequency and presence penalties. (default: :obj:`None`)
71
+ logit_bias (dict, optional): Modify the likelihood of specified tokens
72
+ appearing in the completion. Accepts a json object that maps tokens
73
+ (specified by their token ID in the tokenizer) to an associated
74
+ bias value from :obj:`-100` to :obj:`100`. Mathematically, the bias
75
+ is added to the logits generated by the model prior to sampling.
76
+ The exact effect will vary per model, but values between:obj:` -1`
77
+ and :obj:`1` should decrease or increase likelihood of selection;
78
+ values like :obj:`-100` or :obj:`100` should result in a ban or
79
+ exclusive selection of the relevant token. (default: :obj:`None`)
80
+ user (str, optional): A unique identifier representing your end-user,
81
+ which can help OpenAI to monitor and detect abuse.
82
+ (default: :obj:`None`)
83
+ tools (list[FunctionTool], optional): A list of tools the model may
84
+ call. Currently, only functions are supported as a tool. Use this
85
+ to provide a list of functions the model may generate JSON inputs
86
+ for. A max of 128 functions are supported.
87
+ """
88
+
89
+ temperature: Optional[float] = None
90
+ top_p: Optional[float] = None
91
+ n: Optional[int] = None
92
+ stream: Optional[bool] = None
93
+ stop: Optional[Union[str, Sequence[str]]] = None
94
+ max_tokens: Optional[int] = None
95
+ presence_penalty: Optional[float] = None
96
+ response_format: Optional[Union[Type[BaseModel], Dict]] = None
97
+ frequency_penalty: Optional[float] = None
98
+ logit_bias: Optional[Dict] = None
99
+ user: Optional[str] = None
100
+
101
+
102
+ PPIO_API_PARAMS = {param for param in PPIOConfig.model_fields.keys()}
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Any, Optional, Union
16
+ from typing import Optional, Union
17
17
 
18
18
  from camel.configs.base_config import BaseConfig
19
19
 
@@ -65,11 +65,5 @@ class RekaConfig(BaseConfig):
65
65
  presence_penalty: Optional[float] = None
66
66
  use_search_engine: Optional[bool] = None
67
67
 
68
- def as_dict(self) -> dict[str, Any]:
69
- config_dict = super().as_dict()
70
- if "tools" in config_dict:
71
- del config_dict["tools"] # Reka does not support tool calling
72
- return config_dict
73
-
74
68
 
75
69
  REKA_API_PARAMS = {param for param in RekaConfig().model_fields.keys()}
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Any, Optional, Sequence, Union
16
+ from typing import Optional, Sequence, Union
17
17
 
18
18
  from pydantic import Field
19
19
 
@@ -63,12 +63,6 @@ class SambaVerseAPIConfig(BaseConfig):
63
63
  stop: Optional[Union[str, list[str]]] = None
64
64
  stream: Optional[bool] = None
65
65
 
66
- def as_dict(self) -> dict[str, Any]:
67
- config_dict = super().as_dict()
68
- if "tools" in config_dict:
69
- del config_dict["tools"] # SambaNova does not support tool calling
70
- return config_dict
71
-
72
66
 
73
67
  SAMBA_VERSE_API_PARAMS = {
74
68
  param for param in SambaVerseAPIConfig().model_fields.keys()
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import Any, Optional, Sequence, Union
16
+ from typing import Optional, Sequence, Union
17
17
 
18
18
  from pydantic import Field
19
19
 
@@ -94,12 +94,6 @@ class TogetherAIConfig(BaseConfig):
94
94
  logit_bias: dict = Field(default_factory=dict)
95
95
  user: Optional[str] = None
96
96
 
97
- def as_dict(self) -> dict[str, Any]:
98
- config_dict = super().as_dict()
99
- if "tools" in config_dict:
100
- del config_dict["tools"] # Currently does not support tool calling
101
- return config_dict
102
-
103
97
 
104
98
  TOGETHERAI_API_PARAMS = {
105
99
  param for param in TogetherAIConfig.model_fields.keys()
@@ -235,6 +235,7 @@ class FewShotGenerator(BaseGenerator):
235
235
  "synthetic": str(True),
236
236
  "created": datetime.now().isoformat(),
237
237
  "generator": "few_shot",
238
+ "shots": [e.to_dict() for e in examples],
238
239
  },
239
240
  )
240
241
  except ValidationError as e:
@@ -11,20 +11,24 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ from .azure_embedding import AzureEmbedding
14
15
  from .base import BaseEmbedding
15
16
  from .jina_embedding import JinaEmbedding
16
17
  from .mistral_embedding import MistralEmbedding
17
18
  from .openai_compatible_embedding import OpenAICompatibleEmbedding
18
19
  from .openai_embedding import OpenAIEmbedding
19
20
  from .sentence_transformers_embeddings import SentenceTransformerEncoder
21
+ from .together_embedding import TogetherEmbedding
20
22
  from .vlm_embedding import VisionLanguageEmbedding
21
23
 
22
24
  __all__ = [
23
25
  "BaseEmbedding",
24
26
  "OpenAIEmbedding",
27
+ "AzureEmbedding",
25
28
  "SentenceTransformerEncoder",
26
29
  "VisionLanguageEmbedding",
27
30
  "MistralEmbedding",
28
31
  "OpenAICompatibleEmbedding",
29
32
  "JinaEmbedding",
33
+ "TogetherEmbedding",
30
34
  ]
@@ -0,0 +1,119 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+
16
+ from __future__ import annotations
17
+
18
+ import os
19
+ from typing import Any, Union
20
+
21
+ from openai import AzureOpenAI
22
+
23
+ from camel.embeddings.base import BaseEmbedding
24
+ from camel.types import EmbeddingModelType
25
+ from camel.utils import api_keys_required # Add this import
26
+
27
+
28
+ class AzureEmbedding(BaseEmbedding[str]):
29
+ r"""Provides text embedding functionalities using Azure's OpenAI models.
30
+
31
+ Args:
32
+ model_type (EmbeddingModelType, optional): The model type to be
33
+ used for text embeddings.
34
+ (default: :obj:`TEXT_EMBEDDING_3_SMALL`)
35
+ url (Optional[str], optional): The url to the Azure OpenAI service.
36
+ (default: :obj:`None`)
37
+ api_key (str, optional): The API key for authenticating with the
38
+ Azure OpenAI service. (default: :obj:`None`)
39
+ api_version (str, optional): The API version for Azure OpenAI service.
40
+ (default: :obj:`None`)
41
+ dimensions (Optional[int], optional): The text embedding output
42
+ dimensions. (default: :obj:`None`)
43
+
44
+ Raises:
45
+ RuntimeError: If an unsupported model type is specified.
46
+ ValueError: If required API configuration is missing.
47
+ """
48
+
49
+ @api_keys_required(
50
+ [
51
+ ("api_key", 'AZURE_OPENAI_API_KEY'),
52
+ ("url", 'AZURE_OPENAI_BASE_URL'),
53
+ ]
54
+ )
55
+ def __init__(
56
+ self,
57
+ model_type: EmbeddingModelType = (
58
+ EmbeddingModelType.TEXT_EMBEDDING_3_SMALL
59
+ ),
60
+ url: Union[str, None] = None,
61
+ api_key: Union[str, None] = None,
62
+ api_version: Union[str, None] = None,
63
+ dimensions: Union[int, None] = None,
64
+ ) -> None:
65
+ self.model_type = model_type
66
+ self.api_version = api_version or os.environ.get("AZURE_API_VERSION")
67
+ if dimensions is None:
68
+ self.output_dim = model_type.output_dim
69
+ else:
70
+ if not isinstance(dimensions, int):
71
+ raise ValueError("dimensions must be an integer")
72
+ self.output_dim = dimensions
73
+
74
+ self._api_key = api_key or os.environ.get("AZURE_OPENAI_API_KEY")
75
+ self._url = url or os.environ.get("AZURE_OPENAI_BASE_URL")
76
+
77
+ self.client = AzureOpenAI(
78
+ api_key=self._api_key,
79
+ api_version=self.api_version,
80
+ azure_endpoint=str(self._url),
81
+ )
82
+
83
+ def embed_list(
84
+ self,
85
+ objs: list[str],
86
+ **kwargs: Any,
87
+ ) -> list[list[float]]:
88
+ r"""Embeds a list of texts using the Azure OpenAI model.
89
+
90
+ Args:
91
+ objs (list[str]): The list of texts to embed.
92
+ **kwargs (Any): Additional keyword arguments to pass to the API.
93
+
94
+ Returns:
95
+ list[list[float]]: The embeddings for the input texts.
96
+ """
97
+ if self.model_type == EmbeddingModelType.TEXT_EMBEDDING_ADA_2:
98
+ response = self.client.embeddings.create(
99
+ input=objs,
100
+ model=self.model_type.value,
101
+ **kwargs,
102
+ )
103
+ return [data.embedding for data in response.data]
104
+
105
+ response = self.client.embeddings.create(
106
+ input=objs,
107
+ model=self.model_type.value,
108
+ dimensions=self.output_dim,
109
+ **kwargs,
110
+ )
111
+ return [data.embedding for data in response.data]
112
+
113
+ def get_output_dim(self) -> int:
114
+ r"""Returns the output dimension of the embeddings.
115
+
116
+ Returns:
117
+ int: The dimensionality of the embedding for the current model.
118
+ """
119
+ return self.output_dim
@@ -0,0 +1,136 @@
1
+ # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ import os
15
+ from typing import Any, Optional
16
+
17
+ from openai import OpenAI
18
+
19
+ from camel.embeddings.base import BaseEmbedding
20
+ from camel.logger import get_logger
21
+ from camel.utils import api_keys_required
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ class TogetherEmbedding(BaseEmbedding[str]):
27
+ r"""Provides text embedding functionalities using Together AI's models.
28
+
29
+ Args:
30
+ model_name (str, optional): The model name to be used for text
31
+ embeddings.
32
+ (default: :obj:`togethercomputer/m2-bert-80M-8k-retrieval`)
33
+ api_key (str, optional): The API key for authenticating with the
34
+ Together service. (default: :obj:`None`)
35
+ dimensions (int, optional): The text embedding output dimensions.
36
+ (default: :obj:`None`)
37
+
38
+ Raises:
39
+ ValueError: If the model name format is invalid or if an empty input
40
+ list is provided.
41
+ RuntimeError: If the API request fails.
42
+ """
43
+
44
+ @api_keys_required([("api_key", 'TOGETHER_API_KEY')])
45
+ def __init__(
46
+ self,
47
+ model_name: str = "togethercomputer/m2-bert-80M-8k-retrieval",
48
+ api_key: Optional[str] = None,
49
+ dimensions: Optional[int] = None,
50
+ ) -> None:
51
+ if not isinstance(model_name, str) or not model_name.strip():
52
+ raise ValueError("Model name must be a non-empty string")
53
+
54
+ if dimensions is not None and dimensions <= 0:
55
+ raise ValueError("Dimensions must be a positive integer")
56
+
57
+ self.model_name = model_name
58
+ self._api_key = api_key or os.environ.get("TOGETHER_API_KEY")
59
+ self.output_dim = dimensions
60
+
61
+ # Initialize OpenAI client with Together AI configuration
62
+ self.client = OpenAI(
63
+ timeout=180,
64
+ max_retries=3,
65
+ api_key=self._api_key,
66
+ base_url="https://api.together.xyz/v1",
67
+ )
68
+
69
+ def embed_list(
70
+ self,
71
+ objs: list[str],
72
+ **kwargs: Any,
73
+ ) -> list[list[float]]:
74
+ r"""Generates embeddings for the given texts.
75
+
76
+ Args:
77
+ objs (list[str]): The texts for which to generate the embeddings.
78
+ **kwargs (Any): Extra kwargs passed to the embedding API.
79
+
80
+ Returns:
81
+ list[list[float]]: A list that represents the generated embedding
82
+ as a list of floating-point numbers.
83
+
84
+ Raises:
85
+ ValueError: If the input list is empty.
86
+ RuntimeError: If the API request fails.
87
+ """
88
+ if not objs:
89
+ raise ValueError("Input list cannot be empty")
90
+
91
+ try:
92
+ response = self.client.embeddings.create(
93
+ input=objs,
94
+ model=self.model_name,
95
+ **kwargs,
96
+ )
97
+
98
+ # Set output dimension if not already set
99
+ if self.output_dim is None and response.data:
100
+ self.output_dim = len(response.data[0].embedding)
101
+ logger.debug(
102
+ f"Set output dimension to {self.output_dim} for model "
103
+ f"{self.model_name}"
104
+ )
105
+
106
+ return [data.embedding for data in response.data]
107
+
108
+ except Exception as e:
109
+ raise RuntimeError(
110
+ f"Failed to get embeddings from Together AI: {e}"
111
+ )
112
+
113
+ def get_output_dim(self) -> int:
114
+ r"""Returns the output dimension of the embeddings.
115
+
116
+ Returns:
117
+ int: The dimensionality of the embedding for the current model.
118
+
119
+ Raises:
120
+ ValueError: If the embedding dimension cannot be determined.
121
+ """
122
+ if self.output_dim is None:
123
+ logger.debug(
124
+ "Output dimension not set, "
125
+ "making test embedding to determine it"
126
+ )
127
+ # Make a test embedding to determine the dimension
128
+ self.embed_list(["test"])
129
+
130
+ if self.output_dim is None:
131
+ raise ValueError(
132
+ "Failed to determine embedding dimension for model: "
133
+ f"{self.model_name}"
134
+ )
135
+
136
+ return self.output_dim
@@ -14,6 +14,7 @@
14
14
  from .models import Action, Environment, Observation, StepResult
15
15
  from .multi_step import MultiStepEnv
16
16
  from .single_step import SingleStepEnv
17
+ from .tic_tac_toe import Opponent, TicTacToeEnv
17
18
 
18
19
  __all__ = [
19
20
  "Environment",
@@ -22,4 +23,6 @@ __all__ = [
22
23
  "Action",
23
24
  "Observation",
24
25
  "StepResult",
26
+ "TicTacToeEnv",
27
+ "Opponent",
25
28
  ]
@@ -15,11 +15,10 @@
15
15
  from abc import ABC, abstractmethod
16
16
  from typing import Any, Dict, List, Optional, Tuple
17
17
 
18
+ from camel.environments.models import Action, Observation, StepResult
18
19
  from camel.extractors.base import BaseExtractor
19
20
  from camel.logger import get_logger
20
21
 
21
- from .models import Action, Observation, StepResult
22
-
23
22
  logger = get_logger(__name__)
24
23
 
25
24
 
@@ -73,9 +72,8 @@ class MultiStepEnv(ABC):
73
72
  logger.error(f'Failed to setup environment: {e}')
74
73
  raise
75
74
 
76
- @abstractmethod
77
75
  async def _setup(self) -> None:
78
- pass
76
+ return
79
77
 
80
78
  async def close(self) -> None:
81
79
  r"""Clean up and close all resources used by the environment.
@@ -100,9 +98,8 @@ class MultiStepEnv(ABC):
100
98
  logger.error(f'Failed to teardown environment: {e}')
101
99
  raise
102
100
 
103
- @abstractmethod
104
101
  async def _close(self) -> None:
105
- pass
102
+ return
106
103
 
107
104
  async def reset(self) -> Observation:
108
105
  r"""Reset the environment to an initial state.
@@ -115,6 +112,9 @@ class MultiStepEnv(ABC):
115
112
  """
116
113
 
117
114
  if not self._is_setup:
115
+ logger.warning(
116
+ "reset() called on un-setup environment. Setting up..."
117
+ )
118
118
  await self.setup()
119
119
 
120
120
  # Reset state
@@ -132,7 +132,9 @@ class MultiStepEnv(ABC):
132
132
 
133
133
  return observation
134
134
 
135
- async def step(self, action: Action) -> StepResult:
135
+ async def step(
136
+ self, action: Action
137
+ ) -> Tuple[Observation, float, bool, Dict[str, Any]]:
136
138
  r"""Take a step in the environment using the given action.
137
139
 
138
140
  This method updates the environment state based on the LLM's response,
@@ -157,7 +159,7 @@ class MultiStepEnv(ABC):
157
159
  rewards_dict={},
158
160
  done=True,
159
161
  info={"reason": "max_steps_reached"},
160
- )
162
+ ).as_tuple()
161
163
 
162
164
  if not self._is_setup:
163
165
  raise RuntimeError("Environment not set up. Call setup() first.")
@@ -196,13 +198,13 @@ class MultiStepEnv(ABC):
196
198
  rewards_dict=rewards_dict,
197
199
  done=done,
198
200
  info={
199
- "extraction_result": self.extractor.extract(
201
+ "extraction_result": await self.extractor.extract(
200
202
  action.llm_response
201
203
  ),
202
204
  "step": self._current_step,
203
205
  "state": self._state, # Updated state
204
206
  },
205
- )
207
+ ).as_tuple()
206
208
 
207
209
  @abstractmethod
208
210
  def _get_initial_state(self) -> Dict[str, Any]:
@@ -200,7 +200,13 @@ class SingleStepEnv:
200
200
  self._states_done = [False] * self.current_batch_size
201
201
 
202
202
  observations = [
203
- Observation(question=sample.question, context={}, metadata={})
203
+ Observation(
204
+ question=sample.question,
205
+ context={},
206
+ metadata=sample.metadata
207
+ if sample.metadata is not None
208
+ else {},
209
+ )
204
210
  for sample in self._states
205
211
  ]
206
212
 
@@ -214,7 +220,13 @@ class SingleStepEnv:
214
220
  self._states_done = [False] * batch_size
215
221
 
216
222
  observations = [
217
- Observation(question=sample.question, context={}, metadata={})
223
+ Observation(
224
+ question=sample.question,
225
+ context={},
226
+ metadata=sample.metadata
227
+ if sample.metadata is not None
228
+ else {},
229
+ )
218
230
  for sample in self._states
219
231
  ]
220
232