update README and format code

This commit is contained in:
liangxinbing 2025-03-11 23:07:31 +08:00
parent 737abe4f90
commit 487b44fda8
7 changed files with 85 additions and 56 deletions

View File

@ -143,6 +143,9 @@ Join our networking group on Feishu and share your experience with other develop
Thanks to [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) 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! 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)
and [MetaGPT](https://github.com/mannaandpoem/MetaGPT).
OpenManus is built by contributors from MetaGPT. Huge thanks to this agent community! OpenManus is built by contributors from MetaGPT. Huge thanks to this agent community!
## Cite ## Cite

View File

@ -145,4 +145,7 @@ python run_flow.py
特别感谢 [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) 特别感谢 [anthropic-computer-use](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)
和 [browser-use](https://github.com/browser-use/browser-use) 为本项目提供的基础支持! 和 [browser-use](https://github.com/browser-use/browser-use) 为本项目提供的基础支持!
此外,我们感谢 [AAAJ](https://github.com/metauto-ai/agent-as-a-judge)
和 [MetaGPT](https://github.com/mannaandpoem/MetaGPT).
OpenManus 由 MetaGPT 社区的贡献者共同构建,感谢这个充满活力的智能体开发者社区! OpenManus 由 MetaGPT 社区的贡献者共同构建,感谢这个充满活力的智能体开发者社区!

View File

@ -1,6 +1,6 @@
from typing import Dict, List, Literal, Optional, Union, Tuple, Any
import os
import base64 import base64
import os
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
import litellm import litellm
from litellm import completion, completion_cost from litellm import completion, completion_cost
@ -9,12 +9,17 @@ from litellm.exceptions import (
RateLimitError, RateLimitError,
ServiceUnavailableError, ServiceUnavailableError,
) )
from tenacity import retry, stop_after_attempt, wait_random_exponential, retry_if_exception_type from tenacity import (
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from app.config import LLMSettings, config from app.config import LLMSettings, config
from app.llm.cost import Cost
from app.logger import logger from app.logger import logger
from app.schema import Message from app.schema import Message
from app.llm.cost import Cost
class LLM: class LLM:
@ -32,7 +37,9 @@ class LLM:
def __init__( def __init__(
self, config_name: str = "default", llm_config: Optional[LLMSettings] = None self, config_name: str = "default", llm_config: Optional[LLMSettings] = None
): ):
if not hasattr(self, "initialized"): # Only initialize if not already initialized if not hasattr(
self, "initialized"
): # Only initialize if not already initialized
llm_config = llm_config or config.llm llm_config = llm_config or config.llm
llm_config = llm_config.get(config_name, llm_config["default"]) llm_config = llm_config.get(config_name, llm_config["default"])
@ -41,7 +48,9 @@ class LLM:
self.temperature = getattr(llm_config, "temperature", 0.7) self.temperature = getattr(llm_config, "temperature", 0.7)
self.top_p = getattr(llm_config, "top_p", 0.9) self.top_p = getattr(llm_config, "top_p", 0.9)
self.api_type = getattr(llm_config, "api_type", "openai") 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_key = getattr(
llm_config, "api_key", os.environ.get("OPENAI_API_KEY", "")
)
self.api_version = getattr(llm_config, "api_version", "") self.api_version = getattr(llm_config, "api_version", "")
self.base_url = getattr(llm_config, "base_url", "https://api.openai.com/v1") self.base_url = getattr(llm_config, "base_url", "https://api.openai.com/v1")
self.timeout = getattr(llm_config, "timeout", 60) self.timeout = getattr(llm_config, "timeout", 60)
@ -183,7 +192,9 @@ class LLM:
# Add the cost to our tracker # Add the cost to our tracker
if cost > 0: if cost > 0:
self.cost_tracker.add_cost(cost) self.cost_tracker.add_cost(cost)
logger.info(f"Added cost: ${cost:.6f}, Total: ${self.cost_tracker.accumulated_cost:.6f}") logger.info(
f"Added cost: ${cost:.6f}, Total: ${self.cost_tracker.accumulated_cost:.6f}"
)
return cost return cost
except Exception as e: except Exception as e:
@ -202,7 +213,9 @@ class LLM:
substring in self.base_url substring in self.base_url
for substring in ["localhost", "127.0.0.1", "0.0.0.0"] 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()): if self.model and (
self.model.startswith("ollama") or "local" in self.model.lower()
):
return True return True
return False return False
@ -234,7 +247,9 @@ class LLM:
with open(image_path, "rb") as image_file: with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8") return base64.b64encode(image_file.read()).decode("utf-8")
def prepare_messages(self, text: str, image_path: Optional[str] = None) -> List[dict]: def prepare_messages(
self, text: str, image_path: Optional[str] = None
) -> List[dict]:
""" """
Prepare messages for completion, including multimodal content if needed. Prepare messages for completion, including multimodal content if needed.
@ -257,7 +272,9 @@ class LLM:
] ]
return messages return messages
def do_multimodal_completion(self, text: str, image_path: str) -> Tuple[Any, float, float]: def do_multimodal_completion(
self, text: str, image_path: str
) -> Tuple[Any, float, float]:
""" """
Perform a multimodal completion with text and image. Perform a multimodal completion with text and image.
@ -342,7 +359,7 @@ class LLM:
print(chunk_message, end="", flush=True) print(chunk_message, end="", flush=True)
# For streaming responses, cost is calculated on the last chunk # For streaming responses, cost is calculated on the last chunk
if hasattr(chunk, 'usage') and chunk.usage: if hasattr(chunk, "usage") and chunk.usage:
self._calculate_and_track_cost(chunk) self._calculate_and_track_cost(chunk)
print() # Newline after streaming print() # Newline after streaming
@ -484,6 +501,7 @@ class LLM:
if __name__ == "__main__": if __name__ == "__main__":
# Load environment variables if needed # Load environment variables if needed
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
# Create LLM instance # Create LLM instance
@ -501,5 +519,7 @@ if __name__ == "__main__":
multimodal_response, mm_cost, mm_total_cost = llm.do_multimodal_completion( multimodal_response, mm_cost, mm_total_cost = llm.do_multimodal_completion(
"What's in this image?", image_path "What's in this image?", image_path
) )
print(f"Multimodal response: {multimodal_response['choices'][0]['message']['content']}") print(
f"Multimodal response: {multimodal_response['choices'][0]['message']['content']}"
)
print(f"Cost: ${mm_cost:.6f}, Total cost: ${mm_total_cost:.6f}") print(f"Cost: ${mm_cost:.6f}, Total cost: ${mm_total_cost:.6f}")

View File

@ -11,6 +11,7 @@ from pydantic_core.core_schema import ValidationInfo
from app.tool.base import BaseTool, ToolResult from app.tool.base import BaseTool, ToolResult
MAX_LENGTH = 2000 MAX_LENGTH = 2000
_BROWSER_DESCRIPTION = """ _BROWSER_DESCRIPTION = """
@ -181,7 +182,9 @@ class BrowserUseTool(BaseTool):
elif action == "get_html": elif action == "get_html":
html = await context.get_page_html() html = await context.get_page_html()
truncated = html[:MAX_LENGTH] + "..." if len(html) > MAX_LENGTH else html truncated = (
html[:MAX_LENGTH] + "..." if len(html) > MAX_LENGTH else html
)
return ToolResult(output=truncated) return ToolResult(output=truncated)
elif action == "get_text": elif action == "get_text":