mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-11 15:15:18 +02:00
feat: +tool log
This commit is contained in:
parent
09f75cbaf2
commit
1ed64bcd07
5 changed files with 126 additions and 3 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue