mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(db): support KingbaseES (人大金仓) domestic database (#324)
Add KingbaseES support as an opt-in profile: dedicated migration tree, bilingual seed data, runtime DbType detection (KINGBASE_ES / POSTGRE_SQL), and JDBC URL handling in the datasource manager.
This commit is contained in:
parent
ac035d6d99
commit
446f34b6b5
@ -337,6 +337,39 @@
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<!-- Flyway PostgreSQL support (used by KingbaseES as well since KingbaseES is PostgreSQL-compatible) -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.7.7</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- ===== KingbaseES (人大金仓) JDBC Driver ===== -->
|
||||
<!--
|
||||
KingbaseES JDBC driver is NOT available on Maven Central.
|
||||
Install it manually before building:
|
||||
mvn install:install-file \\
|
||||
-Dfile=/path/to/kingbase8-8.6.0.jar \\
|
||||
-DgroupId=com.kingbase8 \\
|
||||
-DartifactId=kingbase8 \\
|
||||
-Dversion=8.6.0 \\
|
||||
-Dpackaging=jar
|
||||
|
||||
Or configure a private Maven repository that hosts the driver.
|
||||
The driver JAR can be obtained from the KingbaseES installation
|
||||
directory: ${KINGBASE_HOME}/Interface/jdbc/kingbase8-8.6.0.jar
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.kingbase8</groupId>
|
||||
<artifactId>kingbase8</artifactId>
|
||||
<version>8.6.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- ===== Spring Boot Test ===== -->
|
||||
<dependency>
|
||||
|
||||
@ -1,19 +1,29 @@
|
||||
package vip.mate;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* MateClaw - Personal AI Assistant
|
||||
* Powered by Spring AI Alibaba
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootApplication(exclude = {
|
||||
// Disable Spring AI MCP Client auto-configuration (lifecycle owned by McpClientManager).
|
||||
org.springframework.ai.mcp.client.common.autoconfigure.McpClientAutoConfiguration.class,
|
||||
@ -33,22 +43,80 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
@MapperScan("vip.mate.**.repository")
|
||||
public class MateClawApplication {
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
/** Cached DbType for the PaginationInnerInterceptor. */
|
||||
private volatile DbType resolvedDbType;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MateClawApplication.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the actual database type from the live DataSource so the
|
||||
* {@link PaginationInnerInterceptor} always uses the correct dialect,
|
||||
* even when the JDBC URL is wrapped by a proxy (HikariCP, P6Spy, etc.).
|
||||
*
|
||||
* <p>DbType is cached after the first successful detection; a failure
|
||||
* falls back to the value set in {@code mybatis-plus.global-config.db-config.db-type},
|
||||
* or eventually to {@link DbType#MYSQL} — but by then the connection
|
||||
* pool would already have failed.
|
||||
*/
|
||||
@PostConstruct
|
||||
void detectDbType() {
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
String productName = conn.getMetaData().getDatabaseProductName().toLowerCase();
|
||||
if (productName.contains("kingbase")) {
|
||||
resolvedDbType = DbType.KINGBASE_ES;
|
||||
} else if (productName.contains("postgresql")) {
|
||||
resolvedDbType = DbType.POSTGRE_SQL;
|
||||
} else if (productName.contains("mysql") || productName.contains("mariadb")) {
|
||||
resolvedDbType = DbType.MYSQL;
|
||||
} else if (productName.contains("h2")) {
|
||||
resolvedDbType = DbType.H2;
|
||||
} else {
|
||||
// Let the PaginationInnerInterceptor auto-detect at query time
|
||||
resolvedDbType = null;
|
||||
}
|
||||
if (resolvedDbType != null) {
|
||||
log.info("Detected database type: {} (product={})", resolvedDbType, productName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Could not detect database type — PaginationInnerInterceptor will auto-detect on first query: {}",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MyBatis Plus pagination plugin.
|
||||
*
|
||||
* <p>DbType is auto-detected from the JDBC connection at runtime rather
|
||||
* than hardcoded. Hardcoding H2 here meant the MySQL deployment used
|
||||
* the H2 dialect for the count query, which silently returned 0 —
|
||||
* frontends saw records but total=0 and couldn't paginate (RFC-042 P0).
|
||||
* <p>When {@code resolvedDbType} is available the interceptor uses it directly;
|
||||
* otherwise it falls back to JDBC-URL auto-detection, which works for
|
||||
* {@code jdbc:kingbase8://} but not for proxied DataSources (RFC-042 P0).
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
PaginationInnerInterceptor pagination = resolvedDbType != null
|
||||
? new PaginationInnerInterceptor(resolvedDbType)
|
||||
: new PaginationInnerInterceptor();
|
||||
interceptor.addInnerInterceptor(pagination);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a clear "READY" banner after all post-startup initialization,
|
||||
* so operators can tell at a glance when the application is ready to serve.
|
||||
*/
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onReady() {
|
||||
log.info("");
|
||||
log.info("╔══════════════════════════════════════════════════════════════════════╗");
|
||||
log.info("║ MateClaw is READY ✓ ║");
|
||||
log.info("║ Web UI → http://localhost:18088 ║");
|
||||
log.info("║ Swagger → http://localhost:18088/swagger-ui.html ║");
|
||||
log.info("╚══════════════════════════════════════════════════════════════════════╝");
|
||||
log.info("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.mate.channel.dingtalk.DingTalkChannelAdapter;
|
||||
import vip.mate.channel.discord.DiscordChannelAdapter;
|
||||
@ -230,9 +231,12 @@ public class ChannelManager {
|
||||
);
|
||||
|
||||
/**
|
||||
* 应用启动完成后自动加载并启动所有已启用的渠道
|
||||
* 使用 ApplicationReadyEvent 确保数据库 schema/data 初始化完成
|
||||
* 应用启动完成后自动加载并启动所有已启用的渠道。
|
||||
* 使用 ApplicationReadyEvent 确保数据库 schema/data 初始化完成。
|
||||
* {@code @Async} — 渠道适配器的网络建连(如 Discord WebSocket / Telegram webhook)
|
||||
* 可能因外部网络不可达而阻塞数分钟,异步启动避免卡住主线程。
|
||||
*/
|
||||
@Async
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void init() {
|
||||
log.info("Initializing ChannelManager...");
|
||||
|
||||
@ -14,6 +14,12 @@ import java.util.concurrent.Executor;
|
||||
* The inner delegate uses virtual threads (JDK 21); the outer
|
||||
* {@link DelegatingSecurityContextTaskExecutor} wrapper propagates the caller's
|
||||
* SecurityContext (JWT identity, audit permissions) to every @Async invocation.
|
||||
* <p>
|
||||
* A concurrency limit is set to prevent runaway virtual-thread creation
|
||||
* from exhausting the HikariCP pool (typical pattern: a stampede of
|
||||
* {@code @Async} tasks all trying to acquire DB connections at the same
|
||||
* minute boundary). Excess tasks are rejected immediately so the
|
||||
* scheduler threads never block on submission.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@ -21,6 +27,13 @@ import java.util.concurrent.Executor;
|
||||
@EnableAsync
|
||||
public class AsyncSecurityConfig implements AsyncConfigurer {
|
||||
|
||||
/**
|
||||
* Cap in-flight async tasks. Well below HikariCP maximum-pool-size (30)
|
||||
* so async tasks never saturate the pool on their own — non-async paths
|
||||
* (HTTP requests, SSE, channel adapters) always have headroom.
|
||||
*/
|
||||
private static final int ASYNC_CONCURRENCY_LIMIT = 24;
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
// Keep DelegatingSecurityContextTaskExecutor so SecurityContext
|
||||
@ -28,6 +41,7 @@ public class AsyncSecurityConfig implements AsyncConfigurer {
|
||||
// Replace the inner platform-thread pool with a virtual-thread executor.
|
||||
var delegate = new SimpleAsyncTaskExecutorBuilder()
|
||||
.virtualThreads(true)
|
||||
.concurrencyLimit(ASYNC_CONCURRENCY_LIMIT)
|
||||
.threadNamePrefix("async-vt-")
|
||||
.build();
|
||||
return new DelegatingSecurityContextTaskExecutor(delegate);
|
||||
|
||||
@ -38,9 +38,12 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
|
||||
private final DataSource dataSource;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
/** Cached flag: true when running on MySQL/MariaDB, false for H2. */
|
||||
/** Cached flag: true when running on MySQL/MariaDB, false for H2/Kingbase. */
|
||||
private volatile Boolean isMySQL;
|
||||
|
||||
/** Cached flag: true when running on KingbaseES. */
|
||||
private volatile Boolean isKingbase;
|
||||
|
||||
/**
|
||||
* When true, wait for Desktop splash screen to call /setup/init with chosen language.
|
||||
* When false (default), auto-initialize immediately on startup.
|
||||
@ -111,6 +114,8 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
|
||||
String scriptName;
|
||||
if (isMySQL()) {
|
||||
scriptName = "en-US".equals(locale) ? "db/data-mysql-en.sql" : "db/data-mysql-zh.sql";
|
||||
} else if (isKingbase()) {
|
||||
scriptName = "en-US".equals(locale) ? "db/data-kingbase-en.sql" : "db/data-kingbase-zh.sql";
|
||||
} else {
|
||||
scriptName = "en-US".equals(locale) ? "db/data-en.sql" : "db/data-zh.sql";
|
||||
}
|
||||
@ -159,15 +164,29 @@ public class DatabaseBootstrapRunner implements ApplicationRunner {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
String dbProduct = connection.getMetaData().getDatabaseProductName().toLowerCase();
|
||||
isMySQL = dbProduct.contains("mysql") || dbProduct.contains("mariadb");
|
||||
log.info("Detected database: {} (MySQL mode: {})", dbProduct, isMySQL);
|
||||
isKingbase = dbProduct.contains("kingbase");
|
||||
if (isKingbase) {
|
||||
log.info("Detected database: {} (KingbaseES mode)", dbProduct);
|
||||
} else {
|
||||
log.info("Detected database: {} (MySQL mode: {})", dbProduct, isMySQL);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to detect database type, falling back to H2 mode", e);
|
||||
isMySQL = false;
|
||||
isKingbase = false;
|
||||
}
|
||||
}
|
||||
return isMySQL;
|
||||
}
|
||||
|
||||
private boolean isKingbase() {
|
||||
if (isKingbase == null) {
|
||||
// Trigger detection
|
||||
isMySQL();
|
||||
}
|
||||
return isKingbase != null && isKingbase;
|
||||
}
|
||||
|
||||
private void runScript(String path) {
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
populator.setContinueOnError(false);
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package vip.mate.config;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import com.zaxxer.hikari.HikariPoolMXBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Periodically logs HikariCP pool metrics so operators can detect
|
||||
* connection exhaustion / leak patterns before the application stalls.
|
||||
* <p>
|
||||
* Runs every 30s — frequent enough to catch a pool drain within 1-2
|
||||
* ticks, cheap enough to never become a problem itself.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class HikariPoolMonitor {
|
||||
|
||||
private final HikariDataSource hikari;
|
||||
|
||||
public HikariPoolMonitor(DataSource dataSource) {
|
||||
if (dataSource instanceof HikariDataSource hds) {
|
||||
this.hikari = hds;
|
||||
} else if (dataSource instanceof org.springframework.jdbc.datasource.DelegatingDataSource dds
|
||||
&& dds.getTargetDataSource() instanceof HikariDataSource hds) {
|
||||
// Some auto-configurations wrap Hikari in a delegating DS.
|
||||
this.hikari = hds;
|
||||
} else {
|
||||
this.hikari = null;
|
||||
log.info("[HikariMonitor] DataSource is not HikariCP — pool monitor disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 30_000, initialDelay = 60_000)
|
||||
public void logPoolStats() {
|
||||
if (hikari == null) return;
|
||||
|
||||
HikariPoolMXBean pool = hikari.getHikariPoolMXBean();
|
||||
if (pool == null) return;
|
||||
|
||||
int active = pool.getActiveConnections();
|
||||
int idle = pool.getIdleConnections();
|
||||
int total = pool.getTotalConnections();
|
||||
int waiting = pool.getThreadsAwaitingConnection();
|
||||
|
||||
// Normal — log at debug so it doesn't spam the console.
|
||||
log.debug("[HikariMonitor] pool: active={}, idle={}, total={}, max={}, waiting={}",
|
||||
active, idle, total, hikari.getMaximumPoolSize(), waiting);
|
||||
|
||||
// Warning threshold: more than 80 % of the pool is active AND
|
||||
// threads are queued waiting for a connection.
|
||||
int maxPool = hikari.getMaximumPoolSize();
|
||||
if (active > maxPool * 0.8 && waiting > 0) {
|
||||
log.warn("[HikariMonitor] Pool pressure detected — active={}, idle={}, "
|
||||
+ "total={}, max={}, waiting={}", active, idle, total, maxPool, waiting);
|
||||
}
|
||||
|
||||
// Critical: pool is fully saturated AND threads are waiting.
|
||||
if (active >= maxPool && waiting > 0) {
|
||||
log.error("[HikariMonitor] Pool EXHAUSTED — active={}, max={}, waiting={}. "
|
||||
+ "Application will appear frozen until connections are released.",
|
||||
active, maxPool, waiting);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package vip.mate.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
/**
|
||||
* Scheduled-task thread-pool configuration.
|
||||
* <p>
|
||||
* Spring Boot's auto-configured {@code TaskScheduler} defaults to
|
||||
* <b>pool-size = 1</b>, which serializes every {@code @Scheduled}
|
||||
* method across the entire application. With 15+ scheduled beans
|
||||
* (health checks, trigger sync, fact rebuild, feature-flag refresh,
|
||||
* etc.) and several firing on the same cron tick, a single-thread
|
||||
* pool causes back-pressure that makes the application appear "frozen"
|
||||
* when any task blocks briefly on database I/O or lock acquisition.
|
||||
* <p>
|
||||
* This config sets a pool large enough to absorb simultaneous
|
||||
* minute/half-hour boundaries without head-of-line blocking, while
|
||||
* keeping thread count low so the scheduler does not contend with
|
||||
* HikariCP or the async virtual-thread pool.
|
||||
* <p>
|
||||
* <b>IMPORTANT:</b> {@code @EnableScheduling} is declared once on
|
||||
* {@link vip.mate.MateClawApplication}. Declaring it here as well
|
||||
* creates a <em>second</em> {@code ScheduledAnnotationBeanPostProcessor}
|
||||
* that competes with the first, resulting in some tasks unknowingly
|
||||
* scheduled on the default single-thread executor.
|
||||
*/
|
||||
@Configuration
|
||||
public class SchedulingConfig implements SchedulingConfigurer {
|
||||
|
||||
/** Pool threads — enough for concurrent ticks but kept moderate. */
|
||||
private static final int POOL_SIZE = 4;
|
||||
|
||||
@Bean
|
||||
public TaskScheduler taskScheduler() {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(POOL_SIZE);
|
||||
scheduler.setThreadNamePrefix("sched-");
|
||||
scheduler.setRemoveOnCancelPolicy(true);
|
||||
scheduler.setAwaitTerminationSeconds(30);
|
||||
scheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar registrar) {
|
||||
// Re-use the singleton TaskScheduler bean so we never create two
|
||||
// separate thread-pool instances (the @Bean above is the single
|
||||
// source of truth).
|
||||
registrar.setTaskScheduler(taskScheduler());
|
||||
}
|
||||
}
|
||||
@ -42,7 +42,9 @@ public class ShedLockConfig {
|
||||
JdbcTemplateLockProvider.Configuration.builder()
|
||||
.withJdbcTemplate(new JdbcTemplate(dataSource))
|
||||
.withTableName("shedlock")
|
||||
.usingDbTime() // server-side NOW() — avoids node clock drift
|
||||
// usingDbTime() removed — KingbaseES not in ShedLock's
|
||||
// built-in dialect map; app-server time is sufficient
|
||||
// given lockAtMostFor=PT30M
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@ -118,6 +118,14 @@ public class DatasourceConnectionManager implements DisposableBean {
|
||||
extra = (extra == null || extra.isBlank()) ? schemaParam : extra + "&" + schemaParam;
|
||||
}
|
||||
break;
|
||||
case "kingbase":
|
||||
case "kingbasees":
|
||||
baseUrl = String.format("jdbc:kingbase8://%s:%d/%s", host, port, dbName);
|
||||
if (entity.getSchemaName() != null && !entity.getSchemaName().isBlank()) {
|
||||
String schemaParam = "currentSchema=" + entity.getSchemaName();
|
||||
extra = (extra == null || extra.isBlank()) ? schemaParam : extra + "&" + schemaParam;
|
||||
}
|
||||
break;
|
||||
case "clickhouse":
|
||||
baseUrl = String.format("jdbc:clickhouse://%s:%d/%s", host, port, dbName);
|
||||
break;
|
||||
|
||||
@ -79,6 +79,7 @@ public class ProviderInitProbe {
|
||||
this.strategies = map;
|
||||
}
|
||||
|
||||
@Async
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onApplicationReady() {
|
||||
probeAllConfigured();
|
||||
|
||||
@ -2,6 +2,7 @@ package vip.mate.memory.fact.projection;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.mate.agent.AgentService;
|
||||
@ -14,6 +15,9 @@ import java.util.List;
|
||||
* Scheduled full rebuild of the fact projection for all active agents.
|
||||
* Cron expression configured via mate.memory.fact.projection-rebuild-cron.
|
||||
* Only runs when projection-enabled=true.
|
||||
* <p>
|
||||
* {@code @Async} keeps the scheduler-thread pool free — the actual DB
|
||||
* work runs on the virtual-thread async executor.
|
||||
*
|
||||
* @author MateClaw Team
|
||||
*/
|
||||
@ -26,6 +30,7 @@ public class FactProjectionScheduler {
|
||||
private final FactProjectionBuilder projectionBuilder;
|
||||
private final MemoryProperties properties;
|
||||
|
||||
@Async
|
||||
@Scheduled(cron = "${mate.memory.fact.projection-rebuild-cron:0 */30 * * * ?}")
|
||||
public void rebuildAll() {
|
||||
if (!properties.getFact().isProjectionEnabled()) {
|
||||
|
||||
@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import vip.mate.channel.ChannelManager;
|
||||
import vip.mate.llm.service.ModelProviderService;
|
||||
@ -65,7 +66,9 @@ public class PluginManager {
|
||||
* Load all plugins on application startup.
|
||||
* Scans three paths in priority order: workspace > user-global.
|
||||
* Higher priority plugins shadow lower priority ones with the same name.
|
||||
* {@code @Async} — 文件系统扫描和 JAR 类加载不阻塞主启动线程。
|
||||
*/
|
||||
@Async
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
@Order(250)
|
||||
public void loadAllPlugins() {
|
||||
|
||||
71
mateclaw-server/src/main/resources/application-kingbase.yml
Normal file
71
mateclaw-server/src/main/resources/application-kingbase.yml
Normal file
@ -0,0 +1,71 @@
|
||||
spring:
|
||||
datasource:
|
||||
# 人大金仓 KingbaseES 数据源配置
|
||||
# 默认连接参数:
|
||||
# DB_HOST=localhost, DB_PORT=54321, DB_NAME=mateclaw
|
||||
# KingbaseES 基于 PostgreSQL,兼容 PostgreSQL JDBC 协议
|
||||
# 通过环境变量覆盖:DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD
|
||||
#
|
||||
# JDBC 超时参数说明:
|
||||
# connectTimeout=10 — TCP 连接超时(秒),避免 OS 级超时(60-180s)
|
||||
# socketTimeout=30 — socket 读取超时(秒),防止僵死连接永久阻塞
|
||||
# loginTimeout=10 — 数据库登录超时(秒)
|
||||
url: jdbc:kingbase8://${DB_HOST:localhost}:${DB_PORT:54321}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10
|
||||
driver-class-name: com.kingbase8.Driver
|
||||
username: ${DB_USERNAME:system}
|
||||
password: ${DB_PASSWORD:Admin2026@123}
|
||||
hikari:
|
||||
maximum-pool-size: 30
|
||||
minimum-idle: 5
|
||||
connection-timeout: 30000
|
||||
idle-timeout: 300000
|
||||
# 原 1800000(30min) 与巡检中观察到的 30min 卡住周期吻合 — 若存在连接泄漏,
|
||||
# 30min 后 HikariCP 强制回收旧连接才恢复。降至 10min 加快故障自愈速度。
|
||||
max-lifetime: 600000
|
||||
leak-detection-threshold: 30000
|
||||
# 连接池初始化超时:若在此时长内无法获取首个有效连接,快速失败而非无限等待
|
||||
initialization-fail-timeout: 30000
|
||||
# Kingbase ES:每个新连接初始化时强制设置 search_path,
|
||||
# 作为 currentSchema 参数的双重保障,防止连接落入 public schema
|
||||
connection-init-sql: SET search_path TO mateclaw
|
||||
|
||||
flyway:
|
||||
# Flyway uses PostgreSQL JDBC driver because KingbaseES is PostgreSQL-compatible
|
||||
# on the wire protocol level. The application runtime (MyBatis) still uses the
|
||||
# Kingbase8 driver for production queries.
|
||||
#
|
||||
# JDBC 超时参数:避免 Flyway 连接阶段也因 OS 级 TCP 超时而"卡住"
|
||||
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:54321}/${DB_NAME:mateclaw}?currentSchema=mateclaw&connectTimeout=10&socketTimeout=30&loginTimeout=10
|
||||
user: ${DB_USERNAME:system}
|
||||
password: ${DB_PASSWORD:Admin2026@123}
|
||||
locations:
|
||||
- classpath:db/migration/kingbase
|
||||
# 迁移前确保目标 schema 存在(Kingbase 不会自动创建非 public schema)
|
||||
init-sqls:
|
||||
- CREATE SCHEMA IF NOT EXISTS mateclaw
|
||||
# 首次迁移或脚本变更后跳过 checksum 校验,避免因脚本转换导致的校验失败卡住
|
||||
validate-on-migrate: false
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: false
|
||||
|
||||
# MyBatis Plus — Kingbase ES 显式数据库类型
|
||||
# PaginationInnerInterceptor 使用无参构造时依赖 JDBC URL 自动检测,
|
||||
# 在 DataSource 代理/包装层下可能检测失败回退到 MYSQL 方言,
|
||||
# 导致分页 SQL 生成 LIMIT offset,count 而非 LIMIT count OFFSET offset。
|
||||
# 显式指定 kingbase_es 确保所有分页/ID生成/批量操作使用正确的方言。
|
||||
mybatis-plus:
|
||||
global-config:
|
||||
db-config:
|
||||
db-type: kingbase_es
|
||||
|
||||
# Production (multi-tenant server) hardening: fail closed on source-path
|
||||
# validation. With no allowed-source-roots configured, every KB source
|
||||
# directory is rejected rather than allowing full-filesystem reads — so a
|
||||
# missing allow-list cannot silently re-open arbitrary directory scanning.
|
||||
# Operators set mate.wiki.allowed-source-roots to permit specific roots.
|
||||
# The default profile (H2 / desktop / single-tenant) leaves this off.
|
||||
mate:
|
||||
wiki:
|
||||
require-allowed-roots: true
|
||||
@ -13,7 +13,7 @@ spring:
|
||||
# treat the connection as utf8mb4, force the connection collation via
|
||||
# connectionCollation=utf8mb4_unicode_ci — that is what prevents the
|
||||
# `Data truncation: Incorrect string value` errors on emoji/CJK ext.
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mateclaw}?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8mb4_unicode_ci&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:mateclaw}?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8mb4_unicode_ci&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&connectTimeout=10000&socketTimeout=30000
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: ${DB_USERNAME:root}
|
||||
password: ${DB_PASSWORD:mateclaw123}
|
||||
|
||||
@ -19,7 +19,7 @@ spring:
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
# 数据源(默认 H2,生产切换为 mysql profile)
|
||||
# 数据源(默认 H2,生产切换为 mysql 或 kingbase profile)
|
||||
datasource:
|
||||
url: jdbc:h2:file:./data/mateclaw;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
|
||||
driver-class-name: org.h2.Driver
|
||||
@ -131,6 +131,8 @@ mateclaw:
|
||||
# Set this when agents deliver download links to channels/clients that cannot
|
||||
# resolve a relative URL (IM messages, copied links, external downloads).
|
||||
public-base-url: ${MATECLAW_PUBLIC_BASE_URL:}
|
||||
browser:
|
||||
ssrf-check-enabled: false
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:MateClaw-JWT-Secret-Key-2024-Please-Change-In-Production}
|
||||
expiration: 86400000
|
||||
|
||||
1916
mateclaw-server/src/main/resources/db/data-kingbase-en.sql
Normal file
1916
mateclaw-server/src/main/resources/db/data-kingbase-en.sql
Normal file
File diff suppressed because it is too large
Load Diff
1914
mateclaw-server/src/main/resources/db/data-kingbase-zh.sql
Normal file
1914
mateclaw-server/src/main/resources/db/data-kingbase-zh.sql
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,16 @@
|
||||
-- V100: System-level defaults for vision and video sidecar routing.
|
||||
-- When the agent's primary model lacks the modality required by an attachment,
|
||||
-- the runtime delegates a single caption call to the model recorded here.
|
||||
-- Empty value = not configured; the UI then asks the user to pick one.
|
||||
-- Setting value stores mate_model_config.id as a string (provider+model_name pairs are not unique).
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
VALUES (1000002001, 'default.vision_model', '',
|
||||
'Default vision-capable model id (mate_model_config.id) used by sidecar router when primary model lacks VISION modality',
|
||||
NOW(), NOW())
|
||||
ON CONFLICT (id) DO UPDATE SET setting_key = EXCLUDED.setting_key;
|
||||
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
VALUES (1000002002, 'default.video_model', '',
|
||||
'Default video-capable model id (mate_model_config.id) used by sidecar router when primary model lacks VIDEO modality',
|
||||
NOW(), NOW())
|
||||
ON CONFLICT (id) DO UPDATE SET setting_key = EXCLUDED.setting_key;
|
||||
@ -0,0 +1,14 @@
|
||||
-- See the matching H2 file for context. This migration purges any
|
||||
-- orphan rows that earlier releases persisted with a blank rule_id and
|
||||
-- then installs a CHECK constraint so the schema itself rejects blank
|
||||
-- rule_id, defending against any future code path that bypasses the
|
||||
-- service-layer guard. CHECK constraints are enforced from MySQL 8.0.16
|
||||
-- onward; this project targets MySQL 8.0+ so the constraint is live.
|
||||
|
||||
DELETE FROM mate_tool_guard_rule
|
||||
WHERE (rule_id IS NULL OR LENGTH(TRIM(rule_id)) = 0)
|
||||
AND (builtin IS NULL OR builtin = 0);
|
||||
|
||||
ALTER TABLE mate_tool_guard_rule
|
||||
ADD CONSTRAINT ck_tool_guard_rule_id_nonblank
|
||||
CHECK (rule_id IS NOT NULL AND LENGTH(TRIM(rule_id)) > 0);
|
||||
@ -0,0 +1,19 @@
|
||||
-- Enforce unique Agent name within a workspace. See H2 variant for context.
|
||||
--
|
||||
-- Step 1 — rename pre-existing duplicates. PostgreSQL uses UPDATE ... FROM
|
||||
-- instead of MySQL's UPDATE ... JOIN syntax, and || instead of CONCAT.
|
||||
-- SYS_GUID() replaces MySQL's UUID() for deterministic collision avoidance.
|
||||
UPDATE mate_agent t
|
||||
SET name = '__mate_dup_v102__' || t.id || '__' || SYS_GUID()
|
||||
FROM (
|
||||
SELECT workspace_id, name, MIN(id) AS keep_id
|
||||
FROM mate_agent
|
||||
GROUP BY workspace_id, name
|
||||
HAVING COUNT(*) > 1
|
||||
) k
|
||||
WHERE t.workspace_id = k.workspace_id
|
||||
AND t.name = k.name
|
||||
AND t.id <> k.keep_id;
|
||||
|
||||
-- Step 2 — add the unique index, idempotent via IF NOT EXISTS (PostgreSQL-native).
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_workspace_name ON mate_agent (workspace_id, name);
|
||||
@ -0,0 +1,2 @@
|
||||
-- Drop the dead mate_fact_entity_ref table. See H2 variant for context.
|
||||
DROP TABLE IF EXISTS mate_fact_entity_ref;
|
||||
@ -0,0 +1,15 @@
|
||||
-- V104: track which input format a chunk's stored embedding was generated against.
|
||||
-- The embedding input builder concatenates raw title / header breadcrumb / page
|
||||
-- number alongside chunk content; bumping the builder's CURRENT_INPUT_VERSION
|
||||
-- forces a re-embed pass without changing the model. NULL is treated as the
|
||||
-- legacy content-only format and re-embedded lazily on the next pass.
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding_text_version'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN embedding_text_version VARCHAR(32) NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,66 @@
|
||||
-- Reusable user-defined prompt templates ("transformations") that run over
|
||||
-- a raw material's extracted text and persist the LLM output as an artifact
|
||||
-- on the knowledge base. Templates can be flagged apply_default so the
|
||||
-- ingestion pipeline runs them automatically once a raw material reaches
|
||||
-- the completed state. Manual / agent-tool runs are also supported.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_transformation (
|
||||
id BIGINT PRIMARY KEY,
|
||||
|
||||
kb_id BIGINT NULL,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
|
||||
name VARCHAR(64) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(1024),
|
||||
|
||||
prompt_template TEXT NOT NULL,
|
||||
|
||||
apply_default SMALLINT NOT NULL DEFAULT 0,
|
||||
model_id BIGINT NULL,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wtr_kb ON mate_wiki_transformation (kb_id, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wtr_ws ON mate_wiki_transformation (workspace_id, deleted);
|
||||
|
||||
-- Unique name per KB (NULL kb_id rows compete in a shared "global" bucket).
|
||||
-- MySQL treats NULL as distinct in unique indexes, so workspace-wide names
|
||||
-- can technically collide; the service layer enforces uniqueness for the
|
||||
-- NULL-kb_id case in software.
|
||||
CREATE UNIQUE INDEX uk_wtr_kb_name ON mate_wiki_transformation (kb_id, name, deleted);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_transformation_run (
|
||||
id BIGINT PRIMARY KEY,
|
||||
|
||||
transformation_id BIGINT NOT NULL,
|
||||
kb_id BIGINT NOT NULL,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
|
||||
input_kind VARCHAR(16) NOT NULL,
|
||||
raw_id BIGINT NULL,
|
||||
page_id BIGINT NULL,
|
||||
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
|
||||
output TEXT,
|
||||
error VARCHAR(2048),
|
||||
model_id BIGINT NULL,
|
||||
|
||||
triggered_by VARCHAR(32) NOT NULL DEFAULT 'manual',
|
||||
|
||||
started_at TIMESTAMP(3) NULL,
|
||||
completed_at TIMESTAMP(3) NULL,
|
||||
duration_ms BIGINT NULL,
|
||||
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wtrn_tr ON mate_wiki_transformation_run (transformation_id, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wtrn_kb ON mate_wiki_transformation_run (kb_id, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wtrn_raw ON mate_wiki_transformation_run (raw_id, deleted);
|
||||
@ -0,0 +1,24 @@
|
||||
-- Two-part follow-up to V105 so a transformation's output can flow back
|
||||
-- into the KB as a first-class artifact. See the h2 sibling migration for
|
||||
-- the prose explanation. MySQL lacks ADD COLUMN IF NOT EXISTS, so each
|
||||
-- column is guarded by an INFORMATION_SCHEMA check + prepared statement.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_target'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation ADD COLUMN output_target VARCHAR(16) NOT NULL DEFAULT 'none';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'output_page_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation_run ADD COLUMN output_page_id BIGINT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,33 @@
|
||||
-- Page-level embedding columns. See the h2 sibling for the prose
|
||||
-- explanation. MySQL lacks ADD COLUMN IF NOT EXISTS, so each column
|
||||
-- guarded by an INFORMATION_SCHEMA check + prepared statement.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN embedding BYTEA DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding_model'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN embedding_model VARCHAR(64) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'embedding_text_version'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN embedding_text_version VARCHAR(32) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,247 @@
|
||||
-- Starter pack: 7 workspace-wide transformation templates. See h2 sibling
|
||||
-- for the prose explanation. ON CONFLICT DO NOTHING so a re-run (e.g. via repair)
|
||||
-- never clobbers user edits.
|
||||
|
||||
INSERT INTO mate_wiki_transformation
|
||||
(id, kb_id, workspace_id, name, title, description, prompt_template,
|
||||
apply_default, model_id, enabled, output_target, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000004001, NULL, 1,
|
||||
'contract-risk-extract',
|
||||
'合同风险点提取',
|
||||
'逐条审查合同条款,标注风险等级、原文位置、AI 建议改写。配合企业场景 → 合同审查使用。',
|
||||
'你是一名企业法务审查员。从下面的合同文本中完整提取所有需要关注的风险条款,按以下结构输出 Markdown:
|
||||
|
||||
## 风险条款清单
|
||||
|
||||
对每条值得审查的条款,输出三级标题:
|
||||
|
||||
### <条款简称>
|
||||
- **风险等级**:高 / 中 / 低
|
||||
- **条款类型**:赔偿 / 责任限制 / 付款 / 保密 / 竞业 / 终止 / 管辖 / 数据保护 / 其他
|
||||
- **原文位置**:第 X 条 / 第 Y 页(材料未标号时写「未标注」)
|
||||
- **原文摘录**:用「」引用关键句
|
||||
- **风险描述**:≤ 50 字说明风险所在
|
||||
- **建议改写**:给出可直接采用的修订版本
|
||||
|
||||
## 总体评估
|
||||
|
||||
一段话总结这份合同的整体风险水位与签字建议(≤ 200 字)。
|
||||
|
||||
要求:
|
||||
- 不要虚构原文没有的条款
|
||||
- 数字与条款编号保留原样
|
||||
- 中文输出,不要任何客套或元描述
|
||||
|
||||
合同标题:{title}
|
||||
|
||||
合同正文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004002, NULL, 1,
|
||||
'meeting-action-items',
|
||||
'会议纪要 → 行动项',
|
||||
'从会议纪要中穷尽抽取决议 + 行动项(owner / 截止日 / 验收标准),适合周会、决策会议。',
|
||||
'你是会议纪要分析助理。从下面的纪要中穷尽抽取所有行动项与决议,按以下结构输出 Markdown:
|
||||
|
||||
## 决议清单
|
||||
按时间或重要性顺序列出每条明确决议;每条 ≤ 一句话。
|
||||
|
||||
## 行动项清单
|
||||
|
||||
| 序号 | 行动 | 负责人 | 截止日 | 验收标准 |
|
||||
|---|---|---|---|---|
|
||||
|
||||
要求:
|
||||
- 「行动」用动词开头(如「提交」「完成」「对齐」)
|
||||
- 负责人若未明确写「未指派」
|
||||
- 截止日若未明确写「未定」
|
||||
- 验收标准一句话写出「做完是什么样」
|
||||
- 不要把「讨论了 X」当作行动项
|
||||
|
||||
## 风险与依赖
|
||||
一句话列出会议中提到的潜在阻塞或跨团队依赖(≤ 5 条)。
|
||||
|
||||
要求:中文,无客套,无元描述。
|
||||
|
||||
会议主题:{title}
|
||||
|
||||
纪要正文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004003, NULL, 1,
|
||||
'customer-profile',
|
||||
'客户邮件 / 访谈画像',
|
||||
'把客户邮件、会议纪要、CRM 记录合成一份结构化客户画像,配合企业场景 → 客户情报使用。',
|
||||
'你是销售情报员。从下面的客户邮件 / CRM 记录 / 访谈中提取一份客户画像,按以下结构输出 Markdown:
|
||||
|
||||
## 客户档案
|
||||
- **名称**:
|
||||
- **行业 / 规模**:
|
||||
- **当前阶段**:潜在 / 沟通中 / 谈判中 / 已成交(若无明确信号写「未知」)
|
||||
- **决策链关键人**:列出姓名 + 角色 + 倾向
|
||||
|
||||
## 痛点与机会
|
||||
- 3-5 条关键痛点,每条带原文引用
|
||||
- 2-3 条潜在切入点
|
||||
|
||||
## 异议预判
|
||||
列出客户可能的反对意见 + 对应应对话术。
|
||||
|
||||
## 下一步建议
|
||||
- 3 条具体动作,按优先级排序,每条带「为什么现在做」
|
||||
|
||||
要求:不要发明文本没说的事;不确定时写「未提及」。
|
||||
|
||||
客户:{title}
|
||||
|
||||
原文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004004, NULL, 1,
|
||||
'competitor-update',
|
||||
'竞品动态摘要',
|
||||
'把新闻 / 产品 release / 招聘信号 / 客户提及合成一份竞品动态简报。',
|
||||
'你是市场情报员。从下面的材料中提取与竞争对手相关的动态,按以下结构输出 Markdown:
|
||||
|
||||
## 涉及对手
|
||||
列出材料中提到的所有竞品公司或产品。
|
||||
|
||||
## 关键动态
|
||||
|
||||
按时间倒序,每条输出:
|
||||
|
||||
### <对手 / 产品> · <动态简称>
|
||||
- **类型**:新产品 / 招聘 / 融资 / 客户胜出 / 价格调整 / 团队变动 / 其他
|
||||
- **原文摘录**:「」引用
|
||||
- **来源**:网页 / 邮件 / 新闻渠道
|
||||
- **对我们的影响**:威胁 / 机会 / 中性,一句话说明
|
||||
|
||||
## 战术建议
|
||||
3 条针对性的应对动作,按优先级排序。
|
||||
|
||||
## 监控建议
|
||||
列出值得长期追踪的关键词或信号。
|
||||
|
||||
要求:中文,不发明内容,不确定时跳过。
|
||||
|
||||
材料:{title}
|
||||
|
||||
原文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004005, NULL, 1,
|
||||
'resume-structured-extract',
|
||||
'简历结构化',
|
||||
'把简历提取为标准化档案:教育、工作、技能、亮点。适合批量初筛。',
|
||||
'你是 HR 助理。把下面的简历提取为结构化档案:
|
||||
|
||||
## 候选人信息
|
||||
- **姓名**:
|
||||
- **当前职位**:
|
||||
- **总工作年限**:
|
||||
- **专业领域**:
|
||||
|
||||
## 教育经历
|
||||
|
||||
| 学校 | 学位 / 专业 | 时间 |
|
||||
|---|---|---|
|
||||
|
||||
## 工作经历
|
||||
|
||||
按时间倒序,每段输出:
|
||||
|
||||
### <公司> · <职位> · <时间>
|
||||
- **职责摘要**:≤ 30 字
|
||||
- **关键产出**:≤ 3 条 bullet(量化优先)
|
||||
|
||||
## 技能矩阵
|
||||
|
||||
| 技能 | 熟练度 |
|
||||
|---|---|
|
||||
|
||||
## 候选人亮点
|
||||
一段话归纳最值得关注的 3 件事(≤ 150 字)。
|
||||
|
||||
要求:不要发明文本没有的经历;不确定写「未提及」;中文输出。注意:不要把性别 / 年龄 / 户籍写进画像。
|
||||
|
||||
简历:{title}
|
||||
|
||||
原文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004006, NULL, 1,
|
||||
'incident-postmortem',
|
||||
'事故 5-Why 复盘',
|
||||
'从事故报告 / 时间线生成 5-Why 链 + 整改清单 + 相似事故关键词。适合 SRE / 运维团队。',
|
||||
'你是 SRE 事故复盘助理。从下面的事故报告 / 时间线中输出 5-Why 分析:
|
||||
|
||||
## 事故概要
|
||||
- **现象**:1 句话
|
||||
- **影响范围**:用户数 / 系统 / 持续时间
|
||||
- **触发时间**:
|
||||
|
||||
## 5 Whys 链
|
||||
|
||||
1. **现象**:…
|
||||
**Why?** …
|
||||
2. **Why?** …
|
||||
3. **Why?** …
|
||||
4. **Why?** …
|
||||
5. **根因 (Why?)** …
|
||||
|
||||
## 整改清单
|
||||
|
||||
| 序号 | 行动 | 负责团队 | 优先级 | 截止 |
|
||||
|---|---|---|---|---|
|
||||
|
||||
## 相似事故关联
|
||||
列出可能相关的历史事故关键词(用于后续 wiki 检索)。
|
||||
|
||||
## 复盘要点
|
||||
3 条最值得团队记住的教训。
|
||||
|
||||
要求:中文,技术准确,不发明数据。
|
||||
|
||||
事故:{title}
|
||||
|
||||
报告:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0),
|
||||
|
||||
(1000004007, NULL, 1,
|
||||
'paper-imrad',
|
||||
'论文 IMRaD 摘要',
|
||||
'把论文 / 技术报告浓缩为 IMRaD 结构化摘要 + 关键术语表,适合研究型团队。',
|
||||
'你是学术摘要助理。把下面的论文 / 技术报告浓缩为 IMRaD 结构化摘要:
|
||||
|
||||
## Introduction
|
||||
解决什么问题,为什么重要(≤ 100 字)
|
||||
|
||||
## Methods
|
||||
使用什么方法 / 数据 / 模型(≤ 150 字)
|
||||
|
||||
## Results
|
||||
最重要的 3-5 个量化或定性结果(每条 ≤ 30 字)
|
||||
|
||||
## Discussion
|
||||
- **主要洞察**:1-2 句
|
||||
- **局限性**:1-2 条
|
||||
- **可复现性**:高 / 中 / 低,附 1 句理由
|
||||
|
||||
## 关键术语
|
||||
列出 5-8 个核心术语,每个加一句话定义。
|
||||
|
||||
要求:保留 LaTeX 公式(如有),不发明结果,中文写作。
|
||||
|
||||
论文:{title}
|
||||
|
||||
原文:
|
||||
{input_text}',
|
||||
0, NULL, 1, 'page', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
@ -0,0 +1,12 @@
|
||||
-- Output format declared on the template. See h2 sibling for the prose
|
||||
-- explanation. MySQL needs the INFORMATION_SCHEMA guard pattern.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_format'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation ADD COLUMN output_format VARCHAR(16) NOT NULL DEFAULT 'markdown';
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,34 @@
|
||||
-- V10: 声明式 Hook 系统
|
||||
-- mate_hook hook 定义(YAML 文件或 UI 写入)
|
||||
-- mate_hook_run hook 触发审计
|
||||
-- (原本命名为 V9 但与 V9__usage_cache_tokens.sql 撞号;重命名为 V10 以共存)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_hook (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(512),
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
event_type VARCHAR(64) NOT NULL,
|
||||
match_expression TEXT,
|
||||
action_kind VARCHAR(32) NOT NULL,
|
||||
action_config TEXT NOT NULL,
|
||||
rate_limit_per_min INT DEFAULT 60,
|
||||
timeout_ms INT DEFAULT 3000,
|
||||
source VARCHAR(16) DEFAULT 'db',
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_hook_event_type ON mate_hook (event_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_hook_enabled ON mate_hook (enabled);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_hook_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
hook_id BIGINT NOT NULL,
|
||||
event_type VARCHAR(64) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL,
|
||||
duration_ms INT DEFAULT 0,
|
||||
message VARCHAR(512),
|
||||
created_at TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_hook_run_hook_id ON mate_hook_run (hook_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_hook_run_created ON mate_hook_run (created_at);
|
||||
@ -0,0 +1,31 @@
|
||||
-- Record per-run token usage. See h2 sibling for prose explanation.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'input_tokens'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation_run ADD COLUMN input_tokens BIGINT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'output_tokens'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation_run ADD COLUMN output_tokens BIGINT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation_run' AND column_name = 'total_tokens'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation_run ADD COLUMN total_tokens BIGINT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,11 @@
|
||||
-- Optional JSON Schema column. See h2 sibling for the prose explanation.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation' AND column_name = 'output_schema'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation ADD COLUMN output_schema TEXT DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,26 @@
|
||||
-- V112: persist skill bundle files (scripts/ + references/) in the database.
|
||||
--
|
||||
-- Until now scripts/references only lived on the local filesystem of whichever
|
||||
-- node handled the upload. Multi-instance deployments sharing one MySQL would
|
||||
-- have the skill row visible everywhere but the script files only on one node,
|
||||
-- so any other node attempting to run a skill script either failed or ran a
|
||||
-- stale local copy. Treating the database as the canonical bundle store and
|
||||
-- the filesystem as a materialized cache resolves that gap and matches the
|
||||
-- existing pattern for SKILL.md (canonical in mate_skill.skill_content,
|
||||
-- mirrored to disk by the workspace manager).
|
||||
--
|
||||
-- TEXT (16MB) comfortably covers the per-file 1MB cap enforced by
|
||||
-- ZipSkillFetcher and the 50MB total bundle cap.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_skill_file (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
skill_id BIGINT NOT NULL,
|
||||
file_path VARCHAR(512) NOT NULL,
|
||||
content TEXT,
|
||||
content_size INT NOT NULL DEFAULT 0,
|
||||
sha256 CHAR(64),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_skill_file_path ON mate_skill_file (skill_id, file_path);
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_file_skill ON mate_skill_file (skill_id);
|
||||
@ -0,0 +1,15 @@
|
||||
-- Indexes for two recently-added read paths. Mirrors the H2 file in this
|
||||
-- migration set; PostgreSQL supports CREATE INDEX IF NOT EXISTS natively.
|
||||
--
|
||||
-- (1) idx_workspace_file_agent_filename — accelerates the memory search tool
|
||||
-- on mate_workspace_file ("agent_id = ? AND filename LIKE 'prefix%' AND
|
||||
-- content LIKE '%term%'").
|
||||
--
|
||||
-- (2) idx_async_task_conv_status — accelerates listActiveTasks(conversationId)
|
||||
-- ("WHERE conversation_id = ? AND status IN ('pending', 'running')").
|
||||
-- The existing single-column idx_async_task_conv left the status filter
|
||||
-- to a row scan; the compound resolves both in one index seek.
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_filename ON mate_workspace_file (agent_id, filename);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_conv_status ON mate_async_task (conversation_id, status);
|
||||
@ -0,0 +1,12 @@
|
||||
-- Per-conversation pin flag. See the h2 sibling for the rationale.
|
||||
-- MySQL has no ADD COLUMN IF NOT EXISTS; guard via INFORMATION_SCHEMA.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_conversation' AND column_name = 'pinned'
|
||||
) THEN
|
||||
ALTER TABLE mate_conversation ADD COLUMN pinned INT DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,45 @@
|
||||
-- V115: register the xAI (Grok) provider plus its Grok 3 / Grok 4 model catalog.
|
||||
--
|
||||
-- See the H2 copy for full background. The MySQL copy uses INSERT ... ON
|
||||
-- DUPLICATE KEY UPDATE; the api_key column is intentionally omitted from the
|
||||
-- update list so existing deployments that have already configured a key keep it.
|
||||
|
||||
-- -- Provider --------------------------------------------------------------
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time)
|
||||
VALUES (
|
||||
'xai',
|
||||
'xAI (Grok)',
|
||||
'xai-',
|
||||
'OpenAIChatModel',
|
||||
'',
|
||||
'https://api.x.ai/v1',
|
||||
'{}',
|
||||
0, 0, 1, 1, 1, 1,
|
||||
NOW(), NOW()
|
||||
)
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name,
|
||||
api_key_prefix = EXCLUDED.api_key_prefix,
|
||||
chat_model = EXCLUDED.chat_model,
|
||||
base_url = EXCLUDED.base_url,
|
||||
generate_kwargs = EXCLUDED.generate_kwargs,
|
||||
support_model_discovery = EXCLUDED.support_model_discovery,
|
||||
support_connection_check = EXCLUDED.support_connection_check,
|
||||
freeze_url = EXCLUDED.freeze_url,
|
||||
require_api_key = EXCLUDED.require_api_key,
|
||||
update_time = EXCLUDED.update_time;
|
||||
|
||||
-- -- Model catalog ---------------------------------------------------------
|
||||
-- IDs use the 1000000340-1000000343 block reserved for xAI so future Grok
|
||||
-- additions can grow contiguously.
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000340, 'Grok 4', 'xai', 'grok-4', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000341, 'Grok 4 Fast', 'xai', 'grok-4-fast', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000342, 'Grok 3', 'xai', 'grok-3', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000343, 'Grok 3 Mini', 'xai', 'grok-3-mini', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
update_time = EXCLUDED.update_time;
|
||||
@ -0,0 +1,5 @@
|
||||
-- See the H2 file for context. KingbaseES (PostgreSQL) supports
|
||||
-- ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS model_provider VARCHAR(64);
|
||||
ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS model_name VARCHAR(128);
|
||||
@ -0,0 +1,25 @@
|
||||
-- See the H2 file for context. KingbaseES (PostgreSQL) supports
|
||||
-- both ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS natively.
|
||||
--
|
||||
-- Column types: archived_at / last_activity_at use TIMESTAMP(3) to match
|
||||
-- mate_skill_usage_stat.last_loaded_at; lifecycle_state VARCHAR(16);
|
||||
-- pinned SMALLINT.
|
||||
|
||||
ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS lifecycle_state VARCHAR(16) DEFAULT 'active';
|
||||
ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS pinned SMALLINT DEFAULT 0;
|
||||
ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS archived_at TIMESTAMP(3) NULL;
|
||||
ALTER TABLE mate_skill ADD COLUMN IF NOT EXISTS last_activity_at TIMESTAMP(3) NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_lifecycle_state ON mate_skill (lifecycle_state);
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_last_activity_at ON mate_skill (last_activity_at);
|
||||
|
||||
-- One-time backfill: existing rows take their newest usage tick as the
|
||||
-- activity anchor. Rows with no usage stat stay NULL and fall through to
|
||||
-- create_time at query time via the anchor() helper.
|
||||
UPDATE mate_skill SET last_activity_at = (
|
||||
SELECT MAX(last_loaded_at) FROM mate_skill_usage_stat s
|
||||
WHERE s.skill_name = mate_skill.name
|
||||
)
|
||||
WHERE last_activity_at IS NULL;
|
||||
|
||||
UPDATE mate_skill SET lifecycle_state = 'active' WHERE lifecycle_state IS NULL;
|
||||
@ -0,0 +1,15 @@
|
||||
-- V118: Purge residual deleted=1 rows from mate_model_config.
|
||||
--
|
||||
-- V20 retired soft-delete project-wide and physically deleted every deleted=1
|
||||
-- row that existed at that point. V81 then re-introduced an isolated tombstone
|
||||
-- on id=1000000172 (the bogus 'qwen3-plus' catalog entry) -- keeping the row
|
||||
-- around as a logical audit trail. The runtime never re-uses ids and treats
|
||||
-- deleted as a vestige now that @TableLogic is gone, so a tombstoned row
|
||||
-- has no value beyond leaking into LambdaQueryWrapper queries that don't
|
||||
-- explicitly filter deleted=0.
|
||||
--
|
||||
-- The validateModel uniqueness check fix in #173 plugged one such leak; this
|
||||
-- migration eliminates the underlying class of bug by aligning mate_model_config
|
||||
-- with the V20 hard-delete posture. Idempotent: a no-op on databases without
|
||||
-- tombstones.
|
||||
DELETE FROM mate_model_config WHERE deleted = 1;
|
||||
@ -0,0 +1,32 @@
|
||||
-- V100__channel_tool_support.sql (KingbaseES dialect)
|
||||
--
|
||||
-- Mirror of the H2 V100, adapted for KingbaseES (PostgreSQL-compatible).
|
||||
-- PostgreSQL supports ADD COLUMN IF NOT EXISTS and CREATE INDEX IF NOT EXISTS
|
||||
-- natively, so guards are not needed.
|
||||
|
||||
-- 1. Add channel_id column if missing
|
||||
ALTER TABLE mate_tool ADD COLUMN IF NOT EXISTS channel_id BIGINT NULL;
|
||||
|
||||
-- 2. Add channel-id index if missing
|
||||
CREATE INDEX IF NOT EXISTS idx_mate_tool_channel ON mate_tool (channel_id);
|
||||
|
||||
-- 3. Deduplicate same-name rows before adding the unique index — keep
|
||||
-- deleted=0 first, then most recently updated, then largest id.
|
||||
DELETE FROM mate_tool
|
||||
WHERE id IN (
|
||||
SELECT id FROM (
|
||||
SELECT id,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY name
|
||||
ORDER BY
|
||||
CASE WHEN deleted = 0 THEN 0 ELSE 1 END,
|
||||
update_time DESC,
|
||||
id DESC
|
||||
) AS rn
|
||||
FROM mate_tool
|
||||
) ranked
|
||||
WHERE rn > 1
|
||||
);
|
||||
|
||||
-- 4. Add unique index on name if missing
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_mate_tool_name ON mate_tool (name);
|
||||
@ -0,0 +1,23 @@
|
||||
-- V11: Auto Skill Synthesis (RFC-023)
|
||||
-- Agent 自治创建 skill 后记录来源对话和安全扫描状态
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'source_conversation_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN source_conversation_id VARCHAR(64) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- security_scan_status: NULL(旧数据/手动创建) / PASSED / FAILED
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'security_scan_status'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN security_scan_status VARCHAR(16) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,75 @@
|
||||
-- Persistent goal — see h2/V120__agent_goal.sql for full design notes.
|
||||
--
|
||||
-- Kingbase/PostgreSQL differences vs H2:
|
||||
-- 1. CLOB -> TEXT
|
||||
-- 2. TIMESTAMP -> TIMESTAMP(3) for millisecond precision matching V117
|
||||
-- 3. BOOLEAN -> SMALLINT
|
||||
-- 4. H2 uses a PREDICATE unique index for "one active goal per
|
||||
-- conversation"; PostgreSQL does not support filtered unique indexes,
|
||||
-- so we emulate it with a STORED generated column that is NULL for
|
||||
-- non-active rows + a plain unique index. NULLs are excluded from
|
||||
-- uniqueness enforcement by PostgreSQL's default index semantics.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_goal (
|
||||
id BIGINT NOT NULL,
|
||||
conversation_id VARCHAR(64) NOT NULL,
|
||||
agent_id BIGINT NOT NULL,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
created_by VARCHAR(64) NOT NULL,
|
||||
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
exit_criteria TEXT NULL,
|
||||
success_check_prompt TEXT NULL,
|
||||
|
||||
-- DB values are always lowercase (active|paused|completed|abandoned|
|
||||
-- exhausted) — enforced by the GoalStatus enum's @EnumValue
|
||||
-- annotation. The active_conv_key generated column below depends on
|
||||
-- this convention; any uppercase write would defeat uniqueness.
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'active',
|
||||
|
||||
turn_budget INT NOT NULL DEFAULT 20,
|
||||
turns_used INT NOT NULL DEFAULT 0,
|
||||
llm_call_budget INT NOT NULL DEFAULT 200,
|
||||
agent_llm_calls_used INT NOT NULL DEFAULT 0,
|
||||
eval_llm_calls_used INT NOT NULL DEFAULT 0,
|
||||
|
||||
progress_summary TEXT NULL,
|
||||
completion_score DOUBLE PRECISION NULL,
|
||||
last_evaluation_at TIMESTAMP(3) NULL,
|
||||
|
||||
auto_followup_enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
followup_cooldown_seconds INT NOT NULL DEFAULT 0,
|
||||
last_followup_at TIMESTAMP(3) NULL,
|
||||
|
||||
-- Virtual generated column: NULL for non-active or deleted rows so
|
||||
-- they fall out of the unique-index check. PostgreSQL ignores NULL keys
|
||||
-- for uniqueness, giving us "at most one active row per conversation".
|
||||
active_conv_key VARCHAR(80)
|
||||
GENERATED ALWAYS AS (
|
||||
CASE WHEN status = 'active' AND deleted = 0
|
||||
THEN conversation_id ELSE NULL END
|
||||
) STORED,
|
||||
|
||||
version INT NOT NULL DEFAULT 0,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP(3) NOT NULL,
|
||||
update_time TIMESTAMP(3) NOT NULL,
|
||||
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_goal_active_conv ON mate_agent_goal (active_conv_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_goal_conv ON mate_agent_goal (conversation_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_goal_status ON mate_agent_goal (status, last_evaluation_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_goal_owner ON mate_agent_goal (created_by, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_goal_event (
|
||||
id BIGINT NOT NULL,
|
||||
goal_id BIGINT NOT NULL,
|
||||
event_type VARCHAR(32) NOT NULL,
|
||||
message_id BIGINT NULL,
|
||||
detail_json TEXT NULL,
|
||||
create_time TIMESTAMP(3) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_goal_event_goal ON mate_agent_goal_event (goal_id, id);
|
||||
@ -0,0 +1,20 @@
|
||||
-- V121__tool_disclosure_tier.sql (KingbaseES dialect)
|
||||
--
|
||||
-- Mirror of the H2 V121, adapted for KingbaseES (PostgreSQL-compatible).
|
||||
-- PostgreSQL supports ADD COLUMN IF NOT EXISTS natively.
|
||||
--
|
||||
-- See the H2 file for the column semantics.
|
||||
|
||||
-- 1. mate_tool.disclosure_tier (default 'core')
|
||||
ALTER TABLE mate_tool ADD COLUMN IF NOT EXISTS disclosure_tier VARCHAR(16) DEFAULT 'core';
|
||||
|
||||
-- 2. mate_mcp_server.disclosure_tier (default 'core' — MCP tools stay directly
|
||||
-- callable; an admin can move a noisy server to extension)
|
||||
ALTER TABLE mate_mcp_server ADD COLUMN IF NOT EXISTS disclosure_tier VARCHAR(16) DEFAULT 'core';
|
||||
|
||||
-- 3. Seed the heavy generative / browser tools as extension.
|
||||
-- mate_tool.name stores the Java class name (not the @Tool function name).
|
||||
UPDATE mate_tool
|
||||
SET disclosure_tier = 'extension'
|
||||
WHERE name IN ('ImageGenerateTool', 'MusicGenerateTool', 'VideoGenerateTool', 'Model3dGenerateTool', 'BrowserUseTool')
|
||||
AND (disclosure_tier IS NULL OR disclosure_tier = 'core');
|
||||
@ -0,0 +1,13 @@
|
||||
-- V122__fix_generative_tool_tier_names.sql (MySQL dialect)
|
||||
--
|
||||
-- Corrective migration. The first cut of V121 seeded the generative / browser
|
||||
-- tools as extension using their @Tool function names (image_generate, ...),
|
||||
-- but mate_tool.name stores the Java class name (ImageGenerateTool, ...), so the
|
||||
-- UPDATE matched no rows on databases that ran that early version. Re-apply the
|
||||
-- seed by class name. Idempotent: only promotes core → extension and leaves any
|
||||
-- admin-set value untouched.
|
||||
|
||||
UPDATE mate_tool
|
||||
SET disclosure_tier = 'extension'
|
||||
WHERE name IN ('ImageGenerateTool', 'MusicGenerateTool', 'VideoGenerateTool', 'Model3dGenerateTool', 'BrowserUseTool')
|
||||
AND (disclosure_tier IS NULL OR disclosure_tier = 'core');
|
||||
@ -0,0 +1,5 @@
|
||||
-- V100: per-conversation progress ledger (see the H2 copy for full background).
|
||||
--
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_conversation ADD COLUMN IF NOT EXISTS progress_ledger TEXT NULL;
|
||||
@ -0,0 +1,6 @@
|
||||
-- V124: bump default agents' max_iterations 100 → 150 (see H2 copy for full
|
||||
-- background). Same idempotent UPDATE — H2 and MySQL accept identical syntax
|
||||
-- for this UPDATE so no dialect-specific guard is needed.
|
||||
|
||||
UPDATE mate_agent SET max_iterations = 150
|
||||
WHERE id IN (1000000001, 1000000002, 1000000003) AND max_iterations = 100;
|
||||
@ -0,0 +1,4 @@
|
||||
-- V125: Add workspace_base_path column to mate_agent for Agent-level directory override.
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS workspace_base_path VARCHAR(512) DEFAULT NULL;
|
||||
@ -0,0 +1,13 @@
|
||||
-- V126: Two binding-mode flags on mate_agent (KingbaseES).
|
||||
--
|
||||
-- skills_disabled / tools_disabled flip the "zero binding rows" semantic from
|
||||
-- "inherit every globally-enabled capability" to "this agent has explicitly
|
||||
-- opted out". Without these columns, an operator who wanted an agent with no
|
||||
-- skills had to bind a dummy skill — otherwise the runtime fell back to the
|
||||
-- global default and every skill's catalog entry got injected into the system
|
||||
-- prompt (issue #184).
|
||||
--
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS skills_disabled SMALLINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS tools_disabled SMALLINT NOT NULL DEFAULT 0;
|
||||
@ -0,0 +1,25 @@
|
||||
-- V127: Approval auto-grant table (MySQL dialect).
|
||||
-- Idempotent: outer CREATE TABLE uses IF NOT EXISTS; inline KEY clauses
|
||||
-- only execute on first creation, so re-running this migration is safe.
|
||||
CREATE TABLE IF NOT EXISTS mate_approval_grant (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
scope_type VARCHAR(32) NOT NULL,
|
||||
scope_id VARCHAR(64) NOT NULL,
|
||||
tool_name VARCHAR(128) DEFAULT NULL,
|
||||
rule_id VARCHAR(128) DEFAULT NULL,
|
||||
max_severity VARCHAR(16) NOT NULL,
|
||||
grant_kind VARCHAR(24) NOT NULL,
|
||||
expire_at TIMESTAMP DEFAULT NULL,
|
||||
granted_by BIGINT NOT NULL,
|
||||
granted_at TIMESTAMP NOT NULL,
|
||||
revoked SMALLINT NOT NULL DEFAULT 0,
|
||||
revoked_by BIGINT DEFAULT NULL,
|
||||
revoked_at TIMESTAMP DEFAULT NULL,
|
||||
note VARCHAR(500) DEFAULT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_grant_scope ON mate_approval_grant (workspace_id, scope_type, scope_id, tool_name, revoked, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_grant_expire ON mate_approval_grant (expire_at, revoked, deleted);
|
||||
@ -0,0 +1,25 @@
|
||||
-- V128: Approval resolution log table (MySQL dialect).
|
||||
CREATE TABLE IF NOT EXISTS mate_approval_resolution_log (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
-- Nullable: HARD_BLOCK can fire before workspace resolution; see H2 migration
|
||||
-- for full rationale. Per-workspace Dashboard queries filter on workspace_id
|
||||
-- and skip null rows; the global HARD_BLOCK panel surfaces them.
|
||||
workspace_id BIGINT DEFAULT NULL,
|
||||
conversation_id VARCHAR(128) DEFAULT NULL,
|
||||
agent_id VARCHAR(64) DEFAULT NULL,
|
||||
user_id VARCHAR(64) DEFAULT NULL,
|
||||
tool_call_id VARCHAR(64) DEFAULT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
max_severity VARCHAR(16) DEFAULT NULL,
|
||||
rule_ids VARCHAR(512) DEFAULT NULL,
|
||||
decision_source VARCHAR(24) NOT NULL,
|
||||
grant_id BIGINT DEFAULT NULL,
|
||||
pending_id VARCHAR(32) DEFAULT NULL,
|
||||
args_preview VARCHAR(500) DEFAULT NULL,
|
||||
note VARCHAR(500) DEFAULT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_resolution_workspace_time ON mate_approval_resolution_log (workspace_id, create_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_resolution_grant ON mate_approval_resolution_log (grant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_resolution_pending ON mate_approval_resolution_log (pending_id);
|
||||
@ -0,0 +1,7 @@
|
||||
-- V129: Persisted wikilink lint state — KingbaseES dialect.
|
||||
--
|
||||
-- See h2/V129__wiki_page_broken_links.sql for column semantics.
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS broken_links JSONB DEFAULT NULL;
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS broken_links_scanned_at TIMESTAMP(3) DEFAULT NULL;
|
||||
@ -0,0 +1,22 @@
|
||||
-- V12: Wiki chunk persistence (RFC-013 minimal slice → enables RFC-011 embedding)
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_chunk (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
raw_id BIGINT NOT NULL,
|
||||
ordinal INT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
char_count INT NOT NULL,
|
||||
start_offset INT NOT NULL,
|
||||
end_offset INT NOT NULL,
|
||||
content_hash VARCHAR(64) NOT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- MySQL lacks CREATE INDEX IF NOT EXISTS; use INFORMATION_SCHEMA.STATISTICS guard instead.
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_chunk_kb ON mate_wiki_chunk (kb_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_chunk_raw ON mate_wiki_chunk (raw_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_chunk_hash ON mate_wiki_chunk (content_hash);
|
||||
@ -0,0 +1,26 @@
|
||||
-- V129: Store the per-agent primary wiki KB on mate_agent (KingbaseES).
|
||||
--
|
||||
-- Knowledge bases remain workspace-shared; this field only chooses the
|
||||
-- default KB for wiki tools when no kbName/kbId is specified.
|
||||
|
||||
ALTER TABLE mate_agent ADD COLUMN IF NOT EXISTS primary_kb_id BIGINT DEFAULT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_primary_kb ON mate_agent (primary_kb_id);
|
||||
|
||||
UPDATE mate_agent a
|
||||
SET primary_kb_id = (
|
||||
SELECT kb.id
|
||||
FROM mate_wiki_knowledge_base kb
|
||||
WHERE kb.agent_id = a.id
|
||||
AND (kb.workspace_id IS NULL OR kb.workspace_id = a.workspace_id)
|
||||
AND kb.deleted = 0
|
||||
ORDER BY kb.update_time DESC
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE a.primary_kb_id IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM mate_wiki_knowledge_base kb
|
||||
WHERE kb.agent_id = a.id
|
||||
AND (kb.workspace_id IS NULL OR kb.workspace_id = a.workspace_id)
|
||||
AND kb.deleted = 0
|
||||
);
|
||||
@ -0,0 +1,36 @@
|
||||
-- Add Claude Opus 4.8 (regular + -fast variant) model entries to
|
||||
-- mate_model_config for existing deployments. New installs pick these up via
|
||||
-- DatabaseBootstrapRunner from data-mysql-{en,zh}.sql; this migration covers
|
||||
-- operators who already have earlier Flyway versions applied.
|
||||
--
|
||||
-- Claude 4.8 inherits 4.7's strict API contract: temperature / top_p / top_k
|
||||
-- must be NULL (otherwise HTTP 400), and the "xhigh" thinking tier is
|
||||
-- available. Both are handled in AnthropicChatModelBuilder via the
|
||||
-- isClaude47OrLater() detector.
|
||||
--
|
||||
-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert.
|
||||
-- Same V number is used in h2/ for cross-dialect parity.
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
-- Direct Anthropic
|
||||
(1000000290, 'Claude Opus 4.8', 'anthropic', 'claude-opus-4-8', 'Anthropic Claude Opus 4.8 (xhigh adaptive thinking)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000291, 'Claude Opus 4.8 Fast', 'anthropic', 'claude-opus-4-8-fast', 'Claude Opus 4.8 fast variant (higher output speed, 2x pricing)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
-- OpenRouter passthrough
|
||||
(1000000292, 'Claude Opus 4.8', 'openrouter', 'anthropic/claude-opus-4-8', 'Claude Opus 4.8 via OpenRouter', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000293, 'Claude Opus 4.8 Fast', 'openrouter', 'anthropic/claude-opus-4-8-fast', 'Claude Opus 4.8 fast variant via OpenRouter', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
-- Claude Code OAuth (Pro/Max subscription)
|
||||
(1000000294, 'Claude Opus 4.8', 'anthropic-claude-code', 'claude-opus-4-8', 'Claude Opus 4.8 via Claude Code Pro/Max subscription', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
provider = EXCLUDED.provider,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
temperature = EXCLUDED.temperature,
|
||||
max_tokens = EXCLUDED.max_tokens,
|
||||
top_p = EXCLUDED.top_p,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
is_default = EXCLUDED.is_default,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,13 @@
|
||||
-- Update the daily "memory consolidation" cron prompt on existing databases so it
|
||||
-- keeps project-specific volatile facts (codenames, tech stacks, per-project
|
||||
-- decisions) out of the always-on MEMORY.md. Seed scripts only run on fresh
|
||||
-- installs, so existing rows need this data migration to pick up the new wording.
|
||||
-- Scoped to the original default text so user-edited prompts are left untouched.
|
||||
|
||||
UPDATE mate_cron_job
|
||||
SET trigger_message = '请回顾你最近的 memory/ 日记文件,将反复出现的重要信息(用户偏好、稳定事实、经验教训、工作流)提炼整合到 MEMORY.md 中。注意:MEMORY.md 会被注入每一次对话,只整合跨项目长期稳定的信息;具体项目的代号、名称、技术栈、仓库、单项目的指标/预算/团队/上线日期或只对某个项目成立的决策等易变事实,不要写入 MEMORY.md(会随项目切换互相冲突、导致张冠李戴),应留在 daily note 或通过结构化 project 记忆维护。判定口诀:换一个项目后仍成立才进 MEMORY.md。保留日记原文不动,只更新 MEMORY.md。完成后简要说明做了哪些整合。'
|
||||
WHERE trigger_message = '请回顾你最近的 memory/ 日记文件,将反复出现的重要信息(用户偏好、稳定事实、经验教训、工作流)提炼整合到 MEMORY.md 中。保留日记原文不动,只更新 MEMORY.md。完成后简要说明做了哪些整合。';
|
||||
|
||||
UPDATE mate_cron_job
|
||||
SET trigger_message = 'Review your recent memory/ daily note files and consolidate recurring important information (user preferences, stable facts, lessons learned, workflows) into MEMORY.md. Note: MEMORY.md is injected into every conversation, so only consolidate cross-project, long-term stable information; do NOT write project-specific volatile facts into MEMORY.md (project codenames, names, tech stacks, repos, a single project''s metrics/budget/team/launch date, or decisions that hold only for one project) — they conflict across projects and cause mix-ups. Keep those in the daily note or maintain them via structured project memory. Rule of thumb: only facts that still hold after switching projects belong in MEMORY.md. Keep the original daily notes intact, only update MEMORY.md. Briefly describe what consolidations were made.'
|
||||
WHERE trigger_message = 'Review your recent memory/ daily note files and consolidate recurring important information (user preferences, stable facts, lessons learned, workflows) into MEMORY.md. Keep the original daily notes intact, only update MEMORY.md. Briefly describe what consolidations were made.';
|
||||
@ -0,0 +1,23 @@
|
||||
-- V133: Per-agent, per-KB, per-pageType permission for wiki tools.
|
||||
-- Read permission filters retrieval/listing; write permission gates the
|
||||
-- create/compile/delete/archive/enrich/transformation tools. A row with
|
||||
-- page_type='*' is the agent's KB-wide default; an exact page_type row is
|
||||
-- more specific and wins over '*'. Unconfigured (no rows) falls back to the
|
||||
-- KB-level defaultReadPolicy stored in the KB config.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_agent_page_type_permission (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
kb_id BIGINT NOT NULL,
|
||||
page_type VARCHAR(64) NOT NULL,
|
||||
can_read SMALLINT NOT NULL DEFAULT 1,
|
||||
can_create SMALLINT NOT NULL DEFAULT 0,
|
||||
can_update SMALLINT NOT NULL DEFAULT 0,
|
||||
can_delete SMALLINT NOT NULL DEFAULT 0,
|
||||
write_policy VARCHAR(32) NOT NULL DEFAULT 'approval_required',
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_agent_ptperm ON mate_wiki_agent_page_type_permission (agent_id, kb_id, page_type, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_ptperm_agent_kb ON mate_wiki_agent_page_type_permission (agent_id, kb_id, deleted);
|
||||
@ -0,0 +1,78 @@
|
||||
-- V134: KB-scoped pageType profile + structured page metadata columns.
|
||||
-- See the H2 file for the design rationale. PostgreSQL/KingbaseES uses a STORED
|
||||
-- generated column for the "one enabled profile per KB" constraint, and an
|
||||
-- information_schema guard for each idempotent ADD COLUMN (PostgreSQL has no
|
||||
-- ADD COLUMN IF NOT EXISTS).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page_type_profile (
|
||||
id BIGINT NOT NULL,
|
||||
kb_id BIGINT NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
config_json TEXT NOT NULL,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0,
|
||||
-- Yields kb_id only for the live-enabled row; NULL otherwise. PostgreSQL
|
||||
-- ignores NULL keys for uniqueness, giving "at most one enabled per KB".
|
||||
enabled_kb BIGINT
|
||||
GENERATED ALWAYS AS (
|
||||
CASE WHEN enabled = 1 AND deleted = 0 THEN kb_id ELSE NULL END
|
||||
) STORED,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_ptprofile_name ON mate_wiki_page_type_profile (kb_id, name, deleted);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_ptprofile_enabled ON mate_wiki_page_type_profile (enabled_kb);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_ptprofile_kb ON mate_wiki_page_type_profile (kb_id, enabled, deleted);
|
||||
|
||||
-- Structured page metadata columns (idempotent adds).
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_json'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN metadata_json TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_validation_status'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_status VARCHAR(32);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'metadata_validation_json'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN metadata_validation_json TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'template_key'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN template_key VARCHAR(128);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'profile_version'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN profile_version INT;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,56 @@
|
||||
-- V135: Layered knowledge (fact / experience) + page dependency graph.
|
||||
-- See the H2 file for rationale. MySQL uses INFORMATION_SCHEMA guards for the
|
||||
-- idempotent column adds.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'knowledge_layer'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN knowledge_layer VARCHAR(16);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'depends_on_json'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN depends_on_json TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'stale'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN stale SMALLINT NOT NULL DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'stale_reason_json'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN stale_reason_json TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page_dependency (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
page_id BIGINT NOT NULL,
|
||||
depends_on_page_id BIGINT NOT NULL,
|
||||
dependency_type VARCHAR(32) NOT NULL DEFAULT 'fact',
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_page_dep ON mate_wiki_page_dependency (page_id, depends_on_page_id, dependency_type, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_page_dep_reverse ON mate_wiki_page_dependency (kb_id, depends_on_page_id, deleted);
|
||||
@ -0,0 +1,55 @@
|
||||
-- V136: Wiki pipeline runtime — definitions, runs, and per-step runs.
|
||||
-- See the H2 file for design rationale.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_definition (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
owner_agent_id BIGINT NOT NULL,
|
||||
trigger_type VARCHAR(32) NOT NULL,
|
||||
trigger_config_json TEXT,
|
||||
steps_json TEXT NOT NULL,
|
||||
dedup_window_seconds INT NOT NULL DEFAULT 0,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_pipeline_def_name ON mate_wiki_pipeline_definition (kb_id, name, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_def_trigger ON mate_wiki_pipeline_definition (kb_id, trigger_type, enabled, deleted);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
definition_id BIGINT NOT NULL,
|
||||
kb_id BIGINT NOT NULL,
|
||||
status VARCHAR(16) NOT NULL,
|
||||
trigger_type VARCHAR(32) NOT NULL,
|
||||
trigger_subject VARCHAR(128) NOT NULL,
|
||||
trigger_bucket VARCHAR(64) NOT NULL,
|
||||
trigger_payload_json TEXT,
|
||||
input_json TEXT,
|
||||
output_json TEXT,
|
||||
error_message VARCHAR(2048),
|
||||
started_at TIMESTAMP(3),
|
||||
finished_at TIMESTAMP(3),
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_pipeline_run_dedup ON mate_wiki_pipeline_run (definition_id, trigger_type, trigger_subject, trigger_bucket, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_run_def ON mate_wiki_pipeline_run (definition_id, status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_pipeline_step_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
run_id BIGINT NOT NULL,
|
||||
step_id VARCHAR(128) NOT NULL,
|
||||
executor VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL,
|
||||
input_json TEXT,
|
||||
output_json TEXT,
|
||||
error_message VARCHAR(2048),
|
||||
started_at TIMESTAMP(3),
|
||||
finished_at TIMESTAMP(3),
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_pipeline_step_run ON mate_wiki_pipeline_step_run (run_id, status);
|
||||
@ -0,0 +1,107 @@
|
||||
-- V137: Per-owner memory isolation with a three-state visibility scope (MySQL).
|
||||
--
|
||||
-- See the H2 counterpart for the full rationale. MySQL has no
|
||||
-- "ADD COLUMN IF NOT EXISTS", so each column/index is guarded with an
|
||||
-- INFORMATION_SCHEMA existence check + prepared statement for idempotency.
|
||||
--
|
||||
-- Existing rows are backfilled to scope='TEAM' by the NOT NULL DEFAULT so that
|
||||
-- upgrading does NOT hide previously-shared memory.
|
||||
|
||||
-- ---------- mate_workspace_file ----------
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_workspace_file' AND column_name = 'owner_key'
|
||||
) THEN
|
||||
ALTER TABLE mate_workspace_file ADD COLUMN owner_key VARCHAR(128) NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_workspace_file' AND column_name = 'scope'
|
||||
) THEN
|
||||
ALTER TABLE mate_workspace_file ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_scope_owner ON mate_workspace_file (agent_id, scope, owner_key);
|
||||
|
||||
-- Shared rows use the '' sentinel (not NULL) so the unique index below treats
|
||||
-- one shared row per filename as a single slot (NULLs are distinct in unique indexes).
|
||||
UPDATE mate_workspace_file SET owner_key = '' WHERE owner_key IS NULL;
|
||||
|
||||
-- De-duplicate before adding the unique index: the table never had a unique
|
||||
-- constraint and the service layer was check-then-insert, so historical
|
||||
-- duplicates may exist. Keep the most recently inserted row per
|
||||
-- (agent_id, filename, owner_key); drop the rest. The extra derived-table wrap
|
||||
-- is required so MySQL doesn't reject selecting from the table being deleted.
|
||||
--
|
||||
-- IRREVERSIBLE: this keeps MAX(id) (newest row) and PERMANENTLY deletes the
|
||||
-- other rows in a duplicate group — their content / enabled / sort_order are
|
||||
-- not preserved or merged. Duplicates are NOT expected (every write path is
|
||||
-- check-then-insert), so this is a safety net to guarantee the index builds,
|
||||
-- not a routine merge. If a deployment knowingly relies on duplicate rows,
|
||||
-- reconcile them manually before upgrading.
|
||||
DELETE FROM mate_workspace_file
|
||||
WHERE id NOT IN (
|
||||
SELECT keep_id FROM (
|
||||
SELECT MAX(id) AS keep_id
|
||||
FROM mate_workspace_file
|
||||
GROUP BY agent_id, filename, owner_key
|
||||
) t
|
||||
);
|
||||
|
||||
-- One row per (agent, filename, owner): one shared row + one row per PERSONAL
|
||||
-- owner. Hardens the check-then-insert in saveFile/saveMemoryFile against
|
||||
-- concurrent / multi-node duplicates.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_file_owner ON mate_workspace_file (agent_id, filename, owner_key);
|
||||
|
||||
-- ---------- mate_memory_recall ----------
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_memory_recall' AND column_name = 'owner_key'
|
||||
) THEN
|
||||
ALTER TABLE mate_memory_recall ADD COLUMN owner_key VARCHAR(128) NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_memory_recall' AND column_name = 'scope'
|
||||
) THEN
|
||||
ALTER TABLE mate_memory_recall ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_scope_owner ON mate_memory_recall (agent_id, scope, owner_key);
|
||||
|
||||
-- ---------- mate_fact ----------
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_fact' AND column_name = 'owner_key'
|
||||
) THEN
|
||||
ALTER TABLE mate_fact ADD COLUMN owner_key VARCHAR(128) NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_fact' AND column_name = 'scope'
|
||||
) THEN
|
||||
ALTER TABLE mate_fact ADD COLUMN scope VARCHAR(16) NOT NULL DEFAULT 'TEAM';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_scope_owner ON mate_fact (agent_id, scope, owner_key);
|
||||
@ -0,0 +1,7 @@
|
||||
-- Rename the built-in web-search tool from "search" to "web_search".
|
||||
-- DashScope's native protocol reserves the function name "search" and rejects any
|
||||
-- request that declares a tool with that name ("InvalidParameter: Tool names are not
|
||||
-- allowed to be [search]"), which broke tool use for every qwen/DashScope-native model
|
||||
-- that had this tool bound. Migrate existing agent bindings to the new name so they
|
||||
-- keep resolving after the tool was renamed in code. Idempotent.
|
||||
UPDATE mate_agent_tool SET tool_name = 'web_search' WHERE tool_name = 'search';
|
||||
@ -0,0 +1,8 @@
|
||||
-- Raise the default per-request (read) timeout for MCP servers from 30s to 60s.
|
||||
-- A 30s ceiling cut off MCP tools whose single callTool round-trip legitimately
|
||||
-- runs longer (data-heavy or compute-heavy tools), surfacing as a request timeout
|
||||
-- with no retry. The application layer already falls back to 60s when the column
|
||||
-- is null; this aligns the schema default so the value is consistent everywhere.
|
||||
-- Only changes the column default for newly inserted rows — existing rows keep
|
||||
-- whatever value they were given. Idempotent.
|
||||
ALTER TABLE mate_mcp_server ALTER COLUMN read_timeout_seconds SET DEFAULT 60;
|
||||
@ -0,0 +1,21 @@
|
||||
-- V13: Add embedding column to mate_wiki_chunk (RFC-011 Phase 2)
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN embedding BYTEA DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'embedding_model'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN embedding_model VARCHAR(64) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,19 @@
|
||||
-- V140: Structured, checkable criteria for goals (MySQL).
|
||||
--
|
||||
-- See the H2 counterpart for the full rationale. MySQL has no
|
||||
-- "ADD COLUMN IF NOT EXISTS", so the column is guarded with an
|
||||
-- INFORMATION_SCHEMA existence check + prepared statement for idempotency.
|
||||
--
|
||||
-- The column holds the goal's checklist as JSON:
|
||||
-- [{ "id": "C1", "text": "...", "passed": false, "evidence": "" }, ...]
|
||||
-- Additive and nullable, so existing goals load unchanged (a NULL list
|
||||
-- bootstraps on first evaluation).
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_agent_goal' AND column_name = 'criteria'
|
||||
) THEN
|
||||
ALTER TABLE mate_agent_goal ADD COLUMN criteria JSON NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,24 @@
|
||||
-- V141: Per-agent knowledge base access scope for wiki tools (KingbaseES / PostgreSQL 兼容).
|
||||
--
|
||||
-- Knowledge bases are workspace-shared, so by default every agent in a
|
||||
-- workspace can reach every KB in it. This table lets an operator pin an
|
||||
-- agent to a subset of KBs: once at least one enabled row exists for an
|
||||
-- agent, the wiki tools (list/search/read/write) can only see and target
|
||||
-- those KBs. No rows for an agent = unrestricted (workspace-wide), which
|
||||
-- keeps every pre-existing agent behaving exactly as before.
|
||||
--
|
||||
-- The default KB an agent's wiki tools fall back to when no kbId/kbName is
|
||||
-- given still lives on mate_agent.primary_kb_id; this table only narrows the
|
||||
-- visible set, and the primary is expected to be one of the scoped KBs.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_wiki_kb (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
kb_id BIGINT NOT NULL,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_wiki_kb ON mate_agent_wiki_kb (agent_id, kb_id, deleted);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_wiki_kb_agent ON mate_agent_wiki_kb (agent_id, deleted);
|
||||
@ -0,0 +1,13 @@
|
||||
-- Optional target pageType for a transformation whose output_target='page'.
|
||||
-- See the h2 sibling migration for the prose explanation.
|
||||
-- MySQL INFORMATION_SCHEMA guard converted to plpgsql DO block.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_transformation' AND column_name = 'target_page_type'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_transformation ADD COLUMN target_page_type VARCHAR(64) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,6 @@
|
||||
-- V143: Register CodeExecuteTool as a built-in tool.
|
||||
-- ON DUPLICATE KEY UPDATE is the MySQL idempotent upsert.
|
||||
-- Converted to: ON CONFLICT DO UPDATE with EXCLUDED references (KingbaseES / PostgreSQL).
|
||||
INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted)
|
||||
VALUES (1000000023, 'CodeExecuteTool', 'Code Execute', 'Execute a snippet of code (python, bash, or node) that the agent writes on the fly. Lets a documentation-only skill be acted on by running the code its instructions describe. Dangerous operations trigger approval.', 'builtin', 'codeExecuteTool', '🧑💻', 1, 1, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, update_time=EXCLUDED.update_time;
|
||||
@ -0,0 +1,28 @@
|
||||
-- Fix the ckjia-shopping MCP seed to its real production endpoint.
|
||||
--
|
||||
-- V85 seeded a dev/test placeholder (sse + http://localhost:8085/sse +
|
||||
-- "Bearer ${CKJIA_MCP_KEY}"), which can never connect out of the box, so the
|
||||
-- 参考价 / price-comparison skill stayed unusable until an admin hand-edited it.
|
||||
-- The official CKJIA SaaS endpoint is Streamable HTTP at
|
||||
-- https://m.ckjia.com/api/ai/mcp and needs no Authorization header.
|
||||
--
|
||||
-- Also raises both timeouts to 60s: the price-aggregation round-trip
|
||||
-- (multi-platform search) legitimately runs longer than the old 30s ceiling.
|
||||
--
|
||||
-- SAFETY: only rewrites rows that still carry the untouched dev placeholder
|
||||
-- URL, so an admin who already pointed ckjia-shopping at a private CKJIA
|
||||
-- deployment (or the SaaS URL) is left completely alone. Idempotent — after it
|
||||
-- runs the URL no longer matches the WHERE clause. `enabled` is deliberately
|
||||
-- not changed: the server stays opt-in.
|
||||
UPDATE mate_mcp_server
|
||||
SET transport = 'streamable_http',
|
||||
url = 'https://m.ckjia.com/api/ai/mcp',
|
||||
headers_json = NULL,
|
||||
connect_timeout_seconds = 60,
|
||||
read_timeout_seconds = 60,
|
||||
last_status = 'disconnected',
|
||||
last_error = NULL,
|
||||
description = 'CKJIA price comparison MCP server (Streamable HTTP). Disabled by default — enable it in Settings > MCP Connections to use the 参考价 shopping skill.',
|
||||
update_time = NOW()
|
||||
WHERE name = 'ckjia-shopping'
|
||||
AND url = 'http://localhost:8085/sse';
|
||||
@ -0,0 +1,37 @@
|
||||
-- Add Claude Fable 5 model entries to mate_model_config. Unlike earlier Claude
|
||||
-- families, the Fable rows live ONLY here, not in the data-kingbase-{en,zh}.sql
|
||||
-- seed: Flyway runs every version (V1..) on a fresh database, so the migration
|
||||
-- seeds new installs and upgrades existing deployments alike. The trade-off is
|
||||
-- that the description below is English-only (seed files carry localized copy).
|
||||
--
|
||||
-- Fable 5 is a reasoning-first model with a 1M-token context window and native
|
||||
-- vision input. It follows the same strict API contract as Claude 4.7+:
|
||||
-- temperature / top_p / top_k must be NULL (otherwise HTTP 400), and the
|
||||
-- "xhigh" adaptive thinking tier is available. Both are handled in
|
||||
-- AnthropicChatModelBuilder via the isClaudeFable() / isClaude47OrLater()
|
||||
-- detectors. Vision capability is resolved in ModelCapabilityService.
|
||||
--
|
||||
-- ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert.
|
||||
-- Same V number is used in h2/ for cross-dialect parity.
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
-- Direct Anthropic
|
||||
(1000000300, 'Claude Fable 5', 'anthropic', 'claude-fable-5', 'Anthropic Claude Fable 5 (1M context, vision, xhigh adaptive thinking)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
-- OpenRouter passthrough
|
||||
(1000000301, 'Claude Fable 5', 'openrouter', 'anthropic/claude-fable-5', 'Claude Fable 5 via OpenRouter', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
-- Claude Code OAuth (Pro/Max subscription)
|
||||
(1000000302, 'Claude Fable 5', 'anthropic-claude-code', 'claude-fable-5', 'Claude Fable 5 via Claude Code Pro/Max subscription', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
provider = EXCLUDED.provider,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
temperature = EXCLUDED.temperature,
|
||||
max_tokens = EXCLUDED.max_tokens,
|
||||
top_p = EXCLUDED.top_p,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
is_default = EXCLUDED.is_default,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,13 @@
|
||||
-- Per-KB source-watcher toggle. See the h2 sibling for the prose explanation.
|
||||
-- MySQL INFORMATION_SCHEMA guard converted to plpgsql DO block.
|
||||
-- TINYINT(1) converted to SMALLINT.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'watcher_enabled'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_knowledge_base ADD COLUMN watcher_enabled SMALLINT NOT NULL DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,40 @@
|
||||
-- V14: Embedding model UI config (对标 Dify)
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_config' AND column_name = 'model_type'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_config ADD COLUMN model_type VARCHAR(32) DEFAULT 'chat';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'embedding_model_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_knowledge_base ADD COLUMN embedding_model_id BIGINT DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 播种 DashScope embedding(与 chat 模型共享 provider apiKey)
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES (1000001001, 'Text Embedding v3', 'dashscope', 'text-embedding-v3',
|
||||
'DashScope 通义千问 v3 通用文本向量模型(1024 维)', 0, 0, 0,
|
||||
1, 1, 1, 'embedding', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET model_type = 'embedding';
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES (1000001002, 'Text Embedding v2', 'dashscope', 'text-embedding-v2',
|
||||
'DashScope 通义千问 v2 文本向量模型(1536 维)', 0, 0, 0,
|
||||
1, 1, 0, 'embedding', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET model_type = 'embedding';
|
||||
|
||||
-- 系统默认 embedding 模型(id 必须显式指定,与 chat 段 100000xxxx 错开)
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
VALUES (1000001100, 'embedding.default.model.id', '1000001001',
|
||||
'Default embedding model id for wiki semantic search', NOW(), NOW())
|
||||
ON CONFLICT (id) DO UPDATE SET setting_value = EXCLUDED.setting_value;
|
||||
@ -0,0 +1,7 @@
|
||||
-- V15: Purge DashScope model seed rows that are unavailable on the native protocol
|
||||
DELETE FROM mate_model_config
|
||||
WHERE id IN (1000000170, 1000000171)
|
||||
AND provider = 'dashscope'
|
||||
AND builtin = 1;
|
||||
-- 1000000170 = qwen3.5-plus
|
||||
-- 1000000171 = qwen3.5-max
|
||||
@ -0,0 +1,8 @@
|
||||
-- V16: Broaden the DashScope native-protocol purge (see V15).
|
||||
DELETE FROM mate_model_config
|
||||
WHERE provider = 'dashscope'
|
||||
AND (model_name LIKE 'qwen1.%'
|
||||
OR model_name LIKE 'qwen2.%'
|
||||
OR model_name LIKE 'qwen3.%'
|
||||
OR model_name LIKE 'qwen4.%'
|
||||
OR model_name LIKE 'qwen5.%');
|
||||
@ -0,0 +1,630 @@
|
||||
-- MateClaw 数据库初始化脚本(KingbaseES / PostgreSQL 兼容)
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE IF NOT EXISTS mate_user (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
username VARCHAR(64) NOT NULL UNIQUE,
|
||||
password VARCHAR(200) NOT NULL,
|
||||
nickname VARCHAR(64),
|
||||
avatar VARCHAR(256),
|
||||
email VARCHAR(128),
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- Agent 配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_agent (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_type VARCHAR(32) NOT NULL DEFAULT 'react',
|
||||
system_prompt TEXT,
|
||||
model_name VARCHAR(128),
|
||||
max_iterations INT NOT NULL DEFAULT 10,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
icon VARCHAR(256),
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 模型配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
provider VARCHAR(64) NOT NULL DEFAULT 'dashscope',
|
||||
model_name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
temperature DOUBLE PRECISION,
|
||||
max_tokens INT,
|
||||
top_p DOUBLE PRECISION,
|
||||
builtin SMALLINT NOT NULL DEFAULT 1,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
is_default SMALLINT NOT NULL DEFAULT 0,
|
||||
max_input_tokens INT DEFAULT 0,
|
||||
enable_search SMALLINT DEFAULT 0,
|
||||
search_strategy VARCHAR(32) DEFAULT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_model_config_model_name ON mate_model_config (model_name);
|
||||
|
||||
-- 模型 Provider 表
|
||||
CREATE TABLE IF NOT EXISTS mate_model_provider (
|
||||
provider_id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
api_key_prefix VARCHAR(32),
|
||||
chat_model VARCHAR(64),
|
||||
api_key VARCHAR(512),
|
||||
base_url VARCHAR(512),
|
||||
generate_kwargs TEXT,
|
||||
is_custom SMALLINT NOT NULL DEFAULT 0,
|
||||
is_local SMALLINT NOT NULL DEFAULT 0,
|
||||
support_model_discovery SMALLINT NOT NULL DEFAULT 0,
|
||||
support_connection_check SMALLINT NOT NULL DEFAULT 0,
|
||||
freeze_url SMALLINT NOT NULL DEFAULT 0,
|
||||
require_api_key SMALLINT NOT NULL DEFAULT 1,
|
||||
auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key',
|
||||
oauth_access_token TEXT,
|
||||
oauth_refresh_token TEXT,
|
||||
oauth_expires_at BIGINT,
|
||||
oauth_account_id VARCHAR(128),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
-- 系统设置表
|
||||
CREATE TABLE IF NOT EXISTS mate_system_setting (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
setting_key VARCHAR(128) NOT NULL UNIQUE,
|
||||
setting_value TEXT,
|
||||
description VARCHAR(256),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
-- 技能表
|
||||
CREATE TABLE IF NOT EXISTS mate_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
skill_type VARCHAR(32) NOT NULL DEFAULT 'dynamic',
|
||||
icon VARCHAR(256),
|
||||
version VARCHAR(32),
|
||||
author VARCHAR(64),
|
||||
config_json TEXT,
|
||||
source_code TEXT,
|
||||
skill_content TEXT,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
builtin SMALLINT NOT NULL DEFAULT 0,
|
||||
tags VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 工具表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
display_name VARCHAR(128),
|
||||
description TEXT,
|
||||
tool_type VARCHAR(32) NOT NULL DEFAULT 'builtin',
|
||||
bean_name VARCHAR(128),
|
||||
icon VARCHAR(256),
|
||||
mcp_endpoint VARCHAR(256),
|
||||
params_schema TEXT,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
builtin SMALLINT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 渠道表
|
||||
CREATE TABLE IF NOT EXISTS mate_channel (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
agent_id BIGINT,
|
||||
bot_prefix VARCHAR(64),
|
||||
config_json TEXT,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
description VARCHAR(256),
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 会话表
|
||||
CREATE TABLE IF NOT EXISTS mate_conversation (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
title VARCHAR(256),
|
||||
agent_id BIGINT,
|
||||
username VARCHAR(64),
|
||||
message_count INT NOT NULL DEFAULT 0,
|
||||
last_message TEXT,
|
||||
last_active_time TIMESTAMP,
|
||||
stream_status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_conversation_username ON mate_conversation (username);
|
||||
|
||||
-- 消息表
|
||||
CREATE TABLE IF NOT EXISTS mate_message (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(64) NOT NULL,
|
||||
role VARCHAR(32) NOT NULL,
|
||||
content TEXT,
|
||||
content_parts TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
token_usage INT,
|
||||
prompt_tokens INT DEFAULT 0,
|
||||
completion_tokens INT DEFAULT 0,
|
||||
runtime_model VARCHAR(128),
|
||||
runtime_provider VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'completed',
|
||||
metadata JSONB,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
COMMENT ON COLUMN mate_message.metadata IS '存储 toolCalls, plan, currentPhase, pendingApproval 等元数据';
|
||||
CREATE INDEX IF NOT EXISTS idx_message_conversation ON mate_message (conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_message_conv_time ON mate_message (conversation_id, create_time);
|
||||
|
||||
-- 执行计划表
|
||||
CREATE TABLE IF NOT EXISTS mate_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id VARCHAR(64),
|
||||
goal TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
total_steps INT NOT NULL DEFAULT 0,
|
||||
completed_steps INT NOT NULL DEFAULT 0,
|
||||
summary TEXT,
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 子计划步骤表
|
||||
CREATE TABLE IF NOT EXISTS mate_sub_plan (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
plan_id BIGINT NOT NULL,
|
||||
step_index INT NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
result TEXT,
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_sub_plan_plan_id ON mate_sub_plan (plan_id);
|
||||
|
||||
-- 定时任务表
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
cron_expression VARCHAR(128) NOT NULL,
|
||||
timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
|
||||
agent_id BIGINT NOT NULL,
|
||||
task_type VARCHAR(16) NOT NULL DEFAULT 'text',
|
||||
trigger_message TEXT,
|
||||
request_body TEXT,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
next_run_time TIMESTAMP,
|
||||
last_run_time TIMESTAMP,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 渠道会话存储表
|
||||
CREATE TABLE IF NOT EXISTS mate_channel_session (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128) NOT NULL UNIQUE,
|
||||
channel_type VARCHAR(32) NOT NULL,
|
||||
target_id VARCHAR(512) NOT NULL,
|
||||
sender_id VARCHAR(128),
|
||||
sender_name VARCHAR(128),
|
||||
channel_id BIGINT,
|
||||
last_active_time TIMESTAMP NOT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_session_type ON mate_channel_session (channel_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_session_channel_id ON mate_channel_session (channel_id);
|
||||
|
||||
-- 工作区文件表(Agent 级 Markdown 文档管理)
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_file (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
content TEXT,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
enabled SMALLINT NOT NULL DEFAULT 0,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent ON mate_workspace_file (agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent_enabled ON mate_workspace_file (agent_id, enabled);
|
||||
|
||||
-- ==================== MCP Server 管理 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_mcp_server (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
transport VARCHAR(32) NOT NULL DEFAULT 'stdio',
|
||||
url VARCHAR(512),
|
||||
headers_json TEXT,
|
||||
command VARCHAR(512),
|
||||
args_json TEXT,
|
||||
env_json TEXT,
|
||||
cwd VARCHAR(512),
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
connect_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
read_timeout_seconds INT NOT NULL DEFAULT 30,
|
||||
last_status VARCHAR(32) NOT NULL DEFAULT 'disconnected',
|
||||
last_error TEXT,
|
||||
last_connected_time TIMESTAMP,
|
||||
tool_count INT NOT NULL DEFAULT 0,
|
||||
builtin SMALLINT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mcp_server_enabled ON mate_mcp_server (enabled);
|
||||
|
||||
-- ==================== 工具安全治理(ToolGuard) ====================
|
||||
|
||||
-- 工具审批表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_approval (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
pending_id VARCHAR(32) NOT NULL UNIQUE,
|
||||
conversation_id VARCHAR(128) NOT NULL,
|
||||
user_id VARCHAR(64),
|
||||
agent_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
requester_name VARCHAR(128),
|
||||
reply_target VARCHAR(512),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_arguments TEXT,
|
||||
tool_call_payload TEXT,
|
||||
tool_call_hash VARCHAR(64),
|
||||
sibling_tool_calls TEXT,
|
||||
summary TEXT,
|
||||
findings_json TEXT,
|
||||
max_severity VARCHAR(16),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
|
||||
resolved_by VARCHAR(64),
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
resolved_at TIMESTAMP,
|
||||
expire_at TIMESTAMP,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_conv ON mate_tool_approval (conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_status ON mate_tool_approval (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tool_approval_pending_id ON mate_tool_approval (pending_id);
|
||||
|
||||
-- 安全规则表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_rule (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
rule_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
tool_name VARCHAR(128),
|
||||
param_name VARCHAR(128),
|
||||
category VARCHAR(64) NOT NULL,
|
||||
severity VARCHAR(16) NOT NULL,
|
||||
decision VARCHAR(16) NOT NULL DEFAULT 'NEEDS_APPROVAL',
|
||||
pattern VARCHAR(512) NOT NULL,
|
||||
exclude_pattern VARCHAR(512),
|
||||
remediation TEXT,
|
||||
builtin SMALLINT NOT NULL DEFAULT 0,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
priority INT NOT NULL DEFAULT 100,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- 安全全局配置表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_config (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
guard_scope VARCHAR(32) NOT NULL DEFAULT 'all',
|
||||
guarded_tools_json TEXT,
|
||||
denied_tools_json TEXT,
|
||||
file_guard_enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
sensitive_paths_json TEXT,
|
||||
audit_enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
audit_min_severity VARCHAR(16) NOT NULL DEFAULT 'INFO',
|
||||
audit_retention_days INT NOT NULL DEFAULT 90,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
-- 安全审计日志表
|
||||
CREATE TABLE IF NOT EXISTS mate_tool_guard_audit_log (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
conversation_id VARCHAR(128),
|
||||
agent_id VARCHAR(64),
|
||||
user_id VARCHAR(64),
|
||||
channel_type VARCHAR(32),
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
tool_params_json TEXT,
|
||||
decision VARCHAR(16) NOT NULL,
|
||||
max_severity VARCHAR(16),
|
||||
findings_json TEXT,
|
||||
pending_id VARCHAR(32),
|
||||
replay_payload_hash VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_conv ON mate_tool_guard_audit_log (conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_guard_audit_time ON mate_tool_guard_audit_log (create_time);
|
||||
|
||||
-- ==================== 外部数据源 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_datasource (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description VARCHAR(512),
|
||||
db_type VARCHAR(32) NOT NULL,
|
||||
host VARCHAR(256) NOT NULL,
|
||||
port INT NOT NULL,
|
||||
database_name VARCHAR(128) NOT NULL,
|
||||
username VARCHAR(128),
|
||||
password VARCHAR(512),
|
||||
extra_params VARCHAR(512),
|
||||
schema_name VARCHAR(128),
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
last_test_time TIMESTAMP,
|
||||
last_test_ok SMALLINT,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- ==================== 异步任务(视频/图片生成等长耗时操作) ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_async_task (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
task_type VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
||||
conversation_id VARCHAR(128),
|
||||
message_id BIGINT,
|
||||
provider_name VARCHAR(64),
|
||||
provider_task_id VARCHAR(128),
|
||||
request_json TEXT,
|
||||
result_json TEXT,
|
||||
error_message VARCHAR(512),
|
||||
progress INT DEFAULT 0,
|
||||
created_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_async_task_taskid ON mate_async_task (task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_conv ON mate_async_task (conversation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_status ON mate_async_task (status);
|
||||
|
||||
-- ==================== 记忆召回追踪 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_memory_recall (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
snippet_hash VARCHAR(64),
|
||||
snippet_preview VARCHAR(512),
|
||||
recall_count INT NOT NULL DEFAULT 0,
|
||||
daily_count INT NOT NULL DEFAULT 0,
|
||||
query_hashes TEXT,
|
||||
score DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
||||
last_recalled_at TIMESTAMP,
|
||||
promoted SMALLINT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_agent ON mate_memory_recall (agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_agent_file ON mate_memory_recall (agent_id, filename);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_score ON mate_memory_recall (agent_id, score);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_candidates ON mate_memory_recall (agent_id, promoted, deleted);
|
||||
|
||||
-- ==================== Wiki 知识库 ====================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_knowledge_base (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
description TEXT,
|
||||
agent_id BIGINT,
|
||||
config_content TEXT,
|
||||
source_directory VARCHAR(512),
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
page_count INT NOT NULL DEFAULT 0,
|
||||
raw_count INT NOT NULL DEFAULT 0,
|
||||
workspace_id BIGINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_kb_agent ON mate_wiki_knowledge_base (agent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_raw_material (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
source_type VARCHAR(32) NOT NULL DEFAULT 'text',
|
||||
source_path VARCHAR(512),
|
||||
original_content TEXT,
|
||||
extracted_text TEXT,
|
||||
content_hash VARCHAR(64),
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
processing_status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
last_processed_at TIMESTAMP,
|
||||
error_message VARCHAR(512),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_raw_kb ON mate_wiki_raw_material (kb_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_raw_status ON mate_wiki_raw_material (kb_id, processing_status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
slug VARCHAR(256) NOT NULL,
|
||||
title VARCHAR(256) NOT NULL,
|
||||
content TEXT,
|
||||
summary VARCHAR(1024),
|
||||
outgoing_links TEXT,
|
||||
source_raw_ids TEXT,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
last_updated_by VARCHAR(32) NOT NULL DEFAULT 'ai',
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_wiki_page_kb_slug ON mate_wiki_page (kb_id, slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_wiki_page_kb ON mate_wiki_page (kb_id);
|
||||
|
||||
-- =============================================
|
||||
-- 工作区表(Phase 2)
|
||||
-- =============================================
|
||||
|
||||
-- 工作区
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_slug ON mate_workspace (slug);
|
||||
|
||||
-- 工作区成员
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'member',
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member (workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member (user_id);
|
||||
|
||||
-- =============================================
|
||||
-- Agent-Skill / Agent-Tool 绑定表(Phase 3 Sprint 2)
|
||||
-- =============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
config_json TEXT,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_skill ON mate_agent_skill (agent_id, skill_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_tool ON mate_agent_tool (agent_id, tool_name);
|
||||
|
||||
-- =============================================
|
||||
-- CronJob 执行历史(Phase 3 Sprint 3)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_cron_job_run (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
cron_job_id BIGINT NOT NULL,
|
||||
conversation_id VARCHAR(64),
|
||||
status VARCHAR(32) NOT NULL,
|
||||
trigger_type VARCHAR(32) NOT NULL DEFAULT 'scheduled',
|
||||
started_at TIMESTAMP NOT NULL,
|
||||
finished_at TIMESTAMP,
|
||||
error_message TEXT,
|
||||
token_usage INT DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_cron_run_job ON mate_cron_job_run (cron_job_id, started_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
agent_id BIGINT,
|
||||
stat_date DATE NOT NULL,
|
||||
conversation_count INT DEFAULT 0,
|
||||
message_count INT DEFAULT 0,
|
||||
total_tokens BIGINT DEFAULT 0,
|
||||
prompt_tokens BIGINT DEFAULT 0,
|
||||
completion_tokens BIGINT DEFAULT 0,
|
||||
tool_call_count INT DEFAULT 0,
|
||||
error_count INT DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_usage_daily ON mate_usage_daily (workspace_id, agent_id, stat_date);
|
||||
|
||||
-- =============================================
|
||||
-- 操作审计事件表(Phase 3 Sprint 1)
|
||||
-- =============================================
|
||||
CREATE TABLE IF NOT EXISTS mate_audit_event (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT,
|
||||
user_id BIGINT NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
resource_id VARCHAR(128),
|
||||
resource_name VARCHAR(256),
|
||||
detail_json TEXT,
|
||||
ip_address VARCHAR(64),
|
||||
user_agent VARCHAR(256),
|
||||
create_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_ws_time ON mate_audit_event (workspace_id, create_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_user ON mate_audit_event (user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_resource ON mate_audit_event (resource_type, resource_id);
|
||||
|
||||
-- 清理 Codex 不支持的 ChatGPT OAuth 模型(gpt-4o, o3, o4-mini 在 Codex 模式下不可用)
|
||||
DELETE FROM mate_model_config WHERE provider = 'openai-chatgpt' AND model_name IN ('gpt-4o', 'o3', 'o4-mini');
|
||||
@ -0,0 +1,34 @@
|
||||
-- V20: Purge soft-deleted rows from all tables and retire soft-delete semantics.
|
||||
-- @TableLogic has been removed from all entities — the project no longer
|
||||
-- supports soft-delete. Clear residual deleted=1 rows so queries that still
|
||||
-- reference the deleted column (or raw SQL in service layer) continue to
|
||||
-- behave consistently. The deleted column itself is retained with its
|
||||
-- NOT NULL DEFAULT 0 constraint for schema compatibility.
|
||||
DELETE FROM mate_agent WHERE deleted = 1;
|
||||
DELETE FROM mate_agent_skill WHERE deleted = 1;
|
||||
DELETE FROM mate_agent_tool WHERE deleted = 1;
|
||||
DELETE FROM mate_channel WHERE deleted = 1;
|
||||
DELETE FROM mate_channel_session WHERE deleted = 1;
|
||||
DELETE FROM mate_conversation WHERE deleted = 1;
|
||||
DELETE FROM mate_cron_job WHERE deleted = 1;
|
||||
DELETE FROM mate_datasource WHERE deleted = 1;
|
||||
DELETE FROM mate_mcp_server WHERE deleted = 1;
|
||||
DELETE FROM mate_memory_recall WHERE deleted = 1;
|
||||
DELETE FROM mate_message WHERE deleted = 1;
|
||||
DELETE FROM mate_model_config WHERE deleted = 1;
|
||||
DELETE FROM mate_plan WHERE deleted = 1;
|
||||
DELETE FROM mate_plugin WHERE deleted = 1;
|
||||
DELETE FROM mate_skill WHERE deleted = 1;
|
||||
DELETE FROM mate_sub_plan WHERE deleted = 1;
|
||||
DELETE FROM mate_tool WHERE deleted = 1;
|
||||
DELETE FROM mate_tool_approval WHERE deleted = 1;
|
||||
DELETE FROM mate_tool_guard_audit_log WHERE deleted = 1;
|
||||
DELETE FROM mate_tool_guard_rule WHERE deleted = 1;
|
||||
DELETE FROM mate_user WHERE deleted = 1;
|
||||
DELETE FROM mate_wiki_chunk WHERE deleted = 1;
|
||||
DELETE FROM mate_wiki_knowledge_base WHERE deleted = 1;
|
||||
DELETE FROM mate_wiki_page WHERE deleted = 1;
|
||||
DELETE FROM mate_wiki_raw_material WHERE deleted = 1;
|
||||
DELETE FROM mate_workspace WHERE deleted = 1;
|
||||
DELETE FROM mate_workspace_file WHERE deleted = 1;
|
||||
DELETE FROM mate_workspace_member WHERE deleted = 1;
|
||||
@ -0,0 +1,26 @@
|
||||
-- RFC-009 Phase 1: ordered multi-provider fallback chain
|
||||
--
|
||||
-- fallback_priority defines the order in which a provider is tried after the
|
||||
-- primary model exhausts retries:
|
||||
-- 0 : not in the fallback chain (default — matches pre-RFC behavior)
|
||||
-- 1, 2, … : try in ascending order
|
||||
--
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use the INFORMATION_SCHEMA guard so
|
||||
-- this migration is idempotent across redeploys.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'fallback_priority'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN fallback_priority INT DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Seed: keep DashScope as priority 1 so existing deployments preserve the
|
||||
-- single-fallback-to-DashScope behavior the hardcoded path used to provide.
|
||||
UPDATE mate_model_provider
|
||||
SET fallback_priority = 1
|
||||
WHERE provider_id = 'dashscope'
|
||||
AND (fallback_priority IS NULL OR fallback_priority = 0);
|
||||
@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_page_citation (
|
||||
id BIGINT PRIMARY KEY,
|
||||
page_id BIGINT NOT NULL,
|
||||
chunk_id BIGINT NOT NULL,
|
||||
paragraph_idx INT NOT NULL DEFAULT 0,
|
||||
anchor_text VARCHAR(512),
|
||||
confidence DECIMAL(4,3) NOT NULL DEFAULT 1.000,
|
||||
created_by VARCHAR(32) NOT NULL DEFAULT 'system',
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wpc_page ON mate_wiki_page_citation (page_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wpc_chunk ON mate_wiki_page_citation (chunk_id);
|
||||
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS page_type VARCHAR(32) NOT NULL DEFAULT 'concept';
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS purpose_hint TEXT;
|
||||
@ -0,0 +1,25 @@
|
||||
CREATE TABLE IF NOT EXISTS mate_wiki_processing_job (
|
||||
id BIGINT PRIMARY KEY,
|
||||
kb_id BIGINT NOT NULL,
|
||||
raw_id BIGINT NOT NULL,
|
||||
job_type VARCHAR(32) NOT NULL DEFAULT 'heavy_ingest',
|
||||
stage VARCHAR(64) NOT NULL DEFAULT 'queued',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'queued',
|
||||
primary_model_id BIGINT,
|
||||
current_model_id BIGINT,
|
||||
fallback_chain_json TEXT,
|
||||
retry_count INT NOT NULL DEFAULT 0,
|
||||
max_retries INT NOT NULL DEFAULT 3,
|
||||
error_code VARCHAR(64),
|
||||
error_message TEXT,
|
||||
resume_from_stage VARCHAR(64),
|
||||
meta_json TEXT,
|
||||
started_at TIMESTAMP(3),
|
||||
finished_at TIMESTAMP(3),
|
||||
create_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP ,
|
||||
deleted SMALLINT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wpj_raw ON mate_wiki_processing_job (raw_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wpj_status ON mate_wiki_processing_job (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_wpj_kb ON mate_wiki_processing_job (kb_id, status);
|
||||
@ -0,0 +1,24 @@
|
||||
-- RFC-009 Phase 4 PR-3: per-agent provider preferences
|
||||
--
|
||||
-- Lets each agent declare an ordered list of preferred provider ids. Empty
|
||||
-- table for an agent (no rows) means "use the global fallback chain order"
|
||||
-- — fully backwards compatible with pre-PR-3 behavior. When rows exist,
|
||||
-- listed providers are tried in ascending sort_order before any non-listed
|
||||
-- provider is considered.
|
||||
--
|
||||
-- This is purely a routing hint. The runtime walker still gates each entry
|
||||
-- through AvailableProviderPool / ProviderHealthTracker — a preferred
|
||||
-- provider that is HARD-removed or in cooldown is still skipped.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_provider_preference (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
provider_id VARCHAR(128) NOT NULL,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
enabled SMALLINT NOT NULL DEFAULT 1,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_agent_provider ON mate_agent_provider_preference (agent_id, provider_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_provider_order ON mate_agent_provider_preference (agent_id, sort_order);
|
||||
@ -0,0 +1,24 @@
|
||||
-- Dream v2: structured dream report (rfc-035 §4.4)
|
||||
CREATE TABLE IF NOT EXISTS mate_dream_report (
|
||||
id BIGINT PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
mode VARCHAR(32) NOT NULL,
|
||||
topic VARCHAR(256),
|
||||
trigger_source VARCHAR(32) NOT NULL,
|
||||
triggered_by VARCHAR(64),
|
||||
started_at TIMESTAMP NOT NULL,
|
||||
finished_at TIMESTAMP NOT NULL,
|
||||
candidate_count INT NOT NULL,
|
||||
promoted_count INT NOT NULL,
|
||||
rejected_count INT NOT NULL,
|
||||
memory_diff TEXT,
|
||||
llm_reason TEXT,
|
||||
status VARCHAR(16) NOT NULL,
|
||||
error_message TEXT,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted SMALLINT DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX idx_dream_agent_time ON mate_dream_report(agent_id, started_at DESC);
|
||||
CREATE INDEX idx_dream_agent_mode ON mate_dream_report(agent_id, mode, started_at DESC);
|
||||
@ -0,0 +1,6 @@
|
||||
-- Dream v2: candidate state machine fields (rfc-035 4.1.4)
|
||||
-- Phase 1 writes values only; filtering enabled in Phase 2.
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_memory_recall ADD COLUMN IF NOT EXISTS review_count INT DEFAULT 0;
|
||||
ALTER TABLE mate_memory_recall ADD COLUMN IF NOT EXISTS last_reviewed_at TIMESTAMP;
|
||||
@ -0,0 +1,12 @@
|
||||
-- Dream v2 Phase 2b: Morning Card seen state per (user, agent)
|
||||
-- Ref: rfc-034 F5 — DO NOT add to mate_user; use separate table
|
||||
CREATE TABLE IF NOT EXISTS mate_morning_card_seen (
|
||||
id BIGINT PRIMARY KEY ,
|
||||
user_id BIGINT NOT NULL,
|
||||
agent_id BIGINT NOT NULL,
|
||||
last_seen_at TIMESTAMP NOT NULL,
|
||||
last_report_id BIGINT,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_user_agent ON mate_morning_card_seen (user_id, agent_id);
|
||||
@ -0,0 +1,49 @@
|
||||
-- Dream v2 Phase 3: Fact projection tables (read-only derived from canonical)
|
||||
-- Ref: rfc-038 §3.3
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact (
|
||||
id BIGINT PRIMARY KEY ,
|
||||
agent_id BIGINT NOT NULL,
|
||||
source_ref VARCHAR(512) NOT NULL,
|
||||
category VARCHAR(64),
|
||||
subject VARCHAR(256),
|
||||
predicate VARCHAR(256),
|
||||
object_value TEXT,
|
||||
confidence DOUBLE PRECISION DEFAULT 1.0,
|
||||
trust DOUBLE PRECISION DEFAULT 0.5,
|
||||
last_used_at TIMESTAMP,
|
||||
use_count INT DEFAULT 0,
|
||||
extracted_by VARCHAR(32) DEFAULT 'pattern',
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted SMALLINT DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent_source ON mate_fact (agent_id, source_ref);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent_subject ON mate_fact (agent_id, subject);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_agent ON mate_fact (agent_id, deleted);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_entity_ref (
|
||||
id BIGINT PRIMARY KEY ,
|
||||
fact_id BIGINT NOT NULL,
|
||||
entity_name VARCHAR(256) NOT NULL,
|
||||
entity_type VARCHAR(64),
|
||||
role VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_ref_entity ON mate_fact_entity_ref (entity_name, entity_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_fact_ref_fact ON mate_fact_entity_ref (fact_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_fact_contradiction (
|
||||
id BIGINT PRIMARY KEY ,
|
||||
agent_id BIGINT NOT NULL,
|
||||
fact_a_id BIGINT NOT NULL,
|
||||
fact_b_id BIGINT NOT NULL,
|
||||
description TEXT,
|
||||
resolution VARCHAR(32),
|
||||
resolved_at TIMESTAMP,
|
||||
resolved_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted SMALLINT DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_contradiction_agent ON mate_fact_contradiction (agent_id, resolution);
|
||||
@ -0,0 +1,227 @@
|
||||
-- V2: Upgrade schema for databases created before Flyway was introduced.
|
||||
-- MySQL does NOT support ALTER TABLE ... ADD COLUMN IF NOT EXISTS (MariaDB-only).
|
||||
-- We use INFORMATION_SCHEMA + dynamic SQL as an idempotent replacement so this migration
|
||||
-- is safe on BOTH: (a) fresh MySQL installs whose V1 baseline already contains the columns,
|
||||
-- and (b) legacy installs bootstrapped from the old schema.sql that predates those columns.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
slug VARCHAR(64) NOT NULL,
|
||||
description VARCHAR(256),
|
||||
owner_id BIGINT,
|
||||
settings_json TEXT,
|
||||
base_path VARCHAR(512),
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_workspace_slug ON mate_workspace (slug);
|
||||
|
||||
-- mate_workspace.base_path (legacy upgrade path)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_workspace' AND column_name = 'base_path'
|
||||
) THEN
|
||||
ALTER TABLE mate_workspace ADD COLUMN base_path VARCHAR(512);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_member (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
role VARCHAR(32) NOT NULL DEFAULT 'member',
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_workspace ON mate_workspace_member (workspace_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ws_member_user ON mate_workspace_member (user_id);
|
||||
|
||||
-- workspace_id on pre-existing domain tables
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_agent' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_agent ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_channel' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_channel ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_conversation' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_conversation ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_knowledge_base' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_knowledge_base ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_tool' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_tool ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'workspace_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN workspace_id BIGINT NOT NULL DEFAULT 1;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_workspace_file (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
content TEXT,
|
||||
file_size BIGINT NOT NULL DEFAULT 0,
|
||||
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_file_agent ON mate_workspace_file (agent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_usage_daily (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT NOT NULL,
|
||||
agent_id BIGINT NOT NULL,
|
||||
stat_date DATE NOT NULL,
|
||||
conversation_count INT NOT NULL DEFAULT 0,
|
||||
message_count INT NOT NULL DEFAULT 0,
|
||||
tool_call_count INT NOT NULL DEFAULT 0,
|
||||
prompt_tokens BIGINT NOT NULL DEFAULT 0,
|
||||
completion_tokens BIGINT NOT NULL DEFAULT 0,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_usage_daily ON mate_usage_daily (workspace_id, agent_id, stat_date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_audit_event (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
workspace_id BIGINT,
|
||||
user_id BIGINT,
|
||||
username VARCHAR(64),
|
||||
action VARCHAR(64) NOT NULL,
|
||||
resource_type VARCHAR(64),
|
||||
resource_id VARCHAR(128),
|
||||
detail TEXT,
|
||||
ip_address VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_ws_time ON mate_audit_event (workspace_id, create_time);
|
||||
|
||||
-- model provider OAuth columns
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'auth_type'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN auth_type VARCHAR(16) NOT NULL DEFAULT 'api_key';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_access_token'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN oauth_access_token TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_refresh_token'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN oauth_refresh_token TEXT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_expires_at'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN oauth_expires_at BIGINT;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_model_provider' AND column_name = 'oauth_account_id'
|
||||
) THEN
|
||||
ALTER TABLE mate_model_provider ADD COLUMN oauth_account_id VARCHAR(128);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_skill (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
skill_id BIGINT NOT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS mate_agent_tool (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
tool_name VARCHAR(128) NOT NULL,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS mate_memory_recall (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
filename VARCHAR(256) NOT NULL,
|
||||
content TEXT,
|
||||
tags VARCHAR(512),
|
||||
score DOUBLE PRECISION NOT NULL DEFAULT 0.0,
|
||||
last_recalled_at TIMESTAMP,
|
||||
promoted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
update_time TIMESTAMP NOT NULL,
|
||||
deleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_memory_recall_agent ON mate_memory_recall (agent_id);
|
||||
@ -0,0 +1,76 @@
|
||||
-- Register 4 collaboration skills introduced in RFC-044.
|
||||
-- These were previously only in seed data files; this migration ensures they exist
|
||||
-- in all environments (including existing installs that have already run seed data).
|
||||
-- Ref: rfc-044-skill-md-completion-2026-04-23
|
||||
|
||||
INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted)
|
||||
VALUES (1000000016, 'make_plan',
|
||||
'当任务需要多步拆解或不确定执行路径时,向更强 Agent 请求一份分步可落地的执行计划,由当前 Agent 自己执行。',
|
||||
'builtin', '🗺️', '1.3.0', 'MateClaw',
|
||||
'{"upstream":"mateclaw","entryFile":"SKILL.md"}',
|
||||
1, 1, 'plan,delegate,agent,collaboration', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description,
|
||||
skill_type = EXCLUDED.skill_type,
|
||||
icon = EXCLUDED.icon,
|
||||
version = EXCLUDED.version,
|
||||
author = EXCLUDED.author,
|
||||
config_json = EXCLUDED.config_json,
|
||||
enabled = EXCLUDED.enabled,
|
||||
builtin = EXCLUDED.builtin,
|
||||
tags = EXCLUDED.tags,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
|
||||
INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted)
|
||||
VALUES (1000000017, 'chat_with_agent',
|
||||
'当需要咨询其他 Agent、寻求帮助或用户明确要求某个 Agent 参与时,使用本技能进行单次或并行委托。',
|
||||
'builtin', '💬', '1.2.0', 'MateClaw',
|
||||
'{"upstream":"mateclaw","entryFile":"SKILL.md"}',
|
||||
1, 1, 'agent,chat,collaborate,delegate', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description,
|
||||
skill_type = EXCLUDED.skill_type,
|
||||
icon = EXCLUDED.icon,
|
||||
version = EXCLUDED.version,
|
||||
author = EXCLUDED.author,
|
||||
config_json = EXCLUDED.config_json,
|
||||
enabled = EXCLUDED.enabled,
|
||||
builtin = EXCLUDED.builtin,
|
||||
tags = EXCLUDED.tags,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
|
||||
INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted)
|
||||
VALUES (1000000018, 'channel_message',
|
||||
'当需要主动向用户、会话或渠道单向推送消息时使用。任务完成通知、定时提醒、异步结果回推等场景。',
|
||||
'builtin', '📤', '1.3.0', 'MateClaw',
|
||||
'{"upstream":"mateclaw","entryFile":"SKILL.md"}',
|
||||
1, 1, 'channel,message,push,notify,dingtalk,feishu', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description,
|
||||
skill_type = EXCLUDED.skill_type,
|
||||
icon = EXCLUDED.icon,
|
||||
version = EXCLUDED.version,
|
||||
author = EXCLUDED.author,
|
||||
config_json = EXCLUDED.config_json,
|
||||
enabled = EXCLUDED.enabled,
|
||||
builtin = EXCLUDED.builtin,
|
||||
tags = EXCLUDED.tags,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
|
||||
INSERT INTO mate_skill (id, name, description, skill_type, icon, version, author, config_json, enabled, builtin, tags, create_time, update_time, deleted)
|
||||
VALUES (1000000019, 'multi_agent_collaboration',
|
||||
'当任务需要多个 Agent 的专业能力协同完成时,编排多 Agent 并行或串行协作,整合各方结果。',
|
||||
'builtin', '🤝', '1.4.0', 'MateClaw',
|
||||
'{"upstream":"mateclaw","entryFile":"SKILL.md"}',
|
||||
1, 1, 'multi-agent,collaboration,orchestration,parallel', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET description = EXCLUDED.description,
|
||||
skill_type = EXCLUDED.skill_type,
|
||||
icon = EXCLUDED.icon,
|
||||
version = EXCLUDED.version,
|
||||
author = EXCLUDED.author,
|
||||
config_json = EXCLUDED.config_json,
|
||||
enabled = EXCLUDED.enabled,
|
||||
builtin = EXCLUDED.builtin,
|
||||
tags = EXCLUDED.tags,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,5 @@
|
||||
-- V31: Register DocxRenderTool as built-in tool (RFC-045)
|
||||
-- Idempotent: ON DUPLICATE KEY UPDATE keeps the row in sync if it already exists.
|
||||
INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted)
|
||||
VALUES (1000000019, 'DocxRenderTool', 'DOCX Render', 'Render Markdown directly into a .docx and return a one-time download link. In-process Apache POI implementation, no Node.js subprocess; supports headings, bold, lists, tables. Preferred tool for creating new documents.', 'builtin', 'docxRenderTool', '📝', 1, 1, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, bean_name=EXCLUDED.bean_name, icon=EXCLUDED.icon, update_time=EXCLUDED.update_time;
|
||||
@ -0,0 +1,24 @@
|
||||
-- V32: Register Aliyun Bailian Token Plan provider and models
|
||||
-- OpenAI-compatible endpoint for team subscription users.
|
||||
-- freeze_url=1: the endpoint is plan-specific and must not be overridden.
|
||||
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time)
|
||||
VALUES ('bailian-team', '百炼 Token Plan', 'sk-', 'OpenAIChatModel', '', 'https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1', '{}', 0, 0, 0, 1, 1, 1, NOW(), NOW())
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, generate_kwargs=EXCLUDED.generate_kwargs, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time;
|
||||
|
||||
-- Chat models
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000400, 'Qwen 3.6 Plus', 'bailian-team', 'qwen3.6-plus', '百炼团队套餐 — 千问旗舰推理模型,支持视觉理解与文本生成', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000401, 'DeepSeek V3.2', 'bailian-team', 'deepseek-v3.2', '百炼团队套餐 — DeepSeek 最新推理模型', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000402, 'GLM-5', 'bailian-team', 'glm-5', '百炼团队套餐 — 智谱 GLM-5 文本生成模型', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time;
|
||||
|
||||
-- Image generation models (temperature/max_tokens/top_p not applicable)
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000403, 'Qwen Image 2.0', 'bailian-team', 'qwen-image-2.0', '百炼团队套餐 — 千问图片生成模型', NULL, NULL, NULL, 1, 1, 0, 'image', NOW(), NOW(), 0),
|
||||
(1000000404, 'Qwen Image 2.0 Pro', 'bailian-team', 'qwen-image-2.0-pro', '百炼团队套餐 — 千问图片生成旗舰模型', NULL, NULL, NULL, 1, 1, 0, 'image', NOW(), NOW(), 0),
|
||||
(1000000405, 'Wan 2.7 Image', 'bailian-team', 'wan2.7-image', '百炼团队套餐 — 万相图片生成模型', NULL, NULL, NULL, 1, 1, 0, 'image', NOW(), NOW(), 0),
|
||||
(1000000406, 'Wan 2.7 Image Pro', 'bailian-team', 'wan2.7-image-pro', '百炼团队套餐 — 万相图片生成旗舰模型', NULL, NULL, NULL, 1, 1, 0, 'image', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time;
|
||||
@ -0,0 +1,3 @@
|
||||
-- V33: Expand mate_model_provider.api_key from VARCHAR(256) to VARCHAR(512)
|
||||
-- Bailian Token Plan keys exceed 256 chars (observed: 298 chars).
|
||||
ALTER TABLE mate_model_provider ALTER COLUMN api_key TYPE VARCHAR(512);
|
||||
@ -0,0 +1,47 @@
|
||||
-- V34: Add SiliconFlow (CN + INTL) and OpenCode providers with preset models
|
||||
-- SiliconFlow supports model discovery; preset models cover the most popular ones.
|
||||
-- OpenCode is a free-tier provider with two fixed models.
|
||||
|
||||
-- ── Providers ──────────────────────────────────────────────────────────────
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time)
|
||||
VALUES ('siliconflow-cn', '硅基流动 (China)', 'sk-', 'OpenAIChatModel', '', 'https://api.siliconflow.cn/v1', '{}', 0, 0, 1, 1, 1, 1, NOW(), NOW())
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, support_model_discovery=EXCLUDED.support_model_discovery, support_connection_check=EXCLUDED.support_connection_check, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time;
|
||||
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time)
|
||||
VALUES ('siliconflow-intl', '硅基流动 (International)', 'sk-', 'OpenAIChatModel', '', 'https://api.siliconflow.com/v1', '{}', 0, 0, 1, 1, 1, 1, NOW(), NOW())
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, api_key_prefix=EXCLUDED.api_key_prefix, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, support_model_discovery=EXCLUDED.support_model_discovery, support_connection_check=EXCLUDED.support_connection_check, freeze_url=EXCLUDED.freeze_url, require_api_key=EXCLUDED.require_api_key, update_time=EXCLUDED.update_time;
|
||||
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, create_time, update_time)
|
||||
VALUES ('opencode', 'OpenCode', '', 'OpenAIChatModel', '', 'https://opencode.ai/zen/v1', '{}', 0, 0, 0, 1, 1, 0, NOW(), NOW())
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name=EXCLUDED.name, chat_model=EXCLUDED.chat_model, base_url=EXCLUDED.base_url, update_time=EXCLUDED.update_time;
|
||||
|
||||
-- ── SiliconFlow CN — preset popular models ─────────────────────────────────
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000500, 'DeepSeek V3', 'siliconflow-cn', 'deepseek-ai/DeepSeek-V3', '硅基流动 — DeepSeek V3,综合能力强,有免费额度', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000501, 'DeepSeek R1', 'siliconflow-cn', 'deepseek-ai/DeepSeek-R1', '硅基流动 — DeepSeek R1 推理模型', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000502, 'Qwen3 235B A22B', 'siliconflow-cn', 'Qwen/Qwen3-235B-A22B', '硅基流动 — 千问3旗舰 MoE 模型', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000503, 'Qwen3 30B A3B', 'siliconflow-cn', 'Qwen/Qwen3-30B-A3B', '硅基流动 — 千问3高性价比 MoE 模型', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000504, 'GLM-4 9B Chat', 'siliconflow-cn', 'THUDM/glm-4-9b-chat', '硅基流动 — 智谱 GLM-4 9B,免费可用', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000505, 'DeepSeek V3 Pro', 'siliconflow-cn', 'Pro/deepseek-ai/DeepSeek-V3', '硅基流动 Pro — DeepSeek V3 Pro 优先调度版', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000506, 'DeepSeek R1 Pro', 'siliconflow-cn', 'Pro/deepseek-ai/DeepSeek-R1', '硅基流动 Pro — DeepSeek R1 推理 Pro 优先调度版', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time;
|
||||
|
||||
-- ── SiliconFlow INTL — same preset models via international endpoint ────────
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000510, 'DeepSeek V3', 'siliconflow-intl', 'deepseek-ai/DeepSeek-V3', 'SiliconFlow INTL — DeepSeek V3, strong general capability', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000511, 'DeepSeek R1', 'siliconflow-intl', 'deepseek-ai/DeepSeek-R1', 'SiliconFlow INTL — DeepSeek R1 reasoning model', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000512, 'Qwen3 235B A22B', 'siliconflow-intl', 'Qwen/Qwen3-235B-A22B', 'SiliconFlow INTL — Qwen3 flagship MoE model', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000513, 'Qwen3 30B A3B', 'siliconflow-intl', 'Qwen/Qwen3-30B-A3B', 'SiliconFlow INTL — Qwen3 efficient MoE model', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000514, 'GLM-4 9B Chat', 'siliconflow-intl', 'THUDM/glm-4-9b-chat', 'SiliconFlow INTL — Zhipu GLM-4 9B, free tier', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000515, 'DeepSeek V3 Pro', 'siliconflow-intl', 'Pro/deepseek-ai/DeepSeek-V3', 'SiliconFlow INTL Pro — DeepSeek V3 priority tier', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000516, 'DeepSeek R1 Pro', 'siliconflow-intl', 'Pro/deepseek-ai/DeepSeek-R1', 'SiliconFlow INTL Pro — DeepSeek R1 priority tier', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, model_type=EXCLUDED.model_type, update_time=EXCLUDED.update_time;
|
||||
|
||||
-- ── OpenCode — free public models ──────────────────────────────────────────
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, model_type, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000520, 'Big Pickle', 'opencode', 'big-pickle', 'OpenCode 免费模型 — Big Pickle', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0),
|
||||
(1000000521, 'Nemotron 3 Super Free', 'opencode', 'nemotron-3-super-free', 'OpenCode 免费模型 — Nemotron 3 Super', 0.7, 4096, 0.8, 1, 1, 0, 'chat', NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description, update_time=EXCLUDED.update_time;
|
||||
@ -0,0 +1,28 @@
|
||||
-- V35: RFC-042 §2.3 — persist skill security scan result and timestamp.
|
||||
-- Until now findings lived only in SkillRuntimeStatus memory; after a restart
|
||||
-- the admin page couldn't explain why a skill was blocked. These two columns
|
||||
-- keep the last scan's findings (JSONB) and time so the UI can render them
|
||||
-- and offer a rescan control.
|
||||
--
|
||||
-- MySQL has no ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guards so
|
||||
-- the migration is idempotent across redeploys.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'security_scan_result'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN security_scan_result TEXT DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'security_scan_time'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN security_scan_time TIMESTAMP DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,51 @@
|
||||
-- V36: RFC-042 §2.2 — bilingual display names for skills.
|
||||
-- name stays the immutable slug / unique identifier; name_zh and
|
||||
-- name_en are optional locale-specific display labels. The UI falls
|
||||
-- back to name when the locale-matching column is null.
|
||||
--
|
||||
-- MySQL has no ADD COLUMN IF NOT EXISTS; INFORMATION_SCHEMA guards
|
||||
-- make the migration idempotent across redeploys.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'name_zh'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN name_zh VARCHAR(128) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_skill' AND column_name = 'name_en'
|
||||
) THEN
|
||||
ALTER TABLE mate_skill ADD COLUMN name_en VARCHAR(128) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Backfill bilingual names for the 19 builtin skills that already exist on
|
||||
-- upgraded deployments. UPDATE is idempotent — running it again is a no-op
|
||||
-- since the values match. Fresh installs handle this in data-*.sql instead
|
||||
-- (those rows don't exist yet when this migration runs).
|
||||
UPDATE mate_skill SET name_zh = '定时任务', name_en = 'Cron Jobs' WHERE name = 'cron';
|
||||
UPDATE mate_skill SET name_zh = '文件阅读器', name_en = 'File Reader' WHERE name = 'file_reader';
|
||||
UPDATE mate_skill SET name_zh = '钉钉渠道接入', name_en = 'DingTalk Channel' WHERE name = 'dingtalk_channel_connect';
|
||||
UPDATE mate_skill SET name_zh = '邮件管理', name_en = 'Email (Himalaya)' WHERE name = 'himalaya';
|
||||
UPDATE mate_skill SET name_zh = '新闻查询', name_en = 'News' WHERE name = 'news';
|
||||
UPDATE mate_skill SET name_zh = 'PDF 处理', name_en = 'PDF' WHERE name = 'pdf';
|
||||
UPDATE mate_skill SET name_zh = 'Word 文档', name_en = 'Word Document' WHERE name = 'docx';
|
||||
UPDATE mate_skill SET name_zh = 'PPT 演示', name_en = 'PowerPoint' WHERE name = 'pptx';
|
||||
UPDATE mate_skill SET name_zh = 'Excel 表格', name_en = 'Excel' WHERE name = 'xlsx';
|
||||
UPDATE mate_skill SET name_zh = '可见浏览器', name_en = 'Visible Browser' WHERE name = 'browser_visible';
|
||||
UPDATE mate_skill SET name_zh = '浏览器 CDP', name_en = 'Browser CDP' WHERE name = 'browser_cdp';
|
||||
UPDATE mate_skill SET name_zh = '安装指引', name_en = 'Setup Guidance' WHERE name = 'guidance';
|
||||
UPDATE mate_skill SET name_zh = '源码索引', name_en = 'Source Index' WHERE name = 'mateclaw_source_index';
|
||||
UPDATE mate_skill SET name_zh = 'SQL 查询', name_en = 'SQL Query' WHERE name = 'sql_query';
|
||||
UPDATE mate_skill SET name_zh = '乔布斯视角', name_en = 'Steve Jobs Perspective' WHERE name = 'steve_jobs_perspective';
|
||||
UPDATE mate_skill SET name_zh = '制定计划', name_en = 'Make Plan' WHERE name = 'make_plan';
|
||||
UPDATE mate_skill SET name_zh = '咨询智能体', name_en = 'Chat with Agent' WHERE name = 'chat_with_agent';
|
||||
UPDATE mate_skill SET name_zh = '渠道推送', name_en = 'Channel Push' WHERE name = 'channel_message';
|
||||
UPDATE mate_skill SET name_zh = '多智能体协作', name_en = 'Multi-Agent Collaboration' WHERE name = 'multi_agent_collaboration';
|
||||
@ -0,0 +1,6 @@
|
||||
-- RFC-047 P2: Add source_entries column to mate_wiki_page for paired (rawId, rawTitle) lineage.
|
||||
-- Paired entries guarantee title-rawId alignment even when raw titles change.
|
||||
-- Dual-written alongside the existing source_raw_ids for backwards compatibility.
|
||||
-- KingbaseES (PostgreSQL) supports ADD COLUMN IF NOT EXISTS natively.
|
||||
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN IF NOT EXISTS source_entries JSONB NULL;
|
||||
@ -0,0 +1,3 @@
|
||||
-- V38: Expand mate_wiki_chunk.content from TEXT (64KB) to TEXT (16MB)
|
||||
-- In KingbaseES/PostgreSQL, TEXT is already unlimited (up to 1GB),
|
||||
-- so this migration is a no-op. Keep for Flyway version compatibility.
|
||||
@ -0,0 +1,41 @@
|
||||
-- V39: RFC-051 PR-1a — chunk structural metadata.
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard instead.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'page_number'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN page_number INT DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'token_count'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN token_count INT DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'header_breadcrumb'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN header_breadcrumb VARCHAR(1024) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_chunk' AND column_name = 'source_section'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_chunk ADD COLUMN source_section VARCHAR(512) DEFAULT NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,4 @@
|
||||
-- V3: Register CronJobTool as built-in tool (RFC-003)
|
||||
INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted)
|
||||
VALUES (1000000018, 'CronJobTool', 'Scheduled Tasks', 'Create, list, enable/disable, and delete scheduled tasks (cron jobs) through chat.', 'builtin', 'cronJobTool', '⏰', 1, 1, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, display_name=EXCLUDED.display_name, description=EXCLUDED.description, update_time=EXCLUDED.update_time;
|
||||
@ -0,0 +1,11 @@
|
||||
-- V40: RFC-051 PR-2 — page protection flag.
|
||||
-- MySQL lacks ADD COLUMN IF NOT EXISTS; use INFORMATION_SCHEMA guard.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'locked'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN locked SMALLINT NOT NULL DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,10 @@
|
||||
-- V41: RFC-051 PR-7 — soft-archive flag.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'mate_wiki_page' AND column_name = 'archived'
|
||||
) THEN
|
||||
ALTER TABLE mate_wiki_page ADD COLUMN archived SMALLINT NOT NULL DEFAULT 0;
|
||||
END IF;
|
||||
END $$;
|
||||
@ -0,0 +1,37 @@
|
||||
-- Add Claude 4.7 + GPT-5.5 model entries to mate_model_config for existing
|
||||
-- deployments. New installs pick these up via DatabaseBootstrapRunner from
|
||||
-- data-mysql-{en,zh}.sql; this migration covers operators who already have
|
||||
-- earlier Flyway versions applied.
|
||||
--
|
||||
-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert.
|
||||
-- Same V number is used in h2/ for cross-dialect parity.
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
-- GPT-5.5 series (OpenAI / Azure / OpenRouter)
|
||||
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, 1, 1, 0, NOW(), NOW(), 0),
|
||||
-- Claude 4.7 series. NOTE: Claude 4.7 forbids temperature/top_p/top_k —
|
||||
-- handled in AgentAnthropicChatModelBuilder. NULL temperature/top_p in seed
|
||||
-- is the documented signal.
|
||||
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
provider = EXCLUDED.provider,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
temperature = EXCLUDED.temperature,
|
||||
max_tokens = EXCLUDED.max_tokens,
|
||||
top_p = EXCLUDED.top_p,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
is_default = EXCLUDED.is_default,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,32 @@
|
||||
-- RFC-062: Seed the Anthropic Claude Code OAuth provider + its Claude 4.7
|
||||
-- model bindings on existing deployments. New installs already get these
|
||||
-- rows from data-mysql-{en,zh}.sql via DatabaseBootstrapRunner; this
|
||||
-- migration is for operators upgrading from <= V42.
|
||||
--
|
||||
-- INSERT ... ON CONFLICT DO UPDATE is the PostgreSQL idempotent upsert.
|
||||
-- Same V number is used in h2/ for cross-dialect parity.
|
||||
|
||||
INSERT INTO mate_model_provider (provider_id, name, api_key_prefix, chat_model, api_key, base_url, generate_kwargs, is_custom, is_local, support_model_discovery, support_connection_check, freeze_url, require_api_key, auth_type, create_time, update_time)
|
||||
VALUES ('anthropic-claude-code', 'Anthropic Claude Code (OAuth)', '', 'ClaudeCodeChatModel', '', 'https://api.anthropic.com', '{}', 0, 0, 0, 0, 1, 0, 'oauth', NOW(), NOW())
|
||||
ON CONFLICT (provider_id) DO UPDATE SET name = EXCLUDED.name,
|
||||
chat_model = EXCLUDED.chat_model,
|
||||
base_url = EXCLUDED.base_url,
|
||||
auth_type = EXCLUDED.auth_type,
|
||||
update_time = EXCLUDED.update_time;
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', 'Claude Opus 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', 'Claude Sonnet 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name,
|
||||
provider = EXCLUDED.provider,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
temperature = EXCLUDED.temperature,
|
||||
max_tokens = EXCLUDED.max_tokens,
|
||||
top_p = EXCLUDED.top_p,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
is_default = EXCLUDED.is_default,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,31 @@
|
||||
-- Repair migration for V42/V43: Anthropic only released Opus 4.7 — there
|
||||
-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body
|
||||
-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}.
|
||||
--
|
||||
-- Anthropic's current model line includes claude-opus-4-7 but no
|
||||
-- claude-sonnet-4-7. The latest released Sonnet remains claude-sonnet-4-6
|
||||
-- (released alongside Opus 4.6).
|
||||
--
|
||||
-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281
|
||||
-- so user-customised settings (default flag, enabled flag) survive.
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000271 AND model_name = 'claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'anthropic/claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via OpenRouter',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000273 AND model_name = 'anthropic/claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000281 AND model_name = 'claude-sonnet-4-7';
|
||||
@ -0,0 +1,19 @@
|
||||
-- Add DeepSeek V4 (flash + pro) model entries for MySQL deployments.
|
||||
-- Cross-dialect parity with h2/V45 — see that file's header for context.
|
||||
|
||||
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
|
||||
VALUES
|
||||
(1000000282, 'DeepSeek V4 Flash', 'deepseek', 'deepseek-v4-flash', 'DeepSeek V4 Flash (1M context, reasoning via thinking-enabled mode)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0),
|
||||
(1000000283, 'DeepSeek V4 Pro', 'deepseek', 'deepseek-v4-pro', 'DeepSeek V4 Pro (1M context, reasoning via thinking-enabled mode)', NULL, 4096, NULL, 1, 1, 0, NOW(), NOW(), 0)
|
||||
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name,
|
||||
provider = EXCLUDED.provider,
|
||||
model_name = EXCLUDED.model_name,
|
||||
description = EXCLUDED.description,
|
||||
temperature = EXCLUDED.temperature,
|
||||
max_tokens = EXCLUDED.max_tokens,
|
||||
top_p = EXCLUDED.top_p,
|
||||
builtin = EXCLUDED.builtin,
|
||||
enabled = EXCLUDED.enabled,
|
||||
is_default = EXCLUDED.is_default,
|
||||
update_time = EXCLUDED.update_time,
|
||||
deleted = EXCLUDED.deleted;
|
||||
@ -0,0 +1,21 @@
|
||||
-- Default-enable STT on existing deployments. See the h2/ counterpart for
|
||||
-- the "why enabled by default" rationale and the bug history that drove
|
||||
-- the skip-if-exists idiom. Same V number is used in h2/ for cross-dialect
|
||||
-- parity.
|
||||
--
|
||||
-- MySQL doesn't allow INSERT ... SELECT ... WHERE NOT EXISTS without a
|
||||
-- FROM clause, so we synthesise one with FROM DUAL. The end result is
|
||||
-- the same: insert when the setting_key is absent, no-op when it's already
|
||||
-- there.
|
||||
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
SELECT 1000000020, 'sttEnabled', 'true', 'Enable speech-to-text (TalkMode mic input)', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttEnabled');
|
||||
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
SELECT 1000000021, 'sttProvider', 'auto', 'STT provider: auto / openai / dashscope', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttProvider');
|
||||
|
||||
INSERT INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
SELECT 1000000022, 'sttFallbackEnabled', 'true', 'Try alternate STT provider when the primary fails', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM mate_system_setting WHERE setting_key = 'sttFallbackEnabled');
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user