camel-ai 0.1.3__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 (45) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/__init__.py +2 -0
  3. camel/agents/chat_agent.py +40 -53
  4. camel/agents/knowledge_graph_agent.py +221 -0
  5. camel/configs/__init__.py +29 -0
  6. camel/configs/anthropic_config.py +73 -0
  7. camel/configs/base_config.py +22 -0
  8. camel/configs/openai_config.py +132 -0
  9. camel/embeddings/openai_embedding.py +7 -2
  10. camel/functions/__init__.py +13 -8
  11. camel/functions/open_api_function.py +380 -0
  12. camel/functions/open_api_specs/coursera/__init__.py +13 -0
  13. camel/functions/open_api_specs/coursera/openapi.yaml +82 -0
  14. camel/functions/open_api_specs/klarna/__init__.py +13 -0
  15. camel/functions/open_api_specs/klarna/openapi.yaml +87 -0
  16. camel/functions/open_api_specs/speak/__init__.py +13 -0
  17. camel/functions/open_api_specs/speak/openapi.yaml +151 -0
  18. camel/functions/openai_function.py +3 -1
  19. camel/functions/retrieval_functions.py +61 -0
  20. camel/functions/slack_functions.py +275 -0
  21. camel/models/__init__.py +2 -0
  22. camel/models/anthropic_model.py +16 -2
  23. camel/models/base_model.py +8 -2
  24. camel/models/model_factory.py +7 -3
  25. camel/models/openai_audio_models.py +251 -0
  26. camel/models/openai_model.py +12 -4
  27. camel/models/stub_model.py +5 -1
  28. camel/retrievers/__init__.py +2 -0
  29. camel/retrievers/auto_retriever.py +47 -36
  30. camel/retrievers/base.py +42 -37
  31. camel/retrievers/bm25_retriever.py +10 -19
  32. camel/retrievers/cohere_rerank_retriever.py +108 -0
  33. camel/retrievers/vector_retriever.py +43 -26
  34. camel/storages/vectordb_storages/qdrant.py +3 -1
  35. camel/toolkits/__init__.py +21 -0
  36. camel/toolkits/base.py +22 -0
  37. camel/toolkits/github_toolkit.py +245 -0
  38. camel/types/__init__.py +6 -0
  39. camel/types/enums.py +44 -3
  40. camel/utils/__init__.py +4 -2
  41. camel/utils/commons.py +97 -173
  42. {camel_ai-0.1.3.dist-info → camel_ai-0.1.4.dist-info}/METADATA +9 -3
  43. {camel_ai-0.1.3.dist-info → camel_ai-0.1.4.dist-info}/RECORD +44 -26
  44. camel/configs.py +0 -271
  45. {camel_ai-0.1.3.dist-info → camel_ai-0.1.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,132 @@
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 __future__ import annotations
15
+
16
+ from dataclasses import asdict, dataclass, field
17
+ from typing import TYPE_CHECKING, Optional, Sequence
18
+
19
+ from openai._types import NOT_GIVEN, NotGiven
20
+
21
+ from camel.configs.base_config import BaseConfig
22
+
23
+ if TYPE_CHECKING:
24
+ from camel.functions import OpenAIFunction
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class ChatGPTConfig(BaseConfig):
29
+ r"""Defines the parameters for generating chat completions using the
30
+ OpenAI API.
31
+
32
+ Args:
33
+ temperature (float, optional): Sampling temperature to use, between
34
+ :obj:`0` and :obj:`2`. Higher values make the output more random,
35
+ while lower values make it more focused and deterministic.
36
+ (default: :obj:`0.2`)
37
+ top_p (float, optional): An alternative to sampling with temperature,
38
+ called nucleus sampling, where the model considers the results of
39
+ the tokens with top_p probability mass. So :obj:`0.1` means only
40
+ the tokens comprising the top 10% probability mass are considered.
41
+ (default: :obj:`1.0`)
42
+ n (int, optional): How many chat completion choices to generate for
43
+ each input message. (default: :obj:`1`)
44
+ stream (bool, optional): If True, partial message deltas will be sent
45
+ as data-only server-sent events as they become available.
46
+ (default: :obj:`False`)
47
+ stop (str or list, optional): Up to :obj:`4` sequences where the API
48
+ will stop generating further tokens. (default: :obj:`None`)
49
+ max_tokens (int, optional): The maximum number of tokens to generate
50
+ in the chat completion. The total length of input tokens and
51
+ generated tokens is limited by the model's context length.
52
+ (default: :obj:`None`)
53
+ presence_penalty (float, optional): Number between :obj:`-2.0` and
54
+ :obj:`2.0`. Positive values penalize new tokens based on whether
55
+ they appear in the text so far, increasing the model's likelihood
56
+ to talk about new topics. See more information about frequency and
57
+ presence penalties. (default: :obj:`0.0`)
58
+ frequency_penalty (float, optional): Number between :obj:`-2.0` and
59
+ :obj:`2.0`. Positive values penalize new tokens based on their
60
+ existing frequency in the text so far, decreasing the model's
61
+ likelihood to repeat the same line verbatim. See more information
62
+ about frequency and presence penalties. (default: :obj:`0.0`)
63
+ logit_bias (dict, optional): Modify the likelihood of specified tokens
64
+ appearing in the completion. Accepts a json object that maps tokens
65
+ (specified by their token ID in the tokenizer) to an associated
66
+ bias value from :obj:`-100` to :obj:`100`. Mathematically, the bias
67
+ is added to the logits generated by the model prior to sampling.
68
+ The exact effect will vary per model, but values between:obj:` -1`
69
+ and :obj:`1` should decrease or increase likelihood of selection;
70
+ values like :obj:`-100` or :obj:`100` should result in a ban or
71
+ exclusive selection of the relevant token. (default: :obj:`{}`)
72
+ user (str, optional): A unique identifier representing your end-user,
73
+ which can help OpenAI to monitor and detect abuse.
74
+ (default: :obj:`""`)
75
+ tools (list[OpenAIFunction], optional): A list of tools the model may
76
+ call. Currently, only functions are supported as a tool. Use this
77
+ to provide a list of functions the model may generate JSON inputs
78
+ for. A max of 128 functions are supported.
79
+ tool_choice (Union[dict[str, str], str], optional): Controls which (if
80
+ any) tool is called by the model. :obj:`"none"` means the model
81
+ will not call any tool and instead generates a message.
82
+ :obj:`"auto"` means the model can pick between generating a
83
+ message or calling one or more tools. :obj:`"required"` means the
84
+ model must call one or more tools. Specifying a particular tool
85
+ via {"type": "function", "function": {"name": "my_function"}}
86
+ forces the model to call that tool. :obj:`"none"` is the default
87
+ when no tools are present. :obj:`"auto"` is the default if tools
88
+ are present.
89
+ """
90
+
91
+ temperature: float = 0.2 # openai default: 1.0
92
+ top_p: float = 1.0
93
+ n: int = 1
94
+ stream: bool = False
95
+ stop: str | Sequence[str] | NotGiven = NOT_GIVEN
96
+ max_tokens: int | NotGiven = NOT_GIVEN
97
+ presence_penalty: float = 0.0
98
+ frequency_penalty: float = 0.0
99
+ logit_bias: dict = field(default_factory=dict)
100
+ user: str = ""
101
+ tools: Optional[list[OpenAIFunction]] = None
102
+ tool_choice: Optional[dict[str, str] | str] = None
103
+
104
+ def __post_init__(self):
105
+ if self.tools is not None:
106
+ object.__setattr__(
107
+ self,
108
+ 'tools',
109
+ [tool.get_openai_tool_schema() for tool in self.tools],
110
+ )
111
+
112
+
113
+ OPENAI_API_PARAMS = {param for param in asdict(ChatGPTConfig()).keys()}
114
+
115
+
116
+ @dataclass(frozen=True)
117
+ class OpenSourceConfig(BaseConfig):
118
+ r"""Defines parameters for setting up open-source models and includes
119
+ parameters to be passed to chat completion function of OpenAI API.
120
+
121
+ Args:
122
+ model_path (str): The path to a local folder containing the model
123
+ files or the model card in HuggingFace hub.
124
+ server_url (str): The URL to the server running the model inference
125
+ which will be used as the API base of OpenAI API.
126
+ api_params (ChatGPTConfig): An instance of :obj:ChatGPTConfig to
127
+ contain the arguments to be passed to OpenAI API.
128
+ """
129
+
130
+ model_path: str
131
+ server_url: str
132
+ api_params: ChatGPTConfig = field(default_factory=ChatGPTConfig)
@@ -11,7 +11,8 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
- from typing import Any, List
14
+ import os
15
+ from typing import Any, List, Optional
15
16
 
16
17
  from openai import OpenAI
17
18
 
@@ -26,6 +27,8 @@ class OpenAIEmbedding(BaseEmbedding[str]):
26
27
  Args:
27
28
  model (OpenAiEmbeddingModel, optional): The model type to be used for
28
29
  generating embeddings. (default: :obj:`ModelType.ADA_2`)
30
+ api_key (Optional[str]): The API key for authenticating with the
31
+ OpenAI service. (default: :obj:`None`)
29
32
 
30
33
  Raises:
31
34
  RuntimeError: If an unsupported model type is specified.
@@ -34,12 +37,14 @@ class OpenAIEmbedding(BaseEmbedding[str]):
34
37
  def __init__(
35
38
  self,
36
39
  model_type: EmbeddingModelType = EmbeddingModelType.ADA_2,
40
+ api_key: Optional[str] = None,
37
41
  ) -> None:
38
42
  if not model_type.is_openai:
39
43
  raise ValueError("Invalid OpenAI embedding model type.")
40
44
  self.model_type = model_type
41
45
  self.output_dim = model_type.output_dim
42
- self.client = OpenAI()
46
+ self._api_key = api_key or os.environ.get("OPENAI_API_KEY")
47
+ self.client = OpenAI(timeout=60, max_retries=3, api_key=self._api_key)
43
48
 
44
49
  @api_key_required
45
50
  def embed_list(
@@ -11,27 +11,32 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
-
15
- from ..loaders.unstructured_io import UnstructuredIO
16
- from .google_maps_function import MAP_FUNCS
17
- from .math_functions import MATH_FUNCS
14
+ # ruff: noqa: I001
18
15
  from .openai_function import (
19
16
  OpenAIFunction,
20
17
  get_openai_function_schema,
21
18
  get_openai_tool_schema,
22
19
  )
20
+
21
+ from .google_maps_function import MAP_FUNCS
22
+ from .math_functions import MATH_FUNCS
23
+ from .open_api_function import OPENAPI_FUNCS
24
+ from .retrieval_functions import RETRIEVAL_FUNCS
23
25
  from .search_functions import SEARCH_FUNCS
24
26
  from .twitter_function import TWITTER_FUNCS
25
27
  from .weather_functions import WEATHER_FUNCS
28
+ from .slack_functions import SLACK_FUNCS
26
29
 
27
30
  __all__ = [
28
31
  'OpenAIFunction',
29
- 'get_openai_tool_schema',
30
32
  'get_openai_function_schema',
33
+ 'get_openai_tool_schema',
34
+ 'MAP_FUNCS',
31
35
  'MATH_FUNCS',
36
+ 'OPENAPI_FUNCS',
37
+ 'RETRIEVAL_FUNCS',
32
38
  'SEARCH_FUNCS',
33
- 'WEATHER_FUNCS',
34
- 'MAP_FUNCS',
35
39
  'TWITTER_FUNCS',
36
- 'UnstructuredIO',
40
+ 'WEATHER_FUNCS',
41
+ 'SLACK_FUNCS',
37
42
  ]
@@ -0,0 +1,380 @@
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 json
15
+ import os
16
+ from typing import Any, Callable, Dict, List, Tuple
17
+
18
+ import prance
19
+ import requests
20
+
21
+ from camel.functions import OpenAIFunction
22
+ from camel.types import OpenAPIName
23
+
24
+
25
+ def parse_openapi_file(openapi_spec_path: str) -> Dict[str, Any]:
26
+ r"""Load and parse an OpenAPI specification file.
27
+
28
+ This function utilizes the `prance.ResolvingParser` to parse and resolve
29
+ the given OpenAPI specification file, returning the parsed OpenAPI
30
+ specification as a dictionary.
31
+
32
+ Args:
33
+ openapi_spec_path (str): The file path or URL to the OpenAPI
34
+ specification.
35
+
36
+ Returns:
37
+ Dict[str, Any]: The parsed OpenAPI specification as a dictionary.
38
+ """
39
+ # Load the OpenAPI spec
40
+ parser = prance.ResolvingParser(openapi_spec_path)
41
+ openapi_spec = parser.specification
42
+ return openapi_spec
43
+
44
+
45
+ def openapi_spec_to_openai_schemas(
46
+ api_name: str, openapi_spec: Dict[str, Any]
47
+ ) -> List[Dict[str, Any]]:
48
+ r"""Convert OpenAPI specification to OpenAI schema format.
49
+
50
+ This function iterates over the paths and operations defined in an
51
+ OpenAPI specification, filtering out deprecated operations. For each
52
+ operation, it constructs a schema in a format suitable for OpenAI,
53
+ including operation metadata such as function name, description,
54
+ parameters, and request bodies. It raises a ValueError if an operation
55
+ lacks a description or summary.
56
+
57
+ Args:
58
+ api_name (str): The name of the API, used to prefix generated function
59
+ names.
60
+ openapi_spec (Dict[str, Any]): The OpenAPI specification as a
61
+ dictionary.
62
+
63
+ Returns:
64
+ List[Dict[str, Any]]: A list of dictionaries, each representing a
65
+ function in the OpenAI schema format, including details about the
66
+ function's name, description, and parameters.
67
+
68
+ Raises:
69
+ ValueError: If an operation in the OpenAPI specification does not have
70
+ a description or summary.
71
+
72
+ Note:
73
+ This function assumes that the OpenAPI specification follows the 3.0+
74
+ format.
75
+
76
+ Reference:
77
+ https://swagger.io/specification/
78
+ """
79
+ result = []
80
+
81
+ for path, path_item in openapi_spec.get('paths', {}).items():
82
+ for method, op in path_item.items():
83
+ if op.get('deprecated') is True:
84
+ continue
85
+
86
+ # Get the function name from the operationId
87
+ # or construct it from the API method, and path
88
+ function_name = f"{api_name}"
89
+ operation_id = op.get('operationId')
90
+ if operation_id:
91
+ function_name += f"_{operation_id}"
92
+ else:
93
+ function_name += f"{method}{path.replace('/', '_')}"
94
+
95
+ description = op.get('description') or op.get('summary')
96
+ if not description:
97
+ raise ValueError(
98
+ f"{method} {path} Operation from {api_name} "
99
+ f"does not have a description or summary."
100
+ )
101
+ description += " " if description[-1] != " " else ""
102
+ description += f"This function is from {api_name} API. "
103
+
104
+ # If the OpenAPI spec has a description,
105
+ # add it to the operation description
106
+ if 'description' in openapi_spec.get('info', {}):
107
+ description += f"{openapi_spec['info']['description']}"
108
+
109
+ # Get the parameters for the operation, if any
110
+ params = op.get('parameters', [])
111
+ properties: Dict[str, Any] = {}
112
+ required = []
113
+
114
+ for param in params:
115
+ if not param.get('deprecated', False):
116
+ param_name = param['name'] + '_in_' + param['in']
117
+ properties[param_name] = {}
118
+
119
+ if 'description' in param:
120
+ properties[param_name]['description'] = param[
121
+ 'description'
122
+ ]
123
+
124
+ if 'schema' in param:
125
+ if (
126
+ properties[param_name].get('description')
127
+ and 'description' in param['schema']
128
+ ):
129
+ param['schema'].pop('description')
130
+ properties[param_name].update(param['schema'])
131
+
132
+ if param.get('required'):
133
+ required.append(param_name)
134
+
135
+ # If the property dictionary does not have a description,
136
+ # use the parameter name as the description
137
+ if 'description' not in properties[param_name]:
138
+ properties[param_name]['description'] = param['name']
139
+
140
+ if 'type' not in properties[param_name]:
141
+ properties[param_name]['type'] = 'Any'
142
+
143
+ # Process requestBody if present
144
+ if 'requestBody' in op:
145
+ properties['requestBody'] = {}
146
+ requestBody = op['requestBody']
147
+ if requestBody.get('required') is True:
148
+ required.append('requestBody')
149
+
150
+ content = requestBody.get('content', {})
151
+ json_content = content.get('application/json', {})
152
+ json_schema = json_content.get('schema', {})
153
+ if json_schema:
154
+ properties['requestBody'] = json_schema
155
+ if 'description' not in properties['requestBody']:
156
+ properties['requestBody']['description'] = (
157
+ "The request body, with parameters specifically "
158
+ "described under the `properties` key"
159
+ )
160
+
161
+ function = {
162
+ "type": "function",
163
+ "function": {
164
+ "name": function_name,
165
+ "description": description,
166
+ "parameters": {
167
+ "type": "object",
168
+ "properties": properties,
169
+ "required": required,
170
+ },
171
+ },
172
+ }
173
+ result.append(function)
174
+
175
+ return result # Return the result list
176
+
177
+
178
+ def openapi_function_decorator(
179
+ base_url: str, path: str, method: str, operation: Dict[str, Any]
180
+ ) -> Callable:
181
+ r"""Decorate a function to make HTTP requests based on OpenAPI operation
182
+ details.
183
+
184
+ This decorator takes the base URL, path, HTTP method, and operation details
185
+ from an OpenAPI specification, and returns a decorator. The decorated
186
+ function can then be called with keyword arguments corresponding to the
187
+ operation's parameters. The decorator handles constructing the request URL,
188
+ setting headers, query parameters, and the request body as specified by the
189
+ operation details.
190
+
191
+ Args:
192
+ base_url (str): The base URL for the API.
193
+ path (str): The path for the API endpoint, relative to the base URL.
194
+ method (str): The HTTP method (e.g., 'get', 'post') for the request.
195
+ operation (Dict[str, Any]): A dictionary containing the OpenAPI
196
+ operation details, including parameters and request body
197
+ definitions.
198
+
199
+ Returns:
200
+ Callable: A decorator that, when applied to a function, enables the
201
+ function to make HTTP requests based on the provided OpenAPI
202
+ operation details.
203
+ """
204
+
205
+ def inner_decorator(openapi_function: Callable) -> Callable:
206
+ def wrapper(**kwargs):
207
+ request_url = f"{base_url.rstrip('/')}/{path.lstrip('/')}"
208
+ headers = {}
209
+ params = {}
210
+ cookies = {}
211
+
212
+ # Assign parameters to the correct position
213
+ for param in operation.get('parameters', []):
214
+ input_param_name = param['name'] + '_in_' + param['in']
215
+ # Irrelevant arguments does not affect function operation
216
+ if input_param_name in kwargs:
217
+ if param['in'] == 'path':
218
+ request_url = request_url.replace(
219
+ f"{{{param['name']}}}",
220
+ str(kwargs[input_param_name]),
221
+ )
222
+ elif param['in'] == 'query':
223
+ params[param['name']] = kwargs[input_param_name]
224
+ elif param['in'] == 'header':
225
+ headers[param['name']] = kwargs[input_param_name]
226
+ elif param['in'] == 'cookie':
227
+ cookies[param['name']] = kwargs[input_param_name]
228
+
229
+ if 'requestBody' in operation:
230
+ request_body = kwargs.get('requestBody', {})
231
+ content_type_list = list(
232
+ operation.get('requestBody', {}).get('content', {}).keys()
233
+ )
234
+ if content_type_list:
235
+ content_type = content_type_list[0]
236
+ headers.update({"Content-Type": content_type})
237
+
238
+ # send the request body based on the Content-Type
239
+ if content_type == "application/json":
240
+ response = requests.request(
241
+ method.upper(),
242
+ request_url,
243
+ params=params,
244
+ headers=headers,
245
+ cookies=cookies,
246
+ json=request_body,
247
+ )
248
+ else:
249
+ raise ValueError(
250
+ f"Unsupported content type: {content_type}"
251
+ )
252
+ else:
253
+ # If there is no requestBody, no request body is sent
254
+ response = requests.request(
255
+ method.upper(),
256
+ request_url,
257
+ params=params,
258
+ headers=headers,
259
+ cookies=cookies,
260
+ )
261
+
262
+ try:
263
+ return response.json()
264
+ except json.JSONDecodeError:
265
+ raise ValueError(
266
+ "Response could not be decoded as JSON. "
267
+ "Please check the input parameters."
268
+ )
269
+
270
+ return wrapper
271
+
272
+ return inner_decorator
273
+
274
+
275
+ def generate_openapi_funcs(
276
+ api_name: str, openapi_spec: Dict[str, Any]
277
+ ) -> List[Callable]:
278
+ r"""Generates a list of Python functions based on an OpenAPI specification.
279
+
280
+ This function dynamically creates a list of callable functions that
281
+ represent the API operations defined in an OpenAPI specification document.
282
+ Each function is designed to perform an HTTP request corresponding to an
283
+ API operation (e.g., GET, POST) as defined in the specification. The
284
+ functions are decorated with `openapi_function_decorator`, which
285
+ configures them to construct and send the HTTP requests with appropriate
286
+ parameters, headers, and body content.
287
+
288
+ Args:
289
+ api_name (str): The name of the API, used to prefix generated function
290
+ names.
291
+ openapi_spec (Dict[str, Any]): The OpenAPI specification as a
292
+ dictionary.
293
+
294
+ Returns:
295
+ List[Callable]: A list containing the generated functions. Each
296
+ function, when called, will make an HTTP request according to its
297
+ corresponding API operation defined in the OpenAPI specification.
298
+
299
+ Raises:
300
+ ValueError: If the OpenAPI specification does not contain server
301
+ information, which is necessary for determining the base URL for
302
+ the API requests.
303
+ """
304
+ # Check server information
305
+ servers = openapi_spec.get('servers', [])
306
+ if not servers:
307
+ raise ValueError("No server information found in OpenAPI spec.")
308
+ base_url = servers[0].get('url') # Use the first server URL
309
+
310
+ functions = []
311
+
312
+ # Traverse paths and methods
313
+ for path, methods in openapi_spec.get('paths', {}).items():
314
+ for method, operation in methods.items():
315
+ # Get the function name from the operationId
316
+ # or construct it from the API method, and path
317
+ operation_id = operation.get('operationId')
318
+ if operation_id:
319
+ function_name = f"{api_name}_{operation_id}"
320
+ else:
321
+ sanitized_path = path.replace('/', '_').strip('_')
322
+ function_name = f"{api_name}_{method}_{sanitized_path}"
323
+
324
+ @openapi_function_decorator(base_url, path, method, operation)
325
+ def openapi_function(**kwargs):
326
+ pass
327
+
328
+ openapi_function.__name__ = function_name
329
+
330
+ functions.append(openapi_function)
331
+
332
+ return functions
333
+
334
+
335
+ def combine_all_funcs_schemas() -> Tuple[List[Callable], List[Dict[str, Any]]]:
336
+ r"""Combines functions and schemas from multiple OpenAPI specifications.
337
+
338
+ Iterates over a list of OpenAPI specs, parses each, and generates functions
339
+ and schemas based on the defined operations and schemas respectively.
340
+ Assumes specification files are named 'openapi.yaml' located in
341
+ `open_api_specs/<api_name>/`.
342
+
343
+ Returns:
344
+ Tuple[List[Callable], List[Dict[str, Any]]]:: one of callable
345
+ functions for API operations, and another of dictionaries
346
+ representing the schemas from the specifications.
347
+ """
348
+ combined_func_lst = []
349
+ combined_schemas_list = []
350
+
351
+ for api_name in OpenAPIName:
352
+ # Parse the OpenAPI specification for each API
353
+ current_dir = os.path.dirname(__file__)
354
+ spec_file_path = os.path.join(
355
+ current_dir, 'open_api_specs', f'{api_name.value}', 'openapi.yaml'
356
+ )
357
+
358
+ openapi_spec = parse_openapi_file(spec_file_path)
359
+
360
+ # Generate and merge function schemas
361
+ openapi_functions_schemas = openapi_spec_to_openai_schemas(
362
+ api_name.value, openapi_spec
363
+ )
364
+ combined_schemas_list.extend(openapi_functions_schemas)
365
+
366
+ # Generate and merge function lists
367
+ openapi_functions_list = generate_openapi_funcs(
368
+ api_name.value, openapi_spec
369
+ )
370
+ combined_func_lst.extend(openapi_functions_list)
371
+
372
+ return combined_func_lst, combined_schemas_list
373
+
374
+
375
+ combined_funcs_lst, combined_schemas_list = combine_all_funcs_schemas()
376
+
377
+ OPENAPI_FUNCS: List[OpenAIFunction] = [
378
+ OpenAIFunction(a_func, a_schema)
379
+ for a_func, a_schema in zip(combined_funcs_lst, combined_schemas_list)
380
+ ]
@@ -0,0 +1,13 @@
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. ===========
@@ -0,0 +1,82 @@
1
+ openapi: 3.0.1
2
+ info:
3
+ title: Search API
4
+ version: v1
5
+ description: Find recommendation for courses, specializations, and degrees on Coursera.
6
+ servers:
7
+ - url: https://www.coursera.org
8
+ description: API schema for search APIs exposed to 3rd party services (e.g. OpenAI)
9
+ tags:
10
+ - name: SearchV1Controller
11
+ description: the Search V1 Controller API
12
+ paths:
13
+ /api/rest/v1/search:
14
+ post:
15
+ summary:
16
+ A public API that searches the Coursera catalog for products (e.g. courses) that
17
+ are relevant to the provided query string.
18
+ tags:
19
+ - search-v1-controller
20
+ operationId:
21
+ search
22
+ requestBody:
23
+ content:
24
+ application/json:
25
+ schema:
26
+ $ref: '#/components/schemas/SearchQuery'
27
+ required: true
28
+ responses:
29
+ "200":
30
+ description: OK
31
+ content:
32
+ application/json:
33
+ schema:
34
+ $ref: '#/components/schemas/SearchResponse'
35
+ components:
36
+ schemas:
37
+ SearchQuery:
38
+ type: object
39
+ properties:
40
+ query:
41
+ type: string
42
+ required:
43
+ - query
44
+ example:
45
+ query: machine learning
46
+ SearchResponse:
47
+ properties:
48
+ hits:
49
+ type: array
50
+ items:
51
+ $ref: '#/components/schemas/SearchHit'
52
+ SearchHit:
53
+ type: object
54
+ properties:
55
+ name:
56
+ type: string
57
+ partners:
58
+ type: array
59
+ items:
60
+ type: string
61
+ duration:
62
+ type: string
63
+ partnerLogos:
64
+ type: array
65
+ items:
66
+ type: string
67
+ productDifficultyLevel:
68
+ type: string
69
+ entityType:
70
+ type: string
71
+ avgProductRating:
72
+ type: string
73
+ skills:
74
+ type: string
75
+ imageUrl:
76
+ type: string
77
+ isCourseFree:
78
+ type: string
79
+ isPartOfCourseraPlus:
80
+ type: string
81
+ objectUrl:
82
+ type: string