Compare commits

..

118 Commits

Author SHA1 Message Date
liangxinbing 13f98a0fde update README and format code
Pre-commit checks / pre-commit-check (push) Failing after 0s
2025-03-12 00:26:25 +08:00
liangxinbing 487b44fda8 update README and format code 2025-03-11 23:07:31 +08:00
mannaandpoem 737abe4f90 Merge pull request #480 from mczhuge/main
feat: refactor the LLM (using LiteLLM & add cost calculation)
2025-03-11 22:44:29 +08:00
Mingchen Zhuge cbef113736 Update config.py
recover the original `config.py`
2025-03-11 05:44:16 -07:00
Mingchen Zhuge dc8ed530f0 add litellm in the requirements.txt 2025-03-11 15:26:11 +03:00
Mingchen Zhuge 62cfcb182e using LiteLLM to support flexible LLM providing & adding cost calculations 2025-03-11 15:23:08 +03:00
mannaandpoem 111a2bc6b1 Merge pull request #469 from cyzus/fix-infinite-loop-for-simple-query
fix infinite loop for simple query
2025-03-11 19:20:15 +08:00
Yizhou Chi 61d705f6e6 fix prompt order 2025-03-11 17:55:40 +08:00
Yizhou Chi cafd4a18da fix infinite loop 2025-03-11 17:39:28 +08:00
xiangjinyu b67d1b90e0 Adjust feishu group 2025-03-11 16:45:25 +08:00
xiangjinyu 6a89ae8256 Adjust feishu group 2025-03-11 16:42:51 +08:00
xiangjinyu 4d36a4c3e2 Adjust feishu group 2025-03-11 16:41:40 +08:00
xiangjinyu fed6fe1421 Adjust feishu group 2025-03-11 16:40:27 +08:00
xiangjinyu 3a958944a0 Adjust code structure 2025-03-11 16:32:13 +08:00
liangxinbing 8d18c05350 replace max_steps to 20 for Manus 2025-03-11 13:14:43 +08:00
liangxinbing eeefddf6bf Bug Fixing based on basy.py
Repair from Bob Feng8 Xu
2025-03-11 13:13:24 +08:00
liangxinbing 35de1f9e15 remove front-end code 2025-03-11 13:11:43 +08:00
xiangjinyu 2c0b2d1fb3 Merge remote-tracking branch 'origin/main' 2025-03-11 11:47:15 +08:00
xiangjinyu 211d0694e3 update team members in README.md 2025-03-11 11:46:57 +08:00
liangxinbing 6e8de4e35e update README 2025-03-11 10:56:57 +08:00
mannaandpoem 8280b68462 Merge pull request #432 from Feige-cn/main
modified by hook
2025-03-11 10:55:03 +08:00
Feige-cn 8018124af2 modified by hook 2025-03-11 08:39:16 +08:00
liangxinbing 229d6706a4 format code 2025-03-11 07:31:18 +08:00
rxdaozhang d9418c1668 last try 2025-03-11 02:36:50 +08:00
rxdaozhang c37c6c19b5 Revert "fix pre commit hook bug"
This reverts commit 41308f3aca.
2025-03-11 02:28:00 +08:00
rxdaozhang a256d5589a Revert "try fix pre commit hook bug in app.py"
This reverts commit 2fcad5f097.
2025-03-11 02:27:24 +08:00
rxdaozhang 2fcad5f097 try fix pre commit hook bug in app.py 2025-03-11 02:20:50 +08:00
rxdaozhang 41308f3aca fix pre commit hook bug 2025-03-11 02:18:40 +08:00
rxdaozhang 77df9772fe Merge branch 'refactor-planning' of github.com:rxdaozhang/OpenManus into refactor-planning 2025-03-11 02:15:01 +08:00
rxdaozhang 69325c701b fix multiline string SyntaxError another 2025-03-11 02:14:15 +08:00
rxdaozhang f67af3580a fix multiline string SyntaxError 2025-03-11 02:07:04 +08:00
rxdaozhang 34ede1c082 重构:使用PlanStepStatus枚举替代硬编码status 2025-03-11 01:23:16 +08:00
liangxinbing b3c4f5ce22 add Official Website 2025-03-10 23:43:44 +08:00
liangxinbing bfa5a672c2 add Official Website 2025-03-10 23:36:14 +08:00
Isaac 576090439b Merge pull request #398 from Ailerx/main
add fastapi version  in requirements.txt
2025-03-10 20:57:32 +08:00
xiangjinyu 479b47a7d6 update cite in README.md 2025-03-10 20:49:29 +08:00
xiangjinyu 9fc1ea3ee4 update cite in README.md 2025-03-10 20:48:42 +08:00
xiangjinyu 444fc91230 update cite in README.md 2025-03-10 20:46:56 +08:00
liangxinbing b23ab9a42a update Roadmap and format code 2025-03-10 20:13:06 +08:00
Ailerx e38e35d2ab add fastapi version in requirements.txt 2025-03-10 17:26:53 +08:00
xiangjinyu 110213a29f modify main.js to English 2025-03-10 16:24:07 +08:00
xiangjinyu 81b9bb3660 modify app.py to English 2025-03-10 16:13:24 +08:00
xiangjinyu f2ddc63a6f modify index.html to English 2025-03-10 16:03:55 +08:00
Isaac d661578044 Merge pull request #377 from Feige-cn/main
add manus web-ui
2025-03-10 15:45:06 +08:00
Zhaoyang Yu 8b955c7336 Update ReadMe 2025-03-10 14:55:48 +08:00
Zhaoyang Yu 1a3ae5d06b Fix H1 2025-03-10 14:39:21 +08:00
Zhaoyang Yu c3322ee72e Fix Discord link 2025-03-10 14:36:07 +08:00
Zhaoyang Yu a8ee4d1617 Fix discord link 2025-03-10 14:32:25 +08:00
Zhaoyang Yu d19e7d5acd Update official discord 2025-03-10 14:22:00 +08:00
Zhaoyang Yu 186ce2d1c5 Update 2025-03-10 14:13:12 +08:00
Zhaoyang Yu 8ae3a6c77b udpate 2025-03-10 13:45:18 +08:00
飞戈 825855d13a Delete config/config.toml 2025-03-10 12:20:21 +08:00
Feige-cn ecdbb3b26a OpenManus简易web界面 2025-03-10 12:17:07 +08:00
xyuzh c10600d36e update readme 2025-03-09 21:17:06 -07:00
xyuzh 60bb2ce00f update readme 2025-03-09 14:01:57 -07:00
xyuzh 7501b74030 update readme 2025-03-09 13:53:22 -07:00
liangxinbing 0345aa0e95 update Roadmap 2025-03-09 23:58:29 +08:00
liangxinbing 5703502fab update README 2025-03-09 22:28:29 +08:00
liangxinbing 915aed17d1 update logo and format code 2025-03-09 22:27:20 +08:00
liangxinbing 38bb658031 update logo 2025-03-09 21:59:17 +08:00
liangxinbing 2fe2f75c21 add author 2025-03-09 21:57:52 +08:00
xyuzh 1dfc2c41ec [test] add pytest framework prototype 2025-03-09 00:08:49 -08:00
xyuzh 06e2239b70 update readme 2025-03-09 00:06:30 -08:00
liangxinbing b7c11a5db0 add CODE_OF_CONDUCT.md 2025-03-09 14:49:37 +08:00
mannaandpoem abb2516bc3 Merge pull request #318 from K-tang-mkv/tx_dev
feat: improve input handling
2025-03-09 13:40:24 +08:00
gantnocap 52b67090a0 docs: improve installation guide, add uv for fast installation 2025-03-09 13:36:51 +08:00
gantnocap 85bd75d236 feat: improve input handling
- Add support for 'quit' command alongside 'exit'

- Update input prompt to show both exit options

- Enhance empty input handling to catch both empty strings and whitespace

- Replace isspace() check with more robust empty string validation

- Optimize command checking by storing lowercase result

- Improve code readability and user experience
2025-03-09 13:11:44 +08:00
xyuzh e6ed98e53c [fix] fix overlong steps of thinking and avoid redundant steps after task completion && [add timer] set max time 1h to run the agent 2025-03-08 19:46:51 -08:00
mannaandpoem 7a9e22d093 Merge pull request #265 from fred913/main
feat(browser_use_tool): add 'get_text' action to browser use tool
2025-03-09 11:40:20 +08:00
mannaandpoem c71ee143e2 Merge pull request #312 from Kunlun-Zhu/main
Update README.md with OpenManus-RL information
2025-03-09 11:39:26 +08:00
Kunlun Zhu 0027af217c Update README_zh.md with OpenManus-RL information 2025-03-08 21:37:33 -06:00
Sheng Fan 0d0f8ab233 chore(browser_use_tool): fix code style according to pre-commit 2025-03-09 11:30:41 +08:00
Kunlun Zhu cd86cda61e Update README.md with OpenManus-RL information 2025-03-08 21:29:59 -06:00
Zhaoyang Yu 5694a5e89c update 2025-03-08 21:40:09 +08:00
Zhaoyang Yu 6164ac7bb8 Update Roadmap 2025-03-08 20:30:17 +08:00
Sheng Fan 7090490f75 feat(browser_use_tool): add 'read_links' action 2025-03-08 18:23:40 +08:00
XBsleepy 7bcc104aff Merge pull request #272 from XBsleepy/add-test
Update example preview
2025-03-08 18:05:13 +08:00
XBsleepy e47d2eeb02 merge updated example 2025-03-08 18:01:08 +08:00
XBsleepy ee3938dae5 update preview directory 2025-03-08 17:54:11 +08:00
XBsleepy 3024753e35 update japan travel plan example 2025-03-08 17:52:44 +08:00
Sheng Fan cf3603dfcc Merge branch 'mannaandpoem:main' into main 2025-03-08 17:47:30 +08:00
liangxinbing f04beec486 format examples 2025-03-08 17:20:29 +08:00
liangxinbing 56d138220a fix typo in README;
log full arguments on parse json err;
remove empty prompt for main.py and run_flow.py;
2025-03-08 17:17:40 +08:00
mannaandpoem 41063bcd2b Merge pull request #185 from XBsleepy/main
add the first example
2025-03-08 17:12:05 +08:00
Sheng Fan 6b77a99448 feat(browser_use_tool): add 'get_text' action to browser use tool 2025-03-08 16:45:18 +08:00
mannaandpoem e76055b436 Merge pull request #229 from appleboy/docs
docs: enhance README with multilingual support and community engagement
2025-03-08 16:32:12 +08:00
liangxinbing 6716a47823 remove unittest.yaml 2025-03-08 16:02:53 +08:00
liangxinbing dc28e9187b adjust code format 2025-03-08 15:54:23 +08:00
liangxinbing 35e3b5d94b add .github 2025-03-08 15:51:28 +08:00
appleboy a4340aa3b9 docs: enhance README with multilingual support and community engagement
- Update language links in the README to include Traditional and Simplified Chinese options
- Remove outdated HTML paragraph tags from the README
- Add a new README file in Traditional Chinese
- Include installation instructions and configuration steps in the new README
- Add a video demonstration section in the new README
- Enhance the roadmap section with additional planning items in both README files
- Encourage community engagement and contributions in both README files

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-03-08 13:07:50 +08:00
Zhaoyang Yu 0abdd6f9da Update Feishu group 2025-03-08 11:49:00 +08:00
XBsleepy e70332bfa2 add examples dir and the first example 2025-03-08 00:01:29 +08:00
XBsleepy d921fc4248 update logger name to seconds level 2025-03-07 23:27:31 +08:00
Aria F d9e6e9a8e5 #refactor: Update config.example.toml with blur AZURE_OPENAI 2025-03-07 21:00:22 +08:00
Aria F ecac3382ec # feat: AzureOpenaiAPI support 2025-03-07 20:55:02 +08:00
Zhaoyang Yu 5650d3170b Update star history. 2025-03-07 17:13:06 +08:00
Zhaoyang Yu 612fb4964b Update wechat groups 2025-03-07 14:03:46 +08:00
Zhaoyang Yu ab0c243850 Update discord 2025-03-07 13:58:14 +08:00
Zhaoyang Yu 29415b7393 Merge branch 'main' of https://github.com/mannaandpoem/OpenManus 2025-03-07 13:47:18 +08:00
Zhaoyang Yu af65f1f754 Update wechat group 2025-03-07 13:47:16 +08:00
liangxinbing 5226628c3e update requirements.txt 2025-03-07 13:39:29 +08:00
Zhaoyang Yu 5eed101f78 Update wechat group 6 2025-03-07 13:34:18 +08:00
Zhaoyang Yu 5eb5ea1f09 Delete demo directory 2025-03-07 13:32:08 +08:00
liangxinbing d3fb8c440c add assets and update README 2025-03-07 13:06:57 +08:00
liangxinbing 20a08b6e26 add assets and update README 2025-03-07 12:34:55 +08:00
liangxinbing 1e2559a0d1 update README 2025-03-07 12:30:11 +08:00
mannaandpoem a18cf3855d Merge pull request #18 from hugsun/main
📝 updated readme video for easy viewing
2025-03-07 12:28:57 +08:00
mannaandpoem a41e37fd1f Merge branch 'main' into main 2025-03-07 12:28:29 +08:00
liangxinbing 295da18c5d update README 2025-03-07 12:21:07 +08:00
liangxinbing ddbc58e67a add assets and update README 2025-03-07 12:13:43 +08:00
liangxinbing fa89ba33ce add assets and update README 2025-03-07 11:41:54 +08:00
wangfu 247034d90f 📝 updated readme video for easy viewing 2025-03-07 11:37:53 +08:00
liangxinbing 77efc59021 add setup.py 2025-03-07 11:15:30 +08:00
Zhaoyang Yu e55206b7ee Update README.md 2025-03-07 10:56:48 +08:00
Zhaoyang Yu 4df621c5a1 Update README_zh.md 2025-03-07 10:56:01 +08:00
Zhaoyang Yu 240a4a0f0e Update and rename README_zh to README_zh.md 2025-03-07 10:54:40 +08:00
Zhaoyang Yu 90624ecaa2 Create README_zh 2025-03-07 10:54:03 +08:00
liangxinbing e370ae6f93 update README.md 2025-03-07 10:44:50 +08:00
41 changed files with 1932 additions and 338 deletions
+4
View File
@@ -0,0 +1,4 @@
blank_issues_enabled: false
contact_links:
- name: "📑 Read online docs"
about: Find tutorials, use cases, and guides in the OpenManus documentation.
@@ -0,0 +1,14 @@
---
name: "🤔 Request new features"
about: Suggest ideas or features youd like to see implemented in OpenManus.
title: ''
labels: kind/features
assignees: ''
---
**Feature description**
<!-- Provide a clear and concise description of the proposed feature -->
**Your Feature**
<!-- Explain your idea or implementation process. Optionally, include a Pull Request URL. -->
<!-- Ensure accompanying docs/tests/examples are provided for review. -->
+25
View File
@@ -0,0 +1,25 @@
---
name: "🪲 Show me the Bug"
about: Report a bug encountered while using OpenManus and seek assistance.
title: ''
labels: kind/bug
assignees: ''
---
**Bug description**
<!-- Clearly describe the bug you encountered -->
**Bug solved method**
<!-- If resolved, explain the solution. Optionally, include a Pull Request URL. -->
<!-- If unresolved, provide additional details to aid investigation -->
**Environment information**
<!-- System: e.g., Ubuntu 22.04, Python: e.g., 3.12, OpenManus version: e.g., 0.1.0 -->
- System version:
- Python version:
- OpenManus version or branch:
- Installation method (e.g., `pip install -r requirements.txt` or `pip install -e .`):
**Screenshots or logs**
<!-- Attach screenshots or logs to help diagnose the issue -->
+17
View File
@@ -0,0 +1,17 @@
**Features**
<!-- Describe the features or bug fixes in this PR. For bug fixes, link to the issue. -->
- Feature 1
- Feature 2
**Feature Docs**
<!-- Provide RFC, tutorial, or use case links for significant updates. Optional for minor changes. -->
**Influence**
<!-- Explain the impact of these changes for reviewer focus. -->
**Result**
<!-- Include screenshots or logs of unit tests or running results. -->
**Other**
<!-- Additional notes about this PR. -->
+33
View File
@@ -0,0 +1,33 @@
name: Build and upload Python package
on:
workflow_dispatch:
release:
types: [created, published]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install setuptools wheel twine
- name: Set package version
run: |
export VERSION="${GITHUB_REF#refs/tags/v}"
sed -i "s/version=.*/version=\"${VERSION}\",/" setup.py
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py bdist_wheel sdist
twine upload dist/*
+26
View File
@@ -0,0 +1,26 @@
name: Pre-commit checks
on:
pull_request:
branches:
- '**'
push:
branches:
- '**'
jobs:
pre-commit-check:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install pre-commit and tools
run: |
python -m pip install --upgrade pip
pip install pre-commit black==23.1.0 isort==5.12.0 autoflake==2.0.1
- name: Run pre-commit hooks
run: pre-commit run --all-files
+23
View File
@@ -0,0 +1,23 @@
name: Close inactive issues
on:
schedule:
- cron: "5 0 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "inactive"
stale-issue-message: "This issue has been inactive for 30 days. Please comment if you have updates."
close-issue-message: "This issue was closed due to 45 days of inactivity. Reopen if still relevant."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}
+4 -4
View File
@@ -18,22 +18,22 @@ repos:
- id: autoflake
args: [
--remove-all-unused-imports,
--ignore-init-module-imports, # 忽略 __init__.py 中的导入
--ignore-init-module-imports,
--expand-star-imports,
--remove-duplicate-keys,
--remove-unused-variables,
--recursive,
--in-place,
--exclude=__init__.py, # 排除 __init__.py 文件
--exclude=__init__.py,
]
files: \.py$ # 只处理 Python 文件
files: \.py$
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: [
"--profile", "black", # 使用 black 兼容的配置
"--profile", "black",
"--filter-files",
"--lines-after-imports=2",
]
+162
View File
@@ -0,0 +1,162 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people.
* Being respectful of differing opinions, viewpoints, and experiences.
* Giving and gracefully accepting constructive feedback.
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience.
* Focusing on what is best not just for us as individuals, but for the overall
community.
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind.
* Trolling, insulting or derogatory comments, and personal or political attacks.
* Public or private harassment.
* Publishing others' private information, such as a physical or email address,
without their explicit permission.
* Other conduct which could reasonably be considered inappropriate in a
professional setting.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
mannaandpoem@gmail.com
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
### Slack and Discord Etiquettes
These Slack and Discord etiquette guidelines are designed to foster an inclusive, respectful, and productive environment
for all community members. By following these best practices, we ensure effective communication and collaboration while
minimizing disruptions. Lets work together to build a supportive and welcoming community!
- Communicate respectfully and professionally, avoiding sarcasm or harsh language, and remember that tone can be
difficult to interpret in text.
- Use threads for specific discussions to keep channels organized and easier to follow.
- Tag others only when their input is critical or urgent, and use @here, @channel or @everyone sparingly to minimize
disruptions.
- Be patient, as open-source contributors and maintainers often have other commitments and may need time to respond.
- Post questions or discussions in the most relevant
channel ([discord - #general](https://discord.com/channels/1125308739348594758/1138430348557025341)).
- When asking for help or raising issues, include necessary details like links, screenshots, or clear explanations to
provide context.
- Keep discussions in public channels whenever possible to allow others to benefit from the conversation, unless the
matter is sensitive or private.
- Always adhere to [our standards](https://github.com/mannaandpoem/OpenManus/blob/main/CODE_OF_CONDUCT.md#our-standards)
to ensure a welcoming and collaborative environment.
- If you choose to mute a channel, consider setting up alerts for topics that still interest you to stay engaged. For
Slack, Go to Settings → Notifications → My Keywords to add specific keywords that will notify you when mentioned. For
example, if you're here for discussions about LLMs, mute the channel if its too busy, but set notifications to alert
you only when “LLMs” appears in messages. Also for Discord, go to the channel notifications and choose the option that
best describes your need.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+77 -12
View File
@@ -1,18 +1,32 @@
# OpenManus 🙋
Manus is incredible, but OpenManus can achieve any ideas without an Invite Code 🛫!
English | [中文](README_zh.md)
Our team members @mannaandpoem @XiangJinyu @MoshiQAQ @didiforgithub from @MetaGPT built it within 3 hours!
[![GitHub stars](https://img.shields.io/github/stars/mannaandpoem/OpenManus?style=social)](https://github.com/mannaandpoem/OpenManus/stargazers)
&ensp;
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) &ensp;
[![Discord Follow](https://dcbadge.vercel.app/api/server/DYn29wFk9z?style=flat)](https://discord.gg/DYn29wFk9z)
# 👋 OpenManus
Manus is incredible, but OpenManus can achieve any idea without an *Invite Code* 🛫!
Our team members [@Xinbin Liang](https://github.com/mannaandpoem) and [@Jinyu Xiang](https://github.com/XiangJinyu) (core authors), along with [@Zhaoyang Yu](https://github.com/MoshiQAQ), [@Jiayi Zhang](https://github.com/didiforgithub), and [@Sirui Hong](https://github.com/stellaHSR), we are from [@MetaGPT](https://github.com/geekan/MetaGPT). The prototype is launched within 3 hours and we are keeping building!
It's a simple implementation, so we welcome any suggestions, contributions, and feedback!
Enjoy your own agent with OpenManus!
## Project Demo
[Demo Video](https://github.com/mannaandpoem/OpenManus/blob/main/demo/seo_website.mp4)
We're also excited to introduce [OpenManus-RL](https://github.com/OpenManus/OpenManus-RL), an open-source project dedicated to reinforcement learning (RL)- based (such as GRPO) tuning methods for LLM agents, developed collaboratively by researchers from UIUC and OpenManus.
## Project Demo
<video src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" data-canonical-src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px"></video>
## Installation
We provide two installation methods. Method 2 (using uv) is recommended for faster installation and better dependency management.
### Method 1: Using conda
1. Create a new conda environment:
```bash
@@ -33,6 +47,36 @@ cd OpenManus
pip install -r requirements.txt
```
### Method 2: Using uv (Recommended)
1. Install uv (A fast Python package installer and resolver):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
2. Clone the repository:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. Create a new virtual environment and activate it:
```bash
uv venv
source .venv/bin/activate # On Unix/macOS
# Or on Windows:
# .venv\Scripts\activate
```
4. Install dependencies:
```bash
uv pip install -r requirements.txt
```
## Configuration
OpenManus requires configuration for the LLM APIs it uses. Follow these steps to set up your configuration:
@@ -62,6 +106,7 @@ api_key = "sk-..." # Replace with your actual API key
```
## Quick Start
One line for run OpenManus:
```bash
@@ -77,19 +122,39 @@ python run_flow.py
```
## How to contribute
We welcome any friendly suggestions and helpful contributions! Just create issues or submit pull requests.
Or contact @mannaandpoem via 📧email: mannaandpoem@gmail.com
## Roadmap
- [ ] Better Planning
- [ ] Live Demos
- [ ] Replay
- [ ] RL Fine-tuned Models
- [ ] Comprehensive Benchmarks
## Community Group
Join our networking group on Feishu and share your experience with other developers!
<div align="center" style="display: flex; gap: 20px;">
<img src="assets/community_group.jpg" alt="OpenManus 交流群" width="300" />
</div>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=mannaandpoem/OpenManus&type=Date)](https://star-history.com/#mannaandpoem/OpenManus&Date)
## Acknowledgement
Thanks to [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) and [broswer-use](https://github.com/browser-use/browser-use) for providing basic support for this project!
Thanks to [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)
and [browser-use](https://github.com/browser-use/browser-use) for providing basic support for this project!
Additionally, we are grateful to [AAAJ](https://github.com/metauto-ai/agent-as-a-judge), [MetaGPT](https://github.com/geekan/MetaGPT) and [OpenHands](https://github.com/All-Hands-AI/OpenHands).
OpenManus is built by contributors from MetaGPT. Huge thanks to this agent community!
## Cite
```bibtex
@misc{openmanus2025,
author = {Xinbin Liang and Jinyu Xiang and Zhaoyang Yu and Jiayi Zhang and Sirui Hong},
title = {OpenManus: An open-source framework for building general AI agents},
year = {2025},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/mannaandpoem/OpenManus}},
}
```
+150
View File
@@ -0,0 +1,150 @@
[English](README.md) | 中文
[![GitHub stars](https://img.shields.io/github/stars/mannaandpoem/OpenManus?style=social)](https://github.com/mannaandpoem/OpenManus/stargazers)
&ensp;
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) &ensp;
[![Discord Follow](https://dcbadge.vercel.app/api/server/DYn29wFk9z?style=flat)](https://discord.gg/DYn29wFk9z)
# 👋 OpenManus
Manus 非常棒,但 OpenManus 无需邀请码即可实现任何创意 🛫!
我们的团队成员 [@mannaandpoem](https://github.com/mannaandpoem) [@XiangJinyu](https://github.com/XiangJinyu) [@MoshiQAQ](https://github.com/MoshiQAQ) [@didiforgithub](https://github.com/didiforgithub) https://github.com/stellaHSR 来自 [@MetaGPT](https://github.com/geekan/MetaGPT) 组织,我们在 3
小时内完成了原型开发并持续迭代中!
这是一个简洁的实现方案,欢迎任何建议、贡献和反馈!
用 OpenManus 开启你的智能体之旅吧!
我们也非常高兴地向大家介绍 [OpenManus-RL](https://github.com/OpenManus/OpenManus-RL),这是一个专注于基于强化学习(RL,例如 GRPO)的方法来优化大语言模型(LLM)智能体的开源项目,由来自UIUC 和 OpenManus 的研究人员合作开发。
## 项目演示
<video src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" data-canonical-src="https://private-user-images.githubusercontent.com/61239030/420168772-6dcfd0d2-9142-45d9-b74e-d10aa75073c6.mp4?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDEzMTgwNTksIm5iZiI6MTc0MTMxNzc1OSwicGF0aCI6Ii82MTIzOTAzMC80MjAxNjg3NzItNmRjZmQwZDItOTE0Mi00NWQ5LWI3NGUtZDEwYWE3NTA3M2M2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAzMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMzA3VDAzMjIzOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdiZjFkNjlmYWNjMmEzOTliM2Y3M2VlYjgyNDRlZDJmOWE3NWZhZjE1MzhiZWY4YmQ3NjdkNTYwYTU5ZDA2MzYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.UuHQCgWYkh0OQq9qsUWqGsUbhG3i9jcZDAMeHjLt5T4" controls="controls" muted="muted" class="d-block rounded-bottom-2 border-top width-fit" style="max-height:640px; min-height: 200px"></video>
## 安装指南
我们提供两种安装方式。推荐使用方式二(uv),因为它能提供更快的安装速度和更好的依赖管理。
### 方式一:使用 conda
1. 创建新的 conda 环境:
```bash
conda create -n open_manus python=3.12
conda activate open_manus
```
2. 克隆仓库:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. 安装依赖:
```bash
pip install -r requirements.txt
```
### 方式二:使用 uv(推荐)
1. 安装 uv(一个快速的 Python 包管理器):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
2. 克隆仓库:
```bash
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
```
3. 创建并激活虚拟环境:
```bash
uv venv
source .venv/bin/activate # Unix/macOS 系统
# Windows 系统使用:
# .venv\Scripts\activate
```
4. 安装依赖:
```bash
uv pip install -r requirements.txt
```
## 配置说明
OpenManus 需要配置使用的 LLM API,请按以下步骤设置:
1. 在 `config` 目录创建 `config.toml` 文件(可从示例复制):
```bash
cp config/config.example.toml config/config.toml
```
2. 编辑 `config/config.toml` 添加 API 密钥和自定义设置:
```toml
# 全局 LLM 配置
[llm]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # 替换为真实 API 密钥
max_tokens = 4096
temperature = 0.0
# 可选特定 LLM 模型配置
[llm.vision]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
api_key = "sk-..." # 替换为真实 API 密钥
```
## 快速启动
一行命令运行 OpenManus
```bash
python main.py
```
然后通过终端输入你的创意!
如需体验开发中版本,可运行:
```bash
python run_flow.py
```
## 贡献指南
我们欢迎任何友好的建议和有价值的贡献!可以直接创建 issue 或提交 pull request。
或通过 📧 邮件联系 @mannaandpoemmannaandpoem@gmail.com
## 交流群
加入我们的飞书交流群,与其他开发者分享经验!
<div align="center" style="display: flex; gap: 20px;">
<img src="assets/community_group.jpg" alt="OpenManus 交流群" width="300" />
</div>
## Star 数量
[![Star History Chart](https://api.star-history.com/svg?repos=mannaandpoem/OpenManus&type=Date)](https://star-history.com/#mannaandpoem/OpenManus&Date)
## 致谢
特别感谢 [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)
和 [browser-use](https://github.com/browser-use/browser-use) 为本项目提供的基础支持!
此外,我们感谢 [AAAJ](https://github.com/metauto-ai/agent-as-a-judge)[MetaGPT](https://github.com/geekan/MetaGPT) 和 [OpenHands](https://github.com/All-Hands-AI/OpenHands).
OpenManus 由 MetaGPT 社区的贡献者共同构建,感谢这个充满活力的智能体开发者社区!
+3 -1
View File
@@ -4,7 +4,7 @@ from typing import List, Literal, Optional
from pydantic import BaseModel, Field, model_validator
from app.llm import LLM
from app.llm.inference import LLM
from app.logger import logger
from app.schema import AgentState, Memory, Message
@@ -144,6 +144,8 @@ class BaseAgent(BaseModel, ABC):
results.append(f"Step {self.current_step}: {step_result}")
if self.current_step >= self.max_steps:
self.current_step = 0 # setting back to 0 when reached max steps
self.state = AgentState.IDLE # setting the status
results.append(f"Terminated: Reached max steps ({self.max_steps})")
return "\n".join(results) if results else "No steps executed"
+2
View File
@@ -32,3 +32,5 @@ class Manus(ToolCallAgent):
PythonExecute(), GoogleSearch(), BrowserUseTool(), FileSaver(), Terminate()
)
)
max_steps: int = 20
+1 -1
View File
@@ -4,7 +4,7 @@ from typing import Optional
from pydantic import Field
from app.agent.base import BaseAgent
from app.llm import LLM
from app.llm.inference import LLM
from app.schema import AgentState, Memory
+1 -1
View File
@@ -154,7 +154,7 @@ class ToolCallAgent(ReActAgent):
except json.JSONDecodeError:
error_msg = f"Error parsing arguments for {name}: Invalid JSON format"
logger.error(
f"📝 Oops! The arguments for '{name}' don't make sense - invalid JSON"
f"📝 Oops! The arguments for '{name}' don't make sense - invalid JSON, arguments:{command.function.arguments}"
)
return f"Error: {error_msg}"
except Exception as e:
+4
View File
@@ -21,6 +21,8 @@ class LLMSettings(BaseModel):
api_key: str = Field(..., description="API key")
max_tokens: int = Field(4096, description="Maximum number of tokens per request")
temperature: float = Field(1.0, description="Sampling temperature")
api_type: str = Field(..., description="AzureOpenai or Openai")
api_version: str = Field(..., description="Azure Openai version if AzureOpenai")
class AppConfig(BaseModel):
@@ -76,6 +78,8 @@ class Config:
"api_key": base_llm.get("api_key"),
"max_tokens": base_llm.get("max_tokens", 4096),
"temperature": base_llm.get("temperature", 1.0),
"api_type": base_llm.get("api_type", ""),
"api_version": base_llm.get("api_version", ""),
}
config_dict = {
+29
View File
@@ -60,3 +60,32 @@ class BaseFlow(BaseModel, ABC):
@abstractmethod
async def execute(self, input_text: str) -> str:
"""Execute the flow with given input"""
class PlanStepStatus(str, Enum):
"""Enum class defining possible statuses of a plan step"""
NOT_STARTED = "not_started"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
BLOCKED = "blocked"
@classmethod
def get_all_statuses(cls) -> list[str]:
"""Return a list of all possible step status values"""
return [status.value for status in cls]
@classmethod
def get_active_statuses(cls) -> list[str]:
"""Return a list of values representing active statuses (not started or in progress)"""
return [cls.NOT_STARTED.value, cls.IN_PROGRESS.value]
@classmethod
def get_status_marks(cls) -> Dict[str, str]:
"""Return a mapping of statuses to their marker symbols"""
return {
cls.COMPLETED.value: "[✓]",
cls.IN_PROGRESS.value: "[→]",
cls.BLOCKED.value: "[!]",
cls.NOT_STARTED.value: "[ ]",
}
+26 -31
View File
@@ -5,8 +5,8 @@ from typing import Dict, List, Optional, Union
from pydantic import Field
from app.agent.base import BaseAgent
from app.flow.base import BaseFlow
from app.llm import LLM
from app.flow.base import BaseFlow, PlanStepStatus
from app.llm.inference import LLM
from app.logger import logger
from app.schema import AgentState, Message
from app.tool import PlanningTool
@@ -109,12 +109,14 @@ class PlanningFlow(BaseFlow):
# Create a system message for plan creation
system_message = Message.system_message(
"You are a planning assistant. Your task is to create a detailed plan with clear steps."
"You are a planning assistant. Create a concise, actionable plan with clear steps. "
"Focus on key milestones rather than detailed sub-steps. "
"Optimize for clarity and efficiency."
)
# Create a user message with the request
user_message = Message.user_message(
f"Create a detailed plan to accomplish this task: {request}"
f"Create a reasonable plan with clear steps to accomplish the task: {request}"
)
# Call LLM with PlanningTool
@@ -181,11 +183,11 @@ class PlanningFlow(BaseFlow):
# Find first non-completed step
for i, step in enumerate(steps):
if i >= len(step_statuses):
status = "not_started"
status = PlanStepStatus.NOT_STARTED.value
else:
status = step_statuses[i]
if status in ["not_started", "in_progress"]:
if status in PlanStepStatus.get_active_statuses():
# Extract step type/category if available
step_info = {"text": step}
@@ -202,17 +204,17 @@ class PlanningFlow(BaseFlow):
command="mark_step",
plan_id=self.active_plan_id,
step_index=i,
step_status="in_progress",
step_status=PlanStepStatus.IN_PROGRESS.value,
)
except Exception as e:
logger.warning(f"Error marking step as in_progress: {e}")
# Update step status directly if needed
if i < len(step_statuses):
step_statuses[i] = "in_progress"
step_statuses[i] = PlanStepStatus.IN_PROGRESS.value
else:
while len(step_statuses) < i:
step_statuses.append("not_started")
step_statuses.append("in_progress")
step_statuses.append(PlanStepStatus.NOT_STARTED.value)
step_statuses.append(PlanStepStatus.IN_PROGRESS.value)
plan_data["step_statuses"] = step_statuses
@@ -264,7 +266,7 @@ class PlanningFlow(BaseFlow):
command="mark_step",
plan_id=self.active_plan_id,
step_index=self.current_step_index,
step_status="completed",
step_status=PlanStepStatus.COMPLETED.value,
)
logger.info(
f"Marked step {self.current_step_index} as completed in plan {self.active_plan_id}"
@@ -278,10 +280,10 @@ class PlanningFlow(BaseFlow):
# Ensure the step_statuses list is long enough
while len(step_statuses) <= self.current_step_index:
step_statuses.append("not_started")
step_statuses.append(PlanStepStatus.NOT_STARTED.value)
# Update the status
step_statuses[self.current_step_index] = "completed"
step_statuses[self.current_step_index] = PlanStepStatus.COMPLETED.value
plan_data["step_statuses"] = step_statuses
async def _get_plan_text(self) -> str:
@@ -309,23 +311,18 @@ class PlanningFlow(BaseFlow):
# Ensure step_statuses and step_notes match the number of steps
while len(step_statuses) < len(steps):
step_statuses.append("not_started")
step_statuses.append(PlanStepStatus.NOT_STARTED.value)
while len(step_notes) < len(steps):
step_notes.append("")
# Count steps by status
status_counts = {
"completed": 0,
"in_progress": 0,
"blocked": 0,
"not_started": 0,
}
status_counts = {status: 0 for status in PlanStepStatus.get_all_statuses()}
for status in step_statuses:
if status in status_counts:
status_counts[status] += 1
completed = status_counts["completed"]
completed = status_counts[PlanStepStatus.COMPLETED.value]
total = len(steps)
progress = (completed / total) * 100 if total > 0 else 0
@@ -335,21 +332,19 @@ class PlanningFlow(BaseFlow):
plan_text += (
f"Progress: {completed}/{total} steps completed ({progress:.1f}%)\n"
)
plan_text += f"Status: {status_counts['completed']} completed, {status_counts['in_progress']} in progress, "
plan_text += f"{status_counts['blocked']} blocked, {status_counts['not_started']} not started\n\n"
plan_text += f"Status: {status_counts[PlanStepStatus.COMPLETED.value]} completed, {status_counts[PlanStepStatus.IN_PROGRESS.value]} in progress, "
plan_text += f"{status_counts[PlanStepStatus.BLOCKED.value]} blocked, {status_counts[PlanStepStatus.NOT_STARTED.value]} not started\n\n"
plan_text += "Steps:\n"
status_marks = PlanStepStatus.get_status_marks()
for i, (step, status, notes) in enumerate(
zip(steps, step_statuses, step_notes)
):
if status == "completed":
status_mark = "[✓]"
elif status == "in_progress":
status_mark = "[→]"
elif status == "blocked":
status_mark = "[!]"
else: # not_started
status_mark = "[ ]"
# Use status marks to indicate step status
status_mark = status_marks.get(
status, status_marks[PlanStepStatus.NOT_STARTED.value]
)
plan_text += f"{i}. {status_mark} {step}\n"
if notes:
-254
View File
@@ -1,254 +0,0 @@
from typing import Dict, List, Literal, Optional, Union
from openai import (
APIError,
AsyncOpenAI,
AuthenticationError,
OpenAIError,
RateLimitError,
)
from tenacity import retry, stop_after_attempt, wait_random_exponential
from app.config import LLMSettings, config
from app.logger import logger # Assuming a logger is set up in your app
from app.schema import Message
class LLM:
_instances: Dict[str, "LLM"] = {}
def __new__(
cls, config_name: str = "default", llm_config: Optional[LLMSettings] = None
):
if config_name not in cls._instances:
instance = super().__new__(cls)
instance.__init__(config_name, llm_config)
cls._instances[config_name] = instance
return cls._instances[config_name]
def __init__(
self, config_name: str = "default", llm_config: Optional[LLMSettings] = None
):
if not hasattr(self, "client"): # Only initialize if not already initialized
llm_config = llm_config or config.llm
llm_config = llm_config.get(config_name, llm_config["default"])
self.model = llm_config.model
self.max_tokens = llm_config.max_tokens
self.temperature = llm_config.temperature
self.client = AsyncOpenAI(
api_key=llm_config.api_key, base_url=llm_config.base_url
)
@staticmethod
def format_messages(messages: List[Union[dict, Message]]) -> List[dict]:
"""
Format messages for LLM by converting them to OpenAI message format.
Args:
messages: List of messages that can be either dict or Message objects
Returns:
List[dict]: List of formatted messages in OpenAI format
Raises:
ValueError: If messages are invalid or missing required fields
TypeError: If unsupported message types are provided
Examples:
>>> msgs = [
... Message.system_message("You are a helpful assistant"),
... {"role": "user", "content": "Hello"},
... Message.user_message("How are you?")
... ]
>>> formatted = LLM.format_messages(msgs)
"""
formatted_messages = []
for message in messages:
if isinstance(message, dict):
# If message is already a dict, ensure it has required fields
if "role" not in message:
raise ValueError("Message dict must contain 'role' field")
formatted_messages.append(message)
elif isinstance(message, Message):
# If message is a Message object, convert it to dict
formatted_messages.append(message.to_dict())
else:
raise TypeError(f"Unsupported message type: {type(message)}")
# Validate all messages have required fields
for msg in formatted_messages:
if msg["role"] not in ["system", "user", "assistant", "tool"]:
raise ValueError(f"Invalid role: {msg['role']}")
if "content" not in msg and "tool_calls" not in msg:
raise ValueError(
"Message must contain either 'content' or 'tool_calls'"
)
return formatted_messages
@retry(
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
)
async def ask(
self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
stream: bool = True,
temperature: Optional[float] = None,
) -> str:
"""
Send a prompt to the LLM and get the response.
Args:
messages: List of conversation messages
system_msgs: Optional system messages to prepend
stream (bool): Whether to stream the response
temperature (float): Sampling temperature for the response
Returns:
str: The generated response
Raises:
ValueError: If messages are invalid or response is empty
OpenAIError: If API call fails after retries
Exception: For unexpected errors
"""
try:
# Format system and user messages
if system_msgs:
system_msgs = self.format_messages(system_msgs)
messages = system_msgs + self.format_messages(messages)
else:
messages = self.format_messages(messages)
if not stream:
# Non-streaming request
response = await self.client.chat.completions.create(
model=self.model,
messages=messages,
max_tokens=self.max_tokens,
temperature=temperature or self.temperature,
stream=False,
)
if not response.choices or not response.choices[0].message.content:
raise ValueError("Empty or invalid response from LLM")
return response.choices[0].message.content
# Streaming request
response = await self.client.chat.completions.create(
model=self.model,
messages=messages,
max_tokens=self.max_tokens,
temperature=temperature or self.temperature,
stream=True,
)
collected_messages = []
async for chunk in response:
chunk_message = chunk.choices[0].delta.content or ""
collected_messages.append(chunk_message)
print(chunk_message, end="", flush=True)
print() # Newline after streaming
full_response = "".join(collected_messages).strip()
if not full_response:
raise ValueError("Empty response from streaming LLM")
return full_response
except ValueError as ve:
logger.error(f"Validation error: {ve}")
raise
except OpenAIError as oe:
logger.error(f"OpenAI API error: {oe}")
raise
except Exception as e:
logger.error(f"Unexpected error in ask: {e}")
raise
@retry(
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
)
async def ask_tool(
self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
timeout: int = 60,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
temperature: Optional[float] = None,
**kwargs,
):
"""
Ask LLM using functions/tools and return the response.
Args:
messages: List of conversation messages
system_msgs: Optional system messages to prepend
timeout: Request timeout in seconds
tools: List of tools to use
tool_choice: Tool choice strategy
temperature: Sampling temperature for the response
**kwargs: Additional completion arguments
Returns:
ChatCompletionMessage: The model's response
Raises:
ValueError: If tools, tool_choice, or messages are invalid
OpenAIError: If API call fails after retries
Exception: For unexpected errors
"""
try:
# Validate tool_choice
if tool_choice not in ["none", "auto", "required"]:
raise ValueError(f"Invalid tool_choice: {tool_choice}")
# Format messages
if system_msgs:
system_msgs = self.format_messages(system_msgs)
messages = system_msgs + self.format_messages(messages)
else:
messages = self.format_messages(messages)
# Validate tools if provided
if tools:
for tool in tools:
if not isinstance(tool, dict) or "type" not in tool:
raise ValueError("Each tool must be a dict with 'type' field")
# Set up the completion request
response = await self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature or self.temperature,
max_tokens=self.max_tokens,
tools=tools,
tool_choice=tool_choice,
timeout=timeout,
**kwargs,
)
# Check if response is valid
if not response.choices or not response.choices[0].message:
print(response)
raise ValueError("Invalid or empty response from LLM")
return response.choices[0].message
except ValueError as ve:
logger.error(f"Validation error in ask_tool: {ve}")
raise
except OpenAIError as oe:
if isinstance(oe, AuthenticationError):
logger.error("Authentication failed. Check API key.")
elif isinstance(oe, RateLimitError):
logger.error("Rate limit exceeded. Consider increasing retry attempts.")
elif isinstance(oe, APIError):
logger.error(f"API error: {oe}")
raise
except Exception as e:
logger.error(f"Unexpected error in ask_tool: {e}")
raise
View File
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Cost:
"""
Cost class can record various costs during running and evaluation.
Currently we define the following costs:
accumulated_cost: the total cost (USD $) of the current LLM.
"""
def __init__(self) -> None:
self._accumulated_cost: float = 0.0
self._costs: list[float] = []
@property
def accumulated_cost(self) -> float:
return self._accumulated_cost
@accumulated_cost.setter
def accumulated_cost(self, value: float) -> None:
if value < 0:
raise ValueError("Total cost cannot be negative.")
self._accumulated_cost = value
@property
def costs(self) -> list:
return self._costs
def add_cost(self, value: float) -> None:
if value < 0:
raise ValueError("Added cost cannot be negative.")
self._accumulated_cost += value
self._costs.append(value)
def get(self):
"""
Return the costs in a dictionary.
"""
return {"accumulated_cost": self._accumulated_cost, "costs": self._costs}
def log(self):
"""
Log the costs.
"""
cost = self.get()
logs = ""
for key, value in cost.items():
logs += f"{key}: {value}\n"
return logs
+525
View File
@@ -0,0 +1,525 @@
import base64
import os
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
import litellm
from litellm import completion, completion_cost
from litellm.exceptions import (
APIConnectionError,
RateLimitError,
ServiceUnavailableError,
)
from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from app.config import LLMSettings, config
from app.llm.cost import Cost
from app.logger import logger
from app.schema import Message
class LLM:
_instances: Dict[str, "LLM"] = {}
def __new__(
cls, config_name: str = "default", llm_config: Optional[LLMSettings] = None
):
if config_name not in cls._instances:
instance = super().__new__(cls)
instance.__init__(config_name, llm_config)
cls._instances[config_name] = instance
return cls._instances[config_name]
def __init__(
self, config_name: str = "default", llm_config: Optional[LLMSettings] = None
):
if not hasattr(
self, "initialized"
): # Only initialize if not already initialized
llm_config = llm_config or config.llm
llm_config = llm_config.get(config_name, llm_config["default"])
self.model = getattr(llm_config, "model", "gpt-3.5-turbo")
self.max_tokens = getattr(llm_config, "max_tokens", 4096)
self.temperature = getattr(llm_config, "temperature", 0.7)
self.top_p = getattr(llm_config, "top_p", 0.9)
self.api_type = getattr(llm_config, "api_type", "openai")
self.api_key = getattr(
llm_config, "api_key", os.environ.get("OPENAI_API_KEY", "")
)
self.api_version = getattr(llm_config, "api_version", "")
self.base_url = getattr(llm_config, "base_url", "https://api.openai.com/v1")
self.timeout = getattr(llm_config, "timeout", 60)
self.num_retries = getattr(llm_config, "num_retries", 3)
self.retry_min_wait = getattr(llm_config, "retry_min_wait", 1)
self.retry_max_wait = getattr(llm_config, "retry_max_wait", 10)
self.custom_llm_provider = getattr(llm_config, "custom_llm_provider", None)
# Get model info if available
self.model_info = None
try:
self.model_info = litellm.get_model_info(self.model)
except Exception as e:
logger.warning(f"Could not get model info for {self.model}: {e}")
# Configure litellm
if self.api_type == "azure":
litellm.api_base = self.base_url
litellm.api_key = self.api_key
litellm.api_version = self.api_version
else:
litellm.api_key = self.api_key
if self.base_url:
litellm.api_base = self.base_url
# Initialize cost tracker
self.cost_tracker = Cost()
self.initialized = True
# Initialize completion function
self._initialize_completion_function()
def _initialize_completion_function(self):
"""Initialize the completion function with retry logic"""
def attempt_on_error(retry_state):
logger.error(
f"{retry_state.outcome.exception()}. Attempt #{retry_state.attempt_number}"
)
return True
@retry(
reraise=True,
stop=stop_after_attempt(self.num_retries),
wait=wait_random_exponential(
min=self.retry_min_wait, max=self.retry_max_wait
),
retry=retry_if_exception_type(
(RateLimitError, APIConnectionError, ServiceUnavailableError)
),
after=attempt_on_error,
)
def wrapper(*args, **kwargs):
model_name = self.model
if self.api_type == "azure":
model_name = f"azure/{self.model}"
# Set default parameters if not provided
if "max_tokens" not in kwargs:
kwargs["max_tokens"] = self.max_tokens
if "temperature" not in kwargs:
kwargs["temperature"] = self.temperature
if "top_p" not in kwargs:
kwargs["top_p"] = self.top_p
if "timeout" not in kwargs:
kwargs["timeout"] = self.timeout
kwargs["model"] = model_name
# Add API credentials if not in kwargs
if "api_key" not in kwargs:
kwargs["api_key"] = self.api_key
if "base_url" not in kwargs and self.base_url:
kwargs["base_url"] = self.base_url
if "api_version" not in kwargs and self.api_version:
kwargs["api_version"] = self.api_version
if "custom_llm_provider" not in kwargs and self.custom_llm_provider:
kwargs["custom_llm_provider"] = self.custom_llm_provider
resp = completion(**kwargs)
return resp
self._completion = wrapper
@staticmethod
def format_messages(messages: List[Union[dict, Message]]) -> List[dict]:
"""
Format messages for LLM by converting them to OpenAI message format.
Args:
messages: List of messages that can be either dict or Message objects
Returns:
List[dict]: List of formatted messages in OpenAI format
Raises:
ValueError: If messages are invalid or missing required fields
TypeError: If unsupported message types are provided
"""
formatted_messages = []
for message in messages:
if isinstance(message, dict):
# If message is already a dict, ensure it has required fields
if "role" not in message:
raise ValueError("Message dict must contain 'role' field")
formatted_messages.append(message)
elif isinstance(message, Message):
# If message is a Message object, convert it to dict
formatted_messages.append(message.to_dict())
else:
raise TypeError(f"Unsupported message type: {type(message)}")
# Validate all messages have required fields
for msg in formatted_messages:
if msg["role"] not in ["system", "user", "assistant", "tool"]:
raise ValueError(f"Invalid role: {msg['role']}")
if "content" not in msg and "tool_calls" not in msg:
raise ValueError(
"Message must contain either 'content' or 'tool_calls'"
)
return formatted_messages
def _calculate_and_track_cost(self, response) -> float:
"""
Calculate and track the cost of an LLM API call.
Args:
response: The response from litellm
Returns:
float: The calculated cost
"""
try:
# Use litellm's completion_cost function
cost = completion_cost(completion_response=response)
# Add the cost to our tracker
if cost > 0:
self.cost_tracker.add_cost(cost)
logger.info(
f"Added cost: ${cost:.6f}, Total: ${self.cost_tracker.accumulated_cost:.6f}"
)
return cost
except Exception as e:
logger.warning(f"Cost calculation failed: {e}")
return 0.0
def is_local(self) -> bool:
"""
Check if the model is running locally.
Returns:
bool: True if the model is running locally, False otherwise
"""
if self.base_url:
return any(
substring in self.base_url
for substring in ["localhost", "127.0.0.1", "0.0.0.0"]
)
if self.model and (
self.model.startswith("ollama") or "local" in self.model.lower()
):
return True
return False
def do_completion(self, *args, **kwargs) -> Tuple[Any, float, float]:
"""
Perform a completion request and track cost.
Returns:
Tuple[Any, float, float]: (response, current_cost, accumulated_cost)
"""
response = self._completion(*args, **kwargs)
# Calculate and track cost
current_cost = self._calculate_and_track_cost(response)
return response, current_cost, self.cost_tracker.accumulated_cost
@staticmethod
def encode_image(image_path: str) -> str:
"""
Encode an image to base64.
Args:
image_path: Path to the image file
Returns:
str: Base64-encoded image
"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
def prepare_messages(
self, text: str, image_path: Optional[str] = None
) -> List[dict]:
"""
Prepare messages for completion, including multimodal content if needed.
Args:
text: Text content
image_path: Optional path to an image file
Returns:
List[dict]: Formatted messages
"""
messages = [{"role": "user", "content": text}]
if image_path:
base64_image = self.encode_image(image_path)
messages[0]["content"] = [
{"type": "text", "text": text},
{
"type": "image_url",
"image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
},
]
return messages
def do_multimodal_completion(
self, text: str, image_path: str
) -> Tuple[Any, float, float]:
"""
Perform a multimodal completion with text and image.
Args:
text: Text prompt
image_path: Path to the image file
Returns:
Tuple[Any, float, float]: (response, current_cost, accumulated_cost)
"""
messages = self.prepare_messages(text, image_path=image_path)
return self.do_completion(messages=messages)
@retry(
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
)
async def ask(
self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
stream: bool = True,
temperature: Optional[float] = None,
) -> str:
"""
Send a prompt to the LLM and get the response.
Args:
messages: List of conversation messages
system_msgs: Optional system messages to prepend
stream (bool): Whether to stream the response
temperature (float): Sampling temperature for the response
Returns:
str: The generated response
Raises:
ValueError: If messages are invalid or response is empty
Exception: For unexpected errors
"""
try:
# Format system and user messages
if system_msgs:
system_msgs = self.format_messages(system_msgs)
messages = system_msgs + self.format_messages(messages)
else:
messages = self.format_messages(messages)
model_name = self.model
if self.api_type == "azure":
# For Azure, litellm expects model name in format: azure/<deployment_name>
model_name = f"azure/{self.model}"
if not stream:
# Non-streaming request
response = await litellm.acompletion(
model=model_name,
messages=messages,
max_tokens=self.max_tokens,
temperature=temperature or self.temperature,
stream=False,
)
# Calculate and track cost
self._calculate_and_track_cost(response)
if not response.choices or not response.choices[0].message.content:
raise ValueError("Empty or invalid response from LLM")
return response.choices[0].message.content
# Streaming request
collected_messages = []
async for chunk in await litellm.acompletion(
model=model_name,
messages=messages,
max_tokens=self.max_tokens,
temperature=temperature or self.temperature,
stream=True,
):
chunk_message = chunk.choices[0].delta.content or ""
collected_messages.append(chunk_message)
print(chunk_message, end="", flush=True)
# For streaming responses, cost is calculated on the last chunk
if hasattr(chunk, "usage") and chunk.usage:
self._calculate_and_track_cost(chunk)
print() # Newline after streaming
full_response = "".join(collected_messages).strip()
if not full_response:
raise ValueError("Empty response from streaming LLM")
return full_response
except ValueError as ve:
logger.error(f"Validation error: {ve}")
raise
except Exception as e:
logger.error(f"Unexpected error in ask: {e}")
raise
@retry(
wait=wait_random_exponential(min=1, max=60),
stop=stop_after_attempt(6),
)
async def ask_tool(
self,
messages: List[Union[dict, Message]],
system_msgs: Optional[List[Union[dict, Message]]] = None,
timeout: int = 60,
tools: Optional[List[dict]] = None,
tool_choice: Literal["none", "auto", "required"] = "auto",
temperature: Optional[float] = None,
**kwargs,
):
"""
Ask LLM using functions/tools and return the response.
Args:
messages: List of conversation messages
system_msgs: Optional system messages to prepend
timeout: Request timeout in seconds
tools: List of tools to use
tool_choice: Tool choice strategy
temperature: Sampling temperature for the response
**kwargs: Additional completion arguments
Returns:
The model's response
Raises:
ValueError: If tools, tool_choice, or messages are invalid
Exception: For unexpected errors
"""
try:
# Validate tool_choice
if tool_choice not in ["none", "auto", "required"]:
raise ValueError(f"Invalid tool_choice: {tool_choice}")
# Format messages
if system_msgs:
system_msgs = self.format_messages(system_msgs)
messages = system_msgs + self.format_messages(messages)
else:
messages = self.format_messages(messages)
# Validate tools if provided
if tools:
for tool in tools:
if not isinstance(tool, dict) or "type" not in tool:
raise ValueError("Each tool must be a dict with 'type' field")
model_name = self.model
if self.api_type == "azure":
# For Azure, litellm expects model name in format: azure/<deployment_name>
model_name = f"azure/{self.model}"
# Set up the completion request
response = await litellm.acompletion(
model=model_name,
messages=messages,
temperature=temperature or self.temperature,
max_tokens=self.max_tokens,
tools=tools,
tool_choice=tool_choice,
timeout=timeout,
**kwargs,
)
# Calculate and track cost
self._calculate_and_track_cost(response)
# Check if response is valid
if not response.choices or not response.choices[0].message:
print(response)
raise ValueError("Invalid or empty response from LLM")
return response.choices[0].message
except ValueError as ve:
logger.error(f"Validation error: {ve}")
raise
except Exception as e:
logger.error(f"Unexpected error in ask_tool: {e}")
raise
def get_cost(self):
"""
Get the current cost information.
Returns:
dict: Dictionary containing accumulated cost and individual costs
"""
return self.cost_tracker.get()
def log_cost(self):
"""
Log the current cost information.
Returns:
str: Formatted string of cost information
"""
return self.cost_tracker.log()
def get_token_count(self, messages):
"""
Get the token count for a list of messages.
Args:
messages: List of messages
Returns:
int: Token count
"""
return litellm.token_counter(model=self.model, messages=messages)
def __str__(self):
return f"LLM(model={self.model}, base_url={self.base_url})"
def __repr__(self):
return str(self)
# Example usage
if __name__ == "__main__":
# Load environment variables if needed
from dotenv import load_dotenv
load_dotenv()
# Create LLM instance
llm = LLM()
# Test text completion
messages = llm.prepare_messages("Hello, how are you?")
response, cost, total_cost = llm.do_completion(messages=messages)
print(f"Response: {response['choices'][0]['message']['content']}")
print(f"Cost: ${cost:.6f}, Total cost: ${total_cost:.6f}")
# Test multimodal if image path is available
image_path = os.getenv("TEST_IMAGE_PATH")
if image_path and os.path.exists(image_path):
multimodal_response, mm_cost, mm_total_cost = llm.do_multimodal_completion(
"What's in this image?", image_path
)
print(
f"Multimodal response: {multimodal_response['choices'][0]['message']['content']}"
)
print(f"Cost: ${mm_cost:.6f}, Total cost: ${mm_total_cost:.6f}")
+1 -1
View File
@@ -15,7 +15,7 @@ def define_log_level(print_level="INFO", logfile_level="DEBUG", name: str = None
_print_level = print_level
current_date = datetime.now()
formatted_date = current_date.strftime("%Y%m%d")
formatted_date = current_date.strftime("%Y%m%d%H%M%S")
log_name = (
f"{name}_{formatted_date}" if name else formatted_date
) # name a log with prefix name
+4
View File
@@ -10,5 +10,9 @@ BrowserUseTool: Open, browse, and use web browsers.If you open a local HTML file
GoogleSearch: Perform web information retrieval
Terminate: End the current interaction when the task is complete or when you need additional information from the user. Use this tool to signal that you've finished addressing the user's request or need clarification before proceeding further.
Based on user needs, proactively select the most appropriate tool or combination of tools. For complex tasks, you can break down the problem and use different tools step by step to solve it. After using each tool, clearly explain the execution results and suggest the next steps.
Always maintain a helpful, informative tone throughout the interaction. If you encounter any limitations or need more details, clearly communicate this to the user before terminating.
"""
+14 -12
View File
@@ -1,25 +1,27 @@
PLANNING_SYSTEM_PROMPT = """
You are an expert Planning Agent tasked with solving complex problems by creating and managing structured plans.
You are an expert Planning Agent tasked with solving problems efficiently through structured plans.
Your job is:
1. Analyze requests to understand the task scope
2. Create clear, actionable plans with the `planning` tool
2. Create a clear, actionable plan that makes meaningful progress with the `planning` tool
3. Execute steps using available tools as needed
4. Track progress and adapt plans dynamically
5. Use `finish` to conclude when the task is complete
4. Track progress and adapt plans when necessary
5. Use `finish` to conclude immediately when the task is complete
Available tools will vary by task but may include:
- `planning`: Create, update, and track plans (commands: create, update, mark_step, etc.)
- `finish`: End the task when complete
Break tasks into logical, sequential steps. Think about dependencies and verification methods.
Break tasks into logical steps with clear outcomes. Avoid excessive detail or sub-steps.
Think about dependencies and verification methods.
Know when to conclude - don't continue thinking once objectives are met.
"""
NEXT_STEP_PROMPT = """
Based on the current state, what's your next step?
Consider:
1. Do you need to create or refine a plan?
2. Are you ready to execute a specific step?
3. Have you completed the task?
Based on the current state, what's your next action?
Choose the most efficient path forward:
1. Is the plan sufficient, or does it need refinement?
2. Can you execute the next step immediately?
3. Is the task complete? If so, use `finish` right away.
Provide reasoning, then select the appropriate tool or action.
Be concise in your reasoning, then select the appropriate tool or action.
"""
+18 -1
View File
@@ -12,6 +12,8 @@ from pydantic_core.core_schema import ValidationInfo
from app.tool.base import BaseTool, ToolResult
MAX_LENGTH = 2000
_BROWSER_DESCRIPTION = """
Interact with a web browser to perform various actions such as navigation, element interaction,
content extraction, and tab management. Supported actions include:
@@ -20,6 +22,8 @@ content extraction, and tab management. Supported actions include:
- 'input_text': Input text into an element
- 'screenshot': Capture a screenshot
- 'get_html': Get page HTML content
- 'get_text': Get text content of the page
- 'read_links': Get all links on the page
- 'execute_js': Execute JavaScript code
- 'scroll': Scroll the page
- 'switch_tab': Switch to a specific tab
@@ -43,6 +47,7 @@ class BrowserUseTool(BaseTool):
"input_text",
"screenshot",
"get_html",
"get_text",
"execute_js",
"scroll",
"switch_tab",
@@ -177,9 +182,21 @@ class BrowserUseTool(BaseTool):
elif action == "get_html":
html = await context.get_page_html()
truncated = html[:2000] + "..." if len(html) > 2000 else html
truncated = (
html[:MAX_LENGTH] + "..." if len(html) > MAX_LENGTH else html
)
return ToolResult(output=truncated)
elif action == "get_text":
text = await context.execute_javascript("document.body.innerText")
return ToolResult(output=text)
elif action == "read_links":
links = await context.execute_javascript(
"document.querySelectorAll('a[href]').forEach((elem) => {if (elem.innerText) {console.log(elem.innerText, elem.href)}})"
)
return ToolResult(output=links)
elif action == "execute_js":
if not script:
return ToolResult(
-2
View File
@@ -1,4 +1,3 @@
import asyncio
import os
import aiofiles
@@ -58,4 +57,3 @@ The tool accepts content and a file path, and saves the content to that location
return f"Content successfully saved to {file_path}"
except Exception as e:
return f"Error saving file: {str(e)}"
Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

+13 -4
View File
@@ -1,13 +1,22 @@
# Global LLM configuration
[llm]
model = "claude-3-5-sonnet"
base_url = "https://api.openai.com/v1"
model = "gpt-4o" #"claude-3-5-sonnet"
base_url = "https://api.openai.com/v1" # "https://api.anthropic.com"
api_key = "sk-..."
max_tokens = 4096
temperature = 0.0
# [llm] #AZURE OPENAI:
# api_type= 'azure'
# model = "YOUR_MODEL_NAME" #"gpt-4o-mini"
# base_url = "{YOUR_AZURE_ENDPOINT.rstrip('/')}/openai/deployments/{AZURE_DEPOLYMENT_ID}"
# api_key = "AZURE API KEY"
# max_tokens = 8096
# temperature = 0.0
# api_version="AZURE API VERSION" #"2024-08-01-preview"
# Optional configuration for specific LLM models
[llm.vision]
model = "claude-3-5-sonnet"
base_url = "https://api.openai.com/v1"
model = "gpt-4o" # "claude-3-5-sonnet"
base_url = "https://api.openai.com/v1" # "https://api.anthropic.com"
api_key = "sk-..."
BIN
View File
Binary file not shown.
@@ -0,0 +1,62 @@
JAPAN TRAVEL HANDBOOK - GUIDE TO VERSIONS
Location: D:/OpenManus/
1. DETAILED DIGITAL VERSION
File: japan_travel_handbook.html
Best for: Desktop/laptop viewing
Features:
- Complete comprehensive guide
- Detailed itinerary
- Full proposal planning section
- All hotel recommendations
- Comprehensive budget breakdown
Usage: Open in web browser for trip planning and detailed reference
2. PRINT-FRIENDLY VERSION
File: japan_travel_handbook_print.html
Best for: Physical reference during travel
Features:
- Condensed essential information
- Optimized for paper printing
- Clear, printer-friendly formatting
- Quick reference tables
Usage: Print and keep in travel documents folder
3. MOBILE-OPTIMIZED VERSION
File: japan_travel_handbook_mobile.html
Best for: On-the-go reference during trip
Features:
- Touch-friendly interface
- Collapsible sections
- Quick access emergency buttons
- Dark mode support
- Responsive design
Usage: Save to phone's browser bookmarks for quick access
RECOMMENDED SETUP:
1. Before Trip:
- Use detailed version for planning
- Print the print-friendly version
- Save mobile version to phone
2. During Trip:
- Keep printed version with travel documents
- Use mobile version for daily reference
- Access detailed version when needed for specific information
3. Emergency Access:
- Mobile version has quick-access emergency information
- Keep printed version as backup
- All emergency numbers and contacts in both versions
Note: All versions contain the same core information but are formatted differently for optimal use in different situations.
IMPORTANT DATES:
- Trip Duration: April 15-23, 2024
- Proposal Day: April 19, 2024
- Key Reservation Deadlines:
* Flights: Book by January 2024
* Hotels: Book by February 2024
* Restaurant Reservations: Book by January 2024
* JR Pass: Purchase by March 2024
@@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Japan Travel Handbook - April 15-23, 2024</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; margin: 0; padding: 20px; }
.container { max-width: 1000px; margin: 0 auto; }
h1, h2, h3 { color: #333; }
.day-item { background: #f9f9f9; padding: 15px; margin: 10px 0; border-radius: 5px; }
.important-note { background: #ffe6e6; padding: 10px; border-radius: 5px; }
.phrase-table { width: 100%; border-collapse: collapse; }
.phrase-table td, .phrase-table th { border: 1px solid #ddd; padding: 8px; }
.proposal-spot { background: #e6ffe6; padding: 15px; margin: 10px 0; border-radius: 5px; }
.flight-info { background: #e6f3ff; padding: 15px; margin: 10px 0; border-radius: 5px; }
.checklist { background: #fff3e6; padding: 15px; margin: 10px 0; border-radius: 5px; }
.hotels { background: #e6e6ff; padding: 15px; margin: 10px 0; border-radius: 5px; }
.proposal-plan { background: #ffe6ff; padding: 15px; margin: 10px 0; border-radius: 5px; }
.checkbox-list li { list-style-type: none; margin-bottom: 8px; }
.checkbox-list li:before { content: "☐ "; }
.warning { color: #ff4444; }
</style>
</head>
<body>
<div class="container">
[Previous content remains the same...]
<div class="proposal-plan">
<h2>🌸 Proposal Planning Guide 🌸</h2>
<h3>Ring Security & Transport</h3>
<ul>
<li><strong>Carrying the Ring:</strong>
<ul>
<li>Always keep the ring in your carry-on luggage, never in checked bags</li>
<li>Use a discrete, non-branded box or case</li>
<li>Consider travel insurance that covers jewelry</li>
<li>Keep receipt/appraisal documentation separate from the ring</li>
</ul>
</li>
<li><strong>Airport Security Tips:</strong>
<ul>
<li>No need to declare the ring unless value exceeds ¥1,000,000 (~$6,700)</li>
<li>If asked, simply state it's "personal jewelry"</li>
<li>Consider requesting private screening to maintain surprise</li>
<li>Keep ring in original box until through security, then transfer to more discrete case</li>
</ul>
</li>
</ul>
<h3>Proposal Location Details - Maruyama Park</h3>
<ul>
<li><strong>Best Timing:</strong>
<ul>
<li>Date: April 19 (Day 5)</li>
<li>Time: 5:30 PM (30 minutes before sunset)</li>
<li>Park closes at 8:00 PM in April</li>
</ul>
</li>
<li><strong>Specific Spot Recommendations:</strong>
<ul>
<li>Primary Location: Near the famous weeping cherry tree
<br>- Less crowded in early evening
<br>- Beautiful illumination starts at dusk
<br>- Iconic Kyoto backdrop
</li>
<li>Backup Location: Gion Shirakawa area
<br>- Atmospheric stone-paved street
<br>- Traditional buildings and cherry trees
<br>- Beautiful in light rain
</li>
</ul>
</li>
</ul>
<h3>Proposal Day Planning</h3>
<ul>
<li><strong>Morning Preparation:</strong>
<ul>
<li>Confirm weather forecast</li>
<li>Transfer ring to secure pocket/bag</li>
<li>Have backup indoor location details ready</li>
</ul>
</li>
<li><strong>Suggested Timeline:</strong>
<ul>
<li>4:00 PM: Start heading to Maruyama Park area</li>
<li>4:30 PM: Light refreshments at nearby tea house</li>
<li>5:15 PM: Begin walk through park</li>
<li>5:30 PM: Arrive at proposal spot</li>
<li>6:00 PM: Sunset and illumination begins</li>
<li>7:00 PM: Celebratory dinner reservation</li>
</ul>
</li>
</ul>
<h3>Celebration Dinner Options</h3>
<ul>
<li><strong>Traditional Japanese:</strong> Kikunoi Roan
<br>- Intimate 2-star Michelin restaurant
<br>- Advance reservation required (3 months)
<br>- Price: ¥15,000-20,000 per person
</li>
<li><strong>Modern Fusion:</strong> The Sodoh
<br>- Beautiful garden views
<br>- Western-style seating available
<br>- Price: ¥12,000-15,000 per person
</li>
</ul>
<div class="warning">
<h3>Important Notes:</h3>
<ul>
<li>Keep proposal plans in separate notes from shared itinerary</li>
<li>Have a backup plan in case of rain (indoor locations listed above)</li>
<li>Consider hiring a local photographer to capture the moment</li>
<li>Save restaurant staff contact info in case of timing changes</li>
</ul>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Japan Travel Guide (Mobile)</title>
<style>
* { box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
margin: 0;
padding: 10px;
line-height: 1.6;
font-size: 16px;
}
.container {
max-width: 100%;
margin: 0 auto;
}
h1 { font-size: 1.5em; margin: 10px 0; }
h2 { font-size: 1.3em; margin: 8px 0; }
h3 { font-size: 1.1em; margin: 6px 0; }
/* Mobile-friendly cards */
.card {
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
margin: 10px 0;
padding: 15px;
}
/* Collapsible sections */
.collapsible {
background: #f8f9fa;
border: none;
border-radius: 8px;
width: 100%;
padding: 15px;
text-align: left;
font-size: 1.1em;
font-weight: bold;
cursor: pointer;
margin: 5px 0;
}
.content {
display: none;
padding: 10px;
}
.active {
background: #e9ecef;
}
/* Mobile-friendly tables */
.table-wrapper {
overflow-x: auto;
margin: 10px 0;
}
table {
width: 100%;
border-collapse: collapse;
min-width: 300px;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
th {
background: #f8f9fa;
}
/* Touch-friendly lists */
ul, ol {
padding-left: 20px;
margin: 10px 0;
}
li {
margin: 8px 0;
padding: 5px 0;
}
/* Emergency info styling */
.emergency {
background: #ffe6e6;
border-left: 4px solid #ff4444;
padding: 10px;
margin: 10px 0;
}
/* Quick access buttons */
.quick-access {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 10px 0;
}
.quick-btn {
background: #007bff;
color: white;
border: none;
border-radius: 20px;
padding: 10px 20px;
font-size: 0.9em;
cursor: pointer;
flex: 1 1 auto;
text-align: center;
min-width: 120px;
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
body {
background: #1a1a1a;
color: #fff;
}
.card {
background: #2d2d2d;
}
.collapsible {
background: #333;
color: #fff;
}
.active {
background: #404040;
}
th {
background: #333;
}
td, th {
border-color: #404040;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Japan Travel Guide</h1>
<p><strong>April 15-23, 2024</strong></p>
<div class="quick-access">
<button class="quick-btn" onclick="showSection('emergency')">Emergency</button>
<button class="quick-btn" onclick="showSection('phrases')">Phrases</button>
<button class="quick-btn" onclick="showSection('transport')">Transport</button>
<button class="quick-btn" onclick="showSection('proposal')">Proposal</button>
</div>
<div class="emergency card" id="emergency">
<h2>Emergency Contacts</h2>
<ul>
<li>🚑 Emergency: 119</li>
<li>👮 Police: 110</li>
<li>🏢 US Embassy: +81-3-3224-5000</li>
<li>️ Tourist Info: 03-3201-3331</li>
</ul>
</div>
<button class="collapsible">📅 Daily Itinerary</button>
<div class="content">
<div class="table-wrapper">
<table>
<tr><th>Date</th><th>Location</th><th>Activities</th></tr>
<tr><td>Apr 15</td><td>Tokyo</td><td>Arrival, Shinjuku</td></tr>
<tr><td>Apr 16</td><td>Tokyo</td><td>Meiji, Harajuku, Senso-ji</td></tr>
<tr><td>Apr 17</td><td>Tokyo</td><td>Tea Ceremony, Budokan</td></tr>
<tr><td>Apr 18</td><td>Kyoto</td><td>Travel, Kinkaku-ji</td></tr>
<tr><td>Apr 19</td><td>Kyoto</td><td>Fushimi Inari, Proposal</td></tr>
<tr><td>Apr 20</td><td>Nara</td><td>Deer Park, Temples</td></tr>
<tr><td>Apr 21</td><td>Tokyo</td><td>Return, Bay Cruise</td></tr>
</table>
</div>
</div>
<button class="collapsible">🗣️ Essential Phrases</button>
<div class="content">
<div class="table-wrapper">
<table>
<tr><th>English</th><th>Japanese</th></tr>
<tr><td>Thank you</td><td>ありがとう</td></tr>
<tr><td>Excuse me</td><td>すみません</td></tr>
<tr><td>Please</td><td>お願いします</td></tr>
<tr><td>Where is...</td><td>...はどこですか</td></tr>
<tr><td>Help!</td><td>助けて!</td></tr>
</table>
</div>
</div>
<button class="collapsible">🚅 Transportation</button>
<div class="content">
<div class="card">
<h3>Key Routes</h3>
<ul>
<li>Tokyo-Kyoto: 2h15m</li>
<li>Kyoto-Nara: 45m</li>
<li>Last trains: ~midnight</li>
</ul>
<p><strong>JR Pass:</strong> Activate April 15</p>
</div>
</div>
<button class="collapsible">💍 Proposal Plan</button>
<div class="content">
<div class="card">
<h3>April 19 Timeline</h3>
<ul>
<li>4:00 PM: Head to Maruyama Park</li>
<li>5:30 PM: Arrive at spot</li>
<li>7:00 PM: Dinner at Kikunoi Roan</li>
</ul>
<p><strong>Backup:</strong> Gion Shirakawa area</p>
</div>
</div>
<button class="collapsible">💰 Budget Tracker</button>
<div class="content">
<div class="table-wrapper">
<table>
<tr><th>Item</th><th>Budget</th></tr>
<tr><td>Hotels</td><td>$1500-2000</td></tr>
<tr><td>Transport</td><td>$600-800</td></tr>
<tr><td>Food</td><td>$800-1000</td></tr>
<tr><td>Activities</td><td>$600-800</td></tr>
<tr><td>Shopping</td><td>$500-400</td></tr>
</table>
</div>
</div>
</div>
<script>
// Add click handlers for collapsible sections
var coll = document.getElementsByClassName("collapsible");
for (var i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
// Function to show specific section
function showSection(id) {
document.getElementById(id).scrollIntoView({
behavior: 'smooth'
});
}
</script>
</body>
</html>
@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Japan Travel Handbook (Print Version) - April 15-23, 2024</title>
<style>
@media print {
body {
font-family: Arial, sans-serif;
font-size: 11pt;
line-height: 1.4;
margin: 0.5in;
}
h1 { font-size: 16pt; }
h2 { font-size: 14pt; }
h3 { font-size: 12pt; }
.section {
margin: 10px 0;
padding: 5px;
border: 1px solid #ccc;
page-break-inside: avoid;
}
.no-break {
page-break-inside: avoid;
}
table {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
td, th {
border: 1px solid #000;
padding: 4px;
font-size: 10pt;
}
ul, ol {
margin: 5px 0;
padding-left: 20px;
}
li {
margin: 3px 0;
}
.page-break {
page-break-before: always;
}
}
/* Screen styles */
body {
font-family: Arial, sans-serif;
line-height: 1.4;
margin: 20px;
max-width: 800px;
margin: 0 auto;
}
.section {
margin: 15px 0;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
table {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
td, th {
border: 1px solid #000;
padding: 8px;
}
@media screen {
.page-break {
margin: 30px 0;
border-top: 2px dashed #ccc;
}
}
</style>
</head>
<body>
<h1>Japan Travel Handbook (Print Version)</h1>
<p><strong>Trip Dates:</strong> April 15-23, 2024</p>
<div class="section">
<h2>Emergency Contacts & Important Information</h2>
<ul>
<li>Emergency in Japan: 119 (Ambulance/Fire) / 110 (Police)</li>
<li>US Embassy Tokyo: +81-3-3224-5000</li>
<li>Tourist Information Hotline: 03-3201-3331</li>
<li>Your Travel Insurance: [Write number here]</li>
</ul>
</div>
<div class="section">
<h2>Daily Itinerary Summary</h2>
<table>
<tr><th>Date</th><th>Location</th><th>Key Activities</th></tr>
<tr><td>Apr 15</td><td>Tokyo</td><td>Arrival, Shinjuku area exploration</td></tr>
<tr><td>Apr 16</td><td>Tokyo</td><td>Meiji Shrine, Harajuku, Senso-ji, Skytree</td></tr>
<tr><td>Apr 17</td><td>Tokyo</td><td>Tea Ceremony, Budokan, Yanaka Ginza</td></tr>
<tr><td>Apr 18</td><td>Kyoto</td><td>Travel to Kyoto, Kinkaku-ji, Gion</td></tr>
<tr><td>Apr 19</td><td>Kyoto</td><td>Fushimi Inari, Arashiyama, Evening Proposal</td></tr>
<tr><td>Apr 20</td><td>Nara/Kyoto</td><td>Nara Park day trip, deer feeding</td></tr>
<tr><td>Apr 21</td><td>Tokyo</td><td>Return to Tokyo, bay cruise</td></tr>
</table>
</div>
<div class="page-break"></div>
<div class="section">
<h2>Essential Japanese Phrases</h2>
<table>
<tr><th>English</th><th>Japanese</th><th>When to Use</th></tr>
<tr><td>Arigatou gozaimasu</td><td>ありがとうございます</td><td>Thank you (formal)</td></tr>
<tr><td>Sumimasen</td><td>すみません</td><td>Excuse me/Sorry</td></tr>
<tr><td>Onegaishimasu</td><td>お願いします</td><td>Please</td></tr>
<tr><td>Toire wa doko desu ka?</td><td>トイレはどこですか?</td><td>Where is the bathroom?</td></tr>
<tr><td>Eigo ga hanasemasu ka?</td><td>英語が話せますか?</td><td>Do you speak English?</td></tr>
</table>
</div>
<div class="section">
<h2>Transportation Notes</h2>
<ul>
<li>JR Pass: Activate on April 15</li>
<li>Tokyo-Kyoto Shinkansen: ~2h15m</li>
<li>Kyoto-Nara Local Train: ~45m</li>
<li>Last trains: Usually around midnight</li>
<li>Keep ¥3000 for unexpected taxi rides</li>
</ul>
</div>
<div class="page-break"></div>
<div class="section no-break">
<h2>Proposal Day Timeline (April 19)</h2>
<table>
<tr><th>Time</th><th>Activity</th><th>Notes</th></tr>
<tr><td>4:00 PM</td><td>Head to Maruyama Park</td><td>Check weather first</td></tr>
<tr><td>4:30 PM</td><td>Tea house visit</td><td>Light refreshments</td></tr>
<tr><td>5:15 PM</td><td>Park walk begins</td><td>Head to weeping cherry tree</td></tr>
<tr><td>5:30 PM</td><td>Arrive at spot</td><td>Find quiet area</td></tr>
<tr><td>7:00 PM</td><td>Dinner reservation</td><td>Kikunoi Roan</td></tr>
</table>
<p><strong>Backup Location:</strong> Gion Shirakawa area (in case of rain)</p>
</div>
<div class="section">
<h2>Quick Reference Budget</h2>
<table>
<tr><th>Item</th><th>Budget (USD)</th><th>Notes</th></tr>
<tr><td>Hotels</td><td>1500-2000</td><td>Pre-booked</td></tr>
<tr><td>Transport</td><td>600-800</td><td>Including JR Pass</td></tr>
<tr><td>Food</td><td>800-1000</td><td>~$60/person/day</td></tr>
<tr><td>Activities</td><td>600-800</td><td>Including tea ceremony</td></tr>
<tr><td>Shopping</td><td>500-400</td><td>Souvenirs/gifts</td></tr>
</table>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+16
View File
@@ -0,0 +1,16 @@
# Examples
We put some examples in the `examples` directory. All the examples use the same prompt
as [Manus](https://manus.im/?utm_source=ai-bot.cn).
The Model we use is `claude3.5`.
## Japan Travel Plan
**Prompt**
```
I need a 7-day Japan itinerary for April 15-23 from Seattle, with a $2500-5000 budget for my fiancée and me. We love historical sites, hidden gems, and Japanese culture (kendo, tea ceremonies, Zen meditation). We want to see Nara's deer and explore cities on foot. I plan to propose during this trip and need a special location recommendation. Please provide a detailed itinerary and a simple HTML travel handbook with maps, attraction descriptions, essential Japanese phrases, and travel tips we can reference throughout our journey.
```
**preview**
![alt text](./pictures/japan-travel-plan-1.png)
![alt text](./pictures/japan-travel-plan-2.png)
+6 -2
View File
@@ -8,10 +8,14 @@ async def main():
agent = Manus()
while True:
try:
prompt = input("Enter your prompt (or 'exit' to quit): ")
if prompt.lower() == "exit":
prompt = input("Enter your prompt (or 'exit'/'quit' to quit): ")
prompt_lower = prompt.lower()
if prompt_lower in ["exit", "quit"]:
logger.info("Goodbye!")
break
if not prompt.strip():
logger.warning("Skipping empty prompt.")
continue
logger.warning("Processing your request...")
await agent.run(prompt)
except KeyboardInterrupt:
+4 -1
View File
@@ -5,6 +5,7 @@ pyyaml~=6.0.2
loguru~=0.7.3
numpy
datasets~=3.2.0
fastapi~=0.115.11
html2text~=2024.2.26
gymnasium~=1.0.0
@@ -17,4 +18,6 @@ googlesearch-python~=1.3.0
aiofiles~=24.1.0
pydantic_core~=2.27.2
colorama~=0.4.6
colorama~=0.4.6
playwright~=1.49.1
litellm~=1.63.6
+28 -8
View File
@@ -1,32 +1,52 @@
import asyncio
import time
from app.agent.manus import Manus
from app.flow.base import FlowType
from app.flow.flow_factory import FlowFactory
from app.logger import logger
async def run_flow():
agent = Manus()
agents = {
"manus": Manus(),
}
while True:
try:
prompt = input("Enter your prompt (or 'exit' to quit): ")
if prompt.lower() == "exit":
print("Goodbye!")
logger.info("Goodbye!")
break
flow = FlowFactory.create_flow(
flow_type=FlowType.PLANNING,
agents=agent,
agents=agents,
)
if prompt.strip().isspace():
logger.warning("Skipping empty prompt.")
continue
logger.warning("Processing your request...")
print("Processing your request...")
result = await flow.execute(prompt)
print(result)
try:
start_time = time.time()
result = await asyncio.wait_for(
flow.execute(prompt),
timeout=3600, # 60 minute timeout for the entire execution
)
elapsed_time = time.time() - start_time
logger.info(f"Request processed in {elapsed_time:.2f} seconds")
logger.info(result)
except asyncio.TimeoutError:
logger.error("Request processing timed out after 1 hour")
logger.info(
"Operation terminated due to timeout. Please try a simpler request."
)
except KeyboardInterrupt:
print("Goodbye!")
break
logger.info("Operation cancelled by user.")
except Exception as e:
logger.error(f"Error: {str(e)}")
if __name__ == "__main__":
+49
View File
@@ -0,0 +1,49 @@
from setuptools import find_packages, setup
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="openmanus",
version="0.1.0",
author="mannaandpoem and OpenManus Team",
author_email="mannaandpoem@gmail.com",
description="A versatile agent that can solve various tasks using multiple tools",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/mannaandpoem/OpenManus",
packages=find_packages(),
install_requires=[
"pydantic~=2.10.4",
"openai~=1.58.1",
"tenacity~=9.0.0",
"pyyaml~=6.0.2",
"loguru~=0.7.3",
"numpy",
"datasets~=3.2.0",
"html2text~=2024.2.26",
"gymnasium~=1.0.0",
"pillow~=10.4.0",
"browsergym~=0.13.3",
"uvicorn~=0.34.0",
"unidiff~=0.7.5",
"browser-use~=0.1.40",
"googlesearch-python~=1.3.0",
"aiofiles~=24.1.0",
"pydantic_core~=2.27.2",
"colorama~=0.4.6",
],
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires=">=3.12",
entry_points={
"console_scripts": [
"openmanus=main:main",
],
},
)