camel-ai 0.1.5.1__py3-none-any.whl → 0.1.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (86) hide show
  1. camel/agents/__init__.py +2 -0
  2. camel/agents/chat_agent.py +237 -52
  3. camel/agents/critic_agent.py +6 -9
  4. camel/agents/deductive_reasoner_agent.py +93 -40
  5. camel/agents/embodied_agent.py +6 -9
  6. camel/agents/knowledge_graph_agent.py +49 -27
  7. camel/agents/role_assignment_agent.py +14 -12
  8. camel/agents/search_agent.py +122 -0
  9. camel/agents/task_agent.py +26 -38
  10. camel/bots/__init__.py +20 -0
  11. camel/bots/discord_bot.py +103 -0
  12. camel/bots/telegram_bot.py +84 -0
  13. camel/configs/__init__.py +3 -0
  14. camel/configs/anthropic_config.py +1 -1
  15. camel/configs/litellm_config.py +113 -0
  16. camel/configs/openai_config.py +14 -0
  17. camel/embeddings/__init__.py +2 -0
  18. camel/embeddings/openai_embedding.py +2 -2
  19. camel/embeddings/sentence_transformers_embeddings.py +6 -5
  20. camel/embeddings/vlm_embedding.py +146 -0
  21. camel/functions/__init__.py +9 -0
  22. camel/functions/open_api_function.py +161 -33
  23. camel/functions/open_api_specs/biztoc/__init__.py +13 -0
  24. camel/functions/open_api_specs/biztoc/ai-plugin.json +34 -0
  25. camel/functions/open_api_specs/biztoc/openapi.yaml +21 -0
  26. camel/functions/open_api_specs/create_qr_code/__init__.py +13 -0
  27. camel/functions/open_api_specs/create_qr_code/openapi.yaml +44 -0
  28. camel/functions/open_api_specs/nasa_apod/__init__.py +13 -0
  29. camel/functions/open_api_specs/nasa_apod/openapi.yaml +72 -0
  30. camel/functions/open_api_specs/outschool/__init__.py +13 -0
  31. camel/functions/open_api_specs/outschool/ai-plugin.json +34 -0
  32. camel/functions/open_api_specs/outschool/openapi.yaml +1 -0
  33. camel/functions/open_api_specs/outschool/paths/__init__.py +14 -0
  34. camel/functions/open_api_specs/outschool/paths/get_classes.py +29 -0
  35. camel/functions/open_api_specs/outschool/paths/search_teachers.py +29 -0
  36. camel/functions/open_api_specs/security_config.py +21 -0
  37. camel/functions/open_api_specs/web_scraper/__init__.py +13 -0
  38. camel/functions/open_api_specs/web_scraper/ai-plugin.json +34 -0
  39. camel/functions/open_api_specs/web_scraper/openapi.yaml +71 -0
  40. camel/functions/open_api_specs/web_scraper/paths/__init__.py +13 -0
  41. camel/functions/open_api_specs/web_scraper/paths/scraper.py +29 -0
  42. camel/functions/openai_function.py +3 -1
  43. camel/functions/search_functions.py +104 -171
  44. camel/functions/slack_functions.py +16 -3
  45. camel/human.py +3 -1
  46. camel/loaders/base_io.py +3 -1
  47. camel/loaders/unstructured_io.py +16 -22
  48. camel/messages/base.py +135 -46
  49. camel/models/__init__.py +8 -0
  50. camel/models/anthropic_model.py +24 -16
  51. camel/models/base_model.py +6 -1
  52. camel/models/litellm_model.py +112 -0
  53. camel/models/model_factory.py +44 -16
  54. camel/models/nemotron_model.py +71 -0
  55. camel/models/ollama_model.py +121 -0
  56. camel/models/open_source_model.py +8 -2
  57. camel/models/openai_model.py +14 -5
  58. camel/models/stub_model.py +3 -1
  59. camel/models/zhipuai_model.py +125 -0
  60. camel/prompts/__init__.py +6 -0
  61. camel/prompts/base.py +2 -1
  62. camel/prompts/descripte_video_prompt.py +33 -0
  63. camel/prompts/generate_text_embedding_data.py +79 -0
  64. camel/prompts/task_prompt_template.py +13 -3
  65. camel/retrievers/auto_retriever.py +20 -11
  66. camel/retrievers/base.py +4 -2
  67. camel/retrievers/bm25_retriever.py +2 -1
  68. camel/retrievers/cohere_rerank_retriever.py +2 -1
  69. camel/retrievers/vector_retriever.py +10 -4
  70. camel/societies/babyagi_playing.py +2 -1
  71. camel/societies/role_playing.py +18 -20
  72. camel/storages/graph_storages/base.py +1 -0
  73. camel/storages/graph_storages/neo4j_graph.py +5 -3
  74. camel/storages/vectordb_storages/base.py +2 -1
  75. camel/storages/vectordb_storages/milvus.py +5 -2
  76. camel/toolkits/github_toolkit.py +120 -26
  77. camel/types/__init__.py +5 -2
  78. camel/types/enums.py +95 -4
  79. camel/utils/__init__.py +11 -2
  80. camel/utils/commons.py +78 -4
  81. camel/utils/constants.py +26 -0
  82. camel/utils/token_counting.py +62 -7
  83. {camel_ai-0.1.5.1.dist-info → camel_ai-0.1.5.3.dist-info}/METADATA +82 -53
  84. camel_ai-0.1.5.3.dist-info/RECORD +151 -0
  85. camel_ai-0.1.5.1.dist-info/RECORD +0 -119
  86. {camel_ai-0.1.5.1.dist-info → camel_ai-0.1.5.3.dist-info}/WHEEL +0 -0
