Merge branch 'dev' into code_intepreter

This commit is contained in:
yzlin 2024-01-31 00:08:09 +08:00
commit 2fcb2a1cfe
282 changed files with 6993 additions and 3210 deletions

View file

@ -13,7 +13,6 @@ from uuid import uuid4
import aiofiles
from azure.cognitiveservices.speech import AudioConfig, SpeechConfig, SpeechSynthesizer
from metagpt.config import CONFIG
from metagpt.logs import logger
@ -25,8 +24,8 @@ class AzureTTS:
:param subscription_key: key is used to access your Azure AI service API, see: `https://portal.azure.com/` > `Resource Management` > `Keys and Endpoint`
:param region: This is the location (or region) of your resource. You may need to use this field when making calls to this API.
"""
self.subscription_key = subscription_key if subscription_key else CONFIG.AZURE_TTS_SUBSCRIPTION_KEY
self.region = region if region else CONFIG.AZURE_TTS_REGION
self.subscription_key = subscription_key
self.region = region
# 参数参考https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service/language-support?tabs=tts#voice-styles-and-roles
async def synthesize_speech(self, lang, voice, text, output_file):
@ -83,10 +82,6 @@ async def oas3_azsure_tts(text, lang="", voice="", style="", role="", subscripti
role = "Girl"
if not style:
style = "affectionate"
if not subscription_key:
subscription_key = CONFIG.AZURE_TTS_SUBSCRIPTION_KEY
if not region:
region = CONFIG.AZURE_TTS_REGION
xml_value = AzureTTS.role_style_text(role=role, style=style, text=text)
tts = AzureTTS(subscription_key=subscription_key, region=region)

View file

@ -23,7 +23,6 @@ import aiofiles
import websockets as websockets
from pydantic import BaseModel
from metagpt.config import CONFIG
from metagpt.logs import logger
@ -56,9 +55,9 @@ class IFlyTekTTS(object):
:param api_key: WebAPI argument, see: `https://console.xfyun.cn/services/tts`
:param api_secret: WebAPI argument, see: `https://console.xfyun.cn/services/tts`
"""
self.app_id = app_id or CONFIG.IFLYTEK_APP_ID
self.api_key = api_key or CONFIG.IFLYTEK_API_KEY
self.api_secret = api_secret or CONFIG.API_SECRET
self.app_id = app_id
self.api_key = api_key
self.api_secret = api_secret
async def synthesize_speech(self, text, output_file: str, voice=DEFAULT_IFLYTEK_VOICE):
url = self._create_url()
@ -127,14 +126,6 @@ async def oas3_iflytek_tts(text: str, voice: str = "", app_id: str = "", api_key
:return: Returns the Base64-encoded .mp3 file data if successful, otherwise an empty string.
"""
if not app_id:
app_id = CONFIG.IFLYTEK_APP_ID
if not api_key:
api_key = CONFIG.IFLYTEK_API_KEY
if not api_secret:
api_secret = CONFIG.IFLYTEK_API_SECRET
if not voice:
voice = CONFIG.IFLYTEK_VOICE or DEFAULT_IFLYTEK_VOICE
filename = Path(__file__).parent / (uuid.uuid4().hex + ".mp3")
try:

View file

@ -13,7 +13,6 @@ import aiohttp
import requests
from pydantic import BaseModel
from metagpt.config import CONFIG
from metagpt.logs import logger
@ -22,7 +21,7 @@ class MetaGPTText2Image:
"""
:param model_url: Model reset api url
"""
self.model_url = model_url if model_url else CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL
self.model_url = model_url
async def text_2_image(self, text, size_type="512x512"):
"""Text to image
@ -93,6 +92,4 @@ async def oas3_metagpt_text_to_image(text, size_type: str = "512x512", model_url
"""
if not text:
return ""
if not model_url:
model_url = CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL
return await MetaGPTText2Image(model_url).text_2_image(text, size_type=size_type)

View file

@ -7,12 +7,12 @@
"""
from typing import Union
from metagpt.llm import LLM
from metagpt.provider.base_llm import BaseLLM
class Moderation:
def __init__(self):
self.llm = LLM()
def __init__(self, llm: BaseLLM):
self.llm = llm
def handle_moderation_results(self, results):
resp = []

View file

@ -13,7 +13,6 @@ import aiohttp
import requests
from pydantic import BaseModel, Field
from metagpt.config import CONFIG
from metagpt.logs import logger
@ -43,11 +42,12 @@ class ResultEmbedding(BaseModel):
class OpenAIText2Embedding:
def __init__(self, openai_api_key):
def __init__(self, api_key: str, proxy: str):
"""
:param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys`
"""
self.openai_api_key = openai_api_key or CONFIG.OPENAI_API_KEY
self.api_key = api_key
self.proxy = proxy
async def text_2_embedding(self, text, model="text-embedding-ada-002"):
"""Text to embedding
@ -57,8 +57,8 @@ class OpenAIText2Embedding:
:return: A json object of :class:`ResultEmbedding` class if successful, otherwise `{}`.
"""
proxies = {"proxy": CONFIG.openai_proxy} if CONFIG.openai_proxy else {}
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openai_api_key}"}
proxies = {"proxy": self.proxy} if self.proxy else {}
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"}
data = {"input": text, "model": model}
url = "https://api.openai.com/v1/embeddings"
try:
@ -72,16 +72,14 @@ class OpenAIText2Embedding:
# Export
async def oas3_openai_text_to_embedding(text, model="text-embedding-ada-002", openai_api_key=""):
async def oas3_openai_text_to_embedding(text, openai_api_key: str, model="text-embedding-ada-002", proxy: str = ""):
"""Text to embedding
:param text: The text used for embedding.
:param model: One of ['text-embedding-ada-002'], ID of the model to use. For more details, checkout: `https://api.openai.com/v1/models`.
:param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys`
:param config: OpenAI config with API key, For more details, checkout: `https://platform.openai.com/account/api-keys`
:return: A json object of :class:`ResultEmbedding` class if successful, otherwise `{}`.
"""
if not text:
return ""
if not openai_api_key:
openai_api_key = CONFIG.OPENAI_API_KEY
return await OpenAIText2Embedding(openai_api_key).text_2_embedding(text, model=model)
return await OpenAIText2Embedding(api_key=openai_api_key, proxy=proxy).text_2_embedding(text, model=model)

View file

@ -10,16 +10,13 @@
import aiohttp
import requests
from metagpt.llm import LLM
from metagpt.logs import logger
from metagpt.provider.base_llm import BaseLLM
class OpenAIText2Image:
def __init__(self):
"""
:param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys`
"""
self._llm = LLM()
def __init__(self, llm: BaseLLM):
self.llm = llm
async def text_2_image(self, text, size_type="1024x1024"):
"""Text to image
@ -29,7 +26,7 @@ class OpenAIText2Image:
:return: The image data is returned in Base64 encoding.
"""
try:
result = await self._llm.aclient.images.generate(prompt=text, n=1, size=size_type)
result = await self.llm.aclient.images.generate(prompt=text, n=1, size=size_type)
except Exception as e:
logger.error(f"An error occurred:{e}")
return ""
@ -57,13 +54,14 @@ class OpenAIText2Image:
# Export
async def oas3_openai_text_to_image(text, size_type: str = "1024x1024"):
async def oas3_openai_text_to_image(text, size_type: str = "1024x1024", llm: BaseLLM = None):
"""Text to image
:param text: The text used for image conversion.
:param size_type: One of ['256x256', '512x512', '1024x1024']
:param llm: LLM instance
:return: The image data is returned in Base64 encoding.
"""
if not text:
return ""
return await OpenAIText2Image().text_2_image(text, size_type=size_type)
return await OpenAIText2Image(llm).text_2_image(text, size_type=size_type)

View file

@ -10,7 +10,6 @@ from typing import Callable, Coroutine, Literal, Optional, Union, overload
from semantic_kernel.skill_definition import sk_function
from metagpt.config import CONFIG
from metagpt.tools import SearchEngineType
@ -43,22 +42,22 @@ class SearchEngine:
def __init__(
self,
engine: Optional[SearchEngineType] = None,
engine: Optional[SearchEngineType] = SearchEngineType.SERPER_GOOGLE,
run_func: Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]] = None,
**kwargs,
):
engine = engine or CONFIG.search_engine
if engine == SearchEngineType.SERPAPI_GOOGLE:
module = "metagpt.tools.search_engine_serpapi"
run_func = importlib.import_module(module).SerpAPIWrapper().run
run_func = importlib.import_module(module).SerpAPIWrapper(**kwargs).run
elif engine == SearchEngineType.SERPER_GOOGLE:
module = "metagpt.tools.search_engine_serper"
run_func = importlib.import_module(module).SerperWrapper().run
run_func = importlib.import_module(module).SerperWrapper(**kwargs).run
elif engine == SearchEngineType.DIRECT_GOOGLE:
module = "metagpt.tools.search_engine_googleapi"
run_func = importlib.import_module(module).GoogleAPIWrapper().run
run_func = importlib.import_module(module).GoogleAPIWrapper(**kwargs).run
elif engine == SearchEngineType.DUCK_DUCK_GO:
module = "metagpt.tools.search_engine_ddg"
run_func = importlib.import_module(module).DDGAPIWrapper().run
run_func = importlib.import_module(module).DDGAPIWrapper(**kwargs).run
elif engine == SearchEngineType.CUSTOM_ENGINE:
pass # run_func = run_func
else:

View file

@ -7,6 +7,8 @@ import json
from concurrent import futures
from typing import Literal, overload
from metagpt.config2 import config
try:
from duckduckgo_search import DDGS
except ImportError:
@ -15,8 +17,6 @@ except ImportError:
"You can install it by running the command: `pip install -e.[search-ddg]`"
)
from metagpt.config import CONFIG
class DDGAPIWrapper:
"""Wrapper around duckduckgo_search API.
@ -31,8 +31,8 @@ class DDGAPIWrapper:
executor: futures.Executor | None = None,
):
kwargs = {}
if CONFIG.global_proxy:
kwargs["proxies"] = CONFIG.global_proxy
if config.proxy:
kwargs["proxies"] = config.proxy
self.loop = loop
self.executor = executor
self.ddgs = DDGS(**kwargs)

View file

@ -11,7 +11,7 @@ from urllib.parse import urlparse
import httplib2
from pydantic import BaseModel, ConfigDict, Field, field_validator
from metagpt.config import CONFIG
from metagpt.config2 import config
from metagpt.logs import logger
try:
@ -35,7 +35,7 @@ class GoogleAPIWrapper(BaseModel):
@field_validator("google_api_key", mode="before")
@classmethod
def check_google_api_key(cls, val: str):
val = val or CONFIG.google_api_key
val = val or config.search.api_key
if not val:
raise ValueError(
"To use, make sure you provide the google_api_key when constructing an object. Alternatively, "
@ -47,7 +47,7 @@ class GoogleAPIWrapper(BaseModel):
@field_validator("google_cse_id", mode="before")
@classmethod
def check_google_cse_id(cls, val: str):
val = val or CONFIG.google_cse_id
val = val or config.search.cse_id
if not val:
raise ValueError(
"To use, make sure you provide the google_cse_id when constructing an object. Alternatively, "
@ -59,8 +59,8 @@ class GoogleAPIWrapper(BaseModel):
@property
def google_api_client(self):
build_kwargs = {"developerKey": self.google_api_key}
if CONFIG.global_proxy:
parse_result = urlparse(CONFIG.global_proxy)
if config.proxy:
parse_result = urlparse(config.proxy)
proxy_type = parse_result.scheme
if proxy_type == "https":
proxy_type = "http"

View file

@ -10,7 +10,7 @@ from typing import Any, Dict, Optional, Tuple
import aiohttp
from pydantic import BaseModel, ConfigDict, Field, field_validator
from metagpt.config import CONFIG
from metagpt.config2 import config
class SerpAPIWrapper(BaseModel):
@ -32,7 +32,7 @@ class SerpAPIWrapper(BaseModel):
@field_validator("serpapi_api_key", mode="before")
@classmethod
def check_serpapi_api_key(cls, val: str):
val = val or CONFIG.serpapi_api_key
val = val or config.search.api_key
if not val:
raise ValueError(
"To use, make sure you provide the serpapi_api_key when constructing an object. Alternatively, "

View file

@ -11,7 +11,7 @@ from typing import Any, Dict, Optional, Tuple
import aiohttp
from pydantic import BaseModel, ConfigDict, Field, field_validator
from metagpt.config import CONFIG
from metagpt.config2 import config
class SerperWrapper(BaseModel):
@ -25,7 +25,7 @@ class SerperWrapper(BaseModel):
@field_validator("serper_api_key", mode="before")
@classmethod
def check_serper_api_key(cls, val: str):
val = val or CONFIG.serper_api_key
val = val or config.search.api_key
if not val:
raise ValueError(
"To use, make sure you provide the serper_api_key when constructing an object. Alternatively, "

View file

@ -4,6 +4,7 @@
import json
from pathlib import Path
from metagpt.config2 import config
from metagpt.provider.openai_api import OpenAILLM as GPTAPI
from metagpt.utils.common import awrite
@ -281,6 +282,6 @@ class UTGenerator:
"""Choose based on different calling methods"""
result = ""
if self.chatgpt_method == "API":
result = await GPTAPI().aask_code(messages=messages)
result = await GPTAPI(config.get_openai_llm()).aask_code(messages=messages)
return result

View file

@ -1,14 +1,11 @@
#!/usr/bin/env python
"""
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
# -*- coding: utf-8 -*-
from __future__ import annotations
import importlib
from typing import Any, Callable, Coroutine, overload
from metagpt.config import CONFIG
from metagpt.tools import WebBrowserEngineType
from metagpt.utils.parse_html import WebPage
@ -16,10 +13,9 @@ from metagpt.utils.parse_html import WebPage
class WebBrowserEngine:
def __init__(
self,
engine: WebBrowserEngineType | None = None,
engine: WebBrowserEngineType = WebBrowserEngineType.PLAYWRIGHT,
run_func: Callable[..., Coroutine[Any, Any, WebPage | list[WebPage]]] | None = None,
):
engine = engine or CONFIG.web_browser_engine
if engine is None:
raise NotImplementedError

View file

@ -1,7 +1,5 @@
#!/usr/bin/env python
"""
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
# -*- coding: utf-8 -*-
from __future__ import annotations
@ -12,6 +10,7 @@ from typing import Literal
from playwright.async_api import async_playwright
from metagpt.config2 import config
from metagpt.logs import logger
from metagpt.utils.parse_html import WebPage
@ -27,20 +26,16 @@ class PlaywrightWrapper:
def __init__(
self,
browser_type: Literal["chromium", "firefox", "webkit"] | None = None,
browser_type: Literal["chromium", "firefox", "webkit"] | None = "chromium",
launch_kwargs: dict | None = None,
**kwargs,
) -> None:
from metagpt.config import CONFIG
if browser_type is None:
browser_type = CONFIG.playwright_browser_type
self.browser_type = browser_type
launch_kwargs = launch_kwargs or {}
if CONFIG.global_proxy and "proxy" not in launch_kwargs:
if config.proxy and "proxy" not in launch_kwargs:
args = launch_kwargs.get("args", [])
if not any(str.startswith(i, "--proxy-server=") for i in args):
launch_kwargs["proxy"] = {"server": CONFIG.global_proxy}
launch_kwargs["proxy"] = {"server": config.proxy}
self.launch_kwargs = launch_kwargs
context_kwargs = {}
if "ignore_https_errors" in kwargs:
@ -80,8 +75,8 @@ class PlaywrightWrapper:
executable_path = Path(browser_type.executable_path)
if not executable_path.exists() and "executable_path" not in self.launch_kwargs:
kwargs = {}
if CONFIG.global_proxy:
kwargs["env"] = {"ALL_PROXY": CONFIG.global_proxy}
if config.proxy:
kwargs["env"] = {"ALL_PROXY": config.proxy}
await _install_browsers(self.browser_type, **kwargs)
if self._has_run_precheck:

View file

@ -1,7 +1,5 @@
#!/usr/bin/env python
"""
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
# -*- coding: utf-8 -*-
from __future__ import annotations
@ -17,7 +15,7 @@ from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.core.download_manager import WDMDownloadManager
from webdriver_manager.core.http import WDMHttpClient
from metagpt.config import CONFIG
from metagpt.config2 import config
from metagpt.utils.parse_html import WebPage
@ -35,18 +33,16 @@ class SeleniumWrapper:
def __init__(
self,
browser_type: Literal["chrome", "firefox", "edge", "ie"] | None = None,
browser_type: Literal["chrome", "firefox", "edge", "ie"] = "chrome",
launch_kwargs: dict | None = None,
*,
loop: asyncio.AbstractEventLoop | None = None,
executor: futures.Executor | None = None,
) -> None:
if browser_type is None:
browser_type = CONFIG.selenium_browser_type
self.browser_type = browser_type
launch_kwargs = launch_kwargs or {}
if CONFIG.global_proxy and "proxy-server" not in launch_kwargs:
launch_kwargs["proxy-server"] = CONFIG.global_proxy
if config.proxy and "proxy-server" not in launch_kwargs:
launch_kwargs["proxy-server"] = config.proxy
self.executable_path = launch_kwargs.pop("executable_path", None)
self.launch_args = [f"--{k}={v}" for k, v in launch_kwargs.items()]
@ -97,8 +93,8 @@ _webdriver_manager_types = {
class WDMHttpProxyClient(WDMHttpClient):
def get(self, url, **kwargs):
if "proxies" not in kwargs and CONFIG.global_proxy:
kwargs["proxies"] = {"all_proxy": CONFIG.global_proxy}
if "proxies" not in kwargs and config.proxy:
kwargs["proxies"] = {"all_proxy": config.proxy}
return super().get(url, **kwargs)