camel-ai 0.2.37__py3-none-any.whl → 0.2.39__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 +4 -0
- camel/agents/repo_agent.py +2 -2
- camel/benchmarks/apibank.py +1 -1
- camel/benchmarks/apibench.py +1 -1
- camel/configs/__init__.py +3 -0
- camel/configs/modelscope_config.py +59 -0
- camel/datagen/evol_instruct/__init__.py +20 -0
- camel/datagen/evol_instruct/evol_instruct.py +424 -0
- camel/datagen/evol_instruct/scorer.py +166 -0
- camel/datagen/evol_instruct/templates.py +268 -0
- camel/datagen/self_improving_cot.py +1 -1
- camel/datasets/__init__.py +2 -0
- camel/datasets/base_generator.py +22 -9
- camel/datasets/few_shot_generator.py +2 -3
- camel/datasets/self_instruct_generator.py +415 -0
- camel/embeddings/openai_compatible_embedding.py +13 -5
- camel/environments/models.py +10 -4
- camel/environments/single_step.py +181 -41
- camel/interpreters/docker_interpreter.py +2 -2
- camel/interpreters/e2b_interpreter.py +1 -1
- camel/interpreters/internal_python_interpreter.py +1 -1
- camel/interpreters/subprocess_interpreter.py +1 -1
- camel/loaders/__init__.py +2 -2
- camel/loaders/{panda_reader.py → pandas_reader.py} +61 -30
- camel/loaders/unstructured_io.py +2 -1
- camel/memories/blocks/chat_history_block.py +1 -1
- camel/memories/context_creators/score_based.py +198 -67
- camel/models/__init__.py +2 -0
- camel/models/aiml_model.py +9 -3
- camel/models/anthropic_model.py +11 -3
- camel/models/azure_openai_model.py +9 -3
- camel/models/base_audio_model.py +6 -0
- camel/models/base_model.py +4 -0
- camel/models/deepseek_model.py +9 -3
- camel/models/gemini_model.py +9 -3
- camel/models/groq_model.py +9 -3
- camel/models/internlm_model.py +8 -2
- camel/models/model_factory.py +123 -0
- camel/models/modelscope_model.py +208 -0
- camel/models/moonshot_model.py +8 -2
- camel/models/nemotron_model.py +9 -3
- camel/models/nvidia_model.py +9 -3
- camel/models/ollama_model.py +9 -3
- camel/models/openai_audio_models.py +7 -5
- camel/models/openai_compatible_model.py +9 -3
- camel/models/openai_model.py +58 -5
- camel/models/openrouter_model.py +9 -3
- camel/models/qwen_model.py +9 -3
- camel/models/samba_model.py +9 -3
- camel/models/sglang_model.py +11 -4
- camel/models/siliconflow_model.py +8 -2
- camel/models/stub_model.py +2 -1
- camel/models/togetherai_model.py +11 -5
- camel/models/vllm_model.py +10 -4
- camel/models/yi_model.py +9 -3
- camel/models/zhipuai_model.py +11 -5
- camel/retrievers/auto_retriever.py +14 -0
- camel/retrievers/vector_retriever.py +1 -1
- camel/storages/__init__.py +2 -0
- camel/storages/graph_storages/neo4j_graph.py +1 -1
- camel/storages/vectordb_storages/__init__.py +2 -0
- camel/storages/vectordb_storages/base.py +2 -2
- camel/storages/vectordb_storages/milvus.py +2 -2
- camel/storages/vectordb_storages/qdrant.py +2 -2
- camel/storages/vectordb_storages/tidb.py +332 -0
- camel/tasks/task.py +2 -2
- camel/toolkits/__init__.py +9 -1
- camel/toolkits/arxiv_toolkit.py +2 -1
- camel/toolkits/ask_news_toolkit.py +11 -3
- camel/toolkits/audio_analysis_toolkit.py +2 -0
- camel/toolkits/base.py +3 -0
- camel/toolkits/browser_toolkit.py +84 -61
- camel/toolkits/code_execution.py +3 -1
- camel/toolkits/dappier_toolkit.py +2 -1
- camel/toolkits/data_commons_toolkit.py +2 -0
- camel/toolkits/excel_toolkit.py +2 -0
- camel/toolkits/file_write_toolkit.py +2 -0
- camel/toolkits/github_toolkit.py +6 -4
- camel/toolkits/google_scholar_toolkit.py +2 -0
- camel/toolkits/human_toolkit.py +17 -1
- camel/toolkits/image_analysis_toolkit.py +2 -0
- camel/toolkits/linkedin_toolkit.py +2 -1
- camel/toolkits/math_toolkit.py +2 -0
- camel/toolkits/mcp_toolkit.py +42 -52
- camel/toolkits/meshy_toolkit.py +20 -2
- camel/toolkits/networkx_toolkit.py +2 -0
- camel/toolkits/notion_toolkit.py +7 -0
- camel/toolkits/openai_agent_toolkit.py +131 -0
- camel/toolkits/openbb_toolkit.py +2 -1
- camel/toolkits/pubmed_toolkit.py +2 -0
- camel/toolkits/reddit_toolkit.py +2 -1
- camel/toolkits/retrieval_toolkit.py +2 -1
- camel/toolkits/search_toolkit.py +2 -1
- camel/toolkits/searxng_toolkit.py +207 -0
- camel/toolkits/semantic_scholar_toolkit.py +2 -0
- camel/toolkits/slack_toolkit.py +2 -0
- camel/toolkits/stripe_toolkit.py +2 -1
- camel/toolkits/sympy_toolkit.py +2 -0
- camel/toolkits/terminal_toolkit.py +2 -0
- camel/toolkits/thinking_toolkit.py +168 -12
- camel/toolkits/twitter_toolkit.py +2 -1
- camel/toolkits/video_analysis_toolkit.py +2 -1
- camel/toolkits/video_download_toolkit.py +2 -1
- camel/toolkits/weather_toolkit.py +2 -0
- camel/toolkits/whatsapp_toolkit.py +2 -1
- camel/toolkits/zapier_toolkit.py +2 -1
- camel/types/enums.py +66 -0
- camel/types/unified_model_type.py +5 -0
- camel/utils/__init__.py +2 -0
- camel/utils/chunker/code_chunker.py +9 -9
- camel/utils/commons.py +50 -30
- camel/utils/constants.py +2 -2
- camel/utils/mcp.py +79 -0
- camel/verifiers/__init__.py +2 -0
- camel/verifiers/base.py +15 -15
- camel/verifiers/math_verifier.py +182 -0
- camel/verifiers/python_verifier.py +28 -28
- {camel_ai-0.2.37.dist-info → camel_ai-0.2.39.dist-info}/METADATA +54 -4
- {camel_ai-0.2.37.dist-info → camel_ai-0.2.39.dist-info}/RECORD +122 -110
- {camel_ai-0.2.37.dist-info → camel_ai-0.2.39.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.37.dist-info → camel_ai-0.2.39.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -526,6 +526,10 @@ class ChatAgent(BaseAgent):
|
|
|
526
526
|
message.content = response.output_messages[0].content
|
|
527
527
|
if not self._try_format_message(message, response_format):
|
|
528
528
|
logger.warning(f"Failed to parse response: {message.content}")
|
|
529
|
+
logger.warning(
|
|
530
|
+
"To improve reliability, consider using models "
|
|
531
|
+
"that are better equipped to handle structured output"
|
|
532
|
+
)
|
|
529
533
|
|
|
530
534
|
async def _aformat_response_if_needed(
|
|
531
535
|
self,
|
camel/agents/repo_agent.py
CHANGED
|
@@ -17,7 +17,7 @@ from string import Template
|
|
|
17
17
|
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
|
-
from github import Github
|
|
20
|
+
from github.MainClass import Github
|
|
21
21
|
from pydantic import BaseModel
|
|
22
22
|
|
|
23
23
|
from camel.agents import ChatAgent
|
|
@@ -219,7 +219,7 @@ class RepoAgent(ChatAgent):
|
|
|
219
219
|
List[RepositoryInfo]: A list of objects containing information
|
|
220
220
|
about the all repositories, including the contents.
|
|
221
221
|
"""
|
|
222
|
-
from github import Github
|
|
222
|
+
from github.MainClass import Github
|
|
223
223
|
|
|
224
224
|
github_client = Github(self.github_auth_token)
|
|
225
225
|
res = []
|
camel/benchmarks/apibank.py
CHANGED
|
@@ -48,7 +48,7 @@ def process_messages(
|
|
|
48
48
|
Args:
|
|
49
49
|
chat_history (List[Dict[str, Any]):
|
|
50
50
|
A list of dictionaries representing the chat history.
|
|
51
|
-
prompt (str): A
|
|
51
|
+
prompt (str): A prompt to be set as the system message.
|
|
52
52
|
|
|
53
53
|
Returns:
|
|
54
54
|
List[Dict[str, str]]: A list of dictionaries representing
|
camel/benchmarks/apibench.py
CHANGED
|
@@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
# Mapping of dataset names to file names
|
|
33
|
-
# 'Oracle'
|
|
33
|
+
# 'Oracle' retriever used here which means all the full
|
|
34
34
|
# API documentation will be included in the prompt
|
|
35
35
|
dataset_mapping = {
|
|
36
36
|
"huggingface": {
|
camel/configs/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ from .groq_config import GROQ_API_PARAMS, GroqConfig
|
|
|
21
21
|
from .internlm_config import INTERNLM_API_PARAMS, InternLMConfig
|
|
22
22
|
from .litellm_config import LITELLM_API_PARAMS, LiteLLMConfig
|
|
23
23
|
from .mistral_config import MISTRAL_API_PARAMS, MistralConfig
|
|
24
|
+
from .modelscope_config import MODELSCOPE_API_PARAMS, ModelScopeConfig
|
|
24
25
|
from .moonshot_config import MOONSHOT_API_PARAMS, MoonshotConfig
|
|
25
26
|
from .nvidia_config import NVIDIA_API_PARAMS, NvidiaConfig
|
|
26
27
|
from .ollama_config import OLLAMA_API_PARAMS, OllamaConfig
|
|
@@ -85,6 +86,8 @@ __all__ = [
|
|
|
85
86
|
'INTERNLM_API_PARAMS',
|
|
86
87
|
'MoonshotConfig',
|
|
87
88
|
"MOONSHOT_API_PARAMS",
|
|
89
|
+
'ModelScopeConfig',
|
|
90
|
+
'MODELSCOPE_API_PARAMS',
|
|
88
91
|
'SiliconFlowConfig',
|
|
89
92
|
'SILICONFLOW_API_PARAMS',
|
|
90
93
|
'AIMLConfig',
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Optional, Union
|
|
17
|
+
|
|
18
|
+
from camel.configs.base_config import BaseConfig
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ModelScopeConfig(BaseConfig):
|
|
22
|
+
r"""Defines the parameters for generating chat completions using the
|
|
23
|
+
ModelScope API. You can refer to the following link for more details:
|
|
24
|
+
https://www.modelscope.cn/docs/model-service/API-Inference/intro
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
tool_choice (Union[dict[str, str], str], optional): Controls which (if
|
|
28
|
+
any) tool is called by the model. :obj:`"none"` means the model
|
|
29
|
+
will not call any tool and instead generates a message.
|
|
30
|
+
:obj:`"auto"` means the model can pick between generating a
|
|
31
|
+
message or calling one or more tools. :obj:`"required"` or
|
|
32
|
+
specifying a particular tool via
|
|
33
|
+
{"type": "function", "function": {"name": "some_function"}}
|
|
34
|
+
can be used to guide the model to use tools more strongly.
|
|
35
|
+
(default: :obj:`None`)
|
|
36
|
+
max_tokens (int, optional): Specifies the maximum number of tokens
|
|
37
|
+
the model can generate. This sets an upper limit, but does not
|
|
38
|
+
guarantee that this number will always be reached.
|
|
39
|
+
(default: :obj:`None`)
|
|
40
|
+
top_p (float, optional): Controls the randomness of the generated
|
|
41
|
+
results. Lower values lead to less randomness, while higher
|
|
42
|
+
values increase randomness. (default: :obj:`None`)
|
|
43
|
+
temperature (float, optional): Controls the diversity and focus of
|
|
44
|
+
the generated results. Lower values make the output more focused,
|
|
45
|
+
while higher values make it more diverse. (default: :obj:`0.3`)
|
|
46
|
+
stream (bool, optional): If True, enables streaming output.
|
|
47
|
+
(default: :obj:`None`)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
tool_choice: Optional[Union[dict[str, str], str]] = None
|
|
51
|
+
max_tokens: Optional[int] = None
|
|
52
|
+
top_p: Optional[float] = None
|
|
53
|
+
temperature: Optional[float] = None
|
|
54
|
+
stream: Optional[bool] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
MODELSCOPE_API_PARAMS = {
|
|
58
|
+
param for param in ModelScopeConfig.model_fields.keys()
|
|
59
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
from .evol_instruct import EvolInstructPipeline
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'EvolInstructPipeline',
|
|
19
|
+
'MathEvolInstructTemplates',
|
|
20
|
+
]
|
|
@@ -0,0 +1,424 @@
|
|
|
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 random
|
|
16
|
+
import time
|
|
17
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
18
|
+
from math import ceil
|
|
19
|
+
from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast
|
|
20
|
+
|
|
21
|
+
from tqdm import tqdm
|
|
22
|
+
|
|
23
|
+
from camel.agents import ChatAgent
|
|
24
|
+
from camel.datagen.evol_instruct.scorer import BaseScorer, GeneralScorer
|
|
25
|
+
from camel.datagen.evol_instruct.templates import EvolInstructTemplates
|
|
26
|
+
from camel.logger import get_logger
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class EvolInstructPipeline:
|
|
32
|
+
r"""Pipeline for evolving prompts using the Evol-Instruct methodology.
|
|
33
|
+
|
|
34
|
+
Supports custom templates defining evolution strategies and methods. The
|
|
35
|
+
pipeline leverages language models to iteratively refine prompts through
|
|
36
|
+
specified evolution strategies.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
templates (Type[EvolInstructTemplates]): Template class containing
|
|
40
|
+
evolution strategy and method definitions. Must provide
|
|
41
|
+
`EVOL_METHODS` and `STRATEGY` attributes.
|
|
42
|
+
(default: :obj:`EvolInstructTemplates`)
|
|
43
|
+
agent (Optional[ChatAgent]): Chat agent instance for LLM interaction.
|
|
44
|
+
If :obj:`None`, initializes with a default ChatAgent.
|
|
45
|
+
(default: :obj:`None`)
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
templates: Type = EvolInstructTemplates,
|
|
51
|
+
agent: Optional[ChatAgent] = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
r"""Initialize pipeline with templates and language model agent.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
templates (Type[EvolInstructTemplates]): Template class containing
|
|
57
|
+
evolution strategy configurations.
|
|
58
|
+
(default: :obj:`EvolInstructTemplates`)
|
|
59
|
+
agent (Optional[ChatAgent]): Preconfigured chat agent instance.
|
|
60
|
+
Creates a default ChatAgent if not provided.
|
|
61
|
+
(default: :obj:`None`)
|
|
62
|
+
"""
|
|
63
|
+
self.templates = templates
|
|
64
|
+
self.agent = agent or ChatAgent()
|
|
65
|
+
|
|
66
|
+
def _resolve_evolution_method(self, method_key: str) -> str:
|
|
67
|
+
r"""Resolve evolution method key to concrete implementation.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
method_key (str): Input method identifier. Can be:
|
|
71
|
+
- Direct method key from templates.EVOL_METHODS
|
|
72
|
+
- Strategy name from templates.STRATEGY keys
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
str: Resolved method key from EVOL_METHODS
|
|
76
|
+
"""
|
|
77
|
+
if method_key in self.templates.EVOL_METHODS:
|
|
78
|
+
return method_key
|
|
79
|
+
if method_key.upper() in self.templates.STRATEGY:
|
|
80
|
+
strategy = self.templates.STRATEGY[method_key.upper()]
|
|
81
|
+
strategy_methods = strategy["methods"]
|
|
82
|
+
return random.choice(strategy_methods)
|
|
83
|
+
|
|
84
|
+
logger.warning(
|
|
85
|
+
f"Invalid evolution method: {method_key}. "
|
|
86
|
+
f"Using random selection."
|
|
87
|
+
)
|
|
88
|
+
return random.choice(list(self.templates.EVOL_METHODS))
|
|
89
|
+
|
|
90
|
+
def _get_evolution_methods(
|
|
91
|
+
self,
|
|
92
|
+
method: Union[str, List[str]],
|
|
93
|
+
num_generations: int = 2,
|
|
94
|
+
) -> List[str]:
|
|
95
|
+
r"""Get list of evolution methods based on input specification.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
method (Union[str, List[str]]): Specification for method selection.
|
|
99
|
+
Can be:
|
|
100
|
+
- Strategy name for methods from that strategy
|
|
101
|
+
- Specific method name
|
|
102
|
+
- List of method specifications
|
|
103
|
+
num_generations (int): Number of methods to return.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
List[str]: List of resolved method names
|
|
107
|
+
"""
|
|
108
|
+
candidate_methods = []
|
|
109
|
+
|
|
110
|
+
if isinstance(method, list):
|
|
111
|
+
for method_spec in method:
|
|
112
|
+
candidate_methods.append(
|
|
113
|
+
self._resolve_evolution_method(method_spec)
|
|
114
|
+
)
|
|
115
|
+
elif isinstance(method, str):
|
|
116
|
+
if method.upper() in self.templates.STRATEGY:
|
|
117
|
+
strategy = self.templates.STRATEGY[method.upper()]
|
|
118
|
+
candidate_methods = strategy["methods"]
|
|
119
|
+
else:
|
|
120
|
+
candidate_methods = [self._resolve_evolution_method(method)]
|
|
121
|
+
|
|
122
|
+
# Remove duplicates while preserving order
|
|
123
|
+
unique_candidates = []
|
|
124
|
+
for method_name in candidate_methods:
|
|
125
|
+
if method_name not in unique_candidates:
|
|
126
|
+
unique_candidates.append(method_name)
|
|
127
|
+
|
|
128
|
+
if len(unique_candidates) >= num_generations:
|
|
129
|
+
methods = random.sample(unique_candidates, num_generations)
|
|
130
|
+
else:
|
|
131
|
+
methods = unique_candidates.copy()
|
|
132
|
+
while len(methods) < num_generations:
|
|
133
|
+
methods.append(random.choice(unique_candidates))
|
|
134
|
+
|
|
135
|
+
return methods
|
|
136
|
+
|
|
137
|
+
def _generate_single_evolution(
|
|
138
|
+
self,
|
|
139
|
+
prompt: str,
|
|
140
|
+
method: str,
|
|
141
|
+
return_method: bool = False,
|
|
142
|
+
) -> Tuple[str, str]:
|
|
143
|
+
r"""Generate a single evolved prompt from a seed prompt.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
prompt (str): The seed prompt to evolve.
|
|
147
|
+
method (str): The evolution method key to use.
|
|
148
|
+
return_method (bool): If True, returns method along with prompt.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Tuple[str, str]: Evolved prompt and method
|
|
152
|
+
"""
|
|
153
|
+
resolved_method = self._resolve_evolution_method(method)
|
|
154
|
+
|
|
155
|
+
# Find strategy containing the resolved method
|
|
156
|
+
strategy_key = None
|
|
157
|
+
for strategy, group in self.templates.STRATEGY.items():
|
|
158
|
+
if resolved_method in group["methods"]:
|
|
159
|
+
strategy_key = strategy
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
if strategy_key is None:
|
|
163
|
+
strategy_key = random.choice(list(self.templates.STRATEGY.keys()))
|
|
164
|
+
|
|
165
|
+
strategy = self.templates.STRATEGY[strategy_key]
|
|
166
|
+
instruction_template = strategy["meta_instruction"]
|
|
167
|
+
instruction = instruction_template.format(
|
|
168
|
+
method=self.templates.EVOL_METHODS.get(
|
|
169
|
+
resolved_method,
|
|
170
|
+
random.choice(list(self.templates.EVOL_METHODS.values())),
|
|
171
|
+
),
|
|
172
|
+
prompt=prompt,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
self.agent.reset()
|
|
176
|
+
response = self.agent.step(instruction)
|
|
177
|
+
evolved_prompt = response.msgs[0].content.strip()
|
|
178
|
+
|
|
179
|
+
if return_method:
|
|
180
|
+
return (evolved_prompt, resolved_method)
|
|
181
|
+
else:
|
|
182
|
+
return (evolved_prompt, "")
|
|
183
|
+
|
|
184
|
+
def _generate_multiple_evolutions(
|
|
185
|
+
self,
|
|
186
|
+
prompt: str,
|
|
187
|
+
method: Union[str, List[str]],
|
|
188
|
+
num_generations: int = 2,
|
|
189
|
+
keep_original: bool = True,
|
|
190
|
+
num_threads: int = 10,
|
|
191
|
+
) -> List[Tuple[str, str]]:
|
|
192
|
+
r"""Generate multiple evolved versions of a prompt.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
prompt (str): Seed prompt to evolve.
|
|
196
|
+
method (Union[str, List[str]]): Evolution method specification.
|
|
197
|
+
num_generations (int): Candidates to generate per iteration.
|
|
198
|
+
keep_original (bool): Whether to keep the original prompt.
|
|
199
|
+
num_threads (int): Number of threads for parallel processing.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List[Tuple[str, str]]: List of (evolved_prompt, method) pairs
|
|
203
|
+
"""
|
|
204
|
+
results = [(prompt, "original")] if keep_original else []
|
|
205
|
+
|
|
206
|
+
if isinstance(method, list) and len(method) == num_generations:
|
|
207
|
+
candidate_methods = method
|
|
208
|
+
else:
|
|
209
|
+
candidate_methods = self._get_evolution_methods(
|
|
210
|
+
method=method, num_generations=num_generations
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def _process_single_method(method_name: str) -> Tuple[str, str]:
|
|
214
|
+
return self._generate_single_evolution(
|
|
215
|
+
prompt, method_name, return_method=True
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
|
219
|
+
evolved_results = list(
|
|
220
|
+
executor.map(_process_single_method, candidate_methods)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
results.extend(evolved_results)
|
|
224
|
+
return results
|
|
225
|
+
|
|
226
|
+
def _generate_iterative_evolutions(
|
|
227
|
+
self,
|
|
228
|
+
prompt: str,
|
|
229
|
+
evolution_spec: Union[str, List[Union[str, List[str]]]],
|
|
230
|
+
num_generations: int = 2,
|
|
231
|
+
num_iterations: Optional[int] = None,
|
|
232
|
+
keep_original: bool = True,
|
|
233
|
+
scorer: Optional[BaseScorer] = None,
|
|
234
|
+
num_threads: int = 10,
|
|
235
|
+
) -> Dict[int, List[Dict[str, Any]]]:
|
|
236
|
+
r"""Generate iterative evolutions of a prompt with scoring.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
prompt (str): Seed prompt to evolve.
|
|
240
|
+
evolution_spec (Union[str, List[Union[str, List[str]]]]):
|
|
241
|
+
Evolution method specification.
|
|
242
|
+
If a list is provided and num_iterations is None, then
|
|
243
|
+
num_iterations is set to the length of the list.
|
|
244
|
+
num_generations (int): Candidates to generate per iteration.
|
|
245
|
+
num_iterations (Optional[int]): Number of evolution iterations.
|
|
246
|
+
Defaults to the length of evolution_spec.
|
|
247
|
+
keep_original (bool): Include original prompt in results.
|
|
248
|
+
scorer (Optional[BaseScorer]): Scoring model for candidate.
|
|
249
|
+
num_threads (int): Number of threads for parallel processing.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Dict[int, List[Dict[str, Any]]]: Evolution results per iteration,
|
|
253
|
+
where each candidate is represented as a dict with keys:
|
|
254
|
+
"instruction", "method", and "scores".
|
|
255
|
+
"""
|
|
256
|
+
if num_iterations is None:
|
|
257
|
+
if isinstance(evolution_spec, list):
|
|
258
|
+
num_iterations = len(evolution_spec)
|
|
259
|
+
else:
|
|
260
|
+
num_iterations = 1
|
|
261
|
+
|
|
262
|
+
results = {}
|
|
263
|
+
current_prompt = prompt
|
|
264
|
+
scorer = scorer or GeneralScorer()
|
|
265
|
+
|
|
266
|
+
for iteration in range(num_iterations):
|
|
267
|
+
if isinstance(evolution_spec, list):
|
|
268
|
+
if iteration < len(evolution_spec):
|
|
269
|
+
iteration_spec = evolution_spec[iteration]
|
|
270
|
+
else:
|
|
271
|
+
iteration_spec = evolution_spec[-1]
|
|
272
|
+
else:
|
|
273
|
+
iteration_spec = evolution_spec
|
|
274
|
+
|
|
275
|
+
batch_results = self._generate_multiple_evolutions(
|
|
276
|
+
prompt=current_prompt,
|
|
277
|
+
method=iteration_spec,
|
|
278
|
+
num_generations=num_generations,
|
|
279
|
+
keep_original=False,
|
|
280
|
+
num_threads=num_threads,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
scored_results = []
|
|
284
|
+
for candidate, method_used in batch_results:
|
|
285
|
+
scores = scorer.score(current_prompt, candidate)
|
|
286
|
+
scored_results.append(
|
|
287
|
+
{
|
|
288
|
+
"instruction": candidate,
|
|
289
|
+
"method": method_used,
|
|
290
|
+
"scores": scores,
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
best_index = max(
|
|
295
|
+
range(len(scored_results)),
|
|
296
|
+
key=lambda i: sum(
|
|
297
|
+
cast(Dict[str, int], scored_results[i]["scores"]).values()
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
best_candidate = cast(
|
|
302
|
+
str, scored_results[best_index]["instruction"]
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if keep_original:
|
|
306
|
+
results[iteration] = [
|
|
307
|
+
{
|
|
308
|
+
"instruction": current_prompt,
|
|
309
|
+
"method": "original",
|
|
310
|
+
"scores": {},
|
|
311
|
+
},
|
|
312
|
+
*scored_results,
|
|
313
|
+
]
|
|
314
|
+
else:
|
|
315
|
+
results[iteration] = scored_results
|
|
316
|
+
|
|
317
|
+
current_prompt = best_candidate
|
|
318
|
+
|
|
319
|
+
return results
|
|
320
|
+
|
|
321
|
+
def generate(
|
|
322
|
+
self,
|
|
323
|
+
prompts: List[str],
|
|
324
|
+
evolution_spec: Union[str, List[Union[str, List[str]]]],
|
|
325
|
+
num_generations: int = 2,
|
|
326
|
+
num_iterations: Optional[int] = None,
|
|
327
|
+
keep_original: bool = True,
|
|
328
|
+
scorer: Optional[BaseScorer] = None,
|
|
329
|
+
num_chunks: int = 1,
|
|
330
|
+
retry_limit: int = 3,
|
|
331
|
+
retry_delay: float = 1.0,
|
|
332
|
+
num_threads: int = 10,
|
|
333
|
+
) -> List[Dict[int, List[Dict[str, Any]]]]:
|
|
334
|
+
r"""Evolve a batch of prompts through iterative refinement.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
prompts (List[str]): Seed prompts to evolve.
|
|
338
|
+
evolution_spec (Union[str, List[Union[str, List[str]]]]):
|
|
339
|
+
Evolution method specification.
|
|
340
|
+
If a list is provided and num_iterations is None, then
|
|
341
|
+
num_iterations is set to the length of the list.
|
|
342
|
+
num_generations (int): Candidates to generate per iteration.
|
|
343
|
+
num_iterations (Optional[int]): Number of evolution iterations.
|
|
344
|
+
Defaults to the length of evolution_spec.
|
|
345
|
+
keep_original (bool): Include original prompts in results.
|
|
346
|
+
scorer (Optional[BaseScorer]): Scoring model for candidate.
|
|
347
|
+
num_chunks (int): Number of parallel processing chunks.
|
|
348
|
+
retry_limit (int): Max retries for failed generations.
|
|
349
|
+
retry_delay (float): Delay between retries in seconds.
|
|
350
|
+
num_threads (int): Number of threads for parallel processing.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
List[Dict[int, List[Dict[str, Any]]]]: Evolution results.
|
|
354
|
+
"""
|
|
355
|
+
if num_iterations is None:
|
|
356
|
+
if isinstance(evolution_spec, list):
|
|
357
|
+
num_iterations = len(evolution_spec)
|
|
358
|
+
else:
|
|
359
|
+
num_iterations = 1
|
|
360
|
+
|
|
361
|
+
evolution_plan: List[List[List[str]]] = []
|
|
362
|
+
for _ in prompts:
|
|
363
|
+
prompt_plan = []
|
|
364
|
+
for iteration in range(num_iterations):
|
|
365
|
+
if isinstance(evolution_spec, list):
|
|
366
|
+
if iteration < len(evolution_spec):
|
|
367
|
+
raw_spec = evolution_spec[iteration]
|
|
368
|
+
else:
|
|
369
|
+
raw_spec = evolution_spec[-1]
|
|
370
|
+
else:
|
|
371
|
+
raw_spec = evolution_spec
|
|
372
|
+
prompt_plan.append(
|
|
373
|
+
self._get_evolution_methods(raw_spec, num_generations)
|
|
374
|
+
)
|
|
375
|
+
evolution_plan.append(prompt_plan)
|
|
376
|
+
|
|
377
|
+
def _process_prompt(
|
|
378
|
+
args: Tuple[str, List[List[str]]],
|
|
379
|
+
) -> Dict[int, List[Dict[str, Any]]]:
|
|
380
|
+
prompt, methods = args
|
|
381
|
+
retries = 0
|
|
382
|
+
while retries <= retry_limit:
|
|
383
|
+
try:
|
|
384
|
+
return self._generate_iterative_evolutions(
|
|
385
|
+
prompt=prompt,
|
|
386
|
+
evolution_spec=evolution_spec,
|
|
387
|
+
num_generations=num_generations,
|
|
388
|
+
num_iterations=num_iterations,
|
|
389
|
+
keep_original=keep_original,
|
|
390
|
+
scorer=scorer,
|
|
391
|
+
num_threads=num_threads,
|
|
392
|
+
)
|
|
393
|
+
except Exception as e:
|
|
394
|
+
retries += 1
|
|
395
|
+
if retries <= retry_limit:
|
|
396
|
+
logger.warning(
|
|
397
|
+
f"Error processing prompt "
|
|
398
|
+
f"(attempt {retries}/{retry_limit}): {e!s}"
|
|
399
|
+
)
|
|
400
|
+
time.sleep(retry_delay)
|
|
401
|
+
else:
|
|
402
|
+
logger.error("Failed to process prompt.")
|
|
403
|
+
return {}
|
|
404
|
+
|
|
405
|
+
raise RuntimeError("_process_prompt() did not return.")
|
|
406
|
+
|
|
407
|
+
num_chunks = max(1, min(num_chunks, len(prompts)))
|
|
408
|
+
chunk_size = ceil(len(prompts) / num_chunks)
|
|
409
|
+
results = []
|
|
410
|
+
|
|
411
|
+
for chunk_idx in range(0, len(prompts), chunk_size):
|
|
412
|
+
chunk = prompts[chunk_idx : chunk_idx + chunk_size]
|
|
413
|
+
plan_chunk = evolution_plan[chunk_idx : chunk_idx + chunk_size]
|
|
414
|
+
|
|
415
|
+
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
|
416
|
+
chunk_results = list(
|
|
417
|
+
tqdm(
|
|
418
|
+
executor.map(_process_prompt, zip(chunk, plan_chunk)),
|
|
419
|
+
total=len(chunk),
|
|
420
|
+
)
|
|
421
|
+
)
|
|
422
|
+
results.extend(chunk_results)
|
|
423
|
+
|
|
424
|
+
return results
|