mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-14 09:43:03 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10fb689c83 | |||
| 86548903a0 | |||
| cf1578fd14 | |||
| 35b7dc7cda | |||
| b1ed79ae9d | |||
| 507917157d | |||
| ca3ffca4ea | |||
| 3c41abbba8 | |||
| 64a7ed261e | |||
| ade922cbc2 | |||
| effa1df7fa | |||
| 954095aa55 | |||
| c9c9b05f18 | |||
| dd10b00fb6 | |||
| 98d830d08b | |||
| 192ae1bf5f | |||
| a1d3c1e295 | |||
| 9302417e58 | |||
| e9053a4e88 | |||
| f5c37f26a5 | |||
| d11d39ea81 | |||
| 407181db05 |
22
.gitignore
vendored
22
.gitignore
vendored
@@ -38,14 +38,16 @@ build/
|
||||
.DS_Store
|
||||
/data/
|
||||
/config/
|
||||
/src/test/java/memory/test.json
|
||||
/src/test/java/memory/result/input1.json
|
||||
/src/test/java/memory/result/input2.json
|
||||
/src/test/java/memory/result/output1.json
|
||||
/src/test/java/memory/result/output2.json
|
||||
/src/test/java/memory/result/total_input.json
|
||||
/src/test/java/memory/result/input3.json
|
||||
/src/test/java/memory/result/input4.json
|
||||
/src/test/java/memory/result/primary_input.json
|
||||
/src/main/resources/prompt/module/memory/topic_extractor.json.bak
|
||||
/Partner-Core/src/main/java/src/test/java/memory/test.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/input1.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/input2.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/output1.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/output2.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/total_input.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/input3.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/input4.json
|
||||
/Partner-Core/src/main/java/src/test/java/memory/result/primary_input.json
|
||||
/Partner-Core/src/main/java/src/main/resources/prompt/module/memory/topic_extractor.json.bak
|
||||
/backup/
|
||||
/Partner-Main/src/test/java/text/test.json
|
||||
/CLAUDE.md
|
||||
|
||||
1
.idea/.gitignore
generated
vendored
1
.idea/.gitignore
generated
vendored
@@ -6,3 +6,4 @@
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
/inspectionProfiles/Project_Default.xml
|
||||
|
||||
7
.idea/dictionaries/project.xml
generated
Normal file
7
.idea/dictionaries/project.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>zuper</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
8
.idea/encodings.xml
generated
8
.idea/encodings.xml
generated
@@ -1,6 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Api/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/java/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Main/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/Partner-Test-Demo/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
|
||||
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@@ -1,5 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<list size="6">
|
||||
<item index="0" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.Capability" />
|
||||
<item index="1" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityCore" />
|
||||
<item index="2" class="java.lang.String" itemvalue="work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityMethod" />
|
||||
<item index="3" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CapabilityMethod" />
|
||||
<item index="4" class="java.lang.String" itemvalue="work.slhaf.partner.api.capability.annotation.CoordinateManager" />
|
||||
<item index="5" class="java.lang.String" itemvalue="work.slhaf.partner.api.register.capability.annotation.Capability" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
|
||||
33
.xcodemap/config/xcodemap-class-filter.yaml
Normal file
33
.xcodemap/config/xcodemap-class-filter.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
autoDetectedPackages:
|
||||
- factory
|
||||
- module
|
||||
- work.slhaf
|
||||
enableAutoDetect: true
|
||||
entryDisplayConfig:
|
||||
excludedPathPatterns: []
|
||||
skipJsCss: true
|
||||
funcDisplayConfig:
|
||||
skipConstructors: false
|
||||
skipFieldAccess: true
|
||||
skipFieldChange: true
|
||||
skipGetters: false
|
||||
skipNonProjectPackages: false
|
||||
skipPrivateMethods: false
|
||||
skipSetters: false
|
||||
ignoreSameClassCall: null
|
||||
ignoreSamePackageCall: null
|
||||
includedPackagePrefixes: null
|
||||
includedParentClasses: null
|
||||
maxColSize: 32
|
||||
maxNumFirst: 12
|
||||
maxNumFirstImportant: 1024
|
||||
maxNumHash: 3
|
||||
maxNumHashImportant: 256
|
||||
maxObjectDepth: 4
|
||||
maxStrSize: 4096
|
||||
name: xcodemap-filter
|
||||
openMainWindow: true
|
||||
recordMode: manual
|
||||
sourceDisplayConfig:
|
||||
color: blue
|
||||
startOnDebug: false
|
||||
74
Partner-Api/pom.xml
Normal file
74
Partner-Api/pom.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>work.slhaf</groupId>
|
||||
<artifactId>Partner</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>Partner-Api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.17.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.36</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.13.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.5.17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.18.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.36</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.56</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,114 @@
|
||||
package work.slhaf.partner.api.agent;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.AgentRegisterFactory;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentExceptionCallback;
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
||||
import work.slhaf.partner.api.agent.runtime.exception.GlobalExceptionHandler;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.AgentGateway;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class Agent {
|
||||
|
||||
public static AgentGatewayStep newAgent(Class<?> clazz) {
|
||||
if (clazz == null) {
|
||||
throw new AgentLaunchFailedException("Agent class 和 interaction flow context 不能为 null");
|
||||
}
|
||||
return new AgentApp(clazz);
|
||||
}
|
||||
|
||||
public interface AgentGatewayStep {
|
||||
AgentStep setGateway(AgentGateway gateway);
|
||||
}
|
||||
|
||||
public interface AgentStep {
|
||||
AgentStep addBeforeLaunchRunners(Runnable... runners);
|
||||
|
||||
AgentStep addAfterLaunchRunners(Runnable... runners);
|
||||
|
||||
AgentStep setAgentConfigManager(AgentConfigManager agentConfigManager);
|
||||
|
||||
AgentStep setAgentExceptionCallback(AgentExceptionCallback agentExceptionCallback);
|
||||
|
||||
AgentStep addScanPackage(String packageName);
|
||||
|
||||
AgentStep addScanDir(String externalPackagePath);
|
||||
|
||||
void launch();
|
||||
}
|
||||
|
||||
|
||||
public static class AgentApp implements AgentStep, AgentGatewayStep {
|
||||
|
||||
private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
|
||||
private final List<Runnable> beforeLaunchRunners = new ArrayList<>();
|
||||
private final List<Runnable> afterLaunchRunners = new ArrayList<>();
|
||||
private AgentGateway gateway;
|
||||
private final Class<?> applicationClass;
|
||||
|
||||
private AgentApp(Class<?> clazz) {
|
||||
this.applicationClass = clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep setGateway(AgentGateway gateway) {
|
||||
this.gateway = gateway;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep addBeforeLaunchRunners(Runnable... runners) {
|
||||
this.beforeLaunchRunners.addAll(List.of(runners));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep addAfterLaunchRunners(Runnable... runners) {
|
||||
this.afterLaunchRunners.addAll(List.of(runners));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep setAgentConfigManager(AgentConfigManager agentConfigManager) {
|
||||
AgentConfigManager.setINSTANCE(agentConfigManager);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep setAgentExceptionCallback(AgentExceptionCallback agentExceptionCallback) {
|
||||
GlobalExceptionHandler.setExceptionCallback(agentExceptionCallback);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep addScanPackage(String packageName) {
|
||||
AgentRegisterFactory.addScanPackage(packageName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentStep addScanDir(String externalPackagePath) {
|
||||
AgentRegisterFactory.addScanDir(externalPackagePath);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
launchRunners(beforeLaunchRunners);
|
||||
AgentRegisterFactory.launch(applicationClass.getPackageName());
|
||||
executorService.execute(() -> gateway.launch());
|
||||
launchRunners(afterLaunchRunners);
|
||||
}
|
||||
|
||||
private void launchRunners(List<Runnable> runners) {
|
||||
for (Runnable runner : runners) {
|
||||
executorService.execute(runner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package work.slhaf.partner.api.agent.factory;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.CapabilityFactoryExecuteFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public abstract class AgentBaseFactory {
|
||||
public void execute(AgentRegisterContext context) {
|
||||
try {
|
||||
setVariables(context);
|
||||
run();
|
||||
} catch (Exception e) {
|
||||
throw new CapabilityFactoryExecuteFailedException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void setVariables(AgentRegisterContext context);
|
||||
|
||||
protected abstract void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package work.slhaf.partner.api.agent.factory;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import org.reflections.util.ClasspathHelper;
|
||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityCheckFactory;
|
||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityInjectFactory;
|
||||
import work.slhaf.partner.api.agent.factory.capability.CapabilityRegisterFactory;
|
||||
import work.slhaf.partner.api.agent.factory.config.ConfigLoaderFactory;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.exception.ExternalModuleLoadFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.exception.ExternalModulePathNotExistException;
|
||||
import work.slhaf.partner.api.agent.factory.module.ModuleCheckFactory;
|
||||
import work.slhaf.partner.api.agent.factory.module.ModuleInitHookExecuteFactory;
|
||||
import work.slhaf.partner.api.agent.factory.module.ModuleProxyFactory;
|
||||
import work.slhaf.partner.api.agent.factory.module.ModuleRegisterFactory;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.data.AgentContext;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AgentRegisterFactory {
|
||||
|
||||
private static final List<URL> urls = new ArrayList<>();
|
||||
|
||||
private AgentRegisterFactory() {
|
||||
}
|
||||
|
||||
public static void launch(String packageName) {
|
||||
urls.addAll(packageNameToURL(packageName));
|
||||
AgentRegisterContext registerContext = new AgentRegisterContext(urls);
|
||||
//流程
|
||||
//0. 加载配置
|
||||
new ConfigLoaderFactory().execute(registerContext);
|
||||
//1. 注册并检查Capability
|
||||
new CapabilityRegisterFactory().execute(registerContext);
|
||||
new CapabilityCheckFactory().execute(registerContext);
|
||||
//2. 注册并检查Module
|
||||
new ModuleCheckFactory().execute(registerContext);
|
||||
new ModuleRegisterFactory().execute(registerContext);
|
||||
//3. 为module通过动态代理添加PostHook逻辑并进行实例化
|
||||
new ModuleProxyFactory().execute(registerContext);
|
||||
//. 先一步注入Capability,避免因前hook逻辑存在针对能力的引用而报错
|
||||
new CapabilityInjectFactory().execute(registerContext);
|
||||
//. 执行模块PreHook逻辑
|
||||
new ModuleInitHookExecuteFactory().execute(registerContext);
|
||||
|
||||
List<MetaModule> moduleList = registerContext.getModuleFactoryContext().getModuleList();
|
||||
AgentConfigManager.INSTANCE.moduleEnabledStatusFilterAndRecord(moduleList);
|
||||
|
||||
BeanUtil.copyProperties(registerContext, AgentContext.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加可扫描包
|
||||
*
|
||||
* @param packageName 指定的包名
|
||||
*/
|
||||
public static void addScanPackage(String packageName) {
|
||||
urls.addAll(packageNameToURL(packageName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加外部模块目录
|
||||
*
|
||||
* @param externalPackagePath 指定的外部模块目录路径
|
||||
*/
|
||||
public static void addScanDir(String externalPackagePath) {
|
||||
File file = new File(externalPackagePath);
|
||||
if (!file.exists() || !file.isDirectory()) {
|
||||
throw new ExternalModulePathNotExistException("不存在的外部模块目录: " + externalPackagePath);
|
||||
}
|
||||
try {
|
||||
File[] files = file.listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
throw new ExternalModulePathNotExistException("外部模块目录为空: " + externalPackagePath);
|
||||
}
|
||||
for (File f : files) {
|
||||
if (f.getName().endsWith(".jar")) {
|
||||
urls.add(f.toURI().toURL());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ExternalModuleLoadFailedException("外部模块URL获取失败: " + externalPackagePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<URL> packageNameToURL(String packageName) {
|
||||
return ClasspathHelper.forPackage(packageName).stream().toList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.*;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.DuplicateCapabilityException;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCapabilityException;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCapabilityMethodException;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.UnMatchedCoordinatedMethodException;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||
import work.slhaf.partner.api.agent.util.AgentUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||
|
||||
/**
|
||||
* 执行<code>Capability</code>相关检查
|
||||
*/
|
||||
public class CapabilityCheckFactory extends AgentBaseFactory {
|
||||
|
||||
private Reflections reflections;
|
||||
private Set<Class<?>> cores;
|
||||
private Set<Class<?>> capabilities;
|
||||
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
||||
reflections = context.getReflections();
|
||||
cores = factoryContext.getCores();
|
||||
capabilities = factoryContext.getCapabilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
checkCountAndCapabilities();
|
||||
checkCapabilityMethods();
|
||||
checkCoordinatedMethods();
|
||||
checkInjectCapability();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查<code>@InjectCapability</code>注解是否只用在<code>@CapabilityHolder</code>所标识类的字段上
|
||||
*/
|
||||
private void checkInjectCapability() {
|
||||
reflections.getFieldsAnnotatedWith(InjectCapability.class).forEach(field -> {
|
||||
if (!field.getDeclaringClass().isAssignableFrom(CapabilityHolder.class)) {
|
||||
throw new UnMatchedCapabilityException("InjectCapability 注解只能用于 CapabilityHolder 注解所在类");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否包含协调方法,如果存在,则进一步检查是否存在<code>@CoordinateManager</code>提供对应的实现
|
||||
*/
|
||||
private void checkCoordinatedMethods() {
|
||||
//检查各个capability中是否含有ToCoordinated注解
|
||||
//如果含有,则需要查找AbstractCognationManager的子类,看这里是否有对应的Coordinated注解所在方法
|
||||
Set<String> methodsToCoordinated = capabilities.stream()
|
||||
.flatMap(capability -> Arrays.stream(capability.getDeclaredMethods()))
|
||||
.filter(method -> method.isAnnotationPresent(ToCoordinated.class))
|
||||
.map(method -> {
|
||||
String capabilityValue = method.getDeclaringClass().getAnnotation(Capability.class).value();
|
||||
return capabilityValue + "." + methodSignature(method);
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
if (!methodsToCoordinated.isEmpty()) {
|
||||
Set<Class<?>> subTypesOfAbsCM = reflections.getTypesAnnotatedWith(CoordinateManager.class);
|
||||
Set<String> methodsCoordinated = getMethodsCoordinated(subTypesOfAbsCM);
|
||||
if (!methodsCoordinated.equals(methodsToCoordinated)) {
|
||||
// 找出缺少的协调方法
|
||||
Set<String> missingMethods = new HashSet<>(methodsToCoordinated);
|
||||
missingMethods.removeAll(methodsCoordinated);
|
||||
|
||||
// 找出多余的协调方法
|
||||
Set<String> extraMethods = new HashSet<>(methodsCoordinated);
|
||||
extraMethods.removeAll(methodsToCoordinated);
|
||||
|
||||
// 抛出异常或记录错误
|
||||
if (!missingMethods.isEmpty()) {
|
||||
throw new UnMatchedCoordinatedMethodException("缺少协调方法: " + String.join(", ", missingMethods));
|
||||
}
|
||||
if (!extraMethods.isEmpty()) {
|
||||
throw new UnMatchedCoordinatedMethodException("发现多余的协调方法: " + String.join(", ", extraMethods));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getMethodsCoordinated(Set<Class<?>> classes) {
|
||||
Set<String> methodsCoordinated = new HashSet<>();
|
||||
for (Class<?> cm : classes) {
|
||||
Method[] methods = cm.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(Coordinated.class)) {
|
||||
methodsCoordinated.add(method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method));
|
||||
}
|
||||
}
|
||||
}
|
||||
return methodsCoordinated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看在<code>Capability</code>在对应的<core>CapabilityCore</core>中存在尚未实现的方法
|
||||
*/
|
||||
private void checkCapabilityMethods() {
|
||||
HashMap<String, List<Method>> capabilitiesMethods = getCapabilityMethods(capabilities);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Class<?> core : cores) {
|
||||
List<Method> methodsWithAnnotation = Arrays.stream(core.getMethods())
|
||||
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
||||
.toList();
|
||||
List<Method> capabilityMethods = capabilitiesMethods.get(core.getAnnotation(CapabilityCore.class).value());
|
||||
LackRecord lackRecord = checkMethodsMatched(methodsWithAnnotation, capabilityMethods);
|
||||
if (lackRecord.hasNotEmptyRecord()) {
|
||||
sb.append(lackRecord.toLackErrorMsg(core.getAnnotation(CapabilityCore.class).value()));
|
||||
}
|
||||
}
|
||||
if (!sb.isEmpty()) {
|
||||
throw new UnMatchedCapabilityMethodException(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private LackRecord checkMethodsMatched(List<Method> methodsWithAnnotation, List<Method> capabilityMethods) {
|
||||
Set<String> collectedMethodsWithAnnotation = methodsWithAnnotation.stream()
|
||||
.filter(method -> !method.isAnnotationPresent(ToCoordinated.class))
|
||||
.map(AgentUtil::methodSignature)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> collectedCapabilityMethods = capabilityMethods.stream()
|
||||
.filter(method -> !method.isAnnotationPresent(ToCoordinated.class))
|
||||
.map(AgentUtil::methodSignature)
|
||||
.collect(Collectors.toSet());
|
||||
return checkMethodsMatched(collectedMethodsWithAnnotation, collectedCapabilityMethods);
|
||||
}
|
||||
|
||||
private LackRecord checkMethodsMatched(Set<String> collectedMethodsWithAnnotation, Set<String> collectedCapabilityMethods) {
|
||||
List<String> coreLack = new ArrayList<>();
|
||||
List<String> capLack = new ArrayList<>();
|
||||
// 找出 core 中多余的方法
|
||||
for (String coreSig : collectedMethodsWithAnnotation) {
|
||||
if (!collectedCapabilityMethods.contains(coreSig)) {
|
||||
capLack.add(coreSig);
|
||||
}
|
||||
}
|
||||
// 找出 capability 中多余的方法
|
||||
for (String capSig : collectedCapabilityMethods) {
|
||||
if (!collectedMethodsWithAnnotation.contains(capSig)) {
|
||||
coreLack.add(capSig);
|
||||
}
|
||||
}
|
||||
return new LackRecord(coreLack, capLack);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private HashMap<String, List<Method>> getCapabilityMethods(Set<Class<?>> capabilities) {
|
||||
HashMap<String, List<Method>> capabilityMethods = new HashMap<>();
|
||||
capabilities.forEach(capability -> {
|
||||
capabilityMethods.put(capability.getAnnotation(Capability.class).value(), Arrays.stream(capability.getMethods()).toList());
|
||||
});
|
||||
return capabilityMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查<code>Capability</code>和<code>CapabilityCore</code>的数量和标识是否匹配
|
||||
*/
|
||||
private void checkCountAndCapabilities() {
|
||||
if (cores.size() != capabilities.size()) {
|
||||
throw new UnMatchedCapabilityException("Capability 注册异常: 已存在的CapabilityCore与Capability数量不匹配!");
|
||||
}
|
||||
if (!checkValuesMatched(cores, capabilities)) {
|
||||
throw new UnMatchedCapabilityException("Capability 注册异常: 已存在的CapabilityCore与Capability不匹配!");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkValuesMatched(Set<Class<?>> cores, Set<Class<?>> capabilities) {
|
||||
Set<String> coresValues = new HashSet<>();
|
||||
Set<String> capabilitiesValues = new HashSet<>();
|
||||
for (Class<?> core : cores) {
|
||||
CapabilityCore annotation = core.getAnnotation(CapabilityCore.class);
|
||||
if (annotation != null) {
|
||||
if (coresValues.contains(annotation.value())) {
|
||||
throw new DuplicateCapabilityException(String.format("Capability 注册异常: 重复的Capability核心: %s", annotation.value()));
|
||||
}
|
||||
coresValues.add(annotation.value());
|
||||
}
|
||||
}
|
||||
for (Class<?> capability : capabilities) {
|
||||
Capability annotation = capability.getAnnotation(Capability.class);
|
||||
if (annotation != null) {
|
||||
if (capabilitiesValues.contains(annotation.value())) {
|
||||
throw new DuplicateCapabilityException(String.format("Capability 注册异常: 重复的Capability接口: %s", annotation.value()));
|
||||
}
|
||||
capabilitiesValues.add(annotation.value());
|
||||
}
|
||||
}
|
||||
return coresValues.equals(capabilitiesValues);
|
||||
}
|
||||
|
||||
record LackRecord(List<String> coreLack, List<String> capLack) {
|
||||
public boolean hasNotEmptyRecord() {
|
||||
return !coreLack.isEmpty() || !capLack.isEmpty();
|
||||
}
|
||||
|
||||
public String toLackErrorMsg(String capabilityName) {
|
||||
StringBuilder sb = new StringBuilder("\n").append(capabilityName).append("\n");
|
||||
if (!coreLack.isEmpty()) {
|
||||
sb.append("缺少Core方法:").append("\n").append(coreLack).append("\n");
|
||||
}
|
||||
if (!capLack.isEmpty()) {
|
||||
sb.append("缺少Capability方法:").append("\n").append(capLack).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.Capability;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.ToCoordinated;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.ProxySetFailedExceptionCapability;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||
|
||||
/**
|
||||
* 负责执行<code>Capability</code>的注入逻辑
|
||||
*/
|
||||
public class CapabilityInjectFactory extends AgentBaseFactory {
|
||||
|
||||
private Reflections reflections;
|
||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
||||
reflections = context.getReflections();
|
||||
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
||||
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
||||
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
//获取现有的`@InjectCapability`注解所在字段,并获取对应的类,通过动态代理注入对象
|
||||
Set<Field> fields = reflections.getFieldsAnnotatedWith(InjectCapability.class);
|
||||
//在动态代理内部,通过函数路由表调用对应的方法
|
||||
createProxy(fields);
|
||||
}
|
||||
|
||||
private void createProxy(Set<Field> fields) {
|
||||
try {
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
Class<?> fieldType = field.getType();
|
||||
Object instance = Proxy.newProxyInstance(
|
||||
fieldType.getClassLoader(),
|
||||
new Class[]{fieldType},
|
||||
(proxy, method, objects) -> {
|
||||
if (method.isAnnotationPresent(ToCoordinated.class)) {
|
||||
String key = method.getDeclaringClass().getAnnotation(Capability.class).value() + "." + methodSignature(method);
|
||||
return coordinatedMethodsRouterTable.get(key).apply(objects);
|
||||
}
|
||||
String key = fieldType.getAnnotation(Capability.class).value() + "." + methodSignature(method);
|
||||
return methodsRouterTable.get(key).apply(objects);
|
||||
}
|
||||
);
|
||||
field.set(capabilityHolderInstances.get(field.getDeclaringClass()), instance);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ProxySetFailedExceptionCapability("代理设置失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.*;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.CapabilityFactoryExecuteFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.CoreInstancesCreateFailedExceptionCapability;
|
||||
import work.slhaf.partner.api.agent.factory.capability.exception.DuplicateMethodException;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.CapabilityFactoryContext;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static cn.hutool.core.util.ClassUtil.isNormalClass;
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||
|
||||
|
||||
/**
|
||||
* 负责获取<code>@Capability</code>和<code>@CapabilityCore</code>标识的类,并生成函数路由表、设置<code>Core</code>实例用于后续注入
|
||||
*/
|
||||
public final class CapabilityRegisterFactory extends AgentBaseFactory {
|
||||
|
||||
private Reflections reflections;
|
||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
||||
private HashMap<Class<?>, Object> capabilityCoreInstances;
|
||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
||||
private Set<Class<?>> cores;
|
||||
private Set<Class<?>> capabilities;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
CapabilityFactoryContext factoryContext = context.getCapabilityFactoryContext();
|
||||
reflections = context.getReflections();
|
||||
methodsRouterTable = factoryContext.getMethodsRouterTable();
|
||||
coordinatedMethodsRouterTable = factoryContext.getCoordinatedMethodsRouterTable();
|
||||
capabilityCoreInstances = factoryContext.getCapabilityCoreInstances();
|
||||
cores = factoryContext.getCores();
|
||||
capabilities = factoryContext.getCapabilities();
|
||||
capabilityHolderInstances = factoryContext.getCapabilityHolderInstances();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
||||
setCapabilityCoreInstances();
|
||||
setAnnotatedClasses();
|
||||
generateRouterTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置<code>CapabilityCore</code>、<code>Capability</code>注解标识类
|
||||
*/
|
||||
private void setAnnotatedClasses() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
||||
cores.addAll(reflections.getTypesAnnotatedWith(CapabilityCore.class));
|
||||
capabilities.addAll(reflections.getTypesAnnotatedWith(Capability.class));
|
||||
setCapabilityHolderInstances();
|
||||
}
|
||||
|
||||
private void setCapabilityHolderInstances() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
for (Class<?> clazz : reflections.getTypesAnnotatedWith(CapabilityHolder.class)) {
|
||||
if (!isNormalClass(clazz)){
|
||||
continue;
|
||||
}
|
||||
Object o = clazz.getDeclaredConstructor().newInstance();
|
||||
capabilityHolderInstances.put(clazz, o);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成函数路由表
|
||||
*/
|
||||
private void generateRouterTable() {
|
||||
generateMethodsRouterTable();
|
||||
generateCoordinatedMethodsRouterTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成协调函数对应的函数路由表
|
||||
*/
|
||||
private void generateCoordinatedMethodsRouterTable() {
|
||||
Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(Coordinated.class);
|
||||
if (methodsAnnotatedWith.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
//获取所有CM实例
|
||||
HashMap<String, Object> coordinateManagerInstances = getCoordinateManagerInstances();
|
||||
methodsAnnotatedWith.forEach(method -> {
|
||||
String key = method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method);
|
||||
Function<Object[], Object> function = args -> {
|
||||
try {
|
||||
return method.invoke(coordinateManagerInstances.get(key), args);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
coordinatedMethodsRouterTable.put(key, function);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new CapabilityFactoryExecuteFailedException("创建协调方法路由表出错", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取<code>CoordinateManager</code>子类实例
|
||||
*/
|
||||
private HashMap<String, Object> getCoordinateManagerInstances() throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
|
||||
HashMap<String, Object> map = new HashMap<>();
|
||||
for (Class<?> c : reflections.getTypesAnnotatedWith(CoordinateManager.class)) {
|
||||
Constructor<?> constructor = c.getDeclaredConstructor();
|
||||
Object instance = constructor.newInstance();
|
||||
|
||||
Arrays.stream(c.getMethods())
|
||||
.filter(method -> method.isAnnotationPresent(Coordinated.class))
|
||||
.forEach(method -> {
|
||||
String key = method.getAnnotation(Coordinated.class).capability() + "." + methodSignature(method);
|
||||
map.put(key, instance);
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通方法对应的函数路由表
|
||||
*/
|
||||
private void generateMethodsRouterTable() {
|
||||
//扫描`@Capability`与`@CapabilityMethod`注解的类与方法
|
||||
//将`capabilityValue.methodSignature`作为key,函数对象为通过反射拿到的core实例对应的方法
|
||||
cores.forEach(core -> Arrays.stream(core.getMethods())
|
||||
.filter(method -> method.isAnnotationPresent(CapabilityMethod.class))
|
||||
.forEach(method -> {
|
||||
Function<Object[], Object> function = args -> {
|
||||
try {
|
||||
return method.invoke(capabilityCoreInstances.get(core), args);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
String key = core.getAnnotation(CapabilityCore.class).value() + "." + methodSignature(method);
|
||||
if (methodsRouterTable.containsKey(key)) {
|
||||
throw new DuplicateMethodException("重复注册能力方法: " + core.getPackage().getName() + "." + core.getSimpleName() + "#" + method.getName());
|
||||
}
|
||||
methodsRouterTable.put(key, function);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射获取<code>CapabilityCore</code>实例
|
||||
*/
|
||||
private void setCapabilityCoreInstances() {
|
||||
try {
|
||||
for (Class<?> core : cores) {
|
||||
Constructor<?> constructor = core.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
capabilityCoreInstances.put(core, constructor.newInstance());
|
||||
}
|
||||
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
|
||||
IllegalAccessException e) {
|
||||
throw new CoreInstancesCreateFailedExceptionCapability("core实例创建失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于注解能力接口,需要与`@CapabilityCore`对应的`value`一致
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Capability {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于注解Core服务,需标识一个value致用于核心服务发现
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CapabilityCore {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface CapabilityHolder {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface CapabilityMethod {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface CoordinateManager {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于标注协调方法,`value`值需与对应的`@ToCoordinated`保持一致
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Coordinated {
|
||||
String capability();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于注入`Capability`
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface InjectCapability {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 当`@Capability`所注接口中,如果存在方法需要协调多个Core服务的调用,可以通过该注解进行排除
|
||||
* value值为方法对应标识,需与协调实现处的方法标识保持一致
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ToCoordinated {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
||||
|
||||
public class CapabilityCheckFailedException extends AgentLaunchFailedException {
|
||||
public CapabilityCheckFailedException(String message) {
|
||||
super("Capability注册失败: " + message);
|
||||
}
|
||||
|
||||
public CapabilityCheckFailedException(String message, Throwable cause) {
|
||||
super("Capability注册失败: " + message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
||||
|
||||
public class CapabilityFactoryExecuteFailedException extends AgentLaunchFailedException {
|
||||
public CapabilityFactoryExecuteFailedException(String message) {
|
||||
super("CapabilityRegisterFactory 执行失败: " + message);
|
||||
}
|
||||
|
||||
public CapabilityFactoryExecuteFailedException(String message, Throwable cause) {
|
||||
super("CapabilityRegisterFactory 执行失败: " + message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class CoreInstancesCreateFailedExceptionCapability extends CapabilityFactoryExecuteFailedException {
|
||||
public CoreInstancesCreateFailedExceptionCapability(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CoreInstancesCreateFailedExceptionCapability(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class DuplicateCapabilityException extends CapabilityCheckFailedException {
|
||||
public DuplicateCapabilityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DuplicateCapabilityException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class DuplicateMethodException extends CapabilityCheckFailedException{
|
||||
public DuplicateMethodException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DuplicateMethodException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class EmptyCapabilityHolderException extends CapabilityCheckFailedException{
|
||||
public EmptyCapabilityHolderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public EmptyCapabilityHolderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class ProxySetFailedExceptionCapability extends CapabilityFactoryExecuteFailedException {
|
||||
public ProxySetFailedExceptionCapability(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ProxySetFailedExceptionCapability(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class UnMatchedCapabilityException extends CapabilityCheckFailedException{
|
||||
public UnMatchedCapabilityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnMatchedCapabilityException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class UnMatchedCapabilityMethodException extends CapabilityCheckFailedException {
|
||||
public UnMatchedCapabilityMethodException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnMatchedCapabilityMethodException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.capability.exception;
|
||||
|
||||
public class UnMatchedCoordinatedMethodException extends CapabilityCheckFailedException {
|
||||
public UnMatchedCoordinatedMethodException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnMatchedCoordinatedMethodException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package work.slhaf.partner.api.agent.factory.config;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.ConfigFactoryContext;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
import work.slhaf.partner.api.agent.runtime.config.DefaultAgentConfigManager;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigLoaderFactory extends AgentBaseFactory {
|
||||
|
||||
private AgentConfigManager agentConfigManager;
|
||||
private HashMap<String, ModelConfig> modelConfigMap;
|
||||
private HashMap<String, List<Message>> modelPromptMap;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
ConfigFactoryContext factoryContext = context.getConfigFactoryContext();
|
||||
modelConfigMap = factoryContext.getModelConfigMap();
|
||||
modelPromptMap = factoryContext.getModelPromptMap();
|
||||
|
||||
if (AgentConfigManager.INSTANCE == null){
|
||||
AgentConfigManager.setINSTANCE(new DefaultAgentConfigManager());
|
||||
}
|
||||
|
||||
agentConfigManager = AgentConfigManager.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
agentConfigManager.load();
|
||||
agentConfigManager.check();
|
||||
modelConfigMap.putAll(agentConfigManager.getModelConfigMap());
|
||||
modelPromptMap.putAll(agentConfigManager.getModelPromptMap());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class ConfigDirNotExistException extends ConfigFactoryInitFailedException {
|
||||
public ConfigDirNotExistException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConfigDirNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException;
|
||||
|
||||
public class ConfigFactoryInitFailedException extends AgentLaunchFailedException {
|
||||
public ConfigFactoryInitFailedException(String message, Throwable cause) {
|
||||
super("AgentConfigManager 执行失败: " + message, cause);
|
||||
}
|
||||
|
||||
public ConfigFactoryInitFailedException(String message) {
|
||||
super("AgentConfigManager 执行失败: " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.exception.AgentRuntimeException;
|
||||
|
||||
public class ConfigFactoryRuntimeException extends AgentRuntimeException {
|
||||
public ConfigFactoryRuntimeException(String message, Throwable cause) {
|
||||
super("ConfigFactory 运行出错: " + message, cause);
|
||||
}
|
||||
|
||||
public ConfigFactoryRuntimeException(String message) {
|
||||
super("ConfigFactory 运行出错: " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class ConfigGenerateFailedException extends ConfigFactoryInitFailedException {
|
||||
public ConfigGenerateFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConfigGenerateFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class ConfigNotExistException extends ConfigFactoryInitFailedException {
|
||||
public ConfigNotExistException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
public ConfigNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class ConfigUpdateFailedException extends ConfigFactoryRuntimeException{
|
||||
public ConfigUpdateFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConfigUpdateFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class PromptDirNotExistException extends ConfigFactoryInitFailedException {
|
||||
public PromptDirNotExistException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PromptDirNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.exception;
|
||||
|
||||
public class PromptNotExistException extends ConfigFactoryInitFailedException {
|
||||
public PromptNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PromptNotExistException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ModelConfig {
|
||||
private String baseUrl;
|
||||
private String apikey;
|
||||
private String model;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PrimaryModelConfig {
|
||||
private String key;
|
||||
private ModelConfig modelConfig;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.partner.api.agent.factory.config.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class PrimaryModelPrompt {
|
||||
private String key;
|
||||
private List<Message> messages;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package work.slhaf.partner.api.agent.factory.context;
|
||||
|
||||
import lombok.Data;
|
||||
import org.reflections.Reflections;
|
||||
import org.reflections.scanners.Scanners;
|
||||
import org.reflections.util.ConfigurationBuilder;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class AgentRegisterContext {
|
||||
private Reflections reflections;
|
||||
private CapabilityFactoryContext capabilityFactoryContext = new CapabilityFactoryContext();
|
||||
private ConfigFactoryContext configFactoryContext = new ConfigFactoryContext();
|
||||
private ModuleFactoryContext moduleFactoryContext = new ModuleFactoryContext();
|
||||
|
||||
public AgentRegisterContext(List<URL> urls) {
|
||||
reflections = new Reflections(new ConfigurationBuilder().setScanners(
|
||||
Scanners.FieldsAnnotated,
|
||||
Scanners.SubTypes,
|
||||
Scanners.MethodsAnnotated,
|
||||
Scanners.TypesAnnotated
|
||||
)
|
||||
.setUrls(urls)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package work.slhaf.partner.api.agent.factory.context;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Data
|
||||
public class CapabilityFactoryContext {
|
||||
private final HashMap<String, Function<Object[], Object>> methodsRouterTable = new HashMap<>();
|
||||
private final HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable = new HashMap<>();
|
||||
private final HashMap<Class<?>, Object> capabilityCoreInstances = new HashMap<>();
|
||||
private final HashMap<Class<?>, Object> capabilityHolderInstances = new HashMap<>();
|
||||
private Set<Class<?>> cores;
|
||||
private Set<Class<?>> capabilities;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package work.slhaf.partner.api.agent.factory.context;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ConfigFactoryContext {
|
||||
private HashMap<String, List<Message>> modelPromptMap = new HashMap<>();
|
||||
private HashMap<String, ModelConfig> modelConfigMap = new HashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.partner.api.agent.factory.context;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ModuleFactoryContext {
|
||||
private List<MetaModule> moduleList = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.exception;
|
||||
|
||||
public class AgentRegisterFactoryFailedException extends RuntimeException {
|
||||
public AgentRegisterFactoryFailedException(String message, Throwable cause) {
|
||||
super("AgentRegisterFactory 执行失败: " + message, cause);
|
||||
}
|
||||
|
||||
public AgentRegisterFactoryFailedException(String message) {
|
||||
super("AgentRegisterFactory 执行失败: " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.exception;
|
||||
|
||||
public class ExternalModuleLoadFailedException extends AgentRegisterFactoryFailedException{
|
||||
public ExternalModuleLoadFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ExternalModuleLoadFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.exception;
|
||||
|
||||
public class ExternalModulePathNotExistException extends AgentRegisterFactoryFailedException {
|
||||
public ExternalModulePathNotExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExternalModulePathNotExistException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package work.slhaf.partner.api.agent.factory.module;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import org.reflections.Reflections;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleCheckException;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.ActivateModel;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningSubModule;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ModuleCheckFactory extends AgentBaseFactory {
|
||||
|
||||
private Reflections reflections;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
reflections = context.getReflections();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
Set<Class<?>> types = reflections.getTypesAnnotatedWith(AgentModule.class);
|
||||
//检查注解AgentModule所在类是否继承了AgentInteractionModule
|
||||
agentModuleAnnotationCheck(types);
|
||||
//检查AgentModule是否具备无参构造方法
|
||||
moduleConstructorsCheck(types);
|
||||
//检查hook注解所在方法是否位于AgentInteractionModule子类/AgentInteractionSubModule子类/ActivateModel子类
|
||||
hookLocationCheck();
|
||||
//检查实现了ActivateModel的模块数量、名称与prompt是否一致
|
||||
activateModelImplCheck();
|
||||
}
|
||||
|
||||
private void moduleConstructorsCheck(Set<Class<?>> types) {
|
||||
for (Class<?> type : types) {
|
||||
try {
|
||||
type.getConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ModuleCheckException("缺少无参构造方法的模块: " + type.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void activateModelImplCheck() {
|
||||
try {
|
||||
Set<Class<? extends ActivateModel>> types = reflections.getSubTypesOf(ActivateModel.class);
|
||||
Set<String> modelKeySet = new HashSet<>();
|
||||
for (Class<? extends ActivateModel> type : types) {
|
||||
ActivateModel instance = type.getConstructor().newInstance();
|
||||
modelKeySet.add(instance.modelKey());
|
||||
}
|
||||
Set<String> promptKeySet = AgentConfigManager.INSTANCE.getModelPromptMap().keySet();
|
||||
if (!promptKeySet.containsAll(modelKeySet)) {
|
||||
modelKeySet.removeAll(promptKeySet);
|
||||
throw new ModuleCheckException("存在未配置Prompt的ActivateModel实现! 缺少Prompt的ModelKey列表: " + modelKeySet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ModuleCheckException("ActivateModel 检测出错", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void hookLocationCheck() {
|
||||
//检查@AfterExecute注解
|
||||
postHookLocationCheck();
|
||||
//检查@BeforeExecute注解
|
||||
preHookLocationCheck();
|
||||
//检查@Init注解
|
||||
initHookLocationCheck();
|
||||
//检查@AgentModule注解是否只位于普通类上
|
||||
agentModuleLocationCheck();
|
||||
}
|
||||
|
||||
private void agentModuleLocationCheck() {
|
||||
Set<Class<?>> types = reflections.getTypesAnnotatedWith(AgentModule.class);
|
||||
for (Class<?> type : types) {
|
||||
if (!ClassUtil.isNormalClass(type)) {
|
||||
throw new ModuleCheckException("AgentModule 注解仅能位于普通类上! 异常类信息: " + type.getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initHookLocationCheck() {
|
||||
Set<Method> methods = reflections.getMethodsAnnotatedWith(Init.class);
|
||||
Set<Class<?>> types = methods.stream()
|
||||
.map(Method::getDeclaringClass)
|
||||
.collect(Collectors.toSet());
|
||||
checkLocation(types);
|
||||
}
|
||||
|
||||
private void preHookLocationCheck() {
|
||||
Set<Method> methods = reflections.getMethodsAnnotatedWith(BeforeExecute.class);
|
||||
Set<Class<?>> types = methods.stream()
|
||||
.map(Method::getDeclaringClass)
|
||||
.collect(Collectors.toSet());
|
||||
checkLocation(types);
|
||||
}
|
||||
|
||||
|
||||
private void postHookLocationCheck() {
|
||||
Set<Method> methods = reflections.getMethodsAnnotatedWith(AfterExecute.class);
|
||||
Set<Class<?>> types = methods.stream()
|
||||
.map(Method::getDeclaringClass)
|
||||
.collect(Collectors.toSet());
|
||||
checkLocation(types);
|
||||
}
|
||||
|
||||
private void checkLocation(Set<Class<?>> types) {
|
||||
for (Class<?> type : types) {
|
||||
if (AgentRunningModule.class.isAssignableFrom(type)) {
|
||||
continue;
|
||||
}
|
||||
if (AgentRunningSubModule.class.isAssignableFrom(type)) {
|
||||
continue;
|
||||
}
|
||||
if (ActivateModel.class.isAssignableFrom(type)) {
|
||||
continue;
|
||||
}
|
||||
throw new ModuleCheckException("在不支持的类中使用了hook注解: " + type.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
private void agentModuleAnnotationCheck(Set<Class<?>> types) {
|
||||
for (Class<?> type : types) {
|
||||
if (type.isAnnotation()) {
|
||||
continue;
|
||||
}
|
||||
if (AgentRunningModule.class.isAssignableFrom(type) && ClassUtil.isNormalClass(type)) {
|
||||
continue;
|
||||
}
|
||||
throw new ModuleCheckException("存在未继承AgentInteractionModule.class的AgentModule实现: " + type.getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package work.slhaf.partner.api.agent.factory.module;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleInitHookExecuteFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.methodSignature;
|
||||
|
||||
/**
|
||||
* 负责执行前hook逻辑
|
||||
*/
|
||||
public class ModuleInitHookExecuteFactory extends AgentBaseFactory {
|
||||
|
||||
private List<MetaModule> moduleList;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
||||
moduleList = factoryContext.getModuleList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
//遍历模块列表,并向上查找@Init注解
|
||||
for (MetaModule metaModule : moduleList) {
|
||||
List<MetaMethod> initHookMethods = collectInitHookMethods(metaModule.getClazz());
|
||||
proceedInitMethods(metaModule, initHookMethods);
|
||||
}
|
||||
}
|
||||
|
||||
private void proceedInitMethods(MetaModule metaModule, List<MetaMethod> initHookMethods) {
|
||||
for (MetaMethod metaMethod : initHookMethods) {
|
||||
try {
|
||||
metaMethod.getMethod().invoke(metaModule.getInstance());
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new ModuleInitHookExecuteFailedException("模块的init hook方法执行失败! 模块: " + metaModule.getName() + " 方法签名: " + methodSignature(metaMethod.getMethod()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MetaMethod> collectInitHookMethods(Class<?> clazz) {
|
||||
Set<Class<?>> classes = collectExtendedClasses(clazz, AgentRunningModule.class);
|
||||
return classes.stream()
|
||||
.map(Class::getDeclaredMethods)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(method -> method.isAnnotationPresent(Init.class))
|
||||
.map(method -> {
|
||||
MetaMethod metaMethod = new MetaMethod();
|
||||
metaMethod.setMethod(method);
|
||||
metaMethod.setOrder(method.getAnnotation(Init.class).order());
|
||||
return metaMethod;
|
||||
})
|
||||
.sorted(Comparator.comparing(MetaMethod::getOrder))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package work.slhaf.partner.api.agent.factory.module;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.*;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.AfterExecute;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.BeforeExecute;
|
||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleInstanceGenerateFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.module.exception.ModuleProxyGenerateFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaMethod;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static work.slhaf.partner.api.agent.util.AgentUtil.collectExtendedClasses;
|
||||
|
||||
/**
|
||||
* 通过扫描注解<code>@BeforeExecute</code>,获取到各个模块的后hook逻辑并通过动态代理添加到执行逻辑之后
|
||||
*/
|
||||
public class ModuleProxyFactory extends AgentBaseFactory {
|
||||
|
||||
private List<MetaModule> moduleList;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
||||
moduleList = factoryContext.getModuleList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
generateInstances();
|
||||
setHookProxy();
|
||||
}
|
||||
|
||||
private void setHookProxy() {
|
||||
for (MetaModule module : moduleList) {
|
||||
Class<?> clazz = module.getClazz();
|
||||
try {
|
||||
MethodsListRecord record = collectHookMethods(clazz);
|
||||
//生成实例
|
||||
generateProxiedInstances(record, module);
|
||||
} catch (Exception e) {
|
||||
throw new ModuleProxyGenerateFailedException("创建代理对象失败: " + clazz.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateProxiedInstances(MethodsListRecord record, MetaModule metaModule) {
|
||||
try {
|
||||
Class<? extends AgentRunningModule> clazz = metaModule.getClazz();
|
||||
Class<? extends AgentRunningModule> proxyClass = new ByteBuddy()
|
||||
.subclass(clazz)
|
||||
.method(ElementMatchers.isOverriddenFrom(AgentRunningModule.class))
|
||||
.intercept(MethodDelegation.to(new ModuleProxyInterceptor(record.post, record.pre)))
|
||||
.make()
|
||||
.load(ModuleProxyFactory.class.getClassLoader())
|
||||
.getLoaded();
|
||||
metaModule.setInstance(proxyClass.getConstructor().newInstance());
|
||||
} catch (Exception e) {
|
||||
throw new ModuleProxyGenerateFailedException("模块Hook代理生成失败! 代理失败的模块名: " + metaModule.getClazz().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodsListRecord collectHookMethods(Class<?> clazz) {
|
||||
List<MetaMethod> post = new ArrayList<>();
|
||||
List<MetaMethod> pre = new ArrayList<>();
|
||||
//获取该类本身的hook逻辑
|
||||
collectHookMethods(post, pre, clazz);
|
||||
//获取它所继承、实现的抽象类或接口, 以AgentInteractionModule、ActiveModel为终点
|
||||
Set<Class<?>> classes = collectExtendedClasses(clazz, AgentRunningModule.class);
|
||||
//获取这些类中的hook逻辑
|
||||
collectHookMethods(post, pre, classes);
|
||||
return new MethodsListRecord(post, pre);
|
||||
}
|
||||
|
||||
private void collectHookMethods(List<MetaMethod> post, List<MetaMethod> pre, Set<Class<?>> classes) {
|
||||
for (Class<?> type : classes) {
|
||||
collectPreHookMethods(pre, type);
|
||||
collectPostHookMethods(post, type);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectPostHookMethods(List<MetaMethod> post, Class<?> type) {
|
||||
Set<MetaMethod> collectedPostHookMethod = Arrays.stream(type.getDeclaredMethods())
|
||||
.filter(method -> method.isAnnotationPresent(AfterExecute.class))
|
||||
.map(method -> {
|
||||
MetaMethod metaMethod = new MetaMethod();
|
||||
metaMethod.setMethod(method);
|
||||
metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order());
|
||||
return metaMethod;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
post.addAll(collectedPostHookMethod);
|
||||
}
|
||||
|
||||
private void collectPreHookMethods(List<MetaMethod> pre, Class<?> type) {
|
||||
Set<MetaMethod> collectedPreHookMethods = Arrays.stream(type.getDeclaredMethods())
|
||||
.filter(method -> method.isAnnotationPresent(BeforeExecute.class))
|
||||
.map(method -> {
|
||||
MetaMethod metaMethod = new MetaMethod();
|
||||
metaMethod.setMethod(method);
|
||||
metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order());
|
||||
return metaMethod;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
pre.addAll(collectedPreHookMethods);
|
||||
}
|
||||
|
||||
|
||||
private void collectHookMethods(List<MetaMethod> post, List<MetaMethod> pre, Class<?> clazz) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(BeforeExecute.class)) {
|
||||
MetaMethod metaMethod = new MetaMethod();
|
||||
metaMethod.setOrder(method.getAnnotation(BeforeExecute.class).order());
|
||||
pre.add(metaMethod);
|
||||
metaMethod.setMethod(method);
|
||||
} else if (method.isAnnotationPresent(AfterExecute.class)) {
|
||||
MetaMethod metaMethod = new MetaMethod();
|
||||
metaMethod.setOrder(method.getAnnotation(AfterExecute.class).order());
|
||||
post.add(metaMethod);
|
||||
metaMethod.setMethod(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateInstances() {
|
||||
for (MetaModule metaModule : moduleList) {
|
||||
try {
|
||||
Class<? extends AgentRunningModule> clazz = metaModule.getClazz();
|
||||
AgentRunningModule instance = clazz.getConstructor().newInstance();
|
||||
metaModule.setInstance(instance);
|
||||
} catch (Exception e) {
|
||||
throw new ModuleInstanceGenerateFailedException("模块实例构造失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record ModuleProxyInterceptor(List<MetaMethod> postHookMethods, List<MetaMethod> preHookMethods) {
|
||||
@RuntimeType
|
||||
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
|
||||
for (MetaMethod metaMethod : preHookMethods) {
|
||||
metaMethod.getMethod().invoke(proxy);
|
||||
}
|
||||
Object res = zuper.call();
|
||||
for (MetaMethod metaMethod : postHookMethods) {
|
||||
metaMethod.getMethod().invoke(proxy);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
record MethodsListRecord(List<MetaMethod> post, List<MetaMethod> pre) {
|
||||
public MethodsListRecord {
|
||||
post.sort(Comparator.comparingInt(MetaMethod::getOrder));
|
||||
pre.sort(Comparator.comparingInt(MetaMethod::getOrder));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package work.slhaf.partner.api.agent.factory.module;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import org.reflections.Reflections;
|
||||
import work.slhaf.partner.api.agent.factory.AgentBaseFactory;
|
||||
import work.slhaf.partner.api.agent.factory.context.AgentRegisterContext;
|
||||
import work.slhaf.partner.api.agent.factory.context.ModuleFactoryContext;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.AgentModule;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 负责扫描<code>@Module</code>注解获取模块实例
|
||||
*/
|
||||
public class ModuleRegisterFactory extends AgentBaseFactory {
|
||||
|
||||
private Reflections reflections;
|
||||
private List<MetaModule> moduleList;
|
||||
|
||||
@Override
|
||||
protected void setVariables(AgentRegisterContext context) {
|
||||
ModuleFactoryContext factoryContext = context.getModuleFactoryContext();
|
||||
reflections = context.getReflections();
|
||||
moduleList = factoryContext.getModuleList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
setModuleList();
|
||||
}
|
||||
|
||||
private void setModuleList() {
|
||||
//反射扫描获取@AgentModule所在类, 该部分为Agent流程执行模块
|
||||
Set<Class<?>> modules = reflections.getTypesAnnotatedWith(AgentModule.class);
|
||||
for (Class<?> module : modules) {
|
||||
if (!ClassUtil.isNormalClass(module)) {
|
||||
continue;
|
||||
}
|
||||
Class<? extends AgentRunningModule> clazz = module.asSubclass(AgentRunningModule.class);
|
||||
AgentModule agentModule = clazz.getAnnotation(AgentModule.class);
|
||||
MetaModule metaModule = new MetaModule();
|
||||
metaModule.setName(agentModule.name());
|
||||
metaModule.setOrder(agentModule.order());
|
||||
metaModule.setClazz(clazz);
|
||||
moduleList.add(metaModule);
|
||||
}
|
||||
moduleList.sort(Comparator.comparing(MetaModule::getOrder));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 仅适用于以下类中的方法:
|
||||
* 1. <code>@AgentModule</code>注解所在类
|
||||
* 2. <code>ActivateModel</code>子类
|
||||
* 3. <code>AgentRunningModule</code>或者<code>AgentRunningSubModule</code>子类
|
||||
*/
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AfterExecute {
|
||||
int order() default 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于注解执行模块
|
||||
*/
|
||||
@Inherited
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AgentModule {
|
||||
|
||||
/**
|
||||
* 模块名称
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* 模块执行顺序,数字越小执行越靠前
|
||||
*/
|
||||
int order();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 仅适用于以下类中的方法:
|
||||
* 1. <code>@AgentModule</code>注解所在类
|
||||
* 2. <code>ActivateModel</code>子类
|
||||
* 3. <code>AgentRunningModule</code>或者<code>AgentRunningSubModule</code>子类
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BeforeExecute {
|
||||
int order() default 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
||||
|
||||
@AgentModule(name = "core",order = 5)
|
||||
public @interface CoreModule {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Init {
|
||||
int order() default 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
||||
|
||||
public class ModuleCheckException extends ModuleFactoryInitFailedException {
|
||||
public ModuleCheckException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModuleCheckException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
||||
|
||||
public class ModuleFactoryInitFailedException extends RuntimeException {
|
||||
public ModuleFactoryInitFailedException(String message) {
|
||||
super("ModuleFactory 执行失败: "+message);
|
||||
}
|
||||
|
||||
public ModuleFactoryInitFailedException(String message, Throwable cause) {
|
||||
super("ModuleFactory 执行失败: "+message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
||||
|
||||
public class ModuleInitHookExecuteFailedException extends ModuleFactoryInitFailedException {
|
||||
public ModuleInitHookExecuteFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModuleInitHookExecuteFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
||||
|
||||
public class ModuleInstanceGenerateFailedException extends ModuleFactoryInitFailedException {
|
||||
public ModuleInstanceGenerateFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModuleInstanceGenerateFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.exception;
|
||||
|
||||
public class ModuleProxyGenerateFailedException extends ModuleFactoryInitFailedException {
|
||||
public ModuleProxyGenerateFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModuleProxyGenerateFailedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Data
|
||||
public class MetaMethod {
|
||||
private int order;
|
||||
private Method method;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.factory.module.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
|
||||
@Data
|
||||
public class MetaModule {
|
||||
private String name;
|
||||
private int order;
|
||||
private Class<? extends AgentRunningModule> clazz;
|
||||
private AgentRunningModule instance;
|
||||
private boolean enabled = true;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package work.slhaf.partner.api.agent.runtime.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.partner.api.agent.factory.config.exception.ConfigNotExistException;
|
||||
import work.slhaf.partner.api.agent.factory.config.exception.ConfigUpdateFailedException;
|
||||
import work.slhaf.partner.api.agent.factory.config.exception.PromptNotExistException;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
public abstract class AgentConfigManager {
|
||||
|
||||
@Setter
|
||||
public static AgentConfigManager INSTANCE;
|
||||
private static final String DEFAULT_KEY = "default";
|
||||
|
||||
protected HashMap<String, ModelConfig> modelConfigMap;
|
||||
protected HashMap<String, List<Message>> modelPromptMap;
|
||||
protected HashMap<String, Boolean> moduleEnabledStatus;
|
||||
protected List<MetaModule> moduleList;
|
||||
|
||||
public void load() {
|
||||
modelConfigMap = loadModelConfig();
|
||||
modelPromptMap = loadModelPrompt();
|
||||
}
|
||||
|
||||
protected abstract HashMap<String, List<Message>> loadModelPrompt();
|
||||
|
||||
protected abstract HashMap<String, ModelConfig> loadModelConfig();
|
||||
|
||||
public abstract void dumpModelConfig(String key);
|
||||
|
||||
protected abstract void dumpModuleEnabledStatus();
|
||||
|
||||
protected abstract HashMap<String, Boolean> loadModuleEnabledStatusMap();
|
||||
|
||||
public void moduleEnabledStatusFilterAndRecord(List<MetaModule> moduleList) {
|
||||
this.moduleList = moduleList;
|
||||
this.moduleEnabledStatus = loadModuleEnabledStatusMap();
|
||||
|
||||
boolean unmatch = false;
|
||||
for (MetaModule metaModule : moduleList) {
|
||||
String moduleName = metaModule.getName();
|
||||
if (moduleEnabledStatus.containsKey(moduleName)) {
|
||||
metaModule.setEnabled(moduleEnabledStatus.get(moduleName));
|
||||
} else {
|
||||
log.warn("缺少Module {} 启用配置! 将触发更新操作!", moduleName);
|
||||
unmatch = true;
|
||||
}
|
||||
}
|
||||
if (unmatch) {
|
||||
dumpModuleEnabledStatus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对模型Config与Prompt分别进行检验,除了都必须包含default外,还需要确保数量、key一致,毕竟是模型配置与提示词
|
||||
*/
|
||||
public void check() {
|
||||
log.info("[AgentConfigManager]: 执行config与prompt检测...");
|
||||
if (!modelConfigMap.containsKey("default")) {
|
||||
throw new ConfigNotExistException("缺少默认配置! 需确保存在一个模型配置的key为`default`");
|
||||
}
|
||||
if (!modelPromptMap.containsKey("basic")) {
|
||||
throw new PromptNotExistException("缺少基础Prompt! 需要确保存在key为basic的Prompt文件,它将与其他Prompt共同作用于模块节点。");
|
||||
}
|
||||
Set<String> configKeySet = new HashSet<>(modelConfigMap.keySet());
|
||||
configKeySet.remove("default");
|
||||
Set<String> promptKeySet = new HashSet<>(modelPromptMap.keySet());
|
||||
promptKeySet.remove("basic");
|
||||
if (!promptKeySet.containsAll(configKeySet)) {
|
||||
log.warn("存在未被提示词包含的模型配置,该配置将无法生效!");
|
||||
}
|
||||
log.info("[AgentConfigManager]: 检测完毕.");
|
||||
}
|
||||
|
||||
public List<Message> loadModelPrompt(String modelKey) {
|
||||
if (!modelPromptMap.containsKey(modelKey)) {
|
||||
throw new PromptNotExistException("不存在的modelPrompt: " + modelKey);
|
||||
}
|
||||
return modelPromptMap.get(modelKey);
|
||||
}
|
||||
|
||||
public ModelConfig loadModelConfig(String modelKey) {
|
||||
if (!modelConfigMap.containsKey(modelKey)) {
|
||||
return modelConfigMap.get(DEFAULT_KEY);
|
||||
}
|
||||
return modelConfigMap.get(modelKey);
|
||||
}
|
||||
|
||||
public void updateModelConfig(String modelKey, ModelConfig config) {
|
||||
modelConfigMap.put(modelKey, config);
|
||||
dumpModelConfig(modelKey);
|
||||
}
|
||||
|
||||
public void updateModuleEnabledStatus(String key, boolean status) {
|
||||
if (!moduleEnabledStatus.containsKey(key)) {
|
||||
throw new ConfigUpdateFailedException("模块状态更新失败! 不存在的ModuleKey: " + key);
|
||||
}
|
||||
moduleEnabledStatus.put(key, status);
|
||||
dumpModuleEnabledStatus();
|
||||
for (MetaModule metaModule : moduleList) {
|
||||
if (metaModule.getName().equals(key)) {
|
||||
metaModule.setEnabled(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package work.slhaf.partner.api.agent.runtime.config;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import work.slhaf.partner.api.agent.factory.config.exception.*;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.PrimaryModelConfig;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.PrimaryModelPrompt;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认配置工厂
|
||||
* 将从当前运行目录的config文件夹下创建并读取配置
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultAgentConfigManager extends AgentConfigManager {
|
||||
|
||||
private static final String CONFIG_DIR = "./config/";
|
||||
private static final String MODEL_CONFIG_DIR = "./config/model/";
|
||||
private static final String PROMPT_CONFIG_DIR = "./config/prompt/";
|
||||
private static final String MODULE_ENABLED_STATUS_CONFIG_FILE = CONFIG_DIR + "module_enabled_status.json";
|
||||
|
||||
|
||||
@Override
|
||||
protected HashMap<String, List<Message>> loadModelPrompt() {
|
||||
File file = new File(PROMPT_CONFIG_DIR);
|
||||
if (!file.exists() && !file.isDirectory()) {
|
||||
throw new PromptDirNotExistException("未找到提示词目录: " + PROMPT_CONFIG_DIR + " 请手动创建!");
|
||||
}
|
||||
File[] files = file.listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
throw new PromptNotExistException("在目录 " + PROMPT_CONFIG_DIR + " 中未找到提示词配置!");
|
||||
}
|
||||
HashMap<String, List<Message>> promptMap = new HashMap<>();
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
PrimaryModelPrompt primaryModelPrompt = JSONUtil.readJSONObject(f, StandardCharsets.UTF_8).toBean(PrimaryModelPrompt.class);
|
||||
promptMap.put(primaryModelPrompt.getKey(), primaryModelPrompt.getMessages());
|
||||
}
|
||||
return promptMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HashMap<String, ModelConfig> loadModelConfig() {
|
||||
File file = new File(MODEL_CONFIG_DIR);
|
||||
if (!file.exists() || !file.isDirectory()) {
|
||||
throw new ConfigDirNotExistException("未找到配置目录: " + MODEL_CONFIG_DIR + " 请手动创建!");
|
||||
}
|
||||
File[] files = file.listFiles();
|
||||
if (files == null || files.length == 0) {
|
||||
throw new ConfigNotExistException("在目录" + MODEL_CONFIG_DIR + "中未找到配置文件!");
|
||||
}
|
||||
//遍历文件获取所有配置文件并返回
|
||||
HashMap<String, ModelConfig> configMap = new HashMap<>();
|
||||
for (File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
PrimaryModelConfig primaryModelConfig = JSONUtil.readJSONObject(f, StandardCharsets.UTF_8).toBean(PrimaryModelConfig.class);
|
||||
configMap.put(primaryModelConfig.getKey(), primaryModelConfig.getModelConfig());
|
||||
}
|
||||
return configMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HashMap<String, Boolean> loadModuleEnabledStatusMap() {
|
||||
File file = new File(MODULE_ENABLED_STATUS_CONFIG_FILE);
|
||||
try {
|
||||
HashMap<String, Boolean> moduleEnabledStatus = new HashMap<>();
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
for (MetaModule module : moduleList) {
|
||||
moduleEnabledStatus.put(module.getName(), module.isEnabled());
|
||||
}
|
||||
dumpModuleEnabledStatus();
|
||||
} else {
|
||||
JSONObject obj = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8);
|
||||
for (String s : obj.keySet()) {
|
||||
moduleEnabledStatus.put(s, obj.getBool(s));
|
||||
}
|
||||
log.info("ModuleEnabledStatusConfig 配置文件已成功读取!");
|
||||
}
|
||||
return moduleEnabledStatus;
|
||||
} catch (Exception e) {
|
||||
throw new ConfigGenerateFailedException("ModuleEnabledStatusConfig 配置文件创建失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpModelConfig(String key) {
|
||||
try {
|
||||
File file = new File(MODEL_CONFIG_DIR + key + ".json");
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
FileUtils.writeStringToFile(file, JSONUtil.toJsonPrettyStr(modelConfigMap.get(key)), StandardCharsets.UTF_8, false);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigUpdateFailedException("ModelConfig 配置文件更新失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dumpModuleEnabledStatus() {
|
||||
try {
|
||||
File file = new File(MODULE_ENABLED_STATUS_CONFIG_FILE);
|
||||
FileUtils.writeStringToFile(file, JSONUtil.toJsonPrettyStr(moduleEnabledStatus), StandardCharsets.UTF_8, false);
|
||||
} catch (IOException e) {
|
||||
throw new ConfigGenerateFailedException("ModuleEnabledStatus 配置文件更新失败!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package work.slhaf.partner.api.agent.runtime.data;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Data
|
||||
public class AgentContext {
|
||||
|
||||
public static AgentContext INSTANCE = new AgentContext();
|
||||
|
||||
private HashMap<String, Function<Object[], Object>> methodsRouterTable;
|
||||
private HashMap<String, Function<Object[], Object>> coordinatedMethodsRouterTable;
|
||||
private HashMap<Class<?>, Object> capabilityCoreInstances;
|
||||
private HashMap<Class<?>, Object> capabilityHolderInstances;
|
||||
private Set<Class<?>> cores;
|
||||
private Set<Class<?>> capabilities;
|
||||
private List<MetaModule> moduleList;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package work.slhaf.partner.api.agent.runtime.exception;
|
||||
|
||||
public interface AgentExceptionCallback {
|
||||
void onRuntimeException(AgentRuntimeException e);
|
||||
void onFailedException(AgentLaunchFailedException e);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.runtime.exception;
|
||||
|
||||
public class AgentLaunchFailedException extends RuntimeException {
|
||||
public AgentLaunchFailedException(String message, Throwable cause) {
|
||||
super("Agent 启动失败 " + message, cause);
|
||||
}
|
||||
|
||||
public AgentLaunchFailedException(String message) {
|
||||
super("Agent 启动失败 " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.runtime.exception;
|
||||
|
||||
public class AgentRuntimeException extends RuntimeException {
|
||||
public AgentRuntimeException(String message) {
|
||||
super("Agent 执行出错 " + message);
|
||||
}
|
||||
|
||||
public AgentRuntimeException(String message, Throwable cause) {
|
||||
super("Agent 执行出错 " + message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package work.slhaf.partner.api.agent.runtime.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class DefaultAgentExceptionCallback implements AgentExceptionCallback {
|
||||
|
||||
@Override
|
||||
public void onRuntimeException(AgentRuntimeException e) {
|
||||
log.error("Agent 运行异常: ", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailedException(AgentLaunchFailedException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package work.slhaf.partner.api.agent.runtime.exception;
|
||||
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
public static GlobalExceptionHandler INSTANCE = new GlobalExceptionHandler();
|
||||
|
||||
private AgentExceptionCallback exceptionCallback = new DefaultAgentExceptionCallback();
|
||||
|
||||
public void handle(Throwable e) {
|
||||
|
||||
switch (e.getClass().getSimpleName()) {
|
||||
case "AgentRuntimeException":
|
||||
exceptionCallback.onRuntimeException((AgentRuntimeException) e);
|
||||
break;
|
||||
case "AgentLaunchFailedException":
|
||||
exceptionCallback.onFailedException((AgentLaunchFailedException) e);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("未经处理的异常!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setExceptionCallback(AgentExceptionCallback callback) {
|
||||
INSTANCE.exceptionCallback = callback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentInputData;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentOutputData;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
||||
|
||||
public interface AgentGateway {
|
||||
|
||||
void launch();
|
||||
|
||||
<I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext> AgentInteractionAdapter<I, O, C> adapter();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentInputData;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.data.AgentOutputData;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.AgentRunningFlow;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AgentInteractionAdapter<I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext> {
|
||||
|
||||
private static AgentInteractionAdapter<?,?,?> INSTANCE;
|
||||
|
||||
protected AgentRunningFlow<C> agentRunningFlow = new AgentRunningFlow<>();
|
||||
protected List<MetaModule> moduleList = AgentConfigManager.INSTANCE.getModuleList();
|
||||
|
||||
public void receive(I inputData) {
|
||||
C finalInputData = parseInputData(inputData);
|
||||
C outputContext = agentRunningFlow.launch(moduleList, finalInputData);
|
||||
O outputData = parseOutputData(outputContext);
|
||||
send(outputData);
|
||||
}
|
||||
|
||||
protected abstract O parseOutputData(C outputContext);
|
||||
|
||||
protected abstract C parseInputData(I inputData);
|
||||
|
||||
public abstract void send(O outputData);
|
||||
|
||||
public static <I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext> AgentInteractionAdapter<I, O, C> getInstance() {
|
||||
@SuppressWarnings("unchecked")
|
||||
AgentInteractionAdapter<I, O, C> instance = (AgentInteractionAdapter<I, O, C>) INSTANCE;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static <I extends AgentInputData, O extends AgentOutputData, C extends RunningFlowContext> void setInstance(AgentInteractionAdapter<I, O, C> instance) {
|
||||
INSTANCE = instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public abstract class AgentInputData extends InteractionData{
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public abstract class AgentOutputData extends InteractionData{
|
||||
|
||||
private int code;
|
||||
|
||||
public static class StatusCode {
|
||||
public static final int SUCCESS = 1;
|
||||
public static final int FAILED = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public abstract class InteractionData {
|
||||
protected String userInfo;
|
||||
protected String content;
|
||||
protected LocalDateTime dateTime;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow;
|
||||
|
||||
import work.slhaf.partner.api.agent.factory.module.pojo.MetaModule;
|
||||
import work.slhaf.partner.api.agent.runtime.exception.GlobalExceptionHandler;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Agent执行流程
|
||||
*/
|
||||
public class AgentRunningFlow<C extends RunningFlowContext> {
|
||||
|
||||
public C launch(List<MetaModule> moduleList, C interactionContext){
|
||||
try {
|
||||
//流程执行启动
|
||||
for (MetaModule metaModule : moduleList) {
|
||||
metaModule.getInstance().execute(interactionContext);
|
||||
}
|
||||
}catch (Exception e){
|
||||
GlobalExceptionHandler.INSTANCE.handle(e);
|
||||
}
|
||||
return interactionContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import work.slhaf.partner.api.agent.factory.config.pojo.ModelConfig;
|
||||
import work.slhaf.partner.api.agent.factory.module.annotation.Init;
|
||||
import work.slhaf.partner.api.agent.runtime.config.AgentConfigManager;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.Model;
|
||||
import work.slhaf.partner.api.chat.ChatClient;
|
||||
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
||||
import work.slhaf.partner.api.chat.pojo.ChatResponse;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface ActivateModel {
|
||||
|
||||
AgentConfigManager AGENT_CONFIG_MANAGER = AgentConfigManager.INSTANCE;
|
||||
|
||||
@Init
|
||||
default void modelSettings() {
|
||||
Model model = new Model();
|
||||
ModelConfig modelConfig = AgentConfigManager.INSTANCE.loadModelConfig(modelKey());
|
||||
model.setBaseMessages(withBasicPrompt() ? loadSpecificPromptAndBasicPrompt(modelKey()) : loadSpecificPrompt(modelKey()));
|
||||
model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel()));
|
||||
((Module) this).setModel(model);
|
||||
}
|
||||
|
||||
default void updateModelSettings(ChatClient newChatClient) {
|
||||
BeanUtil.copyProperties(newChatClient, chatClient());
|
||||
}
|
||||
|
||||
private List<Message> loadSpecificPrompt(String modelKey) {
|
||||
return AGENT_CONFIG_MANAGER.loadModelPrompt(modelKey);
|
||||
}
|
||||
|
||||
private List<Message> loadSpecificPromptAndBasicPrompt(String modelKey) {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.addAll(AGENT_CONFIG_MANAGER.loadModelPrompt("basic"));
|
||||
messages.addAll(AGENT_CONFIG_MANAGER.loadModelPrompt(modelKey));
|
||||
return messages;
|
||||
}
|
||||
|
||||
default ChatResponse chat() {
|
||||
Model model = getModel();
|
||||
List<Message> temp = new ArrayList<>();
|
||||
temp.addAll(model.getBaseMessages());
|
||||
temp.addAll(model.getChatMessages());
|
||||
return model.getChatClient().runChat(temp);
|
||||
}
|
||||
|
||||
default ChatResponse singleChat(String input) {
|
||||
Model model = getModel();
|
||||
List<Message> temp = new ArrayList<>(model.getBaseMessages());
|
||||
temp.add(new Message(ChatConstant.Character.USER, input));
|
||||
return model.getChatClient().runChat(temp);
|
||||
}
|
||||
|
||||
default void updateChatClientSettings() {
|
||||
Model model = getModel();
|
||||
model.getChatClient().setTemperature(0.4);
|
||||
model.getChatClient().setTop_p(0.8);
|
||||
}
|
||||
|
||||
default List<Message> chatMessages() {
|
||||
return getModel().getChatMessages();
|
||||
}
|
||||
|
||||
default List<Message> baseMessages() {
|
||||
return getModel().getBaseMessages();
|
||||
}
|
||||
|
||||
default ChatClient chatClient() {
|
||||
return getModel().getChatClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅适用Module子类,否则需要重写
|
||||
*
|
||||
* @return 持有的model实例
|
||||
*/
|
||||
default Model getModel() {
|
||||
return ((Module) this).getModel();
|
||||
}
|
||||
|
||||
default void setModel(Model model) {
|
||||
((Module) this).setModel(model);
|
||||
}
|
||||
|
||||
String modelKey();
|
||||
|
||||
boolean withBasicPrompt();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
||||
|
||||
/**
|
||||
* 流程执行模块基类
|
||||
*/
|
||||
public abstract class AgentRunningModule extends Module {
|
||||
public abstract void execute(RunningFlowContext context);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
||||
|
||||
|
||||
/**
|
||||
* 流程子模块基类
|
||||
* @param <I> 输入类型
|
||||
* @param <O> 输出类型
|
||||
*/
|
||||
public abstract class AgentRunningSubModule<I, O> extends Module {
|
||||
|
||||
public abstract O execute(I data);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.CapabilityHolder;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.Model;
|
||||
|
||||
/**
|
||||
* 模块基类
|
||||
*/
|
||||
@CapabilityHolder
|
||||
public abstract class Module {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
protected Model model = new Model();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.chat.ChatClient;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Model {
|
||||
|
||||
protected ChatClient chatClient;
|
||||
protected List<Message> chatMessages;
|
||||
protected List<Message> baseMessages;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package work.slhaf.partner.api.agent.runtime.interaction.flow.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 流程上下文
|
||||
*/
|
||||
@Data
|
||||
public abstract class RunningFlowContext {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package work.slhaf.partner.api.agent.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class AgentUtil {
|
||||
|
||||
public static String methodSignature(Method method) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
sb.append(method.getReturnType().getName()).append(" ");
|
||||
sb.append(method.getName()).append("(");
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
sb.append(paramTypes[i].getName());
|
||||
if (i < paramTypes.length - 1) sb.append(",");
|
||||
}
|
||||
sb.append(")").append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static Set<Class<?>> collectExtendedClasses(Class<?> clazz, Class<?> targetClass) {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
collectExtendedClasses(classes, clazz, targetClass);
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static void collectExtendedClasses(Set<Class<?>> classes, Class<?> clazz, Class<?> target) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass == null || superclass == target) {
|
||||
return;
|
||||
}
|
||||
collectExtendedClasses(classes, superclass, target);
|
||||
classes.add(superclass);
|
||||
collectInterfaces(clazz, classes);
|
||||
}
|
||||
|
||||
private static void collectInterfaces(Class<?> clazz, Set<Class<?>> classes) {
|
||||
for (Class<?> type : clazz.getInterfaces()) {
|
||||
if (classes.add(type)) {
|
||||
collectInterfaces(type, classes);
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -1,15 +1,15 @@
|
||||
package work.slhaf.agent.common.chat;
|
||||
package work.slhaf.partner.api.chat;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import work.slhaf.agent.common.chat.constant.ChatConstant;
|
||||
import work.slhaf.agent.common.chat.pojo.ChatBody;
|
||||
import work.slhaf.agent.common.chat.pojo.ChatResponse;
|
||||
import work.slhaf.agent.common.chat.pojo.Message;
|
||||
import work.slhaf.agent.common.chat.pojo.PrimaryChatResponse;
|
||||
import work.slhaf.partner.api.chat.constant.ChatConstant;
|
||||
import work.slhaf.partner.api.chat.pojo.ChatBody;
|
||||
import work.slhaf.partner.api.chat.pojo.ChatResponse;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.api.chat.pojo.PrimaryChatResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.agent.common.chat.constant;
|
||||
package work.slhaf.partner.api.chat.constant;
|
||||
|
||||
public class ChatConstant {
|
||||
|
||||
@@ -7,13 +7,6 @@ public class ChatConstant {
|
||||
public static final String SYSTEM = "system";
|
||||
public static final String ASSISTANT = "assistant";
|
||||
}
|
||||
|
||||
public static class Model {
|
||||
public static final String DEEP_SEEK_CHAT = "deepseek-chat";
|
||||
public static final String GLM_4_FLASH = "glm-4_flash";
|
||||
public static final String GLM_4_PLUS = "glm-4_plus";
|
||||
public static final String GLM_4_0520 = "glm-4_0520";
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
public static final String SUCCESS = "success";
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.agent.common.chat.pojo;
|
||||
package work.slhaf.partner.api.chat.pojo;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.agent.common.chat.pojo;
|
||||
package work.slhaf.partner.api.chat.pojo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -1,7 +1,7 @@
|
||||
package work.slhaf.agent.common.chat.pojo;
|
||||
package work.slhaf.partner.api.chat.pojo;
|
||||
|
||||
import lombok.*;
|
||||
import work.slhaf.agent.common.serialize.PersistableObject;
|
||||
import work.slhaf.partner.api.common.entity.PersistableObject;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package work.slhaf.agent.common.chat.pojo;
|
||||
package work.slhaf.partner.api.chat.pojo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import work.slhaf.agent.common.serialize.PersistableObject;
|
||||
import work.slhaf.partner.api.common.entity.PersistableObject;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.agent.common.chat.pojo;
|
||||
package work.slhaf.partner.api.chat.pojo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.agent.common.serialize;
|
||||
package work.slhaf.partner.api.common.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
7
Partner-Api/src/test/java/factory/AgentRegisterTest.java
Normal file
7
Partner-Api/src/test/java/factory/AgentRegisterTest.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package factory;
|
||||
|
||||
public class AgentRegisterTest {
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
}
|
||||
24
Partner-Api/src/test/java/module/ModuleProxyTest.java
Normal file
24
Partner-Api/src/test/java/module/ModuleProxyTest.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package module;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ModuleProxyTest {
|
||||
@Test
|
||||
public void test() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
Class<? extends AgentRunningModule> clazz = new ByteBuddy().subclass(MyAgentRunningModule.class)
|
||||
.method(ElementMatchers.isOverriddenFrom(AgentRunningModule.class))
|
||||
.intercept(MethodDelegation.to(
|
||||
new MyModuleProxyInterceptor()
|
||||
))
|
||||
.make()
|
||||
.load(ModuleProxyTest.class.getClassLoader())
|
||||
.getLoaded();
|
||||
clazz.getConstructor().newInstance().execute(null);
|
||||
}
|
||||
}
|
||||
11
Partner-Api/src/test/java/module/MyAgentRunningModule.java
Normal file
11
Partner-Api/src/test/java/module/MyAgentRunningModule.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package module;
|
||||
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.abstracts.AgentRunningModule;
|
||||
import work.slhaf.partner.api.agent.runtime.interaction.flow.entity.RunningFlowContext;
|
||||
|
||||
public class MyAgentRunningModule extends AgentRunningModule {
|
||||
@Override
|
||||
public void execute(RunningFlowContext context) {
|
||||
System.out.println("MyAgentRunningModule");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package module;
|
||||
|
||||
import net.bytebuddy.implementation.bind.annotation.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class MyModuleProxyInterceptor {
|
||||
public MyModuleProxyInterceptor() {}
|
||||
|
||||
@RuntimeType
|
||||
public Object intercept(@Origin Method method, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper, @This Object proxy) throws Exception {
|
||||
System.out.println("22222");
|
||||
Object res = zuper.call();
|
||||
System.out.println("11111");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
33
Partner-Main/pom.xml
Normal file
33
Partner-Main/pom.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>work.slhaf</groupId>
|
||||
<artifactId>Partner</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>Partner-Main</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>work.slhaf</groupId>
|
||||
<artifactId>Partner-Api</artifactId>
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user