camel-ai 0.1.1__py3-none-any.whl → 0.1.4__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 (117) hide show
  1. camel/__init__.py +1 -11
  2. camel/agents/__init__.py +7 -5
  3. camel/agents/chat_agent.py +134 -86
  4. camel/agents/critic_agent.py +28 -17
  5. camel/agents/deductive_reasoner_agent.py +235 -0
  6. camel/agents/embodied_agent.py +92 -40
  7. camel/agents/knowledge_graph_agent.py +221 -0
  8. camel/agents/role_assignment_agent.py +27 -17
  9. camel/agents/task_agent.py +60 -34
  10. camel/agents/tool_agents/base.py +0 -1
  11. camel/agents/tool_agents/hugging_face_tool_agent.py +7 -4
  12. camel/configs/__init__.py +29 -0
  13. camel/configs/anthropic_config.py +73 -0
  14. camel/configs/base_config.py +22 -0
  15. camel/{configs.py → configs/openai_config.py} +37 -64
  16. camel/embeddings/__init__.py +2 -0
  17. camel/embeddings/base.py +3 -2
  18. camel/embeddings/openai_embedding.py +10 -5
  19. camel/embeddings/sentence_transformers_embeddings.py +65 -0
  20. camel/functions/__init__.py +18 -3
  21. camel/functions/google_maps_function.py +335 -0
  22. camel/functions/math_functions.py +7 -7
  23. camel/functions/open_api_function.py +380 -0
  24. camel/functions/open_api_specs/coursera/__init__.py +13 -0
  25. camel/functions/open_api_specs/coursera/openapi.yaml +82 -0
  26. camel/functions/open_api_specs/klarna/__init__.py +13 -0
  27. camel/functions/open_api_specs/klarna/openapi.yaml +87 -0
  28. camel/functions/open_api_specs/speak/__init__.py +13 -0
  29. camel/functions/open_api_specs/speak/openapi.yaml +151 -0
  30. camel/functions/openai_function.py +346 -42
  31. camel/functions/retrieval_functions.py +61 -0
  32. camel/functions/search_functions.py +100 -35
  33. camel/functions/slack_functions.py +275 -0
  34. camel/functions/twitter_function.py +484 -0
  35. camel/functions/weather_functions.py +36 -23
  36. camel/generators.py +65 -46
  37. camel/human.py +17 -11
  38. camel/interpreters/__init__.py +25 -0
  39. camel/interpreters/base.py +49 -0
  40. camel/{utils/python_interpreter.py → interpreters/internal_python_interpreter.py} +129 -48
  41. camel/interpreters/interpreter_error.py +19 -0
  42. camel/interpreters/subprocess_interpreter.py +190 -0
  43. camel/loaders/__init__.py +22 -0
  44. camel/{functions/base_io_functions.py → loaders/base_io.py} +38 -35
  45. camel/{functions/unstructured_io_fuctions.py → loaders/unstructured_io.py} +199 -110
  46. camel/memories/__init__.py +17 -7
  47. camel/memories/agent_memories.py +156 -0
  48. camel/memories/base.py +97 -32
  49. camel/memories/blocks/__init__.py +21 -0
  50. camel/memories/{chat_history_memory.py → blocks/chat_history_block.py} +34 -34
  51. camel/memories/blocks/vectordb_block.py +101 -0
  52. camel/memories/context_creators/__init__.py +3 -2
  53. camel/memories/context_creators/score_based.py +32 -20
  54. camel/memories/records.py +6 -5
  55. camel/messages/__init__.py +2 -2
  56. camel/messages/base.py +99 -16
  57. camel/messages/func_message.py +7 -4
  58. camel/models/__init__.py +6 -2
  59. camel/models/anthropic_model.py +146 -0
  60. camel/models/base_model.py +10 -3
  61. camel/models/model_factory.py +17 -11
  62. camel/models/open_source_model.py +25 -13
  63. camel/models/openai_audio_models.py +251 -0
  64. camel/models/openai_model.py +20 -13
  65. camel/models/stub_model.py +10 -5
  66. camel/prompts/__init__.py +7 -5
  67. camel/prompts/ai_society.py +21 -14
  68. camel/prompts/base.py +54 -47
  69. camel/prompts/code.py +22 -14
  70. camel/prompts/evaluation.py +8 -5
  71. camel/prompts/misalignment.py +26 -19
  72. camel/prompts/object_recognition.py +35 -0
  73. camel/prompts/prompt_templates.py +14 -8
  74. camel/prompts/role_description_prompt_template.py +16 -10
  75. camel/prompts/solution_extraction.py +9 -5
  76. camel/prompts/task_prompt_template.py +24 -21
  77. camel/prompts/translation.py +9 -5
  78. camel/responses/agent_responses.py +5 -2
  79. camel/retrievers/__init__.py +26 -0
  80. camel/retrievers/auto_retriever.py +330 -0
  81. camel/retrievers/base.py +69 -0
  82. camel/retrievers/bm25_retriever.py +140 -0
  83. camel/retrievers/cohere_rerank_retriever.py +108 -0
  84. camel/retrievers/vector_retriever.py +183 -0
  85. camel/societies/__init__.py +1 -1
  86. camel/societies/babyagi_playing.py +56 -32
  87. camel/societies/role_playing.py +188 -133
  88. camel/storages/__init__.py +18 -0
  89. camel/storages/graph_storages/__init__.py +23 -0
  90. camel/storages/graph_storages/base.py +82 -0
  91. camel/storages/graph_storages/graph_element.py +74 -0
  92. camel/storages/graph_storages/neo4j_graph.py +582 -0
  93. camel/storages/key_value_storages/base.py +1 -2
  94. camel/storages/key_value_storages/in_memory.py +1 -2
  95. camel/storages/key_value_storages/json.py +8 -13
  96. camel/storages/vectordb_storages/__init__.py +33 -0
  97. camel/storages/vectordb_storages/base.py +202 -0
  98. camel/storages/vectordb_storages/milvus.py +396 -0
  99. camel/storages/vectordb_storages/qdrant.py +373 -0
  100. camel/terminators/__init__.py +1 -1
  101. camel/terminators/base.py +2 -3
  102. camel/terminators/response_terminator.py +21 -12
  103. camel/terminators/token_limit_terminator.py +5 -3
  104. camel/toolkits/__init__.py +21 -0
  105. camel/toolkits/base.py +22 -0
  106. camel/toolkits/github_toolkit.py +245 -0
  107. camel/types/__init__.py +18 -6
  108. camel/types/enums.py +129 -15
  109. camel/types/openai_types.py +10 -5
  110. camel/utils/__init__.py +20 -13
  111. camel/utils/commons.py +170 -85
  112. camel/utils/token_counting.py +135 -15
  113. {camel_ai-0.1.1.dist-info → camel_ai-0.1.4.dist-info}/METADATA +123 -75
  114. camel_ai-0.1.4.dist-info/RECORD +119 -0
  115. {camel_ai-0.1.1.dist-info → camel_ai-0.1.4.dist-info}/WHEEL +1 -1
  116. camel/memories/context_creators/base.py +0 -72
  117. camel_ai-0.1.1.dist-info/RECORD +0 -75
