2025-03-28 11:40:46 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
import os
|
|
|
|
|
|
2025-03-24 19:07:51 +08:00
|
|
|
from typing import Type, Annotated, Optional, TypedDict
|
2025-04-09 11:47:08 +08:00
|
|
|
from enum import Enum
|
2025-03-24 19:07:51 +08:00
|
|
|
from abc import abstractmethod
|
2025-03-28 11:40:46 +08:00
|
|
|
from dataclasses import dataclass, fields, field
|
2025-03-24 19:07:51 +08:00
|
|
|
|
2025-03-28 11:40:46 +08:00
|
|
|
from langchain_core.runnables import RunnableConfig
|
2025-03-25 05:40:07 +08:00
|
|
|
from langchain_core.messages import BaseMessage
|
2025-03-24 19:07:51 +08:00
|
|
|
from langgraph.graph.state import CompiledStateGraph
|
2025-03-25 05:40:07 +08:00
|
|
|
from langgraph.graph.message import add_messages
|
2025-03-24 19:07:51 +08:00
|
|
|
|
2025-03-29 17:33:09 +08:00
|
|
|
from src.config import SimpleConfig
|
2025-04-02 13:00:25 +08:00
|
|
|
from src.utils import logger
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
|
|
|
class State(TypedDict):
|
|
|
|
|
"""
|
|
|
|
|
定义一个基础 State 供 各类 graph 继承, 其中:
|
|
|
|
|
1. messages 为所有 graph 的核心信息队列, 所有聊天工作流均应该将关键信息补充到此队列中;
|
|
|
|
|
2. history 为所有工作流单次启动时获取 history_len 的 messages 所用(节约成本, 及防止单轮对话 tokens 占用长度达到 llm 支持上限),
|
|
|
|
|
history 中的信息理应是可以被丢弃的.
|
|
|
|
|
"""
|
|
|
|
|
messages: Annotated[list[BaseMessage], add_messages]
|
|
|
|
|
history: Optional[list[BaseMessage]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(kw_only=True)
|
2025-04-02 13:00:25 +08:00
|
|
|
class Configuration(dict):
|
2025-03-24 19:07:51 +08:00
|
|
|
"""
|
|
|
|
|
定义一个基础 Configuration 供 各类 graph 继承
|
|
|
|
|
"""
|
2025-03-28 11:40:46 +08:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_runnable_config(
|
|
|
|
|
cls, config: Optional[RunnableConfig] = None
|
|
|
|
|
) -> Configuration:
|
|
|
|
|
"""Create a Configuration instance from a RunnableConfig object."""
|
|
|
|
|
configurable = (config.get("configurable") or {}) if config else {}
|
|
|
|
|
_fields = {f.name for f in fields(cls) if f.init}
|
|
|
|
|
return cls(**{k: v for k, v in configurable.items() if k in _fields})
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def to_dict(cls):
|
2025-04-02 00:00:04 +08:00
|
|
|
# 创建一个实例来处理 default_factory
|
|
|
|
|
instance = cls()
|
2025-04-02 13:00:25 +08:00
|
|
|
confs = {}
|
|
|
|
|
configurable_items = {}
|
|
|
|
|
for f in fields(cls):
|
|
|
|
|
if f.init and not f.metadata.get("hide", False):
|
|
|
|
|
value = getattr(instance, f.name)
|
|
|
|
|
if callable(value) and hasattr(value, "__call__"):
|
|
|
|
|
confs[f.name] = value()
|
|
|
|
|
else:
|
|
|
|
|
confs[f.name] = value
|
|
|
|
|
|
|
|
|
|
if f.metadata.get("configurable"):
|
|
|
|
|
configurable_items[f.name] = {
|
|
|
|
|
"type": f.type.__name__,
|
|
|
|
|
"name": f.metadata.get("name", f.name),
|
|
|
|
|
"options": f.metadata.get("options", []),
|
|
|
|
|
"default": f.default,
|
|
|
|
|
"description": f.metadata.get("description", ""),
|
|
|
|
|
}
|
|
|
|
|
confs["configurable_items"] = configurable_items
|
|
|
|
|
return confs
|
2025-03-25 05:40:07 +08:00
|
|
|
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseAgent():
|
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
"""
|
|
|
|
|
定义一个基础 Agent 供 各类 graph 继承
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
name: str = field(default="base_agent")
|
|
|
|
|
description: str = field(default="base_agent")
|
|
|
|
|
config_schema: Configuration = Configuration
|
|
|
|
|
requirements: list[str]
|
|
|
|
|
|
2025-03-28 11:40:46 +08:00
|
|
|
def __init__(self, **kwargs):
|
2025-03-31 22:20:29 +08:00
|
|
|
self.check_requirements()
|
|
|
|
|
|
2025-04-01 22:16:07 +08:00
|
|
|
@classmethod
|
|
|
|
|
def get_info(cls):
|
|
|
|
|
return {
|
|
|
|
|
"name": cls.name,
|
|
|
|
|
"description": cls.description,
|
|
|
|
|
"config_schema": cls.config_schema.to_dict(),
|
|
|
|
|
"requirements": cls.requirements if hasattr(cls, "requirements") else [],
|
2025-04-02 13:00:25 +08:00
|
|
|
"all_tools": cls.all_tools if hasattr(cls, "all_tools") else [],
|
2025-04-01 22:16:07 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-31 22:20:29 +08:00
|
|
|
def check_requirements(self):
|
|
|
|
|
if not hasattr(self, "requirements") or not self.requirements:
|
|
|
|
|
return
|
|
|
|
|
for requirement in self.requirements:
|
|
|
|
|
if requirement not in os.environ:
|
2025-04-09 12:05:05 +08:00
|
|
|
raise ValueError(f"没有配置{requirement} 环境变量,请在 src/.env 文件中配置,并重新启动服务")
|
2025-03-31 22:20:29 +08:00
|
|
|
|
|
|
|
|
def stream_values(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs):
|
|
|
|
|
graph = self.get_graph(config_schema=config_schema, **kwargs)
|
|
|
|
|
for event in graph.stream({"messages": messages}, stream_mode="values", config=config_schema):
|
|
|
|
|
yield event["messages"]
|
|
|
|
|
|
|
|
|
|
def stream_messages(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs):
|
|
|
|
|
graph = self.get_graph(config_schema=config_schema, **kwargs)
|
|
|
|
|
|
2025-04-02 00:00:04 +08:00
|
|
|
for msg, metadata in graph.stream({"messages": messages}, stream_mode="messages", config=config_schema):
|
|
|
|
|
yield msg, metadata
|
2025-03-24 19:07:51 +08:00
|
|
|
|
|
|
|
|
@abstractmethod
|
2025-03-31 22:20:29 +08:00
|
|
|
def get_graph(self, **kwargs) -> CompiledStateGraph:
|
2025-03-24 19:07:51 +08:00
|
|
|
pass
|