Update GLPI-Knowledge
release-tag / release-image (push) Successful in 1m35s

This commit is contained in:
2026-07-28 14:35:03 +02:00
parent 8ac1f97174
commit 4cfdac042d
18 changed files with 992 additions and 29 deletions
+77 -7
View File
@@ -45,13 +45,21 @@ type Config struct {
OllamaMaxConcurrent int
OllamaJSONRetries int
KnowledgeDir string
RAGEnabled bool
KnowledgeTopK int
CategoryPromptLimit int
KnowledgeAllowedSources []string
KnowledgeAutoReplySources []string
KnowledgeWebEditEnabled bool
KnowledgeDir string
RAGEnabled bool
KnowledgeTopK int
CategoryPromptLimit int
KnowledgeAllowedSources []string
KnowledgeAutoReplySources []string
KnowledgeWebEditEnabled bool
GLPIKBEnabled bool
GLPIKBPath string
GLPIKBFilter string
GLPIKBLimit int
GLPIKBSyncInterval time.Duration
GLPIKBSource string
GLPIKBAutoReply bool
GLPIKBAutoReplyCategoryIDs []int64
LearningEnabled bool
LearningMaxExamples int
@@ -139,6 +147,14 @@ func Load() (Config, error) {
KnowledgeAllowedSources: envStringList("KNOWLEDGE_ALLOWED_SOURCES", "internal-kb"),
KnowledgeAutoReplySources: envStringList("KNOWLEDGE_AUTO_REPLY_SOURCES", "internal-kb"),
KnowledgeWebEditEnabled: envBool("KNOWLEDGE_WEB_EDIT_ENABLED", false),
GLPIKBEnabled: envBool("GLPI_KB_ENABLED", false),
GLPIKBPath: env("GLPI_KB_PATH", "auto"),
GLPIKBFilter: strings.TrimSpace(os.Getenv("GLPI_KB_FILTER")),
GLPIKBLimit: envInt("GLPI_KB_LIMIT", 500),
GLPIKBSyncInterval: envDuration("GLPI_KB_SYNC_INTERVAL", 10*time.Minute),
GLPIKBSource: strings.ToLower(env("GLPI_KB_SOURCE", "glpi-kb")),
GLPIKBAutoReply: envBool("GLPI_KB_AUTO_REPLY", false),
GLPIKBAutoReplyCategoryIDs: envInt64ListAllowEmpty("GLPI_KB_AUTO_REPLY_CATEGORY_IDS"),
LearningEnabled: envBool("LEARNING_ENABLED", true),
LearningMaxExamples: envInt("LEARNING_MAX_EXAMPLES", 500),
LearningExamplesPerCategory: envInt("LEARNING_EXAMPLES_PER_CATEGORY", 5),
@@ -292,6 +308,38 @@ func (c Config) Validate() error {
return fmt.Errorf("KNOWLEDGE_AUTO_REPLY_SOURCES source %q is not present in KNOWLEDGE_ALLOWED_SOURCES", source)
}
}
if c.GLPIKBEnabled {
if c.GLPIKBLimit < 1 || c.GLPIKBLimit > 5000 {
return errors.New("GLPI_KB_LIMIT must be between 1 and 5000")
}
if c.GLPIKBSyncInterval < time.Minute {
return errors.New("GLPI_KB_SYNC_INTERVAL must be at least 1m")
}
if c.GLPIKBPath != "auto" && !validAPIPath(c.GLPIKBPath) {
return errors.New("GLPI_KB_PATH must be 'auto' or an absolute API path")
}
if strings.TrimSpace(c.GLPIKBSource) == "" {
return errors.New("GLPI_KB_SOURCE must not be empty")
}
if _, ok := allowedSources[c.GLPIKBSource]; !ok {
return fmt.Errorf("GLPI_KB_SOURCE %q must be present in KNOWLEDGE_ALLOWED_SOURCES", c.GLPIKBSource)
}
if c.GLPIKBAutoReply {
if len(c.GLPIKBAutoReplyCategoryIDs) == 0 {
return errors.New("GLPI_KB_AUTO_REPLY_CATEGORY_IDS must contain at least one GLPI KB category when GLPI_KB_AUTO_REPLY=true")
}
found := false
for _, source := range c.KnowledgeAutoReplySources {
if strings.EqualFold(source, c.GLPIKBSource) {
found = true
break
}
}
if !found {
return fmt.Errorf("GLPI_KB_SOURCE %q must be present in KNOWLEDGE_AUTO_REPLY_SOURCES when GLPI_KB_AUTO_REPLY=true", c.GLPIKBSource)
}
}
}
if c.AutoReply && len(c.KnowledgeAutoReplySources) == 0 {
return errors.New("KNOWLEDGE_AUTO_REPLY_SOURCES must contain at least one source when AUTO_REPLY=true")
}
@@ -402,6 +450,28 @@ func envInt64List(key, def string) []int64 {
return out
}
func envInt64ListAllowEmpty(key string) []int64 {
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" || strings.EqualFold(raw, "none") {
return nil
}
parts := strings.Split(raw, ",")
out := make([]int64, 0, len(parts))
seen := map[int64]struct{}{}
for _, part := range parts {
n, err := strconv.ParseInt(strings.TrimSpace(part), 10, 64)
if err != nil || n <= 0 {
return nil
}
if _, ok := seen[n]; ok {
continue
}
seen[n] = struct{}{}
out = append(out, n)
}
return out
}
func envStringList(key, def string) []string {
raw, ok := os.LookupEnv(key)
if !ok {
+35
View File
@@ -124,3 +124,38 @@ func TestKnowledgeWebEditRequiresAuthentication(t *testing.T) {
t.Fatal("expected anonymous KB editing to be rejected")
}
}
func TestValidateGLPIKBRequiresAllowedSource(t *testing.T) {
c := validConfig()
c.GLPIKBEnabled = true
c.GLPIKBPath = "auto"
c.GLPIKBLimit = 100
c.GLPIKBSyncInterval = 10 * time.Minute
c.GLPIKBSource = "glpi-kb"
if err := c.Validate(); err == nil {
t.Fatal("expected GLPI KB source outside allowlist to be rejected")
}
c.KnowledgeAllowedSources = []string{"internal-kb", "glpi-kb"}
if err := c.Validate(); err != nil {
t.Fatalf("expected GLPI KB config to validate: %v", err)
}
}
func TestValidateGLPIKBAutoReplyRequiresCategoryWhitelist(t *testing.T) {
c := validConfig()
c.GLPIKBEnabled = true
c.GLPIKBPath = "auto"
c.GLPIKBLimit = 100
c.GLPIKBSyncInterval = 10 * time.Minute
c.GLPIKBSource = "glpi-kb"
c.KnowledgeAllowedSources = []string{"internal-kb", "glpi-kb"}
c.KnowledgeAutoReplySources = []string{"internal-kb", "glpi-kb"}
c.GLPIKBAutoReply = true
if err := c.Validate(); err == nil {
t.Fatal("expected auto reply category whitelist to be required")
}
c.GLPIKBAutoReplyCategoryIDs = []int64{3}
if err := c.Validate(); err != nil {
t.Fatalf("expected explicit category whitelist to validate: %v", err)
}
}