@@ -18,11 +18,19 @@ from camel.prompts.ai_society import (
18
18
  TextPromptDict,
19
19
  )
20
20
  from camel.prompts.code import CodePromptTemplateDict
21
+ from camel.prompts.descripte_video_prompt import (
22
+ DescriptionVideoPromptTemplateDict,
23
+ )
21
24
  from camel.prompts.evaluation import (
22
25
  EvaluationPromptTemplateDict,
23
26
  )
27
+ from camel.prompts.generate_text_embedding_data import (
28
+ GenerateTextEmbeddingDataPromptTemplateDict,
29
+ )
24
30
  from camel.prompts.misalignment import MisalignmentPromptTemplateDict
25
- from camel.prompts.object_recognition import ObjectRecognitionPromptTemplateDict
31
+ from camel.prompts.object_recognition import (
32
+ ObjectRecognitionPromptTemplateDict,
33
+ )
26
34
  from camel.prompts.role_description_prompt_template import (
27
35
  RoleDescriptionPromptTemplateDict,
28
36
  )
@@ -52,8 +60,10 @@ class TaskPromptTemplateDict(Dict[Any, TextPromptDict]):
52
60
  TaskType.MISALIGNMENT: MisalignmentPromptTemplateDict(),
53
61
  TaskType.TRANSLATION: TranslationPromptTemplateDict(),
54
62
  TaskType.EVALUATION: EvaluationPromptTemplateDict(),
55
- TaskType.SOLUTION_EXTRACTION: SolutionExtractionPromptTemplateDict(),
63
+ TaskType.SOLUTION_EXTRACTION: SolutionExtractionPromptTemplateDict(), # noqa: E501
56
64
  TaskType.ROLE_DESCRIPTION: RoleDescriptionPromptTemplateDict(),
57
- TaskType.OBJECT_RECOGNITION: ObjectRecognitionPromptTemplateDict(),
65
+ TaskType.OBJECT_RECOGNITION: ObjectRecognitionPromptTemplateDict(), # noqa: E501
66
+ TaskType.GENERATE_TEXT_EMBEDDING_DATA: GenerateTextEmbeddingDataPromptTemplateDict(), # noqa: E501
67
+ TaskType.VIDEO_DESCRIPTION: DescriptionVideoPromptTemplateDict(), # noqa: E501
58
68
  }
59
69
  )
@@ -42,7 +42,7 @@ class AutoRetriever:
42
42
  vector_storage_local_path (Optional[str]): Local path for vector
43
43
  storage, if applicable.
44
44
  storage_type (Optional[StorageType]): The type of vector storage to
45
- use. Defaults to `StorageType.MILVUS`.
45
+ use. Defaults to `StorageType.QDRANT`.
46
46
  embedding_model (Optional[BaseEmbedding]): Model used for embedding
47
47
  queries and documents. Defaults to `OpenAIEmbedding()`.
