Wendong-Fan commited on
Commit
3808745
·
1 Parent(s): 4ddd969

update wendong

Browse files
owl/.env_template CHANGED
@@ -1,6 +1,9 @@
1
- # MODEL & API (See https://docs.camel-ai.org/key_modules/models.html#)
 
 
 
2
 
3
- # OPENAI API
4
  # OPENAI_API_KEY= ""
5
  # OPENAI_API_BASE_URL=""
6
 
@@ -15,15 +18,12 @@
15
  #===========================================
16
 
17
  # Google Search API (https://developers.google.com/custom-search/v1/overview)
18
- GOOGLE_API_KEY=""
19
- SEARCH_ENGINE_ID=""
20
-
21
- # Hugging Face API (https://huggingface.co/join)
22
- HF_TOKEN=""
23
 
24
  # Chunkr API (https://chunkr.ai/)
25
- CHUNKR_API_KEY=""
26
 
27
  # Firecrawl API (https://www.firecrawl.dev/)
28
- FIRECRAWL_API_KEY=""
29
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
 
1
+ #===========================================
2
+ # MODEL & API
3
+ # (See https://docs.camel-ai.org/key_modules/models.html#)
4
+ #===========================================
5
 
6
+ # OPENAI API (https://platform.openai.com/api-keys)
7
  # OPENAI_API_KEY= ""
8
  # OPENAI_API_BASE_URL=""
9
 
 
18
  #===========================================
19
 
20
  # Google Search API (https://developers.google.com/custom-search/v1/overview)
21
+ # GOOGLE_API_KEY=""
22
+ # SEARCH_ENGINE_ID=""
 
 
 
23
 
24
  # Chunkr API (https://chunkr.ai/)
25
+ # CHUNKR_API_KEY=""
26
 
27
  # Firecrawl API (https://www.firecrawl.dev/)
28
+ #FIRECRAWL_API_KEY=""
29
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
owl/examples/run.py CHANGED
@@ -25,22 +25,23 @@ from camel.toolkits import (
25
  )
26
  from camel.types import ModelPlatformType, ModelType
27
  from camel.logger import set_log_level
 
28
 
29
- from owl.utils import OwlRolePlaying, run_society, DocumentProcessingToolkit
30
 
31
  load_dotenv()
32
 
33
  set_log_level(level="DEBUG")
34
 
35
 
36
- def construct_society(question: str) -> OwlRolePlaying:
37
  r"""Construct a society of agents based on the given question.
38
 
39
  Args:
40
  question (str): The task or question to be addressed by the society.
41
 
42
  Returns:
43
- OwlRolePlaying: A configured society of agents ready to address the question.
44
  """
45
 
46
  # Create models for different components
@@ -112,7 +113,7 @@ def construct_society(question: str) -> OwlRolePlaying:
112
  }
113
 
114
  # Create and return the society
115
- society = OwlRolePlaying(
116
  **task_kwargs,
117
  user_role_name="user",
118
  user_agent_kwargs=user_agent_kwargs,
 
25
  )
26
  from camel.types import ModelPlatformType, ModelType
27
  from camel.logger import set_log_level
28
+ from camel.societies import RolePlaying
29
 
30
+ from owl.utils import run_society, DocumentProcessingToolkit
31
 
32
  load_dotenv()
33
 
34
  set_log_level(level="DEBUG")
35
 
36
 
37
+ def construct_society(question: str) -> RolePlaying:
38
  r"""Construct a society of agents based on the given question.
39
 
40
  Args:
41
  question (str): The task or question to be addressed by the society.
42
 
43
  Returns:
44
+ RolePlaying: A configured society of agents ready to address the question.
45
  """
46
 
47
  # Create models for different components
 
113
  }
114
 
115
  # Create and return the society
116
+ society = RolePlaying(
117
  **task_kwargs,
118
  user_role_name="user",
119
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_deepseek_zh.py CHANGED
@@ -31,7 +31,9 @@ from camel.toolkits import (
31
  from camel.types import ModelPlatformType, ModelType
32
 
33
 
34
- from owl.utils import OwlRolePlaying, run_society, DocumentProcessingToolkit
 
 
35
 
36
  from camel.logger import set_log_level
37
 
@@ -40,14 +42,14 @@ set_log_level(level="DEBUG")
40
  load_dotenv()
41
 
42
 
43
- def construct_society(question: str) -> OwlRolePlaying:
44
  r"""Construct a society of agents based on the given question.
45
 
46
  Args:
47
  question (str): The task or question to be addressed by the society.
48
 
49
  Returns:
50
- OwlRolePlaying: A configured society of agents ready to address the question.
51
  """
52
 
53
  # Create models for different components
@@ -84,7 +86,7 @@ def construct_society(question: str) -> OwlRolePlaying:
84
  }
85
 
86
  # Create and return the society
87
- society = OwlRolePlaying(
88
  **task_kwargs,
89
  user_role_name="user",
90
  user_agent_kwargs=user_agent_kwargs,
@@ -99,9 +101,7 @@ def construct_society(question: str) -> OwlRolePlaying:
99
  def main():
100
  r"""Main function to run the OWL system with an example question."""
101
  # Example research question
102
- question = (
103
- "搜索OWL项目最近的新闻并生成一篇报告,最后保存到本地。"
104
- )
105
 
106
  # Construct and run the society
107
  society = construct_society(question)
 
31
  from camel.types import ModelPlatformType, ModelType
32
 
33
 
34
+ from owl.utils import run_society
35
+
36
+ from camel.societies import RolePlaying
37
 
38
  from camel.logger import set_log_level
39
 
 
42
  load_dotenv()
43
 
44
 
45
+ def construct_society(question: str) -> RolePlaying:
46
  r"""Construct a society of agents based on the given question.
47
 
48
  Args:
49
  question (str): The task or question to be addressed by the society.
50
 
51
  Returns:
52
+ RolePlaying: A configured society of agents ready to address the question.
53
  """
54
 
55
  # Create models for different components
 
86
  }
87
 
88
  # Create and return the society
89
+ society = RolePlaying(
90
  **task_kwargs,
91
  user_role_name="user",
92
  user_agent_kwargs=user_agent_kwargs,
 
101
  def main():
102
  r"""Main function to run the OWL system with an example question."""
103
  # Example research question
104
+ question = "搜索OWL项目最近的新闻并生成一篇报告,最后保存到本地。"
 
 
105
 
106
  # Construct and run the society
107
  society = construct_society(question)
owl/examples/run_mini.py CHANGED
@@ -22,20 +22,22 @@ from camel.toolkits import (
22
  from camel.types import ModelPlatformType, ModelType
23
  from camel.logger import set_log_level
24
 
25
- from owl.utils import OwlRolePlaying, run_society
 
 
26
 
27
  load_dotenv()
28
  set_log_level(level="DEBUG")
29
 
30
 
31
- def construct_society(question: str) -> OwlRolePlaying:
32
  r"""Construct a society of agents based on the given question.
33
 
34
  Args:
35
  question (str): The task or question to be addressed by the society.
36
 
37
  Returns:
38
- OwlRolePlaying: A configured society of agents ready to address the
39
  question.
40
  """
41
 
@@ -86,7 +88,7 @@ def construct_society(question: str) -> OwlRolePlaying:
86
  }
87
 
88
  # Create and return the society
89
- society = OwlRolePlaying(
90
  **task_kwargs,
91
  user_role_name="user",
92
  user_agent_kwargs=user_agent_kwargs,
 
22
  from camel.types import ModelPlatformType, ModelType
23
  from camel.logger import set_log_level
24
 
25
+ from owl.utils import run_society
26
+
27
+ from camel.societies import RolePlaying
28
 
29
  load_dotenv()
30
  set_log_level(level="DEBUG")
31
 
32
 
33
+ def construct_society(question: str) -> RolePlaying:
34
  r"""Construct a society of agents based on the given question.
35
 
36
  Args:
37
  question (str): The task or question to be addressed by the society.
38
 
39
  Returns:
40
+ RolePlaying: A configured society of agents ready to address the
41
  question.
42
  """
43
 
 
88
  }
89
 
90
  # Create and return the society
91
+ society = RolePlaying(
92
  **task_kwargs,
93
  user_role_name="user",
94
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_ollama.py CHANGED
@@ -25,7 +25,9 @@ from camel.toolkits import (
25
  )
26
  from camel.types import ModelPlatformType
27
 
28
- from owl.utils import OwlRolePlaying, run_society
 
 
29
 
30
  from camel.logger import set_log_level
31
 
@@ -34,14 +36,14 @@ set_log_level(level="DEBUG")
34
  load_dotenv()
35
 
36
 
37
- def construct_society(question: str) -> OwlRolePlaying:
38
  r"""Construct a society of agents based on the given question.
39
 
40
  Args:
41
  question (str): The task or question to be addressed by the society.
42
 
43
  Returns:
44
- OwlRolePlaying: A configured society of agents ready to address the question.
45
  """
46
 
47
  # Create models for different components
@@ -105,7 +107,7 @@ def construct_society(question: str) -> OwlRolePlaying:
105
  }
106
 
107
  # Create and return the society
108
- society = OwlRolePlaying(
109
  **task_kwargs,
110
  user_role_name="user",
111
  user_agent_kwargs=user_agent_kwargs,
 
25
  )
26
  from camel.types import ModelPlatformType
27
 
28
+ from owl.utils import run_society
29
+
30
+ from camel.societies import RolePlaying
31
 
32
  from camel.logger import set_log_level
33
 
 
36
  load_dotenv()
37
 
38
 
39
+ def construct_society(question: str) -> RolePlaying:
40
  r"""Construct a society of agents based on the given question.
41
 
42
  Args:
43
  question (str): The task or question to be addressed by the society.
44
 
45
  Returns:
46
+ RolePlaying: A configured society of agents ready to address the question.
47
  """
48
 
49
  # Create models for different components
 
107
  }
108
 
109
  # Create and return the society
110
+ society = RolePlaying(
111
  **task_kwargs,
112
  user_role_name="user",
113
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_openai_compatiable_model.py CHANGED
@@ -25,8 +25,8 @@ from camel.toolkits import (
25
  )
26
  from camel.types import ModelPlatformType
27
 
28
- from owl.utils import OwlRolePlaying, run_society
29
-
30
  from camel.logger import set_log_level
31
 
32
  set_log_level(level="DEBUG")
@@ -34,14 +34,14 @@ set_log_level(level="DEBUG")
34
  load_dotenv()
35
 
36
 
37
- def construct_society(question: str) -> OwlRolePlaying:
38
  r"""Construct a society of agents based on the given question.
39
 
40
  Args:
41
  question (str): The task or question to be addressed by the society.
42
 
43
  Returns:
44
- OwlRolePlaying: A configured society of agents ready to address the question.
45
  """
46
 
47
  # Create models for different components
@@ -110,7 +110,7 @@ def construct_society(question: str) -> OwlRolePlaying:
110
  }
111
 
112
  # Create and return the society
113
- society = OwlRolePlaying(
114
  **task_kwargs,
115
  user_role_name="user",
116
  user_agent_kwargs=user_agent_kwargs,
 
25
  )
26
  from camel.types import ModelPlatformType
27
 
28
+ from owl.utils import run_society
29
+ from camel.societies import RolePlaying
30
  from camel.logger import set_log_level
31
 
32
  set_log_level(level="DEBUG")
 
34
  load_dotenv()
35
 
36
 
37
+ def construct_society(question: str) -> RolePlaying:
38
  r"""Construct a society of agents based on the given question.
39
 
40
  Args:
41
  question (str): The task or question to be addressed by the society.
42
 
43
  Returns:
44
+ RolePlaying: A configured society of agents ready to address the question.
45
  """
46
 
47
  # Create models for different components
 
110
  }
111
 
112
  # Create and return the society
113
+ society = RolePlaying(
114
  **task_kwargs,
115
  user_role_name="user",
116
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_qwen_mini_zh.py CHANGED
@@ -22,7 +22,9 @@ from camel.models import ModelFactory
22
  from camel.toolkits import BrowserToolkit, SearchToolkit, FileWriteToolkit
23
  from camel.types import ModelPlatformType, ModelType
24
 
25
- from owl.utils import OwlRolePlaying, run_society
 
 
26
 
27
  from camel.logger import set_log_level
28
 
@@ -31,7 +33,7 @@ set_log_level(level="DEBUG")
31
  load_dotenv()
32
 
33
 
34
- def construct_society(question: str) -> OwlRolePlaying:
35
  r"""Construct the society based on the question."""
36
 
37
  user_role_name = "user"
@@ -82,7 +84,7 @@ def construct_society(question: str) -> OwlRolePlaying:
82
  "with_task_specify": False,
83
  }
84
 
85
- society = OwlRolePlaying(
86
  **task_kwargs,
87
  user_role_name=user_role_name,
88
  user_agent_kwargs=user_agent_kwargs,
 
22
  from camel.toolkits import BrowserToolkit, SearchToolkit, FileWriteToolkit
23
  from camel.types import ModelPlatformType, ModelType
24
 
25
+ from owl.utils import run_society
26
+
27
+ from camel.societies import RolePlaying
28
 
29
  from camel.logger import set_log_level
30
 
 
33
  load_dotenv()
34
 
35
 
36
+ def construct_society(question: str) -> RolePlaying:
37
  r"""Construct the society based on the question."""
38
 
39
  user_role_name = "user"
 
84
  "with_task_specify": False,
85
  }
86
 
87
+ society = RolePlaying(
88
  **task_kwargs,
89
  user_role_name=user_role_name,
90
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_qwen_zh.py CHANGED
@@ -28,8 +28,9 @@ from camel.toolkits import (
28
  FileWriteToolkit,
29
  )
30
  from camel.types import ModelPlatformType, ModelType
 
31
 
32
- from owl.utils import OwlRolePlaying, run_society, DocumentProcessingToolkit
33
 
34
  from camel.logger import set_log_level
35
 
@@ -38,7 +39,7 @@ set_log_level(level="DEBUG")
38
  load_dotenv()
39
 
40
 
41
- def construct_society(question: str) -> OwlRolePlaying:
42
  """
43
  Construct a society of agents based on the given question.
44
 
@@ -46,7 +47,7 @@ def construct_society(question: str) -> OwlRolePlaying:
46
  question (str): The task or question to be addressed by the society.
47
 
48
  Returns:
49
- OwlRolePlaying: A configured society of agents ready to address the question.
50
  """
51
 
52
  # Create models for different components
@@ -118,7 +119,7 @@ def construct_society(question: str) -> OwlRolePlaying:
118
  }
119
 
120
  # Create and return the society
121
- society = OwlRolePlaying(
122
  **task_kwargs,
123
  user_role_name="user",
124
  user_agent_kwargs=user_agent_kwargs,
 
28
  FileWriteToolkit,
29
  )
30
  from camel.types import ModelPlatformType, ModelType
31
+ from camel.societies import RolePlaying
32
 
33
+ from owl.utils import run_society, DocumentProcessingToolkit
34
 
35
  from camel.logger import set_log_level
36
 
 
39
  load_dotenv()
40
 
41
 
42
+ def construct_society(question: str) -> RolePlaying:
43
  """
44
  Construct a society of agents based on the given question.
45
 
 
47
  question (str): The task or question to be addressed by the society.
48
 
49
  Returns:
50
+ RolePlaying: A configured society of agents ready to address the question.
51
  """
52
 
53
  # Create models for different components
 
119
  }
120
 
121
  # Create and return the society
122
+ society = RolePlaying(
123
  **task_kwargs,
124
  user_role_name="user",
125
  user_agent_kwargs=user_agent_kwargs,
owl/examples/run_terminal.py CHANGED
@@ -18,26 +18,28 @@ from camel.toolkits import (
18
  SearchToolkit,
19
  BrowserToolkit,
20
  FileWriteToolkit,
21
- TerminalToolkit
22
  )
23
  from camel.types import ModelPlatformType, ModelType
24
  from camel.logger import set_log_level
25
 
26
- from owl.utils import OwlRolePlaying, run_society
 
27
 
28
  load_dotenv()
29
  set_log_level(level="DEBUG")
30
  # Get current script directory
31
  base_dir = os.path.dirname(os.path.abspath(__file__))
32
 
33
- def construct_society(question: str) -> OwlRolePlaying:
 
34
  r"""Construct a society of agents based on the given question.
35
 
36
  Args:
37
  question (str): The task or question to be addressed by the society.
38
 
39
  Returns:
40
- OwlRolePlaying: A configured society of agents ready to address the
41
  question.
42
  """
43
 
@@ -89,7 +91,7 @@ def construct_society(question: str) -> OwlRolePlaying:
89
  }
90
 
91
  # Create and return the society
92
- society = OwlRolePlaying(
93
  **task_kwargs,
94
  user_role_name="user",
95
  user_agent_kwargs=user_agent_kwargs,
@@ -113,7 +115,9 @@ def main():
113
  answer, chat_history, token_count = run_society(society)
114
 
115
  # Output the result
116
- print(f"\033[94mAnswer: {answer}\nChat History: {chat_history}\ntoken_count:{token_count}\033[0m")
 
 
117
 
118
 
119
  if __name__ == "__main__":
 
18
  SearchToolkit,
19
  BrowserToolkit,
20
  FileWriteToolkit,
21
+ TerminalToolkit,
22
  )
23
  from camel.types import ModelPlatformType, ModelType
24
  from camel.logger import set_log_level
25
 
26
+ from owl.utils import run_society
27
+ from camel.societies import RolePlaying
28
 
29
  load_dotenv()
30
  set_log_level(level="DEBUG")
31
  # Get current script directory
32
  base_dir = os.path.dirname(os.path.abspath(__file__))
33
 
34
+
35
+ def construct_society(question: str) -> RolePlaying:
36
  r"""Construct a society of agents based on the given question.
37
 
38
  Args:
39
  question (str): The task or question to be addressed by the society.
40
 
41
  Returns:
42
+ RolePlaying: A configured society of agents ready to address the
43
  question.
44
  """
45
 
 
91
  }
92
 
93
  # Create and return the society
94
+ society = RolePlaying(
95
  **task_kwargs,
96
  user_role_name="user",
97
  user_agent_kwargs=user_agent_kwargs,
 
115
  answer, chat_history, token_count = run_society(society)
116
 
117
  # Output the result
118
+ print(
119
+ f"\033[94mAnswer: {answer}\nChat History: {chat_history}\ntoken_count:{token_count}\033[0m"
120
+ )
121
 
122
 
123
  if __name__ == "__main__":
owl/examples/run_terminal_zh.py CHANGED
@@ -18,27 +18,31 @@ from camel.toolkits import (
18
  SearchToolkit,
19
  BrowserToolkit,
20
  FileWriteToolkit,
21
- TerminalToolkit
22
  )
23
  from camel.types import ModelPlatformType, ModelType
24
  from camel.logger import set_log_level
25
 
26
- from owl.utils import OwlRolePlaying, run_society
 
 
27
 
28
  load_dotenv()
29
  set_log_level(level="DEBUG")
30
- import os
 
31
  # Get current script directory
32
  base_dir = os.path.dirname(os.path.abspath(__file__))
33
 
34
- def construct_society(question: str) -> OwlRolePlaying:
 
35
  r"""Construct a society of agents based on the given question.
36
 
37
  Args:
38
  question (str): The task or question to be addressed by the society.
39
 
40
  Returns:
41
- OwlRolePlaying: A configured society of agents ready to address the
42
  question.
43
  """
44
 
@@ -90,7 +94,7 @@ def construct_society(question: str) -> OwlRolePlaying:
90
  }
91
 
92
  # Create and return the society
93
- society = OwlRolePlaying(
94
  **task_kwargs,
95
  user_role_name="user",
96
  user_agent_kwargs=user_agent_kwargs,
@@ -112,7 +116,9 @@ def main():
112
  answer, chat_history, token_count = run_society(society)
113
 
114
  # Output the result
115
- print(f"\033[94mAnswer: {answer}\nChat History: {chat_history}\ntoken_count:{token_count}\033[0m")
 
 
116
 
117
 
118
  if __name__ == "__main__":
 
18
  SearchToolkit,
19
  BrowserToolkit,
20
  FileWriteToolkit,
21
+ TerminalToolkit,
22
  )
23
  from camel.types import ModelPlatformType, ModelType
24
  from camel.logger import set_log_level
25
 
26
+ from owl.utils import run_society
27
+ from camel.societies import RolePlaying
28
+ import os
29
 
30
  load_dotenv()
31
  set_log_level(level="DEBUG")
32
+
33
+
34
  # Get current script directory
35
  base_dir = os.path.dirname(os.path.abspath(__file__))
36
 
37
+
38
+ def construct_society(question: str) -> RolePlaying:
39
  r"""Construct a society of agents based on the given question.
40
 
41
  Args:
42
  question (str): The task or question to be addressed by the society.
43
 
44
  Returns:
45
+ RolePlaying: A configured society of agents ready to address the
46
  question.
47
  """
48
 
 
94
  }
95
 
96
  # Create and return the society
97
+ society = RolePlaying(
98
  **task_kwargs,
99
  user_role_name="user",
100
  user_agent_kwargs=user_agent_kwargs,
 
116
  answer, chat_history, token_count = run_society(society)
117
 
118
  # Output the result
119
+ print(
120
+ f"\033[94mAnswer: {answer}\nChat History: {chat_history}\ntoken_count:{token_count}\033[0m"
121
+ )
122
 
123
 
124
  if __name__ == "__main__":
owl/nextwebapp.py CHANGED
@@ -1,14 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Import from the correct module path
2
  from owl.utils import run_society
3
  import os
4
  import gradio as gr
5
- import time
6
- import json
7
- from typing import Tuple, List, Dict, Any
8
  import importlib
9
  from dotenv import load_dotenv, set_key, find_dotenv, unset_key
10
 
11
- os.environ['PYTHONIOENCODING'] = 'utf-8'
12
 
13
  # Enhanced CSS with navigation bar and additional styling
14
  custom_css = """
@@ -250,14 +261,14 @@ button.primary:hover {
250
  # Dictionary containing module descriptions
251
  MODULE_DESCRIPTIONS = {
252
  "run": "默认模式:使用OpenAI模型的默认的智能体协作模式,适合大多数任务。",
253
- "run_mini":"使用使用OpenAI模型最小化配置处理任务",
254
- "run_deepseek_zh":"使用deepseek模型处理中文任务",
255
  "run_terminal_zh": "终端模式:可执行命令行操作,支持网络搜索、文件处理等功能。适合需要系统交互的任务,使用OpenAI模型",
256
- "run_gaia_roleplaying":"GAIA基准测试实现,用于评估Agent能力",
257
- "run_openai_compatiable_model":"使用openai兼容模型处理任务",
258
- "run_ollama":"使用本地ollama模型处理任务",
259
- "run_qwen_mini_zh":"使用qwen模型最小化配置处理任务",
260
- "run_qwen_zh":"使用qwen模型处理任务",
261
  }
262
 
263
  # 默认环境变量模板
@@ -292,12 +303,13 @@ FIRECRAWL_API_KEY=""
292
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
293
  """
294
 
 
295
  def format_chat_history(chat_history: List[Dict[str, str]]) -> List[List[str]]:
296
  """将聊天历史格式化为Gradio聊天组件可接受的格式
297
-
298
  Args:
299
  chat_history: 原始聊天历史
300
-
301
  Returns:
302
  List[List[str]]: 格式化后的聊天历史
303
  """
@@ -305,22 +317,23 @@ def format_chat_history(chat_history: List[Dict[str, str]]) -> List[List[str]]:
305
  for message in chat_history:
306
  user_msg = message.get("user", "")
307
  assistant_msg = message.get("assistant", "")
308
-
309
  if user_msg:
310
  formatted_history.append([user_msg, None])
311
  if assistant_msg and formatted_history:
312
  formatted_history[-1][1] = assistant_msg
313
  elif assistant_msg:
314
  formatted_history.append([None, assistant_msg])
315
-
316
  return formatted_history
317
 
 
318
  def validate_input(question: str) -> bool:
319
  """验证用户输入是否有效
320
-
321
  Args:
322
  question: 用户问题
323
-
324
  Returns:
325
  bool: 输入是否有效
326
  """
@@ -329,121 +342,111 @@ def validate_input(question: str) -> bool:
329
  return False
330
  return True
331
 
332
- def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], str, str]:
 
 
 
333
  """运行OWL系统并返回结果
334
-
335
  Args:
336
  question: 用户问题
337
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
338
-
339
  Returns:
340
  Tuple[...]: 回答、聊天历史、令牌计数、状态
341
  """
342
  # 验证输入
343
  if not validate_input(question):
344
- return (
345
- "请输入有效的问题",
346
- [],
347
- "0",
348
- "❌ 错误: 输入无效"
349
- )
350
-
351
  try:
352
  # 确保环境变量已加载
353
  load_dotenv(find_dotenv(), override=True)
354
  # 检查模块是否在MODULE_DESCRIPTIONS中
355
  if example_module not in MODULE_DESCRIPTIONS:
356
  return (
357
- f"所选模块 '{example_module}' 不受支持",
358
- [],
359
- "0",
360
- f"❌ 错误: 不支持的模块"
361
  )
362
-
363
  # 动态导入目标模块
364
  module_path = f"owl.examples.{example_module}"
365
  try:
366
  module = importlib.import_module(module_path)
367
  except ImportError as ie:
368
  return (
369
- f"无法导入模块: {module_path}",
370
- [],
371
- "0",
372
- f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}"
373
  )
374
  except Exception as e:
375
- return (
376
- f"导入模块时发生错误: {module_path}",
377
- [],
378
- "0",
379
- f"❌ 错误: {str(e)}"
380
- )
381
-
382
  # 检查是否包含construct_society函数
383
  if not hasattr(module, "construct_society"):
384
  return (
385
- f"模块 {module_path} 中未找到 construct_society 函数",
386
- [],
387
- "0",
388
- f"❌ 错误: 模块接口不兼容"
389
  )
390
-
391
  # 构建社会模拟
392
  try:
393
  society = module.construct_society(question)
394
  except Exception as e:
395
  return (
396
- f"构建社会模拟时发生错误: {str(e)}",
397
- [],
398
- "0",
399
- f"❌ 错误: 构建失败 - {str(e)}"
400
  )
401
-
402
  # 运行社会模拟
403
  try:
404
  answer, chat_history, token_info = run_society(society)
405
  except Exception as e:
406
  return (
407
- f"运行社会模拟时发生错误: {str(e)}",
408
- [],
409
- "0",
410
- f"❌ 错误: 运行失败 - {str(e)}"
411
  )
412
-
413
  # 格式化聊天历史
414
  try:
415
  formatted_chat_history = format_chat_history(chat_history)
416
- except Exception as e:
417
  # 如果格式化失败,返回空历史记录但继续处理
418
  formatted_chat_history = []
419
-
420
  # 安全地获取令牌计数
421
  if not isinstance(token_info, dict):
422
  token_info = {}
423
-
424
  completion_tokens = token_info.get("completion_token_count", 0)
425
  prompt_tokens = token_info.get("prompt_token_count", 0)
426
  total_tokens = completion_tokens + prompt_tokens
427
-
428
  return (
429
- answer,
430
- formatted_chat_history,
431
- f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
432
- "✅ 成功完成"
433
  )
434
-
435
  except Exception as e:
436
- return (
437
- f"发生错误: {str(e)}",
438
- [],
439
- "0",
440
- f"❌ 错误: {str(e)}"
441
- )
442
 
443
  def update_module_description(module_name: str) -> str:
444
  """返回所选模块的描述"""
445
  return MODULE_DESCRIPTIONS.get(module_name, "无可用描述")
446
 
 
447
  # 环境变量管理功能
448
  def init_env_file():
449
  """初始化.env文件如果不存在"""
@@ -454,11 +457,12 @@ def init_env_file():
454
  dotenv_path = find_dotenv()
455
  return dotenv_path
456
 
 
457
  def load_env_vars():
458
  """加载环境变量并返回字典格式"""
459
  dotenv_path = init_env_file()
460
  load_dotenv(dotenv_path, override=True)
461
-
462
  env_vars = {}
463
  with open(dotenv_path, "r") as f:
464
  for line in f:
@@ -466,79 +470,84 @@ def load_env_vars():
466
  if line and not line.startswith("#"):
467
  if "=" in line:
468
  key, value = line.split("=", 1)
469
- env_vars[key.strip()] = value.strip().strip('"\'')
470
-
471
  return env_vars
472
 
 
473
  def save_env_vars(env_vars):
474
  """保存环境变量到.env文件"""
475
  try:
476
  dotenv_path = init_env_file()
477
-
478
  # 保存每个环境变量
479
  for key, value in env_vars.items():
480
  if key and key.strip(): # 确保键不为空
481
  set_key(dotenv_path, key.strip(), value.strip())
482
-
483
  # 重新加载环境变量以确保生效
484
  load_dotenv(dotenv_path, override=True)
485
-
486
  return True, "环境变量已成功保存!"
487
  except Exception as e:
488
  return False, f"保存环境变量时出错: {str(e)}"
489
 
 
490
  def add_env_var(key, value):
491
  """添加或更新单个环境变量"""
492
  try:
493
  if not key or not key.strip():
494
  return False, "变量名不能为空"
495
-
496
  dotenv_path = init_env_file()
497
  set_key(dotenv_path, key.strip(), value.strip())
498
  load_dotenv(dotenv_path, override=True)
499
-
500
  return True, f"环境变量 {key} 已成功添加/更新!"
501
  except Exception as e:
502
  return False, f"添加环境变量时出错: {str(e)}"
503
 
 
504
  def delete_env_var(key):
505
  """删除环境变量"""
506
  try:
507
  if not key or not key.strip():
508
  return False, "变量名不能为空"
509
-
510
  dotenv_path = init_env_file()
511
  unset_key(dotenv_path, key.strip())
512
-
513
  # 从当前进程环境中也删除
514
  if key in os.environ:
515
  del os.environ[key]
516
-
517
  return True, f"环境变量 {key} 已成功删除!"
518
  except Exception as e:
519
  return False, f"删除环境变量时出错: {str(e)}"
520
 
 
521
  def mask_sensitive_value(key: str, value: str) -> str:
522
  """对敏感信息进行掩码处理
523
-
524
  Args:
525
  key: 环境变量名
526
  value: 环境变量值
527
-
528
  Returns:
529
  str: 处理后的值
530
  """
531
  # 定义需要掩码的敏感关键词
532
- sensitive_keywords = ['key', 'token', 'secret', 'password', 'api']
533
-
534
  # 检查是否包含敏感关键词(不区分大小写)
535
  is_sensitive = any(keyword in key.lower() for keyword in sensitive_keywords)
536
-
537
  if is_sensitive and value:
538
  # 如果是敏感信息且有值,则显示掩码
539
- return '*' * 8
540
  return value
541
 
 
542
  def update_env_table():
543
  """更新环境变量表格显示,对敏感信息进行掩码处理"""
544
  env_vars = load_env_vars()
@@ -546,6 +555,7 @@ def update_env_table():
546
  masked_env_vars = [[k, mask_sensitive_value(k, v)] for k, v in env_vars.items()]
547
  return masked_env_vars
548
 
 
549
  def create_ui():
550
  """创建增强版Gradio界面"""
551
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="blue")) as app:
@@ -569,7 +579,7 @@ def create_ui():
569
  <p>我们的愿景是彻底改变AI代理协作解决现实世界任务的方式。通过利用动态代理交互,OWL能够在多个领域实现更自然、高效和稳健的任务自动化。</p>
570
  </div>
571
  """)
572
-
573
  with gr.Row(elem_id="features"):
574
  gr.HTML("""
575
  <div class="features-section">
@@ -610,7 +620,7 @@ def create_ui():
610
  </div>
611
  </div>
612
  """)
613
-
614
  with gr.Row():
615
  with gr.Column(scale=2):
616
  question_input = gr.Textbox(
@@ -620,26 +630,28 @@ def create_ui():
620
  elem_id="question_input",
621
  show_copy_button=True,
622
  )
623
-
624
  # 增强版模块选择下拉菜单
625
  # 只包含MODULE_DESCRIPTIONS中定义的模块
626
  module_dropdown = gr.Dropdown(
627
  choices=list(MODULE_DESCRIPTIONS.keys()),
628
  value="run_terminal_zh",
629
  label="选择功能模块",
630
- interactive=True
631
  )
632
-
633
  # 模块描述文本框
634
  module_description = gr.Textbox(
635
  value=MODULE_DESCRIPTIONS["run_terminal_zh"],
636
  label="模块描述",
637
  interactive=False,
638
- elem_classes="module-info"
639
  )
640
-
641
- run_button = gr.Button("运行", variant="primary", elem_classes="primary")
642
-
 
 
643
  with gr.Column(scale=1):
644
  gr.Markdown("""
645
  ### 使用指南
@@ -651,127 +663,103 @@ def create_ui():
651
 
652
  > **高级提示**: 对于复杂任务,可以尝试指定具体步骤和预期结果
653
  """)
654
-
655
  status_output = gr.Textbox(label="状态", interactive=False)
656
-
657
  with gr.Tabs():
658
  with gr.TabItem("回答"):
659
  answer_output = gr.Textbox(
660
- label="回答",
661
- lines=10,
662
- elem_classes="answer-box"
663
  )
664
-
665
  with gr.TabItem("对话历史"):
666
  chat_output = gr.Chatbot(
667
- label="完整对话记录",
668
- elem_classes="chat-container",
669
- height=500
670
  )
671
-
672
-
673
-
674
  token_count_output = gr.Textbox(
675
- label="令牌计数",
676
- interactive=False,
677
- elem_classes="token-count"
678
  )
679
-
680
  # 示例问题
681
  examples = [
682
  "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
683
  "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者数量和最近的活跃度。",
684
  "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
685
  "写一个hello world的python文件,保存到本地",
686
-
687
  ]
688
-
689
- gr.Examples(
690
- examples=examples,
691
- inputs=question_input
692
- )
693
  # 新增: 环境变量管理选项卡
694
  with gr.TabItem("环境变量管理", id="env-settings"):
695
- gr.Markdown("""
696
  ## 环境变量管理
697
 
698
  在此处设置模型API密钥和其他服务凭证。这些信息将保存在本地的`.env`文件中,确保您的API密钥安全存储且不会上传到网络。
699
  """)
700
-
701
- # 环境变量表格
702
- env_table = gr.Dataframe(
703
- headers=["变量名", "值"],
704
- datatype=["str", "str"],
705
- row_count=10,
706
- col_count=(2, "fixed"),
707
- value=update_env_table,
708
- label="当前环境变量",
709
- interactive=False
710
- )
711
-
712
- with gr.Row():
713
- with gr.Column(scale=1):
714
- new_env_key = gr.Textbox(label="变量名", placeholder="例如: OPENAI_API_KEY")
715
- with gr.Column(scale=2):
716
- new_env_value = gr.Textbox(label="值", placeholder="输入API密钥或其他配置值")
717
-
718
- with gr.Row():
719
- add_env_button = gr.Button("添加/更新变量", variant="primary")
720
- refresh_button = gr.Button("刷新变量列表")
721
- delete_env_button = gr.Button("删除选定变量", variant="stop")
722
-
723
- env_status = gr.Textbox(label="状态", interactive=False)
724
-
725
- # 变量选择器(用于删除)
726
- env_var_to_delete = gr.Dropdown(
727
- choices=[],
728
- label="选择要删除的变量",
729
- interactive=True
730
- )
731
-
732
- # 更新变量选择器的选项
733
- def update_delete_dropdown():
734
- env_vars = load_env_vars()
735
- return gr.Dropdown.update(choices=list(env_vars.keys()))
736
-
737
- # 连接事件处理函数
738
- add_env_button.click(
739
- fn=lambda k, v: add_env_var(k, v),
740
- inputs=[new_env_key, new_env_value],
741
- outputs=[env_status]
742
- ).then(
743
- fn=update_env_table,
744
- outputs=[env_table]
745
- ).then(
746
- fn=update_delete_dropdown,
747
- outputs=[env_var_to_delete]
748
- ).then(
749
- fn=lambda: ("", ""), # 修改为返回两个空字符串的元组
750
- outputs=[new_env_key, new_env_value]
751
- )
752
-
753
- refresh_button.click(
754
- fn=update_env_table,
755
- outputs=[env_table]
756
- ).then(
757
- fn=update_delete_dropdown,
758
- outputs=[env_var_to_delete]
759
- )
760
-
761
- delete_env_button.click(
762
- fn=lambda k: delete_env_var(k),
763
- inputs=[env_var_to_delete],
764
- outputs=[env_status]
765
- ).then(
766
- fn=update_env_table,
767
- outputs=[env_table]
768
- ).then(
769
- fn=update_delete_dropdown,
770
- outputs=[env_var_to_delete]
771
- )
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
 
774
-
775
  gr.HTML("""
776
  <div class="footer" id="about">
777
  <h3>关于 OWL 多智能体协作系统</h3>
@@ -780,23 +768,24 @@ def create_ui():
780
  <p><a href="https://github.com/camel-ai/owl" target="_blank">GitHub</a></p>
781
  </div>
782
  """)
783
-
784
  # 设置事件处理
785
  run_button.click(
786
  fn=run_owl,
787
- inputs=[question_input, module_dropdown],
788
- outputs=[answer_output, chat_output, token_count_output, status_output]
789
  )
790
-
791
  # 模块选择更新描述
792
  module_dropdown.change(
793
  fn=update_module_description,
794
  inputs=module_dropdown,
795
- outputs=module_description
796
  )
797
-
798
  return app
799
 
 
800
  # 主函数
801
  def main():
802
  try:
@@ -807,7 +796,9 @@ def main():
807
  except Exception as e:
808
  print(f"启动应用程序时发生错误: {str(e)}")
809
  import traceback
 
810
  traceback.print_exc()
811
 
 
812
  if __name__ == "__main__":
813
- main()
 
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
  # Import from the correct module path
15
  from owl.utils import run_society
16
  import os
17
  import gradio as gr
18
+ from typing import Tuple, List, Dict
 
 
19
  import importlib
20
  from dotenv import load_dotenv, set_key, find_dotenv, unset_key
21
 
22
+ os.environ["PYTHONIOENCODING"] = "utf-8"
23
 
24
  # Enhanced CSS with navigation bar and additional styling
25
  custom_css = """
 
261
  # Dictionary containing module descriptions
262
  MODULE_DESCRIPTIONS = {
263
  "run": "默认模式:使用OpenAI模型的默认的智能体协作模式,适合大多数任务。",
264
+ "run_mini": "使用使用OpenAI模型最小化配置处理任务",
265
+ "run_deepseek_zh": "使用deepseek模型处理中文任务",
266
  "run_terminal_zh": "终端模式:可执行命令行操作,支持网络搜索、文件处理等功能。适合需要系统交互的任务,使用OpenAI模型",
267
+ "run_gaia_roleplaying": "GAIA基准测试实现,用于评估Agent能力",
268
+ "run_openai_compatiable_model": "使用openai兼容模型处理任务",
269
+ "run_ollama": "使用本地ollama模型处理任务",
270
+ "run_qwen_mini_zh": "使用qwen模型最小化配置处理任务",
271
+ "run_qwen_zh": "使用qwen模型处理任务",
272
  }
273
 
274
  # 默认环境变量模板
 
303
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
304
  """
305
 
306
+
307
  def format_chat_history(chat_history: List[Dict[str, str]]) -> List[List[str]]:
308
  """将聊天历史格式化为Gradio聊天组件可接受的格式
309
+
310
  Args:
311
  chat_history: 原始聊天历史
312
+
313
  Returns:
314
  List[List[str]]: 格式化后的聊天历史
315
  """
 
317
  for message in chat_history:
318
  user_msg = message.get("user", "")
319
  assistant_msg = message.get("assistant", "")
320
+
321
  if user_msg:
322
  formatted_history.append([user_msg, None])
323
  if assistant_msg and formatted_history:
324
  formatted_history[-1][1] = assistant_msg
325
  elif assistant_msg:
326
  formatted_history.append([None, assistant_msg])
327
+
328
  return formatted_history
329
 
330
+
331
  def validate_input(question: str) -> bool:
332
  """验证用户输入是否有效
333
+
334
  Args:
335
  question: 用户问题
336
+
337
  Returns:
338
  bool: 输入是否有效
339
  """
 
342
  return False
343
  return True
344
 
345
+
346
+ def run_owl(
347
+ question: str, example_module: str
348
+ ) -> Tuple[str, List[List[str]], str, str]:
349
  """运行OWL系统并返回结果
350
+
351
  Args:
352
  question: 用户问题
353
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
354
+
355
  Returns:
356
  Tuple[...]: 回答、聊天历史、令牌计数、状态
357
  """
358
  # 验证输入
359
  if not validate_input(question):
360
+ return ("请输入有效的问题", [], "0", "❌ 错误: 输入无效")
361
+
 
 
 
 
 
362
  try:
363
  # 确保环境变量已加载
364
  load_dotenv(find_dotenv(), override=True)
365
  # 检查模块是否在MODULE_DESCRIPTIONS中
366
  if example_module not in MODULE_DESCRIPTIONS:
367
  return (
368
+ f"所选模块 '{example_module}' 不受支持",
369
+ [],
370
+ "0",
371
+ "❌ 错误: 不支持的模块",
372
  )
373
+
374
  # 动态导入目标模块
375
  module_path = f"owl.examples.{example_module}"
376
  try:
377
  module = importlib.import_module(module_path)
378
  except ImportError as ie:
379
  return (
380
+ f"无法导入模块: {module_path}",
381
+ [],
382
+ "0",
383
+ f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}",
384
  )
385
  except Exception as e:
386
+ return (f"导入模块时发生错误: {module_path}", [], "0", f"❌ 错误: {str(e)}")
387
+
 
 
 
 
 
388
  # 检查是否包含construct_society函数
389
  if not hasattr(module, "construct_society"):
390
  return (
391
+ f"模块 {module_path} 中未找到 construct_society 函数",
392
+ [],
393
+ "0",
394
+ "❌ 错误: 模块接口不兼容",
395
  )
396
+
397
  # 构建社会模拟
398
  try:
399
  society = module.construct_society(question)
400
  except Exception as e:
401
  return (
402
+ f"构建社会模拟时发生错误: {str(e)}",
403
+ [],
404
+ "0",
405
+ f"❌ 错误: 构建失败 - {str(e)}",
406
  )
407
+
408
  # 运行社会模拟
409
  try:
410
  answer, chat_history, token_info = run_society(society)
411
  except Exception as e:
412
  return (
413
+ f"运行社会模拟时发生错误: {str(e)}",
414
+ [],
415
+ "0",
416
+ f"❌ 错误: 运行失败 - {str(e)}",
417
  )
418
+
419
  # 格式化聊天历史
420
  try:
421
  formatted_chat_history = format_chat_history(chat_history)
422
+ except Exception:
423
  # 如果格式化失败,返回空历史记录但继续处理
424
  formatted_chat_history = []
425
+
426
  # 安全地获取令牌计数
427
  if not isinstance(token_info, dict):
428
  token_info = {}
429
+
430
  completion_tokens = token_info.get("completion_token_count", 0)
431
  prompt_tokens = token_info.get("prompt_token_count", 0)
432
  total_tokens = completion_tokens + prompt_tokens
433
+
434
  return (
435
+ answer,
436
+ formatted_chat_history,
437
+ f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
438
+ "✅ 成功完成",
439
  )
440
+
441
  except Exception as e:
442
+ return (f"发生错误: {str(e)}", [], "0", f"❌ 错误: {str(e)}")
443
+
 
 
 
 
444
 
445
  def update_module_description(module_name: str) -> str:
446
  """返回所选模块的描述"""
447
  return MODULE_DESCRIPTIONS.get(module_name, "无可用描述")
448
 
449
+
450
  # 环境变量管理功能
451
  def init_env_file():
452
  """初始化.env文件如果不存在"""
 
457
  dotenv_path = find_dotenv()
458
  return dotenv_path
459
 
460
+
461
  def load_env_vars():
462
  """加载环境变量并返回字典格式"""
463
  dotenv_path = init_env_file()
464
  load_dotenv(dotenv_path, override=True)
465
+
466
  env_vars = {}
467
  with open(dotenv_path, "r") as f:
468
  for line in f:
 
470
  if line and not line.startswith("#"):
471
  if "=" in line:
472
  key, value = line.split("=", 1)
473
+ env_vars[key.strip()] = value.strip().strip("\"'")
474
+
475
  return env_vars
476
 
477
+
478
  def save_env_vars(env_vars):
479
  """保存环境变量到.env文件"""
480
  try:
481
  dotenv_path = init_env_file()
482
+
483
  # 保存每个环境变量
484
  for key, value in env_vars.items():
485
  if key and key.strip(): # 确保键不为空
486
  set_key(dotenv_path, key.strip(), value.strip())
487
+
488
  # 重新加载环境变量以确保生效
489
  load_dotenv(dotenv_path, override=True)
490
+
491
  return True, "环境变量已成功保存!"
492
  except Exception as e:
493
  return False, f"保存环境变量时出错: {str(e)}"
494
 
495
+
496
  def add_env_var(key, value):
497
  """添加或更新单个环境变量"""
498
  try:
499
  if not key or not key.strip():
500
  return False, "变量名不能为空"
501
+
502
  dotenv_path = init_env_file()
503
  set_key(dotenv_path, key.strip(), value.strip())
504
  load_dotenv(dotenv_path, override=True)
505
+
506
  return True, f"环境变量 {key} 已成功添加/更新!"
507
  except Exception as e:
508
  return False, f"添加环境变量时出错: {str(e)}"
509
 
510
+
511
  def delete_env_var(key):
512
  """删除环境变量"""
513
  try:
514
  if not key or not key.strip():
515
  return False, "变量名不能为空"
516
+
517
  dotenv_path = init_env_file()
518
  unset_key(dotenv_path, key.strip())
519
+
520
  # 从当前进程环境中也删除
521
  if key in os.environ:
522
  del os.environ[key]
523
+
524
  return True, f"环境变量 {key} 已成功删除!"
525
  except Exception as e:
526
  return False, f"删除环境变量时出错: {str(e)}"
527
 
528
+
529
  def mask_sensitive_value(key: str, value: str) -> str:
530
  """对敏感信息进行掩码处理
531
+
532
  Args:
533
  key: 环境变量名
534
  value: 环境变量值
535
+
536
  Returns:
537
  str: 处理后的值
538
  """
539
  # 定义需要掩码的敏感关键词
540
+ sensitive_keywords = ["key", "token", "secret", "password", "api"]
541
+
542
  # 检查是否包含敏感关键词(不区分大小写)
543
  is_sensitive = any(keyword in key.lower() for keyword in sensitive_keywords)
544
+
545
  if is_sensitive and value:
546
  # 如果是敏感信息且有值,则显示掩码
547
+ return "*" * 8
548
  return value
549
 
550
+
551
  def update_env_table():
552
  """更新环境变量表格显示,对敏感信息进行掩码处理"""
553
  env_vars = load_env_vars()
 
555
  masked_env_vars = [[k, mask_sensitive_value(k, v)] for k, v in env_vars.items()]
556
  return masked_env_vars
557
 
558
+
559
  def create_ui():
560
  """创建增强版Gradio界面"""
561
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft(primary_hue="blue")) as app:
 
579
  <p>我们的愿景是彻底改变AI代理协作解决现实世界任务的方式。通过利用动态代理交互,OWL能够在多个领域实现更自然、高效和稳健的任务自动化。</p>
580
  </div>
581
  """)
582
+
583
  with gr.Row(elem_id="features"):
584
  gr.HTML("""
585
  <div class="features-section">
 
620
  </div>
621
  </div>
622
  """)
623
+
624
  with gr.Row():
625
  with gr.Column(scale=2):
626
  question_input = gr.Textbox(
 
630
  elem_id="question_input",
631
  show_copy_button=True,
632
  )
633
+
634
  # 增强版模块选择下拉菜单
635
  # 只包含MODULE_DESCRIPTIONS中定义的模块
636
  module_dropdown = gr.Dropdown(
637
  choices=list(MODULE_DESCRIPTIONS.keys()),
638
  value="run_terminal_zh",
639
  label="选择功能模块",
640
+ interactive=True,
641
  )
642
+
643
  # 模块描述文本框
644
  module_description = gr.Textbox(
645
  value=MODULE_DESCRIPTIONS["run_terminal_zh"],
646
  label="模块描述",
647
  interactive=False,
648
+ elem_classes="module-info",
649
  )
650
+
651
+ run_button = gr.Button(
652
+ "运行", variant="primary", elem_classes="primary"
653
+ )
654
+
655
  with gr.Column(scale=1):
656
  gr.Markdown("""
657
  ### 使用指南
 
663
 
664
  > **高级提示**: 对于复杂任务,可以尝试指定具体步骤和预期结果
665
  """)
666
+
667
  status_output = gr.Textbox(label="状态", interactive=False)
668
+
669
  with gr.Tabs():
670
  with gr.TabItem("回答"):
671
  answer_output = gr.Textbox(
672
+ label="回答", lines=10, elem_classes="answer-box"
 
 
673
  )
674
+
675
  with gr.TabItem("对话历史"):
676
  chat_output = gr.Chatbot(
677
+ label="完整对话记录", elem_classes="chat-container", height=500
 
 
678
  )
679
+
 
 
680
  token_count_output = gr.Textbox(
681
+ label="令牌计数", interactive=False, elem_classes="token-count"
 
 
682
  )
683
+
684
  # 示例问题
685
  examples = [
686
  "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
687
  "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者数量和最近的活跃度。",
688
  "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
689
  "写一个hello world的python文件,保存到本地",
 
690
  ]
691
+
692
+ gr.Examples(examples=examples, inputs=question_input)
 
 
 
693
  # 新增: 环境变量管理选项卡
694
  with gr.TabItem("环境变量管理", id="env-settings"):
