resolve conflict

This commit is contained in:
yifeng.wang
2025-05-06 19:58:12 +08:00
24 changed files with 2304 additions and 1276 deletions

View File

@@ -370,6 +370,9 @@ For information on configuring AI models, please refer to our [CAMEL models docu
OWL supports various LLM backends, though capabilities may vary depending on the model's tool calling and multimodal abilities. You can use the following scripts to run with different models:
```bash
# Run with Claude model
python examples/run_claude.py
# Run with Qwen model
python examples/run_qwen_zh.py

View File

@@ -362,6 +362,9 @@ python examples/run_mini.py
OWL 支持多种 LLM 后端,但功能可能因模型的工具调用和多模态能力而异。您可以使用以下脚本来运行不同的模型:
```bash
# 使用 Claude 模型运行
python examples/run_claude.py
# 使用 Qwen 模型运行
python examples/run_qwen_zh.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 331 KiB

View File

@@ -0,0 +1,142 @@
# 🚀 OWL with Qwen3 MCP Integration
This project demonstrates how to use the [CAMEL-AI OWL framework](https://github.com/camel-ai/owl) with **Qwen3** large language model through MCP (Model Context Protocol). The example showcases improved terminal output formatting, markdown log generation, and seamless integration with MCP servers.
## ✨ Features
- **Enhanced Terminal Output**: Colorful, well-formatted console output for better readability
- **Automatic Log Generation**: Creates detailed markdown logs of agent conversations with timestamps
- **Qwen3 Integration**: Seamlessly uses Qwen3-Plus for both user and assistant agents
- **MCP Server Support**: Connects to MCP servers including EdgeOne Pages and Playwright
- **Robust Error Handling**: Graceful cleanup and exception management
## 📋 Prerequisites
- Python 3.10+
- OWL framework installed
- Qwen API key
- Node.js (for Playwright MCP)
## 🛠️ Setup
1. Clone the OWL repository:
```bash
git clone https://github.com/camel-ai/owl
cd owl
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Add your Qwen API key to the `.env` file:
```
QWEN_API_KEY=your_api_key_here
```
4. Configure MCP servers in `mcp_sse_config.json`
5. (Optional) Install Playwright MCP manually:
```bash
npm install -D @playwright/mcp
```
Note: This step is optional as the config will auto-install Playwright MCP through npx.
## 🧩 MCP Servers Included
This example integrates with two powerful MCP servers:
### 1. EdgeOne Pages MCP (`edgeone-pages-mcp`)
EdgeOne Pages MCP is a specialized service that enables:
- **Instant HTML Deployment**: Deploy AI-generated HTML content to EdgeOne Pages with a single call
- **Public Access URLs**: Generate publicly accessible links for deployed content
- **No Setup Required**: Uses an SSE (Server-Sent Events) endpoint, so no local installation is needed
Configuration in `mcp_sse_config.json`:
```json
"edgeone-pages-mcp": {
"type": "sse",
"url": "https://mcp.api-inference.modelscope.cn/sse/fcbc9ff4e9704d"
}
```
### 2. Playwright MCP (`playwright`)
Playwright MCP is a powerful browser automation tool that allows:
- **Web Navigation**: Browse websites and interact with web pages
- **Screen Capture**: Take screenshots and extract page content
- **Element Interaction**: Click, type, and interact with page elements
- **Web Scraping**: Extract structured data from web pages
Configuration in `mcp_sse_config.json`:
```json
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
]
}
```
**Installation Options**:
1. **Auto-installation**: The configuration above automatically installs and runs Playwright MCP through `npx`.
2. **Manual installation**: If you prefer to install it permanently in your project:
```bash
npm install -D @playwright/mcp
```
Then you can update the config to use the local installation:
```json
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp"
]
}
```
## 🚀 Usage
Run the script with a task prompt:
```bash
python run_mcp_qwen3.py "Your task description here"
```
If no task is provided, a default task will be used.
## 📊 Output
1. **Terminal Output**: Colorful, formatted output showing:
- System messages
- Task specifications
- Agent conversations
- Tool calls
- Task completion status
2. **Markdown Logs**: Detailed conversation logs saved to `conversation_logs/` directory
## 🔧 Customization
- Modify `mcp_sse_config.json` to add or remove MCP servers
- Adjust model parameters in the `construct_society` function
- Change the maximum conversation rounds with the `round_limit` parameter
## 📝 Technical Details
This implementation extends the standard OWL run_mcp.py script with:
1. Colorized terminal output using Colorama
2. Structured markdown log generation
3. Improved error handling and graceful termination
4. Enhanced formatting for tool calls and agent messages
## 🤝 Acknowledgements
- [CAMEL-AI Organization](https://github.com/camel-ai)
- [OWL Framework](https://github.com/camel-ai/owl)
- [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction)
- [Qwen API](https://help.aliyun.com/zh/dashscope/developer-reference/api-details)
- [EdgeOne Pages](https://edgeone.cloud.tencent.com/pages)
- [Microsoft Playwright](https://github.com/microsoft/playwright-mcp)

View File

@@ -0,0 +1,14 @@
{
"mcpServers": {
"edgeone-pages-mcp": {
"type": "sse",
"url": "https://mcp.api-inference.modelscope.cn/sse/fcbc9ff4e9704d"
},
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
]
}
}
}

View File

@@ -0,0 +1,332 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import asyncio
import sys
import contextlib
import time
from pathlib import Path
from typing import List, Dict, Any
import os
from colorama import Fore, init
from dotenv import load_dotenv
from camel.agents.chat_agent import ToolCallingRecord
from camel.models import ModelFactory
from camel.toolkits import FunctionTool, MCPToolkit
from camel.types import ModelPlatformType, ModelType
from camel.logger import set_log_level
from camel.utils import print_text_animated
from owl.utils.enhanced_role_playing import OwlRolePlaying, arun_society
import pathlib
# Initialize colorama for cross-platform colored terminal output
init()
base_dir = pathlib.Path(__file__).parent.parent
env_path = base_dir / "owl" / ".env"
load_dotenv(dotenv_path=str(env_path))
set_log_level(level="INFO")
async def construct_society(
question: str,
tools: List[FunctionTool],
) -> OwlRolePlaying:
r"""Build a multi-agent OwlRolePlaying instance for task completion.
Args:
question (str): The task to perform.
tools (List[FunctionTool]): The MCP tools to use for interaction.
Returns:
OwlRolePlaying: The configured society of agents.
"""
models = {
"user": ModelFactory.create(
model_platform=ModelPlatformType.QWEN,
model_type=ModelType.QWEN_PLUS_LATEST,
model_config_dict={"temperature": 0},
),
"assistant": ModelFactory.create(
model_platform=ModelPlatformType.QWEN,
model_type=ModelType.QWEN_PLUS_LATEST,
model_config_dict={"temperature": 0},
),
}
user_agent_kwargs = {"model": models["user"]}
assistant_agent_kwargs = {
"model": models["assistant"],
"tools": tools,
}
task_kwargs = {
"task_prompt": question,
"with_task_specify": False,
}
society = OwlRolePlaying(
**task_kwargs,
user_role_name="user",
user_agent_kwargs=user_agent_kwargs,
assistant_role_name="assistant",
assistant_agent_kwargs=assistant_agent_kwargs,
)
return society
def create_md_file(task: str) -> str:
"""Create a markdown file for the conversation with timestamp in filename.
Args:
task (str): The task being performed.
Returns:
str: Path to the created markdown file.
"""
timestamp = time.strftime("%Y%m%d_%H%M%S")
# Create logs directory if it doesn't exist
logs_dir = Path("conversation_logs")
logs_dir.mkdir(exist_ok=True)
# Create a shortened task name for the filename
task_short = task[:30].replace(" ", "_").replace("/", "_")
filename = f"{logs_dir}/conversation_{timestamp}_{task_short}.md"
# Initialize the file with header
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# Agent Conversation: {task}\n\n")
f.write(f"*Generated on: {time.strftime('%Y-%m-%d %H:%M:%S')}*\n\n")
f.write("## Task Details\n\n")
f.write(f"**Task:** {task}\n\n")
f.write("## Conversation\n\n")
return filename
def write_to_md(filename: str, content: Dict[str, Any]) -> None:
"""Write content to the markdown file.
Args:
filename (str): Path to the markdown file.
content (Dict[str, Any]): Content to write to the file.
"""
with open(filename, "a", encoding="utf-8") as f:
if "system_info" in content:
f.write(f"### System Information\n\n")
for key, value in content["system_info"].items():
f.write(f"**{key}:** {value}\n\n")
if "assistant" in content:
f.write(f"### 🤖 Assistant\n\n")
if "tool_calls" in content:
f.write("**Tool Calls:**\n\n")
for tool_call in content["tool_calls"]:
f.write(f"```\n{tool_call}\n```\n\n")
f.write(f"{content['assistant']}\n\n")
if "user" in content:
f.write(f"### 👤 User\n\n")
f.write(f"{content['user']}\n\n")
if "summary" in content:
f.write(f"## Summary\n\n")
f.write(f"{content['summary']}\n\n")
if "token_count" in content:
f.write(f"**Total tokens used:** {content['token_count']}\n\n")
async def run_society_with_formatted_output(society: OwlRolePlaying, md_filename: str, round_limit: int = 15):
"""Run the society with nicely formatted terminal output and write to markdown.
Args:
society (OwlRolePlaying): The society to run.
md_filename (str): Path to the markdown file for output.
round_limit (int, optional): Maximum number of conversation rounds. Defaults to 15.
Returns:
tuple: (answer, chat_history, token_count)
"""
print(Fore.GREEN + f"AI Assistant sys message:\n{society.assistant_sys_msg}\n")
print(Fore.BLUE + f"AI User sys message:\n{society.user_sys_msg}\n")
print(Fore.YELLOW + f"Original task prompt:\n{society.task_prompt}\n")
print(Fore.CYAN + "Specified task prompt:" + f"\n{society.specified_task_prompt}\n")
print(Fore.RED + f"Final task prompt:\n{society.task_prompt}\n")
# Write system information to markdown
write_to_md(md_filename, {
"system_info": {
"AI Assistant System Message": society.assistant_sys_msg,
"AI User System Message": society.user_sys_msg,
"Original Task Prompt": society.task_prompt,
"Specified Task Prompt": society.specified_task_prompt,
"Final Task Prompt": society.task_prompt
}
})
input_msg = society.init_chat()
chat_history = []
token_count = {"total": 0}
n = 0
while n < round_limit:
n += 1
assistant_response, user_response = await society.astep(input_msg)
md_content = {}
if assistant_response.terminated:
termination_msg = f"AI Assistant terminated. Reason: {assistant_response.info['termination_reasons']}."
print(Fore.GREEN + termination_msg)
md_content["summary"] = termination_msg
write_to_md(md_filename, md_content)
break
if user_response.terminated:
termination_msg = f"AI User terminated. Reason: {user_response.info['termination_reasons']}."
print(Fore.GREEN + termination_msg)
md_content["summary"] = termination_msg
write_to_md(md_filename, md_content)
break
# Handle tool calls for both terminal and markdown
if "tool_calls" in assistant_response.info:
tool_calls: List[ToolCallingRecord] = [
ToolCallingRecord(**call.as_dict())
for call in assistant_response.info['tool_calls']
]
md_content["tool_calls"] = tool_calls
# Print to terminal
print(Fore.GREEN + "AI Assistant:")
for func_record in tool_calls:
print(f"{func_record}")
else:
print(Fore.GREEN + "AI Assistant:")
# Print assistant response to terminal
print(f"{assistant_response.msg.content}\n")
# Print user response to terminal
print(Fore.BLUE + f"AI User:\n\n{user_response.msg.content}\n")
# Build content for markdown file
md_content["assistant"] = assistant_response.msg.content
md_content["user"] = user_response.msg.content
# Write to markdown
write_to_md(md_filename, md_content)
# Update chat history
chat_history.append({
"assistant": assistant_response.msg.content,
"user": user_response.msg.content,
})
# Update token count
if "token_count" in assistant_response.info:
token_count["total"] += assistant_response.info["token_count"]
if "TASK_DONE" in user_response.msg.content:
task_done_msg = "Task completed successfully!"
print(Fore.YELLOW + task_done_msg + "\n")
write_to_md(md_filename, {"summary": task_done_msg})
break
input_msg = assistant_response.msg
# Write token count information
write_to_md(md_filename, {"token_count": token_count["total"]})
# Extract final answer
answer = assistant_response.msg.content if assistant_response and assistant_response.msg else ""
return answer, chat_history, token_count
@contextlib.asynccontextmanager
async def mcp_toolkit_context(config_path):
"""Context manager for safely handling MCP Toolkit connection/disconnection.
Args:
config_path (str): Path to the MCP configuration file.
Yields:
MCPToolkit: The connected MCPToolkit instance.
"""
toolkit = MCPToolkit(config_path=str(config_path))
try:
await toolkit.connect()
print(Fore.GREEN + "Successfully connected to SSE server")
yield toolkit
finally:
# Use a separate try/except to ensure we always attempt to disconnect
try:
await toolkit.disconnect()
print(Fore.GREEN + "Successfully disconnected from SSE server")
except Exception as e:
# Just log the error but don't re-raise as we're in cleanup
print(Fore.RED + f"Warning: Error during disconnect: {e}")
async def main():
# Load SSE server configuration
config_path = Path(__file__).parent / "mcp_sse_config.json"
# Set default task - a simple example query
default_task = (
"Visit the Qwen3 GitHub repository, summarize the introduction of the repository."
"Write a comprehensive HTML documentation site with the following features:"
"A clear introduction to Qwen3"
"Well-organized sections of the technical documentation"
"Practical code examples"
"A visually appealing purple technology theme (e.g. modern, clean, purple-accented design)"
"Finally, deploy the HTML site and open it in the browser."
)
# Use command line argument if provided, otherwise use default task
task = sys.argv[1] if len(sys.argv) > 1 else default_task
try:
# Create markdown file for conversation export
md_filename = create_md_file(task)
print(Fore.CYAN + f"Conversation will be saved to: {md_filename}")
async with mcp_toolkit_context(config_path) as mcp_toolkit:
# Get available tools
tools = [*mcp_toolkit.get_tools()]
# Build and run society
print(Fore.YELLOW + f"Starting task: {task}\n")
society = await construct_society(task, tools)
answer, chat_history, token_count = await run_society_with_formatted_output(society, md_filename)
print(Fore.GREEN + f"\nFinal Result: {answer}")
print(Fore.CYAN + f"Total tokens used: {token_count['total']}")
print(Fore.CYAN + f"Full conversation log saved to: {md_filename}")
except KeyboardInterrupt:
print(Fore.YELLOW + "\nReceived exit signal, shutting down...")
except Exception as e:
print(Fore.RED + f"Error occurred: {e}")
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -131,7 +131,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -105,7 +105,7 @@ def construct_society(question: str) -> OwlRolePlaying:
def main():
r"""Main function to run the OWL system with Azure OpenAI."""
# Example question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

190
examples/run_claude.py Normal file
View File

@@ -0,0 +1,190 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import sys
import pathlib
from dotenv import load_dotenv
from camel.configs import MistralConfig
from camel.models import ModelFactory
from camel.toolkits import (
SearchToolkit,
BrowserToolkit,
FileWriteToolkit,
)
from camel.types import ModelPlatformType, ModelType
from camel.logger import set_log_level
from camel.societies import RolePlaying
from owl.utils import run_society, DocumentProcessingToolkit
base_dir = pathlib.Path(__file__).parent.parent
env_path = base_dir / "owl" / ".env"
load_dotenv(dotenv_path=str(env_path))
set_log_level(level="DEBUG")
def construct_society(question: str) -> RolePlaying:
r"""Construct a society of agents based on the given question.
Args:
question (str): The task or question to be addressed by the society.
Returns:
RolePlaying: A configured society of agents ready to address the question.
"""
# Create models for different components
models = {
"user": ModelFactory.create(
<<<<<<<< HEAD:community_usecase/Smart_city_research/main.py
model_platform=ModelPlatformType.MISTRAL,
model_type=ModelType.MISTRAL_LARGE,
model_config_dict=MistralConfig(temperature=0.0).as_dict(),
),
"assistant": ModelFactory.create(
model_platform=ModelPlatformType.MISTRAL,
model_type=ModelType.MISTRAL_LARGE,
model_config_dict=MistralConfig(temperature=0.0).as_dict(),
),
"browsing": ModelFactory.create(
model_platform=ModelPlatformType.MISTRAL,
model_type=ModelType.MISTRAL_LARGE,
model_config_dict=MistralConfig(temperature=0.0).as_dict(),
),
"planning": ModelFactory.create(
model_platform=ModelPlatformType.MISTRAL,
model_type=ModelType.MISTRAL_LARGE,
model_config_dict=MistralConfig(temperature=0.0).as_dict(),
========
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"assistant": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"browsing": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"planning": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"video": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"image": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
),
"document": ModelFactory.create(
model_platform=ModelPlatformType.ANTHROPIC,
model_type=ModelType.CLAUDE_3_7_SONNET,
model_config_dict={"temperature": 0},
>>>>>>>> main:examples/run_claude.py
),
}
# Configure toolkits
tools = [
*BrowserToolkit(
headless=False, # Set to True for headless mode (e.g., on remote servers)
web_agent_model=models["browsing"],
planning_agent_model=models["planning"],
).get_tools(),
<<<<<<<< HEAD:community_usecase/Smart_city_research/main.py
*PyAutoGUIToolkit().get_tools(),
*TerminalToolkit(working_dir=workspace_dir).get_tools(),
# SearchToolkit().search_duckduckgo,
SearchToolkit().search_google, # Comment this out if you don't have google search
========
*VideoAnalysisToolkit(model=models["video"]).get_tools(),
*CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(),
*ImageAnalysisToolkit(model=models["image"]).get_tools(),
SearchToolkit().search_duckduckgo,
SearchToolkit().search_wiki,
*ExcelToolkit().get_tools(),
>>>>>>>> main:examples/run_claude.py
*DocumentProcessingToolkit(model=models["document"]).get_tools(),
*FileWriteToolkit(output_dir="./").get_tools(),
]
# Configure agent roles and parameters
user_agent_kwargs = {"model": models["user"]}
assistant_agent_kwargs = {"model": models["assistant"], "tools": tools}
# Configure task parameters
task_kwargs = {
"task_prompt": question,
"with_task_specify": False,
}
# Create and return the society
society = RolePlaying(
**task_kwargs,
user_role_name="user",
user_agent_kwargs=user_agent_kwargs,
assistant_role_name="assistant",
assistant_agent_kwargs=assistant_agent_kwargs,
)
return society
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
<<<<<<<< HEAD:community_usecase/Smart_city_research/main.py
default_task = """Conduct a comprehensive research on smart city technologies and implementations:
1. Search for the latest smart city initiatives in major global cities and identify common technologies they use.
2. Browse official websites of 2-3 leading smart city technology providers to understand their key solutions.
3. Analyze how IoT sensors, AI, and data analytics are integrated in traffic management and public transportation systems.
4. Research case studies of successful smart city implementations that reduced energy consumption and carbon emissions.
5. Investigate privacy and security concerns in smart city data collection.
6. Create a brief report documenting your findings, including:
- Top 5 emerging smart city technologies
- Success metrics used to evaluate smart city projects
- Implementation challenges and solutions
- Future trends in smart urban planning
Save the report as 'smart_city_research.md' in the current directory with properly formatted sections.
"""
========
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
>>>>>>>> main:examples/run_claude.py
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task
# Construct and run the society
society = construct_society(task)
answer, chat_history, token_count = run_society(society)
# Output the result
print(f"\033[94mAnswer: {answer}\033[0m")
if __name__ == "__main__":
main()

View File

@@ -127,7 +127,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -142,7 +142,7 @@ def construct_society(question: str) -> OwlRolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Example research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Construct and run the society
# Note: This configuration uses GROQ_LLAMA_3_3_70B for tool-intensive roles (assistant, web, planning, video, image)

View File

@@ -19,6 +19,7 @@ from camel.toolkits import (
SearchToolkit,
BrowserToolkit,
FileWriteToolkit,
CodeExecutionToolkit,
)
from camel.types import ModelPlatformType, ModelType
from camel.logger import set_log_level
@@ -78,6 +79,7 @@ def construct_society(question: str) -> RolePlaying:
web_agent_model=models["browsing"],
planning_agent_model=models["planning"],
).get_tools(),
*CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(),
SearchToolkit().search_duckduckgo,
SearchToolkit().search_wiki,
*FileWriteToolkit(output_dir="./").get_tools(),
@@ -108,7 +110,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -131,7 +131,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

135
examples/run_novita_ai.py Normal file
View File

@@ -0,0 +1,135 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import sys
import pathlib
from dotenv import load_dotenv
from camel.models import ModelFactory
from camel.toolkits import (
CodeExecutionToolkit,
ExcelToolkit,
ImageAnalysisToolkit,
BrowserToolkit,
FileWriteToolkit,
)
from camel.types import ModelPlatformType, ModelType
from camel.logger import set_log_level
from camel.societies import RolePlaying
from owl.utils import run_society, DocumentProcessingToolkit
base_dir = pathlib.Path(__file__).parent.parent
env_path = base_dir / "owl" / ".env"
load_dotenv(dotenv_path=str(env_path))
set_log_level(level="DEBUG")
def construct_society(question: str) -> RolePlaying:
r"""Construct a society of agents based on the given question.
Args:
question (str): The task or question to be addressed by the society.
Returns:
RolePlaying: A configured society of agents ready to address the question.
"""
# Create models for different components
models = {
"user": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
"assistant": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
"browsing": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
"planning": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
"image": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
"document": ModelFactory.create(
model_platform=ModelPlatformType.NOVITA,
model_type=ModelType.NOVITA_LLAMA_4_MAVERICK_17B,
model_config_dict={"temperature": 0},
),
}
# Configure toolkits
tools = [
*BrowserToolkit(
headless=False, # Set to True for headless mode (e.g., on remote servers)
web_agent_model=models["browsing"],
planning_agent_model=models["planning"],
).get_tools(),
*CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(),
*ImageAnalysisToolkit(model=models["image"]).get_tools(),
*ExcelToolkit().get_tools(),
*DocumentProcessingToolkit(model=models["document"]).get_tools(),
*FileWriteToolkit(output_dir="./").get_tools(),
]
# Configure agent roles and parameters
user_agent_kwargs = {"model": models["user"]}
assistant_agent_kwargs = {"model": models["assistant"], "tools": tools}
# Configure task parameters
task_kwargs = {
"task_prompt": question,
"with_task_specify": False,
}
# Create and return the society
society = RolePlaying(
**task_kwargs,
user_role_name="user",
user_agent_kwargs=user_agent_kwargs,
assistant_role_name="assistant",
assistant_agent_kwargs=assistant_agent_kwargs,
)
return society
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task
# Construct and run the society
society = construct_society(task)
answer, chat_history, token_count = run_society(society)
# Output the result
print(f"\033[94mAnswer: {answer}\033[0m")
if __name__ == "__main__":
main()

View File

@@ -126,7 +126,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -129,7 +129,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Example research question
default_task = "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -118,7 +118,7 @@ def construct_society(question: str) -> RolePlaying:
def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file."
default_task = "Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task."
# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

View File

@@ -32,6 +32,9 @@ OPENAI_API_KEY="Your_Key"
#GOOGLE GEMINI API (https://ai.google.dev/gemini-api/docs/api-key)
# GEMINI_API_KEY ="Your_Key"
# NOVITA API (https://novita.ai/settings/key-management?utm_source=github_owl&utm_medium=github_readme&utm_campaign=github_link)
# NOVITA_API_KEY="Your_Key"
#===========================================
# Tools & Services API
#===========================================

View File

@@ -12,13 +12,13 @@
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
from camel.loaders import UnstructuredIO
from camel.toolkits.base import BaseToolkit
from camel.toolkits.function_tool import FunctionTool
from camel.toolkits import ImageAnalysisToolkit, ExcelToolkit
from camel.utils import retry_on_error
from camel.logger import get_logger
from camel.models import BaseModelBackend
from docx2markdown._docx_to_markdown import docx_to_markdown
from chunkr_ai import Chunkr
import requests
import mimetypes
@@ -29,6 +29,7 @@ import os
import subprocess
import xmltodict
import nest_asyncio
import traceback
nest_asyncio.apply()
@@ -52,6 +53,8 @@ class DocumentProcessingToolkit(BaseToolkit):
if cache_dir:
self.cache_dir = cache_dir
self.uio = UnstructuredIO()
@retry_on_error()
def extract_document_content(self, document_path: str) -> Tuple[bool, str]:
r"""Extract the content of a given document (or url) and return the processed text.
@@ -63,7 +66,6 @@ class DocumentProcessingToolkit(BaseToolkit):
Returns:
Tuple[bool, str]: A tuple containing a boolean indicating whether the document was processed successfully, and the content of the document (if success).
"""
import asyncio
logger.debug(
f"Calling extract_document_content function with document_path=`{document_path}`"
@@ -115,71 +117,35 @@ class DocumentProcessingToolkit(BaseToolkit):
return True, content
if self._is_webpage(document_path):
extracted_text = self._extract_webpage_content(document_path)
return True, extracted_text
try:
extracted_text = self._extract_webpage_content(document_path)
return True, extracted_text
except Exception:
try:
elements = self.uio.parse_file_or_url(document_path)
if elements is None:
logger.error(f"Failed to parse the document: {document_path}.")
return False, f"Failed to parse the document: {document_path}."
else:
# Convert elements list to string
elements_str = "\n".join(str(element) for element in elements)
return True, elements_str
except Exception:
return False, "Failed to extract content from the webpage."
else:
# judge if url
parsed_url = urlparse(document_path)
is_url = all([parsed_url.scheme, parsed_url.netloc])
if not is_url:
if not os.path.exists(document_path):
return False, f"Document not found at path: {document_path}."
# if is docx file, use docx2markdown to convert it
if document_path.endswith(".docx"):
if is_url:
tmp_path = self._download_file(document_path)
else:
tmp_path = document_path
file_name = os.path.basename(tmp_path)
md_file_path = f"{file_name}.md"
docx_to_markdown(tmp_path, md_file_path)
# load content of md file
with open(md_file_path, "r") as f:
extracted_text = f.read()
f.close()
return True, extracted_text
try:
result = asyncio.run(self._extract_content_with_chunkr(document_path))
return True, result
elements = self.uio.parse_file_or_url(document_path)
if elements is None:
logger.error(f"Failed to parse the document: {document_path}.")
return False, f"Failed to parse the document: {document_path}."
else:
# Convert elements list to string
elements_str = "\n".join(str(element) for element in elements)
return True, elements_str
except Exception as e:
logger.warning(
f"Error occurred while using Chunkr to process document: {e}"
)
if document_path.endswith(".pdf"):
# try using pypdf to extract text from pdf
try:
from PyPDF2 import PdfReader
if is_url:
tmp_path = self._download_file(document_path)
document_path = tmp_path
# Open file in binary mode for PdfReader
f = open(document_path, "rb")
reader = PdfReader(f)
extracted_text = ""
for page in reader.pages:
extracted_text += page.extract_text()
f.close()
return True, extracted_text
except Exception as pdf_error:
logger.error(
f"Error occurred while processing pdf: {pdf_error}"
)
return (
False,
f"Error occurred while processing pdf: {pdf_error}",
)
# If we get here, either it's not a PDF or PDF processing failed
logger.error(f"Error occurred while processing document: {e}")
logger.error(traceback.format_exc())
return False, f"Error occurred while processing document: {e}"
def _is_webpage(self, url: str) -> bool:

View File

@@ -245,6 +245,7 @@ MODULE_DESCRIPTIONS = {
"run": "Default mode: Using OpenAI model's default agent collaboration mode, suitable for most tasks.",
"run_mini": "Using OpenAI model with minimal configuration to process tasks",
"run_gemini": "Using Gemini model to process tasks",
"run_claude": "Using Claude model to process tasks",
"run_deepseek_zh": "Using deepseek model to process Chinese tasks",
"run_mistral": "Using Mistral models to process tasks",
"run_openai_compatible_model": "Using openai compatible model to process tasks",
@@ -255,6 +256,7 @@ MODULE_DESCRIPTIONS = {
"run_groq": "Using groq model to process tasks",
"run_ppio": "Using ppio model to process tasks",
"run_together_ai": "Using together ai model to process tasks",
"run_novita_ai": "Using novita ai model to process tasks",
}
@@ -640,6 +642,8 @@ def get_api_guide(key: str) -> str:
return "https://chunkr.ai/"
elif "firecrawl" in key_lower:
return "https://www.firecrawl.dev/"
elif "novita" in key_lower:
return "https://novita.ai/settings/key-management?utm_source=github_owl&utm_medium=github_readme&utm_campaign=github_link"
else:
return ""
@@ -1089,7 +1093,7 @@ def create_ui():
label="Question",
elem_id="question_input",
show_copy_button=True,
value="Open Google search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file.",
value="Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task.",
)
# Enhanced module selection dropdown
@@ -1124,7 +1128,7 @@ def create_ui():
# Example questions
examples = [
"Open Google search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file.",
"Open Brave search, summarize the github stars, fork counts, etc. of camel-ai's camel framework, and write the numbers into a python file using the plot package, save it locally, and run the generated python file. Note: You have been provided with the necessary tools to complete this task.",
"Browse Amazon and find a product that is attractive to programmers. Please provide the product name and price",
"Write a hello world python file and save it locally",
]

View File

@@ -245,6 +245,7 @@ MODULE_DESCRIPTIONS = {
"run": "默认模式使用OpenAI模型的默认的智能体协作模式适合大多数任务。",
"run_mini": "使用使用OpenAI模型最小化配置处理任务",
"run_gemini": "使用 Gemini模型处理任务",
"run_claude": "使用 Claude模型处理任务",
"run_deepseek_zh": "使用eepseek模型处理中文任务",
"run_openai_compatible_model": "使用openai兼容模型处理任务",
"run_ollama": "使用本地ollama模型处理任务",
@@ -254,6 +255,7 @@ MODULE_DESCRIPTIONS = {
"run_groq": "使用groq模型处理任务",
"run_ppio": "使用ppio模型处理任务",
"run_together_ai": "使用together ai模型处理任务",
"run_novita_ai": "使用novita ai模型处理任务",
}
@@ -623,6 +625,8 @@ def get_api_guide(key: str) -> str:
return "https://chunkr.ai/"
elif "firecrawl" in key_lower:
return "https://www.firecrawl.dev/"
elif "novita" in key_lower:
return "https://novita.ai/settings/key-management?utm_source=github_owl&utm_medium=github_readme&utm_campaign=github_link"
else:
return ""

68
pyproject.toml Normal file
View File

@@ -0,0 +1,68 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "owl"
version = "0.0.1"
description = "Optimized Workforce Learning for General Multi-Agent Assistance in Real-World Task Automation"
authors = [{ name = "CAMEL-AI.org" }]
requires-python = ">=3.10,<3.13"
readme = "README.md"
license = "Apache-2.0"
keywords = [
"optimized-workforce-learning",
"multi-agent-assistance",
"task-automation",
"real-world-tasks",
"artificial-intelligence",
"agent-collaboration",
"workforce-optimization",
"learning-systems"
]
dependencies = [
"camel-ai[owl]==0.2.52",
"docx2markdown>=0.1.1",
"gradio>=3.50.2",
"mcp-simple-arxiv==0.2.2",
"mcp-server-fetch==2025.1.17",
"xmltodict>=0.14.2",
]
[project.urls]
Homepage = "https://www.camel-ai.org/"
Repository = "https://github.com/camel-ai/owl"
Documentation = "https://docs.camel-ai.org"
[tool.hatch.build.targets.wheel]
packages = ["owl"]
[tool.mypy]
python_version = "3.11"
warn_return_any = false
warn_unused_configs = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
check_untyped_defs = false
disallow_untyped_decorators = false
no_implicit_optional = false
strict_optional = false
ignore_missing_imports = true
allow_redefinition = true
disable_error_code = ["assignment", "arg-type", "return-value"]
[[tool.mypy.overrides]]
module = "camel.*"
ignore_missing_imports = true
follow_imports = "skip"
[[tool.mypy.overrides]]
module = "utils"
ignore_missing_imports = true
[tool.codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = '.git*,*.pdf,*.lock'
check-hidden = true
ignore-regex = '\bBrin\b'
ignore-words-list = 'datas'

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
camel-ai[owl]==0.2.52
docx2markdown>=0.1.1
gradio>=3.50.2
mcp-simple-arxiv==0.2.2
mcp-server-fetch==2025.1.17

2563
uv.lock generated

File diff suppressed because it is too large Load Diff