48
48
  """
@@ -54,7 +54,7 @@ class AutoRetriever:
54
54
  storage_type: Optional[StorageType] = None,
55
55
  embedding_model: Optional[BaseEmbedding] = None,
56
56
  ):
57
- self.storage_type = storage_type or StorageType.MILVUS
57
+ self.storage_type = storage_type or StorageType.QDRANT
58
58
  self.embedding_model = embedding_model or OpenAIEmbedding()
59
59
  self.vector_storage_local_path = vector_storage_local_path
60
60
  self.url_and_api_key = url_and_api_key
@@ -101,8 +101,8 @@ class AutoRetriever:
101
101
  r"""Generates a valid collection name from a given file path or URL.
102
102
 
103
103
  Args:
104
- content_input_path: str. The input URL or file path from which to
105
- generate the collection name.
104
+ content_input_path: str. The input URL or file path from which to
105
+ generate the collection name.
106
106
 
107
107
  Returns:
108
108
  str: A sanitized, valid collection name suitable for use.
@@ -134,7 +134,9 @@ class AutoRetriever:
134
134
  collection_name = collection_name[:30]
135
135
  return collection_name
136
136
 
137
- def _get_file_modified_date_from_file(self, content_input_path: str) -> str:
137
+ def _get_file_modified_date_from_file(
138
+ self, content_input_path: str
139
+ ) -> str:
138
140
  r"""Retrieves the last modified date and time of a given file. This
139
141
  function takes a file path as input and returns the last modified date
140
142
  and time of that file.
@@ -147,9 +149,9 @@ class AutoRetriever:
147
149
  str: The last modified time from file.
148
150
  """
149
151
  mod_time = os.path.getmtime(content_input_path)
150
- readable_mod_time = datetime.datetime.fromtimestamp(mod_time).isoformat(
151
- timespec='seconds'
152
- )
152
+ readable_mod_time = datetime.datetime.fromtimestamp(
153
+ mod_time
154
+ ).isoformat(timespec='seconds')
153
155
  return readable_mod_time
154
156
 
155
157
  def _get_file_modified_date_from_storage(
@@ -211,7 +213,7 @@ class AutoRetriever:
211
213
  `DEFAULT_SIMILARITY_THRESHOLD`.
212
214
  return_detailed_info (bool, optional): Whether to return detailed
213
215
  information including similarity score, content path and
214
- metadata. Defaults to False.
216
+ metadata. Defaults to `False`.
215
217
 
216
218
  Returns:
217
219
  string: By default, returns only the text information. If
@@ -321,8 +323,15 @@ class AutoRetriever:
321
323
  info['text'] for info in all_retrieved_info if 'text' in info
322
324
  )
323
325
 
324
- detailed_info = f"Original Query:\n{{ {query} }}\nRetrieved Context:\n{retrieved_infos}"
325
- text_info = f"Original Query:\n{{ {query} }}\nRetrieved Context:\n{retrieved_infos_text}"
326
+ detailed_info = (
327
+ f"Original Query:\n{{ {query} }}\n"
328
+ f"Retrieved Context:\n{retrieved_infos}"
329
+ )
330
+
331
+ text_info = (
332
+ f"Original Query:\n{{ {query} }}\n"
333
+ f"Retrieved Context:\n{retrieved_infos_text}"
334
+ )
326
335
 
327
336
  if return_detailed_info:
328
337
  return detailed_info
camel/retrievers/base.py CHANGED
@@ -32,7 +32,8 @@ def _query_unimplemented(self, *input: Any) -> None:
32
32
  registered hooks while the latter silently ignores them.
33
33
  """
34
34
  raise NotImplementedError(
35
- f"Retriever [{type(self).__name__}] is missing the required \"query\" function"
35
+ f"Retriever [{type(self).__name__}] is missing the required"
36
+ " \"query\" function"
36
37
  )
37
38
 
38
39
 
@@ -52,7 +53,8 @@ def _process_unimplemented(self, *input: Any) -> None:
52
53
  registered hooks while the latter silently ignores them.
53
54
  """
54
55
  raise NotImplementedError(
55
- f"Retriever [{type(self).__name__}] is missing the required \"process\" function"
56
+ f"Retriever [{type(self).__name__}] is missing the required "
57
+ "\"process\" function"
56
58
  )
57
59
 
58
60
 
@@ -47,7 +47,8 @@ class BM25Retriever(BaseRetriever):
47
47
  from rank_bm25 import BM25Okapi
48
48
  except ImportError as e:
49
49
  raise ImportError(
50
- "Package `rank_bm25` not installed, install by running 'pip install rank_bm25'"
50
+ "Package `rank_bm25` not installed, install by running 'pip "
51
+ "install rank_bm25'"
51
52
  ) from e
52
53
 