@@ -0,0 +1,484 @@
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 datetime
15
+ import os
16
+ from http import HTTPStatus
17
+ from http.client import responses
18
+ from typing import List, Optional, Tuple, Union
19
+
20
+ import requests
21
+
22
+ from camel.functions import OpenAIFunction
23
+
24
+ TWEET_TEXT_LIMIT = 280
25
+
26
+
27
+ def get_twitter_api_key() -> Tuple[str, str]:
28
+ r"""Retrieve the Twitter API key and secret from environment variables.
29
+
30
+ Returns:
31
+ Tuple[str, str]: A tuple containing the Twitter API key and secret.
32
+
33
+ Raises:
34
+ ValueError: If the API key or secret is not found in the environment
35
+ variables.
36
+ """
37
+ # Get `TWITTER_CONSUMER_KEY` and `TWITTER_CONSUMER_SECRET` here:
38
+ # https://developer.twitter.com/en/portal/products/free
39
+ TWITTER_CONSUMER_KEY = os.environ.get("TWITTER_CONSUMER_KEY")
40
+ TWITTER_CONSUMER_SECRET = os.environ.get("TWITTER_CONSUMER_SECRET")
41
+
42
+ if not TWITTER_CONSUMER_KEY or not TWITTER_CONSUMER_SECRET:
43
+ missing_keys = ", ".join(
44
+ [
45
+ "TWITTER_CONSUMER_KEY" if not TWITTER_CONSUMER_KEY else "",
46
+ "TWITTER_CONSUMER_SECRET"
47
+ if not TWITTER_CONSUMER_SECRET
48
+ else "",
49
+ ]
50
+ ).strip(", ")
51
+ raise ValueError(
52
+ f"{missing_keys} not found in environment variables. Get them "
53
+ "here: `https://developer.twitter.com/en/portal/products/free`."
54
+ )
55
+ return TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET
56
+
57
+
58
+ def get_oauth_session() -> requests.Session:
59
+ r'''Initiates an OAuth1Session with Twitter's API and returns it.
60
+
61
+ The function first fetches a request token, then prompts the user to
62
+ authorize the application. After the user has authorized the application
63
+ and provided a verifier (PIN), the function fetches an access token.
64
+ Finally, a new OAuth1Session is created with the access token and returned.
65
+
66
+ Raises:
67
+ RuntimeError: If an error occurs while fetching the OAuth access token
68
+ or the OAuth request token.
69
+
70
+ Returns:
71
+ requests_oauthlib.OAuth1Session: An OAuth1Session object authenticated
72
+ with the user's access token.
73
+
74
+ Reference:
75
+ https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.py
76
+ https://github.com/twitterdev/Twitter-API-v2-sample-code/blob/main/User-Lookup/get_users_me_user_context.py
77
+ '''
78
+ try:
79
+ from requests_oauthlib import OAuth1Session
80
+ except ImportError:
81
+ raise ImportError(
82
+ "Please install `requests_oauthlib` first. You can "
83
+ "install it by running `pip install "
84
+ "requests_oauthlib`."
85
+ )
86
+
87
+ consumer_key, consumer_secret = get_twitter_api_key()
88
+
89
+ # Get request token
90
+ request_token_url = (
91
+ "https://api.twitter.com/oauth/request_token"
92
+ "?oauth_callback=oob&x_auth_access_type=write"
93
+ )
94
+ oauth = OAuth1Session(consumer_key, client_secret=consumer_secret)
95
+
96
+ try:
97
+ fetch_response = oauth.fetch_request_token(request_token_url)
98
+ except Exception as e:
99
+ raise RuntimeError(
100
+ f"Error occurred while fetching the OAuth access token: {e}"
101
+ )
102
+
103
+ resource_owner_key = fetch_response.get("oauth_token")
104
+ resource_owner_secret = fetch_response.get("oauth_token_secret")
105
+
106
+ # Get authorization
107
+ base_authorization_url = "https://api.twitter.com/oauth/authorize"
108
+ authorization_url = oauth.authorization_url(base_authorization_url)
109
+ print("Please go here and authorize: %s" % authorization_url)
110
+ verifier = input("Paste the PIN here: ")
111
+
112
+ # Get the access token
113
+ access_token_url = "https://api.twitter.com/oauth/access_token"
114
+ oauth = OAuth1Session(
115
+ consumer_key,
116
+ client_secret=consumer_secret,
117
+ resource_owner_key=resource_owner_key,
118
+ resource_owner_secret=resource_owner_secret,
119
+ verifier=verifier,
120
+ )
121
+
122
+ try:
123
+ oauth_tokens = oauth.fetch_access_token(access_token_url)
124
+ except Exception as e:
125
+ raise RuntimeError(
126
+ f"Error occurred while fetching the OAuth request token: {e}"
127
+ )
128
+
129
+ # Create a new OAuth1Session with the access token
130
+ oauth = OAuth1Session(
131
+ consumer_key,
132
+ client_secret=consumer_secret,
133
+ resource_owner_key=oauth_tokens["oauth_token"],
134
+ resource_owner_secret=oauth_tokens["oauth_token_secret"],
135
+ )
136
+ return oauth
137
+
138
+
139
+ def handle_http_error(response: requests.Response) -> str:
140
+ r"""Handles the HTTP response by checking the status code and returning an
141
+ appropriate message if there is an error.
142
+
143
+ Args:
144
+ response (requests.Response): The HTTP response to handle.
145
+
146
+ Returns:
147
+ str: A string describing the error, if any. If there is no error, the
148
+ function returns an "Unexpected Exception" message.
149
+
150
+ Reference:
151
+ https://github.com/tweepy/tweepy/blob/master/tweepy/client.py#L64
152
+ """
153
+ if response.status_code in responses:
154
+ # For 5xx server errors, return "Twitter Server Error"
155
+ if 500 <= response.status_code < 600:
156
+ return "Twitter Server Error"
157
+ else:
158
+ error_message = responses[response.status_code] + " Error"
159
+ return error_message
160
+ elif not 200 <= response.status_code < 300:
161
+ return "HTTP Exception"
162
+ else:
163
+ return "Unexpected Exception"
164
+
165
+
166
+ def create_tweet(
167
+ *,
168
+ text: str,
169
+ poll_options: Optional[List[str]] = None,
170
+ poll_duration_minutes: Optional[int] = None,
171
+ quote_tweet_id: Optional[Union[int, str]] = None,
172
+ ) -> str:
173
+ r"""Creates a new tweet, optionally including a poll or a quote tweet, or
174
+ simply a text-only tweet.
175
+
176
+ This function sends a POST request to the Twitter API to create a new
177
+ tweet. The tweet can be a text-only tweet, or optionally include a poll or
178
+ be a quote tweet. A confirmation prompt is presented to the user before the
179
+ tweet is created.
180
+
181
+ Args:
182
+ text (str): The text of the tweet. The Twitter character limit for a
183
+ single tweet is 280 characters.
184
+ poll_options (Optional[List[str]]): A list of poll options for a tweet
185
+ with a poll.
186
+ poll_duration_minutes (Optional[int]): Duration of the poll in minutes
187
+ for a tweet with a poll. This is only required if the request
188
+ includes poll_options.
189
+ quote_tweet_id (Optional[Union[int, str]]): Link to the tweet being
190
+ quoted.
191
+
192
+ Note:
193
+ You can only provide either the `quote_tweet_id` parameter or the pair
194
+ of `poll_duration_minutes` and `poll_options` parameters, not both.
195
+
196
+ Returns:
197
+ str: A message indicating the success of the tweet creation, including
198
+ the tweet ID and text. If the request to the Twitter API is not
199
+ successful, the return is an error message.
200
+
201
+ Reference:
202
+ https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets
203
+ https://github.com/xdevplatform/Twitter-API-v2-sample-code/blob/main/Manage-Tweets/create_tweet.py
204
+ """
205
+ # validate text
206
+ if text is None:
207
+ return "Text cannot be None"
208
+ elif len(text) > TWEET_TEXT_LIMIT:
209
+ return "Text must not exceed 280 characters."
210
+
211
+ # Validate poll options and duration
212
+ if (poll_options is None) != (poll_duration_minutes is None):
213
+ return (
214
+ "Error: Both `poll_options` and `poll_duration_minutes` must "
215
+ "be provided together or not at all."
216
+ )
217
+
218
+ # Validate exclusive parameters
219
+ if quote_tweet_id is not None and (poll_options or poll_duration_minutes):
220
+ return (
221
+ "Error: Cannot provide both `quote_tweet_id` and "
222
+ "(`poll_options` or `poll_duration_minutes`)."
223
+ )
224
+
225
+ # Print the parameters that are not None
226
+ params = {
227
+ "text": text,
228
+ "poll_options": poll_options,
229
+ "poll_duration_minutes": poll_duration_minutes,
230
+ "quote_tweet_id": quote_tweet_id,
231
+ }
232
+ print("You are going to create a tweet with following parameters:")
233
+ for key, value in params.items():
234
+ if value is not None:
235
+ print(f"{key}: {value}")
236
+
237
+ # Add a confirmation prompt at the beginning of the function
238
+ confirm = input("Are you sure you want to create this tweet? (yes/no): ")
239
+ if confirm.lower() != "yes":
240
+ return "Execution cancelled by the user."
241
+
242
+ oauth = get_oauth_session()
243
+ json_data = {}
244
+
245
+ if poll_options is not None and poll_duration_minutes is not None:
246
+ json_data["poll"] = {
247
+ "options": poll_options,
248
+ "duration_minutes": poll_duration_minutes,
249
+ }
250
+
251
+ if quote_tweet_id is not None:
252
+ json_data["quote_tweet_id"] = str(quote_tweet_id) # type: ignore[assignment]
253
+
254
+ json_data["text"] = text # type: ignore[assignment]
255
+
256
+ # Making the request
257
+ response = oauth.post(
258
+ "https://api.twitter.com/2/tweets",
259
+ json=json_data,
260
+ )
261
+
262
+ if response.status_code != HTTPStatus.CREATED:
263
+ error_type = handle_http_error(response)
264
+ # use string concatenation to satisfy flake8
265
+ return (
266
+ "Request returned a(n) "
267
+ + str(error_type)
268
+ + ": "
269
+ + str(response.status_code)
270
+ + " "
271
+ + response.text
272
+ )
273
+
274
+ # Saving the response as JSON
275
+ json_response = response.json()
276
+
277
+ tweet_id = json_response["data"]["id"]
278
+ tweet_text = json_response["data"]["text"]
279
+
280
+ response_str = (
281
+ f"Create tweet successful. "
282
+ f"The tweet ID is: {tweet_id}. "
283
+ f"The tweet text is: '{tweet_text}'."
284
+ )
285
+
286
+ return response_str
287
+
288
+
289
+ def delete_tweet(tweet_id: str) -> str:
290
+ r"""Deletes a tweet with the specified ID for an authorized user.
291
+
292
+ This function sends a DELETE request to the Twitter API to delete
293
+ a tweet with the specified ID. Before sending the request, it
294
+ prompts the user to confirm the deletion.
295
+
296
+ Args:
297
+ tweet_id (str): The ID of the tweet to delete.
298
+
299
+ Returns:
300
+ str: A message indicating the result of the deletion. If the
301
+ deletion was successful, the message includes the ID of the
302
+ deleted tweet. If the deletion was not successful, the message
303
+ includes an error message.
304
+
305
+ Reference:
306
+ https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/delete-tweets-id
307
+ """
308
+ # Print the parameters that are not None
309
+ if tweet_id is not None:
310
+ print(
311
+ f"You are going to delete a tweet with the following "
312
+ f"ID: {tweet_id}"
313
+ )
314
+
315
+ # Add a confirmation prompt at the beginning of the function
316
+ confirm = input("Are you sure you want to delete this tweet? (yes/no): ")
317
+ if confirm.lower() != "yes":
318
+ return "Execution cancelled by the user."
319
+
320
+ oauth = get_oauth_session()
321
+
322
+ # Making the request
323
+ response = oauth.delete(
324
+ f"https://api.twitter.com/2/tweets/{tweet_id}",
325
+ )
326
+
327
+ if response.status_code != HTTPStatus.OK:
328
+ error_type = handle_http_error(response)
329
+ # use string concatenation to satisfy flake8
330
+ return (
331
+ "Request returned a(n) "
332
+ + str(error_type)
333
+ + ": "
334
+ + str(response.status_code)
335
+ + " "
336
+ + response.text
337
+ )
338
+
339
+ # Saving the response as JSON
340
+ json_response = response.json()
341
+ # `deleted_status` may be True or False. Defaults to False if not found.
342
+ deleted_status = json_response.get("data", {}).get("deleted", False)
343
+ response_str = (
344
+ f"Delete tweet successful: {deleted_status}. "
345
+ f"The tweet ID is: {tweet_id}. "
346
+ )
347
+ return response_str
348
+
349
+
350
+ def get_my_user_profile() -> str:
351
+ r"""Retrieves and formats the authenticated user's Twitter profile info.
352
+
353
+ This function sends a GET request to the Twitter API to retrieve the
354
+ authenticated user's profile information, including their pinned tweet.
355
+ It then formats this information into a readable report.
356
+
357
+ Returns:
358
+ str: A formatted report of the authenticated user's Twitter profile
359
+ information. This includes their ID, name, username, description,
360
+ location, most recent tweet ID, profile image URL, account creation
361
+ date, protection status, verification type, public metrics, and
362
+ pinned tweet information. If the request to the Twitter API is not
363
+ successful, the return is an error message.
364
+
365
+ Reference:
366
+ https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me
367
+ """
368
+ oauth = get_oauth_session()
369
+
370
+ tweet_fields = ["created_at", "text"]
371
+ user_fields = [
372
+ "created_at",
373
+ "description",
374
+ "id",
375
+ "location",
376
+ "most_recent_tweet_id",
377
+ "name",
378
+ "pinned_tweet_id",
379
+ "profile_image_url",
380
+ "protected",
381
+ "public_metrics",
382
+ "url",
383
+ "username",
384
+ "verified_type",
385
+ ]
386
+ params = {
387
+ "expansions": "pinned_tweet_id",
388
+ "tweet.fields": ",".join(tweet_fields),
389
+ "user.fields": ",".join(user_fields),
390
+ }
391
+
392
+ response = oauth.get("https://api.twitter.com/2/users/me", params=params)
393
+
394
+ if response.status_code != HTTPStatus.OK:
395
+ error_type = handle_http_error(response)
396
+ error_message = "Request returned a(n) {}: {} {}".format(
397
+ error_type, response.status_code, response.text
398
+ )
399
+ return error_message
400
+
401
+ json_response = response.json()
402
+
403
+ user_info = json_response.get('data', {})
404
+ tweets = json_response.get('includes', {}).get('tweets', [{}])[0]
405
+
406
+ user_report = ""
407
+ user_report += f"ID: {user_info['id']}. "
408
+ user_report += f"Name: {user_info['name']}. "
409
+ user_report += f"Username: {user_info['username']}. "
410
+
411
+ # Define the part of keys that need to be repeatedly processed
412
+ user_info_keys = [
413
+ 'description',
414
+ 'location',
415
+ 'most_recent_tweet_id',
416
+ 'profile_image_url',
417
+ ]
418
+ for key in user_info_keys:
419
+ value = user_info.get(key)
420
+ if user_info.get(key):
421
+ user_report += f"{key.replace('_', ' ').capitalize()}: {value}. "
422
+
423
+ if 'created_at' in user_info:
424
+ created_at = datetime.datetime.strptime(
425
+ user_info['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ"
426
+ )
427
+ date_str = created_at.strftime('%B %d, %Y at %H:%M:%S')
428
+ user_report += f"Account created at: {date_str}. "
429
+
430
+ protection_status = "private" if user_info['protected'] else "public"
431
+ user_report += f"Protected: This user's Tweets are {protection_status}. "
432
+
433
+ verification_messages = {
434
+ 'blue': (
435
+ "The user has a blue verification, typically reserved for "
436
+ "public figures, celebrities, or global brands. "
437
+ ),
438
+ 'business': (
439
+ "The user has a business verification, typically "
440
+ "reserved for businesses and corporations. "
441
+ ),
442
+ 'government': (
443
+ "The user has a government verification, typically "
444
+ "reserved for government officials or entities. "
445
+ ),
446
+ 'none': "The user is not verified. ",
447
+ }
448
+ verification_type = user_info.get('verified_type', 'none')
449
+ user_report += (
450
+ f"Verified type: {verification_messages.get(verification_type)}"
451
+ )
452
+
453
+ if 'public_metrics' in user_info:
454
+ user_report += "Public metrics: "
455
+ metrics = user_info['public_metrics']
456
+ user_report += (
457
+ f"The user has {metrics.get('followers_count', 0)} followers, "
458
+ f"is following {metrics.get('following_count', 0)} users, "
459
+ f"has made {metrics.get('tweet_count', 0)} tweets, "
460
+ f"is listed in {metrics.get('listed_count', 0)} lists, "
461
+ f"and has received {metrics.get('like_count', 0)} likes. "
462
+ )
463
+
464
+ if 'pinned_tweet_id' in user_info:
465
+ user_report += f"Pinned tweet ID: {user_info['pinned_tweet_id']}. "
466
+
467
+ if 'created_at' in tweets and 'text' in tweets:
468
+ user_report += "\nPinned tweet information: "
469
+ tweet_created_at = datetime.datetime.strptime(
470
+ tweets['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ"
471
+ )
472
+ user_report += (
473
+ f"Pinned tweet created at "
474
+ f"{tweet_created_at.strftime('%B %d, %Y at %H:%M:%S')} "
475
+ f"with text: '{tweets['text']}'."
476
+ )
477
+
478
+ return user_report
479
+
480
+
481
+ TWITTER_FUNCS: List[OpenAIFunction] = [
482
+ OpenAIFunction(func) # type: ignore[arg-type]
483
+ for func in [create_tweet, delete_tweet, get_my_user_profile]
484
+ ]
@@ -12,9 +12,9 @@
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
  import os
15
- from typing import List
15
+ from typing import List, Literal
16
16
 
17
- from camel.functions import OpenAIFunction
17
+ from camel.functions.openai_function import OpenAIFunction
18
18
 
19
19
 
20
20
  def get_openweathermap_api_key() -> str:
@@ -29,16 +29,23 @@ def get_openweathermap_api_key() -> str:
29
29
  # Get `OPENWEATHERMAP_API_KEY` here: https://openweathermap.org
30
30
  OPENWEATHERMAP_API_KEY = os.environ.get('OPENWEATHERMAP_API_KEY')
31
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`.")
32
+ raise ValueError(
33
+ "`OPENWEATHERMAP_API_KEY` not found in environment "
34
+ "variables. Get `OPENWEATHERMAP_API_KEY` here: "
35
+ "`https://openweathermap.org`."
36
+ )
35
37
  return OPENWEATHERMAP_API_KEY
36
38
 
37
39
 
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:
40
+ def get_weather_data(
41
+ city: str,
42
+ temp_units: Literal['kelvin', 'celsius', 'fahrenheit'] = 'kelvin',
43
+ wind_units: Literal[
44
+ 'meters_sec', 'miles_hour', 'knots', 'beaufort'
45
+ ] = 'meters_sec',
46
+ visibility_units: Literal['meters', 'miles'] = 'meters',
47
+ time_units: Literal['unix', 'iso', 'date'] = 'unix',
48
+ ) -> str:
42
49
  r"""Fetch and return a comprehensive weather report for a given city as a
43
50
  string. The report includes current weather conditions, temperature,
44
51
  wind details, visibility, and sunrise/sunset times, all formatted as
@@ -46,20 +53,20 @@ def get_weather_data(city: str, temp_units: str = 'kelvin',
46
53
  The function interacts with the OpenWeatherMap API to retrieve the data.
47
54
 
48
55
  Args:
49
- city (string): The name of the city for which the weather information
56
+ city (str): The name of the city for which the weather information
50
57
  is desired. Format "City, CountryCode" (e.g., "Paris, FR"
51
58
  for Paris, France). If the country code is not provided,
52
59
  the API will search for the city in all countries, which
53
60
  may yield incorrect results if multiple cities with the
54
61
  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`)
62
+ temp_units (Literal['kelvin', 'celsius', 'fahrenheit']): Units for
63
+ temperature. (default: :obj:`kelvin`)
64
+ wind_units (Literal['meters_sec', 'miles_hour', 'knots', 'beaufort']):
65
+ Units for wind speed. (default: :obj:`meters_sec`)
66
+ visibility_units (Literal['meters', 'miles']): Units for visibility
67
+ distance. (default: :obj:`meters`)
68
+ time_units (Literal['unix', 'iso', 'date']): Format for sunrise and
69
+ sunset times. (default: :obj:`unix`)
63
70
 
64
71
  Returns:
65
72
  str: A string containing the fetched weather data, formatted in a
@@ -84,7 +91,8 @@ def get_weather_data(city: str, temp_units: str = 'kelvin',
84
91
  except ImportError:
85
92
  raise ImportError(
86
93
  "Please install `pyowm` first. You can install it by running "
87
- "`pip install pyowm`.")
94
+ "`pip install pyowm`."
95
+ )
88
96
 
89
97
  OPENWEATHERMAP_API_KEY = get_openweathermap_api_key()
90
98
  owm = pyowm.OWM(OPENWEATHERMAP_API_KEY)
@@ -105,8 +113,11 @@ def get_weather_data(city: str, temp_units: str = 'kelvin',
105
113
 
106
114
  # Visibility
107
115
  visibility_distance = observation.weather.visibility_distance
108
- visibility = (str(visibility_distance) if visibility_units == 'meters'
109
- else str(observation.weather.visibility(unit='miles')))
116
+ visibility = (
117
+ str(visibility_distance)
118
+ if visibility_units == 'meters'
119
+ else str(observation.weather.visibility(unit='miles'))
120
+ )
110
121
 
111
122
  # Sunrise and Sunset
112
123
  sunrise_time = str(weather.sunrise_time(timeformat=time_units))
@@ -120,14 +131,16 @@ def get_weather_data(city: str, temp_units: str = 'kelvin',
120
131
  f"Min temp: {temperature['temp_min']}°{temp_units.title()}. "
121
132
  f"Wind: {wind_speed} {wind_units} at {wind_deg} degrees. "
122
133
  f"Visibility: {visibility} {visibility_units}. "
123
- f"Sunrise at {sunrise_time}, Sunset at {sunset_time}.")
134
+ f"Sunrise at {sunrise_time}, Sunset at {sunset_time}."
135
+ )
124
136
 
125
137
  return weather_report
126
138
 
127
139
  except Exception as e:
128
140
  error_message = (
129
141
  f"An error occurred while fetching weather data for {city}: "
130
- f"{str(e)}.")
142
+ f"{e!s}."
143
+ )
131
144
  return error_message
132
145
 
133
146