Files

1 line
20 KiB
JSON

{"repo": "bingreeky/maas", "files": {"maas/configs/llm_config.py": {"987f3c1bc9a96e844fe090db3791446e3ef0f5c7": {"t": 1785674191.3681855, "n": 3920, "h": "64449acd11e83212", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2024/1/4 16:33\n@Author : alexanderwu\n@File : llm_config.py\n\"\"\"\n\nfrom enum import Enum\nfrom typing import Optional\n\nfrom pydantic import field_validator\n\nfrom maas.const import CONFIG_ROOT, LLM_API_TIMEOUT, METAGPT_ROOT\nfrom maas.utils.yaml_model import YamlModel\n\n\nclass LLMType(Enum):\n OPENAI = \"openai\"\n ANTHROPIC = \"anthropic\"\n CLAUDE = \"claude\" # alias name of anthropic\n SPARK = \"spark\"\n ZHIPUAI = \"zhipuai\"\n FIREWORKS = \"fireworks\"\n OPEN_LLM = \"open_llm\"\n GEMINI = \"gemini\"\n METAGPT = \"metagpt\"\n AZURE = \"azure\"\n OLLAMA = \"ollama\" # /chat at ollama api\n OLLAMA_GENERATE = \"ollama.generate\" # /generate at ollama api\n OLLAMA_EMBEDDINGS = \"ollama.embeddings\" # /embeddings at ollama api\n OLLAMA_EMBED = \"ollama.embed\" # /embed at ollama api\n QIANFAN = \"qianfan\" # Baidu BCE\n DASHSCOPE = \"dashscope\" # Aliyun LingJi DashScope\n MOONSHOT = \"moonshot\"\n MISTRAL = \"mistral\"\n YI = \"yi\" # lingyiwanwu\n OPENROUTER = \"openrouter\"\n BEDROCK = \"bedrock\"\n ARK = \"ark\" # https://www.volcengine.com/docs/82379/1263482#python-sdk\n\n def __missing__(self, key):\n return self.OPENAI\n\n\nclass LLMConfig(YamlModel):\n \"\"\"Config for LLM\n\n OpenAI: https://github.com/openai/openai-python/blob/main/src/openai/resources/chat/completions.py#L681\n Optional Fields in pydantic: https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields\n \"\"\"\n\n api_key: str = \"sk-\"\n api_type: LLMType = LLMType.OPENAI\n base_url: str = \"https://api.openai.com/v1\"\n api_version: Optional[str] = None\n\n model: Optional[str] = None # also stands for DEPLOYMENT_NAME\n pricing_plan: Optional[str] = None # Cost Settlement Plan Parameters.\n\n # For Cloud Service Provider like Baidu/ Alibaba\n access_key: Optional[str] = None\n secret_key: Optional[str] = None\n session_token: Optional[str] = None\n endpoint: Optional[str] = None # for self-deployed model on the cloud\n\n # For Spark(Xunfei), maybe remove later\n app_id: Optional[str] = None\n api_secret: Optional[str] = None\n domain: Optional[str] = None\n\n # For Chat Completion\n max_token: int = 4096\n temperature: float = 0.0\n top_p: float = 1.0\n top_k: int = 0\n repetition_penalty: float = 1.0\n stop: Optional[str] = None\n presence_penalty: float = 0.0\n frequency_penalty: float = 0.0\n best_of: Optional[int] = None\n n: Optional[int] = None\n stream: bool = True\n seed: Optional[int] = None\n # https://cookbook.openai.com/examples/using_logprobs\n logprobs: Optional[bool] = None\n top_logprobs: Optional[int] = None\n timeout: int = 600\n context_length: Optional[int] = None # Max input tokens\n\n # For Amazon Bedrock\n region_name: str = None\n\n # For Network\n proxy: Optional[str] = None\n\n # Cost Control\n calc_usage: bool = True\n\n # For Messages Control\n use_system_prompt: bool = True\n\n @field_validator(\"api_key\")\n @classmethod\n def check_llm_key(cls, v):\n if v in [\"\", None, \"YOUR_API_KEY\"]:\n repo_config_path = METAGPT_ROOT / \"config/config2.yaml\"\n root_config_path = CONFIG_ROOT / \"config2.yaml\"\n if root_config_path.exists():\n raise ValueError(\n f\"Please set your API key in {root_config_path}. If you also set your config in {repo_config_path}, \\n\"\n f\"the former will overwrite the latter. This may cause unexpected result.\\n\"\n )\n elif repo_config_path.exists():\n raise ValueError(f\"Please set your API key in {repo_config_path}\")\n else:\n raise ValueError(\"Please set your API key in config2.yaml\")\n return v\n\n @field_validator(\"timeout\")\n @classmethod\n def check_timeout(cls, v):\n return v or LLM_API_TIMEOUT\n"}}, "maas/provider/ark_api.py": {"987f3c1bc9a96e844fe090db3791446e3ef0f5c7": {"t": 1785674278.3273704, "n": 3957, "h": "db3b94d51ade5b2e", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\nProvider for volcengine.\nSee Also: https://console.volcengine.com/ark/region:ark+cn-beijing/model\n\nconfig2.yaml example:\n```yaml\nllm:\n base_url: \"https://ark.cn-beijing.volces.com/api/v3\"\n api_type: \"ark\"\n endpoint: \"ep-2024080514****-d****\"\n api_key: \"d47****b-****-****-****-d6e****0fd77\"\n pricing_plan: \"doubao-lite\"\n```\n\"\"\"\nfrom typing import Optional, Union\n\nfrom pydantic import BaseModel\nfrom volcenginesdkarkruntime import AsyncArk\nfrom volcenginesdkarkruntime._base_client import AsyncHttpxClientWrapper\nfrom volcenginesdkarkruntime._streaming import AsyncStream\nfrom volcenginesdkarkruntime.types.chat import ChatCompletion, ChatCompletionChunk\n\nfrom maas.configs.llm_config import LLMType\nfrom maas.const import USE_CONFIG_TIMEOUT\nfrom maas.logs import log_llm_stream\nfrom maas.provider.llm_provider_registry import register_provider\nfrom maas.provider.openai_api import OpenAILLM\nfrom maas.utils.token_counter import DOUBAO_TOKEN_COSTS\n\n\n@register_provider(LLMType.ARK)\nclass ArkLLM(OpenAILLM):\n \"\"\"\n 用于火山方舟的API\n 见:https://www.volcengine.com/docs/82379/1263482\n \"\"\"\n\n aclient: Optional[AsyncArk] = None\n\n def _init_client(self):\n \"\"\"SDK: https://github.com/openai/openai-python#async-usage\"\"\"\n self.model = (\n self.config.endpoint or self.config.model\n ) # endpoint name, See more: https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint\n self.pricing_plan = self.config.pricing_plan or self.model\n kwargs = self._make_client_kwargs()\n self.aclient = AsyncArk(**kwargs)\n\n def _make_client_kwargs(self) -> dict:\n kvs = {\n \"ak\": self.config.access_key,\n \"sk\": self.config.secret_key,\n \"api_key\": self.config.api_key,\n \"base_url\": self.config.base_url,\n }\n kwargs = {k: v for k, v in kvs.items() if v}\n\n # to use proxy, openai v1 needs http_client\n if proxy_params := self._get_proxy_params():\n kwargs[\"http_client\"] = AsyncHttpxClientWrapper(**proxy_params)\n\n return kwargs\n\n def _update_costs(self, usage: Union[dict, BaseModel], model: str = None, local_calc_usage: bool = True):\n if next(iter(DOUBAO_TOKEN_COSTS)) not in self.cost_manager.token_costs:\n self.cost_manager.token_costs.update(DOUBAO_TOKEN_COSTS)\n if model in self.cost_manager.token_costs:\n self.pricing_plan = model\n if self.pricing_plan in self.cost_manager.token_costs:\n super()._update_costs(usage, self.pricing_plan, local_calc_usage)\n\n async def _achat_completion_stream(self, messages: list[dict], timeout=USE_CONFIG_TIMEOUT) -> str:\n response: AsyncStream[ChatCompletionChunk] = await self.aclient.chat.completions.create(\n **self._cons_kwargs(messages, timeout=self.get_timeout(timeout)),\n stream=True,\n extra_body={\"stream_options\": {\"include_usage\": True}}, # 只有增加这个参数才会在流式时最后返回usage\n )\n usage = None\n collected_messages = []\n async for chunk in response:\n chunk_message = chunk.choices[0].delta.content or \"\" if chunk.choices else \"\" # extract the message\n log_llm_stream(chunk_message)\n collected_messages.append(chunk_message)\n if chunk.usage:\n # 火山方舟的流式调用会在最后一个chunk中返回usage,最后一个chunk的choices为[]\n usage = chunk.usage\n\n log_llm_stream(\"\\n\")\n full_reply_content = \"\".join(collected_messages)\n self._update_costs(usage, chunk.model)\n return full_reply_content\n\n async def _achat_completion(self, messages: list[dict], timeout=USE_CONFIG_TIMEOUT) -> ChatCompletion:\n kwargs = self._cons_kwargs(messages, timeout=self.get_timeout(timeout))\n rsp: ChatCompletion = await self.aclient.chat.completions.create(**kwargs)\n self._update_costs(rsp.usage, rsp.model)\n return rsp\n"}}, "maas/tools/iflytek_tts.py": {"987f3c1bc9a96e844fe090db3791446e3ef0f5c7": {"t": 1785674320.0219257, "n": 5401, "h": "2229cb64829f8f1b", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/8/17\n@Author : mashenquan\n@File : iflytek_tts.py\n@Desc : iFLYTEK TTS OAS3 api, which provides text-to-speech functionality\n\"\"\"\nimport base64\nimport hashlib\nimport hmac\nimport json\nimport uuid\nfrom datetime import datetime\nfrom enum import Enum\nfrom pathlib import Path\nfrom time import mktime\nfrom typing import Optional\nfrom urllib.parse import urlencode\nfrom wsgiref.handlers import format_date_time\n\nimport aiofiles\nimport websockets as websockets\nfrom pydantic import BaseModel\n\nfrom maas.logs import logger\n\n\nclass IFlyTekTTSStatus(Enum):\n STATUS_FIRST_FRAME = 0 # The first frame\n STATUS_CONTINUE_FRAME = 1 # The intermediate frame\n STATUS_LAST_FRAME = 2 # The last frame\n\n\nclass AudioData(BaseModel):\n audio: str\n status: int\n ced: str\n\n\nclass IFlyTekTTSResponse(BaseModel):\n code: int\n message: str\n data: Optional[AudioData] = None\n sid: str\n\n\nDEFAULT_IFLYTEK_VOICE = \"xiaoyan\"\n\n\nclass IFlyTekTTS(object):\n def __init__(self, app_id: str, api_key: str, api_secret: str):\n \"\"\"\n :param app_id: Application ID is used to access your iFlyTek service API, see: `https://console.xfyun.cn/services/tts`\n :param api_key: WebAPI argument, see: `https://console.xfyun.cn/services/tts`\n :param api_secret: WebAPI argument, see: `https://console.xfyun.cn/services/tts`\n \"\"\"\n self.app_id = app_id\n self.api_key = api_key\n self.api_secret = api_secret\n\n async def synthesize_speech(self, text, output_file: str, voice=DEFAULT_IFLYTEK_VOICE):\n url = self._create_url()\n data = {\n \"common\": {\"app_id\": self.app_id},\n \"business\": {\"aue\": \"lame\", \"sfl\": 1, \"auf\": \"audio/L16;rate=16000\", \"vcn\": voice, \"tte\": \"utf8\"},\n \"data\": {\"status\": 2, \"text\": str(base64.b64encode(text.encode(\"utf-8\")), \"UTF8\")},\n }\n req = json.dumps(data)\n async with websockets.connect(url) as websocket:\n # send request\n await websocket.send(req)\n\n # receive frames\n async with aiofiles.open(str(output_file), \"wb\") as writer:\n while True:\n v = await websocket.recv()\n rsp = IFlyTekTTSResponse(**json.loads(v))\n if rsp.data:\n binary_data = base64.b64decode(rsp.data.audio)\n await writer.write(binary_data)\n if rsp.data.status != IFlyTekTTSStatus.STATUS_LAST_FRAME.value:\n continue\n break\n\n def _create_url(self):\n \"\"\"Create request url\"\"\"\n url = \"wss://tts-api.xfyun.cn/v2/tts\"\n # Generate a timestamp in RFC1123 format\n now = datetime.now()\n date = format_date_time(mktime(now.timetuple()))\n\n signature_origin = \"host: \" + \"ws-api.xfyun.cn\" + \"\\n\"\n signature_origin += \"date: \" + date + \"\\n\"\n signature_origin += \"GET \" + \"/v2/tts \" + \"HTTP/1.1\"\n # Perform HMAC-SHA256 encryption\n signature_sha = hmac.new(\n self.api_secret.encode(\"utf-8\"), signature_origin.encode(\"utf-8\"), digestmod=hashlib.sha256\n ).digest()\n signature_sha = base64.b64encode(signature_sha).decode(encoding=\"utf-8\")\n\n authorization_origin = 'api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"' % (\n self.api_key,\n \"hmac-sha256\",\n \"host date request-line\",\n signature_sha,\n )\n authorization = base64.b64encode(authorization_origin.encode(\"utf-8\")).decode(encoding=\"utf-8\")\n # Combine the authentication parameters of the request into a dictionary.\n v = {\"authorization\": authorization, \"date\": date, \"host\": \"ws-api.xfyun.cn\"}\n # Concatenate the authentication parameters to generate the URL.\n url = url + \"?\" + urlencode(v)\n return url\n\n\n# Export\nasync def oas3_iflytek_tts(text: str, voice: str = \"\", app_id: str = \"\", api_key: str = \"\", api_secret: str = \"\"):\n \"\"\"Text to speech\n For more details, check out:`https://www.xfyun.cn/doc/tts/online_tts/API.html`\n\n :param voice: Default `xiaoyan`. For more details, checkout: `https://www.xfyun.cn/doc/tts/online_tts/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B`\n :param text: The text used for voice conversion.\n :param app_id: Application ID is used to access your iFlyTek service API, see: `https://console.xfyun.cn/services/tts`\n :param api_key: WebAPI argument, see: `https://console.xfyun.cn/services/tts`\n :param api_secret: WebAPI argument, see: `https://console.xfyun.cn/services/tts`\n :return: Returns the Base64-encoded .mp3 file data if successful, otherwise an empty string.\n\n \"\"\"\n\n filename = Path(__file__).parent / (uuid.uuid4().hex + \".mp3\")\n try:\n tts = IFlyTekTTS(app_id=app_id, api_key=api_key, api_secret=api_secret)\n await tts.synthesize_speech(text=text, output_file=str(filename), voice=voice)\n async with aiofiles.open(str(filename), mode=\"rb\") as reader:\n data = await reader.read()\n base64_string = base64.b64encode(data).decode(\"utf-8\")\n except Exception as e:\n logger.error(f\"text:{text}, error:{e}\")\n base64_string = \"\"\n finally:\n filename.unlink(missing_ok=True)\n\n return base64_string\n"}}, "maas/config2.py": {"987f3c1bc9a96e844fe090db3791446e3ef0f5c7": {"t": 1785674324.087591, "n": 5292, "h": "65d35bc36c1fca06", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2024/1/4 01:25\n@Author : alexanderwu\n@File : config2.py\n\"\"\"\nimport os\nfrom pathlib import Path\nfrom typing import Dict, Iterable, List, Literal, Optional\n\nfrom pydantic import BaseModel, model_validator\n\nfrom maas.configs.browser_config import BrowserConfig\nfrom maas.configs.embedding_config import EmbeddingConfig\nfrom maas.configs.file_parser_config import OmniParseConfig\nfrom maas.configs.llm_config import LLMConfig, LLMType\nfrom maas.configs.mermaid_config import MermaidConfig\nfrom maas.configs.redis_config import RedisConfig\nfrom maas.configs.s3_config import S3Config\nfrom maas.configs.search_config import SearchConfig\nfrom maas.configs.workspace_config import WorkspaceConfig\nfrom maas.const import CONFIG_ROOT, METAGPT_ROOT\nfrom maas.utils.yaml_model import YamlModel\n\n\nclass CLIParams(BaseModel):\n \"\"\"CLI parameters\"\"\"\n\n project_path: str = \"\"\n project_name: str = \"\"\n inc: bool = False\n reqa_file: str = \"\"\n max_auto_summarize_code: int = 0\n git_reinit: bool = False\n\n @model_validator(mode=\"after\")\n def check_project_path(self):\n \"\"\"Check project_path and project_name\"\"\"\n if self.project_path:\n self.inc = True\n self.project_name = self.project_name or Path(self.project_path).name\n return self\n\n\nclass Config(CLIParams, YamlModel):\n \"\"\"Configurations for MetaGPT\"\"\"\n\n # Key Parameters\n llm: LLMConfig\n\n # RAG Embedding\n embedding: EmbeddingConfig = EmbeddingConfig()\n\n # omniparse\n omniparse: OmniParseConfig = OmniParseConfig()\n\n # Global Proxy. Will be used if llm.proxy is not set\n proxy: str = \"\"\n\n # Tool Parameters\n search: SearchConfig = SearchConfig()\n browser: BrowserConfig = BrowserConfig()\n mermaid: MermaidConfig = MermaidConfig()\n\n # Storage Parameters\n s3: Optional[S3Config] = None\n redis: Optional[RedisConfig] = None\n\n # Misc Parameters\n repair_llm_output: bool = False\n prompt_schema: Literal[\"json\", \"markdown\", \"raw\"] = \"json\"\n workspace: WorkspaceConfig = WorkspaceConfig()\n enable_longterm_memory: bool = False\n code_review_k_times: int = 2\n agentops_api_key: str = \"\"\n\n # Will be removed in the future\n metagpt_tti_url: str = \"\"\n language: str = \"English\"\n redis_key: str = \"placeholder\"\n iflytek_app_id: str = \"\"\n iflytek_api_secret: str = \"\"\n iflytek_api_key: str = \"\"\n azure_tts_subscription_key: str = \"\"\n azure_tts_region: str = \"\"\n _extra: dict = dict() # extra config dict\n\n @classmethod\n def from_home(cls, path):\n \"\"\"Load config from ~/.maas/config2.yaml\"\"\"\n pathname = CONFIG_ROOT / path\n if not pathname.exists():\n return None\n return Config.from_yaml_file(pathname)\n\n @classmethod\n def default(cls):\n \"\"\"Load default config\n - Priority: env < default_config_paths\n - Inside default_config_paths, the latter one overwrites the former one\n \"\"\"\n default_config_paths: List[Path] = [\n METAGPT_ROOT / \"config/config2.yaml\",\n CONFIG_ROOT / \"config2.yaml\",\n ]\n\n dicts = [dict(os.environ)]\n dicts += [Config.read_yaml(path) for path in default_config_paths]\n final = merge_dict(dicts)\n return Config(**final)\n\n @classmethod\n def from_llm_config(cls, llm_config: dict):\n \"\"\"user config llm\n example:\n llm_config = {\"api_type\": \"xxx\", \"api_key\": \"xxx\", \"model\": \"xxx\"}\n gpt4 = Config.from_llm_config(llm_config)\n A = Role(name=\"A\", profile=\"Democratic candidate\", goal=\"Win the election\", actions=[a1], watch=[a2], config=gpt4)\n \"\"\"\n llm_config = LLMConfig.model_validate(llm_config)\n dicts = [dict(os.environ)]\n dicts += [{\"llm\": llm_config}]\n final = merge_dict(dicts)\n return Config(**final)\n\n def update_via_cli(self, project_path, project_name, inc, reqa_file, max_auto_summarize_code):\n \"\"\"update config via cli\"\"\"\n\n # Use in the PrepareDocuments action according to Section 2.2.3.5.1 of RFC 135.\n if project_path:\n inc = True\n project_name = project_name or Path(project_path).name\n self.project_path = project_path\n self.project_name = project_name\n self.inc = inc\n self.reqa_file = reqa_file\n self.max_auto_summarize_code = max_auto_summarize_code\n\n @property\n def extra(self):\n return self._extra\n\n @extra.setter\n def extra(self, value: dict):\n self._extra = value\n\n def get_openai_llm(self) -> Optional[LLMConfig]:\n \"\"\"Get OpenAI LLMConfig by name. If no OpenAI, raise Exception\"\"\"\n if self.llm.api_type == LLMType.OPENAI:\n return self.llm\n return None\n\n def get_azure_llm(self) -> Optional[LLMConfig]:\n \"\"\"Get Azure LLMConfig by name. If no Azure, raise Exception\"\"\"\n if self.llm.api_type == LLMType.AZURE:\n return self.llm\n return None\n\n\ndef merge_dict(dicts: Iterable[Dict]) -> Dict:\n \"\"\"Merge multiple dicts into one, with the latter dict overwriting the former\"\"\"\n result = {}\n for dictionary in dicts:\n result.update(dictionary)\n return result\n\n\nconfig = Config.default()\n"}}}}