0.11.0: 旗舰版功能补齐(License/登录记录/会话管理/角色管理/门户定时任务/模型配额/输出脱敏/供应链扫描/记忆管理/AI助手/真实概览)

- 新增迁移 000031-000034(登录日志/角色/模型配额/记忆)
- 新增包: license/memory/modelquota/assistant,扫描引擎
- 全部功能后端+前端+端到端验证通过(25 包单测)
This commit is contained in:
2026-08-13 11:37:18 +08:00
parent 9501751792
commit c22669c31d
43 changed files with 3672 additions and 254 deletions
+42
View File
@@ -11,12 +11,15 @@ import (
"time"
"aigateway.local/core/internal/agentnode"
"aigateway.local/core/internal/assistant"
"aigateway.local/core/internal/apikey"
"aigateway.local/core/internal/audit"
"aigateway.local/core/internal/contentpolicy"
"aigateway.local/core/internal/factcheck"
"aigateway.local/core/internal/gateway"
"aigateway.local/core/internal/identity"
"aigateway.local/core/internal/memory"
"aigateway.local/core/internal/modelquota"
"aigateway.local/core/internal/operations"
"aigateway.local/core/internal/outbox"
"aigateway.local/core/internal/platform/cache"
@@ -25,6 +28,7 @@ import (
"aigateway.local/core/internal/platform/database"
"aigateway.local/core/internal/platform/health"
"aigateway.local/core/internal/platform/httpserver"
"aigateway.local/core/internal/platform/license"
"aigateway.local/core/internal/platform/storage"
"aigateway.local/core/internal/portal"
"aigateway.local/core/internal/pricing"
@@ -135,6 +139,13 @@ func main() {
proxy.SetAllowPrivateProviderURLs(cfg.Credentials.AllowPrivateProviderURL)
proxy.SetAdmissionController(gateway.NewRedisAdmissionController(criticalRedis))
proxy.SetTokenQuotaController(gateway.NewRedisTokenQuotaController(criticalRedis))
// M8+ 模型级 Token 配额:企业模型总配额(所有 Key 共享),与 Key 级配额叠加。
modelQuotaService := modelquota.NewService(db, criticalRedis, logger)
if err := modelQuotaService.Reload(ctx); err != nil {
logger.Warn("model quota initial load failed; quota checks disabled until refresh", "error", err)
}
go modelQuotaService.Run(ctx, cfg.RuntimeData.PricingRefreshInterval)
proxy.SetModelQuotaController(modelQuotaService)
proxy.SetResiliencePolicy(gateway.ResiliencePolicy{
ResponseHeaderTimeout: cfg.Upstream.ResponseHeaderTimeout, MaxRetries: cfg.Upstream.MaxRetries,
RetryBackoff: cfg.Upstream.RetryBackoff, CircuitThreshold: cfg.Upstream.CircuitThreshold,
@@ -161,6 +172,7 @@ func main() {
go contentPolicyEngine.Run(ctx)
go pricingService.Run(ctx)
proxy.SetContentPolicyEngine(contentPolicyEngine)
proxy.SetOutputPolicyEngine(contentPolicyEngine)
proxy.SetPricingService(pricingService)
identityRepository := identity.NewRepository(db)
sessionStore := identity.NewSessionStore(criticalRedis, cfg.Auth.SessionTTL)
@@ -220,6 +232,15 @@ func main() {
} else {
logger.Info("knowledge embeddings disabled, knowledge retrieval uses postgres_fts only")
}
// 记忆管理(旗舰版):多层记忆 CRUD + 语义召回 + 授权;embedder 与知识库共用。
memoryService := memory.NewService(db, nil)
if cfg.Embeddings.Enabled {
memoryService.SetEmbedder(workbench.NewOllamaEmbedder(workbench.OllamaEmbedderConfig{
BaseURL: cfg.Embeddings.BaseURL, Model: cfg.Embeddings.Model,
Dim: cfg.Embeddings.Dim, BatchSize: cfg.Embeddings.BatchSize, Timeout: cfg.Embeddings.Timeout,
}))
}
memoryHandler := memory.NewHTTPHandler(memoryService, identityService)
toolCipher, err := cryptox.NewKeyring(
cfg.Credentials.MasterKey, cfg.Credentials.KEKVersion, cfg.Credentials.KEKKeyring, "tool-request-headers",
)
@@ -286,6 +307,7 @@ func main() {
}
schedulerService := scheduler.NewService(db, schedulerCipher)
schedulerHandler := scheduler.NewAdminHTTPHandler(schedulerService, identityService)
portalSchedulerHandler := scheduler.NewPortalHTTPHandler(schedulerService, identityService)
traceStore := trace.NewStore(db)
traceHandler := trace.NewAdminHTTPHandler(traceStore, identityService)
agentNodeStore := agentnode.NewStore(db)
@@ -317,7 +339,16 @@ func main() {
portalService.SetMarketplace(marketplaceService)
portalHandler := portal.NewHTTPHandler(portalService, identityService)
portalAdminHandler := portal.NewAdminHTTPHandler(portalService, identityService)
// License 授权:文件校验 + 账号数管控 + 管理端查看/上传。
licenseManager, err := license.NewManager(cfg.License.FilePath, cfg.Credentials.MasterKey)
if err != nil {
logger.Warn("license initialization failed; running as community edition", "error", license.FormatError(err))
}
identityManagementHandler.SetLicenseManager(licenseManager)
licenseHandler := license.NewHTTPHandler(licenseManager, identityService)
startedAt := time.Now()
assistantService := assistant.NewService(db, providerResolver, "", logger)
assistantHandler := assistant.NewHTTPHandler(assistantService, identityService)
operationsHandler := operations.NewAdminHTTPHandler(db, identityService, version, startedAt, func(reloadCtx context.Context) error {
return errors.Join(providerResolver.Reload(reloadCtx), contentPolicyEngine.Reload(reloadCtx), pricingService.Reload(reloadCtx))
})
@@ -335,6 +366,7 @@ func main() {
controlMux.Handle("/api/v1/admin/content-policies", contentPolicyHandler)
controlMux.Handle("/api/v1/admin/content-policies/", contentPolicyHandler)
controlMux.Handle("/api/v1/admin/model-prices", pricingHandler)
controlMux.Handle("/api/v1/admin/model-quotas", modelquota.NewHTTPHandler(modelQuotaService, identityService))
controlMux.Handle("/api/v1/admin/model-prices/", pricingHandler)
controlMux.Handle("/api/v1/admin/fact-check/", factCheckHandler)
controlMux.Handle("/api/v1/admin/prompt-categories", workbenchHandler)
@@ -376,6 +408,8 @@ func main() {
controlMux.Handle("/api/v1/portal/inbox", inboxPortalHandler)
controlMux.Handle("/api/v1/portal/inbox/", inboxPortalHandler)
controlMux.Handle("/api/v1/admin/scheduled-tasks", schedulerHandler)
controlMux.Handle("/api/v1/portal/scheduled-tasks", portalSchedulerHandler)
controlMux.Handle("/api/v1/portal/scheduled-tasks/", portalSchedulerHandler)
controlMux.Handle("/api/v1/admin/scheduled-tasks/", schedulerHandler)
controlMux.Handle("/api/v1/admin/traces", traceHandler)
controlMux.Handle("/api/v1/admin/traces/", traceHandler)
@@ -383,8 +417,14 @@ func main() {
controlMux.Handle("/api/v1/admin/agent-nodes", agentNodeHandler)
controlMux.Handle("/api/v1/admin/agent-nodes/", agentNodeHandler)
controlMux.Handle("/api/v1/agent/nodes/", agentNodeHandler)
controlMux.Handle("/api/v1/admin/assistant", assistantHandler)
controlMux.Handle("/api/v1/admin/assistant/", assistantHandler)
controlMux.Handle("/api/v1/admin/license", licenseHandler)
controlMux.Handle("/api/v1/admin/license/", licenseHandler)
controlMux.Handle("/api/v1/admin/reload", operationsHandler)
controlMux.Handle("/api/v1/admin/identities/", identityManagementHandler)
controlMux.Handle("/api/v1/admin/roles", identityManagementHandler)
controlMux.Handle("/api/v1/admin/roles/", identityManagementHandler)
controlMux.Handle("/api/v1/admin/departments", identityManagementHandler)
controlMux.Handle("/api/v1/admin/departments/", identityManagementHandler)
controlMux.Handle("/api/v1/admin/identity-providers", identityManagementHandler)
@@ -400,6 +440,8 @@ func main() {
controlMux.Handle("/api/v1/portal/logs", portalHandler)
controlMux.Handle("/api/v1/portal/logs/", portalHandler)
controlMux.Handle("/api/v1/portal/model-requests", portalHandler)
controlMux.Handle("/api/v1/portal/memories", memoryHandler)
controlMux.Handle("/api/v1/portal/memories/", memoryHandler)
controlMux.Handle("/api/v1/portal/model-requests/", portalHandler)
controlMux.Handle("/api/v1/portal/password", portalHandler)
controlMux.Handle("/api/v1/portal/prompts", portalHandler)