53
54
  self.bm25: BM25Okapi = None
@@ -66,7 +66,8 @@ class CohereRerankRetriever(BaseRetriever):
66
66
  self.api_key = api_key or os.environ["COHERE_API_KEY"]
67
67
  except ValueError as e:
68
68
  raise ValueError(
69
- "Must pass in cohere api key or specify via COHERE_API_KEY environment variable."
69
+ "Must pass in cohere api key or specify via COHERE_API_KEY"
70
+ " environment variable."
70
71
  ) from e
71
72
 
72
73
  self.co = cohere.Client(self.api_key)
@@ -64,7 +64,9 @@ class VectorRetriever(BaseRetriever):
64
64
  self.storage = (
65
65
  storage
66
66
  if storage is not None
67
- else QdrantStorage(vector_dim=self.embedding_model.get_output_dim())
67
+ else QdrantStorage(
68
+ vector_dim=self.embedding_model.get_output_dim()
69
+ )
68
70
  )
69
71
  self.similarity_threshold = similarity_threshold
70
72
  self.unstructured_modules: UnstructuredIO = UnstructuredIO()
@@ -151,7 +153,8 @@ class VectorRetriever(BaseRetriever):
151
153
 
152
154
  if query_results[0].record.payload is None:
153
155
  raise ValueError(
154
- "Payload of vector storage is None, please check the collection."
156
+ "Payload of vector storage is None, please check the "
157
+ "collection."
155
158
  )
156
159
 
157
160
  # format the results
@@ -176,8 +179,11 @@ class VectorRetriever(BaseRetriever):
176
179
  if not formatted_results:
177
180
  return [
178
181
  {
179
- 'text': f"""No suitable information retrieved from {content_path} \
180
- with similarity_threshold = {self.similarity_threshold}."""
182
+ 'text': (
183
+ f"No suitable information retrieved "
184
+ f"from {content_path} with similarity_threshold"
185
+ f" = {self.similarity_threshold}."
186
+ )
181
187
  }
182
188
  ]
183
189
  return formatted_results
@@ -150,7 +150,8 @@ class BabyAGI:
150
150
  if self.task_type in [TaskType.AI_SOCIETY, TaskType.MISALIGNMENT]:
151
151
  task_specify_meta_dict.update(
152
152
  dict(
153
- assistant_role=assistant_role_name, user_role=user_role_name
153
+ assistant_role=assistant_role_name,
154
+ user_role=user_role_name,
154
155
  )
155
156
  )
156
157
  task_specify_meta_dict.update(extend_task_specify_meta_dict or {})
@@ -22,9 +22,10 @@ from camel.agents import (
22
22
  from camel.generators import SystemMessageGenerator
23
23
  from camel.human import Human
24
24
  from camel.messages import BaseMessage
25
+ from camel.models import BaseModelBackend
25
26
  from camel.prompts import TextPrompt
26
27
  from camel.responses import ChatAgentResponse
27
- from camel.types import ModelType, RoleType, TaskType
28
+ from camel.types import RoleType, TaskType
28
29
 
29
30
 
30
31
  class RolePlaying:
@@ -48,9 +49,9 @@ class RolePlaying:
48
49
  in the loop. (default: :obj:`False`)
49
50
  critic_criteria (str, optional): Critic criteria for the critic agent.
50
51
  If not specified, set the criteria to improve task performance.
51
- model_type (ModelType, optional): Model type that will be used for
52
- role playing. If specified, it will override the model in all
53
- agents. (default: :obj:`None`)
52
+ model (BaseModelBackend, optional): The model backend to use for
53
+ generating responses. If specified, it will override the model in
54
+ all agents. (default: :obj:`None`)
54
55
  task_type (TaskType, optional): The type of task to perform.
55
56
  (default: :obj:`TaskType.AI_SOCIETY`)
56
57
  assistant_agent_kwargs (Dict, optional): Additional arguments to pass
@@ -84,7 +85,7 @@ class RolePlaying:
84
85
  with_task_planner: bool = False,
85
86
  with_critic_in_the_loop: bool = False,
86
87
  critic_criteria: Optional[str] = None,
87
- model_type: Optional[ModelType] = None,
88
+ model: Optional[BaseModelBackend] = None,
88
89
  task_type: TaskType = TaskType.AI_SOCIETY,
89
90
  assistant_agent_kwargs: Optional[Dict] = None,
90
91
  user_agent_kwargs: Optional[Dict] = None,
@@ -99,7 +100,7 @@ class RolePlaying:
99
100
  self.with_task_specify = with_task_specify
100
101
  self.with_task_planner = with_task_planner
101
102
  self.with_critic_in_the_loop = with_critic_in_the_loop
102
- self.model_type = model_type
103
+ self.model = model
103
104
  self.task_type = task_type
104
105
  self.task_prompt = task_prompt
105
106
 
@@ -189,12 +190,10 @@ class RolePlaying:
189
190
  )
