Skip to content

Commit 197da3b

Browse files
committed
add tool_* and action_* into core
1 parent f780483 commit 197da3b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3860
-917
lines changed

metagpt/core/actions/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@File : __init__.py
77
"""
88

9-
from metagpt.core.actions.base import Action
9+
from metagpt.core.actions.action import Action
1010
from metagpt.core.actions.action_output import ActionOutput
1111

1212
__all__ = [

metagpt/core/actions/action.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
@Time : 2023/5/11 14:43
5+
@Author : alexanderwu
6+
@File : action.py
7+
"""
8+
9+
from __future__ import annotations
10+
11+
from typing import Any, Optional, Union
12+
13+
from pydantic import BaseModel, ConfigDict, Field, model_validator
14+
15+
from metagpt.core.actions.action_node import ActionNode
16+
from metagpt.core.configs.models_config import ModelsConfig
17+
from metagpt.core.context_mixin import ContextMixin
18+
from metagpt.core.provider.llm_provider_registry import create_llm_instance
19+
from metagpt.core.schema import (
20+
CodePlanAndChangeContext,
21+
CodeSummarizeContext,
22+
CodingContext,
23+
RunCodeContext,
24+
SerializationMixin,
25+
TestingContext,
26+
)
27+
28+
29+
class Action(SerializationMixin, ContextMixin, BaseModel):
30+
model_config = ConfigDict(arbitrary_types_allowed=True)
31+
32+
name: str = ""
33+
i_context: Union[
34+
dict, CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext, CodePlanAndChangeContext, str, None
35+
] = ""
36+
prefix: str = "" # aask*时会加上prefix,作为system_message
37+
desc: str = "" # for skill manager
38+
node: ActionNode = Field(default=None, exclude=True)
39+
# The model name or API type of LLM of the `models` in the `config2.yaml`;
40+
# Using `None` to use the `llm` configuration in the `config2.yaml`.
41+
llm_name_or_type: Optional[str] = None
42+
43+
@model_validator(mode="after")
44+
@classmethod
45+
def _update_private_llm(cls, data: Any) -> Any:
46+
config = ModelsConfig.default().get(data.llm_name_or_type)
47+
if config:
48+
llm = create_llm_instance(config)
49+
llm.cost_manager = data.llm.cost_manager
50+
data.llm = llm
51+
return data
52+
53+
@property
54+
def prompt_schema(self):
55+
return self.config.prompt_schema
56+
57+
@property
58+
def project_name(self):
59+
return self.config.project_name
60+
61+
@project_name.setter
62+
def project_name(self, value):
63+
self.config.project_name = value
64+
65+
@property
66+
def project_path(self):
67+
return self.config.project_path
68+
69+
@model_validator(mode="before")
70+
@classmethod
71+
def set_name_if_empty(cls, values):
72+
if "name" not in values or not values["name"]:
73+
values["name"] = cls.__name__
74+
return values
75+
76+
@model_validator(mode="before")
77+
@classmethod
78+
def _init_with_instruction(cls, values):
79+
if "instruction" in values:
80+
name = values["name"]
81+
i = values.pop("instruction")
82+
values["node"] = ActionNode(key=name, expected_type=str, instruction=i, example="", schema="raw")
83+
return values
84+
85+
def set_prefix(self, prefix):
86+
"""Set prefix for later usage"""
87+
self.prefix = prefix
88+
self.llm.system_prompt = prefix
89+
if self.node:
90+
self.node.llm = self.llm
91+
return self
92+
93+
def __str__(self):
94+
return self.__class__.__name__
95+
96+
def __repr__(self):
97+
return self.__str__()
98+
99+
async def _aask(self, prompt: str, system_msgs: Optional[list[str]] = None) -> str:
100+
"""Append default prefix"""
101+
return await self.llm.aask(prompt, system_msgs)
102+
103+
async def _run_action_node(self, *args, **kwargs):
104+
"""Run action node"""
105+
msgs = args[0]
106+
context = "## History Messages\n"
107+
context += "\n".join([f"{idx}: {i}" for idx, i in enumerate(reversed(msgs))])
108+
return await self.node.fill(req=context, llm=self.llm)
109+
110+
async def run(self, *args, **kwargs):
111+
"""Run action"""
112+
if self.node:
113+
return await self._run_action_node(*args, **kwargs)
114+
raise NotImplementedError("The run method should be implemented in a subclass.")
115+
116+
def override_context(self):
117+
"""Set `private_context` and `context` to the same `Context` object."""
118+
if not self.private_context:
119+
self.private_context = self.context
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
@Time : 2024/1/30 13:52
5+
@Author : alexanderwu
6+
@File : action_graph.py
7+
"""
8+
from __future__ import annotations
9+
10+
# from metagpt.actions.action_node import ActionNode
11+
12+
13+
class ActionGraph:
14+
"""ActionGraph: a directed graph to represent the dependency between actions."""
15+
16+
def __init__(self):
17+
self.nodes = {}
18+
self.edges = {}
19+
self.execution_order = []
20+
21+
def add_node(self, node):
22+
"""Add a node to the graph"""
23+
self.nodes[node.key] = node
24+
25+
def add_edge(self, from_node: "ActionNode", to_node: "ActionNode"):
26+
"""Add an edge to the graph"""
27+
if from_node.key not in self.edges:
28+
self.edges[from_node.key] = []
29+
self.edges[from_node.key].append(to_node.key)
30+
from_node.add_next(to_node)
31+
to_node.add_prev(from_node)
32+
33+
def topological_sort(self):
34+
"""Topological sort the graph"""
35+
visited = set()
36+
stack = []
37+
38+
def visit(k):
39+
if k not in visited:
40+
visited.add(k)
41+
if k in self.edges:
42+
for next_node in self.edges[k]:
43+
visit(next_node)
44+
stack.insert(0, k)
45+
46+
for key in self.nodes:
47+
visit(key)
48+
49+
self.execution_order = stack

0 commit comments

Comments
 (0)