695
+ gr.Markdown("""
696
  ## 环境变量管理
697
 
698
  在此处设置模型API密钥和其他服务凭证。这些信息将保存在本地的`.env`文件中,确保您的API密钥安全存储且不会上传到网络。
699
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
 
701
+ # 环境变量表格
702
+ env_table = gr.Dataframe(
703
+ headers=["变量名", "值"],
704
+ datatype=["str", "str"],
705
+ row_count=10,
706
+ col_count=(2, "fixed"),
707
+ value=update_env_table,
708
+ label="当前环境变量",
709
+ interactive=False,
710
+ )
711
+
712
+ with gr.Row():
713
+ with gr.Column(scale=1):
714
+ new_env_key = gr.Textbox(
715
+ label="变量名", placeholder="例如: OPENAI_API_KEY"
716
+ )
717
+ with gr.Column(scale=2):
718
+ new_env_value = gr.Textbox(
719
+ label="值", placeholder="输入API密钥或其他配置值"
720
+ )
721
+
722
+ with gr.Row():
723
+ add_env_button = gr.Button("添加/更新变量", variant="primary")
724
+ refresh_button = gr.Button("刷新变量列表")
725
+ delete_env_button = gr.Button("删除选定变量", variant="stop")
726
+
727
+ env_status = gr.Textbox(label="状态", interactive=False)
728
+
729
+ # 变量选择器(用于删除)
730
+ env_var_to_delete = gr.Dropdown(
731
+ choices=[], label="选择要删除的变量", interactive=True
732
+ )
733
+
734
+ # 更新变量选择器的选项
735
+ def update_delete_dropdown():
736
+ env_vars = load_env_vars()
737
+ return gr.Dropdown.update(choices=list(env_vars.keys()))
738
+
739
+ # 连接事件处理函数
740
+ add_env_button.click(
741
+ fn=lambda k, v: add_env_var(k, v),
742
+ inputs=[new_env_key, new_env_value],
743
+ outputs=[env_status],
744
+ ).then(fn=update_env_table, outputs=[env_table]).then(
745
+ fn=update_delete_dropdown, outputs=[env_var_to_delete]
746
+ ).then(
747
+ fn=lambda: ("", ""), # 修改为返回两个空字符串的元组
748
+ outputs=[new_env_key, new_env_value],
749
+ )
750
+
751
+ refresh_button.click(fn=update_env_table, outputs=[env_table]).then(
752
+ fn=update_delete_dropdown, outputs=[env_var_to_delete]
753
+ )
754
+
755
+ delete_env_button.click(
756
+ fn=lambda k: delete_env_var(k),
757
+ inputs=[env_var_to_delete],
758
+ outputs=[env_status],
759
+ ).then(fn=update_env_table, outputs=[env_table]).then(
760
+ fn=update_delete_dropdown, outputs=[env_var_to_delete]
761
+ )
762
 
 
763
  gr.HTML("""
764
  <div class="footer" id="about">
765
  <h3>关于 OWL 多智能体协作系统</h3>
 
768
  <p><a href="https://github.com/camel-ai/owl" target="_blank">GitHub</a></p>
769
  </div>
770
  """)
771
+
772
  # 设置事件处理
773
  run_button.click(
774
  fn=run_owl,
775
+ inputs=[question_input, module_dropdown],
776
+ outputs=[answer_output, chat_output, token_count_output, status_output],
777
  )
778
+
779
  # 模块选择更新描述
780
  module_dropdown.change(
781
  fn=update_module_description,
782
  inputs=module_dropdown,
783
+ outputs=module_description,
784
  )
785
+
786
  return app
787
 
788
+
789
  # 主函数
790
  def main():
791
  try:
 
796
  except Exception as e:
797
  print(f"启动应用程序时发生错误: {str(e)}")
798
  import traceback
799
+
800
  traceback.print_exc()
801
 
802
+
803
  if __name__ == "__main__":
804
+ main()
owl/utils/enhanced_role_playing.py CHANGED
@@ -381,6 +381,12 @@ Now please give me instructions to solve over overall task step by step. If the
381
  """
382
  input_msg = society.init_chat(init_prompt)
383
  for _round in range(round_limit):
 
 
 
 
 
 
384
  assistant_response, user_response = society.step(input_msg)
385
  overall_completion_token_count += (
386
  assistant_response.info["usage"]["completion_tokens"]
@@ -408,10 +414,12 @@ Now please give me instructions to solve over overall task step by step. If the
408
  f"Round #{_round} assistant_response:\n {assistant_response.msgs[0].content}"
409
  )
410
 
 
411
  if (
412
  assistant_response.terminated
413
  or user_response.terminated
414
  or "TASK_DONE" in user_response.msg.content
 
415
  ):
416
  break
417
 
 
381
  """
382
  input_msg = society.init_chat(init_prompt)
383
  for _round in range(round_limit):
384
+ # Check if previous user response had TASK_DONE before getting next assistant response
385
+ if _round > 0 and (
386
+ "TASK_DONE" in input_msg.content or "任务已完成" in input_msg.content
387
+ ):
388
+ break
389
+
390
  assistant_response, user_response = society.step(input_msg)
391
  overall_completion_token_count += (
392
  assistant_response.info["usage"]["completion_tokens"]
 
414
  f"Round #{_round} assistant_response:\n {assistant_response.msgs[0].content}"
415
  )
416
 
417
+ # Check other termination conditions
418
  if (
419
  assistant_response.terminated
420
  or user_response.terminated
421
  or "TASK_DONE" in user_response.msg.content
422
+ or "任务已完成" in user_response.msg.content
423
  ):
424
  break
425
 
owl/webapp_zh.py CHANGED
@@ -1,3 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Import from the correct module path
2
  from owl.utils import run_society
3
  import os
@@ -6,19 +19,15 @@ import time
6
  import json
7
  import logging
8
  import datetime
9
- from typing import Tuple, List, Dict, Any
10
  import importlib
11
  from dotenv import load_dotenv, set_key, find_dotenv, unset_key
12
  import threading
13
  import queue
14
- import time
15
- import signal
16
- import sys
17
- import subprocess
18
- import platform
19
- import re
20
 
21
- os.environ['PYTHONIOENCODING'] = 'utf-8'
22
 
23
  # 配置日志系统
24
  def setup_logging():
@@ -26,94 +35,112 @@ def setup_logging():
26
  # 创建logs目录(如果不存在)
27
  logs_dir = os.path.join(os.path.dirname(__file__), "logs")
28
  os.makedirs(logs_dir, exist_ok=True)
29
-
30
  # 生成日志文件名(使用当前日期)
31
  current_date = datetime.datetime.now().strftime("%Y-%m-%d")
32
  log_file = os.path.join(logs_dir, f"gradio_log_{current_date}.txt")
33
-
34
  # 配置根日志记录器(捕获所有日志)
35
  root_logger = logging.getLogger()
36
-
37
  # 清除现有的处理器,避免重复日志
38
  for handler in root_logger.handlers[:]:
39
  root_logger.removeHandler(handler)
40
-
41
  root_logger.setLevel(logging.INFO)
42
-
43
  # 创建文件处理器
44
- file_handler = logging.FileHandler(log_file, encoding='utf-8', mode='a')
45
  file_handler.setLevel(logging.INFO)
46
-
47
  # 创建控制台处理器
48
  console_handler = logging.StreamHandler()
49
  console_handler.setLevel(logging.INFO)
50
-
51
  # 创建格式化器
52
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
 
53
  file_handler.setFormatter(formatter)
54
  console_handler.setFormatter(formatter)
55
-
56
  # 添加处理器到根日志记录器
57
  root_logger.addHandler(file_handler)
58
  root_logger.addHandler(console_handler)
59
-
60
  logging.info("日志系统已初始化,日志文件: %s", log_file)
61
  return log_file
62
 
 
63
  # 全局变量
64
  LOG_FILE = None
65
- LOG_QUEUE = queue.Queue()
66
- LOG_QUEUE2 = queue.Queue() # 对话记录的队列
67
  STOP_LOG_THREAD = threading.Event()
68
  CURRENT_PROCESS = None # 用于跟踪当前运行的进程
69
  STOP_REQUESTED = threading.Event() # 用于标记是否请求停止
70
 
 
71
  # 日志读取和更新函数
72
  def log_reader_thread(log_file):
73
  """后台线程,持续读取日志文件并将新行添加到队列中"""
74
  try:
75
- with open(log_file, 'r', encoding='utf-8') as f:
76
  # 移动到文件末尾
77
  f.seek(0, 2)
78
-
79
  while not STOP_LOG_THREAD.is_set():
80
  line = f.readline()
81
  if line:
82
- LOG_QUEUE.put(line)
83
- LOG_QUEUE2.put(line) # 同时添加到第二个队列
84
  else:
85
  # 没有新行,等待一小段时间
86
  time.sleep(0.1)
87
  except Exception as e:
88
  logging.error(f"日志读取线程出错: {str(e)}")
89
 
 
90
  def get_latest_logs(max_lines=100, queue_source=None):
91
  """从队列中获取最新的日志行,如果队列为空则直接从文件读取
92
-
93
  Args:
94
  max_lines: 最大返回行数
95
  queue_source: 指定使用哪个队列,默认为LOG_QUEUE
96
-
97
  Returns:
98
  str: 日志内容
99
  """
100
  logs = []
101
  log_queue = queue_source if queue_source else LOG_QUEUE
 
 
 
 
 
102
  try:
103
  # 尝试从队列中获取所有可用的日志行
104
- while not log_queue.empty() and len(logs) < max_lines:
105
- logs.append(log_queue.get_nowait())
 
 
106
  except queue.Empty:
107
  pass
108
-
 
 
 
109
  # 如果没有新日志或日志不足,尝试直接从文件读取最后几行
110
  if len(logs) < max_lines and LOG_FILE and os.path.exists(LOG_FILE):
111
  try:
112
- with open(LOG_FILE, 'r', encoding='utf-8') as f:
113
  all_lines = f.readlines()
114
  # 如果队列中已有一些日志,只读取剩余需要的行数
115
  remaining_lines = max_lines - len(logs)
116
- file_logs = all_lines[-remaining_lines:] if len(all_lines) > remaining_lines else all_lines
 
 
 
 
 
117
  # 将文件日志添加到队列日志之前
118
  logs = file_logs + logs
119
  except Exception as e:
@@ -121,51 +148,106 @@ def get_latest_logs(max_lines=100, queue_source=None):
121
  logging.error(error_msg)
122
  if not logs: # 只有在没有任何日志的情况下才添加错误消息
123
  logs = [error_msg]
124
-
125
  # 如果仍然没有日志,返回提示信息
126
  if not logs:
127
- return "暂无日志记录或日志系统未正确初始化。"
128
-
129
- # 格式化日志输出,确保每个日志条目有适当的换行和分隔
130
- formatted_logs = []
131
  for log in logs:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  # 移除开头和结尾的多余空白字符
133
  log = log.strip()
134
-
135
- # 处理包含JSON或代码片段的日志,确保它们有正确的换行和缩进
136
- if '"]"\n}' in log or '\n}\n\n' in log:
137
- # 替换不合理的换行为更清晰的格式
138
- log = log.replace('"]"\n}', '"]" }').replace('\n}\n\n', ' }\n')
139
-
140
- # 检测日期时间格式的开头,这通常表示一个新的日志条目
141
- # 例如:2025-03-14 18:49:31,008 - httpx - INFO
142
- if re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}', log):
143
- # 在新的日志条目前添加一个空行,使日志更易读
144
- formatted_logs.append('\n')
145
-
146
- # 确保每个日志条目以换行符结束
147
- if not log.endswith('\n'):
148
- log += '\n'
149
-
150
  formatted_logs.append(log)
151
-
152
- # 移除第一个可能的额外空行
153
- if formatted_logs and formatted_logs[0] == '\n':
154
- formatted_logs.pop(0)
155
-
156
  return "".join(formatted_logs)
157
 
 
158
  # Dictionary containing module descriptions
159
  MODULE_DESCRIPTIONS = {
160
  "run": "默认模式:使用OpenAI模型的默认的智能体协作模式,适合大多数任务。",
161
- "run_mini":"使用使用OpenAI模型最小化配置处理任务",
162
- "run_deepseek_zh":"使用deepseek模型处理中文任务",
163
  "run_terminal_zh": "终端模式:可执行命令行操作,支持网络搜索、文件处理等功能。适合需要系统交互的任务,使用OpenAI模型",
164
- "run_gaia_roleplaying":"GAIA基准测试实现,用于评估Agent能力",
165
- "run_openai_compatiable_model":"使用openai兼容模型处理任务",
166
- "run_ollama":"使用本地ollama模型处理任务",
167
- "run_qwen_mini_zh":"使用qwen模型最小化配置处理任务",
168
- "run_qwen_zh":"使用qwen模型处理任务",
169
  }
170
 
171
  # API帮助信息
@@ -173,43 +255,43 @@ API_HELP_INFO = {
173
  "OPENAI_API_KEY": {
174
  "name": "OpenAI API",
175
  "desc": "OpenAI API密钥,用于访问GPT系列模型",
176
- "url": "https://platform.openai.com/api-keys"
177
  },
178
  "QWEN_API_KEY": {
179
  "name": "通义千问 API",
180
  "desc": "阿里云通义千问API密钥",
181
- "url": "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key"
182
  },
183
  "DEEPSEEK_API_KEY": {
184
  "name": "DeepSeek API",
185
  "desc": "DeepSeek API密钥",
186
- "url": "https://platform.deepseek.com/api_keys"
187
  },
188
  "GOOGLE_API_KEY": {
189
  "name": "Google Search API",
190
  "desc": "Google自定义搜索API密钥",
191
- "url": "https://developers.google.com/custom-search/v1/overview"
192
  },
193
  "SEARCH_ENGINE_ID": {
194
  "name": "Google Search Engine ID",
195
  "desc": "Google自定义搜索引擎ID",
196
- "url": "https://developers.google.com/custom-search/v1/overview"
197
  },
198
  "HF_TOKEN": {
199
  "name": "Hugging Face API",
200
  "desc": "Hugging Face API令牌",
201
- "url": "https://huggingface.co/join"
202
  },
203
  "CHUNKR_API_KEY": {
204
  "name": "Chunkr API",
205
  "desc": "Chunkr API密钥",
206
- "url": "https://chunkr.ai/"
207
  },
208
  "FIRECRAWL_API_KEY": {
209
  "name": "Firecrawl API",
210
  "desc": "Firecrawl API密钥",
211
- "url": "https://www.firecrawl.dev/"
212
- }
213
  }
214
 
215
  # 默认环境变量模板
@@ -245,13 +327,12 @@ FIRECRAWL_API_KEY=""
245
  """
246
 
247
 
248
-
249
  def validate_input(question: str) -> bool:
250
  """验证用户输入是否有效
251
-
252
  Args:
253
  question: 用户问题
254
-
255
  Returns:
256
  bool: 输入是否有效
257
  """
@@ -260,41 +341,38 @@ def validate_input(question: str) -> bool:
260
  return False
261
  return True
262
 
 
263
  def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
264
  """运行OWL系统并返回结果
265
-
266
  Args:
267
  question: 用户问题
268
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
269
-
270
  Returns:
271
  Tuple[...]: 回答、令牌计数、状态
272
  """
273
  global CURRENT_PROCESS
274
-
275
  # 验证输入
276
  if not validate_input(question):
277
  logging.warning("用户提交了无效的输入")
278
- return (
279
- "请输入有效的问题",
280
- "0",
281
- "❌ 错误: 输入无效"
282
- )
283
-
284
  try:
285
  # 确保环境变量已加载
286
  load_dotenv(find_dotenv(), override=True)
287
  logging.info(f"处理问题: '{question}', 使用模块: {example_module}")
288
-
289
  # 检查模块是否在MODULE_DESCRIPTIONS中
290
  if example_module not in MODULE_DESCRIPTIONS:
291
  logging.error(f"用户选择了不支持的模块: {example_module}")
292
  return (
293
- f"所选模块 '{example_module}' 不受支持",
294
- "0",
295
- f"❌ 错误: 不支持的模块"
296
  )
297
-
298
  # 动态导入目标模块
299
  module_path = f"owl.examples.{example_module}"
300
  try:
@@ -303,41 +381,36 @@ def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
303
  except ImportError as ie:
304
  logging.error(f"无法导入模块 {module_path}: {str(ie)}")
305
  return (
306
- f"无法导入模块: {module_path}",
307
- "0",
308
- f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}"
309
  )
310
  except Exception as e:
311
  logging.error(f"导入模块 {module_path} 时发生错误: {str(e)}")
312
- return (
313
- f"导入模块时发生错误: {module_path}",
314
- "0",
315
- f"❌ 错误: {str(e)}"
316
- )
317
-
318
  # 检查是否包含construct_society函数
319
  if not hasattr(module, "construct_society"):
320
  logging.error(f"模块 {module_path} 中未找到 construct_society 函数")
321
  return (
322
- f"模块 {module_path} 中未找到 construct_society 函数",
323
- "0",
324
- f"❌ 错误: 模块接口不兼容"
325
  )
326
-
327
  # 构建社会模拟
328
  try:
329
  logging.info("正在构建社会模拟...")
330
  society = module.construct_society(question)
331
-
332
 
333
  except Exception as e:
334
  logging.error(f"构建社会模拟时发生错误: {str(e)}")
335
  return (
336
- f"构建社会模拟时发生错误: {str(e)}",
337
- "0",
338
- f"❌ 错误: 构建失败 - {str(e)}"
339
  )
340
-
341
  # 运行社会模拟
342
  try:
343
  logging.info("正在运行社会模拟...")
