camel-ai 0.1.1__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 (75) hide show
  1. camel/__init__.py +30 -0
  2. camel/agents/__init__.py +40 -0
  3. camel/agents/base.py +29 -0
  4. camel/agents/chat_agent.py +539 -0
  5. camel/agents/critic_agent.py +179 -0
  6. camel/agents/embodied_agent.py +138 -0
  7. camel/agents/role_assignment_agent.py +117 -0
  8. camel/agents/task_agent.py +382 -0
  9. camel/agents/tool_agents/__init__.py +20 -0
  10. camel/agents/tool_agents/base.py +40 -0
  11. camel/agents/tool_agents/hugging_face_tool_agent.py +203 -0
  12. camel/configs.py +159 -0
  13. camel/embeddings/__init__.py +20 -0
  14. camel/embeddings/base.py +65 -0
  15. camel/embeddings/openai_embedding.py +74 -0
  16. camel/functions/__init__.py +27 -0
  17. camel/functions/base_io_functions.py +261 -0
  18. camel/functions/math_functions.py +61 -0
  19. camel/functions/openai_function.py +88 -0
  20. camel/functions/search_functions.py +309 -0
  21. camel/functions/unstructured_io_fuctions.py +616 -0
  22. camel/functions/weather_functions.py +136 -0
  23. camel/generators.py +263 -0
  24. camel/human.py +130 -0
  25. camel/memories/__init__.py +28 -0
  26. camel/memories/base.py +75 -0
  27. camel/memories/chat_history_memory.py +111 -0
  28. camel/memories/context_creators/__init__.py +18 -0
  29. camel/memories/context_creators/base.py +72 -0
  30. camel/memories/context_creators/score_based.py +130 -0
  31. camel/memories/records.py +92 -0
  32. camel/messages/__init__.py +38 -0
  33. camel/messages/base.py +223 -0
  34. camel/messages/func_message.py +106 -0
  35. camel/models/__init__.py +26 -0
  36. camel/models/base_model.py +110 -0
  37. camel/models/model_factory.py +59 -0
  38. camel/models/open_source_model.py +144 -0
  39. camel/models/openai_model.py +103 -0
  40. camel/models/stub_model.py +106 -0
  41. camel/prompts/__init__.py +38 -0
  42. camel/prompts/ai_society.py +121 -0
  43. camel/prompts/base.py +227 -0
  44. camel/prompts/code.py +111 -0
  45. camel/prompts/evaluation.py +40 -0
  46. camel/prompts/misalignment.py +84 -0
  47. camel/prompts/prompt_templates.py +117 -0
  48. camel/prompts/role_description_prompt_template.py +53 -0
  49. camel/prompts/solution_extraction.py +44 -0
  50. camel/prompts/task_prompt_template.py +56 -0
  51. camel/prompts/translation.py +42 -0
  52. camel/responses/__init__.py +18 -0
  53. camel/responses/agent_responses.py +42 -0
  54. camel/societies/__init__.py +20 -0
  55. camel/societies/babyagi_playing.py +254 -0
  56. camel/societies/role_playing.py +456 -0
  57. camel/storages/__init__.py +23 -0
  58. camel/storages/key_value_storages/__init__.py +23 -0
  59. camel/storages/key_value_storages/base.py +57 -0
  60. camel/storages/key_value_storages/in_memory.py +51 -0
  61. camel/storages/key_value_storages/json.py +97 -0
  62. camel/terminators/__init__.py +23 -0
  63. camel/terminators/base.py +44 -0
  64. camel/terminators/response_terminator.py +118 -0
  65. camel/terminators/token_limit_terminator.py +55 -0
  66. camel/types/__init__.py +54 -0
  67. camel/types/enums.py +176 -0
  68. camel/types/openai_types.py +39 -0
  69. camel/utils/__init__.py +47 -0
  70. camel/utils/commons.py +243 -0
  71. camel/utils/python_interpreter.py +435 -0
  72. camel/utils/token_counting.py +220 -0
  73. camel_ai-0.1.1.dist-info/METADATA +311 -0
  74. camel_ai-0.1.1.dist-info/RECORD +75 -0
  75. camel_ai-0.1.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,136 @@
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 os
15
+ from typing import List
16
+
17
+ from camel.functions import OpenAIFunction
18
+
19
+
20
+ def get_openweathermap_api_key() -> str:
21
+ r"""Retrieve the OpenWeatherMap API key from environment variables.
22
+
23
+ Returns:
24
+ str: The OpenWeatherMap API key.
25
+
26
+ Raises:
27
+ ValueError: If the API key is not found in the environment variables.
28
+ """
29
+ # Get `OPENWEATHERMAP_API_KEY` here: https://openweathermap.org
30
+ OPENWEATHERMAP_API_KEY = os.environ.get('OPENWEATHERMAP_API_KEY')
31
+ if not OPENWEATHERMAP_API_KEY:
32
+ raise ValueError("`OPENWEATHERMAP_API_KEY` not found in environment "
33
+ "variables. Get `OPENWEATHERMAP_API_KEY` here: "
34
+ "`https://openweathermap.org`.")
35
+ return OPENWEATHERMAP_API_KEY
36
+
37
+
38
+ def get_weather_data(city: str, temp_units: str = 'kelvin',
39
+ wind_units: str = 'meters_sec',
40
+ visibility_units: str = 'meters',
41
+ time_units: str = 'unix') -> str:
42
+ r"""Fetch and return a comprehensive weather report for a given city as a
43
+ string. The report includes current weather conditions, temperature,
44
+ wind details, visibility, and sunrise/sunset times, all formatted as
45
+
46
+ The function interacts with the OpenWeatherMap API to retrieve the data.
47
+
48
+ Args:
49
+ city (string): The name of the city for which the weather information
50
+ is desired. Format "City, CountryCode" (e.g., "Paris, FR"
51
+ for Paris, France). If the country code is not provided,
52
+ the API will search for the city in all countries, which
53
+ may yield incorrect results if multiple cities with the
54
+ same name exist.
55
+ temp_units (string): Units for temperature. Options: 'kelvin',
56
+ 'celsius', 'fahrenheit'. (default: :obj:`kelvin`)
57
+ wind_units (string): Units for wind speed. Options: 'meters_sec',
58
+ 'miles_hour', 'knots', 'beaufort'. (default: :obj:`meters_sec`)
59
+ visibility_units (string): Units for visibility distance. Options:
60
+ 'meters', 'miles'. (default: :obj:`meters`)
61
+ time_units (string): Format for sunrise and sunset times. Options:
62
+ 'unix', 'iso', 'date'. (default: :obj:`unix`)
63
+
64
+ Returns:
65
+ str: A string containing the fetched weather data, formatted in a
66
+ readable manner. If an error occurs, a message indicating the
67
+ error will be returned instead.
68
+
69
+ Example of return string:
70
+ "Weather in Paris, FR: 15°C, feels like 13°C. Max temp: 17°C, Min temp
71
+ : 12°C.
72
+ Wind: 5 m/s at 270 degrees. Visibility: 10 kilometers.
73
+ Sunrise at 05:46:05 (UTC), Sunset at 18:42:20 (UTC)."
74
+
75
+ Note:
76
+ Please ensure that the API key is valid and has permissions to access
77
+ the weather data.
78
+ """
79
+ # NOTE: This tool may not work as expected since the input arguments like
80
+ # `time_units` should be enum types which are not supported yet.
81
+
82
+ try:
83
+ import pyowm
84
+ except ImportError:
85
+ raise ImportError(
86
+ "Please install `pyowm` first. You can install it by running "
87
+ "`pip install pyowm`.")
88
+
89
+ OPENWEATHERMAP_API_KEY = get_openweathermap_api_key()
90
+ owm = pyowm.OWM(OPENWEATHERMAP_API_KEY)
91
+ mgr = owm.weather_manager()
92
+
93
+ try:
94
+ observation = mgr.weather_at_place(city)
95
+ weather = observation.weather
96
+
97
+ # Temperature
98
+ temperature = weather.temperature(temp_units)
99
+
100
+ # Wind
101
+ wind_data = observation.weather.wind(unit=wind_units)
102
+ wind_speed = wind_data.get('speed')
103
+ # 'N/A' if the degree is not available
104
+ wind_deg = wind_data.get('deg', 'N/A')
105
+
106
+ # Visibility
107
+ visibility_distance = observation.weather.visibility_distance
108
+ visibility = (str(visibility_distance) if visibility_units == 'meters'
109
+ else str(observation.weather.visibility(unit='miles')))
110
+
111
+ # Sunrise and Sunset
112
+ sunrise_time = str(weather.sunrise_time(timeformat=time_units))
113
+ sunset_time = str(weather.sunset_time(timeformat=time_units))
114
+
115
+ # Compile all the weather details into a report string
116
+ weather_report = (
117
+ f"Weather in {city}: {temperature['temp']}°{temp_units.title()}, "
118
+ f"feels like {temperature['feels_like']}°{temp_units.title()}. "
119
+ f"Max temp: {temperature['temp_max']}°{temp_units.title()}, "
120
+ f"Min temp: {temperature['temp_min']}°{temp_units.title()}. "
121
+ f"Wind: {wind_speed} {wind_units} at {wind_deg} degrees. "
122
+ f"Visibility: {visibility} {visibility_units}. "
123
+ f"Sunrise at {sunrise_time}, Sunset at {sunset_time}.")
124
+
125
+ return weather_report
126
+
127
+ except Exception as e:
128
+ error_message = (
129
+ f"An error occurred while fetching weather data for {city}: "
130
+ f"{str(e)}.")
131
+ return error_message
132
+
133
+
134
+ WEATHER_FUNCS: List[OpenAIFunction] = [
135
+ OpenAIFunction(func) for func in [get_weather_data]
136
+ ]
camel/generators.py ADDED
@@ -0,0 +1,263 @@
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 Dict, Generator, List, Optional, Set, Tuple
15
+
16
+ from camel.messages import BaseMessage
17
+ from camel.prompts import PromptTemplateGenerator, TextPrompt
18
+ from camel.types import RoleType, TaskType
19
+
20
+
21
+ class SystemMessageGenerator:
22
+ r"""System message generator for agents.
23
+
24
+ Args:
25
+ task_type (TaskType, optional): The task type.
26
+ (default: :obj:`TaskType.AI_SOCIETY`)
27
+ sys_prompts (Optional[Dict[RoleType, str]], optional): The prompts of
28
+ the system messages for each role type. (default: :obj:`None`)
29
+ sys_msg_meta_dict_keys (Optional[Set[str]], optional): The set of keys
30
+ of the meta dictionary used to fill the prompts.
31
+ (default: :obj:`None`)
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ task_type: TaskType = TaskType.AI_SOCIETY,
37
+ sys_prompts: Optional[Dict[RoleType, str]] = None,
38
+ sys_msg_meta_dict_keys: Optional[Set[str]] = None,
39
+ ) -> None:
40
+ self.sys_prompts: Dict[RoleType, str]
41
+
42
+ if sys_prompts is not None:
43
+ self.sys_prompts = sys_prompts
44
+ self.sys_msg_meta_dict_keys = sys_msg_meta_dict_keys or set()
45
+ else:
46
+ assistant_prompt_template = PromptTemplateGenerator(
47
+ ).get_system_prompt(
48
+ task_type,
49
+ RoleType.ASSISTANT,
50
+ )
51
+ user_prompt_template = PromptTemplateGenerator().get_system_prompt(
52
+ task_type,
53
+ RoleType.USER,
54
+ )
55
+ critic_prompt_template = PromptTemplateGenerator(
56
+ ).get_system_prompt(
57
+ task_type,
58
+ RoleType.CRITIC,
59
+ )
60
+ embodiment_prompt_template = PromptTemplateGenerator(
61
+ ).get_system_prompt(
62
+ task_type,
63
+ RoleType.EMBODIMENT,
64
+ )
65
+
66
+ self.sys_prompts = dict()
67
+ self.sys_prompts[RoleType.ASSISTANT] = assistant_prompt_template
68
+ self.sys_prompts[RoleType.USER] = user_prompt_template
69
+ self.sys_prompts[RoleType.CRITIC] = critic_prompt_template
70
+ self.sys_prompts[RoleType.EMBODIMENT] = embodiment_prompt_template
71
+
72
+ self.sys_msg_meta_dict_keys = (
73
+ assistant_prompt_template.key_words
74
+ | user_prompt_template.key_words
75
+ | critic_prompt_template.key_words
76
+ | embodiment_prompt_template.key_words)
77
+
78
+ if RoleType.DEFAULT not in self.sys_prompts:
79
+ self.sys_prompts[RoleType.DEFAULT] = "You are a helpful assistant."
80
+
81
+ def validate_meta_dict_keys(self, meta_dict: Dict[str, str]) -> None:
82
+ r"""Validates the keys of the meta_dict.
83
+
84
+ Args:
85
+ meta_dict (Dict[str, str]): The dictionary to validate.
86
+ """
87
+ if not set(meta_dict.keys()).issubset(self.sys_msg_meta_dict_keys):
88
+ raise ValueError("The keys of the meta_dict should be in "
89
+ f"{self.sys_msg_meta_dict_keys}. "
90
+ f"Got {set(meta_dict.keys())} instead.")
91
+
92
+ def from_dict(
93
+ self,
94
+ meta_dict: Dict[str, str],
95
+ role_tuple: Tuple[str, RoleType] = ("", RoleType.DEFAULT),
96
+ ) -> BaseMessage:
97
+ r"""Generates a system message from a dictionary.
98
+
99
+ Args:
100
+ meta_dict (Dict[str, str]): The dictionary containing the
101
+ information to generate the system message.
102
+ role_tuple (Tuple[str, RoleType], optional): The tuple containing
103
+ the role name and role type. (default: ("", RoleType.DEFAULT))
104
+
105
+ Returns:
106
+ BaseMessage: The generated system message.
107
+ """
108
+ self.validate_meta_dict_keys(meta_dict)
109
+ role_name, role_type = role_tuple
110
+ sys_prompt = self.sys_prompts[role_type]
111
+ sys_prompt = sys_prompt.format(**meta_dict)
112
+ return BaseMessage(role_name=role_name, role_type=role_type,
113
+ meta_dict=meta_dict, content=sys_prompt)
114
+
115
+ def from_dicts(
116
+ self,
117
+ meta_dicts: List[Dict[str, str]],
118
+ role_tuples: List[Tuple[str, RoleType]],
119
+ ) -> List[BaseMessage]:
120
+ r"""Generates a list of system messages from a list of dictionaries.
121
+
122
+ Args:
123
+ meta_dicts (List[Dict[str, str]]): A list of dictionaries
124
+ containing the information to generate the system messages.
125
+ role_tuples (List[Tuple[str, RoleType]]): A list of tuples
126
+ containing the role name and role type for each system message.
127
+
128
+ Returns:
129
+ List[BaseMessage]: A list of generated system messages.
130
+
131
+ Raises:
132
+ ValueError: If the number of meta_dicts and role_tuples are
133
+ different.
134
+ """
135
+ if len(meta_dicts) != len(role_tuples):
136
+ raise ValueError(
137
+ "The number of meta_dicts and role_types should be the same.")
138
+
139
+ return [
140
+ self.from_dict(meta_dict, role_tuple)
141
+ for meta_dict, role_tuple in zip(meta_dicts, role_tuples)
142
+ ]
143
+
144
+
145
+ class RoleNameGenerator:
146
+
147
+ def __init__(self, assistant_role_names_path:
148
+ str = "data/ai_society/assistant_roles.txt",
149
+ user_role_names_path: str = "data/ai_society/user_roles.txt",
150
+ assistant_role_names: Optional[List[str]] = None,
151
+ user_role_names: Optional[List[str]] = None) -> None:
152
+
153
+ if assistant_role_names is None:
154
+ with open(assistant_role_names_path, "r") as f:
155
+ assistant_role_names_: List[str] = f.read().splitlines()
156
+ self.assistant_role_names = [
157
+ " ".join(name.split(" ")[1:])
158
+ for name in assistant_role_names_
159
+ ]
160
+ else:
161
+ self.assistant_role_names = assistant_role_names
162
+
163
+ if user_role_names is None:
164
+ with open(user_role_names_path, "r") as f:
165
+ user_role_names_: List[str] = f.read().splitlines()
166
+ self.user_role_names = [
167
+ " ".join(name.split(" ")[1:]) for name in user_role_names_
168
+ ]
169
+ else:
170
+ self.user_role_names = user_role_names
171
+
172
+ def from_role_files(self) -> Generator[Tuple, None, None]:
173
+ for assistant_role_name in self.assistant_role_names:
174
+ for user_role_name in self.user_role_names:
175
+ yield (assistant_role_name, user_role_name)
176
+
177
+
178
+ class AISocietyTaskPromptGenerator:
179
+
180
+ def __init__(
181
+ self,
182
+ num_tasks: int = 10,
183
+ ) -> None:
184
+ self.generate_tasks_prompt = PromptTemplateGenerator(
185
+ ).get_generate_tasks_prompt(TaskType.AI_SOCIETY)
186
+
187
+ self.num_tasks = num_tasks
188
+
189
+ # TODO: Return role names for user and assistant with the generator.
190
+ def from_role_files(
191
+ self,
192
+ assistant_role_names_path: str = "data/ai_society/assistant_roles.txt",
193
+ user_role_names_path: str = "data/ai_society/user_roles.txt"
194
+ ) -> Generator[Tuple[str, Tuple[str, str]], None, None]:
195
+ roles_generator = RoleNameGenerator(
196
+ assistant_role_names_path, user_role_names_path).from_role_files()
197
+ for role_1, role_2 in roles_generator:
198
+ generate_tasks_prompt = self.generate_tasks_prompt.format(
199
+ assistant_role=role_1, user_role=role_2,
200
+ num_tasks=self.num_tasks)
201
+
202
+ yield (generate_tasks_prompt, (role_1, role_2))
203
+
204
+ def from_role_generator(
205
+ self, role_generator: Generator[Tuple, None, None]
206
+ ) -> Generator[Tuple[str, Tuple[str, str]], None, None]:
207
+ for role_1, role_2 in role_generator:
208
+ generate_tasks_prompt = self.generate_tasks_prompt.format(
209
+ assistant_role=role_1, user_role=role_2,
210
+ num_tasks=self.num_tasks)
211
+
212
+ yield (generate_tasks_prompt, (role_1, role_2))
213
+
214
+
215
+ class SingleTxtGenerator:
216
+
217
+ def __init__(
218
+ self,
219
+ text_file_path: str,
220
+ ) -> None:
221
+
222
+ with open(text_file_path, "r") as f:
223
+ data_list: List[str] = f.read().splitlines()
224
+ self.data_list = [
225
+ " ".join(name.split(" ")[1:]) for name in data_list
226
+ ]
227
+
228
+ def from_role_files(self) -> Generator[str, None, None]:
229
+ for data in self.data_list:
230
+ yield data
231
+
232
+
233
+ class CodeTaskPromptGenerator:
234
+
235
+ def __init__(
236
+ self,
237
+ num_tasks: int = 50,
238
+ ) -> None:
239
+
240
+ self.generate_tasks_prompt = PromptTemplateGenerator(
241
+ ).get_generate_tasks_prompt(TaskType.CODE)
242
+
243
+ self.num_tasks = num_tasks
244
+
245
+ def from_role_files(
246
+ self, languages_path: str = "data/code/languages.txt",
247
+ domains_path: str = "data/code/domains.txt"
248
+ ) -> Generator[Tuple[TextPrompt, str, str], None, None]:
249
+ language_generator = SingleTxtGenerator(
250
+ languages_path).from_role_files()
251
+
252
+ for language in language_generator:
253
+ domains_generator = SingleTxtGenerator(
254
+ domains_path).from_role_files()
255
+ for domain in domains_generator:
256
+ generated_tasks_prompt = self.generate_tasks_prompt.format(
257
+ language=language, domain=domain, num_tasks=self.num_tasks)
258
+ yield generated_tasks_prompt, language, domain
259
+
260
+ def from_role_generator(
261
+ self, role_generator: Generator[Tuple, None, None]
262
+ ) -> Generator[str, None, None]:
263
+ raise NotImplementedError
camel/human.py ADDED
@@ -0,0 +1,130 @@
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 Any, Dict, Sequence
15
+
16
+ from colorama import Fore
17
+
18
+ from camel.messages import BaseMessage
19
+ from camel.responses import ChatAgentResponse
20
+ from camel.utils import print_text_animated
21
+
22
+
23
+ class Human:
24
+ r"""A class representing a human user.
25
+
26
+ Args:
27
+ name (str): The name of the human user.
28
+ (default: :obj:`"Kill Switch Engineer"`).
29
+ logger_color (Any): The color of the menu options displayed to the
30
+ user. (default: :obj:`Fore.MAGENTA`)
31
+
32
+ Attributes:
33
+ name (str): The name of the human user.
34
+ logger_color (Any): The color of the menu options displayed to the
35
+ user.
36
+ input_button (str): The text displayed for the input button.
37
+ kill_button (str): The text displayed for the kill button.
38
+ options_dict (Dict[str, str]): A dictionary containing the options
39
+ displayed to the user.
40
+ """
41
+
42
+ def __init__(self, name: str = "Kill Switch Engineer",
43
+ logger_color: Any = Fore.MAGENTA) -> None:
44
+ self.name = name
45
+ self.logger_color = logger_color
46
+ self.input_button = f"Input by {self.name}."
47
+ self.kill_button = "Stop!!!"
48
+ self.options_dict: Dict[str, str] = dict()
49
+
50
+ def display_options(self, messages: Sequence[BaseMessage]) -> None:
51
+ r"""Displays the options to the user.
52
+
53
+ Args:
54
+ messages (Sequence[BaseMessage]): A list of `BaseMessage` objects.
55
+
56
+ Returns:
57
+ None
58
+ """
59
+ options = [message.content for message in messages]
60
+ options.append(self.input_button)
61
+ options.append(self.kill_button)
62
+ print_text_animated(
63
+ self.logger_color + "\n> Proposals from "
64
+ f"{messages[0].role_name} ({messages[0].role_type}). "
65
+ "Please choose an option:\n")
66
+ for index, option in enumerate(options):
67
+ print_text_animated(
68
+ self.logger_color +
69
+ f"\x1b[3mOption {index + 1}:\n{option}\x1b[0m\n")
70
+ self.options_dict[str(index + 1)] = option
71
+
72
+ def get_input(self) -> str:
73
+ r"""Gets the input from the user.
74
+
75
+ Returns:
76
+ str: The user's input.
77
+ """
78
+ while True:
79
+ human_input = input(
80
+ self.logger_color +
81
+ f"Please enter your choice ([1-{len(self.options_dict)}]): ")
82
+ print("\n")
83
+ if human_input in self.options_dict:
84
+ break
85
+ print_text_animated(self.logger_color +
86
+ "\n> Invalid choice. Please try again.\n")
87
+
88
+ return human_input
89
+
90
+ def parse_input(self, human_input: str) -> str:
91
+ r"""Parses the user's input and returns a `BaseMessage` object.
92
+
93
+ Args:
94
+ human_input (str): The user's input.
95
+
96
+ Returns:
97
+ content: A `str` object representing the user's input.
98
+ """
99
+ if self.options_dict[human_input] == self.input_button:
100
+ content = input(self.logger_color + "Please enter your message: ")
101
+ elif self.options_dict[human_input] == self.kill_button:
102
+ exit(self.logger_color + f"Killed by {self.name}.")
103
+ else:
104
+ content = self.options_dict[human_input]
105
+
106
+ return content
107
+
108
+ def reduce_step(self,
109
+ messages: Sequence[BaseMessage]) -> ChatAgentResponse:
110
+ r"""Performs one step of the conversation by displaying options to the
111
+ user, getting their input, and parsing their choice.
112
+
113
+ Args:
114
+ messages (Sequence[BaseMessage]): A list of BaseMessage objects.
115
+
116
+ Returns:
117
+ ChatAgentResponse: A `ChatAgentResponse` object representing the
118
+ user's choice.
119
+ """
120
+ meta_chat_message = BaseMessage(
121
+ role_name=messages[0].role_name,
122
+ role_type=messages[0].role_type,
123
+ meta_dict=messages[0].meta_dict,
124
+ content="",
125
+ )
126
+ self.display_options(messages)
127
+ human_input = self.get_input()
128
+ content = self.parse_input(human_input)
129
+ message = meta_chat_message.create_new_instance(content)
130
+ return ChatAgentResponse([message], terminated=False, info={})
@@ -0,0 +1,28 @@
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
+ from .records import MemoryRecord, ContextRecord
16
+ from .base import BaseMemory
17
+ from .context_creators.base import BaseContextCreator
18
+ from .context_creators.score_based import ScoreBasedContextCreator
19
+ from .chat_history_memory import ChatHistoryMemory
20
+
21
+ __all__ = [
22
+ 'MemoryRecord',
23
+ 'ContextRecord',
24
+ 'BaseMemory',
25
+ 'ChatHistoryMemory',
26
+ "BaseContextCreator",
27
+ "ScoreBasedContextCreator",
28
+ ]
camel/memories/base.py ADDED
@@ -0,0 +1,75 @@
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
+ from abc import ABC, abstractmethod
16
+ from typing import List, Tuple
17
+
18
+ from camel.memories import MemoryRecord
19
+ from camel.messages import OpenAIMessage
20
+
21
+
22
+ class BaseMemory(ABC):
23
+ r"""An abstract base class that defines the foundational operations for a
24
+ memory component within an agent's memory system.
25
+
26
+ The memory component is tasked with functions like saving chat histories,
27
+ fetching or storing information in vector databases, and other related
28
+ operations. Every memory system should incorporate at least one instance of
29
+ a subclass derived from :obj:`BaseMemory`.
30
+
31
+ These instances, known as "memories", typically communicate using the
32
+ :obj:`MemoryRecord` object. Usually, a memory has at least one "storage"
33
+ mechanism, allowing it to interface with various storage systems, such as
34
+ disks or vector databases. Additionally, some memories might embed other
35
+ memory instances, enabling them to function as a high-level controller
36
+ within the broader memory system.
37
+
38
+ By default, when executing the :obj:`step()` method, an agent retrieves
39
+ messages from its designated memory and combines them with an incoming
40
+ message for input to the agent. Subsequently, both the response message and
41
+ the incoming messages are archived back into the memory.
42
+ """
43
+
44
+ @abstractmethod
45
+ def get_context(self) -> Tuple[List[OpenAIMessage], int]:
46
+ r"""Gets chat context with a proper size for the agent.
47
+
48
+ Returns:
49
+ (List[OpenAIMessage], int): A tuple containing the constructed
50
+ context in OpenAIMessage format and the total token count.
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def write_records(self, records: List[MemoryRecord]) -> None:
56
+ r"""Writes records to the memory, appending them to existing ones.
57
+
58
+ Args:
59
+ records (List[MemoryRecord]): Records to be added to the memory.
60
+ """
61
+ pass
62
+
63
+ def write_record(self, record: MemoryRecord) -> None:
64
+ r"""Writes a record to the memory, appending it to existing ones.
65
+
66
+ Args:
67
+ record (MemoryRecord): Record to be added to the memory.
68
+ """
69
+ self.write_records([record])
70
+
71
+ @abstractmethod
72
+ def clear(self) -> None:
73
+ r"""Clears all messages from the memory.
74
+ """
75
+ pass