mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-04-26 01:06:27 +02:00
feat: +SummarizeCode, refactor project_name
This commit is contained in:
parent
838b3cfcc8
commit
9d84c8f047
31 changed files with 671 additions and 245 deletions
|
|
@ -90,7 +90,7 @@ Python's in-built data structures like lists and dictionaries will be used exten
|
|||
|
||||
For testing, we can use the PyTest framework. This is a mature full-featured Python testing tool that helps you write better programs.
|
||||
|
||||
## project_name:
|
||||
## Project Name:
|
||||
```python
|
||||
"adventure_game"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@
|
|||
@Time : 2023/5/11 17:46
|
||||
@Author : alexanderwu
|
||||
@File : test_debug_error.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
|
||||
"""
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.debug_error import DebugError
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import TEST_CODES_FILE_REPO, TEST_OUTPUTS_FILE_REPO
|
||||
from metagpt.schema import RunCodeContext, RunCodeResult
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
|
||||
EXAMPLE_MSG_CONTENT = '''
|
||||
---
|
||||
## Development Code File Name
|
||||
player.py
|
||||
## Development Code
|
||||
```python
|
||||
CODE_CONTENT = '''
|
||||
from typing import List
|
||||
from deck import Deck
|
||||
from card import Card
|
||||
|
|
@ -58,12 +60,9 @@ class Player:
|
|||
if self.score > 21 and any(card.rank == 'A' for card in self.hand):
|
||||
self.score -= 10
|
||||
return self.score
|
||||
'''
|
||||
|
||||
```
|
||||
## Test File Name
|
||||
test_player.py
|
||||
## Test Code
|
||||
```python
|
||||
TEST_CONTENT = """
|
||||
import unittest
|
||||
from blackjack_game.player import Player
|
||||
from blackjack_game.deck import Deck
|
||||
|
|
@ -114,42 +113,41 @@ class TestPlayer(unittest.TestCase):
|
|||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
```
|
||||
## Running Command
|
||||
python tests/test_player.py
|
||||
## Running Output
|
||||
standard output: ;
|
||||
standard errors: ..F..
|
||||
======================================================================
|
||||
FAIL: test_player_calculate_score_with_multiple_aces (__main__.TestPlayer)
|
||||
----------------------------------------------------------------------
|
||||
Traceback (most recent call last):
|
||||
File "tests/test_player.py", line 46, in test_player_calculate_score_with_multiple_aces
|
||||
self.assertEqual(player.score, 12)
|
||||
AssertionError: 22 != 12
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 5 tests in 0.007s
|
||||
|
||||
FAILED (failures=1)
|
||||
;
|
||||
## instruction:
|
||||
The error is in the development code, specifically in the calculate_score method of the Player class. The method is not correctly handling the case where there are multiple Aces in the player's hand. The current implementation only subtracts 10 from the score once if the score is over 21 and there's an Ace in the hand. However, in the case of multiple Aces, it should subtract 10 for each Ace until the score is 21 or less.
|
||||
## File To Rewrite:
|
||||
player.py
|
||||
## Status:
|
||||
FAIL
|
||||
## Send To:
|
||||
Engineer
|
||||
---
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_debug_error():
|
||||
debug_error = DebugError("debug_error")
|
||||
CONFIG.src_workspace = CONFIG.git_repo.workdir / uuid.uuid4().hex
|
||||
ctx = RunCodeContext(
|
||||
code_filename="player.py",
|
||||
test_filename="test_player.py",
|
||||
command=["python", "tests/test_player.py"],
|
||||
output_filename="output.log",
|
||||
)
|
||||
|
||||
file_name, rewritten_code = await debug_error.run(context=EXAMPLE_MSG_CONTENT)
|
||||
await FileRepository.save_file(filename=ctx.code_filename, content=CODE_CONTENT, relative_path=CONFIG.src_workspace)
|
||||
await FileRepository.save_file(filename=ctx.test_filename, content=TEST_CONTENT, relative_path=TEST_CODES_FILE_REPO)
|
||||
output_data = RunCodeResult(
|
||||
stdout=";",
|
||||
stderr="",
|
||||
summary="======================================================================\n"
|
||||
"FAIL: test_player_calculate_score_with_multiple_aces (__main__.TestPlayer)\n"
|
||||
"----------------------------------------------------------------------\n"
|
||||
"Traceback (most recent call last):\n"
|
||||
' File "tests/test_player.py", line 46, in test_player_calculate_score_'
|
||||
"with_multiple_aces\n"
|
||||
" self.assertEqual(player.score, 12)\nAssertionError: 22 != 12\n\n"
|
||||
"----------------------------------------------------------------------\n"
|
||||
"Ran 5 tests in 0.007s\n\nFAILED (failures=1)\n;\n",
|
||||
)
|
||||
await FileRepository.save_file(
|
||||
filename=ctx.output_filename, content=output_data.json(), relative_path=TEST_OUTPUTS_FILE_REPO
|
||||
)
|
||||
debug_error = DebugError(context=ctx)
|
||||
|
||||
assert "class Player" in rewritten_code # rewrite the same class
|
||||
assert "while self.score > 21" in rewritten_code # a key logic to rewrite to (original one is "if self.score > 12")
|
||||
rsp = await debug_error.run()
|
||||
|
||||
assert "class Player" in rsp # rewrite the same class
|
||||
# a key logic to rewrite to (original one is "if self.score > 12")
|
||||
assert "while self.score > 21" in rsp
|
||||
|
|
|
|||
|
|
@ -4,33 +4,27 @@
|
|||
@Time : 2023/5/11 19:26
|
||||
@Author : alexanderwu
|
||||
@File : test_design_api.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.design_api import WriteDesign
|
||||
from metagpt.const import PRDS_FILE_REPO
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
from tests.metagpt.actions.mock import PRD_SAMPLE
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_design_api():
|
||||
prd = "我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。"
|
||||
inputs = ["我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。", PRD_SAMPLE]
|
||||
for prd in inputs:
|
||||
await FileRepository.save_file("new_prd.txt", content=prd, relative_path=PRDS_FILE_REPO)
|
||||
|
||||
design_api = WriteDesign("design_api")
|
||||
design_api = WriteDesign("design_api")
|
||||
|
||||
result = await design_api.run([Message(content=prd, instruct_content=None)])
|
||||
logger.info(result)
|
||||
result = await design_api.run([Message(content=prd, instruct_content=None)])
|
||||
logger.info(result)
|
||||
|
||||
assert result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_design_api_calculator():
|
||||
prd = PRD_SAMPLE
|
||||
|
||||
design_api = WriteDesign("design_api")
|
||||
result = await design_api.run([Message(content=prd, instruct_content=None)])
|
||||
logger.info(result)
|
||||
|
||||
assert result
|
||||
assert result
|
||||
|
|
|
|||
30
tests/metagpt/actions/test_prepare_documents.py
Normal file
30
tests/metagpt/actions/test_prepare_documents.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/12/6
|
||||
@Author : mashenquan
|
||||
@File : test_prepare_documents.py
|
||||
@Desc: Unit test for prepare_documents.py
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.prepare_documents import PrepareDocuments
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import DOCS_FILE_REPO, REQUIREMENT_FILENAME
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_documents():
|
||||
msg = Message(content="New user requirements balabala...")
|
||||
|
||||
if CONFIG.git_repo:
|
||||
CONFIG.git_repo.delete_repository()
|
||||
CONFIG.git_repo = None
|
||||
|
||||
await PrepareDocuments().run(with_messages=[msg])
|
||||
assert CONFIG.git_repo
|
||||
doc = await FileRepository.get_file(filename=REQUIREMENT_FILENAME, relative_path=DOCS_FILE_REPO)
|
||||
assert doc
|
||||
assert doc.content == msg.content
|
||||
|
|
@ -4,10 +4,12 @@
|
|||
@Time : 2023/5/11 17:46
|
||||
@Author : alexanderwu
|
||||
@File : test_run_code.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.run_code import RunCode
|
||||
from metagpt.schema import RunCodeContext
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -35,37 +37,29 @@ async def test_run_script():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run():
|
||||
action = RunCode()
|
||||
result = await action.run(mode="text", code="print('Hello, World')")
|
||||
assert "PASS" in result
|
||||
|
||||
result = await action.run(
|
||||
mode="script",
|
||||
code="echo 'Hello World'",
|
||||
code_file_name="",
|
||||
test_code="",
|
||||
test_file_name="",
|
||||
command=["echo", "Hello World"],
|
||||
working_directory=".",
|
||||
additional_python_paths=[],
|
||||
)
|
||||
assert "PASS" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_failure():
|
||||
action = RunCode()
|
||||
result = await action.run(mode="text", code="result = 1 / 0")
|
||||
assert "FAIL" in result
|
||||
|
||||
result = await action.run(
|
||||
mode="script",
|
||||
code='python -c "print(1/0)"',
|
||||
code_file_name="",
|
||||
test_code="",
|
||||
test_file_name="",
|
||||
command=["python", "-c", "print(1/0)"],
|
||||
working_directory=".",
|
||||
additional_python_paths=[],
|
||||
)
|
||||
assert "FAIL" in result
|
||||
inputs = [
|
||||
(RunCodeContext(mode="text", code_filename="a.txt", code="print('Hello, World')"), "PASS"),
|
||||
(
|
||||
RunCodeContext(
|
||||
mode="script",
|
||||
code_filename="a.sh",
|
||||
code="echo 'Hello World'",
|
||||
command=["echo", "Hello World"],
|
||||
working_directory=".",
|
||||
),
|
||||
"PASS",
|
||||
),
|
||||
(
|
||||
RunCodeContext(
|
||||
mode="script",
|
||||
code_filename="a.py",
|
||||
code='python -c "print(1/0)"',
|
||||
command=["python", "-c", "print(1/0)"],
|
||||
working_directory=".",
|
||||
),
|
||||
"FAIL",
|
||||
),
|
||||
]
|
||||
for ctx, result in inputs:
|
||||
rsp = await RunCode(context=ctx).run()
|
||||
assert result in rsp.summary
|
||||
|
|
|
|||
195
tests/metagpt/actions/test_summarize_code.py
Normal file
195
tests/metagpt/actions/test_summarize_code.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/5/11 17:46
|
||||
@Author : mashenquan
|
||||
@File : test_summarize_code.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. Unit test for summarize_code.py
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.summarize_code import SummarizeCode
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import CodeSummarizeContext
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
|
||||
DESIGN_CONTENT = """
|
||||
{"Implementation approach": "To develop this snake game, we will use the Python language and choose the Pygame library. Pygame is an open-source Python module collection specifically designed for writing video games. It provides functionalities such as displaying images and playing sounds, making it suitable for creating intuitive and responsive user interfaces. We will ensure efficient game logic to prevent any delays during gameplay. The scoring system will be simple, with the snake gaining points for each food it eats. We will use Pygame's event handling system to implement pause and resume functionality, as well as high-score tracking. The difficulty will increase by speeding up the snake's movement. In the initial version, we will focus on single-player mode and consider adding multiplayer mode and customizable skins in future updates. Based on the new requirement, we will also add a moving obstacle that appears randomly. If the snake eats this obstacle, the game will end. If the snake does not eat the obstacle, it will disappear after 5 seconds. For this, we need to add mechanisms for obstacle generation, movement, and disappearance in the game logic.", "Project_name": "snake_game", "File list": ["main.py", "game.py", "snake.py", "food.py", "obstacle.py", "scoreboard.py", "constants.py", "assets/styles.css", "assets/index.html"], "Data structures and interfaces": "```mermaid\n classDiagram\n class Game{\n +int score\n +int speed\n +bool game_over\n +bool paused\n +Snake snake\n +Food food\n +Obstacle obstacle\n +Scoreboard scoreboard\n +start_game() void\n +pause_game() void\n +resume_game() void\n +end_game() void\n +increase_difficulty() void\n +update() void\n +render() void\n Game()\n }\n class Snake{\n +list body_parts\n +str direction\n +bool grow\n +move() void\n +grow() void\n +check_collision() bool\n Snake()\n }\n class Food{\n +tuple position\n +spawn() void\n Food()\n }\n class Obstacle{\n +tuple position\n +int lifetime\n +bool active\n +spawn() void\n +move() void\n +check_collision() bool\n +disappear() void\n Obstacle()\n }\n class Scoreboard{\n +int high_score\n +update_score(int) void\n +reset_score() void\n +load_high_score() void\n +save_high_score() void\n Scoreboard()\n }\n class Constants{\n }\n Game \"1\" -- \"1\" Snake: has\n Game \"1\" -- \"1\" Food: has\n Game \"1\" -- \"1\" Obstacle: has\n Game \"1\" -- \"1\" Scoreboard: has\n ```", "Program call flow": "```sequenceDiagram\n participant M as Main\n participant G as Game\n participant S as Snake\n participant F as Food\n participant O as Obstacle\n participant SB as Scoreboard\n M->>G: start_game()\n loop game loop\n G->>S: move()\n G->>S: check_collision()\n G->>F: spawn()\n G->>O: spawn()\n G->>O: move()\n G->>O: check_collision()\n G->>O: disappear()\n G->>SB: update_score(score)\n G->>G: update()\n G->>G: render()\n alt if paused\n M->>G: pause_game()\n M->>G: resume_game()\n end\n alt if game_over\n G->>M: end_game()\n end\n end\n```", "Anything UNCLEAR": "There is no need for further clarification as the requirements are already clear."}
|
||||
"""
|
||||
|
||||
TASK_CONTENT = """
|
||||
{"Required Python third-party packages": ["pygame==2.0.1"], "Required Other language third-party packages": ["No third-party packages required for other languages."], "Full API spec": "\n openapi: 3.0.0\n info:\n title: Snake Game API\n version: \"1.0.0\"\n paths:\n /start:\n get:\n summary: Start the game\n responses:\n '200':\n description: Game started successfully\n /pause:\n get:\n summary: Pause the game\n responses:\n '200':\n description: Game paused successfully\n /resume:\n get:\n summary: Resume the game\n responses:\n '200':\n description: Game resumed successfully\n /end:\n get:\n summary: End the game\n responses:\n '200':\n description: Game ended successfully\n /score:\n get:\n summary: Get the current score\n responses:\n '200':\n description: Current score retrieved successfully\n /highscore:\n get:\n summary: Get the high score\n responses:\n '200':\n description: High score retrieved successfully\n components: {}\n ", "Logic Analysis": [["constants.py", "Contains all the constant values like screen size, colors, game speeds, etc. This should be implemented first as it provides the base values for other components."], ["snake.py", "Contains the Snake class with methods for movement, growth, and collision detection. It is dependent on constants.py for configuration values."], ["food.py", "Contains the Food class responsible for spawning food items on the screen. It is dependent on constants.py for configuration values."], ["obstacle.py", "Contains the Obstacle class with methods for spawning, moving, and disappearing of obstacles, as well as collision detection with the snake. It is dependent on constants.py for configuration values."], ["scoreboard.py", "Contains the Scoreboard class for updating, resetting, loading, and saving high scores. It may use constants.py for configuration values and depends on the game's scoring logic."], ["game.py", "Contains the main Game class which includes the game loop and methods for starting, pausing, resuming, and ending the game. It is dependent on snake.py, food.py, obstacle.py, and scoreboard.py."], ["main.py", "The entry point of the game that initializes the game and starts the game loop. It is dependent on game.py."]], "Task list": ["constants.py", "snake.py", "food.py", "obstacle.py", "scoreboard.py", "game.py", "main.py"], "Shared Knowledge": "\n 'constants.py' should contain all the necessary configurations for the game, such as screen dimensions, color definitions, and speed settings. These constants will be used across multiple files, ensuring consistency and ease of updates. Ensure that the Pygame library is initialized correctly in 'main.py' before starting the game loop. Also, make sure that the game's state is managed properly when pausing and resuming the game.\n ", "Anything UNCLEAR": "The interaction between the 'obstacle.py' and the game loop needs to be clearly defined to ensure obstacles appear and disappear correctly. The lifetime of the obstacle and its random movement should be implemented in a way that does not interfere with the game's performance."}
|
||||
"""
|
||||
|
||||
FOOD_PY = """
|
||||
## food.py
|
||||
import random
|
||||
|
||||
class Food:
|
||||
def __init__(self):
|
||||
self.position = (0, 0)
|
||||
|
||||
def generate(self):
|
||||
x = random.randint(0, 9)
|
||||
y = random.randint(0, 9)
|
||||
self.position = (x, y)
|
||||
|
||||
def get_position(self):
|
||||
return self.position
|
||||
|
||||
"""
|
||||
|
||||
GAME_PY = """
|
||||
## game.py
|
||||
import pygame
|
||||
from snake import Snake
|
||||
from food import Food
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
self.score = 0
|
||||
self.level = 1
|
||||
self.snake = Snake()
|
||||
self.food = Food()
|
||||
|
||||
def start_game(self):
|
||||
pygame.init()
|
||||
self.initialize_game()
|
||||
self.game_loop()
|
||||
|
||||
def initialize_game(self):
|
||||
self.score = 0
|
||||
self.level = 1
|
||||
self.snake.reset()
|
||||
self.food.generate()
|
||||
|
||||
def game_loop(self):
|
||||
game_over = False
|
||||
|
||||
while not game_over:
|
||||
self.update()
|
||||
self.draw()
|
||||
self.handle_events()
|
||||
self.check_collision()
|
||||
self.increase_score()
|
||||
self.increase_level()
|
||||
|
||||
if self.snake.is_collision():
|
||||
game_over = True
|
||||
self.game_over()
|
||||
|
||||
def update(self):
|
||||
self.snake.move()
|
||||
|
||||
def draw(self):
|
||||
self.snake.draw()
|
||||
self.food.draw()
|
||||
|
||||
def handle_events(self):
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
quit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_UP:
|
||||
self.snake.change_direction("UP")
|
||||
elif event.key == pygame.K_DOWN:
|
||||
self.snake.change_direction("DOWN")
|
||||
elif event.key == pygame.K_LEFT:
|
||||
self.snake.change_direction("LEFT")
|
||||
elif event.key == pygame.K_RIGHT:
|
||||
self.snake.change_direction("RIGHT")
|
||||
|
||||
def check_collision(self):
|
||||
if self.snake.get_head() == self.food.get_position():
|
||||
self.snake.grow()
|
||||
self.food.generate()
|
||||
|
||||
def increase_score(self):
|
||||
self.score += 1
|
||||
|
||||
def increase_level(self):
|
||||
if self.score % 10 == 0:
|
||||
self.level += 1
|
||||
|
||||
def game_over(self):
|
||||
print("Game Over")
|
||||
self.initialize_game()
|
||||
|
||||
"""
|
||||
|
||||
MAIN_PY = """
|
||||
## main.py
|
||||
import pygame
|
||||
from game import Game
|
||||
|
||||
def main():
|
||||
pygame.init()
|
||||
game = Game()
|
||||
game.start_game()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
"""
|
||||
|
||||
SNAKE_PY = """
|
||||
## snake.py
|
||||
import pygame
|
||||
|
||||
class Snake:
|
||||
def __init__(self):
|
||||
self.body = [(0, 0)]
|
||||
self.direction = (1, 0)
|
||||
|
||||
def move(self):
|
||||
head = self.body[0]
|
||||
dx, dy = self.direction
|
||||
new_head = (head[0] + dx, head[1] + dy)
|
||||
self.body.insert(0, new_head)
|
||||
self.body.pop()
|
||||
|
||||
def change_direction(self, direction):
|
||||
if direction == "UP":
|
||||
self.direction = (0, -1)
|
||||
elif direction == "DOWN":
|
||||
self.direction = (0, 1)
|
||||
elif direction == "LEFT":
|
||||
self.direction = (-1, 0)
|
||||
elif direction == "RIGHT":
|
||||
self.direction = (1, 0)
|
||||
|
||||
def grow(self):
|
||||
tail = self.body[-1]
|
||||
dx, dy = self.direction
|
||||
new_tail = (tail[0] - dx, tail[1] - dy)
|
||||
self.body.append(new_tail)
|
||||
|
||||
def get_head(self):
|
||||
return self.body[0]
|
||||
|
||||
def get_body(self):
|
||||
return self.body[1:]
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_summarize_code():
|
||||
CONFIG.src_workspace = CONFIG.git_repo.workdir / "src"
|
||||
await FileRepository.save_file(filename="1.json", relative_path=SYSTEM_DESIGN_FILE_REPO, content=DESIGN_CONTENT)
|
||||
await FileRepository.save_file(filename="1.json", relative_path=TASK_FILE_REPO, content=TASK_CONTENT)
|
||||
await FileRepository.save_file(filename="food.py", relative_path=CONFIG.src_workspace, content=FOOD_PY)
|
||||
await FileRepository.save_file(filename="game.py", relative_path=CONFIG.src_workspace, content=GAME_PY)
|
||||
await FileRepository.save_file(filename="main.py", relative_path=CONFIG.src_workspace, content=MAIN_PY)
|
||||
await FileRepository.save_file(filename="snake.py", relative_path=CONFIG.src_workspace, content=SNAKE_PY)
|
||||
|
||||
src_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.src_workspace)
|
||||
all_files = src_file_repo.all_files
|
||||
ctx = CodeSummarizeContext(design_filename="1.json", task_filename="1.json", codes_filenames=all_files)
|
||||
action = SummarizeCode(context=ctx)
|
||||
rsp = await action.run()
|
||||
assert rsp
|
||||
logger.info(rsp)
|
||||
|
|
@ -4,26 +4,31 @@
|
|||
@Time : 2023/5/11 17:45
|
||||
@Author : alexanderwu
|
||||
@File : test_write_code.py
|
||||
@Modifiled By: mashenquan, 2023-12-6. According to RFC 135
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.write_code import WriteCode
|
||||
from metagpt.llm import LLM
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import CodingContext, Document
|
||||
from tests.metagpt.actions.mock import TASKS_2, WRITE_CODE_PROMPT_SAMPLE
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_code():
|
||||
api_design = "设计一个名为'add'的函数,该函数接受两个整数作为输入,并返回它们的和。"
|
||||
write_code = WriteCode("write_code")
|
||||
context = CodingContext(
|
||||
filename="task_filename.py", design_doc=Document(content="设计一个名为'add'的函数,该函数接受两个整数作为输入,并返回它们的和。")
|
||||
)
|
||||
doc = Document(content=context.json())
|
||||
write_code = WriteCode(context=doc)
|
||||
|
||||
code = await write_code.run(api_design)
|
||||
logger.info(code)
|
||||
code = await write_code.run()
|
||||
logger.info(code.json())
|
||||
|
||||
# 我们不能精确地预测生成的代码,但我们可以检查某些关键字
|
||||
assert "def add" in code
|
||||
assert "return" in code
|
||||
assert "def add" in code.code_doc.content
|
||||
assert "return" in code.code_doc.content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
import pytest
|
||||
|
||||
from metagpt.actions.write_code_review import WriteCodeReview
|
||||
from metagpt.document import Document
|
||||
from metagpt.schema import CodingContext
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -16,13 +18,15 @@ async def test_write_code_review(capfd):
|
|||
def add(a, b):
|
||||
return a +
|
||||
"""
|
||||
# write_code_review = WriteCodeReview("write_code_review")
|
||||
context = CodingContext(
|
||||
filename="math.py", design_doc=Document(content="编写一个从a加b的函数,返回a+b"), code_doc=Document(content=code)
|
||||
)
|
||||
|
||||
code = await WriteCodeReview().run(context="编写一个从a加b的函数,返回a+b", code=code, filename="math.py")
|
||||
context = await WriteCodeReview(context=context).run()
|
||||
|
||||
# 我们不能精确地预测生成的代码评审,但我们可以检查返回的是否为字符串
|
||||
assert isinstance(code, str)
|
||||
assert len(code) > 0
|
||||
assert isinstance(context.code_doc.content, str)
|
||||
assert len(context.code_doc.content) > 0
|
||||
|
||||
captured = capfd.readouterr()
|
||||
print(f"输出内容: {captured.out}")
|
||||
|
|
|
|||
|
|
@ -9,19 +9,24 @@
|
|||
import pytest
|
||||
|
||||
from metagpt.actions import UserRequirement
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import DOCS_FILE_REPO, PRDS_FILE_REPO, REQUIREMENT_FILENAME
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles.product_manager import ProductManager
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_prd():
|
||||
product_manager = ProductManager()
|
||||
requirements = "开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结"
|
||||
await FileRepository.save_file(filename=REQUIREMENT_FILENAME, content=requirements, relative_path=DOCS_FILE_REPO)
|
||||
prd = await product_manager.run(Message(content=requirements, cause_by=UserRequirement))
|
||||
logger.info(requirements)
|
||||
logger.info(prd)
|
||||
|
||||
# Assert the prd is not None or empty
|
||||
assert prd is not None
|
||||
assert prd != ""
|
||||
assert prd.content != ""
|
||||
assert CONFIG.git_repo.new_file_repository(relative_path=PRDS_FILE_REPO).changed_files
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import pytest
|
|||
|
||||
from metagpt.actions.write_test import WriteTest
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Document, TestingContext
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -24,22 +25,17 @@ async def test_write_test():
|
|||
def generate(self, max_y: int, max_x: int):
|
||||
self.position = (random.randint(1, max_y - 1), random.randint(1, max_x - 1))
|
||||
"""
|
||||
context = TestingContext(filename="food.py", code_doc=Document(filename="food.py", content=code))
|
||||
write_test = WriteTest(context=context)
|
||||
|
||||
write_test = WriteTest()
|
||||
|
||||
test_code = await write_test.run(
|
||||
code_to_test=code,
|
||||
test_file_name="test_food.py",
|
||||
source_file_path="/some/dummy/path/cli_snake_game/cli_snake_game/food.py",
|
||||
workspace="/some/dummy/path/cli_snake_game",
|
||||
)
|
||||
logger.info(test_code)
|
||||
context = await write_test.run()
|
||||
logger.info(context.json())
|
||||
|
||||
# We cannot exactly predict the generated test cases, but we can check if it is a string and if it is not empty
|
||||
assert isinstance(test_code, str)
|
||||
assert "from cli_snake_game.food import Food" in test_code
|
||||
assert "class TestFood(unittest.TestCase)" in test_code
|
||||
assert "def test_generate" in test_code
|
||||
assert isinstance(context.test_doc.content, str)
|
||||
assert "from food import Food" in context.test_doc.content
|
||||
assert "class TestFood(unittest.TestCase)" in context.test_doc.content
|
||||
assert "def test_generate" in context.test_doc.content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ PRD = '''## 原始需求
|
|||
```
|
||||
'''
|
||||
|
||||
SYSTEM_DESIGN = """## project_name
|
||||
SYSTEM_DESIGN = """## Project name
|
||||
```python
|
||||
"smart_search_engine"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ async def test_file_repo():
|
|||
assert {"a.txt"} == await file_repo.get_changed_dependency("b.txt")
|
||||
await file_repo.save("d/e.txt", "EEE")
|
||||
assert ["d/e.txt"] == file_repo.get_change_dir_files("d")
|
||||
assert set(file_repo.all_files) == {"a.txt", "b.txt", "d/e.txt"}
|
||||
await file_repo.delete("d/e.txt")
|
||||
await file_repo.delete("d/e.txt") # delete twice
|
||||
assert set(file_repo.all_files) == {"a.txt", "b.txt"}
|
||||
|
||||
git_repo.delete_repository()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue