feat: merge geekan:main

This commit is contained in:
莘权 马 2024-03-05 16:03:57 +08:00
commit 8e85dd3bc6
84 changed files with 654 additions and 367 deletions

View file

@ -26,7 +26,9 @@ # MetaGPT: The Multi-Agent Framework
</p>
## News
🚀 Feb. 08, 2024: [v0.7.0](https://github.com/geekan/MetaGPT/releases/tag/v0.7.0) released, supporting assigning different LLMs to different Roles. We also introduced [Interpreter](https://github.com/geekan/MetaGPT/blob/main/examples/mi/README.md), a powerful agent capable of solving a wide range of real-world problems.
🚀 March. 01, 2024: Our Data Interpreter paper is on arxiv. Find all design and benchmark details [here](https://arxiv.org/abs/2402.18679)!
🚀 Feb. 08, 2024: [v0.7.0](https://github.com/geekan/MetaGPT/releases/tag/v0.7.0) released, supporting assigning different LLMs to different Roles. We also introduced [Data Interpreter](https://github.com/geekan/MetaGPT/blob/main/examples/di/README.md), a powerful agent capable of solving a wide range of real-world problems.
🚀 Jan. 16, 2024: Our paper [MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework
](https://arxiv.org/abs/2308.00352) accepted for oral presentation **(top 1.2%)** at ICLR 2024, **ranking #1** in the LLM-based Agent category.

18
examples/di/README.md Normal file
View file

@ -0,0 +1,18 @@
# Data Interpreter (DI)
## What is Data Interpreter
Data Interpreter is an agent who solves problems through codes. It understands user requirements, makes plans, writes codes for execution, and uses tools if necessary. These capabilities enable it to tackle a wide range of scenarios, please check out the examples below.
## Example List
- Data visualization
- Machine learning modeling
- Image background removal
- Solve math problems
- Receipt OCR
- Tool usage: web page imitation
- Tool usage: web crawling
- Tool usage: text2image
- Tool usage: email summarization and response
- More on the way!
Please see [here](https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/interpreter/intro.html) for detailed explanation.

View file

@ -5,15 +5,15 @@
@File : crawl_webpage.py
"""
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main():
prompt = """Get data from `paperlist` table in https://papercopilot.com/statistics/iclr-statistics/iclr-2024-statistics/,
and save it to a csv file. paper title must include `multiagent` or `large language model`. *notice: print key variables*"""
mi = Interpreter(use_tools=True)
di = DataInterpreter(use_tools=True)
await mi.run(prompt)
await di.run(prompt)
if __name__ == "__main__":

View file

@ -1,11 +1,11 @@
import asyncio
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main(requirement: str = ""):
mi = Interpreter(use_tools=False)
await mi.run(requirement)
di = DataInterpreter(use_tools=False)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -6,7 +6,7 @@
"""
import os
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main():
@ -22,9 +22,9 @@ async def main():
Firstly, Please help me fetch the latest 5 senders and full letter contents.
Then, summarize each of the 5 emails into one sentence (you can do this by yourself, no need to import other models to do this) and output them in a markdown format."""
mi = Interpreter(use_tools=True)
di = DataInterpreter(use_tools=True)
await mi.run(prompt)
await di.run(prompt)
if __name__ == "__main__":

View file

@ -5,7 +5,7 @@
@Author : mannaandpoem
@File : imitate_webpage.py
"""
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main():
@ -15,9 +15,9 @@ Firstly, utilize Selenium and WebDriver for rendering.
Secondly, convert image to a webpage including HTML, CSS and JS in one go.
Finally, save webpage in a text file.
Note: All required dependencies and environments have been fully installed and configured."""
mi = Interpreter(use_tools=True)
di = DataInterpreter(use_tools=True)
await mi.run(prompt)
await di.run(prompt)
if __name__ == "__main__":

View file

@ -1,12 +1,12 @@
import fire
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main(auto_run: bool = True):
requirement = "Run data analysis on sklearn Wine recognition dataset, include a plot, and train a model to predict wine class (20% as validation), and show validation accuracy."
mi = Interpreter(auto_run=auto_run)
await mi.run(requirement)
di = DataInterpreter(auto_run=auto_run)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -1,6 +1,6 @@
import asyncio
from metagpt.roles.mi.ml_engineer import MLEngineer
from metagpt.roles.di.ml_engineer import MLEngineer
async def main(requirement: str):

View file

@ -1,4 +1,4 @@
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main():
@ -10,9 +10,9 @@ async def main():
coordinates and confidence levels, then recognize the total amount from ocr text content, and finally save as table.
Image path: {image_path}.
NOTE: The environments for Paddle and PaddleOCR are all ready and has been fully installed."""
mi = Interpreter()
di = DataInterpreter()
await mi.run(requirement)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -1,11 +1,11 @@
import asyncio
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main(requirement: str = ""):
mi = Interpreter(use_tools=False)
await mi.run(requirement)
di = DataInterpreter(use_tools=False)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -4,12 +4,12 @@
# @Desc :
import asyncio
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main(requirement: str = ""):
mi = Interpreter(use_tools=True, goal=requirement)
await mi.run(requirement)
di = DataInterpreter(use_tools=True, goal=requirement)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -1,11 +1,11 @@
import asyncio
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main(requirement: str = ""):
mi = Interpreter(use_tools=False)
await mi.run(requirement)
di = DataInterpreter(use_tools=False)
await di.run(requirement)
if __name__ == "__main__":

View file

@ -1,18 +0,0 @@
# MetaGPT Interpreter (MI)
## What is Interpreter
Interpreter is an agent who solves problems through codes. It understands user requirements, makes plans, writes codes for execution, and uses tools if necessary. These capabilities enable it to tackle a wide range of scenarios, please check out the examples below.
## Example List
- Data visualization
- Machine learning modeling
- Image background removal
- Solve math problems
- Receipt OCR
- Tool usage: web page imitation
- Tool usage: web crawling
- Tool usage: text2image
- Tool usage: email summarization and response
- More on the way!
Please see [here](https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/interpreter/mi_intro.html) for detailed explanation.

View file

@ -22,9 +22,9 @@ from metagpt.actions.write_code_review import WriteCodeReview
from metagpt.actions.write_prd import WritePRD
from metagpt.actions.write_prd_review import WritePRDReview
from metagpt.actions.write_test import WriteTest
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.mi.write_analysis_code import WriteCodeWithoutTools, WriteCodeWithTools
from metagpt.actions.mi.write_plan import WritePlan
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.write_analysis_code import WriteCodeWithoutTools, WriteCodeWithTools
from metagpt.actions.di.write_plan import WritePlan
class ActionType(Enum):

View file

@ -8,7 +8,6 @@
from typing import List
from metagpt.actions.action_node import ActionNode
from metagpt.logs import logger
from metagpt.utils.mermaid import MMC1, MMC2
IMPLEMENTATION_APPROACH = ActionNode(
@ -109,14 +108,3 @@ REFINED_NODES = [
DESIGN_API_NODE = ActionNode.from_children("DesignAPI", NODES)
REFINED_DESIGN_NODE = ActionNode.from_children("RefinedDesignAPI", REFINED_NODES)
def main():
prompt = DESIGN_API_NODE.compile(context="")
logger.info(prompt)
prompt = REFINED_DESIGN_NODE.compile(context="")
logger.info(prompt)
if __name__ == "__main__":
main()

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from metagpt.actions.mi.write_analysis_code import BaseWriteAnalysisCode
from metagpt.actions.di.write_analysis_code import BaseWriteAnalysisCode
from metagpt.logs import logger
from metagpt.schema import Message
from metagpt.utils.common import create_func_call_config

View file

@ -3,14 +3,14 @@ from __future__ import annotations
from typing import Tuple
from metagpt.actions import Action
from metagpt.actions.mi.write_analysis_code import WriteCodeWithTools
from metagpt.prompts.mi.ml_action import (
from metagpt.actions.di.write_analysis_code import WriteCodeWithTools
from metagpt.prompts.di.ml_action import (
ML_GENERATE_CODE_PROMPT,
ML_TOOL_USAGE_PROMPT,
PRINT_DATA_COLUMNS,
UPDATE_DATA_COLUMNS,
)
from metagpt.prompts.mi.write_analysis_code import CODE_GENERATOR_WITH_TOOLS
from metagpt.prompts.di.write_analysis_code import CODE_GENERATOR_WITH_TOOLS
from metagpt.schema import Message, Plan
from metagpt.utils.common import create_func_call_config, remove_comments

View file

@ -10,7 +10,7 @@ from typing import Tuple
from metagpt.actions import Action
from metagpt.logs import logger
from metagpt.prompts.mi.write_analysis_code import (
from metagpt.prompts.di.write_analysis_code import (
CODE_GENERATOR_WITH_TOOLS,
SELECT_FUNCTION_TOOLS,
TOOL_RECOMMENDATION_PROMPT,

View file

@ -12,7 +12,7 @@ from typing import Tuple
from metagpt.actions import Action
from metagpt.logs import logger
from metagpt.prompts.mi.write_analysis_code import (
from metagpt.prompts.di.write_analysis_code import (
ASSIGN_TASK_TYPE_CONFIG,
ASSIGN_TASK_TYPE_PROMPT,
)

View file

@ -8,7 +8,6 @@
from typing import List
from metagpt.actions.action_node import ActionNode
from metagpt.logs import logger
REQUIRED_PYTHON_PACKAGES = ActionNode(
key="Required Python packages",
@ -119,14 +118,3 @@ REFINED_NODES = [
PM_NODE = ActionNode.from_children("PM_NODE", NODES)
REFINED_PM_NODE = ActionNode.from_children("REFINED_PM_NODE", REFINED_NODES)
def main():
prompt = PM_NODE.compile(context="")
logger.info(prompt)
prompt = REFINED_PM_NODE.compile(context="")
logger.info(prompt)
if __name__ == "__main__":
main()

View file

@ -6,30 +6,44 @@
@File : write_code_plan_and_change_an.py
"""
import os
from typing import List
from pydantic import Field
from metagpt.actions.action import Action
from metagpt.actions.action_node import ActionNode
from metagpt.logs import logger
from metagpt.schema import CodePlanAndChangeContext
CODE_PLAN_AND_CHANGE = ActionNode(
key="Code Plan And Change",
expected_type=str,
instruction="Developing comprehensive and step-by-step incremental development plan, and write Incremental "
"Change by making a code draft that how to implement incremental development including detailed steps based on the "
"context. Note: Track incremental changes using mark of '+' or '-' for add/modify/delete code, and conforms to the "
"output format of git diff",
example="""
1. Plan for calculator.py: Enhance the functionality of `calculator.py` by extending it to incorporate methods for subtraction, multiplication, and division. Additionally, implement robust error handling for the division operation to mitigate potential issues related to division by zero.
```python
DEVELOPMENT_PLAN = ActionNode(
key="Development Plan",
expected_type=List[str],
instruction="Develop a comprehensive and step-by-step incremental development plan, providing the detail "
"changes to be implemented at each step based on the order of 'Task List'",
example=[
"Enhance the functionality of `calculator.py` by extending it to incorporate methods for subtraction, ...",
"Update the existing codebase in main.py to incorporate new API endpoints for subtraction, ...",
],
)
INCREMENTAL_CHANGE = ActionNode(
key="Incremental Change",
expected_type=List[str],
instruction="Write Incremental Change by making a code draft that how to implement incremental development "
"including detailed steps based on the context. Note: Track incremental changes using the marks `+` and `-` to "
"indicate additions and deletions, and ensure compliance with the output format of `git diff`",
example=[
'''```diff
--- Old/calculator.py
+++ New/calculator.py
class Calculator:
self.result = number1 + number2
return self.result
- def sub(self, number1, number2) -> float:
+ def subtract(self, number1: float, number2: float) -> float:
+ '''
+ """
+ Subtracts the second number from the first and returns the result.
+
+ Args:
@ -38,13 +52,13 @@ class Calculator:
+
+ Returns:
+ float: The difference of number1 and number2.
+ '''
+ """
+ self.result = number1 - number2
+ return self.result
+
def multiply(self, number1: float, number2: float) -> float:
- pass
+ '''
+ """
+ Multiplies two numbers and returns the result.
+
+ Args:
@ -53,15 +67,15 @@ class Calculator:
+
+ Returns:
+ float: The product of number1 and number2.
+ '''
+ """
+ self.result = number1 * number2
+ return self.result
+
def divide(self, number1: float, number2: float) -> float:
- pass
+ '''
+ """
+ ValueError: If the second number is zero.
+ '''
+ """
+ if number2 == 0:
+ raise ValueError('Cannot divide by zero')
+ self.result = number1 / number2
@ -75,10 +89,11 @@ class Calculator:
+ print("Result is already zero, no need to clear.")
+
self.result = 0.0
```
```''',
"""```diff
--- Old/main.py
+++ New/main.py
2. Plan for main.py: Integrate new API endpoints for subtraction, multiplication, and division into the existing codebase of `main.py`. Then, ensure seamless integration with the overall application architecture and maintain consistency with coding standards.
```python
def add_numbers():
result = calculator.add_numbers(num1, num2)
return jsonify({'result': result}), 200
@ -106,6 +121,7 @@ def add_numbers():
if __name__ == '__main__':
app.run()
```""",
],
)
CODE_PLAN_AND_CHANGE_CONTEXT = """
@ -172,14 +188,16 @@ Role: You are a professional engineer; The main goal is to complete incremental
2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.
3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.
4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.
5. Follow Code Plan And Change: If there is any Incremental Change that is marked by the git diff format using '+' and '-' for add/modify/delete code, or Legacy Code files contain "{filename} to be rewritten", you must merge it into the code file according to the plan.
5. Follow Code Plan And Change: If there is any "Incremental Change" that is marked by the git diff format with '+' and '-' symbols, or Legacy Code files contain "{filename} to be rewritten", you must merge it into the code file according to the "Development Plan".
6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
7. Before using a external variable/module, make sure you import it first.
8. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.
9. Attention: Retain details that are not related to incremental development but are important for maintaining the consistency and clarity of the old code.
"""
WRITE_CODE_PLAN_AND_CHANGE_NODE = ActionNode.from_children("WriteCodePlanAndChange", [CODE_PLAN_AND_CHANGE])
CODE_PLAN_AND_CHANGE = [DEVELOPMENT_PLAN, INCREMENTAL_CHANGE]
WRITE_CODE_PLAN_AND_CHANGE_NODE = ActionNode.from_children("WriteCodePlanAndChange", CODE_PLAN_AND_CHANGE)
class WriteCodePlanAndChange(Action):
@ -192,14 +210,14 @@ class WriteCodePlanAndChange(Action):
prd_doc = await self.repo.docs.prd.get(filename=self.i_context.prd_filename)
design_doc = await self.repo.docs.system_design.get(filename=self.i_context.design_filename)
task_doc = await self.repo.docs.task.get(filename=self.i_context.task_filename)
code_text = await self.get_old_codes()
context = CODE_PLAN_AND_CHANGE_CONTEXT.format(
requirement=self.i_context.requirement,
prd=prd_doc.content,
design=design_doc.content,
task=task_doc.content,
code=code_text,
code=await self.get_old_codes(),
)
logger.info("Writing code plan and change..")
return await WRITE_CODE_PLAN_AND_CHANGE_NODE.fill(context=context, llm=self.llm, schema="json")
async def get_old_codes(self) -> str:

View file

@ -56,7 +56,7 @@ REFINED_PRODUCT_GOALS = ActionNode(
key="Refined Product Goals",
expected_type=List[str],
instruction="Update and expand the original product goals to reflect the evolving needs due to incremental "
"development.Ensure that the refined goals align with the current project direction and contribute to its success.",
"development. Ensure that the refined goals align with the current project direction and contribute to its success.",
example=[
"Enhance user engagement through new features",
"Optimize performance for scalability",

View file

@ -16,6 +16,7 @@ from metagpt.utils.yaml_model import YamlModel
class LLMType(Enum):
OPENAI = "openai"
ANTHROPIC = "anthropic"
CLAUDE = "claude" # alias name of anthropic
SPARK = "spark"
ZHIPUAI = "zhipuai"
FIREWORKS = "fireworks"

View file

@ -11,12 +11,12 @@ from pathlib import Path
from typing import Optional, Union
import pandas as pd
from langchain.document_loaders import (
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import (
TextLoader,
UnstructuredPDFLoader,
UnstructuredWordDocumentLoader,
)
from langchain.text_splitter import CharacterTextSplitter
from pydantic import BaseModel, ConfigDict, Field
from tqdm import tqdm

View file

@ -16,6 +16,7 @@ from metagpt.provider.human_provider import HumanProvider
from metagpt.provider.spark_api import SparkLLM
from metagpt.provider.qianfan_api import QianFanLLM
from metagpt.provider.dashscope_api import DashScopeLLM
from metagpt.provider.anthropic_api import AnthropicLLM
__all__ = [
"GeminiLLM",
@ -28,4 +29,5 @@ __all__ = [
"SparkLLM",
"QianFanLLM",
"DashScopeLLM",
"AnthropicLLM",
]

View file

@ -1,37 +1,71 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/7/21 11:15
@Author : Leo Xiao
@File : anthropic_api.py
"""
import anthropic
from anthropic import Anthropic, AsyncAnthropic
from anthropic import AsyncAnthropic
from anthropic.types import Message, Usage
from metagpt.configs.llm_config import LLMConfig
from metagpt.configs.llm_config import LLMConfig, LLMType
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.llm_provider_registry import register_provider
class Claude2:
@register_provider([LLMType.ANTHROPIC, LLMType.CLAUDE])
class AnthropicLLM(BaseLLM):
def __init__(self, config: LLMConfig):
self.config = config
self.__init_anthropic()
def ask(self, prompt: str) -> str:
client = Anthropic(api_key=self.config.api_key)
def __init_anthropic(self):
self.model = self.config.model
self.aclient: AsyncAnthropic = AsyncAnthropic(api_key=self.config.api_key, base_url=self.config.base_url)
res = client.completions.create(
model="claude-2",
prompt=f"{anthropic.HUMAN_PROMPT} {prompt} {anthropic.AI_PROMPT}",
max_tokens_to_sample=1000,
)
return res.completion
def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict:
kwargs = {
"model": self.model,
"messages": messages,
"max_tokens": self.config.max_token,
"stream": stream,
}
if self.use_system_prompt:
# if the model support system prompt, extract and pass it
if messages[0]["role"] == "system":
kwargs["messages"] = messages[1:]
kwargs["system"] = messages[0]["content"] # set system prompt here
return kwargs
async def aask(self, prompt: str) -> str:
aclient = AsyncAnthropic(api_key=self.config.api_key)
def _update_costs(self, usage: Usage, model: str = None, local_calc_usage: bool = True):
usage = {"prompt_tokens": usage.input_tokens, "completion_tokens": usage.output_tokens}
super()._update_costs(usage, model)
res = await aclient.completions.create(
model="claude-2",
prompt=f"{anthropic.HUMAN_PROMPT} {prompt} {anthropic.AI_PROMPT}",
max_tokens_to_sample=1000,
)
return res.completion
def get_choice_text(self, resp: Message) -> str:
return resp.content[0].text
async def _achat_completion(self, messages: list[dict], timeout: int = 3) -> Message:
resp: Message = await self.aclient.messages.create(**self._const_kwargs(messages))
self._update_costs(resp.usage, self.model)
return resp
async def acompletion(self, messages: list[dict], timeout: int = 3) -> Message:
return await self._achat_completion(messages, timeout=timeout)
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
stream = await self.aclient.messages.create(**self._const_kwargs(messages, stream=True))
collected_content = []
usage = Usage(input_tokens=0, output_tokens=0)
async for event in stream:
event_type = event.type
if event_type == "message_start":
usage.input_tokens = event.message.usage.input_tokens
usage.output_tokens = event.message.usage.output_tokens
elif event_type == "content_block_delta":
content = event.delta.text
log_llm_stream(content)
collected_content.append(content)
elif event_type == "message_delta":
usage.output_tokens = event.usage.output_tokens # update final output_tokens
log_llm_stream("\n")
self._update_costs(usage)
full_content = "".join(collected_content)
return full_content

View file

@ -15,10 +15,18 @@ from typing import Dict, Optional, Union
from openai import AsyncOpenAI
from openai.types import CompletionUsage
from pydantic import BaseModel
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from metagpt.configs.llm_config import LLMConfig
from metagpt.logs import logger
from metagpt.schema import Message
from metagpt.utils.common import log_and_reraise
from metagpt.utils.cost_manager import CostManager, Costs
from metagpt.utils.exceptions import handle_exception
@ -134,6 +142,10 @@ class BaseLLM(ABC):
"""FIXME: No code segment filtering has been done here, and all results are actually displayed"""
raise NotImplementedError
@abstractmethod
async def _achat_completion(self, messages: list[dict], timeout=3):
"""_achat_completion implemented by inherited class"""
@abstractmethod
async def acompletion(self, messages: list[dict], timeout=3):
"""Asynchronous version of completion
@ -146,8 +158,22 @@ class BaseLLM(ABC):
"""
@abstractmethod
async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) -> str:
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
"""_achat_completion_stream implemented by inherited class"""
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream: bool = False, timeout: int = 3) -> str:
"""Asynchronous version of completion. Return str. Support stream-print"""
if stream:
return await self._achat_completion_stream(messages, timeout=timeout)
resp = await self._achat_completion(messages, timeout=timeout)
return self.get_choice_text(resp)
def get_choice_text(self, rsp: dict) -> str:
"""Required to provide the first text of choice"""

View file

@ -24,18 +24,10 @@ from dashscope.common.error import (
ModelRequired,
UnsupportedApiProtocol,
)
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM, LLMConfig
from metagpt.provider.llm_provider_registry import LLMType, register_provider
from metagpt.provider.openai_api import log_and_reraise
from metagpt.utils.cost_manager import CostManager
from metagpt.utils.token_counter import DASHSCOPE_TOKEN_COSTS
@ -210,16 +202,16 @@ class DashScopeLLM(BaseLLM):
self._update_costs(dict(resp.usage))
return resp.output
async def _achat_completion(self, messages: list[dict]) -> GenerationOutput:
async def _achat_completion(self, messages: list[dict], timeout: int = 3) -> GenerationOutput:
resp: GenerationResponse = await self.aclient.acall(**self._const_kwargs(messages, stream=False))
self._check_response(resp)
self._update_costs(dict(resp.usage))
return resp.output
async def acompletion(self, messages: list[dict], timeout=3) -> GenerationOutput:
return await self._achat_completion(messages)
return await self._achat_completion(messages, timeout=timeout)
async def _achat_completion_stream(self, messages: list[dict]) -> str:
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
resp = await self.aclient.acall(**self._const_kwargs(messages, stream=True))
collected_content = []
usage = {}
@ -233,16 +225,3 @@ class DashScopeLLM(BaseLLM):
self._update_costs(usage)
full_content = "".join(collected_content)
return full_content
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str:
if stream:
return await self._achat_completion_stream(messages)
resp = await self._achat_completion(messages)
return self.get_choice_text(resp)

View file

@ -13,19 +13,11 @@ from google.generativeai.types.generation_types import (
GenerateContentResponse,
GenerationConfig,
)
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from metagpt.configs.llm_config import LLMConfig, LLMType
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import log_and_reraise
class GeminiGenerativeModel(GenerativeModel):
@ -96,16 +88,16 @@ class GeminiLLM(BaseLLM):
self._update_costs(usage)
return resp
async def _achat_completion(self, messages: list[dict]) -> "AsyncGenerateContentResponse":
async def _achat_completion(self, messages: list[dict], timeout: int = 3) -> "AsyncGenerateContentResponse":
resp: AsyncGenerateContentResponse = await self.llm.generate_content_async(**self._const_kwargs(messages))
usage = await self.aget_usage(messages, resp.text)
self._update_costs(usage)
return resp
async def acompletion(self, messages: list[dict], timeout=3) -> dict:
return await self._achat_completion(messages)
return await self._achat_completion(messages, timeout=timeout)
async def _achat_completion_stream(self, messages: list[dict]) -> str:
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
resp: AsyncGenerateContentResponse = await self.llm.generate_content_async(
**self._const_kwargs(messages, stream=True)
)
@ -120,17 +112,3 @@ class GeminiLLM(BaseLLM):
usage = await self.aget_usage(messages, full_content)
self._update_costs(usage)
return full_content
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str:
"""response in async with stream or non-stream mode"""
if stream:
return await self._achat_completion_stream(messages)
resp = await self._achat_completion(messages)
return self.get_choice_text(resp)

View file

@ -35,10 +35,16 @@ class HumanProvider(BaseLLM):
) -> str:
return self.ask(msg, timeout=timeout)
async def _achat_completion(self, messages: list[dict], timeout=3):
pass
async def acompletion(self, messages: list[dict], timeout=3):
"""dummy implementation of abstract method in base"""
return []
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
pass
async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) -> str:
"""dummy implementation of abstract method in base"""
return ""

View file

@ -4,22 +4,12 @@
import json
from requests import ConnectionError
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from metagpt.configs.llm_config import LLMConfig, LLMType
from metagpt.const import LLM_API_TIMEOUT
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.general_api_requestor import GeneralAPIRequestor
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import log_and_reraise
from metagpt.utils.cost_manager import TokenCostManager
@ -60,7 +50,7 @@ class OllamaLLM(BaseLLM):
chunk = chunk.decode(encoding)
return json.loads(chunk)
async def _achat_completion(self, messages: list[dict]) -> dict:
async def _achat_completion(self, messages: list[dict], timeout: int = 3) -> dict:
resp, _, _ = await self.client.arequest(
method=self.http_method,
url=self.suffix_url,
@ -73,9 +63,9 @@ class OllamaLLM(BaseLLM):
return resp
async def acompletion(self, messages: list[dict], timeout=3) -> dict:
return await self._achat_completion(messages)
return await self._achat_completion(messages, timeout=timeout)
async def _achat_completion_stream(self, messages: list[dict]) -> str:
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
stream_resp, _, _ = await self.client.arequest(
method=self.http_method,
url=self.suffix_url,
@ -101,17 +91,3 @@ class OllamaLLM(BaseLLM):
self._update_costs(usage)
full_content = "".join(collected_content)
return full_content
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str:
"""response in async with stream or non-stream mode"""
if stream:
return await self._achat_completion_stream(messages)
resp = await self._achat_completion(messages)
return self.get_choice_text(resp)

View file

@ -30,7 +30,7 @@ from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.constant import GENERAL_FUNCTION_SCHEMA
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.schema import Message
from metagpt.utils.common import CodeParser, decode_image
from metagpt.utils.common import CodeParser, decode_image, log_and_reraise
from metagpt.utils.cost_manager import CostManager
from metagpt.utils.exceptions import handle_exception
from metagpt.utils.token_counter import (
@ -40,17 +40,6 @@ from metagpt.utils.token_counter import (
)
def log_and_reraise(retry_state):
logger.error(f"Retry attempts exhausted. Last exception: {retry_state.outcome.exception()}")
logger.warning(
"""
Recommend going to https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4#part-XdatdVlhEojeAfxaaEZcMV3ZniQ
See FAQ 5.8
"""
)
raise retry_state.outcome.exception()
@register_provider([LLMType.OPENAI, LLMType.FIREWORKS, LLMType.OPEN_LLM, LLMType.MOONSHOT, LLMType.MISTRAL])
class OpenAILLM(BaseLLM):
"""Check https://platform.openai.com/examples for examples"""

View file

@ -7,19 +7,11 @@ import os
import qianfan
from qianfan import ChatCompletion
from qianfan.resources.typing import JsonBody
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from metagpt.configs.llm_config import LLMConfig, LLMType
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import log_and_reraise
from metagpt.utils.cost_manager import CostManager
from metagpt.utils.token_counter import (
QIANFAN_ENDPOINT_TOKEN_COSTS,
@ -115,15 +107,15 @@ class QianFanLLM(BaseLLM):
self._update_costs(resp.body.get("usage", {}))
return resp.body
async def _achat_completion(self, messages: list[dict]) -> JsonBody:
async def _achat_completion(self, messages: list[dict], timeout: int = 3) -> JsonBody:
resp = await self.aclient.ado(**self._const_kwargs(messages=messages, stream=False))
self._update_costs(resp.body.get("usage", {}))
return resp.body
async def acompletion(self, messages: list[dict], timeout=3) -> JsonBody:
return await self._achat_completion(messages)
async def acompletion(self, messages: list[dict], timeout: int = 3) -> JsonBody:
return await self._achat_completion(messages, timeout=timeout)
async def _achat_completion_stream(self, messages: list[dict]) -> str:
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
resp = await self.aclient.ado(**self._const_kwargs(messages=messages, stream=True))
collected_content = []
usage = {}
@ -137,16 +129,3 @@ class QianFanLLM(BaseLLM):
self._update_costs(usage)
full_content = "".join(collected_content)
return full_content
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str:
if stream:
return await self._achat_completion_stream(messages)
resp = await self._achat_completion(messages)
return self.get_choice_text(resp)

View file

@ -31,12 +31,18 @@ class SparkLLM(BaseLLM):
def get_choice_text(self, rsp: dict) -> str:
return rsp["payload"]["choices"]["text"][-1]["content"]
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
pass
async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str:
# 不支持
# logger.warning("当前方法无法支持异步运行。当你使用acompletion时并不能并行访问。")
w = GetMessageFromWeb(messages, self.config)
return w.run()
async def _achat_completion(self, messages: list[dict], timeout=3):
pass
async def acompletion(self, messages: list[dict], timeout=3):
# 不支持异步
w = GetMessageFromWeb(messages, self.config)

View file

@ -5,21 +5,12 @@
from enum import Enum
from typing import Optional
from requests import ConnectionError
from tenacity import (
after_log,
retry,
retry_if_exception_type,
stop_after_attempt,
wait_random_exponential,
)
from zhipuai.types.chat.chat_completion import Completion
from metagpt.configs.llm_config import LLMConfig, LLMType
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import log_llm_stream
from metagpt.provider.base_llm import BaseLLM
from metagpt.provider.llm_provider_registry import register_provider
from metagpt.provider.openai_api import log_and_reraise
from metagpt.provider.zhipuai.zhipu_model_api import ZhiPuModelAPI
from metagpt.utils.cost_manager import CostManager
@ -87,17 +78,3 @@ class ZhiPuAILLM(BaseLLM):
self._update_costs(usage)
full_content = "".join(collected_content)
return full_content
@retry(
stop=stop_after_attempt(3),
wait=wait_random_exponential(min=1, max=60),
after=after_log(logger, logger.level("WARNING").name),
retry=retry_if_exception_type(ConnectionError),
retry_error_callback=log_and_reraise,
)
async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) -> str:
"""response in async with stream or non-stream mode"""
if stream:
return await self._achat_completion_stream(messages)
resp = await self._achat_completion(messages)
return self.get_choice_text(resp)

View file

@ -2,9 +2,9 @@ from __future__ import annotations
from pydantic import Field
from metagpt.actions.mi.ask_review import ReviewConst
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.mi.write_analysis_code import (
from metagpt.actions.di.ask_review import ReviewConst
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.write_analysis_code import (
WriteCodeWithoutTools,
WriteCodeWithTools,
)
@ -13,9 +13,9 @@ from metagpt.roles import Role
from metagpt.schema import Message, Task, TaskResult
class Interpreter(Role):
name: str = "Ivy"
profile: str = "Interpreter"
class DataInterpreter(Role):
name: str = "David"
profile: str = "DataInterpreter"
auto_run: bool = True
use_tools: bool = False
execute_code: ExecuteNbCode = Field(default_factory=ExecuteNbCode, exclude=True)

View file

@ -1,13 +1,13 @@
from metagpt.actions.mi.debug_code import DebugCode
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.mi.ml_action import UpdateDataColumns, WriteCodeWithToolsML
from metagpt.actions.di.debug_code import DebugCode
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.ml_action import UpdateDataColumns, WriteCodeWithToolsML
from metagpt.logs import logger
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
from metagpt.tools.tool_type import ToolType
from metagpt.utils.common import any_to_str
class MLEngineer(Interpreter):
class MLEngineer(DataInterpreter):
name: str = "Mark"
profile: str = "MLEngineer"
debug_context: list = []

View file

@ -204,7 +204,6 @@ class Engineer(Role):
async def _act_code_plan_and_change(self):
"""Write code plan and change that guides subsequent WriteCode and WriteCodeReview"""
logger.info("Writing code plan and change..")
node = await self.rc.todo.run()
code_plan_and_change = node.instruct_content.model_dump_json()
dependencies = {

View file

@ -4,8 +4,8 @@ import json
from pydantic import BaseModel, Field
from metagpt.actions.mi.ask_review import AskReview, ReviewConst
from metagpt.actions.mi.write_plan import (
from metagpt.actions.di.ask_review import AskReview, ReviewConst
from metagpt.actions.di.write_plan import (
WritePlan,
precheck_update_plan_from_rsp,
update_plan_from_rsp,

View file

@ -49,8 +49,8 @@ class TOTSolver(BaseSolver):
raise NotImplementedError
class InterpreterSolver(BaseSolver):
"""InterpreterSolver: Write&Run code in the graph"""
class DataInterpreterSolver(BaseSolver):
"""DataInterpreterSolver: Write&Run code in the graph"""
async def solve(self):
raise NotImplementedError

View file

@ -810,3 +810,14 @@ def decode_image(img_url_or_b64: str) -> Image:
img_data = BytesIO(base64.b64decode(b64_data))
img = Image.open(img_data)
return img
def log_and_reraise(retry_state: RetryCallState):
logger.error(f"Retry attempts exhausted. Last exception: {retry_state.outcome.exception()}")
logger.warning(
"""
Recommend going to https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4#part-XdatdVlhEojeAfxaaEZcMV3ZniQ
See FAQ 5.8
"""
)
raise retry_state.outcome.exception()

View file

@ -136,6 +136,7 @@ class ProjectRepo(FileRepository):
code_files = self.with_src_path(path=git_workdir / git_workdir.name).srcs.all_files
if not code_files:
return False
return bool(code_files)
def with_src_path(self, path: str | Path) -> ProjectRepo:
try:

View file

@ -43,6 +43,11 @@ TOKEN_COSTS = {
"mistral-small-latest": {"prompt": 0.002, "completion": 0.006},
"mistral-medium-latest": {"prompt": 0.0027, "completion": 0.0081},
"mistral-large-latest": {"prompt": 0.008, "completion": 0.024},
"claude-instant-1.2": {"prompt": 0.0008, "completion": 0.0024},
"claude-2.0": {"prompt": 0.008, "completion": 0.024},
"claude-2.1": {"prompt": 0.008, "completion": 0.024},
"claude-3-sonnet-20240229": {"prompt": 0.003, "completion": 0.015},
"claude-3-opus-20240229": {"prompt": 0.015, "completion": 0.075},
}
@ -166,6 +171,11 @@ TOKEN_MAX = {
"mistral-small-latest": 32768,
"mistral-medium-latest": 32768,
"mistral-large-latest": 32768,
"claude-instant-1.2": 100000,
"claude-2.0": 100000,
"claude-2.1": 200000,
"claude-3-sonnet-20240229": 200000,
"claude-3-opus-20240229": 200000,
}

View file

@ -12,6 +12,7 @@ typer==0.9.0
# google_api_python_client==2.93.0 # Used by search_engine.py
lancedb==0.4.0
langchain==0.1.8
sqlalchemy==2.0.0 # along with langchain
loguru==0.6.0
meilisearch==0.21.0
numpy>=1.24.3,<1.25.0
@ -33,7 +34,7 @@ tqdm==4.65.0
#unstructured[local-inference]
# selenium>4
# webdriver_manager<3.9
anthropic==0.8.1
anthropic==0.18.1
typing-inspect==0.8.0
libcst==1.0.1
qdrant-client==1.7.0

View file

@ -57,7 +57,7 @@ extras_require["dev"] = (["pylint~=3.0.3", "black~=23.3.0", "isort~=5.12.0", "pr
setup(
name="metagpt",
version="0.7.2",
version="0.7.4",
description="The Multi-Agent Framework",
long_description=long_description,
long_description_content_type="text/markdown",

View file

@ -12,6 +12,7 @@ import logging
import os
import re
import uuid
from pathlib import Path
from typing import Callable
import aiohttp.web
@ -270,3 +271,11 @@ def mermaid_mocker(aiohttp_mocker, mermaid_rsp_cache):
aiohttp_mocker.rsp_cache = mermaid_rsp_cache
aiohttp_mocker.check_funcs = check_funcs
yield check_funcs
@pytest.fixture
def git_dir():
"""Fixture to get the unittest directory."""
git_dir = Path(__file__).parent / f"unittest/{uuid.uuid4().hex}"
git_dir.mkdir(parents=True, exist_ok=True)
return git_dir

View file

@ -149,7 +149,7 @@ sequenceDiagram
The requirement analysis suggests the need for a clean and intuitive interface. Since we are using a command-line interface, we need to ensure that the text-based UI is as user-friendly as possible. Further clarification on whether a graphical user interface (GUI) is expected in the future would be helpful for planning the extendability of the game."""
TASKS_SAMPLE = """
TASK_SAMPLE = """
## Required Python packages
- random==2.2.1
@ -345,7 +345,7 @@ REFINED_DESIGN_JSON = {
"Anything UNCLEAR": "",
}
REFINED_TASKS_JSON = {
REFINED_TASK_JSON = {
"Required Python packages": ["random==2.2.1", "Tkinter==8.6"],
"Required Other language third-party packages": ["No third-party dependencies required"],
"Refined Logic Analysis": [
@ -373,7 +373,14 @@ REFINED_TASKS_JSON = {
}
CODE_PLAN_AND_CHANGE_SAMPLE = {
"Code Plan And Change": '\n1. Plan for gui.py: Develop the GUI using Tkinter to replace the command-line interface. Start by setting up the main window and event handling. Then, add widgets for displaying the game status, results, and feedback. Implement interactive elements for difficulty selection and visualize the guess history. Finally, create animations for guess feedback and ensure responsiveness across different screen sizes.\n```python\nclass GUI:\n- pass\n+ def __init__(self):\n+ self.setup_window()\n+\n+ def setup_window(self):\n+ # Initialize the main window using Tkinter\n+ pass\n+\n+ def bind_events(self):\n+ # Bind button clicks and other events\n+ pass\n+\n+ def update_feedback(self, message: str):\n+ # Update the feedback label with the given message\n+ pass\n+\n+ def update_attempts(self, attempts: int):\n+ # Update the attempts label with the number of attempts\n+ pass\n+\n+ def update_history(self, history: list):\n+ # Update the history view with the list of past guesses\n+ pass\n+\n+ def show_difficulty_selector(self):\n+ # Show buttons or a dropdown for difficulty selection\n+ pass\n+\n+ def animate_guess_result(self, correct: bool):\n+ # Trigger an animation for correct or incorrect guesses\n+ pass\n```\n\n2. Plan for main.py: Modify the main.py to initialize the GUI and start the event-driven game loop. Ensure that the GUI is the primary interface for user interaction.\n```python\nclass Main:\n def main(self):\n- user_interface = UI()\n- user_interface.start()\n+ graphical_user_interface = GUI()\n+ graphical_user_interface.setup_window()\n+ graphical_user_interface.bind_events()\n+ # Start the Tkinter main loop\n+ pass\n\n if __name__ == "__main__":\n main_instance = Main()\n main_instance.main()\n```\n\n3. Plan for ui.py: Refactor ui.py to work with the new GUI class. Remove command-line interactions and delegate display and input tasks to the GUI.\n```python\nclass UI:\n- def display_message(self, message: str):\n- print(message)\n+\n+ def display_message(self, message: str):\n+ # This method will now pass the message to the GUI to display\n+ pass\n\n- def get_user_input(self, prompt: str) -> str:\n- return input(prompt)\n+\n+ def get_user_input(self, prompt: str) -> str:\n+ # This method will now trigger the GUI to get user input\n+ pass\n\n- def show_attempts(self, attempts: int):\n- print(f"Number of attempts: {attempts}")\n+\n+ def show_attempts(self, attempts: int):\n+ # This method will now update the GUI with the number of attempts\n+ pass\n\n- def show_history(self, history: list):\n- print("Guess history:")\n- for guess in history:\n- print(guess)\n+\n+ def show_history(self, history: list):\n+ # This method will now update the GUI with the guess history\n+ pass\n```\n\n4. Plan for game.py: Ensure game.py remains mostly unchanged as it contains the core game logic. However, make minor adjustments if necessary to integrate with the new GUI.\n```python\nclass Game:\n # No changes required for now\n```\n'
"Development Plan": [
"Develop the GUI using Tkinter to replace the command-line interface. Start by setting up the main window and event handling. Then, add widgets for displaying the game status, results, and feedback. Implement interactive elements for difficulty selection and visualize the guess history. Finally, create animations for guess feedback and ensure responsiveness across different screen sizes.",
"Modify the main.py to initialize the GUI and start the event-driven game loop. Ensure that the GUI is the primary interface for user interaction.",
],
"Incremental Change": [
"""```diff\nclass GUI:\n- pass\n+ def __init__(self):\n+ self.setup_window()\n+\n+ def setup_window(self):\n+ # Initialize the main window using Tkinter\n+ pass\n+\n+ def bind_events(self):\n+ # Bind button clicks and other events\n+ pass\n+\n+ def update_feedback(self, message: str):\n+ # Update the feedback label with the given message\n+ pass\n+\n+ def update_attempts(self, attempts: int):\n+ # Update the attempts label with the number of attempts\n+ pass\n+\n+ def update_history(self, history: list):\n+ # Update the history view with the list of past guesses\n+ pass\n+\n+ def show_difficulty_selector(self):\n+ # Show buttons or a dropdown for difficulty selection\n+ pass\n+\n+ def animate_guess_result(self, correct: bool):\n+ # Trigger an animation for correct or incorrect guesses\n+ pass\n```""",
"""```diff\nclass Main:\n def main(self):\n- user_interface = UI()\n- user_interface.start()\n+ graphical_user_interface = GUI()\n+ graphical_user_interface.setup_window()\n+ graphical_user_interface.bind_events()\n+ # Start the Tkinter main loop\n+ pass\n\n if __name__ == "__main__":\n main_instance = Main()\n main_instance.main()\n```\n\n3. Plan for ui.py: Refactor ui.py to work with the new GUI class. Remove command-line interactions and delegate display and input tasks to the GUI.\n```python\nclass UI:\n- def display_message(self, message: str):\n- print(message)\n+\n+ def display_message(self, message: str):\n+ # This method will now pass the message to the GUI to display\n+ pass\n\n- def get_user_input(self, prompt: str) -> str:\n- return input(prompt)\n+\n+ def get_user_input(self, prompt: str) -> str:\n+ # This method will now trigger the GUI to get user input\n+ pass\n\n- def show_attempts(self, attempts: int):\n- print(f"Number of attempts: {attempts}")\n+\n+ def show_attempts(self, attempts: int):\n+ # This method will now update the GUI with the number of attempts\n+ pass\n\n- def show_history(self, history: list):\n- print("Guess history:")\n- for guess in history:\n- print(guess)\n+\n+ def show_history(self, history: list):\n+ # This method will now update the GUI with the guess history\n+ pass\n```\n\n4. Plan for game.py: Ensure game.py remains mostly unchanged as it contains the core game logic. However, make minor adjustments if necessary to integrate with the new GUI.\n```python\nclass Game:\n # No changes required for now\n```\n""",
],
}
REFINED_CODE_INPUT_SAMPLE = """

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
import pytest
from metagpt.actions.mi.ask_review import AskReview
from metagpt.actions.di.ask_review import AskReview
@pytest.mark.asyncio

View file

@ -5,7 +5,7 @@
import pytest
from metagpt.actions.mi.debug_code import DebugCode
from metagpt.actions.di.debug_code import DebugCode
from metagpt.schema import Message
ErrorStr = """Tested passed:

View file

@ -1,6 +1,6 @@
import pytest
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode, truncate
from metagpt.actions.di.execute_nb_code import ExecuteNbCode, truncate
@pytest.mark.asyncio

View file

@ -1,6 +1,6 @@
import pytest
from metagpt.actions.mi.ml_action import WriteCodeWithToolsML
from metagpt.actions.di.ml_action import WriteCodeWithToolsML
from metagpt.schema import Plan, Task

View file

@ -2,8 +2,8 @@ import asyncio
import pytest
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.mi.write_analysis_code import (
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.write_analysis_code import (
WriteCodeWithoutTools,
WriteCodeWithTools,
)

View file

@ -1,6 +1,6 @@
import pytest
from metagpt.actions.mi.write_plan import (
from metagpt.actions.di.write_plan import (
Plan,
Task,
WritePlan,

View file

@ -37,7 +37,7 @@ DESIGN = {
}
TASKS = {
TASK = {
"Required Python packages": ["pygame==2.0.1"],
"Required Other language third-party packages": ["No third-party dependencies required"],
"Logic Analysis": [

View file

@ -9,8 +9,10 @@
import pytest
from metagpt.actions.design_api import WriteDesign
from metagpt.llm import LLM
from metagpt.logs import logger
from metagpt.schema import Message
from tests.data.incremental_dev_project.mock import DESIGN_SAMPLE, REFINED_PRD_JSON
@pytest.mark.asyncio
@ -25,3 +27,16 @@ async def test_design_api(context):
logger.info(result)
assert result
@pytest.mark.asyncio
async def test_refined_design_api(context):
await context.repo.docs.prd.save(filename="1.txt", content=str(REFINED_PRD_JSON))
await context.repo.docs.system_design.save(filename="1.txt", content=DESIGN_SAMPLE)
design_api = WriteDesign(context=context, llm=LLM())
result = await design_api.run(Message(content="", instruct_content=None))
logger.info(result)
assert result

View file

@ -9,13 +9,19 @@
import pytest
from metagpt.actions.project_management import WriteTasks
from metagpt.llm import LLM
from metagpt.logs import logger
from metagpt.schema import Message
from tests.data.incremental_dev_project.mock import (
REFINED_DESIGN_JSON,
REFINED_PRD_JSON,
TASK_SAMPLE,
)
from tests.metagpt.actions.mock_json import DESIGN, PRD
@pytest.mark.asyncio
async def test_design_api(context):
async def test_task(context):
await context.repo.docs.prd.save("1.txt", content=str(PRD))
await context.repo.docs.system_design.save("1.txt", content=str(DESIGN))
logger.info(context.git_repo)
@ -26,3 +32,19 @@ async def test_design_api(context):
logger.info(result)
assert result
@pytest.mark.asyncio
async def test_refined_task(context):
await context.repo.docs.prd.save("2.txt", content=str(REFINED_PRD_JSON))
await context.repo.docs.system_design.save("2.txt", content=str(REFINED_DESIGN_JSON))
await context.repo.docs.task.save("2.txt", content=TASK_SAMPLE)
logger.info(context.git_repo)
action = WriteTasks(context=context, llm=LLM())
result = await action.run(Message(content="", instruct_content=None))
logger.info(result)
assert result

View file

@ -10,13 +10,14 @@ from openai._models import BaseModel
from metagpt.actions.action_node import ActionNode, dict_to_markdown
from metagpt.actions.project_management import NEW_REQ_TEMPLATE
from metagpt.actions.project_management_an import REFINED_PM_NODE
from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE
from metagpt.llm import LLM
from tests.data.incremental_dev_project.mock import (
REFINED_DESIGN_JSON,
REFINED_TASKS_JSON,
TASKS_SAMPLE,
REFINED_TASK_JSON,
TASK_SAMPLE,
)
from tests.metagpt.actions.mock_json import TASK
@pytest.fixture()
@ -24,20 +25,40 @@ def llm():
return LLM()
def mock_refined_tasks_json():
return REFINED_TASKS_JSON
def mock_refined_task_json():
return REFINED_TASK_JSON
def mock_task_json():
return TASK
@pytest.mark.asyncio
async def test_project_management_an(mocker):
root = ActionNode.from_children(
"ProjectManagement", [ActionNode(key="", expected_type=str, instruction="", example="")]
)
root.instruct_content = BaseModel()
root.instruct_content.model_dump = mock_task_json
mocker.patch("metagpt.actions.project_management_an.PM_NODE.fill", return_value=root)
node = await PM_NODE.fill(dict_to_markdown(REFINED_DESIGN_JSON), llm)
assert "Logic Analysis" in node.instruct_content.model_dump()
assert "Task list" in node.instruct_content.model_dump()
assert "Shared Knowledge" in node.instruct_content.model_dump()
@pytest.mark.asyncio
async def test_project_management_an_inc(mocker):
root = ActionNode.from_children(
"RefinedProjectManagement", [ActionNode(key="", expected_type=str, instruction="", example="")]
)
root.instruct_content = BaseModel()
root.instruct_content.model_dump = mock_refined_tasks_json
root.instruct_content.model_dump = mock_refined_task_json
mocker.patch("metagpt.actions.project_management_an.REFINED_PM_NODE.fill", return_value=root)
prompt = NEW_REQ_TEMPLATE.format(old_task=TASKS_SAMPLE, context=dict_to_markdown(REFINED_DESIGN_JSON))
prompt = NEW_REQ_TEMPLATE.format(old_task=TASK_SAMPLE, context=dict_to_markdown(REFINED_DESIGN_JSON))
node = await REFINED_PM_NODE.fill(prompt, llm)
assert "Refined Logic Analysis" in node.instruct_content.model_dump()

View file

@ -6,7 +6,7 @@
@File : test_write_code.py
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
"""
import json
from pathlib import Path
import pytest
@ -14,10 +14,27 @@ import pytest
from metagpt.actions.write_code import WriteCode
from metagpt.logs import logger
from metagpt.schema import CodingContext, Document
from metagpt.utils.common import aread
from metagpt.utils.common import CodeParser, aread
from tests.data.incremental_dev_project.mock import (
CODE_PLAN_AND_CHANGE_SAMPLE,
REFINED_CODE_INPUT_SAMPLE,
REFINED_DESIGN_JSON,
REFINED_TASK_JSON,
)
from tests.metagpt.actions.mock_markdown import TASKS_2, WRITE_CODE_PROMPT_SAMPLE
def setup_inc_workdir(context, inc: bool = False):
"""setup incremental workdir for testing"""
context.src_workspace = context.git_repo.workdir / "src"
if inc:
context.config.inc = inc
context.repo.old_workspace = context.repo.git_repo.workdir / "old"
context.config.project_path = "old"
return context
@pytest.mark.asyncio
async def test_write_code(context):
# Prerequisites
@ -81,5 +98,66 @@ async def test_write_code_deps(context):
assert rsp.code_doc.content
@pytest.mark.asyncio
async def test_write_refined_code(context, git_dir):
# Prerequisites
context = setup_inc_workdir(context, inc=True)
await context.repo.docs.system_design.save(filename="1.json", content=json.dumps(REFINED_DESIGN_JSON))
await context.repo.docs.task.save(filename="1.json", content=json.dumps(REFINED_TASK_JSON))
await context.repo.docs.code_plan_and_change.save(
filename="1.json", content=json.dumps(CODE_PLAN_AND_CHANGE_SAMPLE)
)
# old_workspace contains the legacy code
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename="game.py", content=CodeParser.parse_code(block="", text=REFINED_CODE_INPUT_SAMPLE)
)
ccontext = CodingContext(
filename="game.py",
design_doc=await context.repo.docs.system_design.get(filename="1.json"),
task_doc=await context.repo.docs.task.get(filename="1.json"),
code_plan_and_change_doc=await context.repo.docs.code_plan_and_change.get(filename="1.json"),
code_doc=Document(filename="game.py", content="", root_path="src"),
)
coding_doc = Document(root_path="src", filename="game.py", content=ccontext.json())
action = WriteCode(i_context=coding_doc, context=context)
rsp = await action.run()
assert rsp
assert rsp.code_doc.content
@pytest.mark.asyncio
async def test_get_codes(context):
# Prerequisites
context = setup_inc_workdir(context, inc=True)
for filename in ["game.py", "ui.py"]:
await context.repo.with_src_path(context.src_workspace).srcs.save(
filename=filename, content=f"# {filename}\nnew code ..."
)
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename=filename, content=f"# {filename}\nlegacy code ..."
)
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename="gui.py", content="# gui.py\nlegacy code ..."
)
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename="main.py", content='# main.py\nif __name__ == "__main__":\n main()'
)
task_doc = Document(filename="1.json", content=json.dumps(REFINED_TASK_JSON))
context.repo = context.repo.with_src_path(context.src_workspace)
# Ready to write gui.py
codes = await WriteCode.get_codes(task_doc=task_doc, exclude="gui.py", project_repo=context.repo)
codes_inc = await WriteCode.get_codes(task_doc=task_doc, exclude="gui.py", project_repo=context.repo, use_inc=True)
logger.info(codes)
logger.info(codes_inc)
assert codes
assert codes_inc
if __name__ == "__main__":
pytest.main([__file__, "-s"])

View file

@ -5,6 +5,8 @@
@Author : mannaandpoem
@File : test_write_code_plan_and_change_an.py
"""
import json
import pytest
from openai._models import BaseModel
@ -14,15 +16,21 @@ from metagpt.actions.write_code_plan_and_change_an import (
REFINED_TEMPLATE,
WriteCodePlanAndChange,
)
from metagpt.logs import logger
from metagpt.schema import CodePlanAndChangeContext
from metagpt.utils.common import CodeParser
from tests.data.incremental_dev_project.mock import (
CODE_PLAN_AND_CHANGE_SAMPLE,
DESIGN_SAMPLE,
NEW_REQUIREMENT_SAMPLE,
REFINED_CODE_INPUT_SAMPLE,
REFINED_CODE_SAMPLE,
TASKS_SAMPLE,
REFINED_DESIGN_JSON,
REFINED_PRD_JSON,
REFINED_TASK_JSON,
TASK_SAMPLE,
)
from tests.metagpt.actions.test_write_code import setup_inc_workdir
def mock_code_plan_and_change():
@ -30,27 +38,35 @@ def mock_code_plan_and_change():
@pytest.mark.asyncio
async def test_write_code_plan_and_change_an(mocker):
async def test_write_code_plan_and_change_an(mocker, context, git_dir):
context = setup_inc_workdir(context, inc=True)
await context.repo.docs.prd.save(filename="2.json", content=json.dumps(REFINED_PRD_JSON))
await context.repo.docs.system_design.save(filename="2.json", content=json.dumps(REFINED_DESIGN_JSON))
await context.repo.docs.task.save(filename="2.json", content=json.dumps(REFINED_TASK_JSON))
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename="game.py", content=CodeParser.parse_code(block="", text=REFINED_CODE_INPUT_SAMPLE)
)
root = ActionNode.from_children(
"WriteCodePlanAndChange", [ActionNode(key="", expected_type=str, instruction="", example="")]
)
root.instruct_content = BaseModel()
root.instruct_content.model_dump = mock_code_plan_and_change
mocker.patch("metagpt.actions.write_code_plan_and_change_an.WriteCodePlanAndChange.run", return_value=root)
requirement = "New requirement"
prd_filename = "prd.md"
design_filename = "design.md"
task_filename = "task.md"
code_plan_and_change_context = CodePlanAndChangeContext(
requirement=requirement,
prd_filename=prd_filename,
design_filename=design_filename,
task_filename=task_filename,
mocker.patch(
"metagpt.actions.write_code_plan_and_change_an.WRITE_CODE_PLAN_AND_CHANGE_NODE.fill", return_value=root
)
node = await WriteCodePlanAndChange(i_context=code_plan_and_change_context).run()
assert "Code Plan And Change" in node.instruct_content.model_dump()
code_plan_and_change_context = CodePlanAndChangeContext(
requirement="New requirement",
prd_filename="2.json",
design_filename="2.json",
task_filename="2.json",
)
node = await WriteCodePlanAndChange(i_context=code_plan_and_change_context, context=context).run()
assert "Development Plan" in node.instruct_content.model_dump()
assert "Incremental Change" in node.instruct_content.model_dump()
@pytest.mark.asyncio
@ -60,7 +76,7 @@ async def test_refine_code(mocker):
user_requirement=NEW_REQUIREMENT_SAMPLE,
code_plan_and_change=CODE_PLAN_AND_CHANGE_SAMPLE,
design=DESIGN_SAMPLE,
task=TASKS_SAMPLE,
task=TASK_SAMPLE,
code=REFINED_CODE_INPUT_SAMPLE,
logs="",
feedback="",
@ -69,3 +85,25 @@ async def test_refine_code(mocker):
)
code = await WriteCode().write_code(prompt=prompt)
assert "def" in code
@pytest.mark.asyncio
async def test_get_old_code(context, git_dir):
context = setup_inc_workdir(context, inc=True)
await context.repo.with_src_path(context.repo.old_workspace).srcs.save(
filename="game.py", content=REFINED_CODE_INPUT_SAMPLE
)
code_plan_and_change_context = CodePlanAndChangeContext(
requirement="New requirement",
prd_filename="1.json",
design_filename="1.json",
task_filename="1.json",
)
action = WriteCodePlanAndChange(context=context, i_context=code_plan_and_change_context)
old_codes = await action.get_old_codes()
logger.info(old_codes)
assert "def" in old_codes
assert "class" in old_codes

View file

@ -32,5 +32,28 @@ def add(a, b):
print(f"输出内容: {captured.out}")
@pytest.mark.asyncio
async def test_write_code_review_inc(capfd, context):
context.src_workspace = context.repo.workdir / "srcs"
context.config.inc = True
code = """
def add(a, b):
return a +
"""
code_plan_and_change = """
def add(a, b):
- return a +
+ return a + b
"""
coding_context = CodingContext(
filename="math.py",
design_doc=Document(content="编写一个从a加b的函数返回a+b"),
code_doc=Document(content=code),
code_plan_and_change_doc=Document(content=code_plan_and_change),
)
await WriteCodeReview(i_context=coding_context, context=context).run()
if __name__ == "__main__":
pytest.main([__file__, "-s"])

View file

@ -6,6 +6,7 @@
@File : test_write_prd.py
@Modified By: mashenquan, 2023-11-1. According to Chapter 2.2.1 and 2.2.2 of RFC 116, replace `handle` with `run`.
"""
import pytest
from metagpt.actions import UserRequirement, WritePRD
@ -15,6 +16,8 @@ from metagpt.roles.product_manager import ProductManager
from metagpt.roles.role import RoleReactMode
from metagpt.schema import Message
from metagpt.utils.common import any_to_str
from tests.data.incremental_dev_project.mock import NEW_REQUIREMENT_SAMPLE, PRD_SAMPLE
from tests.metagpt.actions.test_write_code import setup_inc_workdir
@pytest.mark.asyncio
@ -34,5 +37,41 @@ async def test_write_prd(new_filename, context):
assert product_manager.context.repo.docs.prd.changed_files
@pytest.mark.asyncio
async def test_write_prd_inc(new_filename, context, git_dir):
context = setup_inc_workdir(context, inc=True)
await context.repo.docs.prd.save("1.txt", PRD_SAMPLE)
await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=NEW_REQUIREMENT_SAMPLE)
action = WritePRD(context=context)
prd = await action.run(Message(content=NEW_REQUIREMENT_SAMPLE, instruct_content=None))
logger.info(NEW_REQUIREMENT_SAMPLE)
logger.info(prd)
# Assert the prd is not None or empty
assert prd is not None
assert prd.content != ""
assert "Refined Requirements" in prd.content
@pytest.mark.asyncio
async def test_fix_debug(new_filename, context, git_dir):
context.src_workspace = context.git_repo.workdir / context.git_repo.workdir.name
await context.repo.with_src_path(context.src_workspace).srcs.save(
filename="main.py", content='if __name__ == "__main__":\nmain()'
)
requirements = "Please fix the bug in the code."
await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=requirements)
action = WritePRD(context=context)
prd = await action.run(Message(content=requirements, instruct_content=None))
logger.info(prd)
# Assert the prd is not None or empty
assert prd is not None
assert prd.content != ""
if __name__ == "__main__":
pytest.main([__file__, "-s"])

View file

@ -56,3 +56,7 @@ mock_llm_config_spark = LLMConfig(
mock_llm_config_qianfan = LLMConfig(api_type="qianfan", access_key="xxx", secret_key="xxx", model="ERNIE-Bot-turbo")
mock_llm_config_dashscope = LLMConfig(api_type="dashscope", api_key="xxx", model="qwen-max")
mock_llm_config_anthropic = LLMConfig(
api_type="anthropic", api_key="xxx", base_url="https://api.anthropic.com", model="claude-3-opus-20240229"
)

View file

@ -3,6 +3,14 @@
# @Desc : default request & response data for provider unittest
from anthropic.types import (
ContentBlock,
ContentBlockDeltaEvent,
Message,
MessageStartEvent,
TextDelta,
)
from anthropic.types import Usage as AnthropicUsage
from dashscope.api_entities.dashscope_response import (
DashScopeAPIResponse,
GenerationOutput,
@ -130,6 +138,38 @@ def get_dashscope_response(name: str) -> GenerationResponse:
)
# For Anthropic
def get_anthropic_response(name: str, stream: bool = False) -> Message:
if stream:
return [
MessageStartEvent(
message=Message(
id="xxx",
model=name,
role="assistant",
type="message",
content=[ContentBlock(text="", type="text")],
usage=AnthropicUsage(input_tokens=10, output_tokens=10),
),
type="message_start",
),
ContentBlockDeltaEvent(
index=0,
delta=TextDelta(text=resp_cont_tmpl.format(name=name), type="text_delta"),
type="content_block_delta",
),
]
else:
return Message(
id="xxx",
model=name,
role="assistant",
type="message",
content=[ContentBlock(text=resp_cont_tmpl.format(name=name), type="text")],
usage=AnthropicUsage(input_tokens=10, output_tokens=10),
)
# For llm general chat functions call
async def llm_general_chat_funcs_test(llm: BaseLLM, prompt: str, messages: list[dict], resp_cont: str):
resp = await llm.aask(prompt, stream=False)

View file

@ -2,31 +2,45 @@
# -*- coding: utf-8 -*-
# @Desc : the unittest of Claude2
import pytest
from anthropic.resources.completions import Completion
from metagpt.provider.anthropic_api import Claude2
from tests.metagpt.provider.mock_llm_config import mock_llm_config
from tests.metagpt.provider.req_resp_const import prompt, resp_cont_tmpl
from metagpt.provider.anthropic_api import AnthropicLLM
from tests.metagpt.provider.mock_llm_config import mock_llm_config_anthropic
from tests.metagpt.provider.req_resp_const import (
get_anthropic_response,
llm_general_chat_funcs_test,
messages,
prompt,
resp_cont_tmpl,
)
resp_cont = resp_cont_tmpl.format(name="Claude")
name = "claude-3-opus-20240229"
resp_cont = resp_cont_tmpl.format(name=name)
def mock_anthropic_completions_create(self, model: str, prompt: str, max_tokens_to_sample: int) -> Completion:
return Completion(id="xx", completion=resp_cont, model="claude-2", stop_reason="stop_sequence", type="completion")
async def mock_anthropic_messages_create(
self, messages: list[dict], model: str, stream: bool = True, max_tokens: int = None, system: str = None
) -> Completion:
if stream:
async def aresp_iterator():
resps = get_anthropic_response(name, stream=True)
for resp in resps:
yield resp
async def mock_anthropic_acompletions_create(self, model: str, prompt: str, max_tokens_to_sample: int) -> Completion:
return Completion(id="xx", completion=resp_cont, model="claude-2", stop_reason="stop_sequence", type="completion")
def test_claude2_ask(mocker):
mocker.patch("anthropic.resources.completions.Completions.create", mock_anthropic_completions_create)
assert resp_cont == Claude2(mock_llm_config).ask(prompt)
return aresp_iterator()
else:
return get_anthropic_response(name)
@pytest.mark.asyncio
async def test_claude2_aask(mocker):
mocker.patch("anthropic.resources.completions.AsyncCompletions.create", mock_anthropic_acompletions_create)
assert resp_cont == await Claude2(mock_llm_config).aask(prompt)
async def test_anthropic_acompletion(mocker):
mocker.patch("anthropic.resources.messages.AsyncMessages.create", mock_anthropic_messages_create)
anthropic_llm = AnthropicLLM(mock_llm_config_anthropic)
resp = await anthropic_llm.acompletion(messages)
assert resp.content[0].text == resp_cont
await llm_general_chat_funcs_test(anthropic_llm, prompt, messages, resp_cont)

View file

@ -27,9 +27,15 @@ class MockBaseLLM(BaseLLM):
def completion(self, messages: list[dict], timeout=3):
return get_part_chat_completion(name)
async def _achat_completion(self, messages: list[dict], timeout=3):
pass
async def acompletion(self, messages: list[dict], timeout=3):
return get_part_chat_completion(name)
async def _achat_completion_stream(self, messages: list[dict], timeout: int = 3) -> str:
pass
async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) -> str:
return default_resp_cont

View file

@ -1,23 +1,23 @@
import pytest
from metagpt.logs import logger
from metagpt.roles.mi.interpreter import Interpreter
from metagpt.roles.di.data_interpreter import DataInterpreter
@pytest.mark.asyncio
@pytest.mark.parametrize("auto_run", [(True), (False)])
async def test_interpreter(mocker, auto_run):
mocker.patch("metagpt.actions.mi.execute_nb_code.ExecuteNbCode.run", return_value=("a successful run", True))
mocker.patch("metagpt.actions.di.execute_nb_code.ExecuteNbCode.run", return_value=("a successful run", True))
mocker.patch("builtins.input", return_value="confirm")
requirement = "Run data analysis on sklearn Iris dataset, include a plot"
tools = []
mi = Interpreter(auto_run=auto_run, use_tools=True, tools=tools)
rsp = await mi.run(requirement)
di = DataInterpreter(auto_run=auto_run, use_tools=True, tools=tools)
rsp = await di.run(requirement)
logger.info(rsp)
assert len(rsp.content) > 0
finished_tasks = mi.planner.plan.get_finished_tasks()
finished_tasks = di.planner.plan.get_finished_tasks()
assert len(finished_tasks) > 0
assert len(finished_tasks[0].code) > 0 # check one task to see if code is recorded

View file

@ -1,11 +1,11 @@
import pytest
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.logs import logger
from metagpt.roles.mi.ml_engineer import MLEngineer
from metagpt.roles.di.ml_engineer import MLEngineer
from metagpt.schema import Message, Plan, Task
from metagpt.tools.tool_type import ToolType
from tests.metagpt.actions.mi.test_debug_code import CODE, DebugContext, ErrorStr
from tests.metagpt.actions.di.test_debug_code import CODE, DebugContext, ErrorStr
def test_mle_init():

View file

@ -6,7 +6,7 @@
import nbformat
import pytest
from metagpt.actions.mi.execute_nb_code import ExecuteNbCode
from metagpt.actions.di.execute_nb_code import ExecuteNbCode
from metagpt.utils.common import read_json_file
from metagpt.utils.save_code import DATA_PATH, save_code_file

View file

@ -3,7 +3,7 @@ from typing import Optional, Union
from metagpt.config2 import config
from metagpt.configs.llm_config import LLMType
from metagpt.logs import log_llm_stream, logger
from metagpt.logs import logger
from metagpt.provider.azure_openai_api import AzureOpenAILLM
from metagpt.provider.openai_api import OpenAILLM
from metagpt.schema import Message
@ -24,17 +24,8 @@ class MockLLM(OriginalLLM):
async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) -> str:
"""Overwrite original acompletion_text to cancel retry"""
if stream:
resp = self._achat_completion_stream(messages, timeout=timeout)
collected_messages = []
async for i in resp:
log_llm_stream(i)
collected_messages.append(i)
full_reply_content = "".join(collected_messages)
usage = self._calc_usage(messages, full_reply_content)
self._update_costs(usage)
return full_reply_content
resp = await self._achat_completion_stream(messages, timeout=timeout)
return resp
rsp = await self._achat_completion(messages, timeout=timeout)
return self.get_choice_text(rsp)