This commit is contained in:
zhouzinimg 2023-10-18 22:17:16 +08:00
parent 315fb65430
commit 041da760e0
147 changed files with 756 additions and 7644 deletions

View file

@ -8,14 +8,12 @@ if sys.implementation.name == "cpython" and platform.system() == "Windows":
if sys.version_info[:2] == (3, 9):
from asyncio.proactor_events import _ProactorBasePipeTransport
# https://github.com/python/cpython/pull/92842
def pacth_del(self, _warn=warnings.warn):
if self._sock is not None:
_warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
self._sock.close()
_ProactorBasePipeTransport.__del__ = pacth_del
if sys.version_info >= (3, 9, 0):

View file

@ -51,12 +51,12 @@ class Action(ABC):
@retry(stop=stop_after_attempt(3), wait=wait_fixed(1))
async def _aask_v1(
self,
prompt: str,
output_class_name: str,
output_data_mapping: dict,
system_msgs: Optional[list[str]] = None,
format="markdown", # compatible to original format
self,
prompt: str,
output_class_name: str,
output_data_mapping: dict,
system_msgs: Optional[list[str]] = None,
format="markdown", # compatible to original format
) -> ActionOutput:
"""Append default prefix"""
if not system_msgs:

View file

@ -40,3 +40,4 @@ class ActionOutput:
new_class.__validator_check_name = classmethod(check_name)
new_class.__root_validator_check_missing_fields = classmethod(check_missing_fields)
return new_class

View file

@ -10,6 +10,5 @@ from metagpt.actions import Action
class BossRequirement(Action):
"""Boss Requirement without any implementation details"""
async def run(self, *args, **kwargs):
raise NotImplementedError

View file

@ -1,5 +1,5 @@
import traceback
from pathlib import Path
import traceback
from metagpt.actions.write_code import WriteCode
from metagpt.logs import logger

View file

@ -7,8 +7,8 @@
"""
import re
from metagpt.actions.action import Action
from metagpt.logs import logger
from metagpt.actions.action import Action
from metagpt.utils.common import CodeParser
PROMPT_TEMPLATE = """
@ -24,8 +24,6 @@ The message is as follows:
Now you should start rewriting the code:
## file name of the code to rewrite: Write code with triple quoto. Do your best to implement THIS IN ONLY ONE FILE.
"""
class DebugError(Action):
def __init__(self, name="DebugError", context=None, llm=None):
super().__init__(name, context, llm)
@ -35,17 +33,17 @@ class DebugError(Action):
# f"\n\n{error}\n\nPlease try to fix the error in this code."
# fixed_code = await self._aask(prompt)
# return fixed_code
async def run(self, context):
if "PASS" in context:
return "", "the original code works fine, no need to debug"
file_name = re.search("## File To Rewrite:\s*(.+\\.py)", context).group(1)
logger.info(f"Debug and rewrite {file_name}")
prompt = PROMPT_TEMPLATE.format(context=context)
rsp = await self._aask(prompt)
code = CodeParser.parse_code(block="", text=rsp)

View file

@ -19,3 +19,4 @@ class DesignReview(Action):
api_review = await self._aask(prompt)
return api_review

View file

@ -26,3 +26,4 @@ class DesignFilenames(Action):
logger.debug(prompt)
logger.debug(design_filenames)
return design_filenames

View file

@ -6,6 +6,7 @@
@File : detail_mining.py
"""
from metagpt.actions import Action, ActionOutput
from metagpt.logs import logger
PROMPT_TEMPLATE = """
##TOPIC
@ -42,7 +43,6 @@ OUTPUT_MAPPING = {
class DetailMining(Action):
"""This class allows LLM to further mine noteworthy details based on specific "##TOPIC"(discussion topic) and "##RECORD" (discussion records), thereby deepening the discussion.
"""
def __init__(self, name="", context=None, llm=None):
super().__init__(name, context, llm)

View file

@ -27,7 +27,6 @@ Requirement: Provide a list of questions for the interviewer to ask the intervie
Attention: Provide as markdown block as the format above, at least 10 questions.
"""
# prepare for a interview
@ -39,3 +38,4 @@ class PrepareInterview(Action):
prompt = PROMPT_TEMPLATE.format(context=context)
question_list = await self._aask_v1(prompt)
return question_list

View file

@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
import json
from typing import Callable
from pydantic import parse_obj_as
@ -59,6 +60,7 @@ a comprehensive summary of the text.
{content}
'''
CONDUCT_RESEARCH_PROMPT = '''### Reference Information
{content}
@ -76,13 +78,12 @@ above. The report must meet the following requirements:
class CollectLinks(Action):
"""Action class to collect links from a search engine."""
def __init__(
self,
name: str = "",
*args,
rank_func: Callable[[list[str]], None] | None = None,
**kwargs,
self,
name: str = "",
*args,
rank_func: Callable[[list[str]], None] | None = None,
**kwargs,
):
super().__init__(name, *args, **kwargs)
self.desc = "Collect links from a search engine."
@ -90,11 +91,11 @@ class CollectLinks(Action):
self.rank_func = rank_func
async def run(
self,
topic: str,
decomposition_nums: int = 4,
url_per_query: int = 4,
system_text: str | None = None,
self,
topic: str,
decomposition_nums: int = 4,
url_per_query: int = 4,
system_text: str | None = None,
) -> dict[str, list[str]]:
"""Run the action to collect links.
@ -119,16 +120,13 @@ class CollectLinks(Action):
def gen_msg():
while True:
search_results = "\n".join(
f"#### Keyword: {i}\n Search Result: {j}\n" for (i, j) in zip(keywords, results))
prompt = SUMMARIZE_SEARCH_PROMPT.format(decomposition_nums=decomposition_nums,
search_results=search_results)
search_results = "\n".join(f"#### Keyword: {i}\n Search Result: {j}\n" for (i, j) in zip(keywords, results))
prompt = SUMMARIZE_SEARCH_PROMPT.format(decomposition_nums=decomposition_nums, search_results=search_results)
yield prompt
remove = max(results, key=len)
remove.pop()
if len(remove) == 0:
break
prompt = reduce_message_length(gen_msg(), self.llm.model, system_text, CONFIG.max_tokens_rsp)
logger.debug(prompt)
queries = await self._aask(prompt, [system_text])
@ -174,12 +172,11 @@ class CollectLinks(Action):
class WebBrowseAndSummarize(Action):
"""Action class to explore the web and provide summaries of articles and webpages."""
def __init__(
self,
*args,
browse_func: Callable[[list[str]], None] | None = None,
**kwargs,
self,
*args,
browse_func: Callable[[list[str]], None] | None = None,
**kwargs,
):
super().__init__(*args, **kwargs)
if CONFIG.model_for_researcher_summary:
@ -191,11 +188,11 @@ class WebBrowseAndSummarize(Action):
self.desc = "Explore the web and provide summaries of articles and webpages."
async def run(
self,
url: str,
*urls: str,
query: str,
system_text: str = RESEARCH_BASE_SYSTEM,
self,
url: str,
*urls: str,
query: str,
system_text: str = RESEARCH_BASE_SYSTEM,
) -> dict[str, str]:
"""Run the action to browse the web and provide summaries.
@ -217,8 +214,7 @@ class WebBrowseAndSummarize(Action):
for u, content in zip([url, *urls], contents):
content = content.inner_text
chunk_summaries = []
for prompt in generate_prompt_chunk(content, prompt_template, self.llm.model, system_text,
CONFIG.max_tokens_rsp):
for prompt in generate_prompt_chunk(content, prompt_template, self.llm.model, system_text, CONFIG.max_tokens_rsp):
logger.debug(prompt)
summary = await self._aask(prompt, [system_text])
if summary == "Not relevant.":
@ -242,17 +238,16 @@ class WebBrowseAndSummarize(Action):
class ConductResearch(Action):
"""Action class to conduct research and generate a research report."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if CONFIG.model_for_researcher_report:
self.llm.model = CONFIG.model_for_researcher_report
async def run(
self,
topic: str,
content: str,
system_text: str = RESEARCH_BASE_SYSTEM,
self,
topic: str,
content: str,
system_text: str = RESEARCH_BASE_SYSTEM,
) -> str:
"""Run the action to conduct research and generate a research report.

View file

@ -99,7 +99,7 @@ class RunCode(Action):
return stdout.decode("utf-8"), stderr.decode("utf-8")
async def run(
self, code, mode="script", code_file_name="", test_code="", test_file_name="", command=[], **kwargs
self, code, mode="script", code_file_name="", test_code="", test_file_name="", command=[], **kwargs
) -> str:
logger.info(f"Running {' '.join(command)}")
if mode == "script":

View file

@ -54,6 +54,7 @@ SEARCH_AND_SUMMARIZE_PROMPT = """
"""
SEARCH_AND_SUMMARIZE_SALES_SYSTEM = """## Requirements
1. Please summarize the latest dialogue based on the reference information (secondary) and dialogue history (primary). Do not include text that is irrelevant to the conversation.
- The context is for reference only. If it is irrelevant to the user's search request history, please reduce its reference and usage.
@ -139,3 +140,4 @@ class SearchAndSummarize(Action):
logger.debug(prompt)
logger.debug(result)
return result

View file

@ -5,14 +5,13 @@
@Author : alexanderwu
@File : write_code.py
"""
from tenacity import retry, stop_after_attempt, wait_fixed
from metagpt.actions import WriteDesign
from metagpt.actions.action import Action
from metagpt.const import WORKSPACE_ROOT
from metagpt.logs import logger
from metagpt.schema import Message
from metagpt.utils.common import CodeParser
from tenacity import retry, stop_after_attempt, wait_fixed
PROMPT_TEMPLATE = """
NOTICE
@ -80,3 +79,4 @@ class WriteCode(Action):
# code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING)
# self._save(context, filename, code)
return code

View file

@ -6,12 +6,11 @@
@File : write_code_review.py
"""
from tenacity import retry, stop_after_attempt, wait_fixed
from metagpt.actions.action import Action
from metagpt.logs import logger
from metagpt.schema import Message
from metagpt.utils.common import CodeParser
from tenacity import retry, stop_after_attempt, wait_fixed
PROMPT_TEMPLATE = """
NOTICE
@ -80,3 +79,4 @@ class WriteCodeReview(Action):
# code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING)
# self._save(context, filename, code)
return code

View file

@ -162,9 +162,9 @@ class WriteDocstring(Action):
self.desc = "Write docstring for code."
async def run(
self, code: str,
system_text: str = PYTHON_DOCSTRING_SYSTEM,
style: Literal["google", "numpy", "sphinx"] = "google",
self, code: str,
system_text: str = PYTHON_DOCSTRING_SYSTEM,
style: Literal["google", "numpy", "sphinx"] = "google",
) -> str:
"""Writes docstrings for the given code and system text in the specified style.
@ -202,7 +202,6 @@ def _simplify_python_code(code: str) -> None:
if __name__ == "__main__":
import fire
async def run(filename: str, overwrite: bool = False, style: Literal["google", "numpy", "sphinx"] = "google"):
with open(filename) as f:
code = f.read()
@ -212,5 +211,4 @@ if __name__ == "__main__":
f.write(code)
return code
fire.Fire(run)

View file

@ -25,3 +25,4 @@ class WritePRDReview(Action):
prompt = self.prd_review_prompt_template.format(prd=self.prd)
review = await self._aask(prompt)
return review

View file

@ -65,3 +65,4 @@ class WriteContent(Action):
"""
prompt = CONTENT_PROMPT.format(topic=topic, language=self.language, directory=self.directory)
return await self._aask(prompt=prompt)

View file

@ -13,9 +13,9 @@ def get_project_root():
current_path = Path.cwd()
while True:
if (
(current_path / ".git").exists()
or (current_path / ".project_root").exists()
or (current_path / ".gitignore").exists()
(current_path / ".git").exists()
or (current_path / ".project_root").exists()
or (current_path / ".gitignore").exists()
):
return current_path
parent_path = current_path.parent

View file

@ -53,3 +53,4 @@ class LocalStore(BaseStore, ABC):
@abstractmethod
def _write(self, docs, metadatas):
raise NotImplementedError

View file

@ -10,7 +10,6 @@ import chromadb
class ChromaStore:
"""If inherited from BaseStore, or importing other modules from metagpt, a Python exception occurs, which is strange."""
def __init__(self, name):
client = chromadb.Client()
collection = client.create_collection(name)

View file

@ -79,3 +79,4 @@ class Document:
return self._get_docs_and_metadatas_by_langchain()
else:
raise NotImplementedError

View file

@ -38,11 +38,11 @@ class QdrantStore(BaseStore):
raise Exception("please check QdrantConnection.")
def create_collection(
self,
collection_name: str,
vectors_config: VectorParams,
force_recreate=False,
**kwargs,
self,
collection_name: str,
vectors_config: VectorParams,
force_recreate=False,
**kwargs,
):
"""
create a collection
@ -97,12 +97,12 @@ class QdrantStore(BaseStore):
)
def search(
self,
collection_name: str,
query: List[float],
query_filter: Filter = None,
k=10,
return_vector=False,
self,
collection_name: str,
query: List[float],
query_filter: Filter = None,
k=10,
return_vector=False,
):
"""
vector search

View file

@ -25,4 +25,4 @@ def print_classes_and_functions(module):
if __name__ == '__main__':
print_classes_and_functions(metagpt)
print_classes_and_functions(metagpt)

View file

@ -12,7 +12,6 @@ from metagpt.provider.openai_api import OpenAIGPTAPI as LLM
DEFAULT_LLM = LLM()
CLAUDE_LLM = Claude()
async def ai_func(prompt):
"""使用LLM进行QA
QA with LLMs

View file

@ -12,7 +12,6 @@ from loguru import logger as _logger
from metagpt.const import PROJECT_ROOT
def define_log_level(print_level="INFO", logfile_level="DEBUG"):
"""调整日志级别到level之上
Adjust the log level to above level
@ -22,5 +21,4 @@ def define_log_level(print_level="INFO", logfile_level="DEBUG"):
_logger.add(PROJECT_ROOT / 'logs/log.txt', level=logfile_level)
return _logger
logger = define_log_level()

View file

@ -51,7 +51,7 @@ class Manager:
# chosen_role_name = self.llm.ask(self.prompt_template.format(context))
# FIXME: 现在通过简单的字典决定流向,但之后还是应该有思考过程
# The direction of flow is now determined by a simple dictionary, but there should still be a thought process afterwards
#The direction of flow is now determined by a simple dictionary, but there should still be a thought process afterwards
next_role_profile = self.role_directions[message.role]
# logger.debug(f"{next_role_profile}")
for _, role in roles.items():

View file

@ -6,8 +6,9 @@
@File : __init__.py
"""
from metagpt.memory.longterm_memory import LongTermMemory
from metagpt.memory.memory import Memory
from metagpt.memory.longterm_memory import LongTermMemory
__all__ = [
"Memory",

View file

@ -68,3 +68,4 @@ class LongTermMemory(Memory):
def clear(self):
super(LongTermMemory, self).clear()
self.memory_storage.clean()

View file

@ -85,3 +85,4 @@ class Memory:
continue
rsp += self.index[action]
return rsp

View file

@ -2,16 +2,16 @@
# -*- coding: utf-8 -*-
# @Desc : the implement of memory storage
from pathlib import Path
from typing import List
from pathlib import Path
from langchain.vectorstores.faiss import FAISS
from metagpt.const import DATA_PATH, MEM_TTL
from metagpt.document_store.faiss_store import FaissStore
from metagpt.logs import logger
from metagpt.schema import Message
from metagpt.utils.serialize import serialize_message, deserialize_message
from metagpt.document_store.faiss_store import FaissStore
class MemoryStorage(FaissStore):
@ -104,3 +104,4 @@ class MemoryStorage(FaissStore):
self.store = None
self._initialized = False

View file

@ -16,6 +16,7 @@ The requirements of the tree-structure plan are:
4. The sub-goals at the bottom level should be basic actions so that I can easily execute them in the game.
"""
DECOMPOSE_USER = """USER:
The goal is to {goal description}. Generate the plan according to the requirements.
"""

View file

@ -66,10 +66,9 @@ # PRD
return prd
```
The main class/function is WritePRD.
Then you should write:
This class is designed to generate a PRD based on input requirements. Notably, there's a template prompt with sections
for product, function, goals, user scenarios, requirements, constraints, performance metrics. This template gets filled
with input requirements and then queries a big language model to produce the detailed PRD.
This class is designed to generate a PRD based on input requirements. Notably, there's a template prompt with sections for product, function, goals, user scenarios, requirements, constraints, performance metrics. This template gets filled with input requirements and then queries a big language model to produce the detailed PRD.

View file

@ -6,6 +6,7 @@
@File : sales.py
"""
SALES_ASSISTANT = """You are a sales assistant helping your sales agent to determine which stage of a sales conversation should the agent move to, or stay at.
Following '===' is the conversation history.
Use this conversation history to make your decision.
@ -28,6 +29,7 @@ The answer needs to be one number only, no words.
If there is no conversation history, output 1.
Do not answer anything else nor add anything to you answer."""
SALES = """Never forget your name is {salesperson_name}. You work as a {salesperson_role}.
You work at company named {company_name}. {company_name}'s business is the following: {company_business}
Company values are the following. {company_values}
@ -52,11 +54,10 @@ Conversation history:
{salesperson_name}:
"""
conversation_stages = {
'1': "Introduction: Start the conversation by introducing yourself and your company. Be polite and respectful while keeping the tone of the conversation professional. Your greeting should be welcoming. Always clarify in your greeting the reason why you are contacting the prospect.",
'2': "Qualification: Qualify the prospect by confirming if they are the right person to talk to regarding your product/service. Ensure that they have the authority to make purchasing decisions.",
'3': "Value proposition: Briefly explain how your product/service can benefit the prospect. Focus on the unique selling points and value proposition of your product/service that sets it apart from competitors.",
'4': "Needs analysis: Ask open-ended questions to uncover the prospect's needs and pain points. Listen carefully to their responses and take notes.",
'5': "Solution presentation: Based on the prospect's needs, present your product/service as the solution that can address their pain points.",
'6': "Objection handling: Address any objections that the prospect may have regarding your product/service. Be prepared to provide evidence or testimonials to support your claims.",
'7': "Close: Ask for the sale by proposing a next step. This could be a demo, a trial or a meeting with decision-makers. Ensure to summarize what has been discussed and reiterate the benefits."}
conversation_stages = {'1' : "Introduction: Start the conversation by introducing yourself and your company. Be polite and respectful while keeping the tone of the conversation professional. Your greeting should be welcoming. Always clarify in your greeting the reason why you are contacting the prospect.",
'2': "Qualification: Qualify the prospect by confirming if they are the right person to talk to regarding your product/service. Ensure that they have the authority to make purchasing decisions.",
'3': "Value proposition: Briefly explain how your product/service can benefit the prospect. Focus on the unique selling points and value proposition of your product/service that sets it apart from competitors.",
'4': "Needs analysis: Ask open-ended questions to uncover the prospect's needs and pain points. Listen carefully to their responses and take notes.",
'5': "Solution presentation: Based on the prospect's needs, present your product/service as the solution that can address their pain points.",
'6': "Objection handling: Address any objections that the prospect may have regarding your product/service. Be prepared to provide evidence or testimonials to support your claims.",
'7': "Close: Ask for the sale by proposing a next step. This could be a demo, a trial or a meeting with decision-makers. Ensure to summarize what has been discussed and reiterate the benefits."}

View file

@ -20,6 +20,7 @@ summary. Pick a suitable emoji for every bullet point. Your response should be i
a YouTube video, use the following text: {{CONTENT}}.
"""
# GCP-VertexAI-Text Summarization (SUMMARIZE_PROMPT_2-5 are from this source)
# https://github.com/GoogleCloudPlatform/generative-ai/blob/main/language/examples/prompt-design/text_summarization.ipynb
# Long documents require a map-reduce process, see the following notebook
@ -38,6 +39,7 @@ Summary:
"""
SUMMARIZE_PROMPT_3 = """
Provide a TL;DR for the following article:
@ -51,6 +53,7 @@ Instead of computing on the individual qubits themselves, we will then compute o
TL;DR:
"""
SUMMARIZE_PROMPT_4 = """
Provide a very short summary in four bullet points for the following article:
@ -65,6 +68,7 @@ Bulletpoints:
"""
SUMMARIZE_PROMPT_5 = """
Please generate a summary of the following conversation and at the end summarize the to-do's for the support Agent:

View file

@ -36,4 +36,4 @@ Strictly limit output according to the following requirements:
3. The output must be strictly in the specified language, {language}.
4. Do not have redundant output, including concluding remarks.
5. Strict requirement not to output the topic "{topic}".
"""
"""

View file

@ -73,6 +73,7 @@ The action_list can contain arbitrary number of actions. The args of each action
6. I will execute your code step by step and give you feedback. If some action fails, I will stop at that action and will not execute its following actions. The feedback will include error messages about the failed action. At that time, you should replan and write the new code just starting from that failed action.
"""
SOP_USER = """USER:
My current state:
- inventory: {inventory}

View file

@ -8,4 +8,5 @@
from metagpt.provider.openai_api import OpenAIGPTAPI
__all__ = ["OpenAIGPTAPI"]

View file

@ -32,3 +32,4 @@ class Claude2:
max_tokens_to_sample=1000,
)
return res.completion

View file

@ -25,3 +25,4 @@ class BaseChatbot(ABC):
@abstractmethod
def ask_code(self, msgs: list) -> str:
"""Ask GPT multiple questions and get a piece of code"""

View file

@ -115,3 +115,4 @@ class BaseGPTAPI(BaseChatbot):
def messages_to_dict(self, messages):
"""objects to [{"role": "user", "content": msg}] etc."""
return [i.to_dict() for i in messages]

View file

@ -41,7 +41,7 @@ class RateLimiter:
self.rpm = rpm
def split_batches(self, batch):
return [batch[i: i + self.rpm] for i in range(0, len(batch), self.rpm)]
return [batch[i : i + self.rpm] for i in range(0, len(batch), self.rpm)]
async def wait_if_needed(self, num_requests):
current_time = time.time()
@ -83,9 +83,8 @@ class CostManager(metaclass=Singleton):
self.total_prompt_tokens += prompt_tokens
self.total_completion_tokens += completion_tokens
cost = (
prompt_tokens * TOKEN_COSTS[model]["prompt"] + completion_tokens * TOKEN_COSTS[model][
"completion"]
) / 1000
prompt_tokens * TOKEN_COSTS[model]["prompt"] + completion_tokens * TOKEN_COSTS[model]["completion"]
) / 1000
self.total_cost += cost
logger.info(
f"Total running cost: ${self.total_cost:.3f} | Max budget: ${CONFIG.max_budget:.3f} | "

View file

@ -6,15 +6,16 @@
@File : __init__.py
"""
from metagpt.roles.architect import Architect
from metagpt.roles.customer_service import CustomerService
from metagpt.roles.engineer import Engineer
from metagpt.roles.product_manager import ProductManager
from metagpt.roles.project_manager import ProjectManager
from metagpt.roles.qa_engineer import QaEngineer
from metagpt.roles.role import Role
from metagpt.roles.sales import Sales
from metagpt.roles.architect import Architect
from metagpt.roles.project_manager import ProjectManager
from metagpt.roles.product_manager import ProductManager
from metagpt.roles.engineer import Engineer
from metagpt.roles.qa_engineer import QaEngineer
from metagpt.roles.seacher import Searcher
from metagpt.roles.sales import Sales
from metagpt.roles.customer_service import CustomerService
__all__ = [
"Role",

View file

@ -23,11 +23,11 @@ class Architect(Role):
"""
def __init__(
self,
name: str = "Bob",
profile: str = "Architect",
goal: str = "Design a concise, usable, complete python system",
constraints: str = "Try to specify good open source tools as much as possible",
self,
name: str = "Bob",
profile: str = "Architect",
goal: str = "Design a concise, usable, complete python system",
constraints: str = "Try to specify good open source tools as much as possible",
) -> None:
"""Initializes the Architect with given attributes."""
super().__init__(name, profile, goal, constraints)

View file

@ -32,3 +32,4 @@ class CustomerService(Sales):
store=None
):
super().__init__(name, profile, desc=desc, store=store)

View file

@ -61,13 +61,13 @@ class Engineer(Role):
"""
def __init__(
self,
name: str = "Alex",
profile: str = "Engineer",
goal: str = "Write elegant, readable, extensible, efficient code",
constraints: str = "The code should conform to standards like PEP8 and be modular and maintainable",
n_borg: int = 1,
use_code_review: bool = False,
self,
name: str = "Alex",
profile: str = "Engineer",
goal: str = "Write elegant, readable, extensible, efficient code",
constraints: str = "The code should conform to standards like PEP8 and be modular and maintainable",
n_borg: int = 1,
use_code_review: bool = False,
) -> None:
"""Initializes the Engineer role with given attributes."""
super().__init__(name, profile, goal, constraints)

View file

@ -21,11 +21,11 @@ class ProductManager(Role):
"""
def __init__(
self,
name: str = "Alice",
profile: str = "Product Manager",
goal: str = "Efficiently create a successful product",
constraints: str = "",
self,
name: str = "Alice",
profile: str = "Product Manager",
goal: str = "Efficiently create a successful product",
constraints: str = "",
) -> None:
"""
Initializes the ProductManager role with given attributes.

View file

@ -22,11 +22,11 @@ class ProjectManager(Role):
"""
def __init__(
self,
name: str = "Eve",
profile: str = "Project Manager",
goal: str = "Improve team efficiency and deliver with quality and quantity",
constraints: str = "",
self,
name: str = "Eve",
profile: str = "Project Manager",
goal: str = "Improve team efficiency and deliver with quality and quantity",
constraints: str = "",
) -> None:
"""
Initializes the ProjectManager role with given attributes.

View file

@ -23,7 +23,6 @@ SUFFIX = """Let's begin!
Question: {input}
Thoughts: {agent_scratchpad}"""
class PromptString(Enum):
REFLECTION_QUESTIONS = "Here are some statements:\n{memory_descriptions}\n\nBased solely on the information above, what are the 3 most prominent high-level questions we can answer about the topic in the statements?\n\n{format_instructions}"

View file

@ -26,12 +26,12 @@ from metagpt.utils.special_tokens import FILENAME_CODE_SEP, MSG_SEP
class QaEngineer(Role):
def __init__(
self,
name="Edward",
profile="QaEngineer",
goal="Write comprehensive and robust tests to ensure codes will work as expected without bugs",
constraints="The test code you write should conform to code standard like PEP8, be modular, easy to read and maintain",
test_round_allowed=5,
self,
name="Edward",
profile="QaEngineer",
goal="Write comprehensive and robust tests to ensure codes will work as expected without bugs",
constraints="The test code you write should conform to code standard like PEP8, be modular, easy to read and maintain",
test_round_allowed=5,
):
super().__init__(name, profile, goal, constraints)
self._init_actions(

View file

@ -21,13 +21,13 @@ class Report(BaseModel):
class Researcher(Role):
def __init__(
self,
name: str = "David",
profile: str = "Researcher",
goal: str = "Gather information and conduct research",
constraints: str = "Ensure accuracy and relevance of information",
language: str = "en-us",
**kwargs,
self,
name: str = "David",
profile: str = "Researcher",
goal: str = "Gather information and conduct research",
constraints: str = "Ensure accuracy and relevance of information",
language: str = "en-us",
**kwargs,
):
super().__init__(name, profile, goal, constraints, **kwargs)
self._init_actions([CollectLinks(name), WebBrowseAndSummarize(name), ConductResearch(name)])
@ -93,10 +93,8 @@ class Researcher(Role):
if __name__ == "__main__":
import fire
async def main(topic: str, language="en-us"):
role = Researcher(topic, language=language)
await role.run(topic)
fire.Fire(main)

View file

@ -32,3 +32,4 @@ class Sales(Role):
else:
action = SearchAndSummarize()
self._init_actions([action])

View file

@ -23,13 +23,13 @@ class Searcher(Role):
constraints (str): Constraints or limitations for the searcher.
engine (SearchEngineType): The type of search engine to use.
"""
def __init__(self,
name: str = 'Alice',
profile: str = 'Smart Assistant',
def __init__(self,
name: str = 'Alice',
profile: str = 'Smart Assistant',
goal: str = 'Provide search services for users',
constraints: str = 'Answer is rich and complete',
engine=SearchEngineType.SERPAPI_GOOGLE,
constraints: str = 'Answer is rich and complete',
engine=SearchEngineType.SERPAPI_GOOGLE,
**kwargs) -> None:
"""
Initializes the Searcher role with given attributes.
@ -53,7 +53,7 @@ class Searcher(Role):
"""Performs the search action in a single process."""
logger.info(f"{self._setting}: ready to {self._rc.todo}")
response = await self._rc.todo.run(self._rc.memory.get(k=0))
if isinstance(response, ActionOutput):
msg = Message(content=response.content, instruct_content=response.instruct_content,
role=self.profile, cause_by=type(self._rc.todo))

View file

@ -29,12 +29,12 @@ class SkAgent(Role):
"""
def __init__(
self,
name: str = "Sunshine",
profile: str = "sk_agent",
goal: str = "Execute task based on passed in task description",
constraints: str = "",
planner_cls=BasicPlanner,
self,
name: str = "Sunshine",
profile: str = "sk_agent",
goal: str = "Execute task based on passed in task description",
constraints: str = "",
planner_cls=BasicPlanner,
) -> None:
"""Initializes the Engineer role with given attributes."""
super().__init__(name, profile, goal, constraints)

View file

@ -29,12 +29,12 @@ class TutorialAssistant(Role):
"""
def __init__(
self,
name: str = "Stitch",
profile: str = "Tutorial Assistant",
goal: str = "Generate tutorial documents",
constraints: str = "Strictly follow Markdown's syntax, with neat and standardized layout",
language: str = "Chinese",
self,
name: str = "Stitch",
profile: str = "Tutorial Assistant",
goal: str = "Generate tutorial documents",
constraints: str = "Strictly follow Markdown's syntax, with neat and standardized layout",
language: str = "Chinese",
):
super().__init__(name, profile, goal, constraints)
self._init_actions([WriteDirectory(language=language)])

View file

@ -50,7 +50,6 @@ class UserMessage(Message):
"""便于支持OpenAI的消息
Facilitate support for OpenAI messages
"""
def __init__(self, content: str):
super().__init__(content, 'user')
@ -60,7 +59,6 @@ class SystemMessage(Message):
"""便于支持OpenAI的消息
Facilitate support for OpenAI messages
"""
def __init__(self, content: str):
super().__init__(content, 'system')
@ -70,7 +68,6 @@ class AIMessage(Message):
"""便于支持OpenAI的消息
Facilitate support for OpenAI messages
"""
def __init__(self, content: str):
super().__init__(content, 'assistant')

View file

@ -8,9 +8,7 @@
"top_p": 1.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0,
"stop_sequences": [
"##END##"
]
"stop_sequences": ["##END##"]
},
"input": {
"parameters": [

View file

@ -59,3 +59,4 @@ class SoftwareCompany(BaseModel):
self._check_balance()
await self.environment.run()
return self.environment.history

View file

@ -6,6 +6,7 @@
@File : __init__.py
"""
from enum import Enum

View file

@ -1,16 +1,16 @@
import inspect
import re
import textwrap
from pathlib import Path
from typing import List, Callable
from pathlib import Path
import wrapt
import textwrap
import inspect
from interpreter.interpreter import Interpreter
from metagpt.actions.clone_function import CloneFunction, run_function_code, run_function_script
from metagpt.config import CONFIG
from metagpt.logs import logger
from metagpt.config import CONFIG
from metagpt.utils.highlight import highlight
from metagpt.actions.clone_function import CloneFunction, run_function_code, run_function_script
def extract_python_code(code: str):
@ -36,7 +36,6 @@ def extract_python_code(code: str):
class OpenCodeInterpreter(object):
"""https://github.com/KillianLucas/open-interpreter"""
def __init__(self, auto_run: bool = True) -> None:
interpreter = Interpreter()
interpreter.auto_run = auto_run
@ -127,5 +126,4 @@ class OpenInterpreterDecorator(object):
except Exception as e:
raise Exception("Could not evaluate Python code", e)
return res
return wrapper(wrapped)

View file

@ -10,7 +10,6 @@ from typing import Union
class GPTPromptGenerator:
"""Using LLM, given an output, request LLM to provide input (supporting instruction, chatbot, and query styles)"""
def __init__(self):
self._generators = {i: getattr(self, f"gen_{i}_style") for i in ['instruction', 'chatbot', 'query']}

View file

@ -10,8 +10,8 @@ import os
from os.path import join
from typing import List
from PIL import Image, PngImagePlugin
from aiohttp import ClientSession
from PIL import Image, PngImagePlugin
from metagpt.config import Config
from metagpt.const import WORKSPACE_ROOT
@ -64,12 +64,12 @@ class SDEngine:
logger.info(self.sd_t2i_url)
def construct_payload(
self,
prompt,
negtive_prompt=default_negative_prompt,
width=512,
height=512,
sd_model="galaxytimemachinesGTM_photoV20",
self,
prompt,
negtive_prompt=default_negative_prompt,
width=512,
height=512,
sd_model="galaxytimemachinesGTM_photoV20",
):
# Configure the payload with provided inputs
self.payload["prompt"] = prompt
@ -120,13 +120,11 @@ def decode_base64_to_image(img, save_name):
image.save(f"{save_name}.png", pnginfo=pnginfo)
return pnginfo, image
def batch_decode_base64_to_image(imgs, save_dir="", save_name=""):
for idx, _img in enumerate(imgs):
save_name = join(save_dir, save_name)
decode_base64_to_image(_img, save_name=save_name)
if __name__ == "__main__":
engine = SDEngine()
prompt = "pixel style, game design, a game interface should be minimalistic and intuitive with the score and high score displayed at the top. The snake and its food should be easily distinguishable. The game should have a simple color scheme, with a contrasting color for the snake and its food. Complete interface boundary"

View file

@ -20,7 +20,7 @@ class SkSearchEngine:
@sk_function(
description="searches results from Google. Useful when you need to find short "
"and succinct answers about a specific topic. Input should be a search query.",
"and succinct answers about a specific topic. Input should be a search query.",
name="searchAsync",
input_description="search",
)
@ -42,7 +42,7 @@ class SearchEngine:
"""
def __init__(
self,
self,
engine: Optional[SearchEngineType] = None,
run_func: Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]] = None,
):
@ -68,19 +68,19 @@ class SearchEngine:
@overload
def run(
self,
query: str,
max_results: int = 8,
as_string: Literal[True] = True,
self,
query: str,
max_results: int = 8,
as_string: Literal[True] = True,
) -> str:
...
@overload
def run(
self,
query: str,
max_results: int = 8,
as_string: Literal[False] = False,
self,
query: str,
max_results: int = 8,
as_string: Literal[False] = False,
) -> list[dict[str, str]]:
...

View file

@ -25,10 +25,10 @@ class DDGAPIWrapper:
"""
def __init__(
self,
*,
loop: asyncio.AbstractEventLoop | None = None,
executor: futures.Executor | None = None,
self,
*,
loop: asyncio.AbstractEventLoop | None = None,
executor: futures.Executor | None = None,
):
kwargs = {}
if CONFIG.global_proxy:
@ -39,29 +39,29 @@ class DDGAPIWrapper:
@overload
def run(
self,
query: str,
max_results: int = 8,
as_string: Literal[True] = True,
focus: list[str] | None = None,
self,
query: str,
max_results: int = 8,
as_string: Literal[True] = True,
focus: list[str] | None = None,
) -> str:
...
@overload
def run(
self,
query: str,
max_results: int = 8,
as_string: Literal[False] = False,
focus: list[str] | None = None,
self,
query: str,
max_results: int = 8,
as_string: Literal[False] = False,
focus: list[str] | None = None,
) -> list[dict[str, str]]:
...
async def run(
self,
query: str,
max_results: int = 8,
as_string: bool = True,
self,
query: str,
max_results: int = 8,
as_string: bool = True,
) -> str | list[dict]:
"""Return the results of a Google search using the official Google API

View file

@ -76,11 +76,11 @@ class GoogleAPIWrapper(BaseModel):
return service.cse()
async def run(
self,
query: str,
max_results: int = 8,
as_string: bool = True,
focus: list[str] | None = None,
self,
query: str,
max_results: int = 8,
as_string: bool = True,
focus: list[str] | None = None,
) -> str | list[dict]:
"""Return the results of a Google search using the official Google API.

View file

@ -24,4 +24,4 @@ class Translator:
@classmethod
def translate_prompt(cls, original, lang='中文'):
return prompt.format(LANG=lang, ORIGINAL=original)
return prompt.format(LANG=lang, ORIGINAL=original)

View file

@ -12,9 +12,9 @@ from metagpt.utils.parse_html import WebPage
class WebBrowserEngine:
def __init__(
self,
engine: WebBrowserEngineType | None = None,
run_func: Callable[..., Coroutine[Any, Any, WebPage | list[WebPage]]] | None = None,
self,
engine: WebBrowserEngineType | None = None,
run_func: Callable[..., Coroutine[Any, Any, WebPage | list[WebPage]]] | None = None,
):
engine = engine or CONFIG.web_browser_engine
@ -46,9 +46,7 @@ class WebBrowserEngine:
if __name__ == "__main__":
import fire
async def main(url: str, *urls: str, engine_type: Literal["playwright", "selenium"] = "playwright", **kwargs):
return await WebBrowserEngine(WebBrowserEngineType(engine_type), **kwargs).run(url, *urls)
fire.Fire(main)

View file

@ -23,10 +23,10 @@ class PlaywrightWrapper:
"""
def __init__(
self,
browser_type: Literal["chromium", "firefox", "webkit"] | None = None,
launch_kwargs: dict | None = None,
**kwargs,
self,
browser_type: Literal["chromium", "firefox", "webkit"] | None = None,
launch_kwargs: dict | None = None,
**kwargs,
) -> None:
if browser_type is None:
browser_type = CONFIG.playwright_browser_type
@ -139,12 +139,11 @@ async def _log_stream(sr, log_func):
_install_lock: asyncio.Lock = None
_install_cache = set()
if __name__ == "__main__":
import fire
async def main(url: str, *urls: str, browser_type: str = "chromium", **kwargs):
return await PlaywrightWrapper(browser_type, **kwargs).run(url, *urls)
fire.Fire(main)

View file

@ -28,12 +28,12 @@ class SeleniumWrapper:
"""
def __init__(
self,
browser_type: Literal["chrome", "firefox", "edge", "ie"] | None = None,
launch_kwargs: dict | None = None,
*,
loop: asyncio.AbstractEventLoop | None = None,
executor: futures.Executor | None = None,
self,
browser_type: Literal["chrome", "firefox", "edge", "ie"] | None = None,
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
@ -117,9 +117,7 @@ def _gen_get_driver_func(browser_type, *args, executable_path=None):
if __name__ == "__main__":
import fire
async def main(url: str, *urls: str, browser_type: str = "chrome", **kwargs):
return await SeleniumWrapper(browser_type, **kwargs).run(url, *urls)
fire.Fire(main)

View file

@ -14,6 +14,7 @@ from metagpt.utils.token_counter import (
count_string_tokens,
)
__all__ = [
"read_docx",
"Singleton",

View file

@ -36,11 +36,11 @@ def py_make_scanner(context):
return parse_object((string, idx + 1), strict, _scan_once, object_hook, object_pairs_hook, memo)
elif nextchar == "[":
return parse_array((string, idx + 1), _scan_once)
elif nextchar == "n" and string[idx: idx + 4] == "null":
elif nextchar == "n" and string[idx : idx + 4] == "null":
return None, idx + 4
elif nextchar == "t" and string[idx: idx + 4] == "true":
elif nextchar == "t" and string[idx : idx + 4] == "true":
return True, idx + 4
elif nextchar == "f" and string[idx: idx + 5] == "false":
elif nextchar == "f" and string[idx : idx + 5] == "false":
return False, idx + 5
m = match_number(string, idx)
@ -51,11 +51,11 @@ def py_make_scanner(context):
else:
res = parse_int(integer)
return res, m.end()
elif nextchar == "N" and string[idx: idx + 3] == "NaN":
elif nextchar == "N" and string[idx : idx + 3] == "NaN":
return parse_constant("NaN"), idx + 3
elif nextchar == "I" and string[idx: idx + 8] == "Infinity":
elif nextchar == "I" and string[idx : idx + 8] == "Infinity":
return parse_constant("Infinity"), idx + 8
elif nextchar == "-" and string[idx: idx + 9] == "-Infinity":
elif nextchar == "-" and string[idx : idx + 9] == "-Infinity":
return parse_constant("-Infinity"), idx + 9
else:
raise StopIteration(idx)
@ -89,7 +89,7 @@ WHITESPACE_STR = " \t\n\r"
def JSONObject(
s_and_end, strict, scan_once, object_hook, object_pairs_hook, memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR
s_and_end, strict, scan_once, object_hook, object_pairs_hook, memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR
):
"""Parse a JSON object from a string and return the parsed object.
@ -118,12 +118,12 @@ def JSONObject(
memo_get = memo.setdefault
# Use a slice to prevent IndexError from being raised, the following
# check will raise a more specific ValueError if the string is empty
nextchar = s[end: end + 1]
nextchar = s[end : end + 1]
# Normally we expect nextchar == '"'
if nextchar != '"' and nextchar != "'":
if nextchar in _ws:
end = _w(s, end).end()
nextchar = s[end: end + 1]
nextchar = s[end : end + 1]
# Trivial empty object
if nextchar == "}":
if object_pairs_hook is not None:
@ -146,9 +146,9 @@ def JSONObject(
key = memo_get(key, key)
# To skip some function call overhead we optimize the fast paths where
# the JSON key separator is ": " or just ":".
if s[end: end + 1] != ":":
if s[end : end + 1] != ":":
end = _w(s, end).end()
if s[end: end + 1] != ":":
if s[end : end + 1] != ":":
raise JSONDecodeError("Expecting ':' delimiter", s, end)
end += 1
@ -179,7 +179,7 @@ def JSONObject(
elif nextchar != ",":
raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
end = _w(s, end).end()
nextchar = s[end: end + 1]
nextchar = s[end : end + 1]
end += 1
if nextchar != '"':
raise JSONDecodeError("Expecting property name enclosed in double quotes", s, end - 1)
@ -257,7 +257,7 @@ def py_scanstring(s, end, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match, delim
else:
uni = _decode_uXXXX(s, end)
end += 5
if 0xD800 <= uni <= 0xDBFF and s[end: end + 2] == "\\u":
if 0xD800 <= uni <= 0xDBFF and s[end : end + 2] == "\\u":
uni2 = _decode_uXXXX(s, end + 1)
if 0xDC00 <= uni2 <= 0xDFFF:
uni = 0x10000 + (((uni - 0xD800) << 10) | (uni2 - 0xDC00))
@ -272,14 +272,14 @@ scanstring = py_scanstring
class CustomDecoder(json.JSONDecoder):
def __init__(
self,
*,
object_hook=None,
parse_float=None,
parse_int=None,
parse_constant=None,
strict=True,
object_pairs_hook=None
self,
*,
object_hook=None,
parse_float=None,
parse_int=None,
parse_constant=None,
strict=True,
object_pairs_hook=None
):
super().__init__(
object_hook=object_hook,

View file

@ -6,9 +6,8 @@
@File : file.py
@Describe : General file operations.
"""
from pathlib import Path
import aiofiles
from pathlib import Path
from metagpt.logs import logger
@ -73,3 +72,4 @@ class File:
except Exception as e:
logger.error(f"Error reading file: {e}")
raise e

View file

@ -1,7 +1,7 @@
# 添加代码语法高亮显示
from pygments import highlight as highlight_
from pygments.formatters import TerminalFormatter, HtmlFormatter
from pygments.lexers import PythonLexer, SqlLexer
from pygments.formatters import TerminalFormatter, HtmlFormatter
def highlight(code: str, language: str = 'python', formatter: str = 'terminal'):

View file

@ -135,6 +135,7 @@ MMC2 = """sequenceDiagram
S-->>SE: return summary
SE-->>M: return summary"""
if __name__ == "__main__":
loop = asyncio.new_event_loop()
result = loop.run_until_complete(mermaid_to_file(MMC1, PROJECT_ROOT / f"{CONFIG.mermaid_engine}/1"))

View file

@ -6,9 +6,9 @@
@File : mermaid.py
"""
import base64
import os
from aiohttp import ClientSession, ClientError
from aiohttp import ClientSession,ClientError
from metagpt.logs import logger

View file

@ -8,13 +8,10 @@
import os
from urllib.parse import urljoin
from playwright.async_api import async_playwright
from metagpt.logs import logger
async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int:
async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048)-> int:
"""
Converts the given Mermaid code to various output formats and saves them to files.
@ -27,21 +24,20 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
Returns:
int: Returns 1 if the conversion and saving were successful, -1 otherwise.
"""
suffixes = ['png', 'svg', 'pdf']
suffixes=['png', 'svg', 'pdf']
__dirname = os.path.dirname(os.path.abspath(__file__))
async with async_playwright() as p:
browser = await p.chromium.launch()
device_scale_factor = 1.0
context = await browser.new_context(
viewport={'width': width, 'height': height},
device_scale_factor=device_scale_factor,
)
viewport={'width': width, 'height': height},
device_scale_factor=device_scale_factor,
)
page = await context.new_page()
async def console_message(msg):
logger.info(msg.text)
page.on('console', console_message)
try:
@ -76,7 +72,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
}''', [mermaid_code, mermaid_config, my_css, background_color])
if 'svg' in suffixes:
if 'svg' in suffixes :
svg_xml = await page.evaluate('''() => {
const svg = document.querySelector('svg');
const xmlSerializer = new XMLSerializer();
@ -86,7 +82,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
with open(f'{output_file_without_suffix}.svg', 'wb') as f:
f.write(svg_xml.encode('utf-8'))
if 'png' in suffixes:
if 'png' in suffixes:
clip = await page.evaluate('''() => {
const svg = document.querySelector('svg');
const rect = svg.getBoundingClientRect();

View file

@ -7,14 +7,11 @@
"""
import os
from urllib.parse import urljoin
from pyppeteer import launch
from metagpt.config import CONFIG
from metagpt.logs import logger
from metagpt.config import CONFIG
async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int:
async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048)-> int:
"""
Converts the given Mermaid code to various output formats and saves them to files.
@ -27,14 +24,15 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
Returns:
int: Returns 1 if the conversion and saving were successful, -1 otherwise.
"""
suffixes = ['png', 'svg', 'pdf']
suffixes = ['png', 'svg', 'pdf']
__dirname = os.path.dirname(os.path.abspath(__file__))
if CONFIG.pyppeteer_executable_path:
browser = await launch(headless=True,
executablePath=CONFIG.pyppeteer_executable_path,
args=['--disable-extensions', "--no-sandbox"]
)
executablePath=CONFIG.pyppeteer_executable_path,
args=['--disable-extensions',"--no-sandbox"]
)
else:
logger.error("Please set the environment variable:PYPPETEER_EXECUTABLE_PATH.")
return -1
@ -43,7 +41,6 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
async def console_message(msg):
logger.info(msg.text)
page.on('console', console_message)
try:
@ -76,7 +73,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
}
}''', [mermaid_code, mermaid_config, my_css, background_color])
if 'svg' in suffixes:
if 'svg' in suffixes :
svg_xml = await page.evaluate('''() => {
const svg = document.querySelector('svg');
const xmlSerializer = new XMLSerializer();
@ -86,7 +83,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
with open(f'{output_file_without_suffix}.svg', 'wb') as f:
f.write(svg_xml.encode('utf-8'))
if 'png' in suffixes:
if 'png' in suffixes:
clip = await page.evaluate('''() => {
const svg = document.querySelector('svg');
const rect = svg.getBoundingClientRect();
@ -97,8 +94,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
height: Math.ceil(rect.height)
};
}''')
await page.setViewport({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height'],
'deviceScaleFactor': device_scale_factor})
await page.setViewport({'width': clip['x'] + clip['width'], 'height': clip['y'] + clip['height'], 'deviceScaleFactor': device_scale_factor})
screenshot = await page.screenshot(clip=clip, omit_background=True, scale='device')
logger.info(f"Generating {output_file_without_suffix}.png..")
with open(f'{output_file_without_suffix}.png', 'wb') as f:
@ -114,3 +110,4 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048,
return -1
finally:
await browser.close()

View file

@ -16,7 +16,7 @@ class WebPage(BaseModel):
class Config:
underscore_attrs_are_private = True
_soup: Optional[BeautifulSoup] = None
_soup : Optional[BeautifulSoup] = None
_title: Optional[str] = None
@property
@ -24,7 +24,7 @@ class WebPage(BaseModel):
if self._soup is None:
self._soup = BeautifulSoup(self.html, "html.parser")
return self._soup
@property
def title(self):
if self._title is None:

View file

@ -37,12 +37,12 @@ def get_docstring_statement(body: DocstringNode) -> cst.SimpleStatementLine:
if not isinstance(expr, cst.Expr):
return None
val = expr.value
if not isinstance(val, (cst.SimpleString, cst.ConcatenatedString)):
return None
evaluated_value = val.evaluated_value
evaluated_value = val.evaluated_value
if isinstance(evaluated_value, bytes):
return None
@ -56,7 +56,6 @@ class DocstringCollector(cst.CSTVisitor):
stack: A list to keep track of the current path in the CST.
docstrings: A dictionary mapping paths in the CST to their corresponding docstrings.
"""
def __init__(self):
self.stack: list[str] = []
self.docstrings: dict[tuple[str, ...], cst.SimpleStatementLine] = {}
@ -97,10 +96,9 @@ class DocstringTransformer(cst.CSTTransformer):
stack: A list to keep track of the current path in the CST.
docstrings: A dictionary mapping paths in the CST to their corresponding docstrings.
"""
def __init__(
self,
docstrings: dict[tuple[str, ...], cst.SimpleStatementLine],
self,
docstrings: dict[tuple[str, ...], cst.SimpleStatementLine],
):
self.stack: list[str] = []
self.docstrings = docstrings
@ -127,8 +125,7 @@ class DocstringTransformer(cst.CSTTransformer):
key = tuple(self.stack)
self.stack.pop()
if hasattr(updated_node, "decorators") and any(
(i.decorator.value == "overload") for i in updated_node.decorators):
if hasattr(updated_node, "decorators") and any((i.decorator.value == "overload") for i in updated_node.decorators):
return updated_node
statement = self.docstrings.get(key)

View file

@ -8,7 +8,6 @@
import docx
def read_docx(file_path: str) -> list:
"""Open a docx file"""
doc = docx.Document(file_path)

View file

@ -20,3 +20,4 @@ class Singleton(abc.ABCMeta, type):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]

View file

@ -1,4 +1,4 @@
# token to separate different code messages in a WriteCode Message content
MSG_SEP = "#*000*#"
MSG_SEP = "#*000*#"
# token to seperate file name and the actual code text in a code message
FILENAME_CODE_SEP = "#*001*#"

View file

@ -3,8 +3,7 @@ from typing import Generator, Sequence
from metagpt.utils.token_counter import TOKEN_MAX, count_string_tokens
def reduce_message_length(msgs: Generator[str, None, None], model_name: str, system_text: str,
reserved: int = 0, ) -> str:
def reduce_message_length(msgs: Generator[str, None, None], model_name: str, system_text: str, reserved: int = 0,) -> str:
"""Reduce the length of concatenated message segments to fit within the maximum token size.
Args:
@ -28,11 +27,11 @@ def reduce_message_length(msgs: Generator[str, None, None], model_name: str, sys
def generate_prompt_chunk(
text: str,
prompt_template: str,
model_name: str,
system_text: str,
reserved: int = 0,
text: str,
prompt_template: str,
model_name: str,
system_text: str,
reserved: int = 0,
) -> Generator[str, None, None]:
"""Split the text into chunks of a maximum token size.
@ -50,9 +49,9 @@ def generate_prompt_chunk(
current_token = 0
current_lines = []
reserved = reserved + count_string_tokens(prompt_template + system_text, model_name)
reserved = reserved + count_string_tokens(prompt_template+system_text, model_name)
# 100 is a magic number to ensure the maximum context length is not exceeded
max_token = TOKEN_MAX.get(model_name, 2048) - reserved - 100
max_token = TOKEN_MAX.get(model_name, 2048) - reserved - 100
while paragraphs:
paragraph = paragraphs.pop(0)
@ -104,7 +103,7 @@ def decode_unicode_escape(text: str) -> str:
return text.encode("utf-8").decode("unicode_escape", "ignore")
def _split_by_count(lst: Sequence, count: int):
def _split_by_count(lst: Sequence , count: int):
avg = len(lst) // count
remainder = len(lst) % count
start = 0

View file

@ -24,6 +24,7 @@ TOKEN_COSTS = {
"text-embedding-ada-002": {"prompt": 0.0004, "completion": 0.0},
}
TOKEN_MAX = {
"gpt-3.5-turbo": 4096,
"gpt-3.5-turbo-0301": 4096,