190
191
  )
191
192
  task_specify_meta_dict.update(extend_task_specify_meta_dict or {})
192
- if self.model_type is not None:
193
+ if self.model is not None:
193
194
  if task_specify_agent_kwargs is None:
194
195
  task_specify_agent_kwargs = {}
195
- task_specify_agent_kwargs.update(
196
- dict(model_type=self.model_type)
197
- )
196
+ task_specify_agent_kwargs.update(dict(model=self.model))
198
197
  task_specify_agent = TaskSpecifyAgent(
199
198
  task_type=self.task_type,
200
199
  output_language=output_language,
@@ -224,12 +223,10 @@ class RolePlaying:
224
223
  agents. (default: :obj:`None`)
225
224
  """
226
225
  if self.with_task_planner:
227
- if self.model_type is not None:
226
+ if self.model is not None:
228
227
  if task_planner_agent_kwargs is None:
229
228
  task_planner_agent_kwargs = {}
230
- task_planner_agent_kwargs.update(
231
- dict(model_type=self.model_type)
232
- )
229
+ task_planner_agent_kwargs.update(dict(model=self.model))
233
230
  task_planner_agent = TaskPlannerAgent(
234
231
  output_language=output_language,
235
232
  **(task_planner_agent_kwargs or {}),
@@ -274,7 +271,8 @@ class RolePlaying:
274
271
  ]:
275
272
  extend_sys_msg_meta_dicts = [
276
273
  dict(
277
- assistant_role=assistant_role_name, user_role=user_role_name
274
+ assistant_role=assistant_role_name,
275
+ user_role=user_role_name,
278
276
  )
279
277
  for _ in range(2)
280
278
  ]
@@ -320,13 +318,13 @@ class RolePlaying:
320
318
  output_language (str, optional): The language to be output by the
321
319
  agents. (default: :obj:`None`)
322
320
  """
323
- if self.model_type is not None:
321
+ if self.model is not None:
324
322
  if assistant_agent_kwargs is None:
325
323
  assistant_agent_kwargs = {}
326
- assistant_agent_kwargs.update(dict(model_type=self.model_type))
324
+ assistant_agent_kwargs.update(dict(model=self.model))
327
325
  if user_agent_kwargs is None:
328
326
  user_agent_kwargs = {}
329
- user_agent_kwargs.update(dict(model_type=self.model_type))
327
+ user_agent_kwargs.update(dict(model=self.model))
330
328
 
331
329
  self.assistant_agent = ChatAgent(
332
330
  init_assistant_sys_msg,
@@ -382,10 +380,10 @@ class RolePlaying:
382
380
  critic_msg_meta_dict,
383
381
  role_tuple=(critic_role_name, RoleType.CRITIC),
384
382
  )
385
- if self.model_type is not None:
383
+ if self.model is not None:
386
384
  if critic_kwargs is None:
387
385
  critic_kwargs = {}
388
- critic_kwargs.update(dict(model_type=self.model_type))
386
+ critic_kwargs.update(dict(model=self.model))
389
387
  self.critic = CriticAgent(
390
388
  self.critic_sys_msg,
391
389
  **(critic_kwargs or {}),
@@ -70,6 +70,7 @@ class BaseGraphStorage(ABC):
70
70
  self, query: str, params: Optional[Dict[str, Any]] = None
71
71
  ) -> List[Dict[str, Any]]:
72
72
  r"""Query the graph store with statement and parameters.
73
+
73
74
  Args:
74
75
  query (str): The query to be executed.
75
76
  params (Optional[Dict[str, Any]]): A dictionary of parameters to
@@ -99,7 +99,9 @@ class Neo4jGraph(BaseGraphStorage):
99
99
  "Please install it with `pip install neo4j`."
100
100
  )
101
101
 
102
- self.driver = neo4j.GraphDatabase.driver(url, auth=(username, password))
102
+ self.driver = neo4j.GraphDatabase.driver(
103
+ url, auth=(username, password)
104
+ )
103
105
  self.database = database
104
106
  self.timeout = timeout
105
107
  self.truncate = truncate
@@ -373,8 +375,8 @@ class Neo4jGraph(BaseGraphStorage):
373
375
  with self.driver.session(database=self.database) as session:
374
376
  session.run(
375
377
  (
376
- "MATCH (n1:{})-[r:{}]->(n2:{}) WHERE n1.id = $subj AND n2.id"
377
- " = $obj DELETE r"
378
+ "MATCH (n1:{})-[r:{}]->(n2:{}) WHERE n1.id = $subj AND"
379
+ " n2.id = $obj DELETE r"
378
380
  ).format(
379
381
  BASE_ENTITY_LABEL.replace("_", ""),
380
382
  rel,
@@ -147,7 +147,8 @@ class BaseVectorStorage(ABC):
147
147
  query: VectorDBQuery,
148
148
  **kwargs: Any,
149
149
  ) -> List[VectorDBQueryResult]:
150
- r"""Searches for similar vectors in the storage based on the provided query.
150
+ r"""Searches for similar vectors in the storage based on the provided
151
+ query.
151
152
 
152
153
  Args:
153
154
  query (VectorDBQuery): The query object containing the search
@@ -155,7 +155,8 @@ class MilvusStorage(BaseVectorStorage):
155
155
  field_name="payload",
156
156
  datatype=DataType.JSON,
157
157
  description=(
158
- 'Any additional metadata or information related' 'to the vector'
158
+ 'Any additional metadata or information related'
159
+ 'to the vector'
159
160
  ),
160
161
  )
161
162
 
@@ -267,7 +268,9 @@ class MilvusStorage(BaseVectorStorage):
267
268
  for record in records:
268
269
  record_dict = {
269
270
  "id": record.id,
270
- "payload": record.payload if record.payload is not None else '',
271
+ "payload": record.payload
272
+ if record.payload is not None
273
+ else '',
271
274
  "vector": record.vector,
272
275
  }
273
276
  validated_data.append(record_dict)
@@ -14,6 +14,7 @@
14
14
 
15
15
  import os
16
16
  from dataclasses import dataclass
17
+ from datetime import datetime, timedelta
17
18
  from typing import List, Optional
18
19
 
19
20
  from camel.functions import OpenAIFunction
@@ -48,7 +49,8 @@ class GithubIssue:
48
49
  body (str): The body/content of the GitHub issue.
49
50
  number (int): The issue number.
50
51
  file_path (str): The path of the file associated with the issue.
51
- file_content (str): The content of the file associated with the issue.
52
+ file_content (str): The content of the file associated with the
53
+ issue.
52
54
  """
53
55
  self.title = title
54
56
  self.body = body
@@ -56,11 +58,12 @@ class GithubIssue:
56
58
  self.file_path = file_path
57
59
  self.file_content = file_content
58
60
 
59
- def summary(self) -> str:
60
- r"""Returns a summary of the issue.
61
+ def __str__(self) -> str:
62
+ r"""Returns a string representation of the issue.
61
63
 
62
64
  Returns:
63
- str: A string containing the title, body, number, file path, and file content of the issue.
65
+ str: A string containing the title, body, number, file path, and
66
+ file content of the issue.
64
67
  """
65
68
  return (
66
69
  f"Title: {self.title}\n"
@@ -71,16 +74,60 @@ class GithubIssue:
71
74
  )
72
75
 
73
76
 
77
+ @dataclass
78
+ class GithubPullRequestDiff:
79
+ r"""Represents a single diff of a pull request on Github.
80
+
81
+ Attributes:
82
+ filename (str): The name of the file that was changed.
83
+ patch (str): The diff patch for the file.
84
+ """
85
+
86
+ filename: str
87
+ patch: str
88
+
89
+ def __str__(self) -> str:
90
+ r"""Returns a string representation of this diff."""
91
+ return f"Filename: {self.filename}\nPatch: {self.patch}"
92
+
93
+
94
+ @dataclass
95
+ class GithubPullRequest:
96
+ r"""Represents a pull request on Github.
97
+
98
+ Attributes:
99
+ title (str): The title of the GitHub pull request.
100
+ body (str): The body/content of the GitHub pull request.
101
+ diffs (List[GithubPullRequestDiff]): A list of diffs for the pull
102
+ request.
103
+ """
104
+
105
+ title: str
106
+ body: str
107
+ diffs: List[GithubPullRequestDiff]
108
+
109
+ def __str__(self) -> str:
110
+ r"""Returns a string representation of the pull request."""
111
+ diff_summaries = '\n'.join(str(diff) for diff in self.diffs)
112
+ return (
113
+ f"Title: {self.title}\n"
114
+ f"Body: {self.body}\n"
115
+ f"Diffs: {diff_summaries}\n"
116
+ )
117
+
118
+
74
119
  class GithubToolkit(BaseToolkit):
75
- r"""A class representing a toolkit for interacting with GitHub repositories.
120
+ r"""A class representing a toolkit for interacting with GitHub
121
+ repositories.
76
122
 
77
- This class provides methods for retrieving open issues, retrieving specific issues,
78
- and creating pull requests in a GitHub repository.
123
+ This class provides methods for retrieving open issues, retrieving
124
+ specific issues, and creating pull requests in a GitHub repository.
79
125
 
80
126
  Args:
81
127
  repo_name (str): The name of the GitHub repository.
82
- access_token (str, optional): The access token to authenticate with GitHub.
83
- If not provided, it will be obtained using the `get_github_access_token` method.
128
+ access_token (str, optional): The access token to authenticate with
129
+ GitHub. If not provided, it will be obtained using the
130
+ `get_github_access_token` method.
84
131
  """
85
132
 
86
133
  def __init__(
@@ -90,8 +137,9 @@ class GithubToolkit(BaseToolkit):
90
137
 
91
138
  Args:
92
139
  repo_name (str): The name of the GitHub repository.
93
- access_token (str, optional): The access token to authenticate with GitHub.
94
- If not provided, it will be obtained using the `get_github_access_token` method.
140
+ access_token (str, optional): The access token to authenticate
141
+ with GitHub. If not provided, it will be obtained using the
142
+ `get_github_access_token` method.
95
143
  """
96
144
  if access_token is None:
97
145
  access_token = self.get_github_access_token()
@@ -101,21 +149,24 @@ class GithubToolkit(BaseToolkit):
101
149
  except ImportError:
102
150
  raise ImportError(
103
151
  "Please install `github` first. You can install it by running "
104
- "`pip install wikipedia`."
152
+ "`pip install pygithub`."
105
153
  )
106
154
  self.github = Github(auth=Auth.Token(access_token))
107
155
  self.repo = self.github.get_repo(repo_name)
108
156
 
109
157
  def get_tools(self) -> List[OpenAIFunction]:
110
- r"""Returns a list of OpenAIFunction objects representing the functions in the toolkit.
158
+ r"""Returns a list of OpenAIFunction objects representing the
159
+ functions in the toolkit.
111
160
 
112
161
  Returns:
113
- List[OpenAIFunction]: A list of OpenAIFunction objects representing the functions in the toolkit.
162
+ List[OpenAIFunction]: A list of OpenAIFunction objects
163
+ representing the functions in the toolkit.
114
164
  """
115
165
  return [
116
166
  OpenAIFunction(self.retrieve_issue_list),
117
167
  OpenAIFunction(self.retrieve_issue),
118
168
  OpenAIFunction(self.create_pull_request),
169
+ OpenAIFunction(self.retrieve_pull_requests),
119
170
  ]
120
171
 
121
172
  def get_github_access_token(self) -> str:
@@ -125,15 +176,16 @@ class GithubToolkit(BaseToolkit):
125
176
  str: A string containing the GitHub access token.
126
177
 
127
178
  Raises:
128
- ValueError: If the API key or secret is not found in the environment variables.
179
+ ValueError: If the API key or secret is not found in the
180
+ environment variables.
129
181
  """
130
182
  # Get `GITHUB_ACCESS_TOKEN` here: https://github.com/settings/tokens
131
183
  GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN")
132
184
 
133
185
  if not GITHUB_ACCESS_TOKEN:
134
186
  raise ValueError(
135
- "`GITHUB_ACCESS_TOKEN` not found in environment variables. Get it "
136
- "here: `https://github.com/settings/tokens`."
187
+ "`GITHUB_ACCESS_TOKEN` not found in environment variables. Get"
188
+ " it here: `https://github.com/settings/tokens`."
137
189
  )
138
190
  return GITHUB_ACCESS_TOKEN
139
191
 
@@ -151,7 +203,7 @@ class GithubToolkit(BaseToolkit):
151
203
  number=issue.number,
152
204
  file_path=issue.labels[
153
205
  0
154
- ].name, # for now we require file path to be the first label in the PR
206
+ ].name, # we require file path to be the first label in the PR
155
207
  file_content=self.retrieve_file_content(issue.labels[0].name),
156
208
  )
157
209
  for issue in issues
@@ -173,9 +225,47 @@ class GithubToolkit(BaseToolkit):
173
225
  issues = self.retrieve_issue_list()
174
226
  for issue in issues:
175
227
  if issue.number == issue_number:
176
- return issue.summary()
228
+ return str(issue)
177
229
  return None
178
230
 
231
+ def retrieve_pull_requests(
232
+ self, days: int, state: str, max_prs: int
233
+ ) -> List[str]:
234
+ r"""Retrieves a summary of merged pull requests from the repository.
235
+ The summary will be provided for the last specified number of days.
236
+
237
+ Args:
238
+ days (int): The number of days to retrieve merged pull requests
239
+ for.
240
+ state (str): A specific state of PRs to retrieve. Can be open or
241
+ closed.
242
+ max_prs (int): The maximum number of PRs to retrieve.
243
+
244
+ Returns:
245
+ List[str]: A list of merged pull request summaries.
246
+ """
247
+ pull_requests = self.repo.get_pulls(state=state)
248
+ merged_prs = []
249
+ earliest_date: datetime = datetime.utcnow() - timedelta(days=days)
250
+
251
+ for pr in pull_requests[:max_prs]:
252
+ if (
253
+ pr.merged
254
+ and pr.merged_at is not None
255
+ and pr.merged_at.timestamp() > earliest_date.timestamp()
256
+ ):
257
+ pr_details = GithubPullRequest(pr.title, pr.body, [])
258
+
259
+ # Get files changed in the PR
260
+ files = pr.get_files()
261
+
262
+ for file in files:
263
+ diff = GithubPullRequestDiff(file.filename, file.patch)
264
+ pr_details.diffs.append(diff)
265
+
266
+ merged_prs.append(str(pr_details))
267
+ return merged_prs
268
+
179
269
  def create_pull_request(
180
270
  self,
181
271
  file_path: str,
@@ -186,19 +276,23 @@ class GithubToolkit(BaseToolkit):
186
276
  ) -> str:
187
277
  r"""Creates a pull request.
188
278
 
189
- This function creates a pull request in specified repository, which updates a
190
- file in the specific path with new content. The pull request description
191
- contains information about the issue title and number.
279
+ This function creates a pull request in specified repository, which
280
+ updates a file in the specific path with new content. The pull request
281
+ description contains information about the issue title and number.
192
282
 
193
283
  Args:
194
- file_path (str): The path of the file to be updated in the repository.
284
+ file_path (str): The path of the file to be updated in the
285
+ repository.
195
286
  new_content (str): The specified new content of the specified file.
196
- pr_title (str): The title of the issue that is solved by this pull request.
287
+ pr_title (str): The title of the issue that is solved by this pull
288
+ request.
197
289
  body (str): The commit message for the pull request.
198
- branch_name (str): The name of the branch to create and submit the pull request from.
290
+ branch_name (str): The name of the branch to create and submit the
291
+ pull request from.
199
292
 
200
293
  Returns:
201
- str: A formatted report of whether the pull request was created successfully or not.
294
+ str: A formatted report of whether the pull request was created
295
+ successfully or not.
202
296
  """
203
297
  sb = self.repo.get_branch(self.repo.default_branch)
204
298
  self.repo.create_git_ref(
camel/types/__init__.py CHANGED
@@ -14,10 +14,11 @@
14
14
  from .enums import (
15
15
  AudioModelType,
16
16
  EmbeddingModelType,
17
+ ModelPlatformType,
17
18
  ModelType,
18
19
  OpenAIBackendRole,
19
- OpenAIImageDetailType,
20
20
  OpenAIImageType,
21
+ OpenAIVisionDetailType,
21
22
  OpenAPIName,
22
23
  RoleType,
23
24
  StorageType,
@@ -58,9 +59,11 @@ __all__ = [
58
59
  'ChatCompletionAssistantMessageParam',
59
60
  'ChatCompletionFunctionMessageParam',
60
61
  'CompletionUsage',
62
+ 'OpenAIVideoType',
61
63
  'OpenAIImageType',
62
- 'OpenAIImageDetailType',
64
+ 'OpenAIVisionDetailType',
63
65
  'OpenAPIName',
66
+ 'ModelPlatformType',
64
67
  'AudioModelType',
65
68
  'VoiceType',
66
69
  ]