diff --git a/.container/Dockerfile b/.container/Dockerfile index ca74882..0245397 100644 --- a/.container/Dockerfile +++ b/.container/Dockerfile @@ -41,11 +41,11 @@ COPY assets/ ./assets/ COPY README_zh.md . # Create startup script -RUN echo '#!/bin/bash\nxvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" python "$@"' > /usr/local/bin/xvfb-python && \ +RUN printf '#!/bin/bash\nxvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" python "$@"' > /usr/local/bin/xvfb-python && \ chmod +x /usr/local/bin/xvfb-python # Create welcome script -RUN echo '#!/bin/bash\necho "Welcome to the OWL Project Docker environment!"\necho "Welcome to OWL Project Docker environment!"\necho ""\necho "Available scripts:"\nls -1 *.py | grep -v "__" | sed "s/^/- /"\necho ""\necho "Run examples:"\necho " xvfb-python run.py # Run default script"\necho " xvfb-python run_deepseek_example.py # Run DeepSeek example"\necho ""\necho "Or use custom query:"\necho " xvfb-python run.py \"Your question\""\necho ""' > /usr/local/bin/owl-welcome && \ +RUN printf '#!/bin/bash\necho "Welcome to the OWL Project Docker environment!"\necho "Welcome to OWL Project Docker environment!"\necho ""\necho "Available scripts:"\nls -1 *.py | grep -v "__" | sed "s/^/- /"\necho ""\necho "Run examples:"\necho " xvfb-python run.py # Run default script"\necho " xvfb-python run_deepseek_example.py # Run DeepSeek example"\necho ""\necho "Or use custom query:"\necho " xvfb-python run.py \"Your question\""\necho ""' > /usr/local/bin/owl-welcome && \ chmod +x /usr/local/bin/owl-welcome # Set working directory diff --git a/README.md b/README.md index 3088b58..505c0cc 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,19 @@ Our vision is to revolutionize how AI agents collaborate to solve real-world tas

+
+

+ 🧩 NEW: COMMUNITY AGENT CHALLENGES! 🧩 +

+

+ Showcase your creativity by designing unique challenges for AI agents!
+ Join our community and see your innovative ideas tackled by cutting-edge AI. +

+

+ View & Submit Challenges +

+
+

🎉 Latest Major Update - March 15, 2025 @@ -113,6 +126,10 @@ Our vision is to revolutionize how AI agents collaborate to solve real-world tas

+- **[2025.03.26]**: Supported Gemini 2.5 Pro, added example run code +- **[2025.03.21]**: Integrated OpenRouter model platform, fix bug with Gemini tool calling +- **[2025.03.20]**: Accept header in MCP Toolkit, support automatic playwright installation +- **[2025.03.16]**: Support Bing search, Baidu search - **[2025.03.12]**: Added Bocha search in SearchToolkit, integrated Volcano Engine model platform, and enhanced Azure and OpenAI Compatible models with structured output and tool calling. - **[2025.03.11]**: We added MCPToolkit, FileWriteToolkit, and TerminalToolkit to enhance OWL agents with MCP tool calling, file writing capabilities, and terminal command execution. - **[2025.03.09]**: We added a web-based user interface that makes it easier to interact with the system. @@ -126,6 +143,8 @@ https://github.com/user-attachments/assets/2a2a825d-39ea-45c5-9ba1-f9d58efbc372 https://private-user-images.githubusercontent.com/55657767/420212194-e813fc05-136a-485f-8df3-f10d9b4e63ec.mp4 +This video demonstrates how to install OWL locally and showcases its capabilities as a cutting-edge framework for multi-agent collaboration: https://www.youtube.com/watch?v=8XlqVyAZOr8 + # ✨️ Core Features - **Online Search**: Support for multiple search engines (including Wikipedia, Google, DuckDuckGo, Baidu, Bocha, etc.) for real-time information retrieval and knowledge acquisition. @@ -367,6 +386,9 @@ python examples/run_deepseek_zh.py # Run with other OpenAI-compatible models python examples/run_openai_compatible_model.py +# Run with Gemini model +python examples/run_gemini.py + # Run with Azure OpenAI python examples/run_azure_openai.py @@ -595,9 +617,11 @@ We welcome contributions from the community! Here's how you can help: 3. Submit pull requests with your improvements **Current Issues Open for Contribution:** -- [#1905](https://github.com/camel-ai/camel/issues/1905) -- [#1712](https://github.com/camel-ai/camel/issues/1712) - +- [#362](https://github.com/camel-ai/owl/issues/362) +- [#1945](https://github.com/camel-ai/camel/issues/1945) +- [#1925](https://github.com/camel-ai/camel/issues/1925) +- [#1915](https://github.com/camel-ai/camel/issues/1915) +- [#1970](https://github.com/camel-ai/camel/issues/1970) To take on an issue, simply leave a comment stating your interest. @@ -606,7 +630,7 @@ Join us ([*Discord*](https://discord.camel-ai.org/) or [*WeChat*](https://ghli.o Join us for further discussions! -![](./assets/community.jpeg) +![](./assets/community.jpg) # ❓ FAQ diff --git a/README_ja.md b/README_ja.md new file mode 100644 index 0000000..a704a56 --- /dev/null +++ b/README_ja.md @@ -0,0 +1,680 @@ +

+ 🦉 OWL: 労働力学習の最適化による、現実世界のタスク自動化における一般的なマルチエージェント支援 +

+ + +
+ +[![ドキュメント][docs-image]][docs-url] +[![Discord][discord-image]][discord-url] +[![X][x-image]][x-url] +[![Reddit][reddit-image]][reddit-url] +[![Wechat][wechat-image]][wechat-url] +[![Wechat][owl-image]][owl-url] +[![Hugging Face][huggingface-image]][huggingface-url] +[![Star][star-image]][star-url] +[![パッケージライセンス][package-license-image]][package-license-url] + + +
+ + +
+ +
+

+ +[中文阅读](https://github.com/camel-ai/owl/tree/main/README_zh.md) | +[コミュニティ](https://github.com/camel-ai/owl#community) | +[インストール](#️-installation) | +[例](https://github.com/camel-ai/owl/tree/main/owl) | +[論文](https://arxiv.org/abs/2303.17760) | +[引用](https://github.com/camel-ai/owl#citation) | +[貢献](https://github.com/camel-ai/owl/graphs/contributors) | +[CAMEL-AI](https://www.camel-ai.org/) + +

+ +
+

+ 🏆 OWLはGAIAベンチマークで58.18の平均スコアを達成し、オープンソースフレームワークの中で🏅️ #1にランクインしました! 🏆 +

+
+ +
+ +🦉 OWLは、タスク自動化の限界を押し広げる最先端のマルチエージェント協力フレームワークであり、[CAMEL-AIフレームワーク](https://github.com/camel-ai/camel)の上に構築されています。 + +私たちのビジョンは、AIエージェントが現実のタスクを解決するためにどのように協力するかを革命的に変えることです。動的なエージェントの相互作用を活用することで、OWLは多様な分野でより自然で効率的かつ堅牢なタスク自動化を実現します。 + +
+ +![](./assets/owl_architecture.png) + +
+ + +
+ + +# 📋 目次 + +- [📋 目次](#-目次) +- [🔥 ニュース](#-ニュース) +- [🎬 デモビデオ](#-デモビデオ) +- [✨️ コア機能](#-コア機能) +- [🛠️ インストール](#️-インストール) +- [🚀 クイックスタート](#-クイックスタート) +- [🧰 ツールキットと機能](#-ツールキットと機能) + - [モデルコンテキストプロトコル (MCP)](#モデルコンテキストプロトコル-mcp) +- [🌐 ウェブインターフェース](#-ウェブインターフェース) +- [🧪 実験](#-実験) +- [⏱️ 将来の計画](#️-将来の計画) +- [📄 ライセンス](#-ライセンス) +- [🖊️ 引用](#️-引用) +- [🤝 貢献](#-貢献) +- [🔥 コミュニティ](#-コミュニティ) +- [❓ FAQ](#-faq) +- [📚 CAMEL依存関係の探索](#-camel依存関係の探索) +- [⭐ Star History](#-star-history) + +# 🔥 ニュース + + +
+

+ 🌟🌟🌟 コミュニティ用ケースの募集! 🌟🌟🌟 +

+

+ コミュニティにOWLの革新的なユースケースを提供してもらうための招待です!
+ トップ10の提出物には特別なコミュニティギフトと認識が与えられます。 +

+

+ 詳細と提出 +

+

+ 提出期限:2025年3月31日 +

+
+ +
+

+ 🧩 新機能:コミュニティエージェントチャレンジ! 🧩 +

+

+ AIエージェントのためのユニークなチャレンジをデザインして、あなたの創造力を発揮してください!
+ コミュニティに参加して、最先端のAIによってあなたの革新的なアイデアが実現されるのを見てみましょう。 +

+

+ チャレンジの表示と提出 +

+
+ +
+

+ 🎉 最新の主要アップデート - 2025年3月15日 +

+

+ 重要な改善点: +

+ 今すぐ試して、タスク自動化の改善されたパフォーマンスを体験してください! +

+
+ +- **[2025.03.21]**: OpenRouterモデルプラットフォームを統合し、Geminiツール呼び出しのバグを修正 +- **[2025.03.20]**: MCPツールキットにAcceptヘッダーを追加し、Playwrightの自動インストールをサポート +- **[2025.03.16]**: Bing検索、Baidu検索をサポート +- **[2025.03.12]**: SearchToolkitにBocha検索を追加し、Volcano Engineモデルプラットフォームを統合し、AzureおよびOpenAI互換モデルの構造化出力とツール呼び出し機能を強化 +- **[2025.03.11]**: MCPToolkit、FileWriteToolkit、およびTerminalToolkitを追加し、MCPツール呼び出し、ファイル書き込み機能、およびターミナルコマンド実行機能を強化 +- **[2025.03.09]**: システムとの対話を容易にするためのウェブベースのユーザーインターフェースを追加 +- **[2025.03.07]**: 🦉 OWLプロジェクトのコードベースをオープンソース化 +- **[2025.03.03]**: OWLはGAIAベンチマークで58.18のスコアを達成し、オープンソースフレームワークの中で1位を獲得 + +# 🎬 デモビデオ + +https://github.com/user-attachments/assets/2a2a825d-39ea-45c5-9ba1-f9d58efbc372 + +https://private-user-images.githubusercontent.com/55657767/420212194-e813fc05-136a-485f-8df3-f10d9b4e63ec.mp4 + +# ✨️ コア機能 + +- **オンライン検索**:複数の検索エンジン(Wikipedia、Google、DuckDuckGo、Baidu、Bochaなど)をサポートし、リアルタイムの情報検索と知識取得を実現 +- **マルチモーダル処理**:インターネットまたはローカルのビデオ、画像、音声データの処理をサポート +- **ブラウザ自動化**:Playwrightフレームワークを利用してブラウザの操作をシミュレートし、スクロール、クリック、入力処理、ダウンロード、ナビゲーションなどをサポート +- **ドキュメント解析**:Word、Excel、PDF、PowerPointファイルからコンテンツを抽出し、テキストまたはMarkdown形式に変換 +- **コード実行**:Pythonコードを記述してインタープリタを使用して実行 +- **組み込みツールキット**:包括的な組み込みツールキットにアクセス可能 + - **モデルコンテキストプロトコル (MCP)**:AIモデルとさまざまなツールやデータソースとの相互作用を標準化するユニバーサルプロトコルレイヤー + - **コアツールキット**:ArxivToolkit、AudioAnalysisToolkit、CodeExecutionToolkit、DalleToolkit、DataCommonsToolkit、ExcelToolkit、GitHubToolkit、GoogleMapsToolkit、GoogleScholarToolkit、ImageAnalysisToolkit、MathToolkit、NetworkXToolkit、NotionToolkit、OpenAPIToolkit、RedditToolkit、SearchToolkit、SemanticScholarToolkit、SymPyToolkit、VideoAnalysisToolkit、WeatherToolkit、BrowserToolkitなど、専門的なタスクに対応する多くのツールキット + +# 🛠️ インストール + +OWLは、ワークフロープリファレンスに合わせた複数のインストール方法をサポートしています。最適なオプションを選択してください。 + +## オプション1:uvを使用する(推奨) + +```bash +# GitHubリポジトリをクローン +git clone https://github.com/camel-ai/owl.git + +# プロジェクトディレクトリに移動 +cd owl + +# uvがインストールされていない場合はインストール +pip install uv + +# 仮想環境を作成し、依存関係をインストール +# Python 3.10、3.11、3.12の使用をサポート +uv venv .venv --python=3.10 + +# 仮想環境をアクティブ化 +# macOS/Linuxの場合 +source .venv/bin/activate +# Windowsの場合 +.venv\Scripts\activate + +# すべての依存関係を含むCAMELをインストール +uv pip install -e . + +# 完了したら仮想環境を終了 +deactivate +``` + +## オプション2:venvとpipを使用する + +```bash +# GitHubリポジトリをクローン +git clone https://github.com/camel-ai/owl.git + +# プロジェクトディレクトリに移動 +cd owl + +# 仮想環境を作成 +# Python 3.10の場合(3.11、3.12でも動作) +python3.10 -m venv .venv + +# 仮想環境をアクティブ化 +# macOS/Linuxの場合 +source .venv/bin/activate +# Windowsの場合 +.venv\Scripts\activate + +# requirements.txtからインストール +pip install -r requirements.txt --use-pep517 +``` + +## オプション3:condaを使用する + +```bash +# GitHubリポジトリをクローン +git clone https://github.com/camel-ai/owl.git + +# プロジェクトディレクトリに移動 +cd owl + +# conda環境を作成 +conda create -n owl python=3.10 + +# conda環境をアクティブ化 +conda activate owl + +# オプション1:パッケージとしてインストール(推奨) +pip install -e . + +# オプション2:requirements.txtからインストール +pip install -r requirements.txt --use-pep517 + +# 完了したらconda環境を終了 +conda deactivate +``` + +## **環境変数の設定** + +OWLは、さまざまなサービスと対話するために複数のAPIキーを必要とします。`owl/.env_template`ファイルには、すべての必要なAPIキーのプレースホルダーと、それらのサービスに登録するためのリンクが含まれています。 + +### オプション1:`.env`ファイルを使用する(推奨) + +1. **テンプレートをコピーして名前を変更**: + ```bash + cd owl + cp .env_template .env + ``` + +2. **APIキーを設定**: + お好みのテキストエディタで`.env`ファイルを開き、対応するフィールドにAPIキーを挿入します。 + + > **注意**:最小限の例(`examples/run_mini.py`)の場合、LLM APIキー(例:`OPENAI_API_KEY`)のみを設定する必要があります。 + +### オプション2:環境変数を直接設定 + +または、ターミナルで環境変数を直接設定することもできます: + +- **macOS/Linux (Bash/Zsh)**: + ```bash + export OPENAI_API_KEY="your-openai-api-key-here" + ``` + +- **Windows (コマンドプロンプト)**: + ```batch + set OPENAI_API_KEY="your-openai-api-key-here" + ``` + +- **Windows (PowerShell)**: + ```powershell + $env:OPENAI_API_KEY = "your-openai-api-key-here" + ``` + +> **注意**:ターミナルで直接設定された環境変数は、現在のセッションでのみ有効です。 + + + +## **Dockerでの実行** + +OWLはDockerを使用して簡単にデプロイでき、異なるプラットフォーム間で一貫した環境を提供します。 + +### **セットアップ手順** + +```bash +# リポジトリをクローン +git clone https://github.com/camel-ai/owl.git +cd owl + +# 環境変数を設定 +cp owl/.env_template owl/.env +# .envファイルを編集し、APIキーを入力 +``` + +### **デプロイメントオプション** + +#### **オプション1:事前構築されたイメージを使用する(推奨)** + +```bash +# このオプションはDocker Hubから即使用可能なイメージをダウンロードします +# 最速であり、ほとんどのユーザーに推奨されます +docker-compose up -d + +# コンテナ内でOWLを実行 +docker-compose exec owl bash +cd .. && source .venv/bin/activate +playwright install-deps +xvfb-python examples/run.py +``` + +#### **オプション2:ローカルでイメージを構築する** + +```bash +# Dockerイメージをカスタマイズする必要があるユーザーやDocker Hubにアクセスできないユーザー向け: +# 1. docker-compose.ymlを開く +# 2. "image: mugglejinx/owl:latest"行をコメントアウト +# 3. "build:"セクションとそのネストされたプロパティをコメント解除 +# 4. 次に実行: +docker-compose up -d --build + +# コンテナ内でOWLを実行 +docker-compose exec owl bash +cd .. && source .venv/bin/activate +playwright install-deps +xvfb-python examples/run.py +``` + +#### **オプション3:便利なスクリプトを使用する** + +```bash +# コンテナディレクトリに移動 +cd .container + +# スクリプトを実行可能にし、Dockerイメージを構築 +chmod +x build_docker.sh +./build_docker.sh + +# 質問を使用してOWLを実行 +./run_in_docker.sh "your question" +``` + +### **MCPデスクトップコマンダーのセットアップ** + +Docker内でMCPデスクトップコマンダーを使用する場合、次を実行: + +```bash +npx -y @wonderwhy-er/desktop-commander setup --force-file-protocol +``` + +クロスプラットフォームサポート、最適化された構成、トラブルシューティングなど、詳細なDocker使用手順については、[DOCKER_README.md](.container/DOCKER_README_en.md)を参照してください。 + +# 🚀 クイックスタート + +## 基本的な使用法 + +インストールと環境変数の設定が完了したら、すぐにOWLを使用できます: + +```bash +python examples/run.py +``` + +## 異なるモデルでの実行 + +### モデルの要件 + +- **ツール呼び出し**:OWLは、さまざまなツールキットと対話するために強力なツール呼び出し機能を持つモデルを必要とします。モデルはツールの説明を理解し、適切なツール呼び出しを生成し、ツールの出力を処理する必要があります。 + +- **マルチモーダル理解**:ウェブインタラクション、画像解析、ビデオ処理を含むタスクには、視覚コンテンツとコンテキストを解釈するためのマルチモーダル機能を持つモデルが必要です。 + +#### サポートされているモデル + +AIモデルの設定に関する情報については、[CAMELモデルドキュメント](https://docs.camel-ai.org/key_modules/models.html#supported-model-platforms-in-camel)を参照してください。 + +> **注意**:最適なパフォーマンスを得るために、OpenAIモデル(GPT-4以降のバージョン)を強く推奨します。私たちの実験では、他のモデルは複雑なタスクやベンチマークで著しく低いパフォーマンスを示すことがあり、特に高度なマルチモーダル理解とツール使用を必要とするタスクでは顕著です。 + +OWLはさまざまなLLMバックエンドをサポートしていますが、機能はモデルのツール呼び出しおよびマルチモーダル機能に依存する場合があります。以下のスクリプトを使用して、異なるモデルで実行できます: + +```bash +# Qwenモデルで実行 +python examples/run_qwen_zh.py + +# Deepseekモデルで実行 +python examples/run_deepseek_zh.py + +# 他のOpenAI互換モデルで実行 +python examples/run_openai_compatible_model.py + +# Azure OpenAIで実行 +python examples/run_azure_openai.py + +# Ollamaで実行 +python examples/run_ollama.py +``` + +LLM APIキーのみを必要とするシンプルなバージョンについては、最小限の例を試してみてください: + +```bash +python examples/run_mini.py +``` + +`examples/run.py`スクリプトを変更して、独自のタスクでOWLエージェントを実行できます: + +```python +# 独自のタスクを定義 +task = "Task description here." + +society = construct_society(question) +answer, chat_history, token_count = run_society(society) + +print(f"\033[94mAnswer: {answer}\033[0m") +``` + +ファイルをアップロードする場合は、質問と一緒にファイルパスを提供するだけです: + +```python +# ローカルファイルを使用したタスク(例:ファイルパス:`tmp/example.docx`) +task = "What is in the given DOCX file? Here is the file path: tmp/example.docx" + +society = construct_society(question) +answer, chat_history, token_count = run_society(society) +print(f"\033[94mAnswer: {answer}\033[0m") +``` + +OWLは自動的にドキュメント関連のツールを呼び出してファイルを処理し、回答を抽出します。 + + +### 例のタスク + +以下のタスクをOWLで試してみてください: + +- "Apple Inc.の最新の株価を調べる" +- "気候変動に関する最近のツイートの感情を分析する" +- "このPythonコードのデバッグを手伝ってください:[ここにコードを貼り付け]" +- "この研究論文の主要なポイントを要約してください:[論文のURL]" +- "このデータセットのデータビジュアライゼーションを作成してください:[データセットのパス]" + +# 🧰 ツールキットと機能 + +## モデルコンテキストプロトコル(MCP) + +OWLのMCP統合は、AIモデルがさまざまなツールやデータソースと相互作用するための標準化された方法を提供します: + +MCPを使用する前に、まずNode.jsをインストールする必要があります。 +### **Node.jsのインストール** +### Windows + +公式インストーラーをダウンロード:[Node.js](https://nodejs.org/en)。 + +インストール中に「Add to PATH」オプションをチェックします。 + +### Linux +```bash +sudo apt update +sudo apt install nodejs npm -y +``` +### Mac +```bash +brew install node +``` + +### **Playwright MCPサービスのインストール** +```bash +npm install -g @executeautomation/playwright-mcp-server +npx playwright install-deps +``` + +`examples/run_mcp.py`の包括的なMCP例を試して、これらの機能を実際に体験してください! + +## 利用可能なツールキット + +> **重要**:ツールキットを効果的に使用するには、強力なツール呼び出し機能を持つモデルが必要です。マルチモーダルツールキット(Web、画像、ビデオ)には、マルチモーダル理解機能を持つモデルも必要です。 + +OWLはさまざまなツールキットをサポートしており、スクリプト内の`tools`リストを変更してカスタマイズできます: + +```python +# ツールキットの設定 +tools = [ + *BrowserToolkit(headless=False).get_tools(), # ブラウザ自動化 + *VideoAnalysisToolkit(model=models["video"]).get_tools(), + *AudioAnalysisToolkit().get_tools(), # OpenAIキーが必要 + *CodeExecutionToolkit(sandbox="subprocess").get_tools(), + *ImageAnalysisToolkit(model=models["image"]).get_tools(), + SearchToolkit().search_duckduckgo, + SearchToolkit().search_google, # 利用できない場合はコメントアウト + SearchToolkit().search_wiki, + SearchToolkit().search_bocha, + SearchToolkit().search_baidu, + *ExcelToolkit().get_tools(), + *DocumentProcessingToolkit(model=models["document"]).get_tools(), + *FileWriteToolkit(output_dir="./").get_tools(), +] +``` + +## 利用可能なツールキット + +主要なツールキットには以下が含まれます: + +### マルチモーダルツールキット(マルチモーダルモデル機能が必要) +- **BrowserToolkit**:ウェブインタラクションとナビゲーションのためのブラウザ自動化 +- **VideoAnalysisToolkit**:ビデオ処理とコンテンツ分析 +- **ImageAnalysisToolkit**:画像解析と解釈 + +### テキストベースのツールキット +- **AudioAnalysisToolkit**:音声処理(OpenAI APIが必要) +- **CodeExecutionToolkit**:Pythonコードの実行と評価 +- **SearchToolkit**:ウェブ検索(Google、DuckDuckGo、Wikipedia) +- **DocumentProcessingToolkit**:ドキュメント解析(PDF、DOCXなど) + +その他の専門ツールキット:ArxivToolkit、GitHubToolkit、GoogleMapsToolkit、MathToolkit、NetworkXToolkit、NotionToolkit、RedditToolkit、WeatherToolkitなど。完全なツールキットのリストについては、[CAMELツールキットドキュメント](https://docs.camel-ai.org/key_modules/tools.html#built-in-toolkits)を参照してください。 + +## カスタマイズ設定 + +利用可能なツールをカスタマイズするには: + +```python +# 1. ツールキットをインポート +from camel.toolkits import BrowserToolkit, SearchToolkit, CodeExecutionToolkit + +# 2. ツールリストを設定 +tools = [ + *BrowserToolkit(headless=True).get_tools(), + SearchToolkit().search_wiki, + *CodeExecutionToolkit(sandbox="subprocess").get_tools(), +] + +# 3. アシスタントエージェントに渡す +assistant_agent_kwargs = {"model": models["assistant"], "tools": tools} +``` + +必要なツールキットのみを選択することで、パフォーマンスを最適化し、リソース使用量を削減できます。 + +# 🌐 ウェブインターフェース + +
+

+ 🚀 強化されたウェブインターフェースが利用可能になりました! +

+

+ 最新のアップデートでシステムの安定性とパフォーマンスが向上しました。 + 使いやすいインターフェースを通じて、OWLの力を探索し始めましょう! +

+
+ +## ウェブUIの起動 + +```bash +# 中国語版を起動 +python owl/webapp_zh.py + +# 英語版を起動 +python owl/webapp.py +``` + +## 機能 + +- **簡単なモデル選択**:異なるモデル(OpenAI、Qwen、DeepSeekなど)を選択 +- **環境変数管理**:UIから直接APIキーやその他の設定を構成 +- **インタラクティブなチャットインターフェース**:使いやすいインターフェースを通じてOWLエージェントと対話 +- **タスク履歴**:対話の履歴と結果を表示 + +ウェブインターフェースはGradioを使用して構築されており、ローカルマシン上で実行されます。設定したモデルAPI呼び出しに必要なデータ以外は外部サーバーに送信されません。 + +# 🧪 実験 + +OWLのGAIAベンチマークスコア58.18を再現するには: + +1. `gaia58.18`ブランチに切り替え: + ```bash + git checkout gaia58.18 + ``` + +2. 評価スクリプトを実行: + ```bash + python run_gaia_roleplaying.py + ``` + +これにより、GAIAベンチマークでトップランクのパフォーマンスを達成したのと同じ構成が実行されます。 + +# ⏱️ 将来の計画 + +私たちはOWLの改善に継続的に取り組んでいます。以下は私たちのロードマップです: + +- [ ] 現実のタスクにおけるマルチエージェント協力の探求と洞察を詳述する技術ブログ記事を書く +- [ ] 特定の分野のタスクに対応する専門ツールを追加してツールキットエコシステムを強化 +- [ ] より高度なエージェント相互作用パターンと通信プロトコルを開発 +- [ ] 複雑な多段階推論タスクのパフォーマンスを向上 + +# 📄 ライセンス + +ソースコードはApache 2.0ライセンスの下でライセンスされています。 + +# 🖊️ 引用 + +このリポジトリが役立つと思われる場合は、以下を引用してください: + + +``` +@misc{owl2025, + title = {OWL: Optimized Workforce Learning for General Multi-Agent Assistance in Real-World Task Automation}, + author = {{CAMEL-AI.org}}, + howpublished = {\url{https://github.com/camel-ai/owl}}, + note = {Accessed: 2025-03-07}, + year = {2025} +} +``` + +# 🤝 貢献 + +私たちはコミュニティからの貢献を歓迎します!以下は、どのように支援できるかです: + +1. [貢献ガイドライン](https://github.com/camel-ai/camel/blob/master/CONTRIBUTING.md)を読む +2. [オープンな問題](https://github.com/camel-ai/camel/issues)を確認するか、新しい問題を作成する +3. 改善点を含むプルリクエストを提出する + +**現在貢献を受け付けている問題:** +- [#362](https://github.com/camel-ai/owl/issues/362) +- [#1945](https://github.com/camel-ai/camel/issues/1945) +- [#1925](https://github.com/camel-ai/camel/issues/1925) +- [#1915](https://github.com/camel-ai/camel/issues/1915) + + +問題を引き受けるには、興味を示すコメントを残すだけです。 + +# 🔥 コミュニティ +エージェントのスケーリング法則を見つけるための限界を押し広げるために、私たちと一緒に参加してください([*Discord*](https://discord.camel-ai.org/)または[*WeChat*](https://ghli.org/camel/wechat.png))。 + +さらなる議論に参加してください! + +![](./assets/community.jpg) + +# ❓ FAQ + +**Q: サンプルスクリプトを起動した後、なぜローカルでChromeが実行されていないのですか?** + +A: OWLがタスクを非ブラウザツール(検索やコード実行など)を使用して完了できると判断した場合、ブラウザは起動しません。ブラウザベースのインタラクションが必要と判断された場合にのみ、ブラウザウィンドウが表示されます。 + +**Q: どのPythonバージョンを使用すべきですか?** + +A: OWLはPython 3.10、3.11、および3.12をサポートしています。 + +**Q: プロジェクトにどのように貢献できますか?** + +A: 参加方法の詳細については、[貢献](#-貢献)セクションを参照してください。コードの改善からドキュメントの更新まで、あらゆる種類の貢献を歓迎します。 + +# 📚 CAMEL依存関係の探索 + +OWLは[CAMEL](https://github.com/camel-ai/camel)フレームワークの上に構築されています。以下は、CAMELのソースコードを探索し、OWLとの連携方法を理解する方法です: + +## CAMELソースコードへのアクセス + +```bash +# CAMELリポジトリをクローン +git clone https://github.com/camel-ai/camel.git +cd camel +``` + +# ⭐ Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=camel-ai/owl&type=Date)](https://star-history.com/#camel-ai/owl&Date) + + + +[docs-image]: https://img.shields.io/badge/Documentation-EB3ECC +[docs-url]: https://camel-ai.github.io/camel/index.html +[star-image]: https://img.shields.io/github/stars/camel-ai/owl?label=stars&logo=github&color=brightgreen +[star-url]: https://github.com/camel-ai/owl/stargazers +[package-license-image]: https://img.shields.io/badge/License-Apache_2.0-blue.svg +[package-license-url]: https://github.com/camel-ai/owl/blob/main/licenses/LICENSE + +[colab-url]: https://colab.research.google.com/drive/1AzP33O8rnMW__7ocWJhVBXjKziJXPtim?usp=sharing +[colab-image]: https://colab.research.google.com/assets/colab-badge.svg +[huggingface-url]: https://huggingface.co/camel-ai +[huggingface-image]: https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-CAMEL--AI-ffc107?color=ffc107&logoColor=white +[discord-url]: https://discord.camel-ai.org/ +[discord-image]: https://img.shields.io/discord/1082486657678311454?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb +[wechat-url]: https://ghli.org/camel/wechat.png +[wechat-image]: https://img.shields.io/badge/WeChat-CamelAIOrg-brightgreen?logo=wechat&logoColor=white +[x-url]: https://x.com/CamelAIOrg +[x-image]: https://img.shields.io/twitter/follow/CamelAIOrg?style=social +[twitter-image]: https://img.shields.io/twitter/follow/CamelAIOrg?style=social&color=brightgreen&logo=twitter +[reddit-url]: https://www.reddit.com/r/CamelAI/ +[reddit-image]: https://img.shields.io/reddit/subreddit-subscribers/CamelAI?style=plastic&logo=reddit&label=r%2FCAMEL&labelColor=white +[ambassador-url]: https://www.camel-ai.org/community +[owl-url]: ./assets/qr_code.jpg +[owl-image]: https://img.shields.io/badge/WeChat-OWLProject-brightgreen?logo=wechat&logoColor=white diff --git a/README_zh.md b/README_zh.md index 06b8b3d..848235c 100644 --- a/README_zh.md +++ b/README_zh.md @@ -99,6 +99,19 @@

+
+

+ 🧩 新增:社区智能体挑战! 🧩 +

+

+ 展示您的创造力,为AI智能体设计独特的挑战!
+ 加入我们的社区,见证您的创新想法被尖端AI技术实现。 +

+

+ 查看与提交挑战 +

+
+

🎉 最新重大更新 - 2025年3月15日 @@ -113,6 +126,10 @@

+- **[2025.03.26]**: 支持Gemini 2.5 Pro模型,添加示例运行代码 +- **[2025.03.21]**: 集成OpenRouter模型平台,修复Gemini工具调用的bug +- **[2025.03.20]**: 在MCP工具包中添加Accept头部,支持自动安装playwright +- **[2025.03.16]**: 支持必应搜索、百度搜索 - **[2025.03.12]**: 在SearchToolkit中添加了Bocha搜索功能,集成了火山引擎模型平台,并更新了Azure和OpenAI Compatible模型的结构化输出和工具调用能力。 - **[2025.03.11]**: 我们添加了 MCPToolkit、FileWriteToolkit 和 TerminalToolkit,增强了 OWL Agent 的 MCP(模型上下文协议)集成、文件写入能力和终端命令执行功能。MCP 作为一个通用协议层,标准化了 AI 模型与各种数据源和工具的交互方式。 - **[2025.03.09]**: 我们添加了基于网页的用户界面,使系统交互变得更加简便。 @@ -125,6 +142,8 @@ https://private-user-images.githubusercontent.com/55657767/420211368-f29f477d-7e https://private-user-images.githubusercontent.com/55657767/420212194-e813fc05-136a-485f-8df3-f10d9b4e63ec.mp4 +此视频演示了如何在本地安装 OWL,展示了它作为一个前沿的多智能体协作框架,推动任务自动化边界的能力:https://www.youtube.com/watch?v=8XlqVyAZOr8 + # ✨️ 核心功能 - **在线搜索**:支持多种搜索引擎(包括维基百科、Google、DuckDuckGo、百度、博查等),实现实时信息检索与知识获取 @@ -363,6 +382,9 @@ python examples/run_qwen_zh.py # 使用 Deepseek 模型运行 python examples/run_deepseek_zh.py +# 使用 Gemini 模型运行 +python examples/run_gemini.py + # 使用其他 OpenAI 兼容模型运行 python examples/run_openai_compatible_model.py @@ -582,8 +604,10 @@ python examples/run_gaia_roleplaying.py 3. 提交包含您改进的拉取请求 **当前开放贡献的问题:** -- [#1905](https://github.com/camel-ai/camel/issues/1905) -- [#1712](https://github.com/camel-ai/camel/issues/1712) +- [#362](https://github.com/camel-ai/owl/issues/362) +- [#1945](https://github.com/camel-ai/camel/issues/1945) +- [#1925](https://github.com/camel-ai/camel/issues/1925) +- [#1915](https://github.com/camel-ai/camel/issues/1915) 要认领一个问题,只需在该问题下留言表明您的兴趣即可。 @@ -592,7 +616,7 @@ python examples/run_gaia_roleplaying.py 加入我们,参与更多讨论! -![](./assets/community.jpeg) +![](./assets/community.jpg) # ❓ 常见问题 diff --git a/assets/community.jpg b/assets/community.jpg new file mode 100644 index 0000000..0500458 Binary files /dev/null and b/assets/community.jpg differ diff --git a/community_challenges.md b/community_challenges.md new file mode 100644 index 0000000..9171edd --- /dev/null +++ b/community_challenges.md @@ -0,0 +1,50 @@ +# 🧩 Community Agent Challenges + +Welcome to the OWL Community Challenges hub! This is where creative minds come together to craft interesting, innovative, and thought-provoking challenges for AI agents. Got an idea for a task that would really put an AI to the test? We want to hear from you! + +## 🚀 Why Submit a Challenge? + +- **Showcase your creativity** - Design unique tasks that highlight your innovative thinking +- **Join our community** - Become part of the growing OWL ecosystem +- **See your ideas in action** - Watch as your challenges are tackled by cutting-edge AI +- **Help shape the future** - Contribute to advancing the capabilities of AI assistants + +## 📝 How to Submit Your Challenge + +It's easy! Simply add your challenge directly to this document by following the template below. The more detailed and creative your challenge, the better! + +## ✨ Challenge Template + +```markdown +### [Challenge Title] + +**Task**: Detailed instructions for the task. + +**Success Criteria**: +- What defines successful completion of this challenge? + +**Hints** (Optional): +- Any helpful tips +``` + +## 🏆 Community Challenges + +### GitHub Repository Statistics Visualization + +**Task**: Open Google search, summarize the GitHub stars, fork counts, and other relevant statistics of camel-ai/camel framework. Then, write these numbers into a Python file using a plotting package (such as matplotlib or seaborn), save the visualization locally, and run the generated Python file to display the chart. + +**Success Criteria**: +- Retrieve accurate GitHub statistics for the camel-ai/camel repository +- Generate a Python script that visualizes the data +- Successfully run the script and create a visualization + + +--- + +### [Your Challenge Here] + +**Task**: + +**Success Criteria**: + +**Hints** (Optional): diff --git a/community_usecase/OWL Interview Preparation Assistant/.env.template b/community_usecase/OWL Interview Preparation Assistant/.env.template new file mode 100644 index 0000000..d8df728 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/.env.template @@ -0,0 +1,27 @@ +# API KEYS +# =========================================== +# Choose ONE of the following API providers: + +# Option 1: OpenAI API (recommended for best results) +# Get API key from: https://platform.openai.com/api-keys +OPENAI_API_KEY="" + +# Option 2: OpenRouter API (for access to Gemini and other models) +# Get API key from: https://openrouter.ai/keys +OPENROUTER_API_KEY="" + +# SEARCH CAPABILITIES +# =========================================== +# Optional but recommended for enhanced research + +# Google Search API (optional but recommended) +# Get from: https://programmablesearchengine.google.com/ +GOOGLE_API_KEY="" +SEARCH_ENGINE_ID="" + +# ADDITIONAL SETTINGS +# =========================================== +# Advanced settings (optional) + +# Logging level: DEBUG, INFO, WARNING, ERROR +LOG_LEVEL="INFO" \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/.gitignore b/community_usecase/OWL Interview Preparation Assistant/.gitignore new file mode 100644 index 0000000..c492d53 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/.gitignore @@ -0,0 +1,176 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +.directory \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/Example Output Interview Prep Assistant(With OWL 🦉) Demo.pdf b/community_usecase/OWL Interview Preparation Assistant/Example Output Interview Prep Assistant(With OWL 🦉) Demo.pdf new file mode 100644 index 0000000..622718c Binary files /dev/null and b/community_usecase/OWL Interview Preparation Assistant/Example Output Interview Prep Assistant(With OWL 🦉) Demo.pdf differ diff --git a/community_usecase/OWL Interview Preparation Assistant/PROJECT_DESCRIPTION.md b/community_usecase/OWL Interview Preparation Assistant/PROJECT_DESCRIPTION.md new file mode 100644 index 0000000..3d584e0 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/PROJECT_DESCRIPTION.md @@ -0,0 +1,98 @@ +# 🦉 Interview Preparation Assistant: AI-Powered Interview Success + +## Project Overview + +The Interview Preparation Assistant is an advanced multi-agent AI system built on the OWL framework that revolutionizes how job seekers prepare for interviews. By leveraging the power of collaborative AI agents, it delivers personalized, comprehensive, and actionable interview preparation materials tailored to specific job roles and companies. + +## The Problem We're Solving + +Job interviews are critical gateways to career opportunities, yet preparation is often fragmented, generic, and time-consuming: + +- **Information Overload**: Job seekers must sift through countless resources to find relevant information +- **Limited Personalization**: Generic interview guides fail to address specific company cultures and role requirements +- **Time Constraints**: Comprehensive research and preparation can take weeks of dedicated effort +- **Technical Complexity**: For technical roles, preparing appropriate code examples and solutions is challenging +- **Anxiety and Uncertainty**: Many candidates feel underprepared, increasing interview anxiety + +## My Solution + +The Interview Preparation Assistant transforms this experience by deploying multiple specialized AI agents working in concert to create a complete interview preparation package: + +### 1. Company Research Agent +Performs deep, real-time research on target companies by: +- Analyzing company websites, news articles, and social media +- Investigating company culture, values, and work environment +- Examining technical stacks, product offerings, and industry positioning +- Reviewing known interview processes and expectations + +### 2. Question Generation Agent +Creates tailored interview questions based on: +- The specific job role and required skills +- Company-specific technologies and methodologies +- Both technical and behavioral aspects of the interview +- Current industry trends and best practices + +### 3. Preparation Plan Agent +Develops structured preparation plans that include: +- Day-by-day preparation schedules +- Prioritized study topics and resources +- Mock interview scenarios with sample answers +- Technical practice problems with detailed solutions + +## Key Differentiators + +What makes my solution unique: + +- **Multi-Agent Collaboration**: Multiple specialized AI agents working together creates more comprehensive and accurate results than a single AI assistant +- **Real-Time Research**: Up-to-date information gathered from the web ensures relevance +- **Deep Personalization**: Materials tailored to specific companies and roles rather than generic advice +- **Technical Depth**: Detailed code examples and technical explanations for engineering roles +- **Structured Output**: Clear, organized preparation materials ready for immediate use +- **Conversation Transparency**: Users can observe the agents' thought processes, building trust and understanding + +## Value Proposition + +The Interview Preparation Assistant delivers significant value to users by: + +- **Saving Time**: Reduces weeks of research and preparation to minutes +- **Increasing Confidence**: Comprehensive preparation materials reduce anxiety and build confidence +- **Improving Performance**: Better-prepared candidates perform stronger in interviews +- **Accelerating Career Growth**: Higher success rates in job interviews lead to better career opportunities +- **Democratizing Access**: Makes high-quality interview preparation accessible to everyone, not just those with professional networks or coaching + +## Use Case Examples + +### Technical Role Preparation +A software engineer applying to Google receives: +- Detailed analysis of Google's engineering culture and interview process +- Coding questions focused on algorithms, data structures, and system design +- Google-specific behavioral questions emphasizing innovation and collaboration +- A 14-day preparation plan with specific practice exercises + +### Business Role Preparation +A marketing manager applying to Apple receives: +- Insights into Apple's marketing philosophy and brand positioning +- Case study questions focused on product launches and customer experience +- Behavioral questions targeting creativity and strategic thinking +- A preparation plan emphasizing Apple's unique approach to marketing + +## Technical Implementation + +The system is built using: +- **OWL Multi-Agent Framework**: Enabling coordinated collaboration between specialized AI agents +- **Dynamic Research Tools**: Real-time web search and content processing +- **Streamlit Interface**: User-friendly web application for easy interaction +- **Advanced LLM Models**: Utilizing state-of-the-art language models (OpenAI/Gemini) + +## Impact and Future Development + +The Interview Preparation Assistant demonstrates the transformative potential of multi-agent AI systems for personalized knowledge work. Future development paths include: + +- **Interview Simulation**: Interactive mock interviews with feedback +- **Performance Analytics**: Tracking preparation progress and identifying areas for improvement +- **Specialized Modules**: Domain-specific preparation for fields like healthcare, finance, etc. +- **Mentor Matching**: Connecting candidates with industry professionals based on preparation insights + +--- + +This project showcases how OWL's collaborative AI framework can transform complex, knowledge-intensive tasks that traditionally required significant human effort into accessible, high-quality services available on demand. \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/README.md b/community_usecase/OWL Interview Preparation Assistant/README.md new file mode 100644 index 0000000..ff0684c --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/README.md @@ -0,0 +1,238 @@ +# 🦉 Interview Preparation Assistant + +An intelligent multi-agent interview preparation system powered by the OWL framework that helps you prepare for job interviews with comprehensive research, tailored questions, and detailed preparation plans. + +![Interview Preparation Assistant](https://github.com/parthshr370/owl/blob/community_usecase/parthshr370/community_usecase/OWL%20Interview%20Preparation%20Assistant/Screenshot_20250321_201930.png?raw=true) + +## ✨ Features + +- **🔍 Company Research**: Automatically researches companies using real-time web data +- **❓ Interview Question Generation**: Creates tailored interview questions specific to your job role and target company +- **📋 Preparation Plans**: Builds comprehensive step-by-step interview preparation plans +- **🧠 AI-Powered Agents**: Leverages multiple AI agents to work together on your interview preparation +- **💻 Code Examples**: Provides code examples for technical roles with explanations +- **🔄 Real-time Progress**: Shows conversation process between AI agents as they prepare your materials + +## 📋 Table of Contents + +- [Requirements](#-requirements) +- [Installation](#-installation) +- [Quick Start](#-quick-start) +- [Usage Guide](#-usage-guide) +- [Configuration](#-configuration) +- [Troubleshooting](#-troubleshooting) +- [Project Structure](#-project-structure) + +## 🛠 Requirements + +- Python 3.10+ (tested on Python 3.10) +- Access to one of the following AI models: + - OpenAI API (GPT-4) + - OpenRouter API (Gemini models) +- Internet connection for web search and company research +- Minimum 8GB RAM + +## 🚀 Installation + +### 1. Clone the OWL Repository + +First, clone the OWL repository, which this project depends on: + +```bash +git clone https://github.com/camel-ai/owl.git +cd owl +``` + +### 2. Create a Virtual Environment + +```bash +# Create a conda environment (recommended) +conda create -n interview_assistant python=3.10 +conda activate interview_assistant + +# OR using venv +python -m venv interview_env +source interview_env/bin/activate # On Windows: interview_env\Scripts\activate +``` + +### 3. Install OWL and Dependencies + +```bash +# Install OWL +pip install -e . + +# Install additional dependencies +pip install streamlit numpy pandas opencv-python +``` + +### 4. Configure API Keys + +Create a `.env` file in the project directory with your API keys: + +```bash +# Navigate to the Interview Preparation Assistant directory +cd community_usecase/new\ int/ + +# Create .env file +touch .env +``` + +Add your API keys to the `.env` file: + +``` +# OpenAI API (recommended for best results) +OPENAI_API_KEY=your_openai_api_key_here + +# OR OpenRouter API (for access to Gemini models) +OPENROUTER_API_KEY=your_openrouter_api_key_here + +# Optional: Google Search API for enhanced research (optional) +GOOGLE_API_KEY=your_google_api_key_here +SEARCH_ENGINE_ID=your_google_search_engine_id_here +``` + +## ⚡ Quick Start + +The fastest way to get started is to use the Streamlit web interface: + +```bash +# Navigate to the project directory +cd community_usecase/new\ int/ + +# Start the web application +streamlit run app.py +``` + +This will open a web browser window with the Interview Preparation Assistant interface where you can: + +1. Enter your target job role (e.g., "Machine Learning Engineer") +2. Enter your target company name (e.g., "Google") +3. Generate interview preparation materials + +## 📚 Usage Guide + +### Web Interface + +The web interface provides three main functions: + +#### 1. Company Research + +Click on "Research Company" to generate a comprehensive report about your target company including: +- Company background and culture +- Technical stack and technologies used +- Interview process and expectations +- Key products and services + + +#### 2. Interview Questions + +Click on "Generate Questions" to create tailored interview questions for your role and company: +- Technical questions with code examples +- Behavioral questions specific to the company culture +- Role-specific questions to showcase your expertise +- Sample answers and solution approaches + + +#### 3. Preparation Plan + +Click on "Create Preparation Plan" to receive a detailed day-by-day preparation guide: +- Structured preparation timeline +- Technical topics to review +- Practice exercises and code challenges +- Research and preparation tasks +- Interview day tips + + +### Command Line Usage + +You can also run specific functions from the command line: + +```bash +# Run company research +python -c "from main import research_company; result = research_company('Google', detailed=True); print(result['answer'])" + +# Generate interview questions +python -c "from main import generate_interview_questions; result = generate_interview_questions('Machine Learning Engineer', 'Google'); print(result['answer'])" + +# Create preparation plan +python -c "from main import create_interview_prep_plan; result = create_interview_prep_plan('Machine Learning Engineer', 'Google'); print(result['answer'])" +``` + +### Log Monitoring + +You can view the logs in real-time in the "System Logs" tab of the web interface to monitor: +- AI agent conversations +- Progress of each request +- Any errors or issues that occur + +## ⚙️ Configuration + +### Customizing Parameters + +You can adjust the following parameters in `main.py`: + +1. **Round Limit**: Change the conversation round limit by modifying the `round_limit` parameter in function calls (default: 5) + +2. **Model Selection**: Edit the model configuration in `construct_interview_assistant()` to use different models + +3. **Output Directory**: Change `INTERVIEW_PREP_DIR` to customize where results are stored + +### Environment Variables + +In addition to API keys, you can customize behavior with these environment variables: + +- `LOG_LEVEL`: Set to `DEBUG`, `INFO`, `WARNING`, or `ERROR` to control logging verbosity + +## 🔧 Troubleshooting + +### Common Issues + +1. **API Key Errors** + - Ensure your API keys are correctly set in the `.env` file + - Check that you're using the correct format without quotes or extra spaces + +2. **Model Errors** + - If using OpenRouter, ensure the model specified is available on your account + - Verify you have sufficient API credits for your requests + +3. **Round Limit Not Working** + - The system enforces a strict limit of 5 conversation rounds to prevent excessive token usage + - You can adjust this in the code if needed, but may encounter higher API costs + +4. **Memory Errors** + - Processing large contexts can require significant memory + - Try using a machine with more RAM or reducing model context sizes + +### Getting Help + +If you encounter issues not covered here: + +1. Check the logs in the "System Logs" tab of the web interface +2. Examine the console output for error messages +3. File an issue on the GitHub repository + +## 📂 Project Structure + +``` +community_usecase/new int/ +├── app.py # Streamlit web interface +├── main.py # Core functionality and API connections +├── config/ +│ └── prompts.py # Prompt templates for different tasks +├── interview_prep/ # Generated interview preparation materials +├── logging_utils.py # Logging utilities +└── README.md # This documentation +``` + +## 📝 License + +This project is built on top of the CAMEL-AI OWL framework, which is licensed under the Apache License 2.0. + +## 🙏 Acknowledgements + +- This project is built on the [CAMEL-AI OWL framework](https://github.com/camel-ai/owl) +- Special thanks to the contributors of CAMEL-AI for making multi-agent AI systems accessible + +--- + +Made with ❤️ for job seekers everywhere. diff --git a/community_usecase/OWL Interview Preparation Assistant/Screenshot_20250321_201930.png b/community_usecase/OWL Interview Preparation Assistant/Screenshot_20250321_201930.png new file mode 100644 index 0000000..3597ed5 Binary files /dev/null and b/community_usecase/OWL Interview Preparation Assistant/Screenshot_20250321_201930.png differ diff --git a/community_usecase/OWL Interview Preparation Assistant/app.py b/community_usecase/OWL Interview Preparation Assistant/app.py new file mode 100644 index 0000000..e5d9ba5 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/app.py @@ -0,0 +1,460 @@ +#app.py +import os +import streamlit as st +import logging +import queue +import time +import sys + +# Add parent directory to path for OWL imports +sys.path.append('../') + +try: + from main import research_company, generate_interview_questions, create_interview_prep_plan +except ImportError as e: + st.error(f"Error importing functions: {e}") + st.stop() + +# Setup logging with queue to capture logs for display +log_queue = queue.Queue() + +class StreamlitLogHandler(logging.Handler): + def __init__(self, log_queue): + super().__init__() + self.log_queue = log_queue + self.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + def emit(self, record): + log_entry = self.format(record) + self.log_queue.put(log_entry) + +# Configure root logger +root_logger = logging.getLogger() +root_logger.setLevel(logging.INFO) +for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) +root_logger.addHandler(StreamlitLogHandler(log_queue)) +root_logger.addHandler(logging.StreamHandler()) # Also log to console + +# Configure Streamlit page +st.set_page_config( + page_title="Interview Prep Assistant(With OWL 🦉)", + page_icon="🦉", + layout="wide" +) + +# Custom CSS +st.markdown(""" + +""", unsafe_allow_html=True) + +def display_conversation(chat_history): + """Display the conversation history in a structured format""" + if not chat_history: + st.info("No conversation available") + return + + st.markdown("
", unsafe_allow_html=True) + + for idx, message in enumerate(chat_history): + round_num = idx + 1 + st.markdown(f"
Round {round_num}
", unsafe_allow_html=True) + + # Display user message + if "user" in message and message["user"]: + st.markdown(f"
🧑‍💼 Job Seeker:
{message['user']}
", unsafe_allow_html=True) + + # Display assistant message + if "assistant" in message and message["assistant"]: + assistant_content = message["assistant"] + # Remove any note about truncation for cleaner display + if "[Note: This conversation was limited" in assistant_content: + assistant_content = assistant_content.replace("[Note: This conversation was limited to maintain response quality. The complete thought process is available in the logs.]", "") + + st.markdown(f"
🦉 Interview Coach:
{assistant_content}
", unsafe_allow_html=True) + + # Display tool calls if any + if "tool_calls" in message and message["tool_calls"]: + for tool in message["tool_calls"]: + tool_name = tool.get('name', 'Unknown Tool') + st.markdown(f"
🔧 Tool Used: {tool_name}
", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + +def display_metrics(duration, token_count, num_rounds): + """Display metrics in a visually appealing format""" + st.markdown("
", unsafe_allow_html=True) + + # Time taken + st.markdown(f""" +
+
{duration:.1f}s
+
Execution Time
+
+ """, unsafe_allow_html=True) + + # Token usage + completion_tokens = token_count.get('completion_token_count', 0) + prompt_tokens = token_count.get('prompt_token_count', 0) + total_tokens = completion_tokens + prompt_tokens + + st.markdown(f""" +
+
{total_tokens:,}
+
Total Tokens
+
+ """, unsafe_allow_html=True) + + # Conversation rounds + st.markdown(f""" +
+
{num_rounds}
+
Conversation Rounds
+
+ """, unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + +def get_logs(): + """Retrieve logs from the queue""" + logs = [] + while not log_queue.empty(): + try: + logs.append(log_queue.get_nowait()) + except queue.Empty: + break + return logs + +def main(): + # Header + st.markdown("

🦉 Interview Preparation Assistant

", unsafe_allow_html=True) + st.markdown("

Powered by multi-agent AI collaboration

", unsafe_allow_html=True) + + # Input section + with st.container(): + col1, col2 = st.columns(2) + with col1: + job_role = st.text_input("Job Role", "Machine Learning Engineer") + with col2: + company_name = st.text_input("Company Name", "Google") + + # Main functionality tabs + tab1, tab2, tab3, tab4 = st.tabs(["Company Research", "Interview Questions", "Preparation Plan", "System Logs"]) + + # Tab 1: Company Research + with tab1: + st.header("🔍 Company Research") + st.write("Get detailed insights about the company to help with your interview preparation.") + + if st.button("Research Company", use_container_width=True): + with st.spinner(): + # Display running indicator + status = st.empty() + status.markdown("
🔄 Researching company information...
", unsafe_allow_html=True) + + # Progress bar + progress = st.progress(0) + + # Progress callback + def update_progress(current_round, max_rounds): + progress_value = min(current_round / max_rounds, 0.95) + progress.progress(progress_value) + status.markdown(f"
🔄 Processing conversation round {current_round}/{max_rounds}...
", unsafe_allow_html=True) + + # Execute research + try: + start_time = time.time() + result = research_company( + company_name=company_name, + detailed=True, # Always use detailed mode + limited_searches=False, # Don't limit searches + progress_callback=update_progress + ) + duration = time.time() - start_time + + # Update progress to complete + progress.progress(1.0) + status.markdown("
✅ Research completed!
", unsafe_allow_html=True) + + # Display metrics + display_metrics( + duration=duration, + token_count=result["token_count"], + num_rounds=len(result["chat_history"]) + ) + + # Display final answer + st.subheader("📝 Research Results") + st.markdown(f"
{result['answer']}
", unsafe_allow_html=True) + + # Display conversation + st.subheader("💬 Conversation Process") + display_conversation(result["chat_history"]) + + except Exception as e: + st.error(f"Error: {str(e)}") + logging.exception("Error in company research") + + # Tab 2: Interview Questions + with tab2: + st.header("❓ Interview Questions") + st.write("Generate tailored interview questions for your target role and company.") + + # Question type selector (adds interactivity but doesn't change behavior for now) + question_type = st.radio( + "Question Type", + ["Technical", "Behavioral", "Company-Specific", "All"], + horizontal=True + ) + + if st.button("Generate Questions", use_container_width=True): + with st.spinner(): + # Display running indicator + status = st.empty() + status.markdown("
🔄 Creating interview questions...
", unsafe_allow_html=True) + + # Progress bar + progress = st.progress(0) + + # Progress callback + def update_progress(current_round, max_rounds): + progress_value = min(current_round / max_rounds, 0.95) + progress.progress(progress_value) + status.markdown(f"
🔄 Processing conversation round {current_round}/{max_rounds}...
", unsafe_allow_html=True) + + # Execute question generation + try: + start_time = time.time() + result = generate_interview_questions( + job_role=job_role, + company_name=company_name, + detailed=True, # Always use detailed mode + limited_searches=False, # Don't limit searches + progress_callback=update_progress + ) + duration = time.time() - start_time + + # Update progress to complete + progress.progress(1.0) + status.markdown("
✅ Questions generated!
", unsafe_allow_html=True) + + # Display metrics + display_metrics( + duration=duration, + token_count=result["token_count"], + num_rounds=len(result["chat_history"]) + ) + + # Display final answer + st.subheader("📝 Generated Questions") + st.markdown(f"
{result['answer']}
", unsafe_allow_html=True) + + # Display conversation + st.subheader("💬 Conversation Process") + display_conversation(result["chat_history"]) + + except Exception as e: + st.error(f"Error: {str(e)}") + logging.exception("Error in question generation") + + # Tab 3: Preparation Plan + with tab3: + st.header("📋 Interview Preparation Plan") + st.write("Create a comprehensive step-by-step plan to prepare for your interview.") + + if st.button("Create Preparation Plan", use_container_width=True): + with st.spinner(): + # Display running indicator + status = st.empty() + status.markdown("
🔄 Creating preparation plan...
", unsafe_allow_html=True) + + # Progress bar + progress = st.progress(0) + + # Progress callback + def update_progress(current_round, max_rounds): + progress_value = min(current_round / max_rounds, 0.95) + progress.progress(progress_value) + status.markdown(f"
🔄 Processing conversation round {current_round}/{max_rounds}...
", unsafe_allow_html=True) + + # Execute plan creation + try: + start_time = time.time() + result = create_interview_prep_plan( + job_role=job_role, + company_name=company_name, + detailed=True, # Always use detailed mode + limited_searches=False, # Don't limit searches + progress_callback=update_progress + ) + duration = time.time() - start_time + + # Update progress to complete + progress.progress(1.0) + status.markdown("
✅ Plan created!
", unsafe_allow_html=True) + + # Display metrics + display_metrics( + duration=duration, + token_count=result["token_count"], + num_rounds=len(result["chat_history"]) + ) + + # Display final answer + st.subheader("📝 Preparation Plan") + st.markdown(f"
{result['answer']}
", unsafe_allow_html=True) + + # Display conversation + st.subheader("💬 Conversation Process") + display_conversation(result["chat_history"]) + + except Exception as e: + st.error(f"Error: {str(e)}") + logging.exception("Error in preparation plan creation") + + # Tab 4: System Logs + with tab4: + st.header("🔧 System Logs") + st.write("View detailed system logs for debugging.") + + logs_container = st.empty() + + # Get and display logs + logs = get_logs() + if logs: + logs_container.code("\n".join(logs)) + else: + logs_container.info("No logs available yet.") + + # Manual refresh button + if st.button("Refresh Logs"): + logs = get_logs() + if logs: + logs_container.code("\n".join(logs)) + else: + logs_container.info("No logs available yet.") + + # Auto-refresh toggle + auto_refresh = st.checkbox("Auto-refresh logs", value=True) + if auto_refresh: + st.markdown( + """ + + """, + unsafe_allow_html=True + ) + +if __name__ == "__main__": + try: + logging.info("Starting Interview Preparation Assistant application") + main() + except Exception as e: + st.error(f"Application error: {str(e)}") + logging.exception("Application error") \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/config/prompts.py b/community_usecase/OWL Interview Preparation Assistant/config/prompts.py new file mode 100644 index 0000000..7702f8a --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/config/prompts.py @@ -0,0 +1,59 @@ +def get_system_prompt() -> str: + """Get the enhanced system prompt for the interview assistant.""" + return """ + You are an advanced Interview Preparation Assistant powered by OWL multi-agent technology. + Your primary task is to provide COMPREHENSIVE, EXTREMELY DETAILED, and HIGHLY SPECIFIC + interview preparation materials with practical examples and actionable advice. + + IMPORTANT OUTPUT REQUIREMENTS: + + 1. EXTREME DETAIL: Do not summarize or truncate your responses. Provide complete, comprehensive + information with multiple sections, subsections, and extensive details. The final output + should be at least 2000 words, ideally 3000-4000 for truly thorough coverage. + + 2. PRACTICAL CODE EXAMPLES: For technical roles, include relevant code snippets, detailed + technical scenarios, and at least 5-10 code samples or system design outlines. + + 3. COMPREHENSIVE CONTENT: Create exceptionally thorough content with step-by-step instructions, + deep explanations, and multiple examples. Never abbreviate or summarize your responses. + + 4. NO TRUNCATION: Never cut off your responses with '...' or similar. Always provide the + complete thought or explanation. + + 5. STRUCTURED OUTPUT: Use clear headings (H1, H2, H3, etc.), bullet points, numbered lists, + and well-organized sections to present the content in a digestible way. + + 6. SPECIFIC IMPLEMENTATIONS: For technical roles, always provide multiple code examples, + approaches, edge cases, and relevant optimizations. + + 7. FILE MANAGEMENT: You may save all information as well-formatted files, but also include + the entire unabridged content directly in your response. + """ + +def get_company_research_prompt(company_name: str) -> str: + """Get a specialized prompt for company research.""" + return f""" + Conduct the most COMPREHENSIVE and EXTREMELY DETAILED research on {company_name} possible. + The final output must be at least 3000 words, covering the company's history, mission, + technology stack, culture, interview process, and more. Provide code or architecture + examples if relevant, and do not abbreviate or summarize. + """ + +def get_question_generator_prompt(job_role: str, company_name: str) -> str: + """Get a specialized prompt for interview question generation.""" + return f""" + Generate an EXTREMELY COMPREHENSIVE, EXHAUSTIVELY DETAILED set of interview questions for + a {job_role} position at {company_name}. Provide at least 30 questions with deep sample + answers, code examples, multiple solution approaches, and a total of 3000+ words. + Do not truncate or summarize. + """ + +def get_preparation_plan_prompt(job_role: str, company_name: str) -> str: + """Get a specialized prompt for creating an interview preparation plan.""" + return f""" + Create a HIGHLY THOROUGH, MULTI-DAY interview preparation plan for a {job_role} position + at {company_name}. The final plan should exceed 2000 words, with detailed daily tasks, + technical reviews, code examples (if relevant), and no summary or truncation. + Cover everything from fundamental skills to advanced interview strategies. + """ + diff --git a/community_usecase/OWL Interview Preparation Assistant/logging_utils.py b/community_usecase/OWL Interview Preparation Assistant/logging_utils.py new file mode 100644 index 0000000..bad7c56 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/logging_utils.py @@ -0,0 +1,275 @@ +import os +import logging +import time +import functools +import inspect +import re +from typing import Dict, Any, List, Tuple, Callable, Optional +import queue + +# Create a singleton log queue that can be shared between modules +class LogQueueSingleton: + _instance = None + + @classmethod + def get_instance(cls): + if cls._instance is None: + cls._instance = queue.Queue() + return cls._instance + +# Custom logging wrapper for tools +def log_tool_usage(func): + """ + Decorator to log when a tool is being used. + """ + @functools.wraps(func) + async def wrapper(*args, **kwargs): + tool_name = func.__name__ + logging.info(f"🔧 TOOL TRIGGERED: {tool_name}") + try: + # Sanitize arguments to avoid logging sensitive info + safe_args = sanitize_args(args) + safe_kwargs = {k: sanitize_value(v) for k, v in kwargs.items()} + logging.info(f"🔍 TOOL ARGS: {tool_name} called with {len(safe_kwargs)} parameters") + + result = await func(*args, **kwargs) + + # Log completion but not the actual result content (might be large or sensitive) + logging.info(f"✅ TOOL COMPLETED: {tool_name}") + return result + except Exception as e: + logging.error(f"❌ TOOL ERROR: {tool_name} - {str(e)}") + raise + return wrapper + +# Non-async version for synchronous functions +def log_tool_usage_sync(func): + """ + Decorator to log when a synchronous tool is being used. + """ + @functools.wraps(func) + def wrapper(*args, **kwargs): + tool_name = func.__name__ + logging.info(f"🔧 TOOL TRIGGERED: {tool_name}") + try: + # Sanitize arguments to avoid logging sensitive info + safe_args = sanitize_args(args) + safe_kwargs = {k: sanitize_value(v) for k, v in kwargs.items()} + logging.info(f"🔍 TOOL ARGS: {tool_name} called with {len(safe_kwargs)} parameters") + + result = func(*args, **kwargs) + + # Log completion but not the actual result content (might be large or sensitive) + logging.info(f"✅ TOOL COMPLETED: {tool_name}") + return result + except Exception as e: + logging.error(f"❌ TOOL ERROR: {tool_name} - {str(e)}") + raise + return wrapper + +def sanitize_args(args): + """Sanitize arguments for logging to avoid sensitive data.""" + safe_args = [] + for arg in args: + safe_args.append(sanitize_value(arg)) + return safe_args + +def sanitize_value(value): + """Sanitize a value for logging.""" + if isinstance(value, str): + if len(value) > 50: + return value[:47] + "..." + return value + elif isinstance(value, (list, tuple)): + return f"{type(value).__name__} with {len(value)} items" + elif isinstance(value, dict): + return f"dict with {len(value)} items" + else: + return f"{type(value).__name__}" + +class LoggingToolkitWrapper: + """ + Wrapper class to add logging to toolkit methods. + """ + def __init__(self, toolkit): + self.toolkit = toolkit + self.toolkit_name = toolkit.__class__.__name__ + logging.info(f"📦 TOOLKIT INITIALIZED: {self.toolkit_name}") + + def __getattr__(self, name): + attr = getattr(self.toolkit, name) + + if callable(attr) and not name.startswith('_'): + if inspect.iscoroutinefunction(attr): + # It's an async function, wrap it with our async decorator + return log_tool_usage(attr) + else: + # For non-async functions + @functools.wraps(attr) + def wrapper(*args, **kwargs): + logging.info(f"🔧 TOOL TRIGGERED: {self.toolkit_name}.{name}") + try: + # Sanitize arguments to avoid logging sensitive info + safe_args = sanitize_args(args) + safe_kwargs = {k: sanitize_value(v) for k, v in kwargs.items()} + logging.info(f"🔍 TOOL ARGS: {name} called with {len(safe_kwargs)} parameters") + + result = attr(*args, **kwargs) + + logging.info(f"✅ TOOL COMPLETED: {self.toolkit_name}.{name}") + return result + except Exception as e: + logging.error(f"❌ TOOL ERROR: {self.toolkit_name}.{name} - {str(e)}") + raise + return wrapper + + return attr + +def wrap_toolkits(toolkits_list): + """ + Wrap a list of toolkits with logging functionality. + """ + wrapped_toolkits = [] + for toolkit in toolkits_list: + wrapped_toolkits.append(LoggingToolkitWrapper(toolkit)) + return wrapped_toolkits +# Find this function in logging_utils.py and replace it with this corrected version + +# Enhanced run_society function with logging +def enhanced_run_society(society, verbose=True): + """ + Enhanced wrapper around the OWL run_society function with detailed logging. + """ + from owl.utils import run_society as original_run_society + + # Log the society setup + user_role = getattr(society, 'user_role_name', 'User') + assistant_role = getattr(society, 'assistant_role_name', 'Assistant') + + logging.info(f"🚀 STARTING AGENT SOCIETY: {user_role} & {assistant_role}") + logging.info(f"📝 TASK: {society.task_prompt[:100]}...") + + # Log agent initialization + logging.info(f"🤖 INITIALIZING AGENT: {assistant_role}") + + # Add hooks to log message exchanges if possible + original_send_message = None + if hasattr(society, 'assistant_agent') and hasattr(society.assistant_agent, 'send_message'): + original_send_message = society.assistant_agent.send_message + + @functools.wraps(original_send_message) + def logged_send_message(*args, **kwargs): + logging.info(f"💬 AGENT MESSAGE: {assistant_role} is processing...") + result = original_send_message(*args, **kwargs) + logging.info(f"📨 AGENT RESPONSE RECEIVED from {assistant_role}") + return result + + society.assistant_agent.send_message = logged_send_message + + # Try to log tool usage if possible + if hasattr(society, 'assistant_agent') and hasattr(society.assistant_agent, 'tools'): + tools = getattr(society.assistant_agent, 'tools', []) + logging.info(f"🧰 AGENT HAS {len(tools)} TOOLS AVAILABLE") + + # Attempt to wrap each tool with logging + for i, tool in enumerate(tools): + if callable(tool): + tool_name = getattr(tool, '__name__', f"tool_{i}") + logging.info(f"🔧 TOOL AVAILABLE: {tool_name}") + + # Run the original function + start_time = time.time() + try: + logging.info(f"⏳ RUNNING SOCIETY...") + # Remove the verbose parameter from the call to original_run_society + answer, chat_history, token_count = original_run_society(society) + end_time = time.time() + duration = end_time - start_time + + # Log prompt and completion tokens separately if available + if isinstance(token_count, dict): + prompt_tokens = token_count.get('prompt_token_count', 0) + completion_tokens = token_count.get('completion_token_count', 0) + logging.info(f"💰 TOKEN USAGE: Prompt={prompt_tokens}, Completion={completion_tokens}, Total={prompt_tokens + completion_tokens}") + else: + logging.info(f"💰 TOKEN USAGE: {token_count}") + + logging.info(f"✅ AGENT SOCIETY COMPLETED: Duration {duration:.2f}s") + + return answer, chat_history, token_count + except Exception as e: + logging.error(f"❌ AGENT SOCIETY ERROR: {str(e)}") + raise + finally: + # Restore original method if we modified it + if original_send_message and hasattr(society, 'assistant_agent'): + society.assistant_agent.send_message = original_send_message + + + +# Function to sanitize logs to avoid exposing sensitive information +def sanitize_log(log_message): + """ + Sanitize log messages to avoid exposing sensitive information like IPs. + """ + # Simple IP address pattern matching + ip_pattern = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' + sanitized = re.sub(ip_pattern, '[REDACTED_IP]', log_message) + + # Redact API keys (common patterns) + api_key_pattern = r'(api[_-]?key|apikey|key|token)["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})["\']?' + sanitized = re.sub(api_key_pattern, r'\1: [REDACTED_API_KEY]', sanitized, flags=re.IGNORECASE) + + # Redact URLs with authentication information + url_auth_pattern = r'(https?://)([^:@/]+:[^@/]+@)([^\s/]+)' + sanitized = re.sub(url_auth_pattern, r'\1[REDACTED_AUTH]@\3', sanitized) + + return sanitized + +# Enhanced StreamlitLogHandler that sanitizes logs +class EnhancedStreamlitLogHandler(logging.Handler): + def __init__(self, log_queue): + super().__init__() + self.log_queue = log_queue + self.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) + + def emit(self, record): + log_entry = self.format(record) + # Sanitize the log to remove sensitive information + sanitized_log = sanitize_log(log_entry) + self.log_queue.put(sanitized_log) + +# Add logging to specific OWL functions if possible +# Add this updated function to logging_utils.py + +# Add logging to specific OWL functions if possible +def patch_owl_logging(): + """Try to patch specific OWL functions to add logging.""" + try: + from owl import utils + + # If run_society exists in utils, patch it to log + if hasattr(utils, 'run_society'): + original_run = utils.run_society + + def logged_run_society(*args, **kwargs): + logging.info("🦉 OWL run_society called") + try: + result = original_run(*args, **kwargs) + logging.info("🦉 OWL run_society completed") + return result + except Exception as e: + logging.error(f"🦉 OWL run_society error: {str(e)}") + raise + + # Replace the original function + utils.run_society = logged_run_society + logging.info("🦉 OWL run_society patched with logging") + + return True + except ImportError: + logging.warning("⚠️ Could not patch OWL logging - module not found") + return False + except Exception as e: + logging.warning(f"⚠️ Error patching OWL logging: {str(e)}") + return False \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/main.py b/community_usecase/OWL Interview Preparation Assistant/main.py new file mode 100644 index 0000000..6e35199 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/main.py @@ -0,0 +1,335 @@ +#main.py +import os +import logging +import time +from typing import Dict, Any, Callable, Optional +from pathlib import Path +import sys + +# Add parent directory to path for OWL imports +sys.path.append('../') +from dotenv import load_dotenv +import numpy as np # Explicitly import numpy to avoid 'numpy' errors +from camel.models import ModelFactory +from camel.types import ModelPlatformType, ModelType +from camel.toolkits import ( + SearchToolkit, + BrowserToolkit, + CodeExecutionToolkit +) +from camel.societies import RolePlaying +from camel.configs import ChatGPTConfig +from owl.utils import run_society # Official run_society with round_limit support + +# Import prompt templates +from config.prompts import ( + get_system_prompt, + get_company_research_prompt, + get_question_generator_prompt, + get_preparation_plan_prompt +) + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +# Load environment variables +load_dotenv() + +# Create the output directory for interview preparation materials +INTERVIEW_PREP_DIR = "./interview_prep" +os.makedirs(INTERVIEW_PREP_DIR, exist_ok=True) + +def run_society_with_strict_limit(society, round_limit=5, progress_callback=None): + """Wrapper around run_society to ensure round limit is strictly enforced + + This implementation hijacks the step method to force termination after a specific number of rounds. + """ + # Track rounds manually + round_count = 0 + + # Save original step function + original_step = society.step + + # Override the step method + def limited_step(*args, **kwargs): + nonlocal round_count + round_count += 1 + + # Report progress if callback is provided + if progress_callback and callable(progress_callback): + progress_callback(round_count, round_limit) + + # Force termination after reaching the round limit + if round_count >= round_limit: + logger.info(f"Reached round limit of {round_limit}, forcibly terminating.") + # Force a TASK_DONE in the user response to trigger termination + result = original_step(*args, **kwargs) + if len(result) >= 2 and hasattr(result[1], 'msgs') and result[1].msgs and len(result[1].msgs) > 0: + result[1].msgs[0].content += "\n\nTASK_DONE" + result[1].terminated = True + return result + + return original_step(*args, **kwargs) + + # Replace the step method + society.step = limited_step + + try: + # Run the conversation with the standard run_society function + answer, chat_history, token_count = run_society(society, round_limit=round_limit) + + # Add a note about the conversation being truncated + if len(chat_history) > 0 and "truncated_note" not in chat_history[-1]: + chat_history[-1]["truncated_note"] = True + if "assistant" in chat_history[-1]: + chat_history[-1]["assistant"] += "\n\n[Note: This conversation was limited to maintain response quality.]" + + return answer, chat_history, token_count + + finally: + # Restore the original step method + society.step = original_step + +def construct_interview_assistant( + job_description: str, + company_name: str, + detailed: bool = True, + limited_searches: bool = True +) -> RolePlaying: + """ + Construct a specialized interview preparation assistant using OWL. + """ + # Select model based on environment variables + if os.environ.get("OPENROUTER_API_KEY"): + logger.info("Using OpenRouter with Gemini model") + model = ModelFactory.create( + model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL, + api_key=os.environ.get("OPENROUTER_API_KEY"), + model_type="google/gemini-2.0-flash-001", + url="https://openrouter.ai/api/v1", + model_config_dict={ + "temperature": 0.6, + "max_tokens": 4000, # Reduced from 10000 to avoid exceeding limits + # Do NOT use context_length - it's not a valid API parameter + } + ) + elif os.environ.get("OPENAI_API_KEY"): + logger.info("Using OpenAI model (GPT-4)") + config = ChatGPTConfig( + temperature=0.3, + max_tokens=4000 + ) + model = ModelFactory.create( + model_platform=ModelPlatformType.OPENAI, + model_type=ModelType.GPT_4O, + model_config_dict=config.as_dict() + ) + else: + raise ValueError("Either OPENAI_API_KEY or OPENROUTER_API_KEY must be set") + + # Configure toolkits - Remove FileWriteToolkit as requested + essential_tools = [ + SearchToolkit().search_duckduckgo, + SearchToolkit().search_wiki, + # Removed the FileWriteToolkit as requested + ] + + if os.environ.get("GOOGLE_API_KEY") and os.environ.get("SEARCH_ENGINE_ID"): + essential_tools.append(SearchToolkit().search_google) + + if detailed: + tools = [ + *essential_tools, + *BrowserToolkit( + headless=True, + web_agent_model=model, + planning_agent_model=model, + ).get_tools(), + *CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(), + ] + logger.info("Using full toolset for comprehensive results (detailed=True)") + else: + tools = essential_tools + logger.info("Using essential toolset for faster results (detailed=False)") + + user_agent_kwargs = {"model": model} + assistant_agent_kwargs = {"model": model, "tools": tools} + + # Build enhanced prompt asking for full, detailed output + base_prompt = get_system_prompt() + enhanced_prompt = f"""{base_prompt} +Task: Help me prepare for an interview at {company_name} for the position of {job_description}. +Requirements: +1. Provide a highly detailed, extremely comprehensive response (aim for at least 2000+ words). +2. Structure the output with clear sections, actionable insights, examples, and code where relevant. +3. Tailor the content specifically to {company_name} and the {job_description} role. +4. Do NOT truncate or summarize—provide the full explanation directly. +""" + + task_kwargs = { + "task_prompt": enhanced_prompt, + "with_task_specify": False, + } + + society = RolePlaying( + **task_kwargs, + user_role_name="job_seeker", + user_agent_kwargs=user_agent_kwargs, + assistant_role_name="interview_coach", + assistant_agent_kwargs=assistant_agent_kwargs, + ) + + # Try to set memory parameters to reduce context size + try: + # Try to access the context creator if it exists + if hasattr(society, '_context_creator') and hasattr(society._context_creator, 'max_tokens'): + society._context_creator.max_tokens = 4000 + # Alternative approach through kwargs if available + elif hasattr(society, '_context_creator_kwargs'): + society._context_creator_kwargs = {"max_tokens": 4000} + except AttributeError: + logger.warning("Could not directly set memory parameters. Using default values.") + + return society + +def research_company( + company_name: str, + detailed: bool = True, + limited_searches: bool = True, + progress_callback: Optional[Callable] = None +) -> Dict[str, Any]: + start_time = time.time() + logging.info(f"Beginning company research for {company_name}") + base_prompt = get_company_research_prompt(company_name) + enhanced_prompt = f"""{base_prompt} + +Please provide the most detailed, in-depth report possible, with no summarization or truncation. +Your response must include extensive coverage, code samples (if relevant), and be at least 2000 words long. +""" + society = construct_interview_assistant("", company_name, detailed=detailed, limited_searches=limited_searches) + society.task_prompt = enhanced_prompt + + # Use our strict wrapper function to enforce limit at exactly 5 rounds + answer, chat_history, token_count = run_society_with_strict_limit( + society, + round_limit=5, + progress_callback=progress_callback + ) + + duration = time.time() - start_time + logging.info(f"Completed company research for {company_name} in {duration:.2f} seconds") + + # Find any files that may have been generated + generated_files = [str(file) for file in Path(INTERVIEW_PREP_DIR).glob("*") if file.is_file()] + + return { + "answer": answer, + "chat_history": chat_history, + "token_count": token_count, + "generated_files": generated_files, + "duration_seconds": duration + } + +def generate_interview_questions( + job_role: str, + company_name: str, + detailed: bool = True, + limited_searches: bool = True, + progress_callback: Optional[Callable] = None +) -> Dict[str, Any]: + start_time = time.time() + logging.info(f"Starting question generation for {job_role} at {company_name} (detailed={detailed})") + + try: + # Ensure numpy is available to prevent 'numpy' errors + import numpy as np + + base_prompt = get_question_generator_prompt(job_role, company_name) + enhanced_prompt = f"""{base_prompt} + +Please provide at least 50 highly specific questions with code examples, multiple solution approaches, +and extremely thorough explanations. Aim for 3000+ words, with no truncation or summarization. +""" + society = construct_interview_assistant(job_role, company_name, detailed=detailed, limited_searches=limited_searches) + society.task_prompt = enhanced_prompt + + # Use our wrapper function to strictly enforce a limit of 5 rounds + answer, chat_history, token_count = run_society_with_strict_limit( + society, + round_limit=5, + progress_callback=progress_callback + ) + + duration = time.time() - start_time + logging.info(f"Completed question generation for {job_role} at {company_name} in {duration:.2f} seconds") + + # Find any files that were generated + generated_files = [str(file) for file in Path(INTERVIEW_PREP_DIR).glob("*") if file.is_file()] + + return { + "answer": answer, + "chat_history": chat_history, + "token_count": token_count, + "generated_files": generated_files, + "duration_seconds": duration + } + + except Exception as e: + logging.error(f"Error in question generation: {str(e)}", exc_info=True) + raise + +def create_interview_prep_plan( + job_role: str, + company_name: str, + detailed: bool = True, + limited_searches: bool = True, + progress_callback: Optional[Callable] = None +) -> Dict[str, Any]: + start_time = time.time() + logging.info(f"Starting preparation plan creation for {job_role} at {company_name} (detailed={detailed})") + + try: + base_prompt = get_preparation_plan_prompt(job_role, company_name) + enhanced_prompt = f"""{base_prompt} + +Please provide a highly thorough, step-by-step preparation plan with multiple days of tasks, +detailed technical reviews, code examples where applicable, and at least 2000 words total. +No truncation or summaries—include the full content. +""" + society = construct_interview_assistant(job_role, company_name, detailed=detailed, limited_searches=limited_searches) + society.task_prompt = enhanced_prompt + + # Use our wrapper function with strict limit of 5 rounds + answer, chat_history, token_count = run_society_with_strict_limit( + society, + round_limit=5, + progress_callback=progress_callback + ) + + duration = time.time() - start_time + logging.info(f"Completed preparation plan creation in {duration:.2f} seconds") + + # Find any files that were generated + generated_files = [str(file) for file in Path(INTERVIEW_PREP_DIR).glob("*") if file.is_file()] + + return { + "answer": answer, + "chat_history": chat_history, + "token_count": token_count, + "generated_files": generated_files, + "duration_seconds": duration + } + + except Exception as e: + logging.error(f"Error in preparation plan creation: {str(e)}", exc_info=True) + raise + +if __name__ == "__main__": + job_role = "Machine Learning Engineer" + company_name = "Google" + result = create_interview_prep_plan(job_role, company_name, detailed=True) + print(f"Answer: {result['answer']}") + print(f"Generated files: {result['generated_files']}") + print(f"Execution time: {result['duration_seconds']:.2f} seconds") + print(f"Conversation rounds: {len(result['chat_history'])}") \ No newline at end of file diff --git a/community_usecase/OWL Interview Preparation Assistant/requirements.txt b/community_usecase/OWL Interview Preparation Assistant/requirements.txt new file mode 100644 index 0000000..6b4c209 --- /dev/null +++ b/community_usecase/OWL Interview Preparation Assistant/requirements.txt @@ -0,0 +1,25 @@ +# Core dependencies +camel-ai[all]==0.2.35 +chunkr-ai>=0.0.41 +docx2markdown>=0.1.1 +streamlit>=1.24.0 + +# UI and visualization +opencv-python>=4.7.0 +matplotlib>=3.7.1 + +# Data handling +numpy>=1.24.3 +pandas>=2.0.2 + +# Utilities +python-dotenv>=1.0.0 +requests>=2.31.0 +tqdm>=4.65.0 + +# Document processing +PyPDF2>=3.0.0 +spacy>=3.5.3 + +# Install spaCy model +# Run after pip install: python -m spacy download en_core_web_sm \ No newline at end of file diff --git a/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/project.py b/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/project.py new file mode 100644 index 0000000..9d969c4 --- /dev/null +++ b/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/project.py @@ -0,0 +1,151 @@ +import streamlit as st +from dotenv import load_dotenv +from pathlib import Path +import os + +# Import Camel-AI and OWL modules +from camel.models import ModelFactory +from camel.types import ModelPlatformType, ModelType +from camel.logger import set_log_level +from camel.societies import RolePlaying +from camel.toolkits import ( + ExcelToolkit, + SearchToolkit, + CodeExecutionToolkit, +) +from owl.utils import run_society +from owl.utils import DocumentProcessingToolkit + +# Set log level to see detailed logs (optional) +set_log_level("DEBUG") + +# Load environment variables from .env file if available + +load_dotenv() + +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.OPENAI, + model_type=ModelType.GPT_4O, + model_config_dict={"temperature": 0}, + ), + "assistant": ModelFactory.create( + model_platform=ModelPlatformType.OPENAI, + model_type=ModelType.GPT_4O, + model_config_dict={"temperature": 0}, + ), + } + + # Configure toolkits + tools = [ + *CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(), + SearchToolkit().search_duckduckgo, + SearchToolkit().search_wiki, + SearchToolkit().search_baidu, + *ExcelToolkit().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 summarize_section(): + st.header("Summarize Medical Text") + text = st.text_area("Enter medical text to summarize:", height=200) + if st.button("Summarize"): + if text: + # Create a task prompt for summarization + task_prompt = f"Summarize the following medical text:\n\n{text}" + society = construct_society(task_prompt) + with st.spinner("Running summarization society..."): + answer, chat_history, token_count = run_society(society) + st.subheader("Summary:") + st.write(answer) + st.write(chat_history) + else: + st.warning("Please enter some text to summarize.") + +def write_and_refine_article_section(): + st.header("Write and Refine Research Article") + topic = st.text_input("Enter the topic for the research article:") + outline = st.text_area("Enter an outline (optional):", height=150) + if st.button("Write and Refine Article"): + if topic: + # Create a task prompt for article writing and refinement + task_prompt = f"Write a research article on the topic: {topic}." + if outline.strip(): + task_prompt += f" Use the following outline as guidance:\n{outline}" + society = construct_society(task_prompt) + with st.spinner("Running research article society..."): + print(task_prompt) + answer, chat_history, token_count = run_society(society) + st.subheader("Article:") + st.write(answer) + st.write(chat_history) + else: + st.warning("Please enter a topic for the research article.") + +def sanitize_data_section(): + st.header("Sanitize Medical Data (PHI)") + data = st.text_area("Enter medical data to sanitize:", height=200) + if st.button("Sanitize Data"): + if data: + # Create a task prompt for data sanitization + task_prompt = f"Sanitize the following medical data by removing any protected health information (PHI):\n\n{data}" + society = construct_society(task_prompt) + with st.spinner("Running data sanitization society..."): + answer, chat_history, token_count = run_society(society) + st.subheader("Sanitized Data:") + st.write(answer) + st.write(chat_history) + else: + st.warning("Please enter medical data to sanitize.") + +def main(): + st.set_page_config(page_title="Multi-Agent AI System with Camel & OWL", layout="wide") + st.title("Multi-Agent AI System with Camel-AI and OWL") + + st.sidebar.title("Select Task") + task = st.sidebar.selectbox("Choose a task:", [ + "Summarize Medical Text", + "Write and Refine Research Article", + "Sanitize Medical Data (PHI)" + ]) + + if task == "Summarize Medical Text": + summarize_section() + elif task == "Write and Refine Research Article": + write_and_refine_article_section() + elif task == "Sanitize Medical Data (PHI)": + sanitize_data_section() + +if __name__ == "__main__": + main() diff --git a/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/readme.md b/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/readme.md new file mode 100644 index 0000000..c8e6204 --- /dev/null +++ b/community_usecase/PHI_Sanitization_Summarization_and_Article_Writing/readme.md @@ -0,0 +1,100 @@ +# 🚀 Collaborative Multi-Agent AI System + +Welcome to my latest project: a **multi-agent AI platform** that automates complex tasks through teamwork! This system combines the power of **CAMEL-AI**, **OWL**, and **Streamlit** to create a seamless, interactive experience for task automation and collaboration. + +--- + +## ✨ Features + +- **🤖 Multi-Agent Teamwork**: CAMEL-AI + OWL frameworks enable real-time collaboration between autonomous agents. +- **💡 Autonomous Agents**: Agents communicate, collaborate, and validate outputs for accurate results. +- **🔗 Seamless Integration**: CAMEL-AI for agent design + OWL for real-time task management. +- **🌐 Streamlit UI**: A clean, interactive app for easy task execution. +- **🚀 Use Cases**: + - Summarize medical texts in seconds. + - Automate research article generation. + - Sanitize PHI data for compliance. + +--- + +## 🛠️ How It Works + +1. **Agent Roles**: Defined using CAMEL-AI's `RolePlaying` class. +2. **Dynamic Toolkits**: Integrated CAMEL-AI's tools for agent functionality. +3. **Real-Time Management**: OWL framework ensures smooth task execution. +4. **User-Friendly Interface**: Streamlit provides an intuitive UI for users. + +--- + +## 🚀 Getting Started + +1. **Clone the repository**: + ```bash + git clone https://github.com/Bipul70701/Multi-Agent-System-OWL.git + cd Multi-Agent-System-OWL + ``` + +2. **Create a virtual environment**: + ```bash + python -m venv venv + ``` + +3. **Activate the virtual environment**: + - On Windows: + ```bash + venv\Scripts\activate + ``` + - On macOS/Linux: + ```bash + source venv/bin/activate + ``` + +4. **Install dependencies**: + ```bash + pip install -r requirements.txt + ``` + +5. **Run the Streamlit app**: + ```bash + streamlit run app.py + ``` + +--- + +## 🔧 Key Components + +- **CAMEL-AI**: Framework for designing and managing autonomous agents. +- **OWL**: Real-time task management and collaboration. +- **Streamlit**: Interactive web app for user interaction. + +--- + +## 📂 Project Structure + +``` +Multi-Agent-System-OWL/ +├── multiagentsystem.py # Streamlit application +├── owl/ # OWL framework and utilities +│ └── utils/ # Utility functions and helpers +├── requirements.txt # List of dependencies +└── README.md # Project documentation +``` + +--- + +## 🌟 Try It Yourself + +Check out the project on GitHub: +🔗 [GitHub Repository](https://github.com/Bipul70701/Multi-Agent-System-OWL) + +--- + +## 🙌 Credits + +- **CAMEL-AI**: For the multi-agent framework. +- **OWL**: For real-time task management. +- **Streamlit**: For the interactive UI. + +--- + +Made with ❤️ by Bipul Kumar Sharma \ No newline at end of file diff --git a/community_usecase/virtual_fitting_room/run_gpt4o.py b/community_usecase/virtual_fitting_room/run_gpt4o.py index daa176d..4a7ee39 100644 --- a/community_usecase/virtual_fitting_room/run_gpt4o.py +++ b/community_usecase/virtual_fitting_room/run_gpt4o.py @@ -13,10 +13,12 @@ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= import os import logging +import functools import json +from typing import Callable, Any, Dict, List from dotenv import load_dotenv -from camel.models import ModelFactory +from camel.models import ModelFactory, BaseModelBackend from camel.toolkits import ( ExcelToolkit, @@ -24,8 +26,9 @@ from camel.toolkits import ( SearchToolkit, BrowserToolkit, FileWriteToolkit, - VirtualTryOnToolkit, + VirtualTryOnToolkit ) +from camel.toolkits.base import BaseToolkit from camel.types import ModelPlatformType from owl.utils import run_society @@ -41,16 +44,15 @@ load_dotenv(dotenv_path=str(env_path)) # set detailed log recording for debug set_log_level(level="DEBUG") logger = get_logger(__name__) -file_handler = logging.FileHandler("tool_calls.log") +file_handler = logging.FileHandler('tool_calls.log') file_handler.setLevel(logging.DEBUG) -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) logger.addHandler(file_handler) root_logger = logging.getLogger() root_logger.addHandler(file_handler) - def construct_society(question: str) -> RolePlaying: r"""Construct a society of agents based on the given question. @@ -105,7 +107,7 @@ def construct_society(question: str) -> RolePlaying: excel_toolkit = ExcelToolkit() file_toolkit = FileWriteToolkit(output_dir="./") virtual_try_on_toolkit = VirtualTryOnToolkit() - + tools = [ *browser_toolkit.get_tools(), *image_toolkit.get_tools(), @@ -142,66 +144,13 @@ def construct_society(question: str) -> RolePlaying: def main(): r"""Main function to run the OWL system with an example question.""" - question = "open https://www.uniqlo.com/eu-at/en/women/tops?path=37608%2C84986%2C85018%2C85207 which shows some clothes on sale. First, directly click one image of clothes which should be an big interactive element (don't wrongly click the small like button overlapped on the image!) to go into its specific details page and then get a partial screenshot for this clothes. Second, only after you've get the partial screenshort of the product, using your own virtual try-on toolkit (there is no built-in virtual try-on button on this website, either no third party tool required) to show me the virtual try-on result with the product." + question = f"open https://www.uniqlo.com/eu-at/en/women/tops?path=37608%2C84986%2C85018%2C85207 which shows some clothes on sale. First, directly click one image of clothes which should be an big interactive element (don't wrongly click the small like button overlapped on the image!) to go into its specific details page and then get a partial screenshot for this clothes. Second, only after you've get the partial screenshort of the product, using your own virtual try-on toolkit (there is no built-in virtual try-on button on this website, either no third party tool required) to show me the virtual try-on result with the product." # Construct and run the society society = construct_society(question) answer, chat_history, token_count = run_society(society) - - # record tool using history (for debug) - analyze_chat_history(chat_history) + # output the result print(f"\033[94mAnswer: {answer}\033[0m") - -def analyze_chat_history(chat_history): - r"""分析聊天历史记录,提取工具调用信息。""" - print("\n============ 工具调用分析 ============") - logger.info("========== 开始分析聊天历史中的工具调用 ==========") - - tool_calls = [] - for i, message in enumerate(chat_history): - if message.get("role") == "assistant" and "tool_calls" in message: - for tool_call in message.get("tool_calls", []): - if tool_call.get("type") == "function": - function = tool_call.get("function", {}) - tool_info = { - "call_id": tool_call.get("id"), - "name": function.get("name"), - "arguments": function.get("arguments"), - "message_index": i, - } - tool_calls.append(tool_info) - print( - f"工具调用: {function.get('name')} 参数: {function.get('arguments')}" - ) - logger.info( - f"工具调用: {function.get('name')} 参数: {function.get('arguments')}" - ) - - elif message.get("role") == "tool" and "tool_call_id" in message: - # 找到对应的工具调用 - for tool_call in tool_calls: - if tool_call.get("call_id") == message.get("tool_call_id"): - result = message.get("content", "") - result_summary = ( - result[:100] + "..." if len(result) > 100 else result - ) - print(f"工具结果: {tool_call.get('name')} 返回: {result_summary}") - logger.info( - f"工具结果: {tool_call.get('name')} 返回: {result_summary}" - ) - - print(f"总共发现 {len(tool_calls)} 个工具调用") - logger.info(f"总共发现 {len(tool_calls)} 个工具调用") - logger.info("========== 结束分析聊天历史中的工具调用 ==========") - - # 将完整聊天历史保存到文件 - with open("chat_history.json", "w", encoding="utf-8") as f: - json.dump(chat_history, f, ensure_ascii=False, indent=2) - - print("记录已保存到 chat_history.json") - print("============ 分析结束 ============\n") - - if __name__ == "__main__": main() diff --git a/examples/run_gemini.py b/examples/run_gemini.py new file mode 100644 index 0000000..19042cf --- /dev/null +++ b/examples/run_gemini.py @@ -0,0 +1,146 @@ +# ========= 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 ( + AudioAnalysisToolkit, + CodeExecutionToolkit, + ExcelToolkit, + ImageAnalysisToolkit, + SearchToolkit, + VideoAnalysisToolkit, + 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.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "assistant": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "browsing": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "planning": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "video": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "image": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + model_config_dict={"temperature": 0}, + ), + "document": ModelFactory.create( + model_platform=ModelPlatformType.GEMINI, + model_type=ModelType.GEMINI_2_5_PRO_EXP, + 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(), + SearchToolkit().search_duckduckgo, + SearchToolkit().search_google, # Comment this out if you don't have google search + SearchToolkit().search_wiki, + *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 = "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." + + # 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() diff --git a/owl/.env_template b/owl/.env_template index 2565a75..59ff30b 100644 --- a/owl/.env_template +++ b/owl/.env_template @@ -4,39 +4,42 @@ #=========================================== # OPENAI API (https://platform.openai.com/api-keys) -OPENAI_API_KEY='Your_Key' +OPENAI_API_KEY="Your_Key" # OPENAI_API_BASE_URL="" # Qwen API (https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key) -# QWEN_API_KEY='Your_Key' +# QWEN_API_KEY="Your_Key" # QWEN_API_BASE_URL="" # DeepSeek API (https://platform.deepseek.com/api_keys) -# DEEPSEEK_API_KEY='Your_Key' +# DEEPSEEK_API_KEY="Your_Key" # DEEPSEEK_API_BASE_URL="" # GROQ API (https://console.groq.com/) -# GROQ_API_KEY='Your_Key' +# GROQ_API_KEY="Your_Key" # GROQ_API_BASE_URL="" # Azure OpenAI API # AZURE_OPENAI_BASE_URL="" # AZURE_API_VERSION="" -# AZURE_OPENAI_API_KEY="" +# AZURE_OPENAI_API_KEY="Your_Key" # AZURE_DEPLOYMENT_NAME="" +#GOOGLE GEMINI API (https://ai.google.dev/gemini-api/docs/api-key) +# GEMINI_API_KEY ="Your_Key" + #=========================================== # Tools & Services API #=========================================== # Google Search API (https://coda.io/@jon-dallas/google-image-search-pack-example/search-engine-id-and-google-api-key-3) -GOOGLE_API_KEY='Your_Key' -SEARCH_ENGINE_ID='Your_ID' +GOOGLE_API_KEY="Your_Key" +SEARCH_ENGINE_ID="Your_ID" # Chunkr API (https://chunkr.ai/) -CHUNKR_API_KEY='Your_Key' +CHUNKR_API_KEY="Your_Key" # Firecrawl API (https://www.firecrawl.dev/) -FIRECRAWL_API_KEY='Your_Key' +FIRECRAWL_API_KEY="Your_Key" #FIRECRAWL_API_URL="https://api.firecrawl.dev" \ No newline at end of file diff --git a/owl/webapp.py b/owl/webapp.py index 54687ec..603aeea 100644 --- a/owl/webapp.py +++ b/owl/webapp.py @@ -244,6 +244,7 @@ def get_latest_logs(max_lines=100, queue_source=None): 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_deepseek_zh": "Using deepseek model to process Chinese tasks", "run_openai_compatible_model": "Using openai compatible model to process tasks", "run_ollama": "Using local ollama model to process tasks", diff --git a/owl/webapp_backup.py b/owl/webapp_backup.py index 912a2e6..a34fc2b 100644 --- a/owl/webapp_backup.py +++ b/owl/webapp_backup.py @@ -265,7 +265,7 @@ MODULE_DESCRIPTIONS = { "run_deepseek_zh": "使用deepseek模型处理中文任务", "run_terminal_zh": "终端模式:可执行命令行操作,支持网络搜索、文件处理等功能。适合需要系统交互的任务,使用OpenAI模型", "run_gaia_roleplaying": "GAIA基准测试实现,用于评估Agent能力", - "run_openai_compatiable_model": "使用openai兼容模型处理任务", + "run_openai_compatible_model": "使用openai兼容模型处理任务", "run_ollama": "使用本地ollama模型处理任务", "run_qwen_mini_zh": "使用qwen模型最小化配置处理任务", "run_qwen_zh": "使用qwen模型处理任务", diff --git a/owl/webapp_jp.py b/owl/webapp_jp.py new file mode 100644 index 0000000..893dd81 --- /dev/null +++ b/owl/webapp_jp.py @@ -0,0 +1,1313 @@ +# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= +# Apache License 2.0(「ライセンス」)に基づいてライセンスされています。 +# あなたはライセンスに準拠している場合を除き、このファイルを使用できません。 +# ライセンスのコピーは以下から入手できます。 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 適用される法律で要求されているか、書面で合意されていない限り、 +# ライセンスに基づいて配布されるソフトウェアは「現状のまま」で、 +# 明示的または黙示的ないかなる種類の保証や条件もなく配布されます。 +# ライセンスに基づく特定の言語での権限と制限については、ライセンスを参照してください。 +# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. ========= +# 正しいモジュールパスからインポート +from utils import run_society +import os +import gradio as gr +import time +import json +import logging +import datetime +from typing import Tuple +import importlib +from dotenv import load_dotenv, set_key, find_dotenv, unset_key +import threading +import queue +import re + +os.environ["PYTHONIOENCODING"] = "utf-8" + + +# ロギングシステムを設定 +def setup_logging(): + """ログをファイル、メモリキュー、およびコンソールに出力するようにロギングシステムを設定""" + # logsディレクトリを作成(存在しない場合) + logs_dir = os.path.join(os.path.dirname(__file__), "logs") + os.makedirs(logs_dir, exist_ok=True) + + # ログファイル名を生成(現在の日付を使用) + current_date = datetime.datetime.now().strftime("%Y-%m-%d") + log_file = os.path.join(logs_dir, f"gradio_log_{current_date}.txt") + + # ルートロガーを設定(すべてのログをキャプチャ) + root_logger = logging.getLogger() + + # 重複ログを避けるために既存のハンドラをクリア + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + + root_logger.setLevel(logging.INFO) + + # ファイルハンドラを作成 + file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a") + file_handler.setLevel(logging.INFO) + + # コンソールハンドラを作成 + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + + # フォーマッタを作成 + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + file_handler.setFormatter(formatter) + console_handler.setFormatter(formatter) + + # Add handlers to root logger + root_logger.addHandler(file_handler) + root_logger.addHandler(console_handler) + + logging.info("ログシステムが初期化されました、ログファイル: %s", log_file) + return log_file + + +# グローバル変数 +LOG_FILE = None +LOG_QUEUE: queue.Queue = queue.Queue() # ログキュー +STOP_LOG_THREAD = threading.Event() +CURRENT_PROCESS = None # 現在実行中のプロセスを追跡するために使用 +STOP_REQUESTED = threading.Event() # 停止が要求されたかどうかをマークするために使用 + + +# ログの読み取りと更新の関数 +def log_reader_thread(log_file): + """継続的にログファイルを読み取り、新しい行をキューに追加するバックグラウンドスレッド""" + try: + with open(log_file, "r", encoding="utf-8") as f: + # ファイルの末尾に移動 + f.seek(0, 2) + + while not STOP_LOG_THREAD.is_set(): + line = f.readline() + if line: + LOG_QUEUE.put(line) # 会話記録キューに追加 + else: + # 新しい行がない場合は短時間待機 + time.sleep(0.1) + except Exception as e: + logging.error(f"ログリーダースレッドエラー: {str(e)}") + + +def get_latest_logs(max_lines=100, queue_source=None): + """キューから最新のログ行を取得するか、キューが空の場合はファイルから直接読み取る + + 引数: + max_lines: 返す最大行数 + queue_source: 使用するキューを指定、デフォルトはLOG_QUEUE + + 戻り値: + str: ログ内容 + """ + logs = [] + log_queue = queue_source if queue_source else LOG_QUEUE + + # 元のキューから削除せずに処理できるように、ログを保存する一時キューを作成 + temp_queue = queue.Queue() + temp_logs = [] + + try: + # キューから利用可能なすべてのログ行を取得 + while not log_queue.empty() and len(temp_logs) < max_lines: + log = log_queue.get_nowait() + temp_logs.append(log) + temp_queue.put(log) # ログを一時キューに戻す + except queue.Empty: + pass + + # 会話記録を処理 + logs = temp_logs + + # 新しいログがないか、十分なログがない場合は、ファイルから直接最後の数行を読み取る + if len(logs) < max_lines and LOG_FILE and os.path.exists(LOG_FILE): + try: + with open(LOG_FILE, "r", encoding="utf-8") as f: + all_lines = f.readlines() + # キューにすでにいくつかのログがある場合は、必要な残りの行だけを読み取る + remaining_lines = max_lines - len(logs) + file_logs = ( + all_lines[-remaining_lines:] + if len(all_lines) > remaining_lines + else all_lines + ) + + # ファイルログをキューログの前に追加 + logs = file_logs + logs + except Exception as e: + error_msg = f"ログファイルの読み取りエラー: {str(e)}" + logging.error(error_msg) + if not logs: # ログがない場合のみエラーメッセージを追加 + logs = [error_msg] + + # まだログがない場合は、プロンプトメッセージを返す + if not logs: + return "初期化中..." + + # ログをフィルタリングし、'camel.agents.chat_agent - INFO'を含むログのみを保持 + filtered_logs = [] + for log in logs: + if "camel.agents.chat_agent - INFO" in log: + filtered_logs.append(log) + + # フィルタリング後にログがない場合は、プロンプトメッセージを返す + if not filtered_logs: + return "まだ会話記録はありません。" + + # Process log content, extract the latest user and assistant messages + simplified_logs = [] + + # Use a set to track messages that have already been processed, to avoid duplicates + processed_messages = set() + + def process_message(role, content): + # Create a unique identifier to track messages + msg_id = f"{role}:{content}" + if msg_id in processed_messages: + return None + + processed_messages.add(msg_id) + content = content.replace("\\n", "\n") + lines = [line.strip() for line in content.split("\n")] + content = "\n".join(lines) + + role_emoji = "🙋" if role.lower() == "user" else "🤖" + role_ja = "ユーザー" if role.lower() == "user" else "アシスタント" + return f"""### {role_emoji} {role_ja}エージェント + +{content}""" + + for log in filtered_logs: + formatted_messages = [] + # Try to extract message array + messages_match = re.search( + r"Model (.*?), index (\d+), processed these messages: (\[.*\])", log + ) + + if messages_match: + try: + messages = json.loads(messages_match.group(3)) + for msg in messages: + if msg.get("role") in ["user", "assistant"]: + formatted_msg = process_message( + msg.get("role"), msg.get("content", "") + ) + if formatted_msg: + formatted_messages.append(formatted_msg) + except json.JSONDecodeError: + pass + + # If JSON parsing fails or no message array is found, try to extract conversation content directly + if not formatted_messages: + user_pattern = re.compile(r"\{'role': 'user', 'content': '(.*?)'\}") + assistant_pattern = re.compile( + r"\{'role': 'assistant', 'content': '(.*?)'\}" + ) + + for content in user_pattern.findall(log): + formatted_msg = process_message("user", content) + if formatted_msg: + formatted_messages.append(formatted_msg) + + for content in assistant_pattern.findall(log): + formatted_msg = process_message("assistant", content) + if formatted_msg: + formatted_messages.append(formatted_msg) + + if formatted_messages: + simplified_logs.append("\n\n".join(formatted_messages)) + + # Format log output, ensure appropriate separation between each conversation record + formatted_logs = [] + for i, log in enumerate(simplified_logs): + # Remove excess whitespace characters from beginning and end + log = log.strip() + + formatted_logs.append(log) + + # Ensure each conversation record ends with a newline + if not log.endswith("\n"): + formatted_logs.append("\n") + + return "\n".join(formatted_logs) + + +# モジュールの説明を含む辞書 +MODULE_DESCRIPTIONS = { + "run": "デフォルトモード: OpenAIモデルのデフォルトエージェント協力モードを使用し、ほとんどのタスクに適しています。", + "run_mini": "最小限の設定でOpenAIモデルを使用してタスクを処理します", + "run_deepseek_zh": "中国語タスクを処理するためにdeepseekモデルを使用します", + "run_openai_compatible_model": "OpenAI互換モデルを使用してタスクを処理します", + "run_ollama": "ローカルのollamaモデルを使用してタスクを処理します", + "run_qwen_mini_zh": "最小限の設定でqwenモデルを使用してタスクを処理します", + "run_qwen_zh": "qwenモデルを使用して中国語タスクを処理します", + "run_azure_openai": "Azure OpenAIモデルを使用してタスクを処理します", + "run_groq": "groqモデルを使用してタスクを処理します", +} + + +# デフォルトの環境変数テンプレート +DEFAULT_ENV_TEMPLATE = """#=========================================== +# モデル & API +# (参照: https://docs.camel-ai.org/key_modules/models.html#) +#=========================================== + +# OPENAI API (https://platform.openai.com/api-keys) +OPENAI_API_KEY='あなたのキー' +# OPENAI_API_BASE_URL="" + +# Azure OpenAI API +# AZURE_OPENAI_BASE_URL="" +# AZURE_API_VERSION="" +# AZURE_OPENAI_API_KEY="" +# AZURE_DEPLOYMENT_NAME="" + + +# Qwen API (https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key) +QWEN_API_KEY='あなたのキー' + +# DeepSeek API (https://platform.deepseek.com/api_keys) +DEEPSEEK_API_KEY='あなたのキー' + +#=========================================== +# ツール & サービス API +#=========================================== + +# Google Search API (https://coda.io/@jon-dallas/google-image-search-pack-example/search-engine-id-and-google-api-key-3) +GOOGLE_API_KEY='あなたのキー' +SEARCH_ENGINE_ID='あなたのID' + +# Chunkr API (https://chunkr.ai/) +CHUNKR_API_KEY='あなたのキー' + +# Firecrawl API (https://www.firecrawl.dev/) +FIRECRAWL_API_KEY='あなたのキー' +#FIRECRAWL_API_URL="https://api.firecrawl.dev" +""" + + +def validate_input(question: str) -> bool: + """ユーザー入力が有効かどうかを検証 + + 引数: + question: ユーザーの質問 + + 戻り値: + bool: 入力が有効かどうか + """ + # 入力が空またはスペースのみかどうかをチェック + if not question or question.strip() == "": + return False + return True + + +def run_owl(question: str, example_module: str) -> Tuple[str, str, str]: + """OWLシステムを実行して結果を返す + + 引数: + question: ユーザーの質問 + example_module: インポートする例モジュール名(例:"run_terminal_zh"や"run_deep") + + 戻り値: + Tuple[...]: 回答、トークン数、ステータス + """ + global CURRENT_PROCESS + + # Validate input + if not validate_input(question): + logging.warning("ユーザーが無効な入力を送信しました") + return ( + "有効な質問を入力してください", + "0", + "❌ エラー: 無効な入力質問", + ) + + try: + # Ensure environment variables are loaded + load_dotenv(find_dotenv(), override=True) + logging.info( + f"質問を処理中: '{question}', モジュール使用: {example_module}" + ) + + # Check if the module is in MODULE_DESCRIPTIONS + if example_module not in MODULE_DESCRIPTIONS: + logging.error(f"ユーザーがサポートされていないモジュールを選択しました: {example_module}") + return ( + f"選択されたモジュール '{example_module}' はサポートされていません", + "0", + "❌ エラー: サポートされていないモジュール", + ) + + # Dynamically import target module + module_path = f"examples.{example_module}" + try: + logging.info(f"モジュールをインポート中: {module_path}") + module = importlib.import_module(module_path) + except ImportError as ie: + logging.error(f"モジュール {module_path} をインポートできません: {str(ie)}") + return ( + f"モジュールをインポートできません: {module_path}", + "0", + f"❌ エラー: モジュール {example_module} が存在しないか、読み込めません - {str(ie)}", + ) + except Exception as e: + logging.error( + f"モジュール {module_path} のインポート中にエラーが発生しました: {str(e)}" + ) + return ( + f"モジュールのインポート中にエラーが発生しました: {module_path}", + "0", + f"❌ エラー: {str(e)}", + ) + + # Check if it contains the construct_society function + if not hasattr(module, "construct_society"): + logging.error( + f"construct_society 関数がモジュール {module_path} に見つかりません" + ) + return ( + f"construct_society 関数がモジュール {module_path} に見つかりません", + "0", + "❌ エラー: モジュールインターフェースが互換性がありません", + ) + + # Build society simulation + try: + logging.info("社会シミュレーションを構築中...") + society = module.construct_society(question) + + except Exception as e: + logging.error(f"社会シミュレーションの構築中にエラーが発生しました: {str(e)}") + return ( + f"社会シミュレーションの構築中にエラーが発生しました: {str(e)}", + "0", + f"❌ エラー: 構築に失敗しました - {str(e)}", + ) + + # Run society simulation + try: + logging.info("社会シミュレーションを実行中...") + answer, chat_history, token_info = run_society(society) + logging.info("社会シミュレーションが完了しました") + except Exception as e: + logging.error(f"社会シミュレーションの実行中にエラーが発生しました: {str(e)}") + return ( + f"社会シミュレーションの実行中にエラーが発生しました: {str(e)}", + "0", + f"❌ エラー: 実行に失敗しました - {str(e)}", + ) + + # Safely get token count + if not isinstance(token_info, dict): + token_info = {} + + completion_tokens = token_info.get("completion_token_count", 0) + prompt_tokens = token_info.get("prompt_token_count", 0) + total_tokens = completion_tokens + prompt_tokens + + logging.info( + f"処理が完了しました、トークン使用量: 完了={completion_tokens}, プロンプト={prompt_tokens}, 合計={total_tokens}" + ) + + return ( + answer, + f"完了トークン: {completion_tokens:,} | プロンプトトークン: {prompt_tokens:,} | 合計: {total_tokens:,}", + "✅ 正常に完了しました", + ) + + except Exception as e: + logging.error( + f"質問の処理中に予期しないエラーが発生しました: {str(e)}" + ) + return (f"エラーが発生しました: {str(e)}", "0", f"❌ エラー: {str(e)}") + + +def update_module_description(module_name: str) -> str: + """選択されたモジュールの説明を返す""" + return MODULE_DESCRIPTIONS.get(module_name, "説明はありません") + + +# フロントエンドから設定された環境変数を保存 +WEB_FRONTEND_ENV_VARS: dict[str, str] = {} + + +def init_env_file(): + """.envファイルが存在しない場合に初期化する""" + dotenv_path = find_dotenv() + if not dotenv_path: + with open(".env", "w") as f: + f.write(DEFAULT_ENV_TEMPLATE) + dotenv_path = find_dotenv() + return dotenv_path + + +def load_env_vars(): + """環境変数を読み込み、辞書形式で返す + + 戻り値: + dict: 環境変数辞書、各値は値とソースを含むタプル(value, source) + """ + dotenv_path = init_env_file() + load_dotenv(dotenv_path, override=True) + + # .envファイルから環境変数を読み込む + env_file_vars = {} + with open(dotenv_path, "r") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): + if "=" in line: + key, value = line.split("=", 1) + env_file_vars[key.strip()] = value.strip().strip("\"'") + + # システム環境変数から取得 + system_env_vars = { + k: v + for k, v in os.environ.items() + if k not in env_file_vars and k not in WEB_FRONTEND_ENV_VARS + } + + # 環境変数をマージしてソースをマーク + env_vars = {} + + # システム環境変数を追加(最低優先度) + for key, value in system_env_vars.items(): + env_vars[key] = (value, "システム") + + # .envファイル環境変数を追加(中程度の優先度) + for key, value in env_file_vars.items(): + env_vars[key] = (value, ".envファイル") + + # フロントエンドで設定された環境変数を追加(最高優先度) + for key, value in WEB_FRONTEND_ENV_VARS.items(): + env_vars[key] = (value, "フロントエンド設定") + # オペレーティングシステムの環境変数も更新されていることを確認 + os.environ[key] = value + + return env_vars + + +def save_env_vars(env_vars): + """環境変数を.envファイルに保存 + + 引数: + env_vars: 辞書、キーは環境変数名、値は文字列または(value, source)タプル + """ + try: + dotenv_path = init_env_file() + + # Save each environment variable + for key, value_data in env_vars.items(): + if key and key.strip(): # Ensure key is not empty + # Handle case where value might be a tuple + if isinstance(value_data, tuple): + value = value_data[0] + else: + value = value_data + + set_key(dotenv_path, key.strip(), value.strip()) + + # Reload environment variables to ensure they take effect + load_dotenv(dotenv_path, override=True) + + return True, "環境変数が正常に保存されました!" + except Exception as e: + return False, f"環境変数の保存中にエラーが発生しました: {str(e)}" + + +def add_env_var(key, value, from_frontend=True): + """単一の環境変数を追加または更新 + + 引数: + key: 環境変数名 + value: 環境変数値 + from_frontend: フロントエンド設定からかどうか、デフォルトはTrue + """ + try: + if not key or not key.strip(): + return False, "変数名は空にできません" + + key = key.strip() + value = value.strip() + + # If from frontend, add to frontend environment variable dictionary + if from_frontend: + WEB_FRONTEND_ENV_VARS[key] = value + # Directly update system environment variables + os.environ[key] = value + + # Also update .env file + dotenv_path = init_env_file() + set_key(dotenv_path, key, value) + load_dotenv(dotenv_path, override=True) + + return True, f"環境変数 {key} が正常に追加/更新されました!" + except Exception as e: + return False, f"環境変数の追加中にエラーが発生しました: {str(e)}" + + +def delete_env_var(key): + """環境変数を削除""" + try: + if not key or not key.strip(): + return False, "変数名は空にできません" + + key = key.strip() + + # Delete from .env file + dotenv_path = init_env_file() + unset_key(dotenv_path, key) + + # Delete from frontend environment variable dictionary + if key in WEB_FRONTEND_ENV_VARS: + del WEB_FRONTEND_ENV_VARS[key] + + # Also delete from current process environment + if key in os.environ: + del os.environ[key] + + return True, f"環境変数 {key} が正常に削除されました!" + except Exception as e: + return False, f"環境変数の削除中にエラーが発生しました: {str(e)}" + + +def is_api_related(key: str) -> bool: + """環境変数がAPI関連かどうかを判断 + + 引数: + key: 環境変数名 + + 戻り値: + bool: API関連かどうか + """ + # API関連キーワード + api_keywords = [ + "api", + "key", + "token", + "secret", + "password", + "openai", + "qwen", + "deepseek", + "google", + "search", + "hf", + "hugging", + "chunkr", + "firecrawl", + ] + + # API関連キーワードが含まれているか確認(大文字小文字を区別しない) + return any(keyword in key.lower() for keyword in api_keywords) + + +def get_api_guide(key: str) -> str: + """環境変数名に基づいて対応するAPIガイドを返す + + 引数: + key: 環境変数名 + + 戻り値: + str: APIガイドリンクまたは説明 + """ + key_lower = key.lower() + if "openai" in key_lower: + return "https://platform.openai.com/api-keys" + elif "qwen" in key_lower or "dashscope" in key_lower: + return "https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key" + elif "deepseek" in key_lower: + return "https://platform.deepseek.com/api_keys" + elif "google" in key_lower: + return "https://coda.io/@jon-dallas/google-image-search-pack-example/search-engine-id-and-google-api-key-3" + elif "search_engine_id" in key_lower: + return "https://coda.io/@jon-dallas/google-image-search-pack-example/search-engine-id-and-google-api-key-3" + elif "chunkr" in key_lower: + return "https://chunkr.ai/" + elif "firecrawl" in key_lower: + return "https://www.firecrawl.dev/" + else: + return "" + + +def update_env_table(): + """環境変数テーブル表示を更新し、API関連の環境変数のみを表示""" + env_vars = load_env_vars() + # Filter out API-related environment variables + api_env_vars = {k: v for k, v in env_vars.items() if is_api_related(k)} + # Convert to list format to meet Gradio Dataframe requirements + # Format: [Variable name, Variable value, Guide link] + result = [] + for k, v in api_env_vars.items(): + guide = get_api_guide(k) + # If there's a guide link, create a clickable link + guide_link = ( + f"🔗 取得" + if guide + else "" + ) + result.append([k, v[0], guide_link]) + return result + + +def save_env_table_changes(data): + """環境変数テーブルへの変更を保存 + + 引数: + data: データフレームデータ、おそらくpandas DataFrameオブジェクト + + 戻り値: + str: 操作ステータス情報、HTML形式のステータスメッセージを含む + """ + try: + logging.info( + f"環境変数テーブルデータの処理を開始します、タイプ: {type(data)}" + ) + + # Get all current environment variables + current_env_vars = load_env_vars() + processed_keys = set() # Record processed keys to detect deleted variables + + # Process pandas DataFrame object + import pandas as pd + + if isinstance(data, pd.DataFrame): + # Get column name information + columns = data.columns.tolist() + logging.info(f"DataFrameの列名: {columns}") + + # Iterate through each row of the DataFrame + for index, row in data.iterrows(): + # Use column names to access data + if len(columns) >= 3: + # Get variable name and value (column 0 is name, column 1 is value) + key = row[0] if isinstance(row, pd.Series) else row.iloc[0] + value = row[1] if isinstance(row, pd.Series) else row.iloc[1] + + # Check if it's an empty row or deleted variable + if ( + key and str(key).strip() + ): # If key name is not empty, add or update + logging.info( + f"環境変数の処理: {key} = {value}" + ) + add_env_var(key, str(value)) + processed_keys.add(key) + # Process other formats + elif isinstance(data, dict): + logging.info(f"辞書形式データのキー: {list(data.keys())}") + # If dictionary format, try different keys + if "data" in data: + rows = data["data"] + elif "values" in data: + rows = data["values"] + elif "value" in data: + rows = data["value"] + else: + # Try using dictionary directly as row data + rows = [] + for key, value in data.items(): + if key not in ["headers", "types", "columns"]: + rows.append([key, value]) + + if isinstance(rows, list): + for row in rows: + if isinstance(row, list) and len(row) >= 2: + key, value = row[0], row[1] + if key and str(key).strip(): + add_env_var(key, str(value)) + processed_keys.add(key) + elif isinstance(data, list): + # 列表格式 + for row in data: + if isinstance(row, list) and len(row) >= 2: + key, value = row[0], row[1] + if key and str(key).strip(): + add_env_var(key, str(value)) + processed_keys.add(key) + else: + logging.error(f"不明なデータ形式: {type(data)}") + return f"❌ 保存に失敗しました: 不明なデータ形式 {type(data)}" + + # Process deleted variables - check if there are variables in current environment not appearing in the table + api_related_keys = {k for k in current_env_vars.keys() if is_api_related(k)} + keys_to_delete = api_related_keys - processed_keys + + # Delete variables no longer in the table + for key in keys_to_delete: + logging.info(f"環境変数の削除: {key}") + delete_env_var(key) + + return "✅ 環境変数が正常に保存されました" + except Exception as e: + import traceback + + error_details = traceback.format_exc() + logging.error(f"環境変数の保存中にエラーが発生しました: {str(e)}\n{error_details}") + return f"❌ 保存に失敗しました: {str(e)}" + + +def get_env_var_value(key): + """環境変数の実際の値を取得 + + 優先順位: フロントエンド設定 > .envファイル > システム環境変数 + """ + # Check frontend configured environment variables + if key in WEB_FRONTEND_ENV_VARS: + return WEB_FRONTEND_ENV_VARS[key] + + # Check system environment variables (including those loaded from .env) + return os.environ.get(key, "") + + +def create_ui(): + """拡張されたGradioインターフェースを作成""" + + def clear_log_file(): + """ログファイルの内容をクリア""" + try: + if LOG_FILE and os.path.exists(LOG_FILE): + # Clear log file content instead of deleting the file + open(LOG_FILE, "w").close() + logging.info("ログファイルがクリアされました") + # Clear log queue + while not LOG_QUEUE.empty(): + try: + LOG_QUEUE.get_nowait() + except queue.Empty: + break + return "" + else: + return "" + except Exception as e: + logging.error(f"ログファイルのクリア中にエラーが発生しました: {str(e)}") + return "" + + # リアルタイムログ更新関数を作成 + def process_with_live_logs(question, module_name): + """質問を処理し、リアルタイムでログを更新""" + global CURRENT_PROCESS + + # Clear log file + clear_log_file() + + # 質問を処理するバックグラウンドスレッドを作成 + result_queue = queue.Queue() + + def process_in_background(): + try: + result = run_owl(question, module_name) + result_queue.put(result) + except Exception as e: + result_queue.put( + (f"エラーが発生しました: {str(e)}", "0", f"❌ エラー: {str(e)}") + ) + + # バックグラウンド処理スレッドを開始 + bg_thread = threading.Thread(target=process_in_background) + CURRENT_PROCESS = bg_thread # 現在のプロセスを記録 + bg_thread.start() + + # 処理が完了するのを待つ間、1秒ごとにログを更新 + while bg_thread.is_alive(): + # 会話記録表示を更新 + logs2 = get_latest_logs(100, LOG_QUEUE) + + # Always update status + yield ( + "0", + " 処理中...", + logs2, + ) + + time.sleep(1) + + # Processing complete, get results + if not result_queue.empty(): + result = result_queue.get() + answer, token_count, status = result + + # Final update of conversation record + logs2 = get_latest_logs(100, LOG_QUEUE) + + # Set different indicators based on status + if "エラー" in status: + status_with_indicator = ( + f" {status}" + ) + else: + status_with_indicator = ( + f" {status}" + ) + + yield token_count, status_with_indicator, logs2 + else: + logs2 = get_latest_logs(100, LOG_QUEUE) + yield ( + "0", + " 終了しました", + logs2, + ) + + with gr.Blocks(title="OWL", theme=gr.themes.Soft(primary_hue="blue")) as app: + gr.Markdown( + """ + # 🦉 OWL マルチエージェント協力システム + + CAMELフレームワークをベースに開発された高度なマルチエージェント協力システムで、エージェント協力を通じて複雑な問題を解決するように設計されています。 + + モデルやツールはローカルスクリプトを変更することでカスタマイズできます。 + + このウェブアプリは現在ベータ開発中です。デモンストレーションとテスト目的のみで提供されており、本番環境での使用はまだ推奨されていません。 + """ + ) + + # Add custom CSS + gr.HTML(""" + + """) + + with gr.Row(): + with gr.Column(scale=0.5): + question_input = gr.Textbox( + lines=5, + placeholder="質問を入力してください...", + label="質問", + elem_id="question_input", + show_copy_button=True, + value="Googleで検索して、camel-aiのcamelフレームワークのGitHubスター数、フォーク数などを要約し、その数値をplotパッケージを使ってPythonファイルに書き込み、ローカルに保存して、生成したPythonファイルを実行してください。", + ) + + # Enhanced module selection dropdown + # Only includes modules defined in MODULE_DESCRIPTIONS + module_dropdown = gr.Dropdown( + choices=list(MODULE_DESCRIPTIONS.keys()), + value="run", + label="機能モジュールを選択", + interactive=True, + ) + + # Module description text box + module_description = gr.Textbox( + value=MODULE_DESCRIPTIONS["run"], + label="モジュールの説明", + interactive=False, + elem_classes="module-info", + ) + + with gr.Row(): + run_button = gr.Button( + "実行", variant="primary", elem_classes="primary" + ) + + status_output = gr.HTML( + value=" 準備完了", + label="ステータス", + ) + token_count_output = gr.Textbox( + label="トークン数", interactive=False, elem_classes="token-count" + ) + + # Example questions + examples = [ + "Googleで検索して、camel-aiのcamelフレームワークのGitHubスター数、フォーク数などを要約し、その数値をplotパッケージを使ってPythonファイルに書き込み、ローカルに保存して、生成したPythonファイルを実行してください。", + "Amazonを閲覧して、プログラマーに魅力的な商品を見つけてください。商品名と価格を提供してください", + "Hello worldを表示するPythonファイルを作成し、ローカルに保存してください", + ] + + gr.Examples(examples=examples, inputs=question_input) + + gr.HTML(""" + + """) + + with gr.Tabs(): # Set conversation record as the default selected tab + with gr.TabItem("会話記録"): + # Add conversation record display area + with gr.Box(): + log_display2 = gr.Markdown( + value="まだ会話記録はありません。", + elem_classes="log-display", + ) + + with gr.Row(): + refresh_logs_button2 = gr.Button("記録を更新") + auto_refresh_checkbox2 = gr.Checkbox( + label="自動更新", value=True, interactive=True + ) + clear_logs_button2 = gr.Button( + "記録をクリア", variant="secondary" + ) + + with gr.TabItem("環境変数管理", id="env-settings"): + with gr.Box(elem_classes="env-manager-container"): + gr.Markdown(""" + ## 環境変数管理 + + ここでモデルAPIキーやその他のサービス認証情報を設定します。この情報はローカルの`.env`ファイルに保存され、APIキーが安全に保存され、ネットワークにアップロードされないことを保証します。APIキーを正しく設定することは、OWLシステムの機能にとって非常に重要です。環境変数はツールの要件に応じて柔軟に設定できます。 + """) + + # Main content divided into two-column layout + with gr.Row(): + # Left column: Environment variable management controls + with gr.Column(scale=3): + with gr.Box(elem_classes="env-controls"): + # Environment variable table - set to interactive for direct editing + gr.Markdown(""" +
+ ヒント: cp .env_template .env を実行してローカルの.envファイルを作成し、実行モジュールに応じて必要な環境変数を柔軟に設定してください +
+ """) + + # Enhanced environment variable table, supporting adding and deleting rows + env_table = gr.Dataframe( + headers=[ + "変数名", + "値", + "取得ガイド", + ], + datatype=[ + "str", + "str", + "html", + ], # Set the last column as HTML type to support links + row_count=10, # Increase row count to allow adding new variables + col_count=(3, "fixed"), + value=update_env_table, + label="APIキーと環境変数", + interactive=True, # Set as interactive, allowing direct editing + elem_classes="env-table", + ) + + # Operation instructions + gr.Markdown( + """ +
+ 操作ガイド: + +
+ """, + elem_classes="env-instructions", + ) + + # Environment variable operation buttons + with gr.Row(elem_classes="env-buttons"): + save_env_button = gr.Button( + "💾 変更を保存", + variant="primary", + elem_classes="env-button", + ) + refresh_button = gr.Button( + "🔄 リストを更新", elem_classes="env-button" + ) + + # Status display + env_status = gr.HTML( + label="操作ステータス", + value="", + elem_classes="env-status", + ) + + # 連接事件処理函数 + save_env_button.click( + fn=save_env_table_changes, + inputs=[env_table], + outputs=[env_status], + ).then(fn=update_env_table, outputs=[env_table]) + + refresh_button.click(fn=update_env_table, outputs=[env_table]) + + # Set up event handling + run_button.click( + fn=process_with_live_logs, + inputs=[question_input, module_dropdown], + outputs=[token_count_output, status_output, log_display2], + ) + + # Module selection updates description + module_dropdown.change( + fn=update_module_description, + inputs=module_dropdown, + outputs=module_description, + ) + + # Conversation record related event handling + refresh_logs_button2.click( + fn=lambda: get_latest_logs(100, LOG_QUEUE), outputs=[log_display2] + ) + + clear_logs_button2.click(fn=clear_log_file, outputs=[log_display2]) + + # Auto refresh control + def toggle_auto_refresh(enabled): + if enabled: + return gr.update(every=3) + else: + return gr.update(every=0) + + auto_refresh_checkbox2.change( + fn=toggle_auto_refresh, + inputs=[auto_refresh_checkbox2], + outputs=[log_display2], + ) + + # No longer automatically refresh logs by default + + return app + + +# メイン関数 +def main(): + try: + # ロギングシステムを初期化 + global LOG_FILE + LOG_FILE = setup_logging() + logging.info("OWL Webアプリケーションが開始されました") + + # ログ読み取りスレッドを開始 + log_thread = threading.Thread( + target=log_reader_thread, args=(LOG_FILE,), daemon=True + ) + log_thread.start() + logging.info("ログ読み取りスレッドが開始されました") + + # .envファイルを初期化(存在しない場合) + init_env_file() + app = create_ui() + + app.queue() + app.launch(share=False, favicon_path="../assets/owl-favicon.ico") + except Exception as e: + logging.error(f"アプリケーションの起動中にエラーが発生しました: {str(e)}") + print(f"アプリケーションの起動中にエラーが発生しました: {str(e)}") + import traceback + + traceback.print_exc() + + finally: + # ログスレッドが停止することを確認 + STOP_LOG_THREAD.set() + STOP_REQUESTED.set() + logging.info("アプリケーションが終了しました") + + +if __name__ == "__main__": + main() diff --git a/owl/webapp_zh.py b/owl/webapp_zh.py index e55d9cb..a1aa800 100644 --- a/owl/webapp_zh.py +++ b/owl/webapp_zh.py @@ -244,8 +244,9 @@ def get_latest_logs(max_lines=100, queue_source=None): MODULE_DESCRIPTIONS = { "run": "默认模式:使用OpenAI模型的默认的智能体协作模式,适合大多数任务。", "run_mini": "使用使用OpenAI模型最小化配置处理任务", + "run_gemini": "使用 Gemini模型处理任务", "run_deepseek_zh": "使用deepseek模型处理中文任务", - "run_openai_compatiable_model": "使用openai兼容模型处理任务", + "run_openai_compatible_model": "使用openai兼容模型处理任务", "run_ollama": "使用本地ollama模型处理任务", "run_qwen_mini_zh": "使用qwen模型最小化配置处理任务", "run_qwen_zh": "使用qwen模型处理任务", diff --git a/pyproject.toml b/pyproject.toml index 6987e74..9b984d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ keywords = [ "learning-systems" ] dependencies = [ - "camel-ai[all]==0.2.35", + "camel-ai[all]==0.2.37", "chunkr-ai>=0.0.41", "docx2markdown>=0.1.1", "gradio>=3.50.2", diff --git a/requirements.txt b/requirements.txt index 8f3194a..335b84a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -camel-ai[all]==0.2.35 +camel-ai[all]==0.2.37 chunkr-ai>=0.0.41 docx2markdown>=0.1.1 gradio>=3.50.2 diff --git a/uv.lock b/uv.lock index 2313fe4..bcaf276 100644 --- a/uv.lock +++ b/uv.lock @@ -379,6 +379,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/47/94b8fcfb8f102b45f2ca427b65a1243376d83d20c27f409170a4cc20e8ff/av-14.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:de04052374dbd36d9e8bcf2ead6501cc45e16bc13036d8cc17dacec96b7f6c51", size = 30857257 }, ] +[[package]] +name = "azure-common" +version = "1.1.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/71/f6f71a276e2e69264a97ad39ef850dca0a04fce67b12570730cb38d0ccac/azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3", size = 20914 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/55/7f118b9c1b23ec15ca05d15a578d8207aa1706bc6f7c87218efffbbf875d/azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad", size = 14462 }, +] + [[package]] name = "azure-core" version = "1.32.0" @@ -393,6 +402,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/83/325bf5e02504dbd8b4faa98197a44cdf8a325ef259b48326a2b6f17f8383/azure_core-1.32.0-py3-none-any.whl", hash = "sha256:eac191a0efb23bfa83fddf321b27b122b4ec847befa3091fa736a5c32c50d7b4", size = 198855 }, ] +[[package]] +name = "azure-search-documents" +version = "11.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-common" }, + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/7d/b45fff4a8e78ea4ad4d779c81dad34eef5300dd5c05b7dffdb85b8cb3d4f/azure_search_documents-11.5.2.tar.gz", hash = "sha256:98977dd1fa4978d3b7d8891a0856b3becb6f02cc07ff2e1ea40b9c7254ada315", size = 300346 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/1b/2cbc9de289ec025bac468d0e7140e469a215ea3371cd043486f9fda70f7d/azure_search_documents-11.5.2-py3-none-any.whl", hash = "sha256:c949d011008a4b0bcee3db91132741b4e4d50ddb3f7e2f48944d949d4b413b11", size = 298764 }, +] + [[package]] name = "azure-storage-blob" version = "12.24.1" @@ -482,7 +506,7 @@ wheels = [ [[package]] name = "camel-ai" -version = "0.2.35" +version = "0.2.37" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama" }, @@ -496,9 +520,9 @@ dependencies = [ { name = "pyyaml" }, { name = "tiktoken" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6b/9d/fac44260ebec63b9199d170fc22b5f3b77a5ab51fb6014fa962b0173f524/camel_ai-0.2.35.tar.gz", hash = "sha256:b90e54a81a73c473a0e673b14db5a32fb3eeec394d61a071cf510d87490f4d49", size = 451009 } +sdist = { url = "https://files.pythonhosted.org/packages/c0/05/1158464a89c0fde62fd916385a6f245c0864036fc575967f0eb8c97ef409/camel_ai-0.2.37.tar.gz", hash = "sha256:4196228846182dc5f0848e7db932f617a744ffeeee939251a1b09cb7d4f9c24a", size = 463910 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/6a/c38145134e6e1a92e553c336d9bb51286bc94c1334e8bc311daa37c6a742/camel_ai-0.2.35-py3-none-any.whl", hash = "sha256:3778da315e7e4893d4d841b561f9d4e0fa6e7976b430daa52e0c887ae18431ec", size = 765923 }, + { url = "https://files.pythonhosted.org/packages/d0/36/926b8b826faf694695b46e0c257f1b33f743802dea77fb5a33b78050f4d0/camel_ai-0.2.37-py3-none-any.whl", hash = "sha256:d1e7bb5ec992baa84a0fa825814e61d67e795ce547058b50eb5c4090300f09df", size = 785083 }, ] [package.optional-dependencies] @@ -540,6 +564,7 @@ all = [ { name = "linkup-sdk" }, { name = "litellm" }, { name = "mcp" }, + { name = "mem0ai" }, { name = "mistralai" }, { name = "mock" }, { name = "mypy" }, @@ -2839,6 +2864,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] +[[package]] +name = "mem0ai" +version = "0.1.74" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-search-documents" }, + { name = "openai" }, + { name = "posthog" }, + { name = "psycopg2-binary" }, + { name = "pydantic" }, + { name = "pytz" }, + { name = "qdrant-client" }, + { name = "sqlalchemy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/43/1a32d445e33e09d60d494839d82e0c236aee07c838fabd234fe4cb78d7bb/mem0ai-0.1.74.tar.gz", hash = "sha256:a19255c3096ecd6ba67dd470a4ca3711bd3e2fac1bcaf3e7f4aecde94475acaf", size = 75535 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/dc/53ca0fee5dd64cb4fe90f70df07af31baa25188f8b355113a8ac18760dbb/mem0ai-0.1.74-py3-none-any.whl", hash = "sha256:93123ec887e06c6660f9d88112e27c20bad283d71fa28cf4ccb01c4f4141b40c", size = 116513 }, +] + [[package]] name = "milvus-lite" version = "2.4.11" @@ -2879,6 +2923,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/d9/617e6af809bf3a1d468e0d58c3997b1dc219a9a9202e650d30c2fc85d481/mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f", size = 31617 }, ] +[[package]] +name = "monotonic" +version = "1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/ca/8e91948b782ddfbd194f323e7e7d9ba12e5877addf04fb2bf8fca38e86ac/monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/67/7e8406a29b6c45be7af7740456f7f37025f0506ae2e05fb9009a53946860/monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c", size = 8154 }, +] + [[package]] name = "more-itertools" version = "10.6.0" @@ -3601,7 +3654,7 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "camel-ai", extras = ["all"], specifier = "==0.2.35" }, + { name = "camel-ai", extras = ["all"], specifier = "==0.2.37" }, { name = "chunkr-ai", specifier = ">=0.0.41" }, { name = "docx2markdown", specifier = ">=0.1.1" }, { name = "gradio", specifier = ">=3.50.2" }, @@ -3847,6 +3900,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9b/fb/a70a4214956182e0d7a9099ab17d50bfcba1056188e9b14f35b9e2b62a0d/portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf", size = 18423 }, ] +[[package]] +name = "posthog" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backoff" }, + { name = "distro" }, + { name = "monotonic" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/c2/6ba36b647a9dee796032503fd695dba5f12ab36d82066af29aac0ea2a02b/posthog-3.21.0.tar.gz", hash = "sha256:62e339789f6f018b6a892357f5703d1f1e63c97aee75061b3dc97c5e5c6a5304", size = 67688 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/c7/4d9bf5d8ec29f9bab4c5c5ff2570748e7db53f98264ce2710406fcc2bbbd/posthog-3.21.0-py2.py3-none-any.whl", hash = "sha256:1e07626bb5219369dd36826881fa61711713e8175d3557db4657e64ecb351467", size = 79571 }, +] + [[package]] name = "prance" version = "23.6.21.0" @@ -4039,6 +4109,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/33/2d74d588408caedd065c2497bdb5ef83ce6082db01289a1e1147f6639802/psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8", size = 249898 }, ] +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/81/331257dbf2801cdb82105306042f7a1637cc752f65f2bb688188e0de5f0b/psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", size = 3043397 }, + { url = "https://files.pythonhosted.org/packages/e7/9a/7f4f2f031010bbfe6a02b4a15c01e12eb6b9b7b358ab33229f28baadbfc1/psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", size = 3274806 }, + { url = "https://files.pythonhosted.org/packages/e5/57/8ddd4b374fa811a0b0a0f49b6abad1cde9cb34df73ea3348cc283fcd70b4/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", size = 2851361 }, + { url = "https://files.pythonhosted.org/packages/f9/66/d1e52c20d283f1f3a8e7e5c1e06851d432f123ef57b13043b4f9b21ffa1f/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", size = 3080836 }, + { url = "https://files.pythonhosted.org/packages/a0/cb/592d44a9546aba78f8a1249021fe7c59d3afb8a0ba51434d6610cc3462b6/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", size = 3264552 }, + { url = "https://files.pythonhosted.org/packages/64/33/c8548560b94b7617f203d7236d6cdf36fe1a5a3645600ada6efd79da946f/psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", size = 3019789 }, + { url = "https://files.pythonhosted.org/packages/b0/0e/c2da0db5bea88a3be52307f88b75eec72c4de62814cbe9ee600c29c06334/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", size = 2871776 }, + { url = "https://files.pythonhosted.org/packages/15/d7/774afa1eadb787ddf41aab52d4c62785563e29949613c958955031408ae6/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", size = 2820959 }, + { url = "https://files.pythonhosted.org/packages/5e/ed/440dc3f5991a8c6172a1cde44850ead0e483a375277a1aef7cfcec00af07/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", size = 2919329 }, + { url = "https://files.pythonhosted.org/packages/03/be/2cc8f4282898306732d2ae7b7378ae14e8df3c1231b53579efa056aae887/psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", size = 2957659 }, + { url = "https://files.pythonhosted.org/packages/d0/12/fb8e4f485d98c570e00dad5800e9a2349cfe0f71a767c856857160d343a5/psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", size = 1024605 }, + { url = "https://files.pythonhosted.org/packages/22/4f/217cd2471ecf45d82905dd09085e049af8de6cfdc008b6663c3226dc1c98/psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", size = 1163817 }, + { url = "https://files.pythonhosted.org/packages/9c/8f/9feb01291d0d7a0a4c6a6bab24094135c2b59c6a81943752f632c75896d6/psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", size = 3043397 }, + { url = "https://files.pythonhosted.org/packages/15/30/346e4683532011561cd9c8dfeac6a8153dd96452fee0b12666058ab7893c/psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", size = 3274806 }, + { url = "https://files.pythonhosted.org/packages/66/6e/4efebe76f76aee7ec99166b6c023ff8abdc4e183f7b70913d7c047701b79/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", size = 2851370 }, + { url = "https://files.pythonhosted.org/packages/7f/fd/ff83313f86b50f7ca089b161b8e0a22bb3c319974096093cd50680433fdb/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", size = 3080780 }, + { url = "https://files.pythonhosted.org/packages/e6/c4/bfadd202dcda8333a7ccafdc51c541dbdfce7c2c7cda89fa2374455d795f/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", size = 3264583 }, + { url = "https://files.pythonhosted.org/packages/5d/f1/09f45ac25e704ac954862581f9f9ae21303cc5ded3d0b775532b407f0e90/psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", size = 3019831 }, + { url = "https://files.pythonhosted.org/packages/9e/2e/9beaea078095cc558f215e38f647c7114987d9febfc25cb2beed7c3582a5/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", size = 2871822 }, + { url = "https://files.pythonhosted.org/packages/01/9e/ef93c5d93f3dc9fc92786ffab39e323b9aed066ba59fdc34cf85e2722271/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", size = 2820975 }, + { url = "https://files.pythonhosted.org/packages/a5/f0/049e9631e3268fe4c5a387f6fc27e267ebe199acf1bc1bc9cbde4bd6916c/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", size = 2919320 }, + { url = "https://files.pythonhosted.org/packages/dc/9a/bcb8773b88e45fb5a5ea8339e2104d82c863a3b8558fbb2aadfe66df86b3/psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", size = 2957617 }, + { url = "https://files.pythonhosted.org/packages/e2/6b/144336a9bf08a67d217b3af3246abb1d027095dab726f0687f01f43e8c03/psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", size = 1024618 }, + { url = "https://files.pythonhosted.org/packages/61/69/3b3d7bd583c6d3cbe5100802efa5beacaacc86e37b653fc708bf3d6853b8/psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", size = 1163816 }, + { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 }, + { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 }, + { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 }, + { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 }, + { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 }, + { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 }, + { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 }, + { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 }, + { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 }, + { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 }, + { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 }, + { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 }, +] + [[package]] name = "ptyprocess" version = "0.7.0" @@ -4505,11 +4619,11 @@ wheels = [ [[package]] name = "pytz" -version = "2025.1" +version = "2024.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, ] [[package]]