camel-ai 0.2.15a0__py3-none-any.whl → 0.2.17__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/agents/chat_agent.py +18 -4
- camel/agents/multi_hop_generator_agent.py +85 -0
- camel/agents/programmed_agent_instruction.py +148 -0
- camel/benchmarks/__init__.py +13 -1
- camel/benchmarks/apibank.py +565 -0
- camel/benchmarks/apibench.py +500 -0
- camel/benchmarks/gaia.py +4 -4
- camel/benchmarks/nexus.py +518 -0
- camel/benchmarks/ragbench.py +333 -0
- camel/bots/__init__.py +1 -1
- camel/bots/discord/__init__.py +26 -0
- camel/bots/discord/discord_app.py +384 -0
- camel/bots/discord/discord_installation.py +64 -0
- camel/bots/discord/discord_store.py +160 -0
- camel/configs/__init__.py +3 -0
- camel/configs/anthropic_config.py +17 -15
- camel/configs/internlm_config.py +60 -0
- camel/data_collector/base.py +5 -5
- camel/data_collector/sharegpt_collector.py +2 -2
- camel/datagen/__init__.py +6 -2
- camel/datagen/{o1datagen.py → cotdatagen.py} +19 -6
- camel/datagen/self_instruct/__init__.py +36 -0
- camel/datagen/self_instruct/filter/__init__.py +34 -0
- camel/datagen/self_instruct/filter/filter_function.py +216 -0
- camel/datagen/self_instruct/filter/filter_registry.py +56 -0
- camel/datagen/self_instruct/filter/instruction_filter.py +81 -0
- camel/datagen/self_instruct/self_instruct.py +393 -0
- camel/datagen/self_instruct/templates.py +382 -0
- camel/datahubs/huggingface.py +12 -2
- camel/datahubs/models.py +2 -3
- camel/embeddings/mistral_embedding.py +5 -1
- camel/embeddings/openai_compatible_embedding.py +6 -1
- camel/embeddings/openai_embedding.py +5 -1
- camel/interpreters/e2b_interpreter.py +5 -1
- camel/loaders/__init__.py +2 -0
- camel/loaders/apify_reader.py +5 -1
- camel/loaders/chunkr_reader.py +5 -1
- camel/loaders/firecrawl_reader.py +0 -30
- camel/loaders/panda_reader.py +337 -0
- camel/logger.py +11 -5
- camel/messages/__init__.py +10 -4
- camel/messages/conversion/conversation_models.py +5 -0
- camel/messages/func_message.py +30 -22
- camel/models/__init__.py +2 -0
- camel/models/anthropic_model.py +6 -23
- camel/models/azure_openai_model.py +1 -2
- camel/models/cohere_model.py +13 -1
- camel/models/deepseek_model.py +5 -1
- camel/models/gemini_model.py +15 -2
- camel/models/groq_model.py +5 -1
- camel/models/internlm_model.py +143 -0
- camel/models/mistral_model.py +19 -8
- camel/models/model_factory.py +3 -0
- camel/models/nemotron_model.py +5 -1
- camel/models/nvidia_model.py +5 -1
- camel/models/openai_model.py +5 -1
- camel/models/qwen_model.py +5 -1
- camel/models/reka_model.py +5 -1
- camel/models/reward/__init__.py +2 -0
- camel/models/reward/nemotron_model.py +5 -1
- camel/models/reward/skywork_model.py +88 -0
- camel/models/samba_model.py +5 -1
- camel/models/togetherai_model.py +5 -1
- camel/models/yi_model.py +5 -1
- camel/models/zhipuai_model.py +5 -1
- camel/schemas/openai_converter.py +5 -1
- camel/storages/graph_storages/nebula_graph.py +89 -20
- camel/storages/graph_storages/neo4j_graph.py +138 -0
- camel/synthetic_datagen/source2synth/data_processor.py +373 -0
- camel/synthetic_datagen/source2synth/models.py +68 -0
- camel/synthetic_datagen/source2synth/user_data_processor_config.py +73 -0
- camel/toolkits/__init__.py +4 -0
- camel/toolkits/arxiv_toolkit.py +20 -3
- camel/toolkits/dappier_toolkit.py +196 -0
- camel/toolkits/function_tool.py +61 -61
- camel/toolkits/google_scholar_toolkit.py +9 -0
- camel/toolkits/meshy_toolkit.py +5 -1
- camel/toolkits/notion_toolkit.py +1 -1
- camel/toolkits/openbb_toolkit.py +869 -0
- camel/toolkits/search_toolkit.py +91 -5
- camel/toolkits/stripe_toolkit.py +5 -1
- camel/toolkits/twitter_toolkit.py +24 -16
- camel/types/__init__.py +4 -2
- camel/types/enums.py +34 -1
- camel/types/openai_types.py +6 -4
- camel/types/unified_model_type.py +5 -0
- camel/utils/__init__.py +2 -0
- camel/utils/commons.py +104 -19
- camel/utils/token_counting.py +3 -3
- {camel_ai-0.2.15a0.dist-info → camel_ai-0.2.17.dist-info}/METADATA +160 -177
- {camel_ai-0.2.15a0.dist-info → camel_ai-0.2.17.dist-info}/RECORD +94 -69
- {camel_ai-0.2.15a0.dist-info → camel_ai-0.2.17.dist-info}/WHEEL +1 -1
- camel/bots/discord_app.py +0 -138
- {camel_ai-0.2.15a0.dist-info → camel_ai-0.2.17.dist-info}/LICENSE +0 -0
camel/toolkits/search_toolkit.py
CHANGED
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
import os
|
|
15
15
|
import xml.etree.ElementTree as ET
|
|
16
|
-
from typing import Any, Dict, List, Optional, TypeAlias, Union
|
|
16
|
+
from typing import Any, Dict, List, Literal, Optional, Type, TypeAlias, Union
|
|
17
17
|
|
|
18
18
|
import requests
|
|
19
|
+
from pydantic import BaseModel
|
|
19
20
|
|
|
20
21
|
from camel.toolkits.base import BaseToolkit
|
|
21
22
|
from camel.toolkits.function_tool import FunctionTool
|
|
@@ -63,6 +64,82 @@ class SearchToolkit(BaseToolkit):
|
|
|
63
64
|
|
|
64
65
|
return result
|
|
65
66
|
|
|
67
|
+
@dependencies_required("linkup")
|
|
68
|
+
@api_keys_required(
|
|
69
|
+
[
|
|
70
|
+
(None, "LINKUP_API_KEY"),
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
def search_linkup(
|
|
74
|
+
self,
|
|
75
|
+
query: str,
|
|
76
|
+
depth: Literal["standard", "deep"] = "standard",
|
|
77
|
+
output_type: Literal[
|
|
78
|
+
"searchResults", "sourcedAnswer", "structured"
|
|
79
|
+
] = "searchResults",
|
|
80
|
+
structured_output_schema: Union[Type[BaseModel], str, None] = None,
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
r"""Search for a query in the Linkup API and return results in various
|
|
83
|
+
formats.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
query (str): The search query.
|
|
87
|
+
depth (Literal["standard", "deep"]): The depth of the search.
|
|
88
|
+
"standard" for a straightforward search, "deep" for a more
|
|
89
|
+
comprehensive search.
|
|
90
|
+
output_type (Literal["searchResults", "sourcedAnswer",
|
|
91
|
+
"structured"]): The type of output:
|
|
92
|
+
- "searchResults" for raw search results,
|
|
93
|
+
- "sourcedAnswer" for an answer with supporting sources,
|
|
94
|
+
- "structured" for output based on a provided schema.
|
|
95
|
+
structured_output_schema (Union[Type[BaseModel], str, None]): If
|
|
96
|
+
`output_type` is "structured",specify the schema of the
|
|
97
|
+
output. Can be a Pydantic BaseModel or a JSON schema string.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dict[str, Any]: A dictionary representing the search result. The
|
|
101
|
+
structure depends on the `output_type`. If an error occurs,
|
|
102
|
+
returns an error message.
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
from linkup import LinkupClient
|
|
106
|
+
|
|
107
|
+
# Initialize the Linkup client with the API key
|
|
108
|
+
LINKUP_API_KEY = os.getenv("LINKUP_API_KEY")
|
|
109
|
+
client = LinkupClient(api_key=LINKUP_API_KEY)
|
|
110
|
+
|
|
111
|
+
# Perform the search using the specified output_type
|
|
112
|
+
response = client.search(
|
|
113
|
+
query=query,
|
|
114
|
+
depth=depth,
|
|
115
|
+
output_type=output_type,
|
|
116
|
+
structured_output_schema=structured_output_schema,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if output_type == "searchResults":
|
|
120
|
+
results = [
|
|
121
|
+
item.__dict__
|
|
122
|
+
for item in response.__dict__.get('results', [])
|
|
123
|
+
]
|
|
124
|
+
return {"results": results}
|
|
125
|
+
|
|
126
|
+
elif output_type == "sourcedAnswer":
|
|
127
|
+
answer = response.__dict__.get('answer', '')
|
|
128
|
+
sources = [
|
|
129
|
+
item.__dict__
|
|
130
|
+
for item in response.__dict__.get('sources', [])
|
|
131
|
+
]
|
|
132
|
+
return {"answer": answer, "sources": sources}
|
|
133
|
+
|
|
134
|
+
elif output_type == "structured" and structured_output_schema:
|
|
135
|
+
return response.__dict__
|
|
136
|
+
|
|
137
|
+
else:
|
|
138
|
+
return {"error": f"Invalid output_type: {output_type}"}
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
return {"error": f"An unexpected error occurred: {e!s}"}
|
|
142
|
+
|
|
66
143
|
@dependencies_required("duckduckgo_search")
|
|
67
144
|
def search_duckduckgo(
|
|
68
145
|
self, query: str, source: str = "text", max_results: int = 5
|
|
@@ -151,7 +228,11 @@ class SearchToolkit(BaseToolkit):
|
|
|
151
228
|
# If no answer found, return an empty list
|
|
152
229
|
return responses
|
|
153
230
|
|
|
154
|
-
@api_keys_required(
|
|
231
|
+
@api_keys_required(
|
|
232
|
+
[
|
|
233
|
+
(None, 'BRAVE_API_KEY'),
|
|
234
|
+
]
|
|
235
|
+
)
|
|
155
236
|
def search_brave(
|
|
156
237
|
self,
|
|
157
238
|
q: str,
|
|
@@ -297,7 +378,12 @@ class SearchToolkit(BaseToolkit):
|
|
|
297
378
|
data = response.json()["web"]
|
|
298
379
|
return data
|
|
299
380
|
|
|
300
|
-
@api_keys_required(
|
|
381
|
+
@api_keys_required(
|
|
382
|
+
[
|
|
383
|
+
(None, 'GOOGLE_API_KEY'),
|
|
384
|
+
(None, 'SEARCH_ENGINE_ID'),
|
|
385
|
+
]
|
|
386
|
+
)
|
|
301
387
|
def search_google(
|
|
302
388
|
self, query: str, num_result_pages: int = 5
|
|
303
389
|
) -> List[Dict[str, Any]]:
|
|
@@ -429,8 +515,7 @@ class SearchToolkit(BaseToolkit):
|
|
|
429
515
|
if not WOLFRAMALPHA_APP_ID:
|
|
430
516
|
raise ValueError(
|
|
431
517
|
"`WOLFRAMALPHA_APP_ID` not found in environment "
|
|
432
|
-
"variables. Get `WOLFRAMALPHA_APP_ID` here: "
|
|
433
|
-
"`https://products.wolframalpha.com/api/`."
|
|
518
|
+
"variables. Get `WOLFRAMALPHA_APP_ID` here: `https://products.wolframalpha.com/api/`."
|
|
434
519
|
)
|
|
435
520
|
|
|
436
521
|
try:
|
|
@@ -618,6 +703,7 @@ class SearchToolkit(BaseToolkit):
|
|
|
618
703
|
"""
|
|
619
704
|
return [
|
|
620
705
|
FunctionTool(self.search_wiki),
|
|
706
|
+
FunctionTool(self.search_linkup),
|
|
621
707
|
FunctionTool(self.search_google),
|
|
622
708
|
FunctionTool(self.search_duckduckgo),
|
|
623
709
|
FunctionTool(self.query_wolfram_alpha),
|
camel/toolkits/stripe_toolkit.py
CHANGED
|
@@ -36,7 +36,11 @@ class StripeToolkit(BaseToolkit):
|
|
|
36
36
|
logger (Logger): a logger to write logs.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
@api_keys_required(
|
|
39
|
+
@api_keys_required(
|
|
40
|
+
[
|
|
41
|
+
(None, "STRIPE_API_KEY"),
|
|
42
|
+
]
|
|
43
|
+
)
|
|
40
44
|
def __init__(self, retries: int = 3):
|
|
41
45
|
r"""Initializes the StripeToolkit with the specified number of
|
|
42
46
|
retries.
|
|
@@ -31,10 +31,12 @@ logger = get_logger(__name__)
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
@api_keys_required(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
[
|
|
35
|
+
(None, "TWITTER_CONSUMER_KEY"),
|
|
36
|
+
(None, "TWITTER_CONSUMER_SECRET"),
|
|
37
|
+
(None, "TWITTER_ACCESS_TOKEN"),
|
|
38
|
+
(None, "TWITTER_ACCESS_TOKEN_SECRET"),
|
|
39
|
+
]
|
|
38
40
|
)
|
|
39
41
|
def create_tweet(
|
|
40
42
|
text: str,
|
|
@@ -132,10 +134,12 @@ def create_tweet(
|
|
|
132
134
|
|
|
133
135
|
|
|
134
136
|
@api_keys_required(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
[
|
|
138
|
+
(None, "TWITTER_CONSUMER_KEY"),
|
|
139
|
+
(None, "TWITTER_CONSUMER_SECRET"),
|
|
140
|
+
(None, "TWITTER_ACCESS_TOKEN"),
|
|
141
|
+
(None, "TWITTER_ACCESS_TOKEN_SECRET"),
|
|
142
|
+
]
|
|
139
143
|
)
|
|
140
144
|
def delete_tweet(tweet_id: str) -> str:
|
|
141
145
|
r"""Deletes a tweet with the specified ID for an authorized user.
|
|
@@ -187,10 +191,12 @@ def delete_tweet(tweet_id: str) -> str:
|
|
|
187
191
|
|
|
188
192
|
|
|
189
193
|
@api_keys_required(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
[
|
|
195
|
+
(None, "TWITTER_CONSUMER_KEY"),
|
|
196
|
+
(None, "TWITTER_CONSUMER_SECRET"),
|
|
197
|
+
(None, "TWITTER_ACCESS_TOKEN"),
|
|
198
|
+
(None, "TWITTER_ACCESS_TOKEN_SECRET"),
|
|
199
|
+
]
|
|
194
200
|
)
|
|
195
201
|
def get_my_user_profile() -> str:
|
|
196
202
|
r"""Retrieves the authenticated user's Twitter profile info.
|
|
@@ -214,10 +220,12 @@ def get_my_user_profile() -> str:
|
|
|
214
220
|
|
|
215
221
|
|
|
216
222
|
@api_keys_required(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
[
|
|
224
|
+
(None, "TWITTER_CONSUMER_KEY"),
|
|
225
|
+
(None, "TWITTER_CONSUMER_SECRET"),
|
|
226
|
+
(None, "TWITTER_ACCESS_TOKEN"),
|
|
227
|
+
(None, "TWITTER_ACCESS_TOKEN_SECRET"),
|
|
228
|
+
]
|
|
221
229
|
)
|
|
222
230
|
def get_user_by_username(username: str) -> str:
|
|
223
231
|
r"""Retrieves one user's Twitter profile info by username (handle).
|
camel/types/__init__.py
CHANGED
|
@@ -33,10 +33,11 @@ from .openai_types import (
|
|
|
33
33
|
ChatCompletion,
|
|
34
34
|
ChatCompletionAssistantMessageParam,
|
|
35
35
|
ChatCompletionChunk,
|
|
36
|
-
ChatCompletionFunctionMessageParam,
|
|
37
36
|
ChatCompletionMessage,
|
|
38
37
|
ChatCompletionMessageParam,
|
|
38
|
+
ChatCompletionMessageToolCall,
|
|
39
39
|
ChatCompletionSystemMessageParam,
|
|
40
|
+
ChatCompletionToolMessageParam,
|
|
40
41
|
ChatCompletionUserMessageParam,
|
|
41
42
|
Choice,
|
|
42
43
|
CompletionUsage,
|
|
@@ -62,7 +63,8 @@ __all__ = [
|
|
|
62
63
|
'ChatCompletionSystemMessageParam',
|
|
63
64
|
'ChatCompletionUserMessageParam',
|
|
64
65
|
'ChatCompletionAssistantMessageParam',
|
|
65
|
-
'
|
|
66
|
+
'ChatCompletionToolMessageParam',
|
|
67
|
+
'ChatCompletionMessageToolCall',
|
|
66
68
|
'CompletionUsage',
|
|
67
69
|
'OpenAIImageType',
|
|
68
70
|
'OpenAIVisionDetailType',
|
camel/types/enums.py
CHANGED
|
@@ -142,6 +142,12 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
142
142
|
# DeepSeek models
|
|
143
143
|
DEEPSEEK_CHAT = "deepseek-chat"
|
|
144
144
|
|
|
145
|
+
# InternLM models
|
|
146
|
+
INTERNLM3_LATEST = "internlm3-latest"
|
|
147
|
+
INTERNLM3_8B_INSTRUCT = "internlm3-8b-instruct"
|
|
148
|
+
INTERNLM2_5_LATEST = "internlm2.5-latest"
|
|
149
|
+
INTERNLM2_PRO_CHAT = "internlm2-pro-chat"
|
|
150
|
+
|
|
145
151
|
def __str__(self):
|
|
146
152
|
return self.value
|
|
147
153
|
|
|
@@ -161,7 +167,15 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
161
167
|
@property
|
|
162
168
|
def support_native_tool_calling(self) -> bool:
|
|
163
169
|
return any(
|
|
164
|
-
[
|
|
170
|
+
[
|
|
171
|
+
self.is_openai,
|
|
172
|
+
self.is_gemini,
|
|
173
|
+
self.is_mistral,
|
|
174
|
+
self.is_qwen,
|
|
175
|
+
self.is_deepseek,
|
|
176
|
+
self.is_cohere,
|
|
177
|
+
self.is_internlm,
|
|
178
|
+
]
|
|
165
179
|
)
|
|
166
180
|
|
|
167
181
|
@property
|
|
@@ -353,6 +367,15 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
353
367
|
ModelType.DEEPSEEK_CHAT,
|
|
354
368
|
}
|
|
355
369
|
|
|
370
|
+
@property
|
|
371
|
+
def is_internlm(self) -> bool:
|
|
372
|
+
return self in {
|
|
373
|
+
ModelType.INTERNLM3_LATEST,
|
|
374
|
+
ModelType.INTERNLM3_8B_INSTRUCT,
|
|
375
|
+
ModelType.INTERNLM2_5_LATEST,
|
|
376
|
+
ModelType.INTERNLM2_PRO_CHAT,
|
|
377
|
+
}
|
|
378
|
+
|
|
356
379
|
@property
|
|
357
380
|
def token_limit(self) -> int:
|
|
358
381
|
r"""Returns the maximum token limit for a given model.
|
|
@@ -411,6 +434,10 @@ class ModelType(UnifiedModelType, Enum):
|
|
|
411
434
|
ModelType.NVIDIA_MISTRAL_LARGE,
|
|
412
435
|
ModelType.NVIDIA_MIXTRAL_8X7B,
|
|
413
436
|
ModelType.QWEN_QWQ_32B,
|
|
437
|
+
ModelType.INTERNLM3_8B_INSTRUCT,
|
|
438
|
+
ModelType.INTERNLM3_LATEST,
|
|
439
|
+
ModelType.INTERNLM2_5_LATEST,
|
|
440
|
+
ModelType.INTERNLM2_PRO_CHAT,
|
|
414
441
|
}:
|
|
415
442
|
return 32_768
|
|
416
443
|
elif self in {
|
|
@@ -634,6 +661,7 @@ class ModelPlatformType(Enum):
|
|
|
634
661
|
NVIDIA = "nvidia"
|
|
635
662
|
DEEPSEEK = "deepseek"
|
|
636
663
|
SGLANG = "sglang"
|
|
664
|
+
INTERNLM = "internlm"
|
|
637
665
|
|
|
638
666
|
@property
|
|
639
667
|
def is_openai(self) -> bool:
|
|
@@ -736,6 +764,11 @@ class ModelPlatformType(Enum):
|
|
|
736
764
|
r"""Returns whether this platform is DeepSeek."""
|
|
737
765
|
return self is ModelPlatformType.DEEPSEEK
|
|
738
766
|
|
|
767
|
+
@property
|
|
768
|
+
def is_internlm(self) -> bool:
|
|
769
|
+
r"""Returns whether this platform is InternLM."""
|
|
770
|
+
return self is ModelPlatformType.INTERNLM
|
|
771
|
+
|
|
739
772
|
|
|
740
773
|
class AudioModelType(Enum):
|
|
741
774
|
TTS_1 = "tts-1"
|
camel/types/openai_types.py
CHANGED
|
@@ -16,10 +16,10 @@ from openai.types.chat.chat_completion import ChatCompletion, Choice
|
|
|
16
16
|
from openai.types.chat.chat_completion_assistant_message_param import (
|
|
17
17
|
ChatCompletionAssistantMessageParam,
|
|
18
18
|
)
|
|
19
|
-
from openai.types.chat.
|
|
20
|
-
|
|
21
|
-
ChatCompletionFunctionMessageParam,
|
|
19
|
+
from openai.types.chat.chat_completion_tool_message_param import (
|
|
20
|
+
ChatCompletionToolMessageParam,
|
|
22
21
|
)
|
|
22
|
+
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
|
|
23
23
|
from openai.types.chat.chat_completion_message import ChatCompletionMessage
|
|
24
24
|
from openai.types.chat.chat_completion_message_param import (
|
|
25
25
|
ChatCompletionMessageParam,
|
|
@@ -33,6 +33,7 @@ from openai.types.chat.chat_completion_user_message_param import (
|
|
|
33
33
|
from openai.types.completion_usage import CompletionUsage
|
|
34
34
|
from openai.types.chat import ParsedChatCompletion
|
|
35
35
|
from openai._types import NOT_GIVEN, NotGiven
|
|
36
|
+
from openai.types.chat import ChatCompletionMessageToolCall
|
|
36
37
|
|
|
37
38
|
Choice = Choice
|
|
38
39
|
ChatCompletion = ChatCompletion
|
|
@@ -42,7 +43,8 @@ ChatCompletionMessageParam = ChatCompletionMessageParam
|
|
|
42
43
|
ChatCompletionSystemMessageParam = ChatCompletionSystemMessageParam
|
|
43
44
|
ChatCompletionUserMessageParam = ChatCompletionUserMessageParam
|
|
44
45
|
ChatCompletionAssistantMessageParam = ChatCompletionAssistantMessageParam
|
|
45
|
-
|
|
46
|
+
ChatCompletionToolMessageParam = ChatCompletionToolMessageParam
|
|
47
|
+
ChatCompletionMessageToolCall = ChatCompletionMessageToolCall
|
|
46
48
|
CompletionUsage = CompletionUsage
|
|
47
49
|
NOT_GIVEN = NOT_GIVEN
|
|
48
50
|
NotGiven = NotGiven
|
|
@@ -113,6 +113,11 @@ class UnifiedModelType(str):
|
|
|
113
113
|
r"""Returns whether the model is a Qwen model."""
|
|
114
114
|
return True
|
|
115
115
|
|
|
116
|
+
@property
|
|
117
|
+
def is_internlm(self) -> bool:
|
|
118
|
+
r"""Returns whether the model is a InternLM model."""
|
|
119
|
+
return True
|
|
120
|
+
|
|
116
121
|
@property
|
|
117
122
|
def support_native_structured_output(self) -> bool:
|
|
118
123
|
r"""Returns whether the model supports native structured output."""
|
camel/utils/__init__.py
CHANGED
|
@@ -19,6 +19,7 @@ from .commons import (
|
|
|
19
19
|
check_server_running,
|
|
20
20
|
create_chunks,
|
|
21
21
|
dependencies_required,
|
|
22
|
+
download_github_subdirectory,
|
|
22
23
|
download_tasks,
|
|
23
24
|
func_string_to_callable,
|
|
24
25
|
generate_prompt_for_structured_output,
|
|
@@ -79,5 +80,6 @@ __all__ = [
|
|
|
79
80
|
"track_agent",
|
|
80
81
|
"handle_http_error",
|
|
81
82
|
"get_pydantic_model",
|
|
83
|
+
"download_github_subdirectory",
|
|
82
84
|
"generate_prompt_for_structured_output",
|
|
83
85
|
]
|
camel/utils/commons.py
CHANGED
|
@@ -21,6 +21,7 @@ import time
|
|
|
21
21
|
import zipfile
|
|
22
22
|
from functools import wraps
|
|
23
23
|
from http import HTTPStatus
|
|
24
|
+
from pathlib import Path
|
|
24
25
|
from typing import (
|
|
25
26
|
Any,
|
|
26
27
|
Callable,
|
|
@@ -29,6 +30,7 @@ from typing import (
|
|
|
29
30
|
Mapping,
|
|
30
31
|
Optional,
|
|
31
32
|
Set,
|
|
33
|
+
Tuple,
|
|
32
34
|
Type,
|
|
33
35
|
TypeVar,
|
|
34
36
|
cast,
|
|
@@ -231,41 +233,79 @@ def is_module_available(module_name: str) -> bool:
|
|
|
231
233
|
return False
|
|
232
234
|
|
|
233
235
|
|
|
234
|
-
def api_keys_required(
|
|
235
|
-
|
|
236
|
-
|
|
236
|
+
def api_keys_required(
|
|
237
|
+
param_env_list: List[Tuple[Optional[str], str]],
|
|
238
|
+
) -> Callable[[F], F]:
|
|
239
|
+
r"""A decorator to check if the required API keys are provided in the
|
|
240
|
+
environment variables or as function arguments.
|
|
237
241
|
|
|
238
242
|
Args:
|
|
239
|
-
|
|
243
|
+
param_env_list (List[Tuple[Optional[str], str]]): A list of tuples
|
|
244
|
+
where each tuple contains a function argument name (as the first
|
|
245
|
+
element, or None) and the corresponding environment variable name
|
|
246
|
+
(as the second element) that holds the API key.
|
|
240
247
|
|
|
241
248
|
Returns:
|
|
242
|
-
Callable[[F], F]: The original function with the added check
|
|
243
|
-
for required API keys.
|
|
249
|
+
Callable[[F], F]: The original function wrapped with the added check
|
|
250
|
+
for the required API keys.
|
|
244
251
|
|
|
245
252
|
Raises:
|
|
246
|
-
ValueError: If any of the required API keys are missing
|
|
247
|
-
|
|
253
|
+
ValueError: If any of the required API keys are missing, either
|
|
254
|
+
from the function arguments or environment variables.
|
|
248
255
|
|
|
249
256
|
Example:
|
|
250
257
|
::
|
|
251
258
|
|
|
252
|
-
@api_keys_required(
|
|
253
|
-
|
|
254
|
-
|
|
259
|
+
@api_keys_required([
|
|
260
|
+
('api_key_arg', 'API_KEY_1'),
|
|
261
|
+
('another_key_arg', 'API_KEY_2'),
|
|
262
|
+
(None, 'API_KEY_3'),
|
|
263
|
+
])
|
|
264
|
+
def some_api_function(api_key_arg=None, another_key_arg=None):
|
|
265
|
+
# Function implementation that requires API keys
|
|
255
266
|
"""
|
|
267
|
+
import inspect
|
|
256
268
|
|
|
257
269
|
def decorator(func: F) -> F:
|
|
258
270
|
@wraps(func)
|
|
259
271
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
272
|
+
signature = inspect.signature(func)
|
|
273
|
+
bound_arguments = signature.bind(*args, **kwargs)
|
|
274
|
+
bound_arguments.apply_defaults()
|
|
275
|
+
arguments = bound_arguments.arguments
|
|
276
|
+
|
|
277
|
+
missing_keys = []
|
|
278
|
+
for param_name, env_var_name in param_env_list:
|
|
279
|
+
if not isinstance(env_var_name, str):
|
|
280
|
+
raise TypeError(
|
|
281
|
+
f"Environment variable name must be a string, got"
|
|
282
|
+
f" {type(env_var_name)}"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
value = None
|
|
286
|
+
if (
|
|
287
|
+
param_name
|
|
288
|
+
): # If param_name is provided, check function argument first
|
|
289
|
+
if not isinstance(param_name, str):
|
|
290
|
+
raise TypeError(
|
|
291
|
+
f"Parameter name must be a string, "
|
|
292
|
+
f"got {type(param_name)}"
|
|
293
|
+
)
|
|
294
|
+
value = arguments.get(param_name)
|
|
295
|
+
# If we found a valid value in arguments, continue to next
|
|
296
|
+
# item
|
|
297
|
+
if value:
|
|
298
|
+
continue
|
|
299
|
+
|
|
300
|
+
# Check environment variable if no valid value found yet
|
|
301
|
+
value = os.environ.get(env_var_name)
|
|
302
|
+
if not value or value.strip() == "":
|
|
303
|
+
missing_keys.append(env_var_name)
|
|
304
|
+
|
|
305
|
+
if missing_keys:
|
|
267
306
|
raise ValueError(
|
|
268
|
-
|
|
307
|
+
"Missing or empty required API keys in "
|
|
308
|
+
f"environment variables: {', '.join(missing_keys)}"
|
|
269
309
|
)
|
|
270
310
|
return func(*args, **kwargs)
|
|
271
311
|
|
|
@@ -609,6 +649,51 @@ def retry_request(
|
|
|
609
649
|
raise
|
|
610
650
|
|
|
611
651
|
|
|
652
|
+
def download_github_subdirectory(
|
|
653
|
+
repo: str, subdir: str, data_dir: Path, branch="main"
|
|
654
|
+
):
|
|
655
|
+
r"""Download subdirectory of the Github repo of
|
|
656
|
+
the benchmark.
|
|
657
|
+
|
|
658
|
+
This function downloads all files and subdirectories from a
|
|
659
|
+
specified subdirectory of a GitHub repository and
|
|
660
|
+
saves them to a local directory.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
repo (str): The name of the GitHub repository
|
|
664
|
+
in the format "owner/repo".
|
|
665
|
+
subdir (str): The path to the subdirectory
|
|
666
|
+
within the repository to download.
|
|
667
|
+
data_dir (Path): The local directory where
|
|
668
|
+
the files will be saved.
|
|
669
|
+
branch (str, optional): The branch of the repository to use.
|
|
670
|
+
Defaults to "main".
|
|
671
|
+
"""
|
|
672
|
+
from tqdm import tqdm
|
|
673
|
+
|
|
674
|
+
api_url = (
|
|
675
|
+
f"https://api.github.com/repos/{repo}/contents/{subdir}?ref={branch}"
|
|
676
|
+
)
|
|
677
|
+
headers = {"Accept": "application/vnd.github.v3+json"}
|
|
678
|
+
response = requests.get(api_url, headers=headers)
|
|
679
|
+
response.raise_for_status()
|
|
680
|
+
files = response.json()
|
|
681
|
+
os.makedirs(data_dir, exist_ok=True)
|
|
682
|
+
|
|
683
|
+
for file in tqdm(files, desc="Downloading"):
|
|
684
|
+
file_path = data_dir / file["name"]
|
|
685
|
+
|
|
686
|
+
if file["type"] == "file":
|
|
687
|
+
file_url = file["download_url"]
|
|
688
|
+
file_response = requests.get(file_url)
|
|
689
|
+
with open(file_path, "wb") as f:
|
|
690
|
+
f.write(file_response.content)
|
|
691
|
+
elif file["type"] == "dir":
|
|
692
|
+
download_github_subdirectory(
|
|
693
|
+
repo, f'{subdir}/{file["name"]}', file_path, branch
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
|
|
612
697
|
def generate_prompt_for_structured_output(
|
|
613
698
|
response_format: Optional[Type[BaseModel]],
|
|
614
699
|
user_message: str,
|
camel/utils/token_counting.py
CHANGED
|
@@ -253,11 +253,11 @@ class AnthropicTokenCounter(BaseTokenCounter):
|
|
|
253
253
|
Returns:
|
|
254
254
|
int: Number of tokens in the messages.
|
|
255
255
|
"""
|
|
256
|
-
from anthropic.types
|
|
256
|
+
from anthropic.types import MessageParam
|
|
257
257
|
|
|
258
|
-
return self.client.
|
|
258
|
+
return self.client.messages.count_tokens(
|
|
259
259
|
messages=[
|
|
260
|
-
|
|
260
|
+
MessageParam(
|
|
261
261
|
content=str(msg["content"]),
|
|
262
262
|
role="user" if msg["role"] == "user" else "assistant",
|
|
263
263
|
)
|