@@ -346,42 +419,45 @@ def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
346
  except Exception as e:
347
  logging.error(f"运行社会模拟时发生错误: {str(e)}")
348
  return (
349
- f"运行社会模拟时发生错误: {str(e)}",
350
- "0",
351
- f"❌ 错误: 运行失败 - {str(e)}"
352
  )
353
-
354
 
355
-
356
  # 安全地获取令牌计数
357
  if not isinstance(token_info, dict):
358
  token_info = {}
359
-
360
  completion_tokens = token_info.get("completion_token_count", 0)
361
  prompt_tokens = token_info.get("prompt_token_count", 0)
362
  total_tokens = completion_tokens + prompt_tokens
363
-
364
- logging.info(f"处理完成,令牌使用: 完成={completion_tokens}, 提示={prompt_tokens}, 总计={total_tokens}")
365
-
 
 
366
  return (
367
- answer,
368
- f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
369
- "✅ 成功完成"
370
  )
371
-
372
  except Exception as e:
373
  logging.error(f"处理问题时发生未捕获的错误: {str(e)}")
374
- return (
375
- f"发生错误: {str(e)}",
376
- "0",
377
- f"❌ 错误: {str(e)}"
378
- )
379
 
380
  def update_module_description(module_name: str) -> str:
381
  """返回所选模块的描述"""
382
  return MODULE_DESCRIPTIONS.get(module_name, "无可用描述")
383
 
 
384
  # 环境变量管理功能
 
 
 
 
 
385
  def init_env_file():
386
  """初始化.env文件如果不存在"""
387
  dotenv_path = find_dotenv()
@@ -391,116 +467,332 @@ def init_env_file():
391
  dotenv_path = find_dotenv()
392
  return dotenv_path
393
 
 
394
  def load_env_vars():
395
- """加载环境变量并返回字典格式"""
 
 
 
 
396
  dotenv_path = init_env_file()
397
  load_dotenv(dotenv_path, override=True)
398
-
399
- env_vars = {}
 
400
  with open(dotenv_path, "r") as f:
401
  for line in f:
402
  line = line.strip()
403
  if line and not line.startswith("#"):
404
  if "=" in line:
405
  key, value = line.split("=", 1)
406
- env_vars[key.strip()] = value.strip().strip('"\'')
407
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  return env_vars
409
 
 
410
  def save_env_vars(env_vars):
411
- """保存环境变量到.env文件"""
 
 
 
 
412
  try:
413
  dotenv_path = init_env_file()
414
-
415
  # 保存每个环境变量
416
- for key, value in env_vars.items():
417
  if key and key.strip(): # 确保键不为空
 
 
 
 
 
 
418
  set_key(dotenv_path, key.strip(), value.strip())
419
-
420
  # 重新加载环境变量以确保生效
421
  load_dotenv(dotenv_path, override=True)
422
-
423
  return True, "环境变量已成功保存!"
424
  except Exception as e:
425
  return False, f"保存环境变量时出错: {str(e)}"
426
 
427
- def add_env_var(key, value):
428
- """添加或更新单个环境变量"""
 
 
 
 
 
 
 
429
  try:
430
  if not key or not key.strip():
431
  return False, "变量名不能为空"
432
-
 
 
 
 
 
 
 
 
 
 
433
  dotenv_path = init_env_file()
434
- set_key(dotenv_path, key.strip(), value.strip())
435
  load_dotenv(dotenv_path, override=True)
436
-
437
  return True, f"环境变量 {key} 已成功添加/更新!"
438
  except Exception as e:
439
  return False, f"添加环境变量时出错: {str(e)}"
440
 
 
441
  def delete_env_var(key):
442
  """删除环境变量"""
443
  try:
444
  if not key or not key.strip():
445
  return False, "变量名不能为空"
446
-
 
 
 
447
  dotenv_path = init_env_file()
448
- unset_key(dotenv_path, key.strip())
449
-
 
 
 
 
450
  # 从当前进程环境中也删除
451
  if key in os.environ:
452
  del os.environ[key]
453
-
454
  return True, f"环境变量 {key} 已成功删除!"
455
  except Exception as e:
456
  return False, f"删除环境变量时出错: {str(e)}"
457
 
458
- def mask_sensitive_value(key: str, value: str) -> str:
459
- """对敏感信息进行掩码处理
460
-
 
461
  Args:
462
  key: 环境变量名
463
- value: 环境变量值
464
-
465
  Returns:
466
- str: 处理后的值
467
  """
468
- # 定义需要掩码的敏感关键词
469
- sensitive_keywords = ['key', 'token', 'secret', 'password', 'api']
470
-
471
- # 检查是否包含敏感关键词(不区分大小写)
472
- is_sensitive = any(keyword in key.lower() for keyword in sensitive_keywords)
473
-
474
- if is_sensitive and value:
475
- # 如果是敏感信息且有值,则显示掩码
476
- return '*' * 8
477
- return value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
 
479
  def update_env_table():
480
- """更新环境变量表格显示,对敏感信息进行掩码处理"""
481
  env_vars = load_env_vars()
482
- # 对敏感值进行掩码处理
483
- masked_env_vars = [[k, mask_sensitive_value(k, v)] for k, v in env_vars.items()]
484
- return masked_env_vars
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
 
486
  def create_ui():
487
  """创建增强版Gradio界面"""
488
-
489
- # 定义日志更新函数
490
- def update_logs():
491
- """获取最新日志并返回给前端显示"""
492
- return get_latest_logs(100)
493
-
494
  def update_logs2():
495
  """获取最新对话记录并返回给前端显示"""
496
- return get_latest_logs(100, LOG_QUEUE2)
497
-
498
  def clear_log_file():
499
  """清空日志文件内容"""
500
  try:
501
  if LOG_FILE and os.path.exists(LOG_FILE):
502
  # 清空日志文件内容而不是删除文件
503
- open(LOG_FILE, 'w').close()
504
  logging.info("日志文件已清空")
505
  # 清空日志队列
506
  while not LOG_QUEUE.empty():
@@ -508,82 +800,85 @@ def create_ui():
508
  LOG_QUEUE.get_nowait()
509
  except queue.Empty:
510
  break
511
- # 清空第二个日志队列
512
- while not LOG_QUEUE2.empty():
513
- try:
514
- LOG_QUEUE2.get_nowait()
515
- except queue.Empty:
516
- break
517
- return "日志文件已清空"
518
  else:
519
- return "日志文件不存在或未设置"
520
  except Exception as e:
521
  logging.error(f"清空日志文件时出错: {str(e)}")
522
- return f"清空日志文件时出错: {str(e)}"
523
-
524
  # 创建一个实时日志更新函数
525
  def process_with_live_logs(question, module_name):
526
  """处理问题并实时更新日志"""
527
  global CURRENT_PROCESS
528
-
529
  # 创建一个后台线程来处理问题
530
  result_queue = queue.Queue()
531
-
532
  def process_in_background():
533
  try:
534
  result = run_owl(question, module_name)
535
  result_queue.put(result)
536
  except Exception as e:
537
  result_queue.put((f"发生错误: {str(e)}", "0", f"❌ 错误: {str(e)}"))
538
-
539
  # 启动后台处理线程
540
  bg_thread = threading.Thread(target=process_in_background)
541
  CURRENT_PROCESS = bg_thread # 记录当前进程
542
  bg_thread.start()
543
-
544
  # 在等待处理完成的同时,每秒更新一次日志
545
  while bg_thread.is_alive():
546
- # 更新日志显示
547
- logs = get_latest_logs(100)
548
- logs2 = get_latest_logs(100, LOG_QUEUE2)
549
-
550
  # 始终更新状态
551
- yield None, "0", "<span class='status-indicator status-running'></span> 处理中...", logs, logs2
552
-
 
 
 
 
553
  time.sleep(1)
554
-
555
  # 处理完成,获取结果
556
  if not result_queue.empty():
557
  result = result_queue.get()
558
  answer, token_count, status = result
559
-
560
- # 最后一次更新日志
561
- logs = get_latest_logs(100)
562
- logs2 = get_latest_logs(100, LOG_QUEUE2)
563
-
564
  # 根据状态设置不同的指示器
565
  if "错误" in status:
566
- status_with_indicator = f"<span class='status-indicator status-error'></span> {status}"
 
 
567
  else:
568
- status_with_indicator = f"<span class='status-indicator status-success'></span> {status}"
569
-
570
- yield answer, token_count, status_with_indicator, logs, logs2
 
 
571
  else:
572
- logs = get_latest_logs(100)
573
- logs2 = get_latest_logs(100, LOG_QUEUE2)
574
- yield "操作未完成", "0", "<span class='status-indicator status-error'></span> 已终止", logs, logs2
575
-
 
 
 
576
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
577
- gr.Markdown(
578
- """
579
  # 🦉 OWL 多智能体协作系统
580
 
581
  基于CAMEL框架开发的先进多智能体协作系统,旨在通过智能体协作解决复杂问题。
582
  """
583
- )
584
-
585
- # 添加自定义CSS
586
- gr.HTML("""
587
  <style>
588
  /* 聊天容器样式 */
589
  .chat-container .chatbot {
@@ -646,6 +941,130 @@ def create_ui():
646
  line-height: 1.4;
647
  }
648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
 
650
  @keyframes pulse {
651
  0% { opacity: 1; }
@@ -654,218 +1073,162 @@ def create_ui():
654
  }
655
  </style>
656
  """)
657
-
658
- with gr.Row():
659
- with gr.Column(scale=1):
660
- question_input = gr.Textbox(
661
- lines=5,
662
- placeholder="请输入您的问题...",
663
- label="问题",
664
- elem_id="question_input",
665
- show_copy_button=True,
666
- )
667
-
668
- # 增强版模块选择下拉菜单
669
- # 只包含MODULE_DESCRIPTIONS中定义的模块
670
- module_dropdown = gr.Dropdown(
671
- choices=list(MODULE_DESCRIPTIONS.keys()),
672
- value="run",
673
- label="选择功能模块",
674
- interactive=True
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  )
676
-
677
- # 模块描述文本框
678
- module_description = gr.Textbox(
679
- value=MODULE_DESCRIPTIONS["run"],
680
- label="模块描述",
 
 
 
 
 
 
 
 
 
 
 
681
  interactive=False,
682
- elem_classes="module-info"
 
 
 
 
683
  )
684
-
685
  with gr.Row():
686
- run_button = gr.Button("运行", variant="primary", elem_classes="primary")
687
-
688
- status_output = gr.HTML(
689
- value="<span class='status-indicator status-success'></span> 已就绪",
690
- label="状态"
691
- )
692
- token_count_output = gr.Textbox(
693
- label="令牌计数",
694
- interactive=False,
695
- elem_classes="token-count"
696
- )
697
-
698
-
699
-
700
- with gr.Tabs(): # 设置对话记录为默认选中的标签页
701
- with gr.TabItem("对话记录"):
702
- # 添加对话记录显示区域
703
- log_display2 = gr.Textbox(
704
- label="对话记录",
705
- lines=25,
706
- max_lines=100,
707
- interactive=False,
708
- autoscroll=True,
709
- show_copy_button=True,
710
- elem_classes="log-display",
711
- container=True
712
- )
713
-
714
- with gr.Row():
715
- refresh_logs_button2 = gr.Button("刷新记录")
716
- auto_refresh_checkbox2 = gr.Checkbox(
717
- label="自动刷新",
718
- value=True,
719
- interactive=True
720
- )
721
- clear_logs_button2 = gr.Button("清空记录", variant="secondary")
722
-
723
- with gr.TabItem("系统日志"):
724
- # 添加日志显示区域
725
- log_display = gr.Textbox(
726
- label="系统日志",
727
- lines=25,
728
- max_lines=100,
729
- interactive=False,
730
- autoscroll=True,
731
- show_copy_button=True,
732
- elem_classes="log-display",
733
- container=True
734
- )
735
-
736
- with gr.Row():
737
- refresh_logs_button = gr.Button("刷新日志")
738
- auto_refresh_checkbox = gr.Checkbox(
739
- label="自动刷新",
740
- value=True,
741
- interactive=True
742
- )
743
- clear_logs_button = gr.Button("清空日志", variant="secondary")
744
- with gr.TabItem("回答"):
745
- answer_output = gr.Textbox(
746
- label="回答",
747
- lines=10,
748
- elem_classes="answer-box"
749
  )
750
-
751
 
752
-
753
-
754
-
755
- with gr.TabItem("环境变量管理", id="env-settings"):
756
  gr.Markdown("""
757
- ## 环境变量管理
758
-
759
- 在此处设置模型API密钥和其他服务凭证。这些信息将保存在本地的`.env`文件中,确保您的API密钥安全存储且不会上传到网络。
760
- """)
761
-
762
- # 添加API密钥获取指南
763
- gr.Markdown("### API密钥获取指南")
764
-
765
- for key, info in API_HELP_INFO.items():
766
- with gr.Accordion(f"{info['name']} ({key})", open=False):
767
- gr.Markdown(f"""
768
- - **说明**: {info['desc']}
769
- - **获取地址**: [{info['url']}]({info['url']})
770
- """)
771
-
772
- gr.Markdown("---")
773
-
774
- # 环境变量表格
775
- env_table = gr.Dataframe(
776
- headers=["变量名", "值"],
777
- datatype=["str", "str"],
778
- row_count=10,
779
- col_count=(2, "fixed"),
780
- value=update_env_table,
781
- label="当前环境变量",
782
- interactive=False
783
- )
784
-
785
- with gr.Row():
786
- with gr.Column(scale=1):
787
- new_env_key = gr.Textbox(label="变量名", placeholder="例如: OPENAI_API_KEY")
788
- with gr.Column(scale=2):
789
- new_env_value = gr.Textbox(label="值", placeholder="输入API密钥或其他配置值")
790
-
791
- with gr.Row():
792
- add_env_button = gr.Button("添加/更新变量", variant="primary")
793
- refresh_button = gr.Button("刷新变量列表")
794
- delete_env_button = gr.Button("删除选定变量", variant="stop")
795
-
796
- env_status = gr.Textbox(label="状态", interactive=False)
797
-
798
- # 变量选择器(用于删除)
799
- env_var_to_delete = gr.Dropdown(
800
- choices=[],
801
- label="选择要删除的变量",
802
- interactive=True
803
- )
804
-
805
- # 更新变量选择器的选项
806
- def update_delete_dropdown():
807
- env_vars = load_env_vars()
808
- return gr.Dropdown.update(choices=list(env_vars.keys()))
809
-
810
- # 连接事件处理函数
811
- add_env_button.click(
812
- fn=lambda k, v: add_env_var(k, v),
813
- inputs=[new_env_key, new_env_value],
814
- outputs=[env_status]
815
- ).then(
816
- fn=update_env_table,
817
- outputs=[env_table]
818
- ).then(
819
- fn=update_delete_dropdown,
820
- outputs=[env_var_to_delete]
821
- ).then(
822
- fn=lambda: ("", ""), # 修改为返回两个空字符串的元组
823
- outputs=[new_env_key, new_env_value]
824
- )
825
-
826
- refresh_button.click(
827
- fn=update_env_table,
828
- outputs=[env_table]
829
- ).then(
830
- fn=update_delete_dropdown,
831
- outputs=[env_var_to_delete]
832
- )
833
-
834
- delete_env_button.click(
835
- fn=lambda k: delete_env_var(k),
836
- inputs=[env_var_to_delete],
837
- outputs=[env_status]
838
- ).then(
839
- fn=update_env_table,
840
- outputs=[env_table]
841
- ).then(
842
- fn=update_delete_dropdown,
843
- outputs=[env_var_to_delete]
844
- )
845
 
846
-
847
-
848
-
849
-
850
- # 示例问题
851
- examples = [
852
- "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
853
- "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者名称,把内容整理成一个markdown文件保存到本地",
854
- "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
855
- "写一个hello world的python文件,保存到本地",
856
-
857
- ]
858
-
859
- gr.Examples(
860
- examples=examples,
861
- inputs=question_input
862
- )
863
-
864
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
 
867
-
868
- gr.HTML("""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
  <div class="footer" id="about">
870
  <h3>关于 OWL 多智能体协作系统</h3>
871
  <p>OWL 是一个基于CAMEL框架开发的先进多智能体协作系统,旨在通过智能体协作解决复杂问题。</p>
@@ -873,78 +1236,46 @@ def create_ui():
873
  <p><a href="https://github.com/camel-ai/owl" target="_blank">GitHub</a></p>
874
  </div>
875
  """)
876
-
877
- # 设置事件处理
878
- run_button.click(
879
- fn=process_with_live_logs,
880
- inputs=[question_input, module_dropdown],
881
- outputs=[answer_output, token_count_output, status_output, log_display, log_display2]
882
- )
883
-
884
- # 模块选择更新描述
885
- module_dropdown.change(
886
- fn=update_module_description,
887
- inputs=module_dropdown,
888
- outputs=module_description
889
- )
890
-
891
- # 日志相关事件处理
892
- refresh_logs_button.click(
893
- fn=update_logs,
894
- outputs=[log_display]
895
- )
896
-
897
- refresh_logs_button2.click(
898
- fn=update_logs2,
899
- outputs=[log_display2]
900
- )
901
-
902
- clear_logs_button.click(
903
- fn=clear_log_file,
904
- outputs=[log_display]
905
- )
906
-
907
- clear_logs_button2.click(
908
- fn=clear_log_file,
909
- outputs=[log_display2]
910
- )
911
-
912
- # 自动刷新控制
913
- def toggle_auto_refresh(enabled):
914
- if enabled:
915
- return gr.update(every=3)
916
- else:
917
- return gr.update(every=0)
918
-
919
- auto_refresh_checkbox.change(
920
- fn=toggle_auto_refresh,
921
- inputs=[auto_refresh_checkbox],
922
- outputs=[log_display]
923
- )
924
-
925
- auto_refresh_checkbox2.change(
926
- fn=toggle_auto_refresh,
927
- inputs=[auto_refresh_checkbox2],
928
- outputs=[log_display2]
929
- )
930
-
931
- # 设置自动刷新(默认每3秒刷新一次)
932
- if auto_refresh_checkbox.value:
933
- app.load(
934
- fn=update_logs,
935
- outputs=[log_display],
936
- every=2
937
- )
938
-
939
- if auto_refresh_checkbox2.value:
940
- app.load(
941
- fn=update_logs2,
942
- outputs=[log_display2],
943
- every=2
944
- )
945
-
946
  return app
947
 
 
948
  # 主函数
949
  def main():
950
  try:
@@ -952,35 +1283,40 @@ def main():
952
  global LOG_FILE
953
  LOG_FILE = setup_logging()
954
  logging.info("OWL Web应用程序启动")
955
-
956
  # 启动日志读取线程
957
- log_thread = threading.Thread(target=log_reader_thread, args=(LOG_FILE,), daemon=True)
 
 
958
  log_thread.start()
959
  logging.info("日志读取线程已启动")
960
-
961
  # 初始化.env文件(如果不存在)
962
  init_env_file()
963
  app = create_ui()
964
-
965
  # 注册应用关闭时的清理函数
966
  def cleanup():
967
  global STOP_LOG_THREAD, STOP_REQUESTED
968
  STOP_LOG_THREAD.set()
969
  STOP_REQUESTED.set()
970
  logging.info("应用程序关闭,停止日志线程")
971
- app.queue()
972
- app.launch(share=False,server_name="127.0.0.1",server_port=7860)
 
973
  except Exception as e:
974
  logging.error(f"启动应用程序时发生错误: {str(e)}")
975
  print(f"启动应用程序时发生错误: {str(e)}")
976
  import traceback
 
977
  traceback.print_exc()
978
-
979
  finally:
980
  # 确保日志线程停止
981
  STOP_LOG_THREAD.set()
982
  STOP_REQUESTED.set()
983
  logging.info("应用程序关闭")
984
 
 
985
  if __name__ == "__main__":
986
- main()
 
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
  # Import from the correct module path
15
  from owl.utils import run_society
16
  import os
 
19
  import json
20
  import logging
21
  import datetime
22
+ from typing import Tuple
23
  import importlib
24
  from dotenv import load_dotenv, set_key, find_dotenv, unset_key
25
  import threading
26
  import queue
27
+ import re # For regular expression operations
28
+
29
+ os.environ["PYTHONIOENCODING"] = "utf-8"
 
 
 
30
 
 
31
 
32
  # 配置日志系统
33
  def setup_logging():
 
35
  # 创建logs目录(如果不存在)
36
  logs_dir = os.path.join(os.path.dirname(__file__), "logs")
37
  os.makedirs(logs_dir, exist_ok=True)
38
+
39
  # 生成日志文件名(使用当前日期)
40
  current_date = datetime.datetime.now().strftime("%Y-%m-%d")
41
  log_file = os.path.join(logs_dir, f"gradio_log_{current_date}.txt")
42
+
43
  # 配置根日志记录器(捕获所有日志)
44
  root_logger = logging.getLogger()
45
+
46
  # 清除现有的处理器,避免重复日志
47
  for handler in root_logger.handlers[:]:
48
  root_logger.removeHandler(handler)
49
+
50
  root_logger.setLevel(logging.INFO)
51
+
52
  # 创建文件处理器
53
+ file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a")
54
  file_handler.setLevel(logging.INFO)
55
+
56
  # 创建控制台处理器
57
  console_handler = logging.StreamHandler()
58
  console_handler.setLevel(logging.INFO)
59
+
60
  # 创建格式化器
61
+ formatter = logging.Formatter(
62
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
63
+ )
64
  file_handler.setFormatter(formatter)
65
  console_handler.setFormatter(formatter)
66
+
67
  # 添加处理器到根日志记录器
68
  root_logger.addHandler(file_handler)
69
  root_logger.addHandler(console_handler)
70
+
71
  logging.info("日志系统已初始化,日志文件: %s", log_file)
72
  return log_file
73
 
74
+
75
  # 全局变量
76
  LOG_FILE = None
77
+ LOG_QUEUE: queue.Queue = queue.Queue() # 日志队列
 
78
  STOP_LOG_THREAD = threading.Event()
79
  CURRENT_PROCESS = None # 用于跟踪当前运行的进程
80
  STOP_REQUESTED = threading.Event() # 用于标记是否请求停止
81
 
82
+
83
  # 日志读取和更新函数
84
  def log_reader_thread(log_file):
85
  """后台线程,持续读取日志文件并将新行添加到队列中"""
86
  try:
87
+ with open(log_file, "r", encoding="utf-8") as f:
88
  # 移动到文件末尾
89
  f.seek(0, 2)
90
+
91
  while not STOP_LOG_THREAD.is_set():
92
  line = f.readline()
93
  if line:
94
+ LOG_QUEUE.put(line) # 添加到对话记录队列
 
95
  else:
96
  # 没有新行,等待一小段时间
97
  time.sleep(0.1)
98
  except Exception as e:
99
  logging.error(f"日志读取线程出错: {str(e)}")
100
 
101
+
102
  def get_latest_logs(max_lines=100, queue_source=None):
103
  """从队列中获取最新的日志行,如果队列为空则直接从文件读取
104
+
105
  Args:
106
  max_lines: 最大返回行数
107
  queue_source: 指定使用哪个队列,默认为LOG_QUEUE
108
+
109
  Returns:
110
  str: 日志内容
111
  """
112
  logs = []
113
  log_queue = queue_source if queue_source else LOG_QUEUE
114
+
115
+ # 创建一个临时队列来存储日志,以便我们可以处理它们而不会从原始队列中删除它们
116
+ temp_queue = queue.Queue()
117
+ temp_logs = []
118
+
119
  try:
120
  # 尝试从队列中获取所有可用的日志行
121
+ while not log_queue.empty() and len(temp_logs) < max_lines:
122
+ log = log_queue.get_nowait()
123
+ temp_logs.append(log)
124
+ temp_queue.put(log) # 将日志放回临时队列
125
  except queue.Empty:
126
  pass
127
+
128
+ # 处理对话记录
129
+ logs = temp_logs
130
+
131
  # 如果没有新日志或日志不足,尝试直接从文件读取最后几行
132
  if len(logs) < max_lines and LOG_FILE and os.path.exists(LOG_FILE):
133
  try:
134
+ with open(LOG_FILE, "r", encoding="utf-8") as f:
135
  all_lines = f.readlines()
136
  # 如果队列中已有一些日志,只读取剩余需要的行数
137
  remaining_lines = max_lines - len(logs)
138
+ file_logs = (
139
+ all_lines[-remaining_lines:]
140
+ if len(all_lines) > remaining_lines
141
+ else all_lines
142
+ )
143
+
144
  # 将文件日志添加到队列日志之前
145
  logs = file_logs + logs
146
  except Exception as e:
 
148
  logging.error(error_msg)
149
  if not logs: # 只有在没有任何日志的情况下才添加错误消息
150
  logs = [error_msg]
151
+
152
  # 如果仍然没有日志,返回提示信息
153
  if not logs:
154
+ return "暂无对话记录。"
155
+
156
+ # 过滤日志,只保留 camel.agents.chat_agent - INFO 的日志
157
+ filtered_logs = []
158
  for log in logs:
159
+ if "camel.agents.chat_agent - INFO" in log:
160
+ filtered_logs.append(log)
161
+
162
+ # 如果过滤后没有日志,返回提示信息
163
+ if not filtered_logs:
164
+ return "暂无对话记录。"
165
+
166
+ # 处理日志内容,提取最新的用户和助手消息
167
+ simplified_logs = []
168
+
169
+ # 使用集合来跟踪已经处理过的消息,避免重复
170
+ processed_messages = set()
171
+
172
+ def process_message(role, content):
173
+ # 创建一个唯一标识符来跟踪消息
174
+ msg_id = f"{role}:{content}"
175
+ if msg_id in processed_messages:
176
+ return None
177
+
178
+ processed_messages.add(msg_id)
179
+ content = content.replace("\\n", "\n")
180
+ lines = [line.strip() for line in content.split("\n")]
181
+ content = "\n".join(lines)
182
+
183
+ return f"[{role.title()} Agent]: {content}"
184
+
185
+ for log in filtered_logs:
186
+ formatted_messages = []
187
+ # 尝试提取消息数组
188
+ messages_match = re.search(
189
+ r"Model (.*?), index (\d+), processed these messages: (\[.*\])", log
190
+ )
191
+
192
+ if messages_match:
193
+ try:
194
+ messages = json.loads(messages_match.group(3))
195
+ for msg in messages:
196
+ if msg.get("role") in ["user", "assistant"]:
197
+ formatted_msg = process_message(
198
+ msg.get("role"), msg.get("content", "")
199
+ )
200
+ if formatted_msg:
201
+ formatted_messages.append(formatted_msg)
202
+ except json.JSONDecodeError:
203
+ pass
204
+
205
+ # 如果JSON解析失败或没有找到消息数组,尝试直接提取对话内容
206
+ if not formatted_messages:
207
+ user_pattern = re.compile(r"\{'role': 'user', 'content': '(.*?)'\}")
208
+ assistant_pattern = re.compile(
209
+ r"\{'role': 'assistant', 'content': '(.*?)'\}"
210
+ )
211
+
212
+ for content in user_pattern.findall(log):
213
+ formatted_msg = process_message("user", content)
214
+ if formatted_msg:
215
+ formatted_messages.append(formatted_msg)
216
+
217
+ for content in assistant_pattern.findall(log):
218
+ formatted_msg = process_message("assistant", content)
219
+ if formatted_msg:
220
+ formatted_messages.append(formatted_msg)
221
+
222
+ if formatted_messages:
223
+ simplified_logs.append("\n\n".join(formatted_messages))
224
+
225
+ # 格式化日志输出,确保每个对话记录之间有适当的分隔
226
+ formatted_logs = []
227
+ for i, log in enumerate(simplified_logs):
228
  # 移除开头和结尾的多余空白字符
229
  log = log.strip()
230
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  formatted_logs.append(log)
232
+
233
+ # 确保每个对话记录以换行符结束
234
+ if not log.endswith("\n"):
235
+ formatted_logs.append("\n")
236
+
237
  return "".join(formatted_logs)
238
 
239
+
240
  # Dictionary containing module descriptions
241
  MODULE_DESCRIPTIONS = {
242
  "run": "默认模式:使用OpenAI模型的默认的智能体协作模式,适合大多数任务。",
243
+ "run_mini": "使用使用OpenAI模型最小化配置处理任务",
244
+ "run_deepseek_zh": "使用deepseek模型处理中文任务",
245
  "run_terminal_zh": "终端模式:可执行命令行操作,支持网络搜索、文件处理等功能。适合需要系统交互的任务,使用OpenAI模型",
246
+ "run_gaia_roleplaying": "GAIA基准测试实现,用于评估Agent能力",
247
+ "run_openai_compatiable_model": "使用openai兼容模型处理任务",
248
+ "run_ollama": "使用本地ollama模型处理任务",
249
+ "run_qwen_mini_zh": "使用qwen模型最小化配置处理任务",
250
+ "run_qwen_zh": "使用qwen模型处理任务",
251
  }
252
 
253
  # API帮助信息
 
255
  "OPENAI_API_KEY": {
256
  "name": "OpenAI API",
257
  "desc": "OpenAI API密钥,用于访问GPT系列模型",
258
+ "url": "https://platform.openai.com/api-keys",
259
  },
260
  "QWEN_API_KEY": {
261
  "name": "通义千问 API",
262
  "desc": "阿里云通义千问API密钥",
263
+ "url": "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key",
264
  },
265
  "DEEPSEEK_API_KEY": {
266
  "name": "DeepSeek API",
267
  "desc": "DeepSeek API密钥",
268
+ "url": "https://platform.deepseek.com/api_keys",
269
  },
270
  "GOOGLE_API_KEY": {
271
  "name": "Google Search API",
272
  "desc": "Google自定义搜索API密钥",
273
+ "url": "https://developers.google.com/custom-search/v1/overview",
274
  },
275
  "SEARCH_ENGINE_ID": {
276
  "name": "Google Search Engine ID",
277
  "desc": "Google自定义搜索引擎ID",
278
+ "url": "https://developers.google.com/custom-search/v1/overview",
279
  },
280
  "HF_TOKEN": {
281
  "name": "Hugging Face API",
282
  "desc": "Hugging Face API令牌",
283
+ "url": "https://huggingface.co/join",
284
  },
285
  "CHUNKR_API_KEY": {
286
  "name": "Chunkr API",
287
  "desc": "Chunkr API密钥",
288
+ "url": "https://chunkr.ai/",
289
  },
290
  "FIRECRAWL_API_KEY": {
291
  "name": "Firecrawl API",
292
  "desc": "Firecrawl API密钥",
293
+ "url": "https://www.firecrawl.dev/",
294
+ },
295
  }
296
 
297
  # 默认环境变量模板
 
327
  """
328
 
329
 
 
330
  def validate_input(question: str) -> bool:
331
  """验证用户输入是否有效
332
+
333
  Args:
334
  question: 用户问题
335
+
336
  Returns:
337
  bool: 输入是否有效
338
  """
 
341
  return False
342
  return True
343
 
344
+
345
  def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
346
  """运行OWL系统并返回结果
347
+
348
  Args:
349
  question: 用户问题
350
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
351
+
352
  Returns:
353
  Tuple[...]: 回答、令牌计数、状态
354
  """
355
  global CURRENT_PROCESS
356
+
357
  # 验证输入
358
  if not validate_input(question):
359
  logging.warning("用户提交了无效的输入")
360
+ return ("请输入有效的问题", "0", "❌ 错误: 输入无效")
361
+
 
 
 
 
362
  try:
363
  # 确保环境变量已加载
364
  load_dotenv(find_dotenv(), override=True)
365
  logging.info(f"处理问题: '{question}', 使用模块: {example_module}")
366
+
367
  # 检查模块是否在MODULE_DESCRIPTIONS中
368
  if example_module not in MODULE_DESCRIPTIONS:
369
  logging.error(f"用户选择了不支持的模块: {example_module}")
370
  return (
371
+ f"所选模块 '{example_module}' 不受支持",
372
+ "0",
373
+ "❌ 错误: 不支持的模块",
374
  )
375
+
376
  # 动态导入目标模块
377
  module_path = f"owl.examples.{example_module}"
378
  try:
 
381
  except ImportError as ie:
382
  logging.error(f"无法导入模块 {module_path}: {str(ie)}")
383
  return (
384
+ f"无法导入模块: {module_path}",
385
+ "0",
386
+ f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}",
387
  )
388
  except Exception as e:
389
  logging.error(f"导入模块 {module_path} 时发生错误: {str(e)}")
390
+ return (f"导入模块时发生错误: {module_path}", "0", f"❌ 错误: {str(e)}")
391
+
 
 
 
 
392
  # 检查是否包含construct_society函数
393
  if not hasattr(module, "construct_society"):
394
  logging.error(f"模块 {module_path} 中未找到 construct_society 函数")
395
  return (
396
+ f"模块 {module_path} 中未找到 construct_society 函数",
397
+ "0",
398
+ "❌ 错误: 模块接口不兼容",
399
  )
400
+
401
  # 构建社会模拟
402
  try:
403
  logging.info("正在构建社会模拟...")
404
  society = module.construct_society(question)
 
405
 
406
  except Exception as e:
407
  logging.error(f"构建社会模拟时发生错误: {str(e)}")
408
  return (
409
+ f"构建社会模拟时发生错误: {str(e)}",
410
+ "0",
411
+ f"❌ 错误: 构建失败 - {str(e)}",
412
  )
413
+
414
  # 运行社会模拟
415
  try:
416
  logging.info("正在运行社会模拟...")
 
419
  except Exception as e:
420
  logging.error(f"运行社会模拟时发生错误: {str(e)}")
421
  return (
422
+ f"运行社会模拟时发生错误: {str(e)}",
423
+ "0",
424
+ f"❌ 错误: 运行失败 - {str(e)}",
425
  )
 
426
 
 
427
  # 安全地获取令牌计数
428
  if not isinstance(token_info, dict):
429
  token_info = {}
430
+
431
  completion_tokens = token_info.get("completion_token_count", 0)
432
  prompt_tokens = token_info.get("prompt_token_count", 0)
433
  total_tokens = completion_tokens + prompt_tokens
434
+
435
+ logging.info(
436
+ f"处理完成,令牌使用: 完成={completion_tokens}, 提示={prompt_tokens}, 总计={total_tokens}"
437
+ )
438
+
439
  return (
440
+ answer,
441
+ f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
442
+ "✅ 成功完成",
443
  )
444
+
445
  except Exception as e:
446
  logging.error(f"处理问题时发生未捕获的错误: {str(e)}")
447
+ return (f"发生错误: {str(e)}", "0", f"❌ 错误: {str(e)}")
448
+
 
 
 
449
 
450
  def update_module_description(module_name: str) -> str:
451
  """返回所选模块的描述"""
452
  return MODULE_DESCRIPTIONS.get(module_name, "无可用描述")
453
 
454
+
455
  # 环境变量管理功能
456
+
457
+ # 存储前端配置的环境变量
458
+ WEB_FRONTEND_ENV_VARS: dict[str, str] = {}
459
+
460
+
461
  def init_env_file():
462
  """初始化.env文件如果不存在"""
463
  dotenv_path = find_dotenv()
 
467
  dotenv_path = find_dotenv()
468
  return dotenv_path
469
 
470
+
471
  def load_env_vars():
472
+ """加载环境变量并返回字典格式
473
+
474
+ Returns:
475
+ dict: 环境变量字典,每个值为一个包含值和来源的元组 (value, source)
476
+ """
477
  dotenv_path = init_env_file()
478
  load_dotenv(dotenv_path, override=True)
479
+
480
+ # 从.env文件读取环境变量
481
+ env_file_vars = {}
482
  with open(dotenv_path, "r") as f:
483
  for line in f:
484
  line = line.strip()
485
  if line and not line.startswith("#"):
486
  if "=" in line:
487
  key, value = line.split("=", 1)
488
+ env_file_vars[key.strip()] = value.strip().strip("\"'")
489
+
490
+ # 从系统环境变量中获取
491
+ system_env_vars = {
492
+ k: v
493
+ for k, v in os.environ.items()
494
+ if k not in env_file_vars and k not in WEB_FRONTEND_ENV_VARS
495
+ }
496
+
497
+ # 合并环境变量,并标记来源
498
+ env_vars = {}
499
+
500
+ # 添加系统环境变量(最低优先级)
501
+ for key, value in system_env_vars.items():
502
+ env_vars[key] = (value, "系统")
503
+
504
+ # 添加.env文件环境变量(中等优先级)
505
+ for key, value in env_file_vars.items():
506
+ env_vars[key] = (value, ".env文件")
507
+
508
+ # 添加前端配置的环境变量(最高优先级)
509
+ for key, value in WEB_FRONTEND_ENV_VARS.items():
510
+ env_vars[key] = (value, "前端配置")
511
+ # 确保操作系统环境变量也被更新
512
+ os.environ[key] = value
513
+
514
  return env_vars
515
 
516
+
517
  def save_env_vars(env_vars):
518
+ """保存环境变量到.env文件
519
+
520
+ Args:
521
+ env_vars: 字典,键为环境变量名,值可以是字符串或(值,来源)元组
522
+ """
523
  try:
524
  dotenv_path = init_env_file()
525
+
526
  # 保存每个环境变量
527
+ for key, value_data in env_vars.items():
528
  if key and key.strip(): # 确保键不为空
529
+ # 处理值可能是元组的情况
530
+ if isinstance(value_data, tuple):
531
+ value = value_data[0]
532
+ else:
533
+ value = value_data
534
+
535
  set_key(dotenv_path, key.strip(), value.strip())
536
+
537
  # 重新加载环境变量以确保生效
538
  load_dotenv(dotenv_path, override=True)
539
+
540
  return True, "环境变量已成功保存!"
541
  except Exception as e:
542
  return False, f"保存环境变量时出错: {str(e)}"
543
 
544
+
545
+ def add_env_var(key, value, from_frontend=True):
546
+ """添加或更新单个环境变量
547
+
548
+ Args:
549
+ key: 环境变量名
550
+ value: 环境变量值
551
+ from_frontend: 是否来自前端配置,默认为True
552
+ """
553
  try:
554
  if not key or not key.strip():
555
  return False, "变量名不能为空"
556
+
557
+ key = key.strip()
558
+ value = value.strip()
559
+
560
+ # 如果来自前端,则添加到前端环境变量字典
561
+ if from_frontend:
562
+ WEB_FRONTEND_ENV_VARS[key] = value
563
+ # 直接更新系统环境变量
564
+ os.environ[key] = value
565
+
566
+ # 同时更新.env文件
567
  dotenv_path = init_env_file()
568
+ set_key(dotenv_path, key, value)
569
  load_dotenv(dotenv_path, override=True)
570
+
571
  return True, f"环境变量 {key} 已成功添加/更新!"
572
  except Exception as e:
573
  return False, f"添加环境变量时出错: {str(e)}"
574
 
575
+
576
  def delete_env_var(key):
577
  """删除环境变量"""
578
  try:
579
  if not key or not key.strip():
580
  return False, "变量名不能为空"
581
+
582
+ key = key.strip()
583
+
584
+ # 从.env文件中删除
585
  dotenv_path = init_env_file()
586
+ unset_key(dotenv_path, key)
587
+
588
+ # 从前端环境变量字典中删除
589
+ if key in WEB_FRONTEND_ENV_VARS:
590
+ del WEB_FRONTEND_ENV_VARS[key]
591
+
592
  # 从当前进程环境中也删除
593
  if key in os.environ:
594
  del os.environ[key]
595
+
596
  return True, f"环境变量 {key} 已成功删除!"
597
  except Exception as e:
598
  return False, f"删除环境变量时出错: {str(e)}"
599
 
600
+
601
+ def is_api_related(key: str) -> bool:
602
+ """判断环境变量是否与API相关
603
+
604
  Args:
605
  key: 环境变量名
606
+
 
607
  Returns:
608
+ bool: 是否与API相关
609
  """
610
+ # API相关的关键词
611
+ api_keywords = [
612
+ "api",
613
+ "key",
614
+ "token",
615
+ "secret",
616
+ "password",
617
+ "openai",
618
+ "qwen",
619
+ "deepseek",
620
+ "google",
621
+ "search",
622
+ "hf",
623
+ "hugging",
624
+ "chunkr",
625
+ "firecrawl",
626
+ ]
627
+
628
+ # 检查是否包含API相关关键词(不区分大小写)
629
+ return any(keyword in key.lower() for keyword in api_keywords)
630
+
631
+
632
+ def get_api_guide(key: str) -> str:
633
+ """根据环境变量名返回对应的API获取指南
634
+
635
+ Args:
636
+ key: 环境变量名
637
+
638
+ Returns:
639
+ str: API获取指南链接或说明
640
+ """
641
+ key_lower = key.lower()
642
+ if "openai" in key_lower:
643
+ return "https://platform.openai.com/api-keys"
644
+ elif "qwen" in key_lower or "dashscope" in key_lower:
645
+ return "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key"
646
+ elif "deepseek" in key_lower:
647
+ return "https://platform.deepseek.com/api_keys"
648
+ elif "google" in key_lower:
649
+ return "https://developers.google.com/custom-search/v1/overview"
650
+ elif "chunkr" in key_lower:
651
+ return "https://chunkr.ai/"
652
+ elif "firecrawl" in key_lower:
653
+ return "https://www.firecrawl.dev/"
654
+ else:
655
+ return ""
656
+
657
 
658
  def update_env_table():
659
+ """更新环境变量表格显示,只显示API相关的环境变量"""
660
  env_vars = load_env_vars()
661
+ # 过滤出API相关的环境变量
662
+ api_env_vars = {k: v for k, v in env_vars.items() if is_api_related(k)}
663
+ # 转换为列表格式,以符合Gradio Dataframe的要求
664
+ # 格式: [变量名, 变量值, 获取指南链接]
665
+ result = []
666
+ for k, v in api_env_vars.items():
667
+ guide = get_api_guide(k)
668
+ # 如果有指南链接,创建一个可点击的链接
669
+ guide_link = (
670
+ f"<a href='{guide}' target='_blank' class='guide-link'>🔗 获取</a>"
671
+ if guide
672
+ else ""
673
+ )
674
+ result.append([k, v[0], guide_link])
675
+ return result
676
+
677
+
678
+ def save_env_table_changes(data):
679
+ """保存环境变量表格的更改
680
+
681
+ Args:
682
+ data: Dataframe数据,可能是pandas DataFrame对象
683
+
684
+ Returns:
685
+ str: 操作状态信息,包含HTML格式的状态消息
686
+ """
687
+ try:
688
+ logging.info(f"开始处理环境变量表格数据,类型: {type(data)}")
689
+
690
+ # 获取当前所有环境变量
691
+ current_env_vars = load_env_vars()
692
+ processed_keys = set() # 记录已处理的键,用于检测删除的变量
693
+
694
+ # 处理pandas DataFrame对象
695
+ import pandas as pd
696
+
697
+ if isinstance(data, pd.DataFrame):
698
+ # 获取列名信息
699
+ columns = data.columns.tolist()
700
+ logging.info(f"DataFrame列名: {columns}")
701
+
702
+ # 遍历DataFrame的每一行
703
+ for index, row in data.iterrows():
704
+ # 使用列名或索引访问数据
705
+ if len(columns) >= 3:
706
+ # 如果有列名,使用列名访问
707
+ key = row.iloc[1] if hasattr(row, "iloc") else row[1]
708
+ value = row.iloc[2] if hasattr(row, "iloc") else row[2]
709
+
710
+ # 检查是否为空行或已删除的变量
711
+ if key and str(key).strip(): # 如果键名不为空,则添加或更新
712
+ logging.info(f"处理环境变量: {key} = {value}")
713
+ add_env_var(key, str(value))
714
+ processed_keys.add(key)
715
+ # 处理其他格式
716
+ elif isinstance(data, dict):
717
+ logging.info(f"字典格式数据的键: {list(data.keys())}")
718
+ # 如果是字典格式,尝试不同的键
719
+ if "data" in data:
720
+ rows = data["data"]
721
+ elif "values" in data:
722
+ rows = data["values"]
723
+ elif "value" in data:
724
+ rows = data["value"]
725
+ else:
726
+ # 尝试直接使用字典作为行数据
727
+ rows = []
728
+ for key, value in data.items():
729
+ if key not in ["headers", "types", "columns"]:
730
+ rows.append([key, value])
731
+
732
+ if isinstance(rows, list):
733
+ for row in rows:
734
+ if isinstance(row, list) and len(row) >= 2:
735
+ key, value = row[0], row[1]
736
+ if key and str(key).strip():
737
+ add_env_var(key, str(value))
738
+ processed_keys.add(key)
739
+ elif isinstance(data, list):
740
+ # 列表格式
741
+ for row in data:
742
+ if isinstance(row, list) and len(row) >= 2:
743
+ key, value = row[0], row[1]
744
+ if key and str(key).strip():
745
+ add_env_var(key, str(value))
746
+ processed_keys.add(key)
747
+ else:
748
+ logging.error(f"未知的数据格式: {type(data)}")
749
+ return f"❌ 保存失败: 未知的数据格式 {type(data)}"
750
+
751
+ # 处理删除的变量 - 检查当前环境变量中是否有未在表格中出现的变量
752
+ api_related_keys = {k for k in current_env_vars.keys() if is_api_related(k)}
753
+ keys_to_delete = api_related_keys - processed_keys
754
+
755
+ # 删除不再表格中的变量
756
+ for key in keys_to_delete:
757
+ logging.info(f"删除环境变量: {key}")
758
+ delete_env_var(key)
759
+
760
+ return "✅ 环境变量已成功保存"
761
+ except Exception as e:
762
+ import traceback
763
+
764
+ error_details = traceback.format_exc()
765
+ logging.error(f"保存环境变量时出错: {str(e)}\n{error_details}")
766
+ return f"❌ 保存失败: {str(e)}"
767
+
768
+
769
+ def get_env_var_value(key):
770
+ """获取环境变量的实际值
771
+
772
+ 优先级:前端配置 > .env文件 > 系统环境变量
773
+ """
774
+ # 检查前端配置的环境变量
775
+ if key in WEB_FRONTEND_ENV_VARS:
776
+ return WEB_FRONTEND_ENV_VARS[key]
777
+
778
+ # 检查系统环境变量(包括从.env加载的)
779
+ return os.environ.get(key, "")
780
+
781
 
782
  def create_ui():
783
  """创建增强版Gradio界面"""
784
+
785
+ # 定义对话记录更新函数
 
 
 
 
786
  def update_logs2():
787
  """获取最新对话记录并返回给前端显示"""
788
+ return get_latest_logs(100, LOG_QUEUE)
789
+
790
  def clear_log_file():
791
  """清空日志文件内容"""
792
  try:
793
  if LOG_FILE and os.path.exists(LOG_FILE):
794
  # 清空日志文件内容而不是删除文件
795
+ open(LOG_FILE, "w").close()
796
  logging.info("日志文件已清空")
797
  # 清空日志队列
798
  while not LOG_QUEUE.empty():
 
800
  LOG_QUEUE.get_nowait()
801
  except queue.Empty:
802
  break
803
+ return ""
 
 
 
 
 
 
804
  else:
805
+ return ""
806
  except Exception as e:
807
  logging.error(f"清空日志文件时出错: {str(e)}")
808
+ return ""
809
+
810
  # 创建一个实时日志更新函数
811
  def process_with_live_logs(question, module_name):
812
  """处理问题并实时更新日志"""
813
  global CURRENT_PROCESS
814
+
815
  # 创建一个后台线程来处理问题
816
  result_queue = queue.Queue()
817
+
818
  def process_in_background():
819
  try:
820
  result = run_owl(question, module_name)
821
  result_queue.put(result)
822
  except Exception as e:
823
  result_queue.put((f"发生错误: {str(e)}", "0", f"❌ 错误: {str(e)}"))
824
+
825
  # 启动后台处理线程
826
  bg_thread = threading.Thread(target=process_in_background)
827
  CURRENT_PROCESS = bg_thread # 记录当前进程
828
  bg_thread.start()
829
+
830
  # 在等待处理完成的同时,每秒更新一次日志
831
  while bg_thread.is_alive():
832
+ # 更新对话记录显示
833
+ logs2 = get_latest_logs(100, LOG_QUEUE)
834
+
 
835
  # 始终更新状态
836
+ yield (
837
+ "0",
838
+ "<span class='status-indicator status-running'></span> 处理中...",
839
+ logs2,
840
+ )
841
+
842
  time.sleep(1)
843
+
844
  # 处理完成,获取结果
845
  if not result_queue.empty():
846
  result = result_queue.get()
847
  answer, token_count, status = result
848
+
849
+ # 最后一次更新对话记录
850
+ logs2 = get_latest_logs(100, LOG_QUEUE)
851
+
 
852
  # 根据状态设置不同的指示器
853
  if "错误" in status:
854
+ status_with_indicator = (
855
+ f"<span class='status-indicator status-error'></span> {status}"
856
+ )
857
  else:
858
+ status_with_indicator = (
859
+ f"<span class='status-indicator status-success'></span> {status}"
860
+ )
861
+
862
+ yield token_count, status_with_indicator, logs2
863
  else:
864
+ logs2 = get_latest_logs(100, LOG_QUEUE)
865
+ yield (
866
+ "0",
867
+ "<span class='status-indicator status-error'></span> 已终止",
868
+ logs2,
869
+ )
870
+
871
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
872
+ gr.Markdown(
873
+ """
874
  # 🦉 OWL 多智能体协作系统
875
 
876
  基于CAMEL框架开发的先进多智能体协作系统,旨在通过智能体协作解决复杂问题。
877
  """
878
+ )
879
+
880
+ # 添加自定义CSS
881
+ gr.HTML("""
882
  <style>
883
  /* 聊天容器样式 */
884
  .chat-container .chatbot {
 
941
  line-height: 1.4;
942
  }
943
 
944
+ /* 环境变量管理样式 */
945
+ .env-manager-container {
946
+ border-radius: 10px;
947
+ padding: 15px;
948
+ background-color: #f9f9f9;
949
+ margin-bottom: 20px;
950
+ }
951
+
952
+ .env-controls, .api-help-container {
953
+ border-radius: 8px;
954
+ padding: 15px;
955
+ background-color: white;
956
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
957
+ height: 100%;
958
+ }
959
+
960
+ .env-add-group, .env-delete-group {
961
+ margin-top: 20px;
962
+ padding: 15px;
963
+ border-radius: 8px;
964
+ background-color: #f5f8ff;
965
+ border: 1px solid #e0e8ff;
966
+ }
967
+
968
+ .env-delete-group {
969
+ background-color: #fff5f5;
970
+ border: 1px solid #ffe0e0;
971
+ }
972
+
973
+ .env-buttons {
974
+ justify-content: flex-start;
975
+ gap: 10px;
976
+ margin-top: 10px;
977
+ }
978
+
979
+ .env-button {
980
+ min-width: 100px;
981
+ }
982
+
983
+ .delete-button {
984
+ background-color: #dc3545;
985
+ color: white;
986
+ }
987
+
988
+ .env-table {
989
+ margin-bottom: 15px;
990
+ }
991
+
992
+ /* 改进环境变量表格样式 */
993
+ .env-table table {
994
+ border-collapse: separate;
995
+ border-spacing: 0;
996
+ width: 100%;
997
+ border-radius: 8px;
998
+ overflow: hidden;
999
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
1000
+ }
1001
+
1002
+ .env-table th {
1003
+ background-color: #f0f7ff;
1004
+ padding: 12px 15px;
1005
+ text-align: left;
1006
+ font-weight: 600;
1007
+ color: #2c7be5;
1008
+ border-bottom: 2px solid #e0e8ff;
1009
+ }
1010
+
1011
+ .env-table td {
1012
+ padding: 10px 15px;
1013
+ border-bottom: 1px solid #f0f0f0;
1014
+ }
1015
+
1016
+ .env-table tr:hover td {
1017
+ background-color: #f9fbff;
1018
+ }
1019
+
1020
+ .env-table tr:last-child td {
1021
+ border-bottom: none;
1022
+ }
1023
+
1024
+ /* 状态图标样式 */
1025
+ .status-icon-cell {
1026
+ text-align: center;
1027
+ font-size: 1.2em;
1028
+ }
1029
+
1030
+ /* 链接样式 */
1031
+ .guide-link {
1032
+ color: #2c7be5;
1033
+ text-decoration: none;
1034
+ cursor: pointer;
1035
+ font-weight: 500;
1036
+ }
1037
+
1038
+ .guide-link:hover {
1039
+ text-decoration: underline;
1040
+ }
1041
+
1042
+ .env-status {
1043
+ margin-top: 15px;
1044
+ font-weight: 500;
1045
+ padding: 10px;
1046
+ border-radius: 6px;
1047
+ transition: all 0.3s ease;
1048
+ }
1049
+
1050
+ .env-status-success {
1051
+ background-color: #d4edda;
1052
+ color: #155724;
1053
+ border: 1px solid #c3e6cb;
1054
+ }
1055
+
1056
+ .env-status-error {
1057
+ background-color: #f8d7da;
1058
+ color: #721c24;
1059
+ border: 1px solid #f5c6cb;
1060
+ }
1061
+
1062
+ .api-help-accordion {
1063
+ margin-bottom: 8px;
1064
+ border-radius: 6px;
1065
+ overflow: hidden;
1066
+ }
1067
+
1068
 
1069
  @keyframes pulse {
1070
  0% { opacity: 1; }
 
1073
  }
1074
  </style>
1075
  """)
1076
+
1077
+ with gr.Row():
1078
+ with gr.Column(scale=1):
1079
+ question_input = gr.Textbox(
1080
+ lines=5,
1081
+ placeholder="请输入您的问题...",
1082
+ label="问题",
1083
+ elem_id="question_input",
1084
+ show_copy_button=True,
1085
+ )
1086
+
1087
+ # 增强版模块选择下拉菜单
1088
+ # 只包含MODULE_DESCRIPTIONS中定义的模块
1089
+ module_dropdown = gr.Dropdown(
1090
+ choices=list(MODULE_DESCRIPTIONS.keys()),
1091
+ value="run_qwen_zh",
1092
+ label="选择功能模块",
1093
+ interactive=True,
1094
+ )
1095
+
1096
+ # 模块描述文本框
1097
+ module_description = gr.Textbox(
1098
+ value=MODULE_DESCRIPTIONS["run_qwen_zh"],
1099
+ label="模块描述",
1100
+ interactive=False,
1101
+ elem_classes="module-info",
1102
+ )
1103
+
1104
+ with gr.Row():
1105
+ run_button = gr.Button(
1106
+ "运行", variant="primary", elem_classes="primary"
1107
  )
1108
+
1109
+ status_output = gr.HTML(
1110
+ value="<span class='status-indicator status-success'></span> 已就绪",
1111
+ label="状态",
1112
+ )
1113
+ token_count_output = gr.Textbox(
1114
+ label="令牌计数", interactive=False, elem_classes="token-count"
1115
+ )
1116
+
1117
+ with gr.Tabs(): # 设置对话记录为默认选中的标签页
1118
+ with gr.TabItem("对话记录"):
1119
+ # 添加对话记录显示区域
1120
+ log_display2 = gr.Textbox(
1121
+ label="对话记录",
1122
+ lines=25,
1123
+ max_lines=100,
1124
  interactive=False,
1125
+ autoscroll=True,
1126
+ show_copy_button=True,
1127
+ elem_classes="log-display",
1128
+ container=True,
1129
+ value="",
1130
  )
1131
+
1132
  with gr.Row():
1133
+ refresh_logs_button2 = gr.Button("刷新记录")
1134
+ auto_refresh_checkbox2 = gr.Checkbox(
1135
+ label="自动刷新", value=True, interactive=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1136
  )
1137
+ clear_logs_button2 = gr.Button("清空记录", variant="secondary")
1138
 
1139
+ with gr.TabItem("环境变量管理", id="env-settings"):
1140
+ with gr.Box(elem_classes="env-manager-container"):
 
 
1141
  gr.Markdown("""
1142
+ ## 环境变量管理
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1143
 
1144
+ 在此处设置模型API密钥和其他服务凭证。这些信息将保存在本地的`.env`文件中,确保您的API密钥安全存储且不会上传到网络。
1145
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1146
 
1147
+ # 主要内容分为两列布局
1148
+ with gr.Row():
1149
+ # 左侧列:环境变量管理控件
1150
+ with gr.Column(scale=3):
1151
+ with gr.Box(elem_classes="env-controls"):
1152
+ # 环境变量表格 - 设置为可交互以直接编辑
1153
+ gr.Markdown("### 环境变量管理")
1154
+ gr.Markdown("""
1155
+ 管理您的API密钥和其他环境变量。正确设置API密钥对于OWL系统的功能至关重要。
1156
+
1157
+ <div style="background-color: #e7f3fe; border-left: 6px solid #2196F3; padding: 10px; margin: 15px 0; border-radius: 4px;">
1158
+ <strong>提示:</strong> 请确保正确设置API密钥以确保系统功能正常
1159
+ </div>
1160
+ """)
1161
 
1162
+ # 增强版环境变量表格,支持添加和删除行
1163
+ env_table = gr.Dataframe(
1164
+ headers=["变量名", "值", "获取指南"],
1165
+ datatype=[
1166
+ "str",
1167
+ "str",
1168
+ "html",
1169
+ ], # 将最后��列设置为html类型以支持链接
1170
+ row_count=10, # 增加行数,以便添加新变量
1171
+ col_count=(3, "fixed"),
1172
+ value=update_env_table,
1173
+ label="API密钥和环境变量",
1174
+ interactive=True, # 设置为可交互,允许直接编辑
1175
+ elem_classes="env-table",
1176
+ )
1177
 
1178
+ # 操作说明
1179
+ gr.Markdown(
1180
+ """
1181
+ <div style="background-color: #fff3cd; border-left: 6px solid #ffc107; padding: 10px; margin: 15px 0; border-radius: 4px;">
1182
+ <strong>操作指南</strong>:
1183
+ <ul style="margin-top: 8px; margin-bottom: 8px;">
1184
+ <li><strong>编辑变量</strong>: 直接点击表格中的"值"单元格进行编辑</li>
1185
+ <li><strong>添加变量</strong>: 在空白行中输入新的变量名和值</li>
1186
+ <li><strong>删除变量</strong>: 清空变量名即可删除该行</li>
1187
+ <li><strong>获取API密钥</strong>: 点击"获取指南"列中的链接获取相应API密钥</li>
1188
+ </ul>
1189
+ <strong>注意</strong>: 所有API密钥都安全地存储在本地,不会上传到网络
1190
+ </div>
1191
+ """,
1192
+ elem_classes="env-instructions",
1193
+ )
1194
+
1195
+ # 环境变量操作按钮
1196
+ with gr.Row(elem_classes="env-buttons"):
1197
+ save_env_button = gr.Button(
1198
+ "💾 保存更改",
1199
+ variant="primary",
1200
+ elem_classes="env-button",
1201
+ )
1202
+ refresh_button = gr.Button(
1203
+ "🔄 刷新列表", elem_classes="env-button"
1204
+ )
1205
+
1206
+ # 状态显示
1207
+ env_status = gr.HTML(
1208
+ label="操作状态",
1209
+ value="",
1210
+ elem_classes="env-status",
1211
+ )
1212
+
1213
+ # 连接事件处理函数
1214
+ save_env_button.click(
1215
+ fn=save_env_table_changes,
1216
+ inputs=[env_table],
1217
+ outputs=[env_status],
1218
+ ).then(fn=update_env_table, outputs=[env_table])
1219
+
1220
+ refresh_button.click(fn=update_env_table, outputs=[env_table])
1221
+
1222
+ # 示例问题
1223
+ examples = [
1224
+ "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
1225
+ "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
1226
+ "写一个hello world的python文件,保存到本地",
1227
+ ]
1228
+
1229
+ gr.Examples(examples=examples, inputs=question_input)
1230
+
1231
+ gr.HTML("""
1232
  <div class="footer" id="about">
1233
  <h3>关于 OWL 多智能体协作系统</h3>
1234
  <p>OWL 是一个基于CAMEL框架开发的先进多智能体协作系统,旨在通过智能体协作解决复杂问题。</p>
 
1236
  <p><a href="https://github.com/camel-ai/owl" target="_blank">GitHub</a></p>
1237
  </div>
1238
  """)
1239
+
1240
+ # 设置事件处理
1241
+ run_button.click(
1242
+ fn=process_with_live_logs,
1243
+ inputs=[question_input, module_dropdown],
1244
+ outputs=[token_count_output, status_output, log_display2],
1245
+ )
1246
+
1247
+ # 模块选择更新描述
1248
+ module_dropdown.change(
1249
+ fn=update_module_description,
1250
+ inputs=module_dropdown,
1251
+ outputs=module_description,
1252
+ )
1253
+
1254
+ # 对话记录相关事件处理
1255
+ refresh_logs_button2.click(
1256
+ fn=lambda: get_latest_logs(100, LOG_QUEUE), outputs=[log_display2]
1257
+ )
1258
+
1259
+ clear_logs_button2.click(fn=clear_log_file, outputs=[log_display2])
1260
+
1261
+ # 自动刷新控制
1262
+ def toggle_auto_refresh(enabled):
1263
+ if enabled:
1264
+ return gr.update(every=3)
1265
+ else:
1266
+ return gr.update(every=0)
1267
+
1268
+ auto_refresh_checkbox2.change(
1269
+ fn=toggle_auto_refresh,
1270
+ inputs=[auto_refresh_checkbox2],
1271
+ outputs=[log_display2],
1272
+ )
1273
+
1274
+ # 不再默认自动刷新日志
1275
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1276
  return app
1277
 
1278
+
1279
  # 主函数
1280
  def main():
1281
  try:
 
1283
  global LOG_FILE
1284
  LOG_FILE = setup_logging()
1285
  logging.info("OWL Web应用程序启动")
1286
+
1287
  # 启动日志读取线程
1288
+ log_thread = threading.Thread(
1289
+ target=log_reader_thread, args=(LOG_FILE,), daemon=True
1290
+ )
1291
  log_thread.start()
1292
  logging.info("日志读取线程已启动")
1293
+
1294
  # 初始化.env文件(如果不存在)
1295
  init_env_file()
1296
  app = create_ui()
1297
+
1298
  # 注册应用关闭时的清理函数
1299
  def cleanup():
1300
  global STOP_LOG_THREAD, STOP_REQUESTED
1301
  STOP_LOG_THREAD.set()
1302
  STOP_REQUESTED.set()
1303
  logging.info("应用程序关闭,停止日志线程")
1304
+
1305
+ app.queue()
1306
+ app.launch(share=False, server_name="127.0.0.1", server_port=7860)
1307
  except Exception as e:
1308
  logging.error(f"启动应用程序时发生错误: {str(e)}")
1309
  print(f"启动应用程序时发生错误: {str(e)}")
1310
  import traceback
1311
+
1312
  traceback.print_exc()
1313
+
1314
  finally:
1315
  # 确保日志线程停止
1316
  STOP_LOG_THREAD.set()
1317
  STOP_REQUESTED.set()
1318
  logging.info("应用程序关闭")
1319
 
1320
+
1321
  if __name__ == "__main__":
1322
+ main()
pyproject.toml CHANGED
@@ -21,7 +21,7 @@ keywords = [
21
  "learning-systems"
22
  ]
23
  dependencies = [
24
- "camel-ai[all]==0.2.27",
25
  "chunkr-ai>=0.0.41",
26
  "docx2markdown>=0.1.1",
27
  "gradio>=3.50.2",
 
21
  "learning-systems"
22
  ]
23
  dependencies = [
24
+ "camel-ai[all]==0.2.30",
25
  "chunkr-ai>=0.0.41",
26
  "docx2markdown>=0.1.1",
27
  "gradio>=3.50.2",
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- camel-ai[all]==0.2.27
2
  chunkr-ai>=0.0.41
3
  docx2markdown>=0.1.1
4
  gradio>=3.50.2
 
1
+ camel-ai[all]==0.2.30
2
  chunkr-ai>=0.0.41
3
  docx2markdown>=0.1.1
4
  gradio>=3.50.2
uv.lock CHANGED
@@ -482,7 +482,7 @@ wheels = [
482
 
483
  [[package]]
484
  name = "camel-ai"
485
- version = "0.2.27"
486
  source = { registry = "https://pypi.org/simple" }
487
  dependencies = [
488
  { name = "colorama" },
@@ -499,9 +499,9 @@ dependencies = [
499
  { name = "pyyaml" },
500
  { name = "tiktoken" },
501
  ]
502
- sdist = { url = "https://files.pythonhosted.org/packages/ff/27/2bce666ae7f7d0db276d037b3afe84a460e782438e5cacc08de20417233b/camel_ai-0.2.27.tar.gz", hash = "sha256:4689245ad48f51e5e602d2651cf463afe212bcf046633a19c2189574c1f3481a", size = 441363 }
503
  wheels = [
504
- { url = "https://files.pythonhosted.org/packages/b0/fa/94f5b41cb6babc81aac00494b170ec2bea058b6c00f477ceb3e886c49177/camel_ai-0.2.27-py3-none-any.whl", hash = "sha256:c4a6597791faf2f2161c56c2579e60850557b126135b29af77ebd08fa0774e0b", size = 746387 },
505
  ]
506
 
507
  [package.optional-dependencies]
@@ -3575,7 +3575,7 @@ dependencies = [
3575
 
3576
  [package.metadata]
3577
  requires-dist = [
3578
- { name = "camel-ai", extras = ["all"], specifier = "==0.2.27" },
3579
  { name = "chunkr-ai", specifier = ">=0.0.41" },
3580
  { name = "docx2markdown", specifier = ">=0.1.1" },
3581
  { name = "gradio", specifier = ">=3.50.2" },
 
482
 
483
  [[package]]
484
  name = "camel-ai"
485
+ version = "0.2.30"
486
  source = { registry = "https://pypi.org/simple" }
487
  dependencies = [
488
  { name = "colorama" },
 
499
  { name = "pyyaml" },
500
  { name = "tiktoken" },
501
  ]
502
+ sdist = { url = "https://files.pythonhosted.org/packages/ef/86/57cbcae86d2d60dab0aad31b5302525c75f45ff5edc3c3819a378fa9e12c/camel_ai-0.2.30.tar.gz", hash = "sha256:e1639376e70e9cf1477eca88d1bdc1813855cbd1db683528e1f93027b6aa0b0a", size = 442842 }
503
  wheels = [
504
+ { url = "https://files.pythonhosted.org/packages/85/fe/8f1d17896aedbc9e0dfa1bff40d560e5a6808d9b727e04c293be6be5954f/camel_ai-0.2.30-py3-none-any.whl", hash = "sha256:e09eec860331cdb4da4e49f46f5d45345a81820c5847556fdf9e7827dd9bbfa9", size = 752672 },
505
  ]
506
 
507
  [package.optional-dependencies]
 
3575
 
3576
  [package.metadata]
3577
  requires-dist = [
3578
+ { name = "camel-ai", extras = ["all"], specifier = "==0.2.30" },
3579
  { name = "chunkr-ai", specifier = ">=0.0.41" },
3580
  { name = "docx2markdown", specifier = ">=0.1.1" },
3581
  { name = "gradio", specifier = ">=3.50.2" },