feat: +tool log

This commit is contained in:
莘权 马 2024-03-31 10:53:08 +08:00
parent 09f75cbaf2
commit 1ed64bcd07
5 changed files with 126 additions and 3 deletions

View file

@ -246,6 +246,7 @@ class IntentDetect(Action):
async def _merge(self):
self.result = IntentDetectResult(clarifications=self._dialog_intentions.clarifications)
distinct = {}
# Consolidate intentions under the same SOP.
for i in self._intent_to_sops:
if i.sop_index == 0: # 1-based index
refs = self._get_intent_ref(i.intent)
@ -260,12 +261,14 @@ class IntentDetect(Action):
if len(intents) > 1:
merge_intents[sop_index] = intents
continue
# Merge single intention
refs = self._get_intent_ref(intents[0])
item = IntentDetectIntentionSOP(intention=IntentDetectIntentionRef(intent=intents[0], refs=refs))
sop_index = intent_to_sops.get(intents[0])
item.sop = SOP_CONFIG[sop_index - 1] # 1-based index
self.result.intentions.append(item)
# Merge repetitive intentions into one
for sop_index, intents in merge_intents.items():
intent_ref = IntentDetectIntentionRef(intent="\n".join(intents), refs=[])
for i in intents:
@ -338,6 +341,7 @@ class LightIntentDetect(IntentDetect):
async def _merge(self):
self.result = IntentDetectResult(clarifications=[])
distinct = {}
# Consolidate intentions under the same SOP.
for i in self._intent_to_sops:
if i.sop_index == 0: # 1-based index
ref = self._get_intent_ref(i.intent)
@ -352,6 +356,7 @@ class LightIntentDetect(IntentDetect):
if len(intents) > 1:
merge_intents[sop_index] = intents
continue
# Merge single intention
ref = self._get_intent_ref(intents[0])
item = IntentDetectIntentionSOP(intention=IntentDetectIntentionRef(intent=intents[0], refs=[ref]))
sop_index = intent_to_sops.get(intents[0]) # 1-based
@ -359,6 +364,7 @@ class LightIntentDetect(IntentDetect):
item.sop = SOP_CONFIG[sop_index - 1] # 1-based index
self.result.intentions.append(item)
# Merge repetitive intentions into one
for sop_index, intents in merge_intents.items():
intent_ref = IntentDetectIntentionRef(intent="\n".join(intents), refs=[])
for i in intents:

View file

@ -11,10 +11,34 @@ from __future__ import annotations
import sys
from datetime import datetime
from functools import partial
from typing import List
from loguru import logger as _logger
from pydantic import BaseModel, Field
from metagpt.const import METAGPT_ROOT
from metagpt.schema import BaseEnum
class ToolOutputItem(BaseModel):
type_: str = Field(alias="type", default="str", description="Data type of `value` field.")
name: str
value: str
class ToolName(str, BaseEnum):
Terminal = "Terminal"
Plan = "Plan"
Browser = "Browser"
Files = "Files"
WritePRD = "WritePRD"
WriteDesign = "WriteDesign"
WriteProjectPlan = "WriteProjectPlan"
WriteCode = "WriteCode"
WriteUntTest = "WriteUntTest"
FixBug = "FixBug"
GitArchive = "GitArchive"
ImportRepo = "ImportRepo"
def define_log_level(print_level="INFO", logfile_level="DEBUG", name: str = None):
@ -36,9 +60,13 @@ def log_llm_stream(msg):
_llm_stream_log(msg)
def log_tool_output(output: dict, tool_name: str = ""):
def log_tool_output(output: ToolOutputItem | List[ToolOutputItem], tool_name: str = ""):
"""interface for logging tool output, can be set to log tool output in different ways to different places with set_tool_output_logfunc"""
_tool_output_log(output)
if not _tool_output_log or not output:
return
outputs = output if isinstance(output, list) else [output]
_tool_output_log(output=[i.model_dump() for i in outputs], tool_name=tool_name)
def set_llm_stream_logfunc(func):

View file

@ -22,9 +22,11 @@ class MGX(DataInterpreter):
# Extract intent and sop prompt
intention_ref = ""
for i in todo.result.intentions:
if not intention_ref:
intention_ref = "\n".join(i.intention.refs)
if not i.sop:
continue
intention_ref = "\n".join(i.intention.refs)
self.intents[intention_ref] = i.sop.sop
logger.debug(f"refs: {intention_ref}, sop: {i.sop.sop}")
sop_str = "\n".join([f"- {i}" for i in i.sop.sop])

View file

@ -21,6 +21,7 @@ import os.path
import uuid
from abc import ABC
from asyncio import Queue, QueueEmpty, wait_for
from enum import Enum
from json import JSONDecodeError
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, Union
@ -785,3 +786,26 @@ class UMLClassView(UMLClassMeta):
method.return_type = i.return_args.type_
class_view.methods.append(method)
return class_view
class BaseEnum(Enum):
"""Base class for enums."""
def __new__(cls, value, desc=None):
"""
Construct an instance of the enum member.
Args:
cls: The class.
value: The value of the enum member.
desc: The description of the enum member. Defaults to None.
"""
if issubclass(cls, str):
obj = str.__new__(cls, value)
elif issubclass(cls, int):
obj = int.__new__(cls, value)
else:
obj = object.__new__(cls)
obj._value_ = value
obj.desc = desc
return obj

View file

@ -6,6 +6,7 @@ from pathlib import Path
from typing import Optional
from metagpt.const import BUGFIX_FILENAME, REQUIREMENT_FILENAME
from metagpt.logs import ToolName, ToolOutputItem, log_tool_output
from metagpt.schema import BugFixContext, Message
from metagpt.tools.tool_registry import register_tool
from metagpt.utils.common import any_to_str
@ -48,6 +49,17 @@ async def write_prd(idea: str, project_path: Optional[str | Path] = None) -> Pat
role = ProductManager(context=ctx)
msg = await role.run(with_message=Message(content=idea, cause_by=UserRequirement))
await role.run(with_message=msg)
outputs = [
ToolOutputItem(name="PRD File", value=str(ctx.repo.docs.prd.workdir / i))
for i in ctx.repo.docs.prd.changed_files.keys()
]
for i in ctx.repo.resources.competitive_analysis.changed_files.keys():
outputs.append(
ToolOutputItem(name="Competitive Analysis", value=str(ctx.repo.resources.competitive_analysis.workdir / i))
)
log_tool_output(output=outputs, tool_name=ToolName.WritePRD)
return ctx.repo.docs.prd.workdir
@ -79,6 +91,21 @@ async def write_design(prd_path: str | Path) -> Path:
role = Architect(context=ctx)
await role.run(with_message=Message(content="", cause_by=WritePRD))
outputs = [
ToolOutputItem(name="Intermedia Design File", value=str(ctx.repo.docs.system_design.workdir / i))
for i in ctx.repo.docs.system_design.changed_files.keys()
]
for i in ctx.repo.resources.system_design.changed_files.keys():
outputs.append(ToolOutputItem(name="Design File", value=str(ctx.repo.resources.system_design.workdir / i)))
for i in ctx.repo.resources.data_api_design.changed_files.keys():
outputs.append(
ToolOutputItem(name="Class Diagram File", value=str(ctx.repo.resources.data_api_design.workdir / i))
)
for i in ctx.repo.resources.seq_flow.changed_files.keys():
outputs.append(ToolOutputItem(name="Sequence Diagram File", value=str(ctx.repo.resources.seq_flow.workdir / i)))
log_tool_output(output=outputs, tool_name=ToolName.WriteDesign)
return ctx.repo.docs.system_design.workdir
@ -110,6 +137,13 @@ async def write_project_plan(system_design_path: str | Path) -> Path:
role = ProjectManager(context=ctx)
await role.run(with_message=Message(content="", cause_by=WriteDesign))
outputs = [
ToolOutputItem(name="Project Plan", value=str(ctx.repo.docs.task.workdir / i))
for i in ctx.repo.docs.task.changed_files.key()
]
log_tool_output(output=outputs, tool_name=ToolName.WriteProjectPlan)
return ctx.repo.docs.task.workdir
@ -153,6 +187,13 @@ async def write_codes(task_path: str | Path, inc: bool = False) -> Path:
me = {any_to_str(role), role.name}
while me.intersection(msg.send_to):
msg = await role.run(with_message=msg)
outputs = [
ToolOutputItem(name="Source File", value=str(ctx.repo.srcs.workdir / i))
for i in ctx.repo.srcs.changed_files.keys()
]
log_tool_output(output=outputs, tool_name=ToolName.WriteCode)
return ctx.repo.srcs.workdir
@ -192,6 +233,13 @@ async def run_qa_test(src_path: str | Path) -> Path:
while not env.is_idle:
await env.run()
outputs = [
ToolOutputItem(name="Unit Test File", value=str(ctx.repo.tests.workdir / i))
for i in ctx.repo.tests.changed_files.keys()
]
log_tool_output(output=outputs, tool_name=ToolName.WriteUntTest)
return ctx.repo.tests.workdir
@ -237,6 +285,13 @@ async def fix_bug(project_path: str | Path, issue: str) -> Path:
me = {any_to_str(role), role.name}
while me.intersection(msg.send_to):
msg = await role.run(with_message=msg)
outputs = [
ToolOutputItem(name="Changed File", value=str(ctx.repo.srcs.workdir / i))
for i in ctx.repo.srcs.changed_files.keys()
]
log_tool_output(output=outputs, tool_name=ToolName.FixBug)
return project_path
@ -269,6 +324,10 @@ async def git_archive(project_path: str | Path) -> str:
ctx = Context()
ctx.set_repo_dir(project_path)
ctx.git_repo.archive()
outputs = [ToolOutputItem(name="Git Commit", value=str(ctx.repo.workdir))]
log_tool_output(output=outputs, tool_name=ToolName.GitArchive)
return ctx.git_repo.log()
@ -298,4 +357,8 @@ async def import_git_repo(url: str) -> Path:
ctx = Context()
action = ImportRepo(repo_path=url, context=ctx)
await action.run()
outputs = [ToolOutputItem(name="MetaGPT Project", value=str(ctx.repo.workdir))]
log_tool_output(output=outputs, tool_name=ToolName.ImportRepo)
return ctx.repo.workdir