camel-ai 0.2.50__py3-none-any.whl → 0.2.52__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.
- camel/__init__.py +1 -1
- camel/environments/single_step.py +79 -11
- camel/models/azure_openai_model.py +27 -9
- camel/models/ollama_model.py +15 -10
- camel/models/vllm_model.py +15 -9
- camel/runtime/__init__.py +2 -0
- camel/runtime/daytona_runtime.py +248 -0
- camel/toolkits/__init__.py +2 -0
- camel/toolkits/klavis_toolkit.py +228 -0
- camel/toolkits/mcp_toolkit.py +19 -3
- camel/types/enums.py +3 -0
- camel/utils/commons.py +2 -0
- {camel_ai-0.2.50.dist-info → camel_ai-0.2.52.dist-info}/METADATA +3 -1
- {camel_ai-0.2.50.dist-info → camel_ai-0.2.52.dist-info}/RECORD +16 -14
- {camel_ai-0.2.50.dist-info → camel_ai-0.2.52.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.50.dist-info → camel_ai-0.2.52.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
|
+
import asyncio
|
|
15
16
|
import random
|
|
16
17
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
17
18
|
|
|
@@ -58,6 +59,7 @@ class SingleStepEnv:
|
|
|
58
59
|
self,
|
|
59
60
|
dataset: Union[StaticDataset, BaseGenerator],
|
|
60
61
|
verifier: BaseVerifier,
|
|
62
|
+
timeout: Optional[float] = 180.0,
|
|
61
63
|
**kwargs,
|
|
62
64
|
) -> None:
|
|
63
65
|
r"""Initialize the SingleStepEnv.
|
|
@@ -67,12 +69,15 @@ class SingleStepEnv:
|
|
|
67
69
|
problems from.
|
|
68
70
|
verifier (BaseVerifier): Verifier used to evaluate LLM responses
|
|
69
71
|
against ground-truth answers.
|
|
72
|
+
timeout (Optional[float], optional): The execution timeout in
|
|
73
|
+
seconds. (default: :obj:`180.0`)
|
|
70
74
|
**kwargs: Optional metadata or configuration values.
|
|
71
75
|
|
|
72
76
|
Notes:
|
|
73
77
|
This class assumes all interactions are single-step: one question,
|
|
74
78
|
one LLM response, one reward.
|
|
75
79
|
"""
|
|
80
|
+
self._timeout = timeout
|
|
76
81
|
self.dataset = dataset
|
|
77
82
|
self.verifier = verifier
|
|
78
83
|
self._metadata = kwargs
|
|
@@ -279,6 +284,7 @@ class SingleStepEnv:
|
|
|
279
284
|
or if `reset()` has not been called.
|
|
280
285
|
ValueError: If invalid action format, duplicate indices,
|
|
281
286
|
or out-of-bounds indices are detected.
|
|
287
|
+
asyncio.TimeoutError: If the step execution exceeds the timeout.
|
|
282
288
|
"""
|
|
283
289
|
|
|
284
290
|
if not self._is_setup:
|
|
@@ -307,18 +313,34 @@ class SingleStepEnv:
|
|
|
307
313
|
f"total batch size ({self.current_batch_size})"
|
|
308
314
|
)
|
|
309
315
|
|
|
310
|
-
indices = [act.index for act in actions]
|
|
311
316
|
proposed_solutions = [act.llm_response for act in actions]
|
|
312
|
-
ground_truths: List[str] = [
|
|
313
|
-
|
|
314
|
-
|
|
317
|
+
ground_truths: List[str] = [
|
|
318
|
+
self._states[idx].final_answer for idx in indices
|
|
319
|
+
]
|
|
315
320
|
|
|
316
321
|
try:
|
|
317
|
-
verification_results = await
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
verification_results = await asyncio.wait_for(
|
|
323
|
+
self.verifier.verify_batch(
|
|
324
|
+
solutions=proposed_solutions,
|
|
325
|
+
reference_answers=ground_truths, # type: ignore [arg-type]
|
|
326
|
+
raise_on_error=True,
|
|
327
|
+
),
|
|
328
|
+
timeout=self._timeout,
|
|
329
|
+
)
|
|
330
|
+
except asyncio.TimeoutError as e:
|
|
331
|
+
logger.error(
|
|
332
|
+
f"Step verification timed out after {self._timeout}s: {e}"
|
|
321
333
|
)
|
|
334
|
+
# Return timeout verification results
|
|
335
|
+
verification_results = [
|
|
336
|
+
VerificationResult(
|
|
337
|
+
result="",
|
|
338
|
+
status=VerificationOutcome.TIMEOUT,
|
|
339
|
+
error_message=f"Verification timed out "
|
|
340
|
+
f"after {self._timeout}s",
|
|
341
|
+
)
|
|
342
|
+
for _ in range(len(proposed_solutions))
|
|
343
|
+
]
|
|
322
344
|
except Exception as e:
|
|
323
345
|
logger.error(f"Verification failed: {e}")
|
|
324
346
|
# Return failed verification results with status=FAILURE
|
|
@@ -331,9 +353,55 @@ class SingleStepEnv:
|
|
|
331
353
|
for _ in range(len(proposed_solutions))
|
|
332
354
|
]
|
|
333
355
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
)
|
|
356
|
+
# Track which solutions have been processed and which have timed out
|
|
357
|
+
total_rewards = [0.0] * len(proposed_solutions)
|
|
358
|
+
rewards_dicts = [{"correctness": 0.0}] * len(proposed_solutions)
|
|
359
|
+
|
|
360
|
+
try:
|
|
361
|
+
# First try to compute all rewards with a timeout
|
|
362
|
+
computed_rewards, computed_rewards_dicts = await asyncio.wait_for(
|
|
363
|
+
self._compute_reward_batch(
|
|
364
|
+
proposed_solutions, verification_results
|
|
365
|
+
),
|
|
366
|
+
timeout=self._timeout,
|
|
367
|
+
)
|
|
368
|
+
# If successful, use all the computed values
|
|
369
|
+
total_rewards = computed_rewards
|
|
370
|
+
rewards_dicts = computed_rewards_dicts
|
|
371
|
+
except asyncio.TimeoutError as e:
|
|
372
|
+
logger.error(
|
|
373
|
+
f"Reward computation timed out after {self._timeout}s: {e}"
|
|
374
|
+
)
|
|
375
|
+
# Try to compute rewards one by one to identify which ones time out
|
|
376
|
+
for i, (solution, result) in enumerate(
|
|
377
|
+
zip(proposed_solutions, verification_results)
|
|
378
|
+
):
|
|
379
|
+
try:
|
|
380
|
+
individual_rewards = await asyncio.wait_for(
|
|
381
|
+
self._compute_custom_reward(solution, result),
|
|
382
|
+
timeout=self._timeout,
|
|
383
|
+
)
|
|
384
|
+
# If successful, calculate the reward for this solution
|
|
385
|
+
correctness_reward = (
|
|
386
|
+
self.ACCURACY_REWARD if result.status else 0.0
|
|
387
|
+
)
|
|
388
|
+
rewards_dict = {
|
|
389
|
+
"correctness": correctness_reward,
|
|
390
|
+
**individual_rewards,
|
|
391
|
+
}
|
|
392
|
+
total_rewards[i] = sum(rewards_dict.values())
|
|
393
|
+
rewards_dicts[i] = rewards_dict
|
|
394
|
+
except asyncio.TimeoutError:
|
|
395
|
+
logger.warning(
|
|
396
|
+
f"Reward computation for solution {i} timed out"
|
|
397
|
+
)
|
|
398
|
+
except Exception as e:
|
|
399
|
+
logger.warning(
|
|
400
|
+
f"Error computing reward for solution {i}: {e}"
|
|
401
|
+
)
|
|
402
|
+
except Exception as e:
|
|
403
|
+
logger.error(f"Reward computation failed: {e}")
|
|
404
|
+
|
|
337
405
|
# Create and return step results in batch
|
|
338
406
|
step_results = [
|
|
339
407
|
StepResult(
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import os
|
|
15
|
-
from typing import Any, Dict, List, Optional, Type, Union
|
|
15
|
+
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
|
16
16
|
|
|
17
17
|
from openai import AsyncAzureOpenAI, AsyncStream, AzureOpenAI, Stream
|
|
18
18
|
from pydantic import BaseModel
|
|
@@ -27,6 +27,8 @@ from camel.types import (
|
|
|
27
27
|
)
|
|
28
28
|
from camel.utils import BaseTokenCounter, OpenAITokenCounter
|
|
29
29
|
|
|
30
|
+
AzureADTokenProvider = Callable[[], str]
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
class AzureOpenAIModel(BaseModelBackend):
|
|
32
34
|
r"""Azure OpenAI API in a unified BaseModelBackend interface.
|
|
@@ -46,6 +48,12 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
46
48
|
(default: :obj:`None`)
|
|
47
49
|
azure_deployment_name (Optional[str], optional): The deployment name
|
|
48
50
|
you chose when you deployed an azure model. (default: :obj:`None`)
|
|
51
|
+
azure_ad_token (Optional[str], optional): Your Azure Active Directory
|
|
52
|
+
token, https://www.microsoft.com/en-us/security/business/
|
|
53
|
+
identity-access/microsoft-entra-id. (default: :obj:`None`)
|
|
54
|
+
azure_ad_token_provider (Optional[AzureADTokenProvider], optional): A
|
|
55
|
+
function that returns an Azure Active Directory token, will be
|
|
56
|
+
invoked on every request. (default: :obj:`None`)
|
|
49
57
|
token_counter (Optional[BaseTokenCounter], optional): Token counter to
|
|
50
58
|
use for the model. If not provided, :obj:`OpenAITokenCounter`
|
|
51
59
|
will be used. (default: :obj:`None`)
|
|
@@ -68,6 +76,8 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
68
76
|
token_counter: Optional[BaseTokenCounter] = None,
|
|
69
77
|
api_version: Optional[str] = None,
|
|
70
78
|
azure_deployment_name: Optional[str] = None,
|
|
79
|
+
azure_ad_token_provider: Optional["AzureADTokenProvider"] = None,
|
|
80
|
+
azure_ad_token: Optional[str] = None,
|
|
71
81
|
) -> None:
|
|
72
82
|
if model_config_dict is None:
|
|
73
83
|
model_config_dict = ChatGPTConfig().as_dict()
|
|
@@ -79,15 +89,19 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
79
89
|
)
|
|
80
90
|
|
|
81
91
|
self.api_version = api_version or os.environ.get("AZURE_API_VERSION")
|
|
82
|
-
self.
|
|
92
|
+
self._azure_deployment_name = azure_deployment_name or os.environ.get(
|
|
83
93
|
"AZURE_DEPLOYMENT_NAME"
|
|
84
94
|
)
|
|
95
|
+
self._azure_ad_token = azure_ad_token or os.environ.get(
|
|
96
|
+
"AZURE_AD_TOKEN"
|
|
97
|
+
)
|
|
98
|
+
self.azure_ad_token_provider = azure_ad_token_provider
|
|
85
99
|
if self.api_version is None:
|
|
86
100
|
raise ValueError(
|
|
87
101
|
"Must provide either the `api_version` argument "
|
|
88
102
|
"or `AZURE_API_VERSION` environment variable."
|
|
89
103
|
)
|
|
90
|
-
if self.
|
|
104
|
+
if self._azure_deployment_name is None:
|
|
91
105
|
raise ValueError(
|
|
92
106
|
"Must provide either the `azure_deployment_name` argument "
|
|
93
107
|
"or `AZURE_DEPLOYMENT_NAME` environment variable."
|
|
@@ -95,18 +109,22 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
95
109
|
|
|
96
110
|
self._client = AzureOpenAI(
|
|
97
111
|
azure_endpoint=str(self._url),
|
|
98
|
-
azure_deployment=self.
|
|
112
|
+
azure_deployment=self._azure_deployment_name,
|
|
99
113
|
api_version=self.api_version,
|
|
100
114
|
api_key=self._api_key,
|
|
115
|
+
azure_ad_token=self._azure_ad_token,
|
|
116
|
+
azure_ad_token_provider=self.azure_ad_token_provider,
|
|
101
117
|
timeout=self._timeout,
|
|
102
118
|
max_retries=3,
|
|
103
119
|
)
|
|
104
120
|
|
|
105
121
|
self._async_client = AsyncAzureOpenAI(
|
|
106
122
|
azure_endpoint=str(self._url),
|
|
107
|
-
azure_deployment=self.
|
|
123
|
+
azure_deployment=self._azure_deployment_name,
|
|
108
124
|
api_version=self.api_version,
|
|
109
125
|
api_key=self._api_key,
|
|
126
|
+
azure_ad_token=self._azure_ad_token,
|
|
127
|
+
azure_ad_token_provider=self.azure_ad_token_provider,
|
|
110
128
|
timeout=self._timeout,
|
|
111
129
|
max_retries=3,
|
|
112
130
|
)
|
|
@@ -193,7 +211,7 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
193
211
|
|
|
194
212
|
return self._client.chat.completions.create(
|
|
195
213
|
messages=messages,
|
|
196
|
-
model=self.
|
|
214
|
+
model=self._azure_deployment_name, # type:ignore[arg-type]
|
|
197
215
|
**request_config,
|
|
198
216
|
)
|
|
199
217
|
|
|
@@ -209,7 +227,7 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
209
227
|
|
|
210
228
|
return await self._async_client.chat.completions.create(
|
|
211
229
|
messages=messages,
|
|
212
|
-
model=self.
|
|
230
|
+
model=self._azure_deployment_name, # type:ignore[arg-type]
|
|
213
231
|
**request_config,
|
|
214
232
|
)
|
|
215
233
|
|
|
@@ -232,7 +250,7 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
232
250
|
|
|
233
251
|
return self._client.beta.chat.completions.parse(
|
|
234
252
|
messages=messages,
|
|
235
|
-
model=self.
|
|
253
|
+
model=self._azure_deployment_name, # type:ignore[arg-type]
|
|
236
254
|
**request_config,
|
|
237
255
|
)
|
|
238
256
|
|
|
@@ -255,7 +273,7 @@ class AzureOpenAIModel(BaseModelBackend):
|
|
|
255
273
|
|
|
256
274
|
return await self._async_client.beta.chat.completions.parse(
|
|
257
275
|
messages=messages,
|
|
258
|
-
model=self.
|
|
276
|
+
model=self._azure_deployment_name, # type:ignore[arg-type]
|
|
259
277
|
**request_config,
|
|
260
278
|
)
|
|
261
279
|
|
camel/models/ollama_model.py
CHANGED
|
@@ -16,10 +16,13 @@ import subprocess
|
|
|
16
16
|
from typing import Any, Dict, Optional, Union
|
|
17
17
|
|
|
18
18
|
from camel.configs import OLLAMA_API_PARAMS, OllamaConfig
|
|
19
|
+
from camel.logger import get_logger
|
|
19
20
|
from camel.models.openai_compatible_model import OpenAICompatibleModel
|
|
20
21
|
from camel.types import ModelType
|
|
21
22
|
from camel.utils import BaseTokenCounter
|
|
22
23
|
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
class OllamaModel(OpenAICompatibleModel):
|
|
25
28
|
r"""Ollama service interface.
|
|
@@ -60,20 +63,22 @@ class OllamaModel(OpenAICompatibleModel):
|
|
|
60
63
|
) -> None:
|
|
61
64
|
if model_config_dict is None:
|
|
62
65
|
model_config_dict = OllamaConfig().as_dict()
|
|
63
|
-
|
|
66
|
+
self._url = url or os.environ.get("OLLAMA_BASE_URL")
|
|
64
67
|
timeout = timeout or float(os.environ.get("MODEL_TIMEOUT", 180))
|
|
68
|
+
self._model_type = model_type
|
|
69
|
+
|
|
70
|
+
if not self._url:
|
|
71
|
+
self._start_server()
|
|
72
|
+
|
|
65
73
|
super().__init__(
|
|
66
|
-
model_type=
|
|
74
|
+
model_type=self._model_type,
|
|
67
75
|
model_config_dict=model_config_dict,
|
|
68
|
-
api_key=
|
|
69
|
-
url=
|
|
76
|
+
api_key="Not_Used",
|
|
77
|
+
url=self._url,
|
|
70
78
|
token_counter=token_counter,
|
|
71
79
|
timeout=timeout,
|
|
72
80
|
)
|
|
73
81
|
|
|
74
|
-
if not self._url:
|
|
75
|
-
self._start_server()
|
|
76
|
-
|
|
77
82
|
def _start_server(self) -> None:
|
|
78
83
|
r"""Starts the Ollama server in a subprocess."""
|
|
79
84
|
try:
|
|
@@ -83,12 +88,12 @@ class OllamaModel(OpenAICompatibleModel):
|
|
|
83
88
|
stderr=subprocess.PIPE,
|
|
84
89
|
)
|
|
85
90
|
self._url = "http://localhost:11434/v1"
|
|
86
|
-
|
|
91
|
+
logger.info(
|
|
87
92
|
f"Ollama server started on {self._url} "
|
|
88
|
-
f"for {self.
|
|
93
|
+
f"for {self._model_type} model."
|
|
89
94
|
)
|
|
90
95
|
except Exception as e:
|
|
91
|
-
|
|
96
|
+
logger.error(f"Failed to start Ollama server: {e}.")
|
|
92
97
|
|
|
93
98
|
def check_model_config(self):
|
|
94
99
|
r"""Check whether the model configuration contains any
|
camel/models/vllm_model.py
CHANGED
|
@@ -16,10 +16,13 @@ import subprocess
|
|
|
16
16
|
from typing import Any, Dict, Optional, Union
|
|
17
17
|
|
|
18
18
|
from camel.configs import VLLM_API_PARAMS, VLLMConfig
|
|
19
|
+
from camel.logger import get_logger
|
|
19
20
|
from camel.models.openai_compatible_model import OpenAICompatibleModel
|
|
20
21
|
from camel.types import ModelType
|
|
21
22
|
from camel.utils import BaseTokenCounter
|
|
22
23
|
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
# flake8: noqa: E501
|
|
25
28
|
class VLLMModel(OpenAICompatibleModel):
|
|
@@ -62,18 +65,21 @@ class VLLMModel(OpenAICompatibleModel):
|
|
|
62
65
|
) -> None:
|
|
63
66
|
if model_config_dict is None:
|
|
64
67
|
model_config_dict = VLLMConfig().as_dict()
|
|
65
|
-
|
|
68
|
+
self._url = url or os.environ.get("VLLM_BASE_URL")
|
|
66
69
|
timeout = timeout or float(os.environ.get("MODEL_TIMEOUT", 180))
|
|
70
|
+
self._model_type = model_type
|
|
71
|
+
|
|
72
|
+
if not self._url:
|
|
73
|
+
self._start_server()
|
|
74
|
+
|
|
67
75
|
super().__init__(
|
|
68
|
-
model_type=
|
|
76
|
+
model_type=self._model_type,
|
|
69
77
|
model_config_dict=model_config_dict,
|
|
70
|
-
api_key=
|
|
71
|
-
url=
|
|
78
|
+
api_key="Not_Used",
|
|
79
|
+
url=self._url,
|
|
72
80
|
token_counter=token_counter,
|
|
73
81
|
timeout=timeout,
|
|
74
82
|
)
|
|
75
|
-
if not self._url:
|
|
76
|
-
self._start_server()
|
|
77
83
|
|
|
78
84
|
def _start_server(self) -> None:
|
|
79
85
|
r"""Starts the vllm server in a subprocess."""
|
|
@@ -84,12 +90,12 @@ class VLLMModel(OpenAICompatibleModel):
|
|
|
84
90
|
stderr=subprocess.PIPE,
|
|
85
91
|
)
|
|
86
92
|
self._url = "http://localhost:8000/v1"
|
|
87
|
-
|
|
93
|
+
logger.info(
|
|
88
94
|
f"vllm server started on {self._url} "
|
|
89
|
-
f"for {self.
|
|
95
|
+
f"for {self._model_type} model."
|
|
90
96
|
)
|
|
91
97
|
except Exception as e:
|
|
92
|
-
|
|
98
|
+
logger.error(f"Failed to start vllm server: {e}.")
|
|
93
99
|
|
|
94
100
|
def check_model_config(self):
|
|
95
101
|
r"""Check whether the model configuration contains any
|
camel/runtime/__init__.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from .base import BaseRuntime
|
|
15
15
|
from .configs import TaskConfig
|
|
16
|
+
from .daytona_runtime import DaytonaRuntime
|
|
16
17
|
from .docker_runtime import DockerRuntime
|
|
17
18
|
from .llm_guard_runtime import LLMGuardRuntime
|
|
18
19
|
from .remote_http_runtime import RemoteHttpRuntime
|
|
@@ -28,4 +29,5 @@ __all__ = [
|
|
|
28
29
|
"LLMGuardRuntime",
|
|
29
30
|
"TaskConfig",
|
|
30
31
|
"UbuntuDockerRuntime",
|
|
32
|
+
"DaytonaRuntime",
|
|
31
33
|
]
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import inspect
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from functools import wraps
|
|
19
|
+
from typing import Any, Dict, List, Optional, Union
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
from camel.logger import get_logger
|
|
24
|
+
from camel.runtime import BaseRuntime
|
|
25
|
+
from camel.toolkits.function_tool import FunctionTool
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DaytonaRuntime(BaseRuntime):
|
|
31
|
+
r"""A runtime that executes functions in a Daytona sandbox environment.
|
|
32
|
+
Requires the Daytona server to be running and an API key configured.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
api_key (Optional[str]): The Daytona API key for authentication. If not
|
|
36
|
+
provided, it will try to use the DAYTONA_API_KEY environment
|
|
37
|
+
variable. (default: :obj: `None`)
|
|
38
|
+
api_url (Optional[str]): The URL of the Daytona server. If not
|
|
39
|
+
provided, it will try to use the DAYTONA_API_URL environment
|
|
40
|
+
variable. If none is provided, it will use "http://localhost:8000".
|
|
41
|
+
(default: :obj: `None`)
|
|
42
|
+
language (Optional[str]): The programming language for the sandbox.
|
|
43
|
+
(default: :obj: `"python"`)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
api_url: Optional[str] = None,
|
|
50
|
+
language: Optional[str] = "python",
|
|
51
|
+
):
|
|
52
|
+
from daytona_sdk import Daytona, DaytonaConfig
|
|
53
|
+
|
|
54
|
+
super().__init__()
|
|
55
|
+
self.api_key = api_key or os.environ.get('DAYTONA_API_KEY')
|
|
56
|
+
self.api_url = api_url or os.environ.get('DAYTONA_API_URL')
|
|
57
|
+
self.language = language
|
|
58
|
+
self.config = DaytonaConfig(api_key=self.api_key, api_url=self.api_url)
|
|
59
|
+
self.daytona = Daytona(self.config)
|
|
60
|
+
self.sandbox = None
|
|
61
|
+
self.entrypoint: Dict[str, str] = dict()
|
|
62
|
+
|
|
63
|
+
def build(self) -> "DaytonaRuntime":
|
|
64
|
+
r"""Create and start a Daytona sandbox.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
DaytonaRuntime: The current runtime.
|
|
68
|
+
"""
|
|
69
|
+
from daytona_sdk import CreateSandboxParams
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
params = CreateSandboxParams(language=self.language)
|
|
73
|
+
self.sandbox = self.daytona.create(params)
|
|
74
|
+
if self.sandbox is None:
|
|
75
|
+
raise RuntimeError("Failed to create sandbox.")
|
|
76
|
+
logger.info(f"Sandbox created with ID: {self.sandbox.id}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error(f"Failed to create sandbox: {e!s}")
|
|
79
|
+
raise RuntimeError(f"Daytona sandbox creation failed: {e!s}")
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
def _cleanup(self):
|
|
83
|
+
r"""Clean up the sandbox when exiting."""
|
|
84
|
+
if self.sandbox:
|
|
85
|
+
try:
|
|
86
|
+
self.daytona.remove(self.sandbox)
|
|
87
|
+
logger.info(f"Sandbox {self.sandbox.id} removed")
|
|
88
|
+
self.sandbox = None
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"Failed to remove sandbox: {e!s}")
|
|
91
|
+
|
|
92
|
+
def add(
|
|
93
|
+
self,
|
|
94
|
+
funcs: Union[FunctionTool, List[FunctionTool]],
|
|
95
|
+
entrypoint: str,
|
|
96
|
+
arguments: Optional[Dict[str, Any]] = None,
|
|
97
|
+
) -> "DaytonaRuntime":
|
|
98
|
+
r"""Add a function or list of functions to the runtime.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
funcs (Union[FunctionTool, List[FunctionTool]]): The function or
|
|
102
|
+
list of functions to add.
|
|
103
|
+
entrypoint (str): The entrypoint for the function.
|
|
104
|
+
arguments (Optional[Dict[str, Any]]): The arguments for the
|
|
105
|
+
function. (default: :obj: `None`)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
DaytonaRuntime: The current runtime.
|
|
109
|
+
"""
|
|
110
|
+
if not isinstance(funcs, list):
|
|
111
|
+
funcs = [funcs]
|
|
112
|
+
if arguments is not None:
|
|
113
|
+
entrypoint += json.dumps(arguments, ensure_ascii=False)
|
|
114
|
+
|
|
115
|
+
def make_wrapper(inner_func, func_name, func_code):
|
|
116
|
+
r"""Creates a wrapper for a function to execute it in the
|
|
117
|
+
Daytona sandbox.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
inner_func (Callable): The function to wrap.
|
|
121
|
+
func_name (str): The name of the function.
|
|
122
|
+
func_code (str): The source code of the function.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Callable: A wrapped function that executes in the sandbox.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
@wraps(inner_func)
|
|
129
|
+
def wrapper(*args, **kwargs):
|
|
130
|
+
if not self.sandbox:
|
|
131
|
+
raise RuntimeError(
|
|
132
|
+
"Sandbox not initialized. Call build() first."
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
for key, value in kwargs.items():
|
|
137
|
+
if isinstance(value, BaseModel):
|
|
138
|
+
kwargs[key] = value.model_dump()
|
|
139
|
+
args_str = json.dumps(args, ensure_ascii=True)
|
|
140
|
+
kwargs_str = json.dumps(kwargs, ensure_ascii=True)
|
|
141
|
+
except (TypeError, ValueError) as e:
|
|
142
|
+
logger.error(f"Failed to serialize arguments: {e!s}")
|
|
143
|
+
return {"error": f"Argument serialization failed: {e!s}"}
|
|
144
|
+
|
|
145
|
+
# Upload function code to the sandbox
|
|
146
|
+
script_path = f"/home/daytona/{func_name}.py"
|
|
147
|
+
try:
|
|
148
|
+
self.sandbox.fs.upload_file(
|
|
149
|
+
script_path, func_code.encode()
|
|
150
|
+
)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error(
|
|
153
|
+
f"Failed to upload function {func_name}: {e!s}"
|
|
154
|
+
)
|
|
155
|
+
return {"error": f"Upload failed: {e!s}"}
|
|
156
|
+
|
|
157
|
+
exec_code = (
|
|
158
|
+
f"import sys\n"
|
|
159
|
+
f"sys.path.append('/home/daytona')\n"
|
|
160
|
+
f"import json\n"
|
|
161
|
+
f"from {func_name} import {func_name}\n"
|
|
162
|
+
f"args = json.loads('{args_str}')\n"
|
|
163
|
+
f"kwargs = json.loads('{kwargs_str}')\n"
|
|
164
|
+
f"result = {func_name}(*args, **kwargs)\n"
|
|
165
|
+
f"print(json.dumps(result) if result is not "
|
|
166
|
+
f"None else 'null')"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Execute the function in the sandbox
|
|
170
|
+
try:
|
|
171
|
+
response = self.sandbox.process.code_run(exec_code)
|
|
172
|
+
return (
|
|
173
|
+
json.loads(response.result)
|
|
174
|
+
if response.result
|
|
175
|
+
else None
|
|
176
|
+
)
|
|
177
|
+
except json.JSONDecodeError as e:
|
|
178
|
+
logger.error(
|
|
179
|
+
f"Failed to decode JSON response for {func_name}"
|
|
180
|
+
)
|
|
181
|
+
return {"error": f"JSON decoding failed: {e!s}"}
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(
|
|
184
|
+
f"Failed to execute function {func_name}: {e!s}"
|
|
185
|
+
)
|
|
186
|
+
return {"error": f"Execution failed: {e!s}"}
|
|
187
|
+
|
|
188
|
+
return wrapper
|
|
189
|
+
|
|
190
|
+
for func in funcs:
|
|
191
|
+
inner_func = func.func
|
|
192
|
+
func_name = func.get_function_name()
|
|
193
|
+
func_code = inspect.getsource(inner_func).strip()
|
|
194
|
+
|
|
195
|
+
func.func = make_wrapper(inner_func, func_name, func_code)
|
|
196
|
+
self.tools_map[func_name] = func
|
|
197
|
+
self.entrypoint[func_name] = entrypoint
|
|
198
|
+
|
|
199
|
+
return self
|
|
200
|
+
|
|
201
|
+
def info(self) -> str:
|
|
202
|
+
r"""Get information about the current sandbox.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
str: Information about the sandbox.
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
RuntimeError: If the sandbox is not initialized.
|
|
209
|
+
"""
|
|
210
|
+
if self.sandbox is None:
|
|
211
|
+
raise RuntimeError("Failed to create sandbox.")
|
|
212
|
+
info = self.sandbox.info()
|
|
213
|
+
return (
|
|
214
|
+
f"Sandbox {info.name}:\n"
|
|
215
|
+
f"State: {info.state}\n"
|
|
216
|
+
f"Resources: {info.resources.cpu} CPU, {info.resources.memory} RAM"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def __del__(self):
|
|
220
|
+
r"""Clean up the sandbox when the object is deleted."""
|
|
221
|
+
if hasattr(self, 'sandbox'):
|
|
222
|
+
self._cleanup()
|
|
223
|
+
|
|
224
|
+
def stop(self) -> "DaytonaRuntime":
|
|
225
|
+
r"""Stop and remove the sandbox.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
DaytonaRuntime: The current runtime.
|
|
229
|
+
"""
|
|
230
|
+
self._cleanup()
|
|
231
|
+
return self
|
|
232
|
+
|
|
233
|
+
def reset(self) -> "DaytonaRuntime":
|
|
234
|
+
r"""Reset the sandbox by stopping and rebuilding it.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
DaytonaRuntime: The current runtime.
|
|
238
|
+
"""
|
|
239
|
+
return self.stop().build()
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def docs(self) -> str:
|
|
243
|
+
r"""Get the URL for the Daytona API documentation.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
str: The URL for the API documentation.
|
|
247
|
+
"""
|
|
248
|
+
return "https://www.daytona.io/docs/python-sdk/daytona/"
|
camel/toolkits/__init__.py
CHANGED
|
@@ -68,6 +68,7 @@ from .pyautogui_toolkit import PyAutoGUIToolkit
|
|
|
68
68
|
from .openai_agent_toolkit import OpenAIAgentToolkit
|
|
69
69
|
from .searxng_toolkit import SearxNGToolkit
|
|
70
70
|
from .jina_reranker_toolkit import JinaRerankerToolkit
|
|
71
|
+
from .klavis_toolkit import KlavisToolkit
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
__all__ = [
|
|
@@ -124,4 +125,5 @@ __all__ = [
|
|
|
124
125
|
'OpenAIAgentToolkit',
|
|
125
126
|
'SearxNGToolkit',
|
|
126
127
|
'JinaRerankerToolkit',
|
|
128
|
+
'KlavisToolkit',
|
|
127
129
|
]
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
import requests
|
|
19
|
+
|
|
20
|
+
from camel.logger import get_logger
|
|
21
|
+
from camel.toolkits.base import BaseToolkit
|
|
22
|
+
from camel.toolkits.function_tool import FunctionTool
|
|
23
|
+
from camel.utils import MCPServer, api_keys_required, dependencies_required
|
|
24
|
+
|
|
25
|
+
logger = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@MCPServer()
|
|
29
|
+
class KlavisToolkit(BaseToolkit):
|
|
30
|
+
r"""A class representing a toolkit for interacting with Klavis API.
|
|
31
|
+
|
|
32
|
+
This class provides methods for interacting with Klavis MCP server
|
|
33
|
+
instances, retrieving server information, managing tools, and handling
|
|
34
|
+
authentication.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
api_key (str): The API key for authenticating with Klavis API.
|
|
38
|
+
base_url (str): The base URL for Klavis API endpoints.
|
|
39
|
+
timeout (Optional[float]): The timeout value for API requests
|
|
40
|
+
in seconds. If None, no timeout is applied.
|
|
41
|
+
(default: :obj:`None`)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@dependencies_required("requests")
|
|
45
|
+
@api_keys_required(
|
|
46
|
+
[
|
|
47
|
+
(None, "KLAVIS_API_KEY"),
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
def __init__(self, timeout: Optional[float] = None) -> None:
|
|
51
|
+
r"""Initialize the KlavisToolkit with API client. The API key is
|
|
52
|
+
retrieved from environment variables.
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(timeout=timeout)
|
|
55
|
+
self.api_key = os.environ.get("KLAVIS_API_KEY")
|
|
56
|
+
self.base_url = "https://api.klavis.ai"
|
|
57
|
+
|
|
58
|
+
def _request(
|
|
59
|
+
self,
|
|
60
|
+
method: str,
|
|
61
|
+
endpoint: str,
|
|
62
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
63
|
+
additional_headers: Optional[Dict[str, str]] = None,
|
|
64
|
+
) -> Dict[str, Any]:
|
|
65
|
+
r"""Make an HTTP request to the Klavis API.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
method (str): HTTP method (e.g., 'GET', 'POST', 'DELETE').
|
|
69
|
+
endpoint (str): API endpoint path.
|
|
70
|
+
payload (Optional[Dict[str, Any]]): JSON payload for POST
|
|
71
|
+
requests.
|
|
72
|
+
additional_headers (Optional[Dict[str, str]]): Additional
|
|
73
|
+
headers to include in the request.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dict[str, Any]: The JSON response from the API or an error
|
|
77
|
+
dict.
|
|
78
|
+
"""
|
|
79
|
+
url = f"{self.base_url}{endpoint}"
|
|
80
|
+
headers = {
|
|
81
|
+
'accept': 'application/json',
|
|
82
|
+
'Authorization': f'Bearer {self.api_key}',
|
|
83
|
+
}
|
|
84
|
+
if additional_headers:
|
|
85
|
+
headers.update(additional_headers)
|
|
86
|
+
|
|
87
|
+
logger.debug(
|
|
88
|
+
f"Making {method} request to {url} with payload: {payload}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
response = requests.request(
|
|
93
|
+
method,
|
|
94
|
+
url,
|
|
95
|
+
headers=headers,
|
|
96
|
+
json=payload,
|
|
97
|
+
timeout=self.timeout,
|
|
98
|
+
)
|
|
99
|
+
response.raise_for_status()
|
|
100
|
+
return response.json()
|
|
101
|
+
except requests.exceptions.RequestException as e:
|
|
102
|
+
logger.error(f"Request failed for {method} {endpoint}: {e!s}")
|
|
103
|
+
return {"error": f"Request failed: {e!s}"}
|
|
104
|
+
except ValueError:
|
|
105
|
+
logger.error(f"Response for {method} {endpoint} is not valid JSON")
|
|
106
|
+
return {"error": "Response is not valid JSON"}
|
|
107
|
+
|
|
108
|
+
def create_server_instance(
|
|
109
|
+
self, server_name: str, user_id: str, platform_name: str
|
|
110
|
+
) -> Dict[str, Any]:
|
|
111
|
+
r"""Create a Server-Sent Events (SSE) URL for a specified MCP server.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
server_name (str): The name of the target MCP server.
|
|
115
|
+
user_id (str): The ID for the user requesting the server URL.
|
|
116
|
+
platform_name (str): The name of the platform associated
|
|
117
|
+
with the user.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Dict[str, Any]: Response containing the server instance details.
|
|
121
|
+
"""
|
|
122
|
+
endpoint = "/mcp-server/instance/create"
|
|
123
|
+
payload = {
|
|
124
|
+
"serverName": server_name,
|
|
125
|
+
"userId": user_id,
|
|
126
|
+
"platformName": platform_name,
|
|
127
|
+
}
|
|
128
|
+
headers = {'Content-Type': 'application/json'}
|
|
129
|
+
return self._request(
|
|
130
|
+
'POST', endpoint, payload=payload, additional_headers=headers
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def get_server_instance(self, instance_id: str) -> Dict[str, Any]:
|
|
134
|
+
r"""Get details of a specific server connection instance.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
instance_id (str): The ID of the connection instance whose status
|
|
138
|
+
is being checked.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dict[str, Any]: Details about the server instance.
|
|
142
|
+
"""
|
|
143
|
+
endpoint = f"/mcp-server/instance/get/{instance_id}"
|
|
144
|
+
return self._request('GET', endpoint)
|
|
145
|
+
|
|
146
|
+
def delete_auth_data(self, instance_id: str) -> Dict[str, Any]:
|
|
147
|
+
r"""Delete authentication metadata for a specific server
|
|
148
|
+
connection instance.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
instance_id (str): The ID of the connection instance to
|
|
152
|
+
delete auth for.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dict[str, Any]: Status response for the operation.
|
|
156
|
+
"""
|
|
157
|
+
endpoint = f"/mcp-server/instance/delete-auth/{instance_id}"
|
|
158
|
+
return self._request('DELETE', endpoint)
|
|
159
|
+
|
|
160
|
+
def delete_server_instance(self, instance_id: str) -> Dict[str, Any]:
|
|
161
|
+
r"""Completely removes a server connection instance.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
instance_id (str): The ID of the connection instance to delete.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Dict[str, Any]: Status response for the operation.
|
|
168
|
+
"""
|
|
169
|
+
endpoint = f"/mcp-server/instance/delete/{instance_id}"
|
|
170
|
+
return self._request('DELETE', endpoint)
|
|
171
|
+
|
|
172
|
+
def get_server_tools(self, server_name: str) -> Dict[str, Any]:
|
|
173
|
+
r"""Get list of tool names for a specific MCP server.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
server_name (str): The name of the target MCP server.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dict[str, Any]: List of tools available for the specified server.
|
|
180
|
+
"""
|
|
181
|
+
endpoint = f"/mcp-server/tools/{server_name}"
|
|
182
|
+
return self._request('GET', endpoint)
|
|
183
|
+
|
|
184
|
+
def get_all_servers(self) -> Dict[str, Any]:
|
|
185
|
+
r"""Get all MCP servers with their basic information.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Dict[str, Any]: Information about all available MCP servers.
|
|
189
|
+
"""
|
|
190
|
+
endpoint = "/mcp-server/servers"
|
|
191
|
+
return self._request('GET', endpoint)
|
|
192
|
+
|
|
193
|
+
def set_auth_token(
|
|
194
|
+
self, instance_id: str, auth_token: str
|
|
195
|
+
) -> Dict[str, Any]:
|
|
196
|
+
r"""Sets an authentication token for a specific instance.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
instance_id (str): The ID for the connection instance.
|
|
200
|
+
auth_token (str): The authentication token to save.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Dict[str, Any]: Status response for the operation.
|
|
204
|
+
"""
|
|
205
|
+
endpoint = "/mcp-server/instance/set-auth-token"
|
|
206
|
+
payload = {"instanceId": instance_id, "authToken": auth_token}
|
|
207
|
+
headers = {'Content-Type': 'application/json'}
|
|
208
|
+
return self._request(
|
|
209
|
+
'POST', endpoint, payload=payload, additional_headers=headers
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
213
|
+
r"""Returns a list of FunctionTool objects representing the functions
|
|
214
|
+
in the toolkit.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
List[FunctionTool]: A list of FunctionTool objects representing
|
|
218
|
+
the functions in the toolkit.
|
|
219
|
+
"""
|
|
220
|
+
return [
|
|
221
|
+
FunctionTool(self.create_server_instance),
|
|
222
|
+
FunctionTool(self.get_server_instance),
|
|
223
|
+
FunctionTool(self.delete_auth_data),
|
|
224
|
+
FunctionTool(self.delete_server_instance),
|
|
225
|
+
FunctionTool(self.get_server_tools),
|
|
226
|
+
FunctionTool(self.get_all_servers),
|
|
227
|
+
FunctionTool(self.set_auth_token),
|
|
228
|
+
]
|
camel/toolkits/mcp_toolkit.py
CHANGED
|
@@ -60,6 +60,8 @@ class MCPClient(BaseToolkit):
|
|
|
60
60
|
timeout (Optional[float]): Connection timeout. (default: :obj:`'None'`)
|
|
61
61
|
headers (Dict[str, str]): Headers for the HTTP request.
|
|
62
62
|
(default: :obj:`'None'`)
|
|
63
|
+
strict (Optional[bool]): Whether to enforce strict mode for the
|
|
64
|
+
function call. (default: :obj:`False`)
|
|
63
65
|
"""
|
|
64
66
|
|
|
65
67
|
def __init__(
|
|
@@ -69,6 +71,7 @@ class MCPClient(BaseToolkit):
|
|
|
69
71
|
env: Optional[Dict[str, str]] = None,
|
|
70
72
|
timeout: Optional[float] = None,
|
|
71
73
|
headers: Optional[Dict[str, str]] = None,
|
|
74
|
+
strict: Optional[bool] = False,
|
|
72
75
|
):
|
|
73
76
|
from mcp import Tool
|
|
74
77
|
|
|
@@ -78,6 +81,7 @@ class MCPClient(BaseToolkit):
|
|
|
78
81
|
self.args = args or []
|
|
79
82
|
self.env = env or {}
|
|
80
83
|
self.headers = headers or {}
|
|
84
|
+
self.strict = strict
|
|
81
85
|
|
|
82
86
|
self._mcp_tools: List[Tool] = []
|
|
83
87
|
self._session: Optional['ClientSession'] = None
|
|
@@ -317,13 +321,15 @@ class MCPClient(BaseToolkit):
|
|
|
317
321
|
"additionalProperties": False,
|
|
318
322
|
}
|
|
319
323
|
|
|
324
|
+
# Because certain parameters in MCP may include keywords that are not
|
|
325
|
+
# supported by OpenAI, it is essential to set "strict" to False.
|
|
320
326
|
return {
|
|
321
327
|
"type": "function",
|
|
322
328
|
"function": {
|
|
323
329
|
"name": mcp_tool.name,
|
|
324
330
|
"description": mcp_tool.description
|
|
325
331
|
or "No description provided.",
|
|
326
|
-
"strict":
|
|
332
|
+
"strict": self.strict,
|
|
327
333
|
"parameters": parameters,
|
|
328
334
|
},
|
|
329
335
|
}
|
|
@@ -363,6 +369,8 @@ class MCPToolkit(BaseToolkit):
|
|
|
363
369
|
instances to manage.
|
|
364
370
|
config_path (Optional[str]): Path to a JSON configuration file
|
|
365
371
|
defining MCP servers.
|
|
372
|
+
strict (Optional[bool]): Whether to enforce strict mode for the
|
|
373
|
+
function call. (default: :obj:`False`)
|
|
366
374
|
|
|
367
375
|
Note:
|
|
368
376
|
Either `servers` or `config_path` must be provided. If both are
|
|
@@ -397,6 +405,7 @@ class MCPToolkit(BaseToolkit):
|
|
|
397
405
|
self,
|
|
398
406
|
servers: Optional[List[MCPClient]] = None,
|
|
399
407
|
config_path: Optional[str] = None,
|
|
408
|
+
strict: Optional[bool] = False,
|
|
400
409
|
):
|
|
401
410
|
super().__init__()
|
|
402
411
|
|
|
@@ -409,16 +418,22 @@ class MCPToolkit(BaseToolkit):
|
|
|
409
418
|
self.servers: List[MCPClient] = servers or []
|
|
410
419
|
|
|
411
420
|
if config_path:
|
|
412
|
-
self.servers.extend(
|
|
421
|
+
self.servers.extend(
|
|
422
|
+
self._load_servers_from_config(config_path, strict)
|
|
423
|
+
)
|
|
413
424
|
|
|
414
425
|
self._exit_stack = AsyncExitStack()
|
|
415
426
|
self._connected = False
|
|
416
427
|
|
|
417
|
-
def _load_servers_from_config(
|
|
428
|
+
def _load_servers_from_config(
|
|
429
|
+
self, config_path: str, strict: Optional[bool] = False
|
|
430
|
+
) -> List[MCPClient]:
|
|
418
431
|
r"""Loads MCP server configurations from a JSON file.
|
|
419
432
|
|
|
420
433
|
Args:
|
|
421
434
|
config_path (str): Path to the JSON configuration file.
|
|
435
|
+
strict (bool): Whether to enforce strict mode for the
|
|
436
|
+
function call. (default: :obj:`False`)
|
|
422
437
|
|
|
423
438
|
Returns:
|
|
424
439
|
List[MCPClient]: List of configured MCPClient instances.
|
|
@@ -462,6 +477,7 @@ class MCPToolkit(BaseToolkit):
|
|
|
462
477
|
args=cfg.get("args", []),
|
|
463
478
|
env={**os.environ, **cfg.get("env", {})},
|
|
464
479
|
timeout=cfg.get("timeout", None),
|
|
480
|
+
strict=strict,
|
|
465
481
|
)
|
|
466
482
|
all_servers.append(server)
|
|
467
483
|
|
camel/types/enums.py
CHANGED
|
@@ -105,6 +105,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
105
105
|
TOGETHER_LLAMA_4_SCOUT = "meta-llama/Llama-4-Scout-17B-16E-Instruct"
|
|
106
106
|
|
|
107
107
|
# PPIO platform models support tool calling
|
|
108
|
+
PPIO_DEEPSEEK_PROVER_V2_671B = "deepseek/deepseek-prover-v2-671b"
|
|
108
109
|
PPIO_DEEPSEEK_R1_TURBO = "deepseek/deepseek-r1-turbo"
|
|
109
110
|
PPIO_DEEPSEEK_V3_TURBO = "deepseek/deepseek-v3-turbo"
|
|
110
111
|
PPIO_DEEPSEEK_R1_COMMUNITY = "deepseek/deepseek-r1/community"
|
|
@@ -716,6 +717,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
716
717
|
@property
|
|
717
718
|
def is_ppio(self) -> bool:
|
|
718
719
|
return self in {
|
|
720
|
+
ModelType.PPIO_DEEPSEEK_PROVER_V2_671B,
|
|
719
721
|
ModelType.PPIO_DEEPSEEK_R1_TURBO,
|
|
720
722
|
ModelType.PPIO_DEEPSEEK_V3_TURBO,
|
|
721
723
|
ModelType.PPIO_DEEPSEEK_R1_COMMUNITY,
|
|
@@ -928,6 +930,7 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
928
930
|
}:
|
|
929
931
|
return 14_336
|
|
930
932
|
elif self in {
|
|
933
|
+
ModelType.PPIO_DEEPSEEK_PROVER_V2_671B,
|
|
931
934
|
ModelType.NOVITA_DOLPHIN_MIXTRAL_8X22B,
|
|
932
935
|
}:
|
|
933
936
|
return 16_000
|
camel/utils/commons.py
CHANGED
|
@@ -352,6 +352,8 @@ def api_keys_required(
|
|
|
352
352
|
key_way = "https://platform.lingyiwanwu.com/docs"
|
|
353
353
|
elif env_var_name == 'ZHIPUAI_API_KEY':
|
|
354
354
|
key_way = "https://www.zhipuai.cn/"
|
|
355
|
+
elif env_var_name == 'KLAVIS_API_KEY':
|
|
356
|
+
key_way = "https://www.klavis.ai/docs"
|
|
355
357
|
|
|
356
358
|
if missing_keys:
|
|
357
359
|
raise ValueError(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: camel-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.52
|
|
4
4
|
Summary: Communicative Agents for AI Society Study
|
|
5
5
|
Project-URL: Homepage, https://www.camel-ai.org/
|
|
6
6
|
Project-URL: Repository, https://github.com/camel-ai/camel
|
|
@@ -36,6 +36,7 @@ Requires-Dist: dappier<0.4,>=0.3.3; extra == 'all'
|
|
|
36
36
|
Requires-Dist: datacommons-pandas<0.0.4,>=0.0.3; extra == 'all'
|
|
37
37
|
Requires-Dist: datacommons<2,>=1.4.3; extra == 'all'
|
|
38
38
|
Requires-Dist: datasets<4,>=3; extra == 'all'
|
|
39
|
+
Requires-Dist: daytona-sdk==0.14.0; extra == 'all'
|
|
39
40
|
Requires-Dist: diffusers<0.26,>=0.25.0; extra == 'all'
|
|
40
41
|
Requires-Dist: discord-py<3,>=2.3.2; extra == 'all'
|
|
41
42
|
Requires-Dist: docker<8,>=7.1.0; extra == 'all'
|
|
@@ -170,6 +171,7 @@ Requires-Dist: types-tqdm<5,>=4.66.0; extra == 'dev'
|
|
|
170
171
|
Requires-Dist: uv==0.6.5; extra == 'dev'
|
|
171
172
|
Provides-Extra: dev-tools
|
|
172
173
|
Requires-Dist: agentops<0.4,>=0.3.21; extra == 'dev-tools'
|
|
174
|
+
Requires-Dist: daytona-sdk==0.14.0; extra == 'dev-tools'
|
|
173
175
|
Requires-Dist: docker<8,>=7.1.0; extra == 'dev-tools'
|
|
174
176
|
Requires-Dist: e2b-code-interpreter<2,>=1.0.3; extra == 'dev-tools'
|
|
175
177
|
Requires-Dist: ipykernel<7,>=6.0.0; extra == 'dev-tools'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
camel/__init__.py,sha256=
|
|
1
|
+
camel/__init__.py,sha256=4hOpc6x6mtK5OsacUZpZFBla8BA-4Ru7OKm_aTxLJso,912
|
|
2
2
|
camel/generators.py,sha256=JRqj9_m1PF4qT6UtybzTQ-KBT9MJQt18OAAYvQ_fr2o,13844
|
|
3
3
|
camel/human.py,sha256=9X09UmxI2JqQnhrFfnZ3B9EzFmVfdSWQcjLWTIXKXe0,4962
|
|
4
4
|
camel/logger.py,sha256=rZVeOVYuQ9RYJ5Tqyv0usqy0g4zaVEq4qSfZ9nd2640,5755
|
|
@@ -114,7 +114,7 @@ camel/embeddings/vlm_embedding.py,sha256=HZFdcz1YzkFPzMj45_jaCVmDQJyccoXN561aLWl
|
|
|
114
114
|
camel/environments/__init__.py,sha256=fwk74TJO5OaOhL5Pmiz6s6h77B_-DGav5m_HbfS1oUQ,1053
|
|
115
115
|
camel/environments/models.py,sha256=jVcCyU7xObKoWPnkshmPqyyKi3AOiMVVtUZA-tWEYUU,4194
|
|
116
116
|
camel/environments/multi_step.py,sha256=HPIH2W-iWsmtDLeN1gjxo7LoAnMQQZzdmfjhmRoBAxg,8534
|
|
117
|
-
camel/environments/single_step.py,sha256=
|
|
117
|
+
camel/environments/single_step.py,sha256=zrAXLOBGGKQFnGENasUnmFO_T-rjppGPK2CRBLGu7aQ,23019
|
|
118
118
|
camel/environments/tic_tac_toe.py,sha256=axRDN9yUVH8PzACkD6zenWiQKFDaupGsQ7NmSvbJCIc,19080
|
|
119
119
|
camel/extractors/__init__.py,sha256=lgtDl8zWvN826fJVKqRv05w556YZ-EdrHwdzKphywgA,1097
|
|
120
120
|
camel/extractors/base.py,sha256=3jvuZpq27nlADDCX3GfubOpeb_zt-E9rzxF3x4lYm8s,10404
|
|
@@ -163,7 +163,7 @@ camel/models/_utils.py,sha256=hob1ehnS5xZitMCdYToHVgaTB55JnaP4_DSWnTEfVsg,2045
|
|
|
163
163
|
camel/models/aiml_model.py,sha256=HO_jRvMN2WEVlZRc_UG-OoZ1hv8sEiO5j_7BBKuGlp0,3753
|
|
164
164
|
camel/models/anthropic_model.py,sha256=0jilp8dBrk1JAP0dkw_YX21uL3_81ZGL0qmKZiAI2w0,4331
|
|
165
165
|
camel/models/aws_bedrock_model.py,sha256=cpY66Wcwb-clNf0TCch9WI8au3_GGxHBYUd09rGQi_U,4353
|
|
166
|
-
camel/models/azure_openai_model.py,sha256
|
|
166
|
+
camel/models/azure_openai_model.py,sha256=-_c4SjYOA-BM6n01FuaaQ8m_QucG8_DbV_zFozaesDg,12067
|
|
167
167
|
camel/models/base_audio_model.py,sha256=_VUWh1L3rh8mldNvM5R6jBOKtvmTeBKJyRxAdPJmPlY,3324
|
|
168
168
|
camel/models/base_model.py,sha256=eDeUlgH8iS0Stk6zommzqce4dfD4Qj51tvgXUs5ys4s,14474
|
|
169
169
|
camel/models/cohere_model.py,sha256=OgRHxlPrei-NT5UVDFf6lVR88k6eKnmcZMyFj4XmONE,14880
|
|
@@ -183,7 +183,7 @@ camel/models/nemotron_model.py,sha256=APYCcpDMytZNsXFhdcLnO8bttc6pQWAo6e5h2VTJVw
|
|
|
183
183
|
camel/models/netmind_model.py,sha256=EmyKBupEQlciI69ba_oF36RNT7EJYqGLtMJ_QVZPFaE,3959
|
|
184
184
|
camel/models/novita_model.py,sha256=tU8RXsV-4wYmQhXcZkPXdlXRvLwsnj_pEJVSxnhXiXc,3921
|
|
185
185
|
camel/models/nvidia_model.py,sha256=XkHBZJ94nkYB1j9AgyPsorYfvgO1Ys6S6_z3Qh3vrhM,3766
|
|
186
|
-
camel/models/ollama_model.py,sha256=
|
|
186
|
+
camel/models/ollama_model.py,sha256=sc49XZT9KQeUWjvLqXn17kKa1jgAyAHiy2MHiUjQrzw,4416
|
|
187
187
|
camel/models/openai_audio_models.py,sha256=BSixkXlc8xirQLl2qCla-g6_y9wDLnMZVHukHrhzw98,13344
|
|
188
188
|
camel/models/openai_compatible_model.py,sha256=PmNMQKXMPk-4HYiDqn8dJ8cMH8ukl4MAPVtqWyWDnfY,10481
|
|
189
189
|
camel/models/openai_model.py,sha256=atxW2N6xcA9kqGAE6moqKYZD3IxIxOjmKcQnBs7ff7w,12826
|
|
@@ -196,7 +196,7 @@ camel/models/sglang_model.py,sha256=GsRxh3yrOQRu5gJgPdES289vCiiUHoBvKw-YZ-iwlI0,
|
|
|
196
196
|
camel/models/siliconflow_model.py,sha256=mdVcf-5N-ZWS5qGL2pq7TsAsvlhCXxL6jxavxeElFRw,4432
|
|
197
197
|
camel/models/stub_model.py,sha256=JvjeEkXS7RMcR_UA_64a3T6S0QALUhOaMQs-aI7Etug,5955
|
|
198
198
|
camel/models/togetherai_model.py,sha256=16q-0jsO5HIy9K0elSlR-aA9EDN5VhPXPGBG6cU1t1w,3937
|
|
199
|
-
camel/models/vllm_model.py,sha256=
|
|
199
|
+
camel/models/vllm_model.py,sha256=bB4-pkXnMD6aGXumCYpJY62JCWRMuK57kIpzYdK3mJQ,4493
|
|
200
200
|
camel/models/volcano_model.py,sha256=Lsg0BDVPaP50vRNW0D37liEsa71RmvapBxN_JCcHe-4,3674
|
|
201
201
|
camel/models/watsonx_model.py,sha256=2jIfx5dGBxxVqaZIUBTd01mtw_XuCUzqtcmKpubibME,9136
|
|
202
202
|
camel/models/yi_model.py,sha256=hZ_LYZWYhDkjBA3UcDiY2sGPFH3jEbxHaRy6IMvLYZ4,3742
|
|
@@ -235,10 +235,11 @@ camel/retrievers/bm25_retriever.py,sha256=feQgn3dwnbQG8H7KSbFzjaFAw3KtKObfoyA6gm
|
|
|
235
235
|
camel/retrievers/cohere_rerank_retriever.py,sha256=tzMS3HG4wD3gbIAVcHsC5fTbFxuiNrT4qJ10oJMJ0BA,4032
|
|
236
236
|
camel/retrievers/hybrid_retrival.py,sha256=2ySeXnLunoBHarqssijjxn7bAQGypAXX6QdzxYstreM,9466
|
|
237
237
|
camel/retrievers/vector_retriever.py,sha256=Fp6k7FkoeUXLiUokqsMIO74Dh2mJcmJP2TrCP-6Ekcc,11051
|
|
238
|
-
camel/runtime/__init__.py,sha256=
|
|
238
|
+
camel/runtime/__init__.py,sha256=bFbqDIST69V6LC_Oe5YBz-8qlUaJt4zWGFFT0ctiMLI,1273
|
|
239
239
|
camel/runtime/api.py,sha256=JfkHqQ_Xs3ZNl4TXC_obV3Ymi21ltP1LAkXzBA3H_Lo,3227
|
|
240
240
|
camel/runtime/base.py,sha256=KKBLO6PwWEWBXbYaVtO9JXqBT8110iYpMhgNSIxQvYQ,1512
|
|
241
241
|
camel/runtime/configs.py,sha256=7swJ6shD5CAnQj16D_j1BWJjRDHLue8_BJcQ1-YiKJI,2431
|
|
242
|
+
camel/runtime/daytona_runtime.py,sha256=bhQyn1ZRNcDg-gDWOzjGszMl1keL90xnvLZ-EH--DaU,9165
|
|
242
243
|
camel/runtime/docker_runtime.py,sha256=Q1eN4rWxDB4PF_G7nq64Rk7xhRZgAhNqmlxsu6YMdCg,13578
|
|
243
244
|
camel/runtime/llm_guard_runtime.py,sha256=n8MImZsYLXCfr6LG6BFvlC75Al16GMJMvGp4vFsob6E,8101
|
|
244
245
|
camel/runtime/remote_http_runtime.py,sha256=Emgz2xz4vbwCvErDk6Ni25aDA3EBXoPOQ7U70AgVg0w,6669
|
|
@@ -292,7 +293,7 @@ camel/terminators/__init__.py,sha256=t8uqrkUnXEOYMXQDgaBkMFJ0EXFKI0kmx4cUimli3Ls
|
|
|
292
293
|
camel/terminators/base.py,sha256=xmJzERX7GdSXcxZjAHHODa0rOxRChMSRboDCNHWSscs,1511
|
|
293
294
|
camel/terminators/response_terminator.py,sha256=n3G5KP6Oj7-7WlRN0yFcrtLpqAJKaKS0bmhrWlFfCgQ,4982
|
|
294
295
|
camel/terminators/token_limit_terminator.py,sha256=YWv6ZR8R9yI2Qnf_3xES5bEE_O5bb2CxQ0EUXfMh34c,2118
|
|
295
|
-
camel/toolkits/__init__.py,sha256=
|
|
296
|
+
camel/toolkits/__init__.py,sha256=sGw0LKAG4WNVWL7NUYlQH0f0IE_gm2VMSPbkSLCJ-10,4429
|
|
296
297
|
camel/toolkits/arxiv_toolkit.py,sha256=Bs2-K1yfmqhEhHoQ0j00KoI8LpOd8M3ApXcvI_-ApVw,6303
|
|
297
298
|
camel/toolkits/ask_news_toolkit.py,sha256=WfWaqwEo1Apbil3-Rb5y65Ws43NU4rAFWZu5VHe4los,23448
|
|
298
299
|
camel/toolkits/audio_analysis_toolkit.py,sha256=dnDtQJbztVBwBpamSyCfigPw2GBnDAfi3fOPgql4Y50,8941
|
|
@@ -312,9 +313,10 @@ camel/toolkits/google_scholar_toolkit.py,sha256=WQ9a0HQr0vro1Uo1m--alCUXvMmaHVKe
|
|
|
312
313
|
camel/toolkits/human_toolkit.py,sha256=ZbhXPA0G3mQkcNW_jPwywReAft2pIJG0WkVXOG48s1w,2328
|
|
313
314
|
camel/toolkits/image_analysis_toolkit.py,sha256=gnbX2by7_pxPpjUEY8bOj1tV-hGC3mwqEvQRMXyZ9TM,7535
|
|
314
315
|
camel/toolkits/jina_reranker_toolkit.py,sha256=U-V9qZ7SKP3SPTh_0CsE7ZKNqS9jNkmKYsIN1Mk-vFY,8118
|
|
316
|
+
camel/toolkits/klavis_toolkit.py,sha256=L-OiGwgaIue8QiIijNlvGbgtwjGUszLqs9bVRW5md7c,8244
|
|
315
317
|
camel/toolkits/linkedin_toolkit.py,sha256=wn4eXwYYlVA7doTna7k7WYhUqTBF83W79S-UJs_IQr0,8065
|
|
316
318
|
camel/toolkits/math_toolkit.py,sha256=KdI8AJ9Dbq5cfWboAYJUYgSkmADMCO5eZ6yqYkWuiIQ,3686
|
|
317
|
-
camel/toolkits/mcp_toolkit.py,sha256=
|
|
319
|
+
camel/toolkits/mcp_toolkit.py,sha256=Vzw6ACr4z7Emzdobfw8diPB3Wd2G_SDfUKlJoS9lsxI,19024
|
|
318
320
|
camel/toolkits/memory_toolkit.py,sha256=TeKYd5UMwgjVpuS2orb-ocFL13eUNKujvrFOruDCpm8,4436
|
|
319
321
|
camel/toolkits/meshy_toolkit.py,sha256=NbgdOBD3FYLtZf-AfonIv6-Q8-8DW129jsaP1PqI2rs,7126
|
|
320
322
|
camel/toolkits/mineru_toolkit.py,sha256=vRX9LholLNkpbJ6axfEN4pTG85aWb0PDmlVy3rAAXhg,6868
|
|
@@ -368,14 +370,14 @@ camel/toolkits/open_api_specs/web_scraper/openapi.yaml,sha256=u_WalQ01e8W1D27VnZ
|
|
|
368
370
|
camel/toolkits/open_api_specs/web_scraper/paths/__init__.py,sha256=OKCZrQCDwaWtXIN_2rA9FSqEvgpQRieRoHh7Ek6N16A,702
|
|
369
371
|
camel/toolkits/open_api_specs/web_scraper/paths/scraper.py,sha256=aWy1_ppV4NVVEZfnbN3tu9XA9yAPAC9bRStJ5JuXMRU,1117
|
|
370
372
|
camel/types/__init__.py,sha256=VLWhAt857IFct3XepY5BNOIhyhDhfmODTezr9jhO_TI,2251
|
|
371
|
-
camel/types/enums.py,sha256=
|
|
373
|
+
camel/types/enums.py,sha256=zisVc5hTqrbrwtH3lijzalP9zMI2pMW62-SIDizn8EQ,56786
|
|
372
374
|
camel/types/openai_types.py,sha256=8ZFzLe-zGmKNPfuVZFzxlxAX98lGf18gtrPhOgMmzus,2104
|
|
373
375
|
camel/types/unified_model_type.py,sha256=TpiUmJ3IuX8LNLtTUeUcVM7U82r4ClSq3ZQlNX3ODKs,5351
|
|
374
376
|
camel/types/agents/__init__.py,sha256=cbvVkogPoZgcwZrgxLH6EtpGXk0kavF79nOic0Dc1vg,786
|
|
375
377
|
camel/types/agents/tool_calling_record.py,sha256=qa-vLyKvYzWprRkFFl1928xaw9CnfacIebHaqM-oY3s,1814
|
|
376
378
|
camel/utils/__init__.py,sha256=0rN9DFjUh7Gv6HIJBhm9edmLKecK-89j9q0TC4ABsZY,2737
|
|
377
379
|
camel/utils/async_func.py,sha256=KqoktGSWjZBuAMQ2CV0X6FRgHGlzCKLfeaWvp-f1Qz8,1568
|
|
378
|
-
camel/utils/commons.py,sha256=
|
|
380
|
+
camel/utils/commons.py,sha256=dzZ1zR2RIb6MCCSw_jfil36BGcVe0q4Jy2t2y2T2Vwg,33693
|
|
379
381
|
camel/utils/constants.py,sha256=cqnxmpUeOwrtsR-tRO4bbOc6ZP19TLj7avjm3FONMJs,1410
|
|
380
382
|
camel/utils/deduplication.py,sha256=UHikAtOW1TTDunf2t_wa2kFbmkrXWf7HfOKwLvwCxzo,8958
|
|
381
383
|
camel/utils/filename.py,sha256=HYNc1wbSCgNR1CN21cwHxdAhpnsf5ySJ6jUDfeqOK20,2532
|
|
@@ -392,7 +394,7 @@ camel/verifiers/math_verifier.py,sha256=tA1D4S0sm8nsWISevxSN0hvSVtIUpqmJhzqfbuMo
|
|
|
392
394
|
camel/verifiers/models.py,sha256=GdxYPr7UxNrR1577yW4kyroRcLGfd-H1GXgv8potDWU,2471
|
|
393
395
|
camel/verifiers/physics_verifier.py,sha256=c1grrRddcrVN7szkxhv2QirwY9viIRSITWeWFF5HmLs,30187
|
|
394
396
|
camel/verifiers/python_verifier.py,sha256=ogTz77wODfEcDN4tMVtiSkRQyoiZbHPY2fKybn59lHw,20558
|
|
395
|
-
camel_ai-0.2.
|
|
396
|
-
camel_ai-0.2.
|
|
397
|
-
camel_ai-0.2.
|
|
398
|
-
camel_ai-0.2.
|
|
397
|
+
camel_ai-0.2.52.dist-info/METADATA,sha256=MhuSMpOxRFCshNY5GspU1s-rIkuzPYzsRH921CgJoSA,44000
|
|
398
|
+
camel_ai-0.2.52.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
399
|
+
camel_ai-0.2.52.dist-info/licenses/LICENSE,sha256=id0nB2my5kG0xXeimIu5zZrbHLS6EQvxvkKkzIHaT2k,11343
|
|
400
|
+
camel_ai-0.2.52.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|