ci / test (services/knowledge) (push) Successful in 47s
ci / test (services/agent) (push) Successful in 55s
ci / test (platform/neuroforge) (push) Successful in 56s
ci / docker-build (push) Failing after 1m10s
ci / test (services/control) (push) Successful in 1m26s
release-tag / release-image (push) Successful in 6m43s
6803 lines
235 KiB
Diff
6803 lines
235 KiB
Diff
diff --git a/.env.example b/.env.example
|
||
index 45a8d49..d3c6f7d 100644
|
||
--- a/.env.example
|
||
+++ b/.env.example
|
||
@@ -29,7 +29,7 @@
|
||
# 01. MEGA STACK - RELEASE / HOST PORTS / PFADE
|
||
###############################################################################
|
||
# Immutable Registry-Tag der sechs Projekt-Images. "latest" ist produktiv verboten.
|
||
-IMAGE_TAG=1.6.1
|
||
+IMAGE_TAG=1.6.2
|
||
|
||
CONTROL_HOST_PORT=8070
|
||
AGENT_HOST_PORT=8080
|
||
@@ -74,6 +74,12 @@ OPENAI_API_KEY=
|
||
# qwen3:8b statt gemma3 setzen. Das Modell muss vorher in Ollama vorhanden sein.
|
||
OLLAMA_MODEL=gemma3
|
||
OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+# Optional NeuroForge GPU-worker override; falls back to OLLAMA_API_KEY.
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
+# Optional NeuroForge-only override; takes precedence over OLLAMA_API_KEY.
|
||
+NEUROFORGE_OLLAMA_API_KEY=
|
||
OLLAMA_TIMEOUT=10m
|
||
OLLAMA_MAX_CONCURRENT=1
|
||
OLLAMA_NUM_PREDICT=768
|
||
diff --git a/.env.subagent.example b/.env.subagent.example
|
||
index 3276990..968db6f 100644
|
||
--- a/.env.subagent.example
|
||
+++ b/.env.subagent.example
|
||
@@ -1,5 +1,5 @@
|
||
-# Remote NeuroForge subagent host (v1.6.1)
|
||
-IMAGE_TAG=1.6.1
|
||
+# Remote NeuroForge subagent host (v1.6.2)
|
||
+IMAGE_TAG=1.6.2
|
||
NEUROFORGE_MASTER_URL=https://neuroforge.internal.example
|
||
NEUROFORGE_WORKER_TOKEN=CHANGE_ME_WORKER_TOKEN_SHARED_WITH_MASTER
|
||
NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s
|
||
@@ -16,5 +16,10 @@ NEUROFORGE_GPU_WORKER_CONCURRENCY=1
|
||
NEUROFORGE_WORKER_OLLAMA_URL=http://host.docker.internal:11434
|
||
OLLAMA_MODEL=gemma4
|
||
OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
NEUROFORGE_OLLAMA_NUM_CTX=8192
|
||
OLLAMA_KEEP_ALIVE=10m
|
||
+
|
||
+# Optional GPU-worker-specific override; falls back to OLLAMA_API_KEY.
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
diff --git ab/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
|
||
new file mode 100644
|
||
index 0000000..5c5d928
|
||
--- /dev/null
|
||
+++ b/.gitea/workflows/ci.yml
|
||
@@ -0,0 +1,39 @@
|
||
+name: ci
|
||
+
|
||
+on:
|
||
+ push:
|
||
+ branches: [main]
|
||
+ pull_request:
|
||
+ branches: [main]
|
||
+
|
||
+jobs:
|
||
+ test:
|
||
+ runs-on: ubuntu-latest
|
||
+ strategy:
|
||
+ fail-fast: false
|
||
+ matrix:
|
||
+ module:
|
||
+ - platform/neuroforge
|
||
+ - services/agent
|
||
+ - services/knowledge
|
||
+ - services/control
|
||
+ steps:
|
||
+ - uses: actions/checkout@v4
|
||
+ - uses: actions/setup-go@v5
|
||
+ with:
|
||
+ go-version-file: ${{ matrix.module }}/go.mod
|
||
+ cache: false
|
||
+ - name: Test
|
||
+ working-directory: ${{ matrix.module }}
|
||
+ run: go test ./...
|
||
+ - name: Vet
|
||
+ working-directory: ${{ matrix.module }}
|
||
+ run: go vet ./...
|
||
+
|
||
+ docker-build:
|
||
+ runs-on: ubuntu-latest
|
||
+ steps:
|
||
+ - uses: actions/checkout@v4
|
||
+ - uses: docker/setup-buildx-action@v3
|
||
+ - name: Build all release images
|
||
+ run: docker buildx bake --set '*.output=type=cacheonly'
|
||
diff --git a/.gitea/workflows/release-tag.yml ba/.gitea/workflows/release-tag.yml
|
||
deleted file mode 100644
|
||
index 668bef1..0000000
|
||
--- a/.gitea/workflows/release-tag.yml
|
||
+++ /dev/null
|
||
@@ -1,116 +0,0 @@
|
||
-name: release-tag
|
||
-on:
|
||
- push:
|
||
- branches:
|
||
- - 'main'
|
||
-
|
||
-jobs:
|
||
- release-image:
|
||
- runs-on: ubuntu-latest
|
||
- env:
|
||
- DOCKER_ORG: sendnrw
|
||
- DOCKER_LATEST: latest
|
||
- RUNNER_TOOL_CACHE: /toolcache
|
||
- steps:
|
||
- - name: Checkout
|
||
- uses: actions/checkout@v3
|
||
- with:
|
||
- fetch-depth: 0
|
||
-
|
||
- - name: Set up QEMU
|
||
- uses: docker/setup-qemu-action@v2
|
||
-
|
||
- - name: Set up Docker BuildX
|
||
- uses: docker/setup-buildx-action@v2
|
||
- with:
|
||
- config-inline: |
|
||
- [registry."git.send.nrw"]
|
||
- http = true
|
||
- insecure = true
|
||
-
|
||
- - name: Login to Registry
|
||
- uses: docker/login-action@v2
|
||
- with:
|
||
- registry: git.send.nrw
|
||
- username: ${{ secrets.DOCKER_USERNAME }}
|
||
- password: ${{ secrets.DOCKER_PASSWORD }}
|
||
-
|
||
- - name: Get Meta
|
||
- id: meta
|
||
- run: |
|
||
- echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
||
-
|
||
- - name: Build and push NeuroForge
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./platform/neuroforge
|
||
- file: ./platform/neuroforge/Dockerfile
|
||
- target: server
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-neuroforge:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-neuroforge:${{ env.DOCKER_LATEST }}
|
||
-
|
||
- - name: Build and push NeuroForge Worker
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./platform/neuroforge
|
||
- file: ./platform/neuroforge/Dockerfile
|
||
- target: worker
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-neuroforge-worker:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-neuroforge-worker:${{ env.DOCKER_LATEST }}
|
||
-
|
||
- - name: Build and push Agent
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./services/agent
|
||
- file: ./services/agent/Dockerfile
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-agent:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-agent:${{ env.DOCKER_LATEST }}
|
||
-
|
||
- - name: Build and push Agent Data Init
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./services/agent
|
||
- file: ./services/agent/Dockerfile
|
||
- target: data-init
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-agent-data-init:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-agent-data-init:${{ env.DOCKER_LATEST }}
|
||
-
|
||
- - name: Build and push Knowledgebase
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./services/knowledge
|
||
- file: ./services/knowledge/Dockerfile
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-knowledge:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-knowledge:${{ env.DOCKER_LATEST }}
|
||
-
|
||
- - name: Build and push Control Center
|
||
- uses: docker/build-push-action@v4
|
||
- with:
|
||
- context: ./services/control
|
||
- file: ./services/control/Dockerfile
|
||
- platforms: |
|
||
- linux/amd64
|
||
- push: true
|
||
- tags: |
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-control:${{ steps.meta.outputs.REPO_VERSION }}
|
||
- git.send.nrw/${{ env.DOCKER_ORG }}/glpi-neuroforge-mega-control:${{ env.DOCKER_LATEST }}
|
||
diff --git ab/.gitea/workflows/release.yml b/.gitea/workflows/release.yml
|
||
new file mode 100644
|
||
index 0000000..d63343f
|
||
--- /dev/null
|
||
+++ b/.gitea/workflows/release.yml
|
||
@@ -0,0 +1,42 @@
|
||
+name: release
|
||
+
|
||
+on:
|
||
+ push:
|
||
+ tags:
|
||
+ - 'v*'
|
||
+
|
||
+jobs:
|
||
+ release-images:
|
||
+ runs-on: ubuntu-latest
|
||
+ env:
|
||
+ REGISTRY: git.send.nrw/sendnrw
|
||
+ RUNNER_TOOL_CACHE: /toolcache
|
||
+ steps:
|
||
+ - uses: actions/checkout@v4
|
||
+ with:
|
||
+ fetch-depth: 0
|
||
+ - uses: docker/setup-qemu-action@v3
|
||
+ - uses: docker/setup-buildx-action@v3
|
||
+ with:
|
||
+ config-inline: |
|
||
+ [registry."git.send.nrw"]
|
||
+ http = true
|
||
+ insecure = true
|
||
+ - uses: docker/login-action@v3
|
||
+ with:
|
||
+ registry: git.send.nrw
|
||
+ username: ${{ secrets.DOCKER_USERNAME }}
|
||
+ password: ${{ secrets.DOCKER_PASSWORD }}
|
||
+ - name: Resolve immutable release version
|
||
+ id: meta
|
||
+ shell: bash
|
||
+ run: |
|
||
+ set -euo pipefail
|
||
+ VERSION="$(git describe --tags --exact-match HEAD | sed 's/^v//')"
|
||
+ test -n "$VERSION"
|
||
+ test "$(cat VERSION)" = "$VERSION"
|
||
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||
+ - name: Build and push all release images
|
||
+ env:
|
||
+ IMAGE_TAG: ${{ steps.meta.outputs.version }}
|
||
+ run: docker buildx bake --push
|
||
diff --git ab/BUILD-AND-RELEASE.md b/BUILD-AND-RELEASE.md
|
||
new file mode 100644
|
||
index 0000000..325daa8
|
||
--- /dev/null
|
||
+++ b/BUILD-AND-RELEASE.md
|
||
@@ -0,0 +1,43 @@
|
||
+# Build and Release
|
||
+
|
||
+## Repository image set
|
||
+
|
||
+The repository publishes six immutable project images:
|
||
+
|
||
+- `glpi-neuroforge-mega-neuroforge`
|
||
+- `glpi-neuroforge-mega-neuroforge-worker`
|
||
+- `glpi-neuroforge-mega-agent`
|
||
+- `glpi-neuroforge-mega-agent-data-init`
|
||
+- `glpi-neuroforge-mega-knowledge`
|
||
+- `glpi-neuroforge-mega-control`
|
||
+
|
||
+The default registry is `git.send.nrw/sendnrw`.
|
||
+
|
||
+## Local build
|
||
+
|
||
+From repository root:
|
||
+
|
||
+```bash
|
||
+docker buildx bake
|
||
+```
|
||
+
|
||
+Override registry/tag when required:
|
||
+
|
||
+```bash
|
||
+REGISTRY=registry.example/org IMAGE_TAG=1.6.2 docker buildx bake
|
||
+```
|
||
+
|
||
+## CI
|
||
+
|
||
+`.gitea/workflows/ci.yml` runs `go test ./...` and `go vet ./...` for NeuroForge, Agent, Knowledge and Control and then performs a Buildx Bake build of the full image set.
|
||
+
|
||
+## Release
|
||
+
|
||
+Set `VERSION` and all compose/deployment tags to the desired immutable version, then push a matching tag:
|
||
+
|
||
+```bash
|
||
+git tag v1.6.2
|
||
+git push origin v1.6.2
|
||
+```
|
||
+
|
||
+`.gitea/workflows/release.yml` verifies that the git tag matches `VERSION` and pushes all six images with tag `1.6.2`. Project production compose files do not require `latest`.
|
||
diff --git ab/DISTRIBUTED-DEPLOYMENT-BUNDLE.md b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md
|
||
new file mode 100644
|
||
index 0000000..a031908
|
||
--- /dev/null
|
||
+++ b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md
|
||
@@ -0,0 +1,5 @@
|
||
+# v1.6.2 Distributed Deployment Bundle
|
||
+
|
||
+This is the unchanged v1.6.2 application release plus ready-to-edit deployment kits under `deployments/`. No application code or persisted data format was changed.
|
||
+
|
||
+See `deployments/README.md`.
|
||
diff --git ab/MIGRATION-FROM-MEGA.md b/MIGRATION-FROM-MEGA.md
|
||
new file mode 100644
|
||
index 0000000..30740ad
|
||
--- /dev/null
|
||
+++ b/MIGRATION-FROM-MEGA.md
|
||
@@ -0,0 +1,33 @@
|
||
+# Migration: Mega v1.6.1 -> Agent + Knowledge Core
|
||
+
|
||
+Der Core-Modus verändert das Datenformat der Knowledge-JSONs nicht.
|
||
+
|
||
+## Variante A: Knowledge-Verzeichnis weiterverwenden
|
||
+
|
||
+Setze in `deployments/agent/.env` und `deployments/knowledge/.env` denselben `CORE_DATA_ROOT`, sodass beide auf denselben Hostpfad zeigen.
|
||
+
|
||
+## Variante B: Agent-Index übernehmen
|
||
+
|
||
+Der Agent speichert seinen persistenten lokalen Knowledge-Index unter `/app/data`. Der Core-Compose bind-mountet dafür `runtime/agent-data`.
|
||
+Um einen bestehenden Named Volume zu übernehmen, zuerst dessen Namen ermitteln und dann offline kopieren. Niemals das laufende Volume gleichzeitig von zwei Agent-Instanzen beschreiben lassen.
|
||
+
|
||
+## Funktionale Änderungen gegenüber Mega
|
||
+
|
||
+Nicht verfügbar im Core-Modus:
|
||
+
|
||
+- NeuroForge Memories/Synapses/Graph
|
||
+- autonome Research Goals
|
||
+- Staging-Synthese durch NeuroForge
|
||
+- CPU/GPU Subagent-Orchestrator
|
||
+- Control Center
|
||
+
|
||
+Weiter verfügbar:
|
||
+
|
||
+- GLPI Polling/OAuth2
|
||
+- Agent WebUI und Diagnose
|
||
+- lokale Knowledge-Indizierung und Embeddings
|
||
+- Hybrid Retrieval/RAG
|
||
+- Kategorien/Keywords/Source Policy
|
||
+- lokale Human-in-the-loop Lernbeispiele
|
||
+- optional GLPI-KB-Synchronisation
|
||
+- Knowledge Editor/Search
|
||
diff --git a/README.md b/README.md
|
||
index 8145919..1869f73 100644
|
||
--- a/README.md
|
||
+++ b/README.md
|
||
@@ -252,3 +252,15 @@ NEUROFORGE_GOAL_LEARNING_ENABLED=true
|
||
```
|
||
|
||
Damit bleibt rohes Chat-/Assistant-Lernen weiterhin deaktiviert.
|
||
+
|
||
+## v1.6.2: vollständige Deployment-Rollen und Ollama Bearer Auth
|
||
+
|
||
+Das Repository enthält neben dem vollständigen Mega-Compose eigenständige Kits unter `deployments/` für `master`, `cpu-subagent`, `gpu-subagent`, `agent`, `knowledge`, `ollama` und `combined`. Damit können der produktive GLPI-Agent/Knowledge-Core und die NeuroForge-Orchestrierung getrennt oder gemeinsam betrieben werden.
|
||
+
|
||
+Für Ollama-kompatible Endpunkte hinter einem Bearer-geschützten Gateway kann optional gesetzt werden:
|
||
+
|
||
+```env
|
||
+OLLAMA_API_KEY=
|
||
+```
|
||
+
|
||
+Agent, Knowledge, NeuroForge und modellfähige Subagents senden den Wert dann als `Authorization: Bearer ...`. NeuroForge kennt zusätzlich `NEUROFORGE_OLLAMA_API_KEY` und `NEUROFORGE_WORKER_OLLAMA_API_KEY` als rollenbezogene Overrides. Details: `docs/OLLAMA-BEARER-AUTH.md`.
|
||
diff --git ab/RELEASE-NOTES-v1.6.2.md b/RELEASE-NOTES-v1.6.2.md
|
||
new file mode 100644
|
||
index 0000000..e7e060a
|
||
--- /dev/null
|
||
+++ b/RELEASE-NOTES-v1.6.2.md
|
||
@@ -0,0 +1,59 @@
|
||
+# GLPI NeuroForge Mega v1.6.2
|
||
+
|
||
+v1.6.2 is the consolidated production release that reunifies the full Mega repository after the stripped-core packaging experiment and adds authenticated Ollama-compatible endpoints.
|
||
+
|
||
+## Complete deployment set
|
||
+
|
||
+The repository now ships all supported operating modes together:
|
||
+
|
||
+- full single-host Mega stack,
|
||
+- distributed Master,
|
||
+- remote CPU subagent,
|
||
+- remote GPU subagent with Ollama,
|
||
+- standalone GLPI Agent,
|
||
+- standalone Knowledge service,
|
||
+- standalone Ollama,
|
||
+- combined Agent + Knowledge + Ollama core,
|
||
+- Prometheus/Grafana example monitoring on the Master.
|
||
+
|
||
+The v1.6.1 recovery/OOM hardening remains included, including startup pre-compaction of legacy relink payloads, bounded durable-job payload retention, lower graph-backfill pressure, visible startup phases, stricter corrupted-state handling and HNSW checkpoint memory reductions.
|
||
+
|
||
+## Ollama Bearer authentication
|
||
+
|
||
+A shared optional variable is now supported:
|
||
+
|
||
+```env
|
||
+OLLAMA_API_KEY=
|
||
+```
|
||
+
|
||
+When non-empty, clients send:
|
||
+
|
||
+```http
|
||
+Authorization: Bearer <OLLAMA_API_KEY>
|
||
+```
|
||
+
|
||
+for Ollama-compatible `/api/tags`, `/api/chat` and `/api/embed` requests.
|
||
+
|
||
+Supported clients:
|
||
+
|
||
+- GLPI Agent, including every node in its Ollama pool,
|
||
+- Knowledge AI fallback,
|
||
+- NeuroForge provider and readiness/provider-health checks,
|
||
+- NeuroForge GPU/model subagents.
|
||
+
|
||
+NeuroForge also supports role-specific overrides:
|
||
+
|
||
+```env
|
||
+NEUROFORGE_OLLAMA_API_KEY=
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
+```
|
||
+
|
||
+The role-specific value takes precedence over `OLLAMA_API_KEY` for the corresponding NeuroForge component.
|
||
+
|
||
+The key is stored as a NeuroForge secret when configured through `NEUROFORGE_OLLAMA_API_KEY`; admin APIs expose only configured/masked status unless secret reveal is explicitly enabled.
|
||
+
|
||
+Native Ollama does not itself become authenticated merely by setting this variable. The option is intended for an authenticated Ollama-compatible gateway/reverse proxy or another Ollama-compatible endpoint that validates Bearer tokens. Direct native Ollama deployments should normally leave it empty.
|
||
+
|
||
+## CI/release consolidation
|
||
+
|
||
+Gitea workflows now live only at repository root and cover all four Go modules. The release workflow builds and pushes the six immutable project images. `docker-bake.hcl` contains the same complete image set. Production compose files remain pinned through `IMAGE_TAG`; no `latest` project image is required.
|
||
diff --git a/VERSION b/VERSION
|
||
index 9c6d629..fdd3be6 100644
|
||
--- a/VERSION
|
||
+++ b/VERSION
|
||
@@ -1 +1 @@
|
||
-1.6.1
|
||
+1.6.2
|
||
diff --git ab/deployments/README.md b/deployments/README.md
|
||
new file mode 100644
|
||
index 0000000..6ee8c5a
|
||
--- /dev/null
|
||
+++ b/deployments/README.md
|
||
@@ -0,0 +1,16 @@
|
||
+# Distributed Deployment Kits (v1.6.2)
|
||
+
|
||
+This directory contains three independent deployment kits:
|
||
+
|
||
+- `master/` - authoritative NeuroForge Master plus Agent, Knowledge, Control, optional SearXNG and optional Prometheus/Grafana.
|
||
+- `cpu-subagent/` - CPU worker for `vector.relink` / graph convergence.
|
||
+- `gpu-subagent/` - GPU worker plus local Ollama for `model.chat` and `model.embed`.
|
||
+
|
||
+The generated `NEUROFORGE_WORKER_TOKEN` is identical in all three `.env` files. Replace the RFC 5737 example IP addresses (`192.0.2.x`) with real reachable addresses before starting.
|
||
+
|
||
+Recommended order:
|
||
+1. GPU subagent: `docker compose --profile monitoring up -d`
|
||
+2. CPU subagent: `docker compose --profile monitoring up -d`
|
||
+3. Master: fill GLPI credentials, then `docker compose --profile research --profile monitoring up -d`
|
||
+
|
||
+Only the Master holds authoritative NeuroForge state. Workers are disposable execution nodes.
|
||
diff --git ab/deployments/agent/.env b/deployments/agent/.env
|
||
new file mode 100644
|
||
index 0000000..62c86d2
|
||
--- /dev/null
|
||
+++ b/deployments/agent/.env
|
||
@@ -0,0 +1,164 @@
|
||
+###############################################################################
|
||
+# GLPI AI Agent - standalone / stripped core
|
||
+###############################################################################
|
||
+IMAGE_TAG=1.6.2
|
||
+AGENT_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:1.6.2
|
||
+AGENT_DATA_INIT_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:1.6.2
|
||
+CORE_NETWORK=glpi-ai-core
|
||
+CORE_DATA_ROOT=../../runtime
|
||
+AGENT_BIND_IP=127.0.0.1
|
||
+AGENT_HOST_PORT=9980
|
||
+
|
||
+# Pflicht: GLPI High-Level API / OAuth2 Service Account
|
||
+GLPI_URL=https://CHANGE_ME_GLPI_HOST
|
||
+GLPI_API_VERSION=v2.3
|
||
+GLPI_CLIENT_ID=CHANGE_ME_GLPI_CLIENT_ID
|
||
+GLPI_CLIENT_SECRET=CHANGE_ME_GLPI_CLIENT_SECRET
|
||
+GLPI_USERNAME=CHANGE_ME_GLPI_USERNAME
|
||
+GLPI_PASSWORD=CHANGE_ME_GLPI_PASSWORD
|
||
+GLPI_AGENT_USER_ID=0
|
||
+GLPI_ALLOW_INSECURE_HTTP=false
|
||
+
|
||
+# Sichere Einführung
|
||
+DRY_RUN=true
|
||
+AUTO_CATEGORY=true
|
||
+AUTO_REPLY=false
|
||
+AUTO_PRIORITY=false
|
||
+AUTO_ESCALATION=false
|
||
+LOG_LEVEL=info
|
||
+
|
||
+# Agent WebUI
|
||
+WEB_USERNAME=admin
|
||
+WEB_PASSWORD=CHANGE_ME_LONG_AGENT_WEB_PASSWORD
|
||
+WEB_ALLOW_ANONYMOUS=false
|
||
+AI_CONTENT_LABEL_ENABLED=true
|
||
+WEBHOOK_SECRET=
|
||
+
|
||
+# Polling
|
||
+GLPI_ALLOWED_STATUS_IDS=1
|
||
+GLPI_POLL_INTERVAL=30s
|
||
+GLPI_POLL_LIMIT=50
|
||
+GLPI_TICKET_FILTER=status.id==1
|
||
+GLPI_TIMEOUT=20s
|
||
+
|
||
+# Ollama: entweder separater Ollama-Compose im gemeinsamen CORE_NETWORK
|
||
+# oder eine vom Agent-Container erreichbare externe URL eintragen.
|
||
+OLLAMA_URL=http://ollama:11434
|
||
+OLLAMA_URLS=http://ollama:11434
|
||
+OLLAMA_NODE_NAMES=ollama-01
|
||
+OLLAMA_NODE_WEIGHTS=1
|
||
+OLLAMA_MODEL=gemma4
|
||
+OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+OLLAMA_ROUTING_MODE=least_inflight
|
||
+OLLAMA_NODE_MAX_INFLIGHT=1
|
||
+OLLAMA_NODE_HEALTH_INTERVAL=15s
|
||
+OLLAMA_NODE_FAILURE_COOLDOWN=30s
|
||
+OLLAMA_NODE_REQUEST_TIMEOUT=10m
|
||
+OLLAMA_FAILOVER_ENABLED=true
|
||
+OLLAMA_FAILOVER_ATTEMPTS=0
|
||
+OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true
|
||
+OLLAMA_REQUIRE_EMBEDDING_MODEL=true
|
||
+OLLAMA_TIMEOUT=10m
|
||
+OLLAMA_MAX_CONCURRENT=1
|
||
+OLLAMA_NUM_PREDICT=768
|
||
+OLLAMA_JSON_RETRIES=1
|
||
+OLLAMA_KEEP_ALIVE=10m
|
||
+OLLAMA_THINK=false
|
||
+
|
||
+# Lokale Knowledge/RAG-Funktion - KEIN NeuroForge erforderlich
|
||
+RAG_ENABLED=true
|
||
+KNOWLEDGE_VECTOR_BACKEND=local
|
||
+KNOWLEDGE_EMBEDDING_PROFILE=auto
|
||
+KNOWLEDGE_INDEX_MODE=incremental
|
||
+KNOWLEDGE_EMBED_BATCH_SIZE=64
|
||
+KNOWLEDGE_INDEX_SCAN_INTERVAL=5m
|
||
+KNOWLEDGE_CATEGORY_MODE=unscoped
|
||
+KNOWLEDGE_CATEGORY_MAP_FILE=/app/data/knowledge-category-map.json
|
||
+KNOWLEDGE_IGNORE_GLOBS=
|
||
+KNOWLEDGE_RETRIEVAL_FLOOR=0.30
|
||
+KNOWLEDGE_CANDIDATE_MAX_GAP=0.20
|
||
+KNOWLEDGE_TOP_K=6
|
||
+KNOWLEDGE_AUDIT_TOP_K=10
|
||
+KNOWLEDGE_MIN_SCORE=0.70
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_RETRIEVAL=0.45
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_AI=0.35
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_CATEGORY=0.20
|
||
+KNOWLEDGE_WEIGHT_SEMANTIC=0.45
|
||
+KNOWLEDGE_WEIGHT_TITLE=0.20
|
||
+KNOWLEDGE_WEIGHT_LEXICAL=0.20
|
||
+KNOWLEDGE_WEIGHT_KEYWORDS=0.075
|
||
+KNOWLEDGE_WEIGHT_CATEGORY=0.075
|
||
+KNOWLEDGE_CHUNK_WORDS=160
|
||
+KNOWLEDGE_CHUNK_OVERLAP_WORDS=30
|
||
+KNOWLEDGE_MAX_CHUNKS_PER_DOC=24
|
||
+KNOWLEDGE_MAX_QUERY_CHUNKS=64
|
||
+CATEGORY_PROMPT_LIMIT=80
|
||
+KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+KNOWLEDGE_CATEGORY_SOURCES=internal-category
|
||
+KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+KNOWLEDGE_WEB_EDIT_ENABLED=false
|
||
+
|
||
+# GLPI-eigene KB optional zusätzlich synchronisieren
|
||
+GLPI_KB_ENABLED=false
|
||
+GLPI_KB_PATH=auto
|
||
+GLPI_KB_FILTER=
|
||
+GLPI_KB_LIMIT=500
|
||
+GLPI_KB_SYNC_INTERVAL=10m
|
||
+GLPI_KB_SOURCE=glpi-kb
|
||
+GLPI_KB_AUTO_REPLY=false
|
||
+GLPI_KB_AUTO_REPLY_CATEGORY_IDS=
|
||
+GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=false
|
||
+GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS=
|
||
+
|
||
+# Lokales Human-in-the-loop Category Learning bleibt möglich
|
||
+LEARNING_ENABLED=true
|
||
+LEARNING_MAX_EXAMPLES=500
|
||
+LEARNING_EXAMPLES_PER_CATEGORY=5
|
||
+# NeuroForge Outcome-Learning ist im stripped Core bewusst deaktiviert.
|
||
+OUTCOME_LEARNING_ENABLED=false
|
||
+OUTCOME_RETRIEVAL_ENABLED=false
|
||
+OUTCOME_LEARNING_FAIL_OPEN=true
|
||
+OUTCOME_LEARNING_MAX_OUTCOMES=2000
|
||
+OUTCOME_RETRIEVAL_SEARCH_K=6
|
||
+OUTCOME_RETRIEVAL_MIN_SIMILARITY=0.58
|
||
+OUTCOME_RETRIEVAL_FAIL_OPEN=true
|
||
+
|
||
+# Kommunikation / Policy
|
||
+COMMUNICATION_LANGUAGE=de-DE
|
||
+COMMUNICATION_STYLE=formal
|
||
+COMMUNICATION_SALUTATION=Guten Tag,
|
||
+COMMUNICATION_CLOSING=Mit freundlichen Grüßen
|
||
+COMMUNICATION_SIGNATURE=IT-Service
|
||
+CATEGORY_CONFIDENCE=0.90
|
||
+REPLY_CONFIDENCE=0.97
|
||
+
|
||
+# Optionale Kontextfunktionen im Core zunächst deaktiviert
|
||
+CONTEXT_ENABLED=false
|
||
+CONTEXT_TIMEOUT=12s
|
||
+CONTEXT_RELEVANCE_MIN_SCORE=0.20
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS=true
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT=true
|
||
+CONTEXT_STATUS_REPLY_ENABLED=false
|
||
+CHANGE_CALENDAR_ENABLED=false
|
||
+MAJOR_INCIDENTS_ENABLED=false
|
||
+USER_DEVICE_CONTEXT_ENABLED=false
|
||
+UPTIME_KUMA_ENABLED=false
|
||
+
|
||
+# Optionale Priorisierung / Eskalation im stripped Standard aus
|
||
+PRIORITY_ENABLED=false
|
||
+PRIORITY_CONFIDENCE=0.88
|
||
+PRIORITY_ANALYSIS_TIMEOUT=45s
|
||
+PRIORITY_MAX_INCREASE=1
|
||
+ESCALATION_ENABLED=false
|
||
+ESCALATION_SCAN_INTERVAL=15m
|
||
+ESCALATION_MIN_AGE=4h
|
||
+ESCALATION_MIN_INACTIVITY=2h
|
||
+ESCALATION_ANALYSIS_TIMEOUT=45s
|
||
+ESCALATION_CONFIDENCE=0.88
|
||
+ESCALATION_MAX_LEVEL=3
|
||
+ESCALATION_ALLOWED_ACTIONS=none
|
||
+
|
||
+QUEUE_SIZE=256
|
||
+WORKERS=2
|
||
diff --git ab/deployments/agent/docker-compose.build.yml b/deployments/agent/docker-compose.build.yml
|
||
new file mode 100644
|
||
index 0000000..37248cf
|
||
--- /dev/null
|
||
+++ b/deployments/agent/docker-compose.build.yml
|
||
@@ -0,0 +1,10 @@
|
||
+services:
|
||
+ agent-data-init:
|
||
+ image: glpi-ai-agent-data-init-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/agent
|
||
+ target: data-init
|
||
+ agent:
|
||
+ image: glpi-ai-agent-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/agent
|
||
diff --git ab/deployments/agent/docker-compose.yml b/deployments/agent/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..856b750
|
||
--- /dev/null
|
||
+++ b/deployments/agent/docker-compose.yml
|
||
@@ -0,0 +1,63 @@
|
||
+name: glpi-ai-agent-core
|
||
+services:
|
||
+ agent-data-init:
|
||
+ image: ${AGENT_DATA_INIT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: "no"
|
||
+ user: "0:0"
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ cap_add:
|
||
+ - CHOWN
|
||
+ - FOWNER
|
||
+
|
||
+ agent:
|
||
+ image: ${AGENT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: unless-stopped
|
||
+ env_file:
|
||
+ - .env
|
||
+ environment:
|
||
+ HTTP_ADDR: :8080
|
||
+ DATA_DIR: /app/data
|
||
+ KNOWLEDGE_DIR: /app/knowledge
|
||
+ KNOWLEDGE_VECTOR_BACKEND: local
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ OUTCOME_LEARNING_ENABLED: "false"
|
||
+ OUTCOME_RETRIEVAL_ENABLED: "false"
|
||
+ BRAIN_ACTIVITY_URL: ""
|
||
+ BRAIN_ACTIVITY_API_KEY: ""
|
||
+ NEUROFORGE_URL: ""
|
||
+ NEUROFORGE_API_KEY: ""
|
||
+ CONTROL_READ_TOKEN: ""
|
||
+ ports:
|
||
+ - ${AGENT_BIND_IP:-127.0.0.1}:${AGENT_HOST_PORT:-9980}:8080
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/app/knowledge:ro
|
||
+ depends_on:
|
||
+ agent-data-init:
|
||
+ condition: service_completed_successfully
|
||
+ networks:
|
||
+ - core
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=64m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ healthcheck:
|
||
+ test: ["CMD", "/app/glpi-ai-agent", "healthcheck"]
|
||
+ interval: 15s
|
||
+ timeout: 3s
|
||
+ retries: 8
|
||
+ start_period: 10s
|
||
+ stop_grace_period: 20s
|
||
+
|
||
+networks:
|
||
+ core:
|
||
+ external: true
|
||
+ name: ${CORE_NETWORK:-glpi-ai-core}
|
||
diff --git ab/deployments/combined/.env b/deployments/combined/.env
|
||
new file mode 100644
|
||
index 0000000..5f77328
|
||
--- /dev/null
|
||
+++ b/deployments/combined/.env
|
||
@@ -0,0 +1,183 @@
|
||
+###############################################################################
|
||
+# GLPI AI Agent - standalone / stripped core
|
||
+###############################################################################
|
||
+IMAGE_TAG=1.6.2
|
||
+AGENT_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:1.6.2
|
||
+AGENT_DATA_INIT_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:1.6.2
|
||
+CORE_NETWORK=glpi-ai-core
|
||
+CORE_DATA_ROOT=../../runtime
|
||
+AGENT_BIND_IP=127.0.0.1
|
||
+AGENT_HOST_PORT=9980
|
||
+
|
||
+# Pflicht: GLPI High-Level API / OAuth2 Service Account
|
||
+GLPI_URL=https://CHANGE_ME_GLPI_HOST
|
||
+GLPI_API_VERSION=v2.3
|
||
+GLPI_CLIENT_ID=CHANGE_ME_GLPI_CLIENT_ID
|
||
+GLPI_CLIENT_SECRET=CHANGE_ME_GLPI_CLIENT_SECRET
|
||
+GLPI_USERNAME=CHANGE_ME_GLPI_USERNAME
|
||
+GLPI_PASSWORD=CHANGE_ME_GLPI_PASSWORD
|
||
+GLPI_AGENT_USER_ID=28
|
||
+GLPI_ALLOW_INSECURE_HTTP=false
|
||
+
|
||
+# Sichere Einführung
|
||
+DRY_RUN=true
|
||
+AUTO_CATEGORY=true
|
||
+AUTO_REPLY=false
|
||
+AUTO_PRIORITY=false
|
||
+AUTO_ESCALATION=false
|
||
+LOG_LEVEL=info
|
||
+
|
||
+# Agent WebUI
|
||
+WEB_USERNAME=admin
|
||
+WEB_PASSWORD=CHANGE_ME_LONG_AGENT_WEB_PASSWORD
|
||
+WEB_ALLOW_ANONYMOUS=false
|
||
+AI_CONTENT_LABEL_ENABLED=true
|
||
+WEBHOOK_SECRET=
|
||
+
|
||
+# Polling
|
||
+GLPI_ALLOWED_STATUS_IDS=1
|
||
+GLPI_POLL_INTERVAL=30s
|
||
+GLPI_POLL_LIMIT=50
|
||
+GLPI_TICKET_FILTER=status.id==1
|
||
+GLPI_TIMEOUT=20s
|
||
+
|
||
+# Ollama: entweder separater Ollama-Compose im gemeinsamen CORE_NETWORK
|
||
+# oder eine vom Agent-Container erreichbare externe URL eintragen.
|
||
+OLLAMA_URL=http://10.2.10.40:8080
|
||
+OLLAMA_URLS=http://10.2.10.40:8080
|
||
+OLLAMA_NODE_NAMES=ollama-gateway
|
||
+OLLAMA_NODE_WEIGHTS=1
|
||
+OLLAMA_MODEL=gemma4
|
||
+OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+OLLAMA_ROUTING_MODE=least_inflight
|
||
+OLLAMA_NODE_MAX_INFLIGHT=1
|
||
+OLLAMA_NODE_HEALTH_INTERVAL=15s
|
||
+OLLAMA_NODE_FAILURE_COOLDOWN=30s
|
||
+OLLAMA_NODE_REQUEST_TIMEOUT=10m
|
||
+OLLAMA_FAILOVER_ENABLED=true
|
||
+OLLAMA_FAILOVER_ATTEMPTS=0
|
||
+OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true
|
||
+OLLAMA_REQUIRE_EMBEDDING_MODEL=true
|
||
+OLLAMA_TIMEOUT=10m
|
||
+OLLAMA_MAX_CONCURRENT=1
|
||
+OLLAMA_NUM_PREDICT=768
|
||
+OLLAMA_JSON_RETRIES=1
|
||
+OLLAMA_KEEP_ALIVE=10m
|
||
+OLLAMA_THINK=false
|
||
+
|
||
+# Lokale Knowledge/RAG-Funktion - KEIN NeuroForge erforderlich
|
||
+RAG_ENABLED=true
|
||
+KNOWLEDGE_VECTOR_BACKEND=local
|
||
+KNOWLEDGE_EMBEDDING_PROFILE=auto
|
||
+KNOWLEDGE_INDEX_MODE=incremental
|
||
+KNOWLEDGE_EMBED_BATCH_SIZE=64
|
||
+KNOWLEDGE_INDEX_SCAN_INTERVAL=5m
|
||
+KNOWLEDGE_CATEGORY_MODE=unscoped
|
||
+KNOWLEDGE_CATEGORY_MAP_FILE=/app/data/knowledge-category-map.json
|
||
+KNOWLEDGE_IGNORE_GLOBS=
|
||
+KNOWLEDGE_RETRIEVAL_FLOOR=0.30
|
||
+KNOWLEDGE_CANDIDATE_MAX_GAP=0.20
|
||
+KNOWLEDGE_TOP_K=6
|
||
+KNOWLEDGE_AUDIT_TOP_K=10
|
||
+KNOWLEDGE_MIN_SCORE=0.70
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_RETRIEVAL=0.45
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_AI=0.35
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_CATEGORY=0.20
|
||
+KNOWLEDGE_WEIGHT_SEMANTIC=0.45
|
||
+KNOWLEDGE_WEIGHT_TITLE=0.20
|
||
+KNOWLEDGE_WEIGHT_LEXICAL=0.20
|
||
+KNOWLEDGE_WEIGHT_KEYWORDS=0.075
|
||
+KNOWLEDGE_WEIGHT_CATEGORY=0.075
|
||
+KNOWLEDGE_CHUNK_WORDS=160
|
||
+KNOWLEDGE_CHUNK_OVERLAP_WORDS=30
|
||
+KNOWLEDGE_MAX_CHUNKS_PER_DOC=24
|
||
+KNOWLEDGE_MAX_QUERY_CHUNKS=64
|
||
+CATEGORY_PROMPT_LIMIT=80
|
||
+KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+KNOWLEDGE_CATEGORY_SOURCES=internal-category
|
||
+KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+KNOWLEDGE_WEB_EDIT_ENABLED=false
|
||
+
|
||
+# GLPI-eigene KB optional zusätzlich synchronisieren
|
||
+GLPI_KB_ENABLED=false
|
||
+GLPI_KB_PATH=auto
|
||
+GLPI_KB_FILTER=
|
||
+GLPI_KB_LIMIT=500
|
||
+GLPI_KB_SYNC_INTERVAL=10m
|
||
+GLPI_KB_SOURCE=glpi-kb
|
||
+GLPI_KB_AUTO_REPLY=false
|
||
+GLPI_KB_AUTO_REPLY_CATEGORY_IDS=
|
||
+GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=false
|
||
+GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS=
|
||
+
|
||
+# Lokales Human-in-the-loop Category Learning bleibt möglich
|
||
+LEARNING_ENABLED=true
|
||
+LEARNING_MAX_EXAMPLES=500
|
||
+LEARNING_EXAMPLES_PER_CATEGORY=5
|
||
+# NeuroForge Outcome-Learning ist im stripped Core bewusst deaktiviert.
|
||
+OUTCOME_LEARNING_ENABLED=false
|
||
+OUTCOME_RETRIEVAL_ENABLED=false
|
||
+OUTCOME_LEARNING_FAIL_OPEN=true
|
||
+OUTCOME_LEARNING_MAX_OUTCOMES=2000
|
||
+OUTCOME_RETRIEVAL_SEARCH_K=6
|
||
+OUTCOME_RETRIEVAL_MIN_SIMILARITY=0.58
|
||
+OUTCOME_RETRIEVAL_FAIL_OPEN=true
|
||
+
|
||
+# Kommunikation / Policy
|
||
+COMMUNICATION_LANGUAGE=de-DE
|
||
+COMMUNICATION_STYLE=formal
|
||
+COMMUNICATION_SALUTATION=Guten Tag,
|
||
+COMMUNICATION_CLOSING=Mit freundlichen Grüßen
|
||
+COMMUNICATION_SIGNATURE=IT-Service
|
||
+CATEGORY_CONFIDENCE=0.90
|
||
+REPLY_CONFIDENCE=0.97
|
||
+
|
||
+# Optionale Kontextfunktionen im Core zunächst deaktiviert
|
||
+CONTEXT_ENABLED=false
|
||
+CONTEXT_TIMEOUT=12s
|
||
+CONTEXT_RELEVANCE_MIN_SCORE=0.20
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS=true
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT=true
|
||
+CONTEXT_STATUS_REPLY_ENABLED=false
|
||
+CHANGE_CALENDAR_ENABLED=false
|
||
+MAJOR_INCIDENTS_ENABLED=false
|
||
+USER_DEVICE_CONTEXT_ENABLED=false
|
||
+UPTIME_KUMA_ENABLED=false
|
||
+
|
||
+# Optionale Priorisierung / Eskalation im stripped Standard aus
|
||
+PRIORITY_ENABLED=false
|
||
+PRIORITY_CONFIDENCE=0.88
|
||
+PRIORITY_ANALYSIS_TIMEOUT=45s
|
||
+PRIORITY_MAX_INCREASE=1
|
||
+ESCALATION_ENABLED=false
|
||
+ESCALATION_SCAN_INTERVAL=15m
|
||
+ESCALATION_MIN_AGE=4h
|
||
+ESCALATION_MIN_INACTIVITY=2h
|
||
+ESCALATION_ANALYSIS_TIMEOUT=45s
|
||
+ESCALATION_CONFIDENCE=0.88
|
||
+ESCALATION_MAX_LEVEL=3
|
||
+ESCALATION_ALLOWED_ACTIONS=none
|
||
+
|
||
+QUEUE_SIZE=256
|
||
+WORKERS=2
|
||
+
|
||
+# Combined stack / Ollama / Knowledge UI
|
||
+OLLAMA_IMAGE=ollama/ollama:0.33.2
|
||
+OLLAMA_BIND_IP=127.0.0.1
|
||
+OLLAMA_HOST_PORT=11434
|
||
+KNOWLEDGE_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:1.6.2
|
||
+KNOWLEDGE_BIND_IP=127.0.0.1
|
||
+KNOWLEDGE_HOST_PORT=9981
|
||
+KB_APP_MODE=editor
|
||
+APP_TITLE=Knowledge Base Editor
|
||
+APP_SUBTITLE=GLPI AI Agent Core
|
||
+AUTO_RELOAD_INTERVAL=60s
|
||
+BASIC_AUTH_USER=admin
|
||
+BASIC_AUTH_PASSWORD=CHANGE_ME_LONG_KNOWLEDGE_PASSWORD
|
||
+AI_FALLBACK_ENABLED=false
|
||
+OLLAMA_BASE_URL=http://10.2.10.40:8080
|
||
+OLLAMA_STAGING_AUTO_REPLY=false
|
||
+OLLAMA_STAGING_MIN_SCORE=0.78
|
||
+KB_INTEGRATION_TOKEN=
|
||
diff --git ab/deployments/combined/docker-compose.build.yml b/deployments/combined/docker-compose.build.yml
|
||
new file mode 100644
|
||
index 0000000..2a2e3ac
|
||
--- /dev/null
|
||
+++ b/deployments/combined/docker-compose.build.yml
|
||
@@ -0,0 +1,14 @@
|
||
+services:
|
||
+ agent-data-init:
|
||
+ image: glpi-ai-agent-data-init-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/agent
|
||
+ target: data-init
|
||
+ agent:
|
||
+ image: glpi-ai-agent-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/agent
|
||
+ knowledge:
|
||
+ image: glpi-ai-knowledge-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/knowledge
|
||
diff --git ab/deployments/combined/docker-compose.yml b/deployments/combined/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..7f4b170
|
||
--- /dev/null
|
||
+++ b/deployments/combined/docker-compose.yml
|
||
@@ -0,0 +1,100 @@
|
||
+name: glpi-ai-core
|
||
+services:
|
||
+ ollama:
|
||
+ image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2}
|
||
+ restart: unless-stopped
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/ollama:/root/.ollama
|
||
+ ports:
|
||
+ - ${OLLAMA_BIND_IP:-127.0.0.1}:${OLLAMA_HOST_PORT:-11434}:11434
|
||
+ networks:
|
||
+ core:
|
||
+ aliases: [ollama]
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+
|
||
+ agent-data-init:
|
||
+ image: ${AGENT_DATA_INIT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: "no"
|
||
+ user: "0:0"
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop: [ALL]
|
||
+ cap_add: [CHOWN, FOWNER]
|
||
+
|
||
+ agent:
|
||
+ image: ${AGENT_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: unless-stopped
|
||
+ env_file: [.env]
|
||
+ environment:
|
||
+ HTTP_ADDR: :8080
|
||
+ DATA_DIR: /app/data
|
||
+ KNOWLEDGE_DIR: /app/knowledge
|
||
+ OLLAMA_URL: http://ollama:11434
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ OLLAMA_URLS: http://ollama:11434
|
||
+ KNOWLEDGE_VECTOR_BACKEND: local
|
||
+ OUTCOME_LEARNING_ENABLED: "false"
|
||
+ OUTCOME_RETRIEVAL_ENABLED: "false"
|
||
+ BRAIN_ACTIVITY_URL: ""
|
||
+ BRAIN_ACTIVITY_API_KEY: ""
|
||
+ NEUROFORGE_URL: ""
|
||
+ NEUROFORGE_API_KEY: ""
|
||
+ CONTROL_READ_TOKEN: ""
|
||
+ ports:
|
||
+ - ${AGENT_BIND_IP:-127.0.0.1}:${AGENT_HOST_PORT:-9980}:8080
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/agent-data:/app/data
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/app/knowledge:ro
|
||
+ depends_on:
|
||
+ agent-data-init:
|
||
+ condition: service_completed_successfully
|
||
+ ollama:
|
||
+ condition: service_started
|
||
+ networks: [core]
|
||
+ read_only: true
|
||
+ tmpfs: [/tmp:size=64m,mode=1777]
|
||
+ security_opt: [no-new-privileges:true]
|
||
+ cap_drop: [ALL]
|
||
+ healthcheck:
|
||
+ test: ["CMD", "/app/glpi-ai-agent", "healthcheck"]
|
||
+ interval: 15s
|
||
+ timeout: 3s
|
||
+ retries: 8
|
||
+ start_period: 10s
|
||
+ stop_grace_period: 20s
|
||
+
|
||
+ knowledge:
|
||
+ image: ${KNOWLEDGE_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: unless-stopped
|
||
+ env_file: [.env]
|
||
+ environment:
|
||
+ APP_MODE: ${KB_APP_MODE:-editor}
|
||
+ DATA_DIR: /data/knowledge
|
||
+ BACKUP_DIR: /data/backups
|
||
+ STAGING_DIR: /data/staging
|
||
+ LISTEN_ADDR: :8080
|
||
+ OLLAMA_BASE_URL: http://ollama:11434
|
||
+ BRAIN_ACTIVITY_URL: ""
|
||
+ BRAIN_ACTIVITY_API_KEY: ""
|
||
+ ports:
|
||
+ - ${KNOWLEDGE_BIND_IP:-127.0.0.1}:${KNOWLEDGE_HOST_PORT:-9981}:8080
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/data/knowledge:rw
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/staging:/data/staging:rw
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/backups:/data/backups:rw
|
||
+ depends_on:
|
||
+ ollama:
|
||
+ condition: service_started
|
||
+ networks: [core]
|
||
+ read_only: true
|
||
+ tmpfs: [/tmp:size=32m,mode=1777]
|
||
+ security_opt: [no-new-privileges:true]
|
||
+ cap_drop: [ALL]
|
||
+ stop_grace_period: 35s
|
||
+
|
||
+networks:
|
||
+ core:
|
||
+ name: ${CORE_NETWORK:-glpi-ai-core}
|
||
diff --git ab/deployments/cpu-subagent/.env b/deployments/cpu-subagent/.env
|
||
new file mode 100644
|
||
index 0000000..a7a93b5
|
||
--- /dev/null
|
||
+++ b/deployments/cpu-subagent/.env
|
||
@@ -0,0 +1,17 @@
|
||
+# NeuroForge v1.6.2 CPU Subagent
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+IMAGE_TAG=1.6.2
|
||
+
|
||
+# Reachable Master endpoint. Use TLS/reverse proxy outside a trusted LAN.
|
||
+NEUROFORGE_MASTER_URL=http://192.0.2.10:8090
|
||
+NEUROFORGE_WORKER_TOKEN=CHANGE_ME_SHARED_WORKER_TOKEN
|
||
+NEUROFORGE_CPU_WORKER_ID=cpu-node-01
|
||
+NEUROFORGE_CPU_WORKER_CAPABILITIES=cpu,vector.relink
|
||
+NEUROFORGE_CPU_WORKER_CONCURRENCY=4
|
||
+NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s
|
||
+
|
||
+# Optional Prometheus host telemetry (start profile monitoring).
|
||
+NODE_EXPORTER_IMAGE=prom/node-exporter:v1.12.1
|
||
+EXPORTER_BIND_ADDRESS=0.0.0.0
|
||
+NODE_EXPORTER_HOST_PORT=9100
|
||
diff --git ab/deployments/cpu-subagent/docker-compose.yml b/deployments/cpu-subagent/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..18c716a
|
||
--- /dev/null
|
||
+++ b/deployments/cpu-subagent/docker-compose.yml
|
||
@@ -0,0 +1,46 @@
|
||
+name: neuroforge-cpu-subagent
|
||
+services:
|
||
+ cpu-subagent:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set
|
||
+ immutable IMAGE_TAG}
|
||
+ command:
|
||
+ - -server
|
||
+ - ${NEUROFORGE_MASTER_URL:?Set reachable NeuroForge master URL}
|
||
+ - -id
|
||
+ - ${NEUROFORGE_CPU_WORKER_ID:?Set CPU worker ID}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ NEUROFORGE_WORKER_TOKEN: ${NEUROFORGE_WORKER_TOKEN:?Set worker token shared
|
||
+ with master}
|
||
+ NEUROFORGE_WORKER_RESOURCE_CLASS: cpu
|
||
+ NEUROFORGE_WORKER_CAPABILITIES: ${NEUROFORGE_CPU_WORKER_CAPABILITIES:-cpu,vector.relink}
|
||
+ NEUROFORGE_WORKER_MAX_CONCURRENCY: ${NEUROFORGE_CPU_WORKER_CONCURRENCY:-2}
|
||
+ NEUROFORGE_WORKER_HEARTBEAT_INTERVAL: ${NEUROFORGE_WORKER_HEARTBEAT_INTERVAL:-15s}
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=32m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ node-exporter:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${NODE_EXPORTER_IMAGE:-prom/node-exporter:v1.12.1}
|
||
+ restart: unless-stopped
|
||
+ command:
|
||
+ - --path.procfs=/host/proc
|
||
+ - --path.sysfs=/host/sys
|
||
+ - --path.rootfs=/rootfs
|
||
+ - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
|
||
+ ports:
|
||
+ - ${EXPORTER_BIND_ADDRESS:-0.0.0.0}:${NODE_EXPORTER_HOST_PORT:-9100}:9100
|
||
+ volumes:
|
||
+ - /proc:/host/proc:ro
|
||
+ - /sys:/host/sys:ro
|
||
+ - /:/rootfs:ro,rslave
|
||
+ read_only: true
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
diff --git ab/deployments/cpu-subagent/preflight.sh b/deployments/cpu-subagent/preflight.sh
|
||
new file mode 100755
|
||
index 0000000..435c650
|
||
--- /dev/null
|
||
+++ b/deployments/cpu-subagent/preflight.sh
|
||
@@ -0,0 +1,11 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+[ -f .env ] || { echo 'missing .env'; exit 1; }
|
||
+for k in IMAGE_TAG NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_CPU_WORKER_ID; do
|
||
+ v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env)
|
||
+ [ -n "$v" ] || { echo "missing: $k"; exit 1; }
|
||
+done
|
||
+if grep -Eq '192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then echo 'placeholder/example values remain in .env'; exit 1; fi
|
||
+[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; }
|
||
+docker compose --profile monitoring config >/dev/null
|
||
+echo 'cpu subagent preflight: OK'
|
||
diff --git ab/deployments/gpu-subagent/.env b/deployments/gpu-subagent/.env
|
||
new file mode 100644
|
||
index 0000000..224a325
|
||
--- /dev/null
|
||
+++ b/deployments/gpu-subagent/.env
|
||
@@ -0,0 +1,30 @@
|
||
+# NeuroForge v1.6.2 GPU Subagent + Ollama
|
||
+IMAGE_TAG=1.6.2
|
||
+
|
||
+# Reachable Master endpoint. Use TLS/reverse proxy outside a trusted LAN.
|
||
+NEUROFORGE_MASTER_URL=http://192.0.2.10:8090
|
||
+NEUROFORGE_WORKER_TOKEN=CHANGE_ME_SHARED_WORKER_TOKEN
|
||
+NEUROFORGE_GPU_WORKER_ID=gpu-node-01
|
||
+NEUROFORGE_GPU_WORKER_CAPABILITIES=gpu,model.chat,model.embed
|
||
+NEUROFORGE_GPU_WORKER_CONCURRENCY=1
|
||
+NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s
|
||
+
|
||
+# Ollama runs locally on this GPU host and is also exposed to the Master host.
|
||
+OLLAMA_IMAGE=ollama/ollama:0.33.2
|
||
+OLLAMA_BIND_ADDRESS=0.0.0.0
|
||
+OLLAMA_HOST_PORT=11434
|
||
+OLLAMA_MODEL=gemma4
|
||
+OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+# Optional NeuroForge GPU-worker override; falls back to OLLAMA_API_KEY.
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
+NEUROFORGE_OLLAMA_NUM_CTX=8192
|
||
+OLLAMA_KEEP_ALIVE=10m
|
||
+
|
||
+# Optional Prometheus host/GPU telemetry (start profile monitoring).
|
||
+NODE_EXPORTER_IMAGE=prom/node-exporter:v1.12.1
|
||
+DCGM_EXPORTER_IMAGE=nvcr.io/nvidia/k8s/dcgm-exporter:4.6.0-4.8.3-distroless
|
||
+EXPORTER_BIND_ADDRESS=0.0.0.0
|
||
+NODE_EXPORTER_HOST_PORT=9100
|
||
+DCGM_EXPORTER_HOST_PORT=9400
|
||
diff --git ab/deployments/gpu-subagent/docker-compose.yml b/deployments/gpu-subagent/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..1787d81
|
||
--- /dev/null
|
||
+++ b/deployments/gpu-subagent/docker-compose.yml
|
||
@@ -0,0 +1,120 @@
|
||
+name: neuroforge-gpu-subagent
|
||
+services:
|
||
+ ollama:
|
||
+ image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2}
|
||
+ restart: unless-stopped
|
||
+ gpus: all
|
||
+ environment:
|
||
+ OLLAMA_HOST: 0.0.0.0:11434
|
||
+ OLLAMA_KEEP_ALIVE: ${OLLAMA_KEEP_ALIVE:-10m}
|
||
+ ports:
|
||
+ - ${OLLAMA_BIND_ADDRESS:-0.0.0.0}:${OLLAMA_HOST_PORT:-11434}:11434
|
||
+ volumes:
|
||
+ - ollama-data:/root/.ollama
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ healthcheck:
|
||
+ test:
|
||
+ - CMD
|
||
+ - ollama
|
||
+ - list
|
||
+ interval: 15s
|
||
+ timeout: 5s
|
||
+ retries: 20
|
||
+ start_period: 15s
|
||
+ gpu-subagent:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set
|
||
+ immutable IMAGE_TAG}
|
||
+ command:
|
||
+ - -server
|
||
+ - ${NEUROFORGE_MASTER_URL:?Set reachable NeuroForge master URL}
|
||
+ - -id
|
||
+ - ${NEUROFORGE_GPU_WORKER_ID:?Set GPU worker ID}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ NEUROFORGE_WORKER_TOKEN: ${NEUROFORGE_WORKER_TOKEN:?Set worker token shared
|
||
+ with master}
|
||
+ NEUROFORGE_WORKER_RESOURCE_CLASS: gpu
|
||
+ NEUROFORGE_WORKER_CAPABILITIES: ${NEUROFORGE_GPU_WORKER_CAPABILITIES:-gpu,model.chat,model.embed}
|
||
+ NEUROFORGE_WORKER_MAX_CONCURRENCY: ${NEUROFORGE_GPU_WORKER_CONCURRENCY:-1}
|
||
+ NEUROFORGE_WORKER_HEARTBEAT_INTERVAL: ${NEUROFORGE_WORKER_HEARTBEAT_INTERVAL:-15s}
|
||
+ NEUROFORGE_WORKER_OLLAMA_URL: http://ollama:11434
|
||
+ NEUROFORGE_WORKER_OLLAMA_API_KEY: ${NEUROFORGE_WORKER_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ NEUROFORGE_WORKER_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma4}
|
||
+ NEUROFORGE_WORKER_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
+ NEUROFORGE_WORKER_OLLAMA_NUM_CTX: ${NEUROFORGE_OLLAMA_NUM_CTX:-8192}
|
||
+ NEUROFORGE_WORKER_OLLAMA_KEEP_ALIVE: ${OLLAMA_KEEP_ALIVE:-10m}
|
||
+ depends_on:
|
||
+ ollama-model-chat-init:
|
||
+ condition: service_completed_successfully
|
||
+ ollama-model-embed-init:
|
||
+ condition: service_completed_successfully
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=32m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ node-exporter:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${NODE_EXPORTER_IMAGE:-prom/node-exporter:v1.12.1}
|
||
+ restart: unless-stopped
|
||
+ command:
|
||
+ - --path.procfs=/host/proc
|
||
+ - --path.sysfs=/host/sys
|
||
+ - --path.rootfs=/rootfs
|
||
+ - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
|
||
+ ports:
|
||
+ - ${EXPORTER_BIND_ADDRESS:-0.0.0.0}:${NODE_EXPORTER_HOST_PORT:-9100}:9100
|
||
+ volumes:
|
||
+ - /proc:/host/proc:ro
|
||
+ - /sys:/host/sys:ro
|
||
+ - /:/rootfs:ro,rslave
|
||
+ read_only: true
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ dcgm-exporter:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${DCGM_EXPORTER_IMAGE:-nvcr.io/nvidia/k8s/dcgm-exporter:4.6.0-4.8.3-distroless}
|
||
+ restart: unless-stopped
|
||
+ gpus: all
|
||
+ cap_add:
|
||
+ - SYS_ADMIN
|
||
+ ports:
|
||
+ - ${EXPORTER_BIND_ADDRESS:-0.0.0.0}:${DCGM_EXPORTER_HOST_PORT:-9400}:9400
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ ollama-model-chat-init:
|
||
+ image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2}
|
||
+ restart: 'no'
|
||
+ command:
|
||
+ - pull
|
||
+ - ${OLLAMA_MODEL:-gemma4}
|
||
+ environment:
|
||
+ OLLAMA_HOST: http://ollama:11434
|
||
+ depends_on:
|
||
+ ollama:
|
||
+ condition: service_healthy
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ ollama-model-embed-init:
|
||
+ image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2}
|
||
+ restart: 'no'
|
||
+ command:
|
||
+ - pull
|
||
+ - ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
+ environment:
|
||
+ OLLAMA_HOST: http://ollama:11434
|
||
+ depends_on:
|
||
+ ollama:
|
||
+ condition: service_healthy
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+volumes:
|
||
+ ollama-data: null
|
||
diff --git ab/deployments/gpu-subagent/preflight.sh b/deployments/gpu-subagent/preflight.sh
|
||
new file mode 100755
|
||
index 0000000..2061a06
|
||
--- /dev/null
|
||
+++ b/deployments/gpu-subagent/preflight.sh
|
||
@@ -0,0 +1,11 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+[ -f .env ] || { echo 'missing .env'; exit 1; }
|
||
+for k in IMAGE_TAG NEUROFORGE_MASTER_URL NEUROFORGE_WORKER_TOKEN NEUROFORGE_GPU_WORKER_ID OLLAMA_MODEL OLLAMA_EMBEDDING_MODEL; do
|
||
+ v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env)
|
||
+ [ -n "$v" ] || { echo "missing: $k"; exit 1; }
|
||
+done
|
||
+if grep -Eq '192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then echo 'placeholder/example values remain in .env'; exit 1; fi
|
||
+[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; }
|
||
+docker compose --profile monitoring config >/dev/null
|
||
+echo 'gpu subagent preflight: OK'
|
||
diff --git ab/deployments/knowledge/.env b/deployments/knowledge/.env
|
||
new file mode 100644
|
||
index 0000000..ab2c3c7
|
||
--- /dev/null
|
||
+++ b/deployments/knowledge/.env
|
||
@@ -0,0 +1,33 @@
|
||
+###############################################################################
|
||
+# Knowledge Editor/Search - standalone / stripped core
|
||
+###############################################################################
|
||
+IMAGE_TAG=1.6.2
|
||
+KNOWLEDGE_IMAGE=git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:1.6.2
|
||
+CORE_NETWORK=glpi-ai-core
|
||
+CORE_DATA_ROOT=../../runtime
|
||
+KNOWLEDGE_BIND_IP=127.0.0.1
|
||
+KNOWLEDGE_HOST_PORT=9981
|
||
+
|
||
+KB_APP_MODE=editor
|
||
+APP_TITLE=Knowledge Base Editor
|
||
+APP_SUBTITLE=GLPI AI Agent Core
|
||
+AUTO_RELOAD_INTERVAL=60s
|
||
+
|
||
+# Für Produktion setzen
|
||
+BASIC_AUTH_USER=admin
|
||
+BASIC_AUTH_PASSWORD=CHANGE_ME_LONG_KNOWLEDGE_PASSWORD
|
||
+
|
||
+# Ohne KI-Fallback ist Knowledge vollständig unabhängig von Ollama.
|
||
+AI_FALLBACK_ENABLED=false
|
||
+# Nur bei AI_FALLBACK_ENABLED=true relevant:
|
||
+OLLAMA_BASE_URL=http://ollama:11434
|
||
+OLLAMA_MODEL=gemma4
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+OLLAMA_TIMEOUT=10m
|
||
+OLLAMA_MAX_CONCURRENT=1
|
||
+OLLAMA_STAGING_AUTO_REPLY=false
|
||
+OLLAMA_STAGING_MIN_SCORE=0.78
|
||
+
|
||
+# Nur für externe Staging-API nötig; Browser-Editor funktioniert auch leer.
|
||
+KB_INTEGRATION_TOKEN=
|
||
diff --git ab/deployments/knowledge/docker-compose.build.yml b/deployments/knowledge/docker-compose.build.yml
|
||
new file mode 100644
|
||
index 0000000..82f3010
|
||
--- /dev/null
|
||
+++ b/deployments/knowledge/docker-compose.build.yml
|
||
@@ -0,0 +1,5 @@
|
||
+services:
|
||
+ knowledge:
|
||
+ image: glpi-ai-knowledge-local:1.6.2
|
||
+ build:
|
||
+ context: ../../services/knowledge
|
||
diff --git ab/deployments/knowledge/docker-compose.yml b/deployments/knowledge/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..b0b7e05
|
||
--- /dev/null
|
||
+++ b/deployments/knowledge/docker-compose.yml
|
||
@@ -0,0 +1,37 @@
|
||
+name: glpi-ai-knowledge-core
|
||
+services:
|
||
+ knowledge:
|
||
+ image: ${KNOWLEDGE_IMAGE:-git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:-1.6.2}}
|
||
+ restart: unless-stopped
|
||
+ env_file:
|
||
+ - .env
|
||
+ environment:
|
||
+ APP_MODE: ${KB_APP_MODE:-editor}
|
||
+ DATA_DIR: /data/knowledge
|
||
+ BACKUP_DIR: /data/backups
|
||
+ STAGING_DIR: /data/staging
|
||
+ LISTEN_ADDR: :8080
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ BRAIN_ACTIVITY_URL: ""
|
||
+ BRAIN_ACTIVITY_API_KEY: ""
|
||
+ ports:
|
||
+ - ${KNOWLEDGE_BIND_IP:-127.0.0.1}:${KNOWLEDGE_HOST_PORT:-9981}:8080
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/knowledge:/data/knowledge:rw
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/staging:/data/staging:rw
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/backups:/data/backups:rw
|
||
+ networks:
|
||
+ - core
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=32m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ stop_grace_period: 35s
|
||
+
|
||
+networks:
|
||
+ core:
|
||
+ external: true
|
||
+ name: ${CORE_NETWORK:-glpi-ai-core}
|
||
diff --git ab/deployments/master/.env b/deployments/master/.env
|
||
new file mode 100644
|
||
index 0000000..4efd6fd
|
||
--- /dev/null
|
||
+++ b/deployments/master/.env
|
||
@@ -0,0 +1,957 @@
|
||
+###############################################################################
|
||
+# GLPI NEUROFORGE MEGA v1.6.2 - VOLLSTÄNDIGE .ENV.example
|
||
+#
|
||
+# Diese Datei ist die zentrale Konfiguration für docker compose.
|
||
+# Sie enthält:
|
||
+# - GLPI AI Agent (vollständige produktive Optionen)
|
||
+# - NeuroForge / Vector Backend / Controlled Learning
|
||
+# - Knowledge Editor
|
||
+# - Control Center
|
||
+# - optional SearXNG Research
|
||
+# - optionale Codebase-Memory-UI
|
||
+#
|
||
+# SICHERER START:
|
||
+# DRY_RUN=true
|
||
+# AUTO_REPLY=false
|
||
+# AUTO_PRIORITY=false
|
||
+# AUTO_ESCALATION=false
|
||
+# NEUROFORGE_RESEARCH_ENABLED=false
|
||
+# NEUROFORGE_AUTONOMY_ENABLED=false
|
||
+#
|
||
+# WICHTIG:
|
||
+# Interne Container-Adressen/Ports wie HTTP_ADDR, DATA_DIR, KNOWLEDGE_DIR,
|
||
+# OLLAMA_URL, NEUROFORGE_URL und BRAIN_ACTIVITY_URL werden im Mega-Compose
|
||
+# fest verdrahtet. Dadurch können alte Standalone-Werte die Container-
|
||
+# Kommunikation nicht versehentlich beschädigen.
|
||
+###############################################################################
|
||
+
|
||
+###############################################################################
|
||
+# 01. MEGA STACK - RELEASE / HOST PORTS / PFADE
|
||
+###############################################################################
|
||
+# Immutable Registry-Tag der sechs Projekt-Images. "latest" ist produktiv verboten.
|
||
+IMAGE_TAG=1.6.2
|
||
+
|
||
+CONTROL_HOST_PORT=8070
|
||
+AGENT_HOST_PORT=8080
|
||
+KNOWLEDGE_HOST_PORT=8081
|
||
+NEUROFORGE_HOST_PORT=8090
|
||
+OLLAMA_HOST_PORT=11434
|
||
+SEARXNG_HOST_PORT=8888
|
||
+
|
||
+# Gemeinsame produktive Knowledge-Quelle auf dem Docker-Host.
|
||
+KB_DATA_PATH=./knowledge
|
||
+KB_DATA_MOUNT_MODE=rw
|
||
+KB_BACKUP_PATH=./backups
|
||
+KB_STAGING_PATH=./staging
|
||
+
|
||
+###############################################################################
|
||
+# 02. MEGA STACK - SECURITY / SERVICE TOKENS
|
||
+###############################################################################
|
||
+# Erzeugen z. B. mit: ./scripts/generate-secrets.sh
|
||
+NEUROFORGE_ADMIN_TOKEN=CHANGE_ME_MASTER_ADMIN_TOKEN
|
||
+NEUROFORGE_APP_API_KEY=CHANGE_ME_MASTER_APP_API_KEY
|
||
+# Nur Agent/Knowledge Integrations- und Outcome-Schreibpfade.
|
||
+NEUROFORGE_INTEGRATION_TOKEN=CHANGE_ME_MASTER_INTEGRATION_TOKEN
|
||
+# Nur NeuroForge Status/Graph-Lesezugriffe des Control Centers.
|
||
+NEUROFORGE_CONTROL_READ_TOKEN=CHANGE_ME_MASTER_CONTROL_READ_TOKEN
|
||
+NEUROFORGE_WORKER_TOKEN=CHANGE_ME_SHARED_WORKER_TOKEN
|
||
+NEUROFORGE_METRICS_TOKEN=CHANGE_ME_MASTER_METRICS_TOKEN
|
||
+# Nur bei Clusterbetrieb erforderlich.
|
||
+NEUROFORGE_CLUSTER_TOKEN=
|
||
+KB_INTEGRATION_TOKEN=CHANGE_ME_KB_INTEGRATION_TOKEN
|
||
+CONTROL_READ_TOKEN=CHANGE_ME_AGENT_CONTROL_READ_TOKEN
|
||
+# Eigene Anmeldung des Control Centers; /healthz bleibt absichtlich öffentlich.
|
||
+CONTROL_BASIC_AUTH_USER=admin
|
||
+CONTROL_BASIC_AUTH_PASSWORD=CHANGE_ME_CONTROL_PASSWORD
|
||
+# Optionaler externer OpenAI-Fallback. Leer = deaktiviert.
|
||
+OPENAI_API_KEY=
|
||
+
|
||
+###############################################################################
|
||
+# 03. SHARED OLLAMA RUNTIME
|
||
+###############################################################################
|
||
+# Dieses Modell wird von Agent, Knowledge und NeuroForge gemeinsam verwendet.
|
||
+# Wenn du dein bisheriges Verhalten beibehalten willst, kannst du hier z. B.
|
||
+# qwen3:8b statt gemma3 setzen. Das Modell muss vorher in Ollama vorhanden sein.
|
||
+OLLAMA_MODEL=gemma4
|
||
+OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+# Optional NeuroForge-only override; takes precedence over OLLAMA_API_KEY.
|
||
+NEUROFORGE_OLLAMA_API_KEY=
|
||
+OLLAMA_TIMEOUT=10m
|
||
+OLLAMA_MAX_CONCURRENT=1
|
||
+OLLAMA_NUM_PREDICT=768
|
||
+OLLAMA_JSON_RETRIES=1
|
||
+OLLAMA_KEEP_ALIVE=10m
|
||
+OLLAMA_THINK=false
|
||
+# NeuroForge verwendet für lange, quellengebundene KB-Synthesen ein eigenes
|
||
+# Call-Budget. num_predict=0 ist hier absichtlich: dadurch gilt das jeweilige
|
||
+# Staging-/Verification-Limit statt eines globalen niedrigen Ollama-Limits.
|
||
+NEUROFORGE_OLLAMA_NUM_CTX=8192
|
||
+NEUROFORGE_OLLAMA_NUM_PREDICT=0
|
||
+
|
||
+###############################################################################
|
||
+# 04. KNOWLEDGE EDITOR
|
||
+###############################################################################
|
||
+KB_APP_MODE=editor
|
||
+APP_TITLE=Knowledge Base Editor
|
||
+APP_SUBTITLE=GLPI NeuroForge Mega
|
||
+AUTO_RELOAD_INTERVAL=60s
|
||
+
|
||
+# Für Produktion setzen. Leer würde die optionale Basic-Auth deaktivieren.
|
||
+BASIC_AUTH_USER=admin
|
||
+BASIC_AUTH_PASSWORD=CHANGE_ME_KNOWLEDGE_PASSWORD
|
||
+
|
||
+AI_FALLBACK_ENABLED=true
|
||
+# Optional externer Ollama-Endpunkt. Leer/fehlend = interner Compose-Service http://ollama:11434.
|
||
+OLLAMA_BASE_URL=http://192.0.2.22:11434
|
||
+OLLAMA_STAGING_AUTO_REPLY=false
|
||
+OLLAMA_STAGING_MIN_SCORE=0.78
|
||
+
|
||
+###############################################################################
|
||
+# 05. NEUROFORGE VECTOR BACKEND / INTEGRATION
|
||
+###############################################################################
|
||
+# local | dual | neuroforge
|
||
+KNOWLEDGE_VECTOR_BACKEND=dual
|
||
+NEUROFORGE_NAMESPACE=glpi-agent
|
||
+NEUROFORGE_TIMEOUT=15s
|
||
+NEUROFORGE_SEARCH_K=128
|
||
+# true = bei NeuroForge-Ausfall lokale/lexikalische Evidenz weiterverwenden.
|
||
+NEUROFORGE_FAIL_OPEN=true
|
||
+
|
||
+###############################################################################
|
||
+# 06. CONTROLLED LEARNING / VALIDATED OUTCOMES
|
||
+###############################################################################
|
||
+# Verhindert automatisches Langzeitlernen aus rohem Chatinput/AI-Ausgaben.
|
||
+NEUROFORGE_CONTROLLED_LEARNING=true
|
||
+# Separate gate for semantic goal-cycle memories. Required for manual/autonomous
|
||
+# Goal cycles to learn while Controlled Learning remains enabled.
|
||
+NEUROFORGE_GOAL_LEARNING_ENABLED=false
|
||
+# Mega-Readiness prüft Ollama /api/tags sowie Chat- und Embedding-Modell live.
|
||
+NEUROFORGE_READINESS_OLLAMA_LIVE=true
|
||
+
|
||
+# Ticket -> KI-Vorschlag -> Techniker bestätigt/korrigiert -> Trusted Outcome.
|
||
+OUTCOME_LEARNING_ENABLED=true
|
||
+OUTCOME_LEARNING_FAIL_OPEN=false
|
||
+OUTCOME_LEARNING_MAX_OUTCOMES=2000
|
||
+
|
||
+# Menschlich validierte Erfahrungen als sekundäre Evidenz abrufen.
|
||
+OUTCOME_RETRIEVAL_ENABLED=true
|
||
+OUTCOME_RETRIEVAL_SEARCH_K=6
|
||
+OUTCOME_RETRIEVAL_MIN_SIMILARITY=0.58
|
||
+OUTCOME_RETRIEVAL_FAIL_OPEN=true
|
||
+
|
||
+###############################################################################
|
||
+# 07. OPTIONAL RESEARCH / SEARXNG / AUTONOMY
|
||
+###############################################################################
|
||
+# SearXNG-Container wird nur mit `docker compose --profile research ...` gestartet.
|
||
+SEARXNG_IMAGE=docker.io/searxng/searxng:latest
|
||
+SEARXNG_SECRET=CHANGE_ME_SEARXNG_SECRET
|
||
+
|
||
+# Research und Autonomy sind absichtlich getrennt.
|
||
+NEUROFORGE_RESEARCH_ENABLED=false
|
||
+NEUROFORGE_SEARXNG_ENABLED=false
|
||
+NEUROFORGE_SEARXNG_URL=http://searxng:8080
|
||
+NEUROFORGE_RESEARCH_GOAL_ENABLED=true
|
||
+NEUROFORGE_AUTONOMY_ENABLED=false
|
||
+NEUROFORGE_AUTONOMY_INTERVAL_MINUTES=30
|
||
+NEUROFORGE_RESEARCH_MAX_QUERIES=2
|
||
+NEUROFORGE_RESEARCH_MAX_PAGES=4
|
||
+
|
||
+# Research -> Knowledge human-review staging bridge. Never writes production KB.
|
||
+NEUROFORGE_KB_STAGING_ENABLED=true
|
||
+NEUROFORGE_KB_STAGING_MIN_EVIDENCE=4
|
||
+NEUROFORGE_KB_STAGING_MIN_SOURCES=2
|
||
+# 0 allows a human-review draft from multiple independent sources before exact
|
||
+# semantic corroboration exists. Raise to 1+ for stricter environments.
|
||
+NEUROFORGE_KB_STAGING_MIN_CORROBORATIONS=0
|
||
+NEUROFORGE_KB_STAGING_MAX_EVIDENCE=12
|
||
+# llm = echte, quellengebundene Artikelsynthese (Production default).
|
||
+# evidence = nur diagnostisches Roh-Evidence-Bundle, nicht als Artikel geeignet.
|
||
+NEUROFORGE_KB_STAGING_SYNTHESIS_MODE=llm
|
||
+# Produktions-Gate: mindestens eine belastbare Erst-/Herstellerquelle muss im
|
||
+# tatsächlich an die Synthese übergebenen Evidence-Set enthalten sein.
|
||
+NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_SOURCE=true
|
||
+NEUROFORGE_KB_STAGING_MIN_AUTHORITATIVE_SOURCES=1
|
||
+# Kommagetrennte zusätzliche First-Party-Domains. Built-ins umfassen u.a.
|
||
+# Microsoft, Fortinet, NVIDIA, Cisco, Broadcom/VMware, Red Hat, Ubuntu, Apple,
|
||
+# Google und Mozilla. Leer = nur Built-ins.
|
||
+NEUROFORGE_KB_STAGING_AUTHORITATIVE_DOMAINS=
|
||
+# Zweite LLM-Stufe prüft jede materielle Draft-Aussage gegen konkrete E*-Belege.
|
||
+NEUROFORGE_KB_STAGING_VERIFY_CLAIMS=true
|
||
+NEUROFORGE_KB_STAGING_MIN_CLAIM_COVERAGE=1.0
|
||
+# Handlungsanweisungen/Commands müssen durch eine autoritative Quelle belegt sein.
|
||
+NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_ACTIONS=true
|
||
+NEUROFORGE_KB_STAGING_MAX_VERIFICATION_STATEMENTS=32
|
||
+# Einmaliger Grounding-Repair ist erlaubt; anschließend wird vollständig neu geprüft.
|
||
+NEUROFORGE_KB_STAGING_VERIFICATION_REPAIR=true
|
||
+# Article-Depth-Gate: `text` ist der vollständige KB-Artikel; `answer` bleibt eine
|
||
+# kompakte operative Zusammenfassung für den Agenten. Die Evidence wird für den
|
||
+# Prompt fair über alle ausgewählten Belege verteilt und auf ein Context-Budget
|
||
+# begrenzt, damit bei typischen 8k-Modellkontexten genügend Output-Budget bleibt.
|
||
+NEUROFORGE_KB_STAGING_SYNTHESIS_MAX_TOKENS=2600
|
||
+NEUROFORGE_KB_STAGING_EVIDENCE_PROMPT_MAX_CHARS=14000
|
||
+NEUROFORGE_KB_STAGING_MIN_ARTICLE_CHARS=3500
|
||
+NEUROFORGE_KB_STAGING_TARGET_ARTICLE_CHARS=6500
|
||
+NEUROFORGE_KB_STAGING_MAX_ARTICLE_CHARS=10000
|
||
+NEUROFORGE_KB_STAGING_MIN_ANSWER_CHARS=160
|
||
+NEUROFORGE_KB_STAGING_MAX_ANSWER_CHARS=1200
|
||
+
|
||
+###############################################################################
|
||
+# 07B. MASTER / SUBAGENTS / DURABLE ORCHESTRATOR / KNOWLEDGE GRAPH
|
||
+###############################################################################
|
||
+# Der NeuroForge-Master hält den autoritativen Zustand. Subagents ziehen nur
|
||
+# capability-passende Jobs und arbeiten unter lease/heartbeat fencing.
|
||
+NEUROFORGE_WORKER_LEASE_SECONDS=120
|
||
+NEUROFORGE_WORKER_HEARTBEAT_SECONDS=15
|
||
+NEUROFORGE_WORKER_STALE_AFTER_SECONDS=60
|
||
+NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS=3
|
||
+NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS=15
|
||
+NEUROFORGE_WORKER_MAX_QUEUED_JOBS=5000
|
||
+NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB=128
|
||
+# Worker-Ergebnisse, die Master-State verändern (z.B. vector.relink), werden
|
||
+# zweiphasig persistiert und bei Master-Neustart idempotent weiter angewendet.
|
||
+NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS=5
|
||
+NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS=5
|
||
+NEUROFORGE_WORKER_JOB_RETENTION_HOURS=24
|
||
+NEUROFORGE_WORKER_MAX_TERMINAL_JOBS=2000
|
||
+# Lokale Compose-Subagents. Zusätzliche Remote-Subagents können mit
|
||
+# docker-compose.subagent.yml auf anderen Hosts gestartet werden.
|
||
+NEUROFORGE_CPU_WORKER_CONCURRENCY=2
|
||
+NEUROFORGE_GPU_WORKER_CONCURRENCY=1
|
||
+NEUROFORGE_WORKER_HEARTBEAT_INTERVAL=15s
|
||
+# Bestehende/importierte knowledge.chunk-Memories werden kontrolliert zu einem
|
||
+# n:m-Graphen verlinkt. Kein O(N²)-Queue-Burst: ANN-Kandidaten + bounded queue.
|
||
+NEUROFORGE_GRAPH_BACKFILL_ENABLED=true
|
||
+NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS=10
|
||
+NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE=16
|
||
+NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED=64
|
||
+NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE=3
|
||
+NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER=6
|
||
+NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES=360
|
||
+NEUROFORGE_GRAPH_REQUIRE_WORKER=true
|
||
+# Retrieval kann echte mehrstufige Assoziationsketten verfolgen.
|
||
+NEUROFORGE_GRAPH_MAX_HOPS=3
|
||
+NEUROFORGE_GRAPH_HOP_DECAY=0.60
|
||
+NEUROFORGE_GRAPH_MAX_EXPANSION=64
|
||
+NEUROFORGE_GRAPH_MIN_EDGE_WEIGHT=0.05
|
||
+# LLM/Embedding-Aufgaben werden bevorzugt an GPU-Subagents delegiert. Bei nicht
|
||
+# verfügbarem GPU-Subagent fällt der Master auf seinen Provider-Router zurück.
|
||
+NEUROFORGE_OFFLOAD_CHAT=true
|
||
+NEUROFORGE_OFFLOAD_EMBEDDINGS=true
|
||
+NEUROFORGE_DISTRIBUTED_INFERENCE_WAIT_SECONDS=180
|
||
+
|
||
+###############################################################################
|
||
+# 08. OPTIONAL CODEBASE MEMORY MCP / ENGINEERING UI
|
||
+###############################################################################
|
||
+# Developer-only. Für den Produktivbetrieb nicht erforderlich.
|
||
+CODEBASE_MEMORY_URL=
|
||
+PUBLIC_CODEBASE_MEMORY_URL=http://localhost:9749
|
||
+
|
||
+###############################################################################
|
||
+# 09. AGENT - VOLLSTÄNDIGE KONFIGURATION
|
||
+###############################################################################
|
||
+# 09A. GLPI AI AGENT - ALLGEMEINER BETRIEB
|
||
+###############################################################################
|
||
+# true:
|
||
+# Der Agent analysiert vollständig, schreibt aber keine Änderungen nach GLPI.
|
||
+#
|
||
+# false:
|
||
+# Durch die Policy freigegebene Aktionen werden tatsächlich ausgeführt.
|
||
+#
|
||
+# Für Tests / Einführung:
|
||
+# true
|
||
+DRY_RUN=true
|
||
+# Mögliche Werte:
|
||
+# debug
|
||
+# info
|
||
+# warn
|
||
+# error
|
||
+LOG_LEVEL=info
|
||
+# HTTP-Listener INNERHALB des Agent-Containers.
|
||
+# Im Mega-Compose wird dieser Wert zusätzlich fest auf :8080 überschrieben.
|
||
+# Der veröffentlichte Host-Port wird ausschließlich über AGENT_HOST_PORT gesteuert.
|
||
+HTTP_ADDR=:8080
|
||
+# Persistentes Verzeichnis IM Container.
|
||
+#
|
||
+# Compose mountet:
|
||
+# agent-data:/app/data
|
||
+#
|
||
+# Enthält unter anderem:
|
||
+# - Knowledge-Index
|
||
+# - Audit/Run-Daten
|
||
+# - Category Learning
|
||
+# - Managed Knowledge
|
||
+# - GLPI-KB-Cache
|
||
+DATA_DIR=/app/data
|
||
+###############################################################################
|
||
+# 06. AGENT WEBUI / API / DIAGNOSE
|
||
+###############################################################################
|
||
+# Benutzer für Agent-Dashboard, Knowledge-Verwaltung und Diagnose-Cockpit.
|
||
+WEB_USERNAME=admin
|
||
+WEB_PASSWORD=CHANGE_ME_AGENT_WEB_PASSWORD
|
||
+# false:
|
||
+# Anmeldung erforderlich.
|
||
+#
|
||
+# true:
|
||
+# Weboberfläche ohne Authentifizierung erreichbar.
|
||
+#
|
||
+# In Produktion normalerweise false.
|
||
+WEB_ALLOW_ANONYMOUS=false
|
||
+# TrustedNet-Kennzeichnung vor automatisch ausgewählten Antworten.
|
||
+#
|
||
+# true:
|
||
+# TrustedNet-KI-Badge wird vor Anrede und Antwort eingefügt.
|
||
+#
|
||
+# false:
|
||
+# keine KI-Kennzeichnung.
|
||
+AI_CONTENT_LABEL_ENABLED=true
|
||
+###############################################################################
|
||
+# 07. OPTIONALER GLPI-WEBHOOK
|
||
+###############################################################################
|
||
+# Optionales Shared Secret für eingehende GLPI-Webhooks.
|
||
+#
|
||
+# Der Absender muss dasselbe Secret z. B. über:
|
||
+# X-Webhook-Secret
|
||
+# übertragen.
|
||
+#
|
||
+# Leer lassen, falls kein Webhook verwendet wird.
|
||
+WEBHOOK_SECRET=
|
||
+###############################################################################
|
||
+# 08. GLPI 11 / HIGH-LEVEL API / OAUTH2
|
||
+###############################################################################
|
||
+GLPI_URL=https://YOUR-GLPI-HOST
|
||
+# Verwendete GLPI High-Level API.
|
||
+GLPI_API_VERSION=v2.3
|
||
+# OAuth2 Service Account.
|
||
+GLPI_CLIENT_ID=CHANGE_ME_GLPI_CLIENT_ID
|
||
+GLPI_CLIENT_SECRET=CHANGE_ME_GLPI_CLIENT_SECRET
|
||
+GLPI_USERNAME=CHANGE_ME_GLPI_USERNAME
|
||
+GLPI_PASSWORD=CHANGE_ME_GLPI_PASSWORD
|
||
+# Numerische GLPI-Benutzer-ID des Service-Accounts.
|
||
+#
|
||
+# Wird unter anderem benötigt, um Agent-Followups von menschlichen
|
||
+# Followups unterscheiden zu können.
|
||
+GLPI_AGENT_USER_ID=0
|
||
+# Nur für lokale Testsysteme ohne TLS.
|
||
+#
|
||
+# Produktion:
|
||
+# false
|
||
+GLPI_ALLOW_INSECURE_HTTP=false
|
||
+###############################################################################
|
||
+# 09. GLPI TICKET-POLLING
|
||
+###############################################################################
|
||
+# Fail-closed Whitelist erlaubter GLPI-Ticketstatus.
|
||
+#
|
||
+# Beispiel:
|
||
+# 1
|
||
+# 1,2
|
||
+#
|
||
+# Status 1 entspricht typischerweise "Neu".
|
||
+GLPI_ALLOWED_STATUS_IDS=1
|
||
+# Polling-Intervall.
|
||
+GLPI_POLL_INTERVAL=30s
|
||
+# Maximale Anzahl Tickets pro Poll.
|
||
+GLPI_POLL_LIMIT=50
|
||
+# Optionale serverseitige Vorfilterung.
|
||
+#
|
||
+# Die Agent-Policy prüft GLPI_ALLOWED_STATUS_IDS anschließend trotzdem selbst.
|
||
+#
|
||
+# Änderungen der Syntax immer gegen /api.php/doc der eigenen GLPI-Instanz
|
||
+# prüfen.
|
||
+GLPI_TICKET_FILTER=status.id==1
|
||
+# HTTP-Timeout für GLPI-Aufrufe.
|
||
+GLPI_TIMEOUT=20s
|
||
+###############################################################################
|
||
+# 10. GLPI AI AGENT - OLLAMA-POOL
|
||
+###############################################################################
|
||
+# Einzelnode-Kompatibilität. Wird nur verwendet, wenn OLLAMA_URLS leer ist.
|
||
+OLLAMA_URL=http://192.0.2.22:11434
|
||
+
|
||
+# Mehrere Ollama-Instanzen, durch Komma getrennt. Alle Nodes sollten dieselbe
|
||
+# Ollama-Version, dasselbe Chat-Modell und dasselbe Embedding-Modell besitzen.
|
||
+# Beispiel für vorhandene Lenovo-Nodes:
|
||
+# OLLAMA_URLS=http://10.20.30.21:11434,http://10.20.30.22:11434,http://10.20.30.23:11434
|
||
+OLLAMA_URLS=http://192.0.2.22:11434
|
||
+
|
||
+# Optionale lesbare Namen; Anzahl muss exakt zu OLLAMA_URLS passen.
|
||
+# OLLAMA_NODE_NAMES=lenovo-01,lenovo-02,lenovo-03
|
||
+OLLAMA_NODE_NAMES=gpu-node-01
|
||
+
|
||
+# Optionale Gewichte 1..100; nur für OLLAMA_ROUTING_MODE=weighted relevant.
|
||
+# OLLAMA_NODE_WEIGHTS=1,1,1
|
||
+OLLAMA_NODE_WEIGHTS=1
|
||
+
|
||
+# Routing-Modi:
|
||
+# least_inflight = Node mit den wenigsten laufenden Requests (empfohlen)
|
||
+# round_robin = zyklische Verteilung
|
||
+# weighted = Verteilung anhand OLLAMA_NODE_WEIGHTS und Auslastung
|
||
+# fastest_recent = bevorzugt die zuletzt schnellsten Nodes
|
||
+OLLAMA_ROUTING_MODE=least_inflight
|
||
+
|
||
+# Maximale parallele Requests JE Node. Für integrierte GPUs/RAM-Sharing 1.
|
||
+OLLAMA_NODE_MAX_INFLIGHT=1
|
||
+
|
||
+# Regelmäßige Prüfung von /api/tags.
|
||
+OLLAMA_NODE_HEALTH_INTERVAL=15s
|
||
+
|
||
+# Nach einem retryfähigen Netzwerk-/HTTP-Fehler wird der Node so lange nicht
|
||
+# für neue Requests verwendet.
|
||
+OLLAMA_NODE_FAILURE_COOLDOWN=30s
|
||
+
|
||
+# Maximalzeit für einen einzelnen Request an genau einen Node. Der übergeordnete
|
||
+# Analyse-Timeout kann kürzer sein und hat dann Vorrang.
|
||
+OLLAMA_NODE_REQUEST_TIMEOUT=10m
|
||
+
|
||
+# Bei Netzwerkfehlern, HTTP 408/429/5xx oder ungültigem Response-JSON auf einen
|
||
+# anderen kompatiblen Node wechseln.
|
||
+OLLAMA_FAILOVER_ENABLED=true
|
||
+
|
||
+# Maximale Anzahl verschiedener Nodes je logischem Request. 0 bedeutet:
|
||
+# automatisch alle konfigurierten Nodes. Ein positiver Wert darf höchstens der
|
||
+# Zahl der OLLAMA_URLS-Einträge entsprechen.
|
||
+OLLAMA_FAILOVER_ATTEMPTS=0
|
||
+
|
||
+# Bei abweichenden Chat-/Embedding-Modelldigests wird der Pool vollständig
|
||
+# fail-closed. Für reproduzierbare Entscheidungen unbedingt true lassen.
|
||
+OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true
|
||
+
|
||
+# true: Jeder Node muss auch OLLAMA_EMBEDDING_MODEL installiert haben.
|
||
+# Bei RAG empfohlen. false erlaubt Chat-only-Nodes; Embedding-Requests werden
|
||
+# trotzdem nur an Nodes mit erkanntem Embedding-Modell gesendet.
|
||
+OLLAMA_REQUIRE_EMBEDDING_MODEL=true
|
||
+
|
||
+# OLLAMA_MODEL ist bereits oben im gemeinsamen Compose-/Ollama-Bereich gesetzt:
|
||
+# OLLAMA_MODEL=qwen3:8b
|
||
+# Embedding-Modell für RAG.
|
||
+# OLLAMA_EMBEDDING_MODEL=embeddinggemma # already configured in SHARED OLLAMA RUNTIME above
|
||
+
|
||
+# Modellspezifisches Retrieval-Prompting.
|
||
+# auto = Modell automatisch erkennen; für embeddinggemma empfohlen.
|
||
+# plain = keine modellspezifischen Retrieval-Prompts.
|
||
+KNOWLEDGE_EMBEDDING_PROFILE=auto
|
||
+
|
||
+# Gesamtbudget für Ollama-Aufrufe und Fallback für Node-Request-Timeouts.
|
||
+# OLLAMA_TIMEOUT ist bereits oben gesetzt.
|
||
+# OLLAMA_MAX_CONCURRENT bleibt als Legacy-Alias für
|
||
+# OLLAMA_NODE_MAX_INFLIGHT erhalten, falls der neue Wert nicht gesetzt ist.
|
||
+
|
||
+# Maximale Anzahl generierter Tokens für strukturierte Antworten.
|
||
+# OLLAMA_NUM_PREDICT=768 # already configured in SHARED OLLAMA RUNTIME above
|
||
+# Wiederholungen bei semantisch/strukturell fehlerhaftem Modell-JSON.
|
||
+# Diese Wiederholungen sind von Netzwerk-Failover getrennt.
|
||
+# OLLAMA_JSON_RETRIES=1 # already configured in SHARED OLLAMA RUNTIME above
|
||
+# Ollama-Modell nach Benutzung im Speicher halten.
|
||
+# OLLAMA_KEEP_ALIVE=10m # already configured in SHARED OLLAMA RUNTIME above
|
||
+# Thinking bei unterstützten Modellen deaktivieren.
|
||
+# OLLAMA_THINK=false # already configured in SHARED OLLAMA RUNTIME above
|
||
+###############################################################################
|
||
+# 11. KNOWLEDGE BASE / RAG - BASIS
|
||
+###############################################################################
|
||
+# Knowledge-Verzeichnis IM Agent-Container.
|
||
+#
|
||
+# Compose sollte hierhin KB_DATA_PATH mounten:
|
||
+# ${KB_DATA_PATH:-./knowledge}:/app/knowledge:ro
|
||
+KNOWLEDGE_DIR=/app/knowledge
|
||
+# Gesamtes Retrieval-System aktivieren.
|
||
+RAG_ENABLED=true
|
||
+###############################################################################
|
||
+# 12. EXTERNE KNOWLEDGE-KATEGORIEN
|
||
+###############################################################################
|
||
+# Verhalten bei String-/Fremdkategorien, z. B.:
|
||
+#
|
||
+# "AI-Staging"
|
||
+# "Outlook"
|
||
+# "E-Mail"
|
||
+# "Signatur"
|
||
+#
|
||
+# Mögliche Werte:
|
||
+#
|
||
+# unscoped
|
||
+# Artikel bleibt nutzbar.
|
||
+# Fremdkategorien können als Retrieval-Metadaten dienen.
|
||
+#
|
||
+# skip
|
||
+# Artikel mit unbekannten Kategorien überspringen.
|
||
+#
|
||
+# strict
|
||
+# unbekannte Kategorie als Fehler behandeln.
|
||
+#
|
||
+# Für eine gemeinsam mit anderen Anwendungen verwendete KB:
|
||
+# unscoped
|
||
+KNOWLEDGE_CATEGORY_MODE=unscoped
|
||
+# Optionales Mapping von Fremdkategorien auf GLPI-ITIL-Kategorie-IDs.
|
||
+#
|
||
+# Beispiel knowledge-category-map.json:
|
||
+#
|
||
+# {
|
||
+# "Outlook": 12,
|
||
+# "E-Mail": 12,
|
||
+# "Active Directory": 2,
|
||
+# "Security": [20,21]
|
||
+# }
|
||
+KNOWLEDGE_CATEGORY_MAP_FILE=/app/data/knowledge-category-map.json
|
||
+# Optional bestimmte KB-Dateien ignorieren.
|
||
+#
|
||
+# Beispiele:
|
||
+# KB-SEC-ATTCK-*.json
|
||
+# legacy-*.json,external-only-*.json
|
||
+#
|
||
+# Leer:
|
||
+# keine zusätzlichen Ignore-Regeln.
|
||
+KNOWLEDGE_IGNORE_GLOBS=
|
||
+###############################################################################
|
||
+# 13. PERSISTENTER KNOWLEDGE-INDEX
|
||
+###############################################################################
|
||
+# Mögliche Werte:
|
||
+#
|
||
+# incremental
|
||
+# Persistent gespeicherten Index sofort verwenden.
|
||
+# Neue/geänderte Dateien anschließend inkrementell nachziehen.
|
||
+# Für Produktion empfohlen.
|
||
+#
|
||
+# rebuild
|
||
+# vollständigen Index neu erzeugen.
|
||
+#
|
||
+# readonly
|
||
+# nur bestehenden Index verwenden, keine Änderungen übernehmen.
|
||
+KNOWLEDGE_INDEX_MODE=incremental
|
||
+# Anzahl Texte pro Embedding-Batch.
|
||
+KNOWLEDGE_EMBED_BATCH_SIZE=64
|
||
+# Intervall für neue/geänderte/gelöschte Dateien.
|
||
+#
|
||
+# Beispiele:
|
||
+# 30s
|
||
+# 1m
|
||
+# 5m
|
||
+#
|
||
+# 0:
|
||
+# keinen automatischen Hintergrundscan durchführen.
|
||
+KNOWLEDGE_INDEX_SCAN_INTERVAL=5m
|
||
+###############################################################################
|
||
+# 14. RETRIEVAL / DYNAMISCHE KANDIDATENAUSWAHL
|
||
+###############################################################################
|
||
+# Unterhalb dieses Retrieval-Scores wird eine KB nicht als geeigneter
|
||
+# Kandidat betrachtet.
|
||
+#
|
||
+# Der Wert ist KEINE Wahrscheinlichkeit.
|
||
+KNOWLEDGE_RETRIEVAL_FLOOR=0.30
|
||
+# Maximale Differenz zum besten Treffer.
|
||
+#
|
||
+# Beispiel:
|
||
+#
|
||
+# bester Treffer 0.82
|
||
+# MAX_GAP 0.20
|
||
+# dynamischer Cutoff 0.62
|
||
+#
|
||
+# Ein Kandidat mit 0.55 würde dann nicht an die KI gesendet.
|
||
+KNOWLEDGE_CANDIDATE_MAX_GAP=0.20
|
||
+# Maximale Anzahl Knowledge-Kandidaten, die tatsächlich an Ollama gehen.
|
||
+KNOWLEDGE_TOP_K=6
|
||
+# Anzahl Kandidaten für Audit / Diagnose.
|
||
+#
|
||
+# Kann größer als KNOWLEDGE_TOP_K sein.
|
||
+KNOWLEDGE_AUDIT_TOP_K=10
|
||
+###############################################################################
|
||
+# 15. HYBRID-RETRIEVAL - RANKING-GEWICHTE
|
||
+###############################################################################
|
||
+# Die Werte beschreiben die Gewichtung beim KB-Ranking.
|
||
+#
|
||
+# Summe aktuell:
|
||
+# 1.0
|
||
+#
|
||
+# Fehlende Metadaten sollen nicht automatisch negativ bewertet werden.
|
||
+# Embedding-/Chunk-Semantik.
|
||
+KNOWLEDGE_WEIGHT_SEMANTIC=0.45
|
||
+# Ticket-Betreff gegenüber KB-Titel.
|
||
+KNOWLEDGE_WEIGHT_TITLE=0.20
|
||
+# Lexikalische / sprachliche Übereinstimmung.
|
||
+KNOWLEDGE_WEIGHT_LEXICAL=0.20
|
||
+# KB-Keywords.
|
||
+KNOWLEDGE_WEIGHT_KEYWORDS=0.075
|
||
+# Kategorie-/Lernsignal.
|
||
+KNOWLEDGE_WEIGHT_CATEGORY=0.075
|
||
+###############################################################################
|
||
+# 16. FINALE EVIDENZ FÜR AUTO-REPLY
|
||
+###############################################################################
|
||
+# Mindestwert der FINALEN Evidenz.
|
||
+#
|
||
+# WICHTIG:
|
||
+# Das ist nicht der reine Retrieval-Score.
|
||
+#
|
||
+# Die finale Evidenz kombiniert:
|
||
+# - Retrieval
|
||
+# - AI Confidence
|
||
+# - Kategorieübereinstimmung
|
||
+KNOWLEDGE_MIN_SCORE=0.70
|
||
+# Gewicht Retrieval.
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_RETRIEVAL=0.45
|
||
+# Gewicht KI-Auswahl / KI-Confidence.
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_AI=0.35
|
||
+# Gewicht Kategorieübereinstimmung.
|
||
+KNOWLEDGE_EVIDENCE_WEIGHT_CATEGORY=0.20
|
||
+###############################################################################
|
||
+# 17. KNOWLEDGE-CHUNKING
|
||
+###############################################################################
|
||
+# Ungefähre Anzahl Wörter pro Dokument-Chunk.
|
||
+KNOWLEDGE_CHUNK_WORDS=160
|
||
+# Überlappung benachbarter Chunks.
|
||
+KNOWLEDGE_CHUNK_OVERLAP_WORDS=30
|
||
+# Maximale Anzahl Chunks pro KB-Dokument.
|
||
+KNOWLEDGE_MAX_CHUNKS_PER_DOC=24
|
||
+# Maximale Anzahl Query-Chunks bei sehr langen Tickets.
|
||
+KNOWLEDGE_MAX_QUERY_CHUNKS=64
|
||
+# Maximale Anzahl Kategorien im Kategorie-Prompt.
|
||
+CATEGORY_PROMPT_LIMIT=80
|
||
+###############################################################################
|
||
+# 18. KNOWLEDGE-QUELLEN / TRUST POLICY
|
||
+###############################################################################
|
||
+# Quellen für normale Knowledge-Suche und mögliche Antwortkandidaten.
|
||
+# Indexiert wird die Vereinigung mit KNOWLEDGE_CATEGORY_SOURCES.
|
||
+#
|
||
+# Beispiele:
|
||
+# internal-kb
|
||
+# glpi-kb
|
||
+# runbook
|
||
+# vendor-docs
|
||
+KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+# Quellen, die ausschließlich die Kategorieentscheidung unterstützen.
|
||
+# Ohne explizite Angabe wird aus Kompatibilitätsgründen KNOWLEDGE_ALLOWED_SOURCES verwendet.
|
||
+# Mit "none" wird Knowledge-Einfluss auf die Kategorisierung deaktiviert.
|
||
+KNOWLEDGE_CATEGORY_SOURCES=internal-category
|
||
+# Nur diese Quellen dürfen grundsätzlich automatische Antworten liefern.
|
||
+#
|
||
+# Muss eine Teilmenge von KNOWLEDGE_ALLOWED_SOURCES sein.
|
||
+#
|
||
+# Beispiel zum kompletten Abschalten:
|
||
+# KNOWLEDGE_AUTO_REPLY_SOURCES=none
|
||
+KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb,vendor-docs,vendor-docs-ms,vendor-docs-linux,vendor-docs-sec
|
||
+# Webbasierte Bearbeitung von Agent-eigenen Knowledge-Artikeln.
|
||
+#
|
||
+# Diese werden unter:
|
||
+# DATA_DIR/knowledge-managed
|
||
+# gespeichert.
|
||
+#
|
||
+# Das statische KNOWLEDGE_DIR bleibt read-only.
|
||
+KNOWLEDGE_WEB_EDIT_ENABLED=false
|
||
+# Nur Mega-Compose: expliziter Legacy-Schalter für den Agent-eigenen Editor.
|
||
+AGENT_LEGACY_KNOWLEDGE_EDITOR_ENABLED=false
|
||
+###############################################################################
|
||
+# 19. GLPI KNOWLEDGE BASE CONNECTOR
|
||
+###############################################################################
|
||
+# GLPI-interne Knowledge Base synchronisieren.
|
||
+GLPI_KB_ENABLED=true
|
||
+# auto:
|
||
+# Agent ermittelt die KnowbaseItem-Route aus /api.php/doc.json.
|
||
+GLPI_KB_PATH=auto
|
||
+# Optionaler serverseitiger GLPI-Filter.
|
||
+#
|
||
+# Leer:
|
||
+# alle für den Service Account sichtbaren Artikel, begrenzt durch LIMIT.
|
||
+GLPI_KB_FILTER=
|
||
+# Maximale Anzahl GLPI-KB-Artikel.
|
||
+GLPI_KB_LIMIT=500
|
||
+# Synchronisationsintervall.
|
||
+GLPI_KB_SYNC_INTERVAL=10m
|
||
+# source-Wert importierter GLPI-KB-Artikel.
|
||
+GLPI_KB_SOURCE=glpi-kb
|
||
+# true:
|
||
+# GLPI-KB-Artikel können grundsätzlich Auto-Replies auslösen.
|
||
+#
|
||
+# Zusätzlich gelten weiterhin alle anderen Policy-Gates wie Retrieval,
|
||
+# KI-Auswahl, Evidenz, Sprache, Stil und vorhandene Antworten.
|
||
+GLPI_KB_AUTO_REPLY=false
|
||
+# Whitelist der GLPI KNOWLEDGE-BASE-Kategorie-IDs.
|
||
+#
|
||
+# WICHTIG:
|
||
+# Dies sind NICHT die ITIL-/Ticketkategorie-IDs. Ein kategorisierter Artikel
|
||
+# ist genau dann grundsätzlich für Auto-Reply freigegeben, wenn mindestens
|
||
+# eine seiner GLPI-KB-Kategorien hier enthalten ist.
|
||
+#
|
||
+# Mehrere Werte:
|
||
+# 1,2,7
|
||
+GLPI_KB_AUTO_REPLY_CATEGORY_IDS=1
|
||
+# VERALTET / WIRD IGNORIERT:
|
||
+# Ticket-/ITIL-Kategorien geben einen GLPI-Wissensartikel nicht mehr für
|
||
+# Auto-Reply frei. Die Variable bleibt nur erhalten, damit alte .env-Dateien
|
||
+# verständlich migriert werden können. Wert bitte leeren oder entfernen.
|
||
+GLPI_KB_AUTO_REPLY_ITIL_CATEGORY_IDS=
|
||
+# GLPI-KB-Artikel ohne Knowledge-Base-Kategorie bleiben standardmäßig gesperrt.
|
||
+#
|
||
+# true:
|
||
+# Solche Artikel dürfen ausschließlich dann Auto-Reply verwenden, wenn ihre
|
||
+# konkrete GLPI-KnowbaseItem-ID zusätzlich in
|
||
+# GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS steht.
|
||
+GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=false
|
||
+# Explizite GLPI-KnowbaseItem-IDs für unkategorisierte Artikel.
|
||
+# Beispiel: Das synchronisierte Dokument GLPI-KB-1 entspricht Artikel-ID 1.
|
||
+# Diese Liste ist bei ALLOW_UNCATEGORIZED=true verpflichtend.
|
||
+GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS=
|
||
+###############################################################################
|
||
+# 20. HUMAN-IN-THE-LOOP / KATEGORIE-LERNEN
|
||
+###############################################################################
|
||
+# Menschlich bestätigte/korrigierte Entscheidungen als Lernbeispiele verwenden.
|
||
+#
|
||
+# Der Agent lernt NICHT automatisch aus seinen eigenen unbestätigten
|
||
+# Entscheidungen.
|
||
+LEARNING_ENABLED=true
|
||
+# Maximale Anzahl gespeicherter Beispiele.
|
||
+LEARNING_MAX_EXAMPLES=500
|
||
+# Maximale Beispiele pro Kategorie im Prompt.
|
||
+LEARNING_EXAMPLES_PER_CATEGORY=5
|
||
+###############################################################################
|
||
+# 21. KOMMUNIKATIONSPOLICY
|
||
+###############################################################################
|
||
+# Erwartete Sprache von Auto-Reply-KBs.
|
||
+COMMUNICATION_LANGUAGE=de-DE
|
||
+# Erwarteter Kommunikationsstil.
|
||
+COMMUNICATION_STYLE=formal
|
||
+# Wird vor die Knowledge-Antwort gesetzt.
|
||
+COMMUNICATION_SALUTATION=Guten Tag,
|
||
+# Abschluss.
|
||
+COMMUNICATION_CLOSING=Mit freundlichen Grüßen
|
||
+COMMUNICATION_SIGNATURE=IT-Service
|
||
+###############################################################################
|
||
+# 22. OPERATIONAL CONTEXT - GLOBAL
|
||
+###############################################################################
|
||
+# Globaler Schalter für zusätzliche Betriebsinformationen:
|
||
+# - Changes
|
||
+# - Major Incidents
|
||
+# - Requester-Geräte
|
||
+# - Uptime Kuma
|
||
+CONTEXT_ENABLED=true
|
||
+# Timeout für Kontextabfragen.
|
||
+CONTEXT_TIMEOUT=12s
|
||
+# Mindestscore, ab dem Incident/Outage als für das Ticket relevant gilt.
|
||
+CONTEXT_RELEVANCE_MIN_SCORE=0.20
|
||
+# true:
|
||
+# Fehler einer aktivierten Kontextquelle blockieren Auto-Reply.
|
||
+#
|
||
+# Fail-closed und für Produktion empfohlen.
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS=true
|
||
+# true:
|
||
+# relevante zentrale Störung blockiert individuelle Standardantwort.
|
||
+CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT=true
|
||
+###############################################################################
|
||
+# 23. GLPI CHANGE CALENDAR
|
||
+###############################################################################
|
||
+CHANGE_CALENDAR_ENABLED=true
|
||
+# API-Route.
|
||
+GLPI_CHANGE_PATH=/Assistance/Change
|
||
+# Optionaler serverseitiger GLPI-Filter.
|
||
+GLPI_CHANGE_FILTER=
|
||
+# Maximale Anzahl geladener Changes.
|
||
+GLPI_CHANGE_LIMIT=100
|
||
+# Betrachteter Zeitraum in der Vergangenheit.
|
||
+CHANGE_LOOKBACK=72h
|
||
+# Betrachteter Zeitraum in der Zukunft.
|
||
+CHANGE_LOOKAHEAD=24h
|
||
+###############################################################################
|
||
+# 24. MAJOR INCIDENTS
|
||
+###############################################################################
|
||
+# Major Incidents über GLPI-Tickets ermitteln.
|
||
+#
|
||
+# Erst aktivieren, wenn GLPI_MAJOR_INCIDENT_FILTER getestet wurde.
|
||
+MAJOR_INCIDENTS_ENABLED=false
|
||
+# Expliziter Filter für Tickets, die als Major Incident gelten.
|
||
+GLPI_MAJOR_INCIDENT_FILTER=
|
||
+GLPI_MAJOR_INCIDENT_LIMIT=20
|
||
+###############################################################################
|
||
+# 25. REQUESTER -> GERÄT / ASSET CONTEXT
|
||
+###############################################################################
|
||
+# Zusätzlich zu direkt verknüpften Ticket-Assets Geräte des Requesters suchen.
|
||
+USER_DEVICE_CONTEXT_ENABLED=true
|
||
+# Asset-Routen.
|
||
+GLPI_USER_DEVICE_PATHS=/Assets/Computer
|
||
+# {{user_id}} wird vom Agenten ersetzt.
|
||
+GLPI_USER_DEVICE_FILTER_TEMPLATE=user.id=={{user_id}}
|
||
+# Maximale Anzahl Geräte je Suche.
|
||
+GLPI_USER_DEVICE_LIMIT=20
|
||
+###############################################################################
|
||
+# 26. UPTIME KUMA
|
||
+###############################################################################
|
||
+# Globaler Schalter für Uptime-Kuma-Kontext.
|
||
+UPTIME_KUMA_ENABLED=false
|
||
+UPTIME_KUMA_URL=https://uptime.example.com
|
||
+# Mögliche Werte:
|
||
+#
|
||
+# metrics
|
||
+# authentifizierte Prometheus-Metrics.
|
||
+#
|
||
+# status_page
|
||
+# öffentliche/publizierte Statusseiten.
|
||
+UPTIME_KUMA_MODE=metrics
|
||
+# Nur in metrics erforderlich.
|
||
+UPTIME_KUMA_API_KEY=
|
||
+# Nur in status_page erforderlich.
|
||
+#
|
||
+# Mehrere Slugs:
|
||
+# it-services,network,applications
|
||
+UPTIME_KUMA_STATUS_PAGES=it-services
|
||
+UPTIME_KUMA_TIMEOUT=10s
|
||
+# Maximale Anzahl gleichzeitig berücksichtigter Probleme.
|
||
+UPTIME_KUMA_MAX_ISSUES=20
|
||
+# Maintenance ebenfalls als Kontext berücksichtigen.
|
||
+UPTIME_KUMA_INCLUDE_MAINTENANCE=true
|
||
+
|
||
+# Optional: bei eindeutig passender Uptime-Kuma-Störung oder Wartung einen
|
||
+# ausschließlich vom Betreiber vorgegebenen Text senden. Die KI erzeugt keinen
|
||
+# Antworttext; sie wählt nur einen aktiven Kandidaten und liefert eine Confidence.
|
||
+CONTEXT_STATUS_REPLY_ENABLED=false
|
||
+CONTEXT_STATUS_REPLY_MIN_RELEVANCE=0.50
|
||
+CONTEXT_STATUS_REPLY_MIN_AI_CONFIDENCE=0.80
|
||
+# Finaler Score = Relevanz × KI-Confidence.
|
||
+CONTEXT_STATUS_REPLY_MIN_FINAL_SCORE=0.45
|
||
+# Literal \n wird als Zeilenumbruch interpretiert. Verfügbare Platzhalter:
|
||
+# {{service_name}}, {{status}}, {{status_page}}, {{message}},
|
||
+# {{incident_title}}, {{incident_content}}, {{last_heartbeat}}
|
||
+CONTEXT_INCIDENT_REPLY_TEXT=Zu Ihrer Meldung liegt derzeit wahrscheinlich eine zentrale Störung bei {{service_name}} vor. Die Einschränkung kann damit zusammenhängen. Wir beobachten den Status.
|
||
+CONTEXT_MAINTENANCE_REPLY_TEXT=Für {{service_name}} läuft derzeit eine Wartung. Die von Ihnen beschriebene Einschränkung kann damit zusammenhängen. Bitte testen Sie den Dienst nach Abschluss der Wartung erneut.
|
||
+###############################################################################
|
||
+# 27. POLICY-GATES
|
||
+###############################################################################
|
||
+# Automatische Kategorisierung zulassen.
|
||
+AUTO_CATEGORY=true
|
||
+# Automatische Antworten grundsätzlich zulassen.
|
||
+#
|
||
+# DRY_RUN=true verhindert trotzdem das tatsächliche Schreiben nach GLPI.
|
||
+AUTO_REPLY=false
|
||
+# Mindestconfidence der KI für Kategorieänderungen.
|
||
+CATEGORY_CONFIDENCE=0.90
|
||
+# Mindestconfidence der KI für Antwortauswahl.
|
||
+#
|
||
+# Dies allein reicht NICHT für Auto-Reply.
|
||
+# Zusätzlich gelten unter anderem:
|
||
+#
|
||
+# - Knowledge-Evidenz
|
||
+# - Retrieval-Regeln
|
||
+# - Source Policy
|
||
+# - KB auto_reply
|
||
+# - Kommunikationspolicy
|
||
+# - Followup-Prüfung
|
||
+# - Kontext-/Incident-Regeln
|
||
+# - zweite Followup-Prüfung unmittelbar vor dem Schreiben
|
||
+REPLY_CONFIDENCE=0.97
|
||
+###############################################################################
|
||
+# 28. KI-PRIORISIERUNG
|
||
+###############################################################################
|
||
+# Separater KI-Lauf zur Empfehlung der GLPI-Priorität. Der Lauf wird im
|
||
+# Diagnose-Cockpit unabhängig von Kategorie, Status und Antwort gespeichert.
|
||
+PRIORITY_ENABLED=true
|
||
+# Standardmäßig Shadow Mode: Empfehlung und Policy-Gates werden protokolliert,
|
||
+# GLPI wird nicht verändert. Für Live-Schreibzugriffe zusätzlich DRY_RUN=false.
|
||
+AUTO_PRIORITY=false
|
||
+PRIORITY_CONFIDENCE=0.88
|
||
+# Eigener Fail-open-Timeout für diesen optionalen KI-Lauf. Kategorie und Antwort laufen danach weiter.
|
||
+PRIORITY_ANALYSIS_TIMEOUT=45s
|
||
+# Automatische Erhöhung je Ticketlauf; Herabstufungen sind grundsätzlich gesperrt.
|
||
+PRIORITY_MAX_INCREASE=1
|
||
+# Nur kontrollierte, kommaseparierte Grundcodes dürfen eine Empfehlung tragen.
|
||
+PRIORITY_ALLOWED_REASON_CODES=multiple_users_affected,site_affected,organization_affected,core_service_unavailable,security_incident_suspected,data_loss_possible,legal_or_regulatory_risk,business_deadline,no_workaround,safety_relevant,exam_or_event_critical
|
||
+###############################################################################
|
||
+# 29. ZEITGESTEUERTE KI-ESKALATION
|
||
+###############################################################################
|
||
+# Unabhängiger Scheduler. Er prüft offene Tickets auch ohne Änderung von date_mod.
|
||
+ESCALATION_ENABLED=false
|
||
+# Standardmäßig werden nur Diagnose-/Shadow-Läufe erzeugt.
|
||
+# Live-Ausführung benötigt zusätzlich DRY_RUN=false und GLPI_AGENT_USER_ID.
|
||
+AUTO_ESCALATION=false
|
||
+ESCALATION_SCAN_INTERVAL=15m
|
||
+# Mindestalter des Tickets seit date_creation, bevor es in den Eskalationsscan gelangt.
|
||
+ESCALATION_MIN_AGE=4h
|
||
+# Mindestdauer seit der letzten menschlichen Aktivität für den Grund
|
||
+# no_human_response. SLA-, Security- und Major-Incident-Gründe können unabhängig
|
||
+# davon greifen. Agent-Followups werden über GLPI_AGENT_USER_ID ausgenommen.
|
||
+ESCALATION_MIN_INACTIVITY=2h
|
||
+# Eigenes KI-Zeitbudget; blockiert die normalen Ticketläufe nicht unbegrenzt.
|
||
+ESCALATION_ANALYSIS_TIMEOUT=45s
|
||
+ESCALATION_CONFIDENCE=0.88
|
||
+ESCALATION_MAX_LEVEL=3
|
||
+# Zeitfenster vor time_to_resolve, in dem sla_at_risk deterministisch wahr wird.
|
||
+ESCALATION_SLA_RISK_WINDOW=2h
|
||
+# Aktionsspezifische Mindeststufen.
|
||
+ESCALATION_SERVICE_OWNER_MIN_LEVEL=2
|
||
+ESCALATION_MANAGER_REVIEW_MIN_LEVEL=3
|
||
+# Mindest-Relevanz eines vom Kontextkollektor gelieferten Major Incidents.
|
||
+ESCALATION_MAJOR_INCIDENT_MIN_RELEVANCE=0.50
|
||
+ESCALATION_ALLOWED_REASON_CODES=no_human_response,sla_at_risk,sla_breached,business_deadline,no_workaround,security_incident_suspected,unassigned,major_incident_candidate
|
||
+# Jede Aktion muss einzeln freigegeben werden. Sichere Einführung: zunächst nur
|
||
+# none,raise_priority; weitere Aktionen erst nach Konfiguration der Ziele aktivieren.
|
||
+# Verfügbar: none,raise_priority,assign_second_level,assign_security_team,
|
||
+# notify_service_owner,link_major_incident,request_manager_review
|
||
+ESCALATION_ALLOWED_ACTIONS=none,raise_priority
|
||
+
|
||
+# Zielgruppen/-benutzer für Zuweisungs- und Benachrichtigungsaktionen.
|
||
+# Es handelt sich um numerische GLPI-IDs.
|
||
+ESCALATION_SECOND_LEVEL_GROUP_ID=0
|
||
+ESCALATION_SECURITY_GROUP_ID=0
|
||
+ESCALATION_SERVICE_OWNER_GROUP_ID=0
|
||
+ESCALATION_SERVICE_OWNER_USER_ID=0
|
||
+ESCALATION_MANAGER_REVIEW_GROUP_ID=0
|
||
+ESCALATION_MANAGER_REVIEW_USER_ID=0
|
||
+
|
||
+# Zu jeder ausgeführten Aktion kann ein privater GLPI-Followup geschrieben werden.
|
||
+ESCALATION_ADD_PRIVATE_FOLLOWUP=true
|
||
+# Platzhalter: {{ticket_id}}, {{ticket_name}}, {{level}}, {{action}}, {{reason}},
|
||
+# {{reason_codes}}, {{major_incident_id}}, {{major_incident_name}},
|
||
+# {{major_incident_score}}.
|
||
+# Literal \n wird in Template-Werten als Zeilenumbruch interpretiert.
|
||
+ESCALATION_SECOND_LEVEL_NOTE=Automatische Eskalation Stufe {{level}}: Übergabe an den Second-Level-Support. Gründe: {{reason_codes}}. KI-Begründung: {{reason}}
|
||
+ESCALATION_SECURITY_NOTE=Automatische Eskalation Stufe {{level}}: Übergabe an das Security-Team. Gründe: {{reason_codes}}. KI-Begründung: {{reason}}
|
||
+ESCALATION_SERVICE_OWNER_NOTE=Automatische Eskalation Stufe {{level}}: Service Owner wurde zur Prüfung einbezogen. Gründe: {{reason_codes}}. KI-Begründung: {{reason}}
|
||
+ESCALATION_MAJOR_INCIDENT_NOTE="Automatische Eskalation Stufe {{level}}: Verknüpfung mit Major Incident #{{major_incident_id}} ({{major_incident_name}}). Relevanz: {{major_incident_score}}. Gründe: {{reason_codes}}."
|
||
+ESCALATION_MANAGER_REVIEW_NOTE=Automatische Eskalation Stufe {{level}}: Management-Review angefordert. Gründe: {{reason_codes}}. KI-Begründung: {{reason}}
|
||
+
|
||
+# Optionaler ausgehender Webhook für Service-Owner- und Management-Benachrichtigungen.
|
||
+# Das Token wird nie über die Status-API ausgegeben.
|
||
+ESCALATION_WEBHOOK_URL=
|
||
+ESCALATION_WEBHOOK_BEARER_TOKEN=
|
||
+ESCALATION_WEBHOOK_TIMEOUT=10s
|
||
+# Nur für isolierte Testnetze; HTTPS ist der sichere Standard.
|
||
+ESCALATION_WEBHOOK_ALLOW_INSECURE_HTTP=false
|
||
+
|
||
+# GLPI-Adapter für Zuweisungen. Die Feldnamen müssen zur OpenAPI-Beschreibung der
|
||
+# konkreten GLPI-Installation passen. Unterstützte Payload-Formen:
|
||
+# assigned_groups/assigned_users = Liste von {"id":...};
|
||
+# group/group_tech/user/user_tech = einzelnes {"id":...}.
|
||
+GLPI_ESCALATION_GROUP_PATCH_FIELD=assigned_groups
|
||
+GLPI_ESCALATION_USER_PATCH_FIELD=assigned_users
|
||
+
|
||
+# Installationsspezifischer Adapter für link_major_incident. Beide Werte sind
|
||
+# erforderlich. Platzhalter im Pfad/JSON: {{ticket_id}}, {{source_ticket_id}},
|
||
+# {{major_incident_id}}, {{target_ticket_id}}.
|
||
+GLPI_ESCALATION_ITIL_LINK_PATH=
|
||
+GLPI_ESCALATION_ITIL_LINK_BODY=
|
||
+
|
||
+# Leer = GLPI_TICKET_FILTER verwenden. Für Produktion ausdrücklich auf offene,
|
||
+# eskalierbare Status und die gewünschte Einheit beschränken.
|
||
+GLPI_ESCALATION_FILTER=
|
||
+GLPI_ESCALATION_LIMIT=100
|
||
+###############################################################################
|
||
+# 30. WORKER / PRIORITÄTSQUEUE
|
||
+###############################################################################
|
||
+# Maximale Anzahl wartender Jobs.
|
||
+QUEUE_SIZE=256
|
||
+# Parallele Ticket-Worker. Der Ollama-Pool kann nur so viele unabhängige
|
||
+# Ticketpipelines gleichzeitig verteilen, wie Worker aktiv sind. Für drei
|
||
+# gleichartige Nodes ist WORKERS=3 ein sinnvoller Lasttest; jeder Node bleibt
|
||
+# zusätzlich durch OLLAMA_NODE_MAX_INFLIGHT begrenzt.
|
||
+WORKERS=2
|
||
+
|
||
+###############################################################################
|
||
+# 31. DISTRIBUTED MASTER / MONITORING
|
||
+###############################################################################
|
||
+NEUROFORGE_BIND_ADDRESS=0.0.0.0
|
||
+PROMETHEUS_IMAGE=prom/prometheus:v3.14.0
|
||
+GRAFANA_IMAGE=grafana/grafana:13.2.0
|
||
+NODE_EXPORTER_IMAGE=prom/node-exporter:v1.12.1
|
||
+PROMETHEUS_HOST_PORT=9090
|
||
+PROMETHEUS_RETENTION=30d
|
||
+GRAFANA_HOST_PORT=3000
|
||
+GRAFANA_ADMIN_USER=admin
|
||
+GRAFANA_ADMIN_PASSWORD=CHANGE_ME_GRAFANA_ADMIN_PASSWORD
|
||
+GRAFANA_ROOT_URL=http://localhost:3000
|
||
+# Set to the real subagent host/IP. Used only by Prometheus host telemetry.
|
||
+CPU_SUBAGENT_NODE_EXPORTER_TARGET=192.0.2.21:9100
|
||
+GPU_SUBAGENT_NODE_EXPORTER_TARGET=192.0.2.22:9100
|
||
+GPU_SUBAGENT_DCGM_EXPORTER_TARGET=192.0.2.22:9400
|
||
diff --git ab/deployments/master/docker-compose.yml b/deployments/master/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..9bd0c2b
|
||
--- /dev/null
|
||
+++ b/deployments/master/docker-compose.yml
|
||
@@ -0,0 +1,594 @@
|
||
+name: glpi-neuroforge-master
|
||
+services:
|
||
+ searxng:
|
||
+ image: ${SEARXNG_IMAGE:-docker.io/searxng/searxng:latest}
|
||
+ profiles:
|
||
+ - research
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ SEARXNG_SECRET: ${SEARXNG_SECRET}
|
||
+ SEARXNG_BASE_URL: http://searxng:8080/
|
||
+ FORCE_OWNERSHIP: 'false'
|
||
+ volumes:
|
||
+ - ./deploy/searxng/settings.yml:/etc/searxng/settings.yml:ro
|
||
+ - searxng-cache:/var/cache/searxng
|
||
+ ports:
|
||
+ - 127.0.0.1:${SEARXNG_HOST_PORT:-8888}:8080
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=64m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ neuroforge:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG
|
||
+ to an immutable release tag, for example 1.6.2}
|
||
+ command:
|
||
+ - -data
|
||
+ - /app/data
|
||
+ - -listen
|
||
+ - :8080
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ NEUROFORGE_ADMIN_TOKEN: ${NEUROFORGE_ADMIN_TOKEN:?Set a unique NeuroForge admin
|
||
+ token}
|
||
+ NEUROFORGE_APP_API_KEY: ${NEUROFORGE_APP_API_KEY:?Set a unique NeuroForge app
|
||
+ API key}
|
||
+ NEUROFORGE_INTEGRATION_TOKEN: ${NEUROFORGE_INTEGRATION_TOKEN:?Set a unique NeuroForge
|
||
+ integration token}
|
||
+ NEUROFORGE_CONTROL_READ_TOKEN: ${NEUROFORGE_CONTROL_READ_TOKEN:?Set a unique
|
||
+ NeuroForge control read token}
|
||
+ NEUROFORGE_WORKER_TOKEN: ${NEUROFORGE_WORKER_TOKEN:?Set a unique NeuroForge
|
||
+ worker token}
|
||
+ NEUROFORGE_METRICS_TOKEN: ${NEUROFORGE_METRICS_TOKEN:?Set a unique NeuroForge
|
||
+ metrics token}
|
||
+ NEUROFORGE_CLUSTER_TOKEN: ${NEUROFORGE_CLUSTER_TOKEN:-}
|
||
+ OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||
+ NEUROFORGE_OLLAMA_URL: ${OLLAMA_BASE_URL:?Set Ollama URL reachable from master,
|
||
+ normally GPU subagent host}
|
||
+ NEUROFORGE_OLLAMA_API_KEY: ${NEUROFORGE_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ NEUROFORGE_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
+ NEUROFORGE_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
+ NEUROFORGE_OLLAMA_NUM_CTX: ${NEUROFORGE_OLLAMA_NUM_CTX:-8192}
|
||
+ NEUROFORGE_OLLAMA_NUM_PREDICT: ${NEUROFORGE_OLLAMA_NUM_PREDICT:-0}
|
||
+ NEUROFORGE_WORKER_LEASE_SECONDS: ${NEUROFORGE_WORKER_LEASE_SECONDS:-120}
|
||
+ NEUROFORGE_WORKER_HEARTBEAT_SECONDS: ${NEUROFORGE_WORKER_HEARTBEAT_SECONDS:-15}
|
||
+ NEUROFORGE_WORKER_STALE_AFTER_SECONDS: ${NEUROFORGE_WORKER_STALE_AFTER_SECONDS:-60}
|
||
+ NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_DEFAULT_MAX_ATTEMPTS:-3}
|
||
+ NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_RETRY_BACKOFF_SECONDS:-15}
|
||
+ NEUROFORGE_WORKER_MAX_QUEUED_JOBS: ${NEUROFORGE_WORKER_MAX_QUEUED_JOBS:-5000}
|
||
+ NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB: ${NEUROFORGE_WORKER_MAX_QUEUED_PAYLOAD_MB:-128}
|
||
+ NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS: ${NEUROFORGE_WORKER_MASTER_APPLY_MAX_ATTEMPTS:-5}
|
||
+ NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS: ${NEUROFORGE_WORKER_MASTER_APPLY_BACKOFF_SECONDS:-5}
|
||
+ NEUROFORGE_WORKER_JOB_RETENTION_HOURS: ${NEUROFORGE_WORKER_JOB_RETENTION_HOURS:-24}
|
||
+ NEUROFORGE_WORKER_MAX_TERMINAL_JOBS: ${NEUROFORGE_WORKER_MAX_TERMINAL_JOBS:-2000}
|
||
+ NEUROFORGE_GRAPH_BACKFILL_ENABLED: ${NEUROFORGE_GRAPH_BACKFILL_ENABLED:-true}
|
||
+ NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS: ${NEUROFORGE_GRAPH_BACKFILL_INTERVAL_SECONDS:-10}
|
||
+ NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE: ${NEUROFORGE_GRAPH_BACKFILL_BATCH_SIZE:-16}
|
||
+ NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED: ${NEUROFORGE_GRAPH_BACKFILL_MAX_QUEUED:-64}
|
||
+ NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE: ${NEUROFORGE_GRAPH_BACKFILL_MIN_DEGREE:-3}
|
||
+ NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER: ${NEUROFORGE_GRAPH_CANDIDATE_MULTIPLIER:-6}
|
||
+ NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES: ${NEUROFORGE_GRAPH_RETRY_AFTER_MINUTES:-360}
|
||
+ NEUROFORGE_GRAPH_REQUIRE_WORKER: ${NEUROFORGE_GRAPH_REQUIRE_WORKER:-true}
|
||
+ NEUROFORGE_GRAPH_MAX_HOPS: ${NEUROFORGE_GRAPH_MAX_HOPS:-3}
|
||
+ NEUROFORGE_GRAPH_HOP_DECAY: ${NEUROFORGE_GRAPH_HOP_DECAY:-0.60}
|
||
+ NEUROFORGE_GRAPH_MAX_EXPANSION: ${NEUROFORGE_GRAPH_MAX_EXPANSION:-64}
|
||
+ NEUROFORGE_GRAPH_MIN_EDGE_WEIGHT: ${NEUROFORGE_GRAPH_MIN_EDGE_WEIGHT:-0.05}
|
||
+ NEUROFORGE_OFFLOAD_CHAT: ${NEUROFORGE_OFFLOAD_CHAT:-true}
|
||
+ NEUROFORGE_OFFLOAD_EMBEDDINGS: ${NEUROFORGE_OFFLOAD_EMBEDDINGS:-true}
|
||
+ NEUROFORGE_DISTRIBUTED_INFERENCE_WAIT_SECONDS: ${NEUROFORGE_DISTRIBUTED_INFERENCE_WAIT_SECONDS:-180}
|
||
+ NEUROFORGE_READINESS_OLLAMA_LIVE: ${NEUROFORGE_READINESS_OLLAMA_LIVE:-true}
|
||
+ NEUROFORGE_CONTROLLED_LEARNING: ${NEUROFORGE_CONTROLLED_LEARNING:-true}
|
||
+ NEUROFORGE_GOAL_LEARNING_ENABLED: ${NEUROFORGE_GOAL_LEARNING_ENABLED:-false}
|
||
+ NEUROFORGE_RESEARCH_ENABLED: ${NEUROFORGE_RESEARCH_ENABLED:-false}
|
||
+ NEUROFORGE_SEARXNG_ENABLED: ${NEUROFORGE_SEARXNG_ENABLED:-false}
|
||
+ NEUROFORGE_SEARXNG_URL: ${NEUROFORGE_SEARXNG_URL:-http://searxng:8080}
|
||
+ NEUROFORGE_RESEARCH_GOAL_ENABLED: ${NEUROFORGE_RESEARCH_GOAL_ENABLED:-true}
|
||
+ NEUROFORGE_AUTONOMY_ENABLED: ${NEUROFORGE_AUTONOMY_ENABLED:-false}
|
||
+ NEUROFORGE_AUTONOMY_INTERVAL_MINUTES: ${NEUROFORGE_AUTONOMY_INTERVAL_MINUTES:-30}
|
||
+ NEUROFORGE_RESEARCH_MAX_QUERIES: ${NEUROFORGE_RESEARCH_MAX_QUERIES:-2}
|
||
+ NEUROFORGE_RESEARCH_MAX_PAGES: ${NEUROFORGE_RESEARCH_MAX_PAGES:-4}
|
||
+ NEUROFORGE_KB_STAGING_ENABLED: ${NEUROFORGE_KB_STAGING_ENABLED:-true}
|
||
+ NEUROFORGE_KB_STAGING_URL: http://knowledge:8080/api/integrations/staging
|
||
+ NEUROFORGE_KB_STAGING_TOKEN: ${KB_INTEGRATION_TOKEN:?Set the Knowledge integration
|
||
+ token}
|
||
+ NEUROFORGE_KB_STAGING_MIN_EVIDENCE: ${NEUROFORGE_KB_STAGING_MIN_EVIDENCE:-4}
|
||
+ NEUROFORGE_KB_STAGING_MIN_SOURCES: ${NEUROFORGE_KB_STAGING_MIN_SOURCES:-2}
|
||
+ NEUROFORGE_KB_STAGING_MIN_CORROBORATIONS: ${NEUROFORGE_KB_STAGING_MIN_CORROBORATIONS:-0}
|
||
+ NEUROFORGE_KB_STAGING_MAX_EVIDENCE: ${NEUROFORGE_KB_STAGING_MAX_EVIDENCE:-12}
|
||
+ NEUROFORGE_KB_STAGING_SYNTHESIS_MODE: ${NEUROFORGE_KB_STAGING_SYNTHESIS_MODE:-llm}
|
||
+ NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_SOURCE: ${NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_SOURCE:-true}
|
||
+ NEUROFORGE_KB_STAGING_MIN_AUTHORITATIVE_SOURCES: ${NEUROFORGE_KB_STAGING_MIN_AUTHORITATIVE_SOURCES:-1}
|
||
+ NEUROFORGE_KB_STAGING_AUTHORITATIVE_DOMAINS: ${NEUROFORGE_KB_STAGING_AUTHORITATIVE_DOMAINS:-}
|
||
+ NEUROFORGE_KB_STAGING_VERIFY_CLAIMS: ${NEUROFORGE_KB_STAGING_VERIFY_CLAIMS:-true}
|
||
+ NEUROFORGE_KB_STAGING_MIN_CLAIM_COVERAGE: ${NEUROFORGE_KB_STAGING_MIN_CLAIM_COVERAGE:-1.0}
|
||
+ NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_ACTIONS: ${NEUROFORGE_KB_STAGING_REQUIRE_AUTHORITATIVE_ACTIONS:-true}
|
||
+ NEUROFORGE_KB_STAGING_MAX_VERIFICATION_STATEMENTS: ${NEUROFORGE_KB_STAGING_MAX_VERIFICATION_STATEMENTS:-32}
|
||
+ NEUROFORGE_KB_STAGING_VERIFICATION_REPAIR: ${NEUROFORGE_KB_STAGING_VERIFICATION_REPAIR:-true}
|
||
+ NEUROFORGE_KB_STAGING_SYNTHESIS_MAX_TOKENS: ${NEUROFORGE_KB_STAGING_SYNTHESIS_MAX_TOKENS:-2600}
|
||
+ NEUROFORGE_KB_STAGING_EVIDENCE_PROMPT_MAX_CHARS: ${NEUROFORGE_KB_STAGING_EVIDENCE_PROMPT_MAX_CHARS:-14000}
|
||
+ NEUROFORGE_KB_STAGING_MIN_ARTICLE_CHARS: ${NEUROFORGE_KB_STAGING_MIN_ARTICLE_CHARS:-3500}
|
||
+ NEUROFORGE_KB_STAGING_TARGET_ARTICLE_CHARS: ${NEUROFORGE_KB_STAGING_TARGET_ARTICLE_CHARS:-6500}
|
||
+ NEUROFORGE_KB_STAGING_MAX_ARTICLE_CHARS: ${NEUROFORGE_KB_STAGING_MAX_ARTICLE_CHARS:-10000}
|
||
+ NEUROFORGE_KB_STAGING_MIN_ANSWER_CHARS: ${NEUROFORGE_KB_STAGING_MIN_ANSWER_CHARS:-160}
|
||
+ NEUROFORGE_KB_STAGING_MAX_ANSWER_CHARS: ${NEUROFORGE_KB_STAGING_MAX_ANSWER_CHARS:-1200}
|
||
+ ports:
|
||
+ - ${NEUROFORGE_BIND_ADDRESS:-0.0.0.0}:${NEUROFORGE_HOST_PORT:-8090}:8080
|
||
+ volumes:
|
||
+ - neuroforge-data:/app/data
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=64m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ healthcheck:
|
||
+ test:
|
||
+ - CMD
|
||
+ - wget
|
||
+ - -q
|
||
+ - -O
|
||
+ - '-'
|
||
+ - http://127.0.0.1:8080/readyz
|
||
+ interval: 15s
|
||
+ timeout: 3s
|
||
+ retries: 8
|
||
+ start_period: 15s
|
||
+ stop_grace_period: 35s
|
||
+ agent-data-init:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set
|
||
+ IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
+ restart: 'no'
|
||
+ user: 0:0
|
||
+ volumes:
|
||
+ - agent-data:/app/data
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ cap_add:
|
||
+ - CHOWN
|
||
+ - FOWNER
|
||
+ agent:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG
|
||
+ to an immutable release tag, for example 1.6.2}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ AI_CONTENT_LABEL_ENABLED: ${AI_CONTENT_LABEL_ENABLED:-}
|
||
+ AUTO_CATEGORY: ${AUTO_CATEGORY:-}
|
||
+ AUTO_ESCALATION: ${AUTO_ESCALATION:-}
|
||
+ AUTO_PRIORITY: ${AUTO_PRIORITY:-}
|
||
+ AUTO_REPLY: ${AUTO_REPLY:-}
|
||
+ BRAIN_ACTIVITY_API_KEY: ${NEUROFORGE_INTEGRATION_TOKEN:?Set the NeuroForge integration
|
||
+ token}
|
||
+ BRAIN_ACTIVITY_URL: http://neuroforge:8080/api/v1/integrations/events
|
||
+ CATEGORY_CONFIDENCE: ${CATEGORY_CONFIDENCE:-}
|
||
+ CATEGORY_PROMPT_LIMIT: ${CATEGORY_PROMPT_LIMIT:-}
|
||
+ CHANGE_CALENDAR_ENABLED: ${CHANGE_CALENDAR_ENABLED:-}
|
||
+ CHANGE_LOOKAHEAD: ${CHANGE_LOOKAHEAD:-}
|
||
+ CHANGE_LOOKBACK: ${CHANGE_LOOKBACK:-}
|
||
+ COMMUNICATION_CLOSING: ${COMMUNICATION_CLOSING:-}
|
||
+ COMMUNICATION_LANGUAGE: ${COMMUNICATION_LANGUAGE:-}
|
||
+ COMMUNICATION_SALUTATION: ${COMMUNICATION_SALUTATION:-}
|
||
+ COMMUNICATION_SIGNATURE: ${COMMUNICATION_SIGNATURE:-}
|
||
+ COMMUNICATION_STYLE: ${COMMUNICATION_STYLE:-}
|
||
+ CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS: ${CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS:-}
|
||
+ CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT: ${CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT:-}
|
||
+ CONTEXT_ENABLED: ${CONTEXT_ENABLED:-}
|
||
+ CONTEXT_INCIDENT_REPLY_TEXT: ${CONTEXT_INCIDENT_REPLY_TEXT:-}
|
||
+ CONTEXT_MAINTENANCE_REPLY_TEXT: ${CONTEXT_MAINTENANCE_REPLY_TEXT:-}
|
||
+ CONTEXT_RELEVANCE_MIN_SCORE: ${CONTEXT_RELEVANCE_MIN_SCORE:-}
|
||
+ CONTEXT_STATUS_REPLY_ENABLED: ${CONTEXT_STATUS_REPLY_ENABLED:-}
|
||
+ CONTEXT_STATUS_REPLY_MIN_AI_CONFIDENCE: ${CONTEXT_STATUS_REPLY_MIN_AI_CONFIDENCE:-}
|
||
+ CONTEXT_STATUS_REPLY_MIN_FINAL_SCORE: ${CONTEXT_STATUS_REPLY_MIN_FINAL_SCORE:-}
|
||
+ CONTEXT_STATUS_REPLY_MIN_RELEVANCE: ${CONTEXT_STATUS_REPLY_MIN_RELEVANCE:-}
|
||
+ CONTEXT_TIMEOUT: ${CONTEXT_TIMEOUT:-}
|
||
+ CONTROL_READ_TOKEN: ${CONTROL_READ_TOKEN:?Set the Agent control read token}
|
||
+ DATA_DIR: /app/data
|
||
+ DRY_RUN: ${DRY_RUN:-}
|
||
+ ESCALATION_ADD_PRIVATE_FOLLOWUP: ${ESCALATION_ADD_PRIVATE_FOLLOWUP:-}
|
||
+ ESCALATION_ALLOWED_ACTIONS: ${ESCALATION_ALLOWED_ACTIONS:-}
|
||
+ ESCALATION_ALLOWED_REASON_CODES: ${ESCALATION_ALLOWED_REASON_CODES:-}
|
||
+ ESCALATION_ANALYSIS_TIMEOUT: ${ESCALATION_ANALYSIS_TIMEOUT:-}
|
||
+ ESCALATION_CONFIDENCE: ${ESCALATION_CONFIDENCE:-}
|
||
+ ESCALATION_ENABLED: ${ESCALATION_ENABLED:-}
|
||
+ ESCALATION_MAJOR_INCIDENT_MIN_RELEVANCE: ${ESCALATION_MAJOR_INCIDENT_MIN_RELEVANCE:-}
|
||
+ ESCALATION_MAJOR_INCIDENT_NOTE: ${ESCALATION_MAJOR_INCIDENT_NOTE:-}
|
||
+ ESCALATION_MANAGER_REVIEW_GROUP_ID: ${ESCALATION_MANAGER_REVIEW_GROUP_ID:-}
|
||
+ ESCALATION_MANAGER_REVIEW_MIN_LEVEL: ${ESCALATION_MANAGER_REVIEW_MIN_LEVEL:-}
|
||
+ ESCALATION_MANAGER_REVIEW_NOTE: ${ESCALATION_MANAGER_REVIEW_NOTE:-}
|
||
+ ESCALATION_MANAGER_REVIEW_USER_ID: ${ESCALATION_MANAGER_REVIEW_USER_ID:-}
|
||
+ ESCALATION_MAX_LEVEL: ${ESCALATION_MAX_LEVEL:-}
|
||
+ ESCALATION_MIN_AGE: ${ESCALATION_MIN_AGE:-}
|
||
+ ESCALATION_MIN_INACTIVITY: ${ESCALATION_MIN_INACTIVITY:-}
|
||
+ ESCALATION_SCAN_INTERVAL: ${ESCALATION_SCAN_INTERVAL:-}
|
||
+ ESCALATION_SECOND_LEVEL_GROUP_ID: ${ESCALATION_SECOND_LEVEL_GROUP_ID:-}
|
||
+ ESCALATION_SECOND_LEVEL_NOTE: ${ESCALATION_SECOND_LEVEL_NOTE:-}
|
||
+ ESCALATION_SECURITY_GROUP_ID: ${ESCALATION_SECURITY_GROUP_ID:-}
|
||
+ ESCALATION_SECURITY_NOTE: ${ESCALATION_SECURITY_NOTE:-}
|
||
+ ESCALATION_SERVICE_OWNER_GROUP_ID: ${ESCALATION_SERVICE_OWNER_GROUP_ID:-}
|
||
+ ESCALATION_SERVICE_OWNER_MIN_LEVEL: ${ESCALATION_SERVICE_OWNER_MIN_LEVEL:-}
|
||
+ ESCALATION_SERVICE_OWNER_NOTE: ${ESCALATION_SERVICE_OWNER_NOTE:-}
|
||
+ ESCALATION_SERVICE_OWNER_USER_ID: ${ESCALATION_SERVICE_OWNER_USER_ID:-}
|
||
+ ESCALATION_SLA_RISK_WINDOW: ${ESCALATION_SLA_RISK_WINDOW:-}
|
||
+ ESCALATION_WEBHOOK_ALLOW_INSECURE_HTTP: ${ESCALATION_WEBHOOK_ALLOW_INSECURE_HTTP:-}
|
||
+ ESCALATION_WEBHOOK_BEARER_TOKEN: ${ESCALATION_WEBHOOK_BEARER_TOKEN:-}
|
||
+ ESCALATION_WEBHOOK_TIMEOUT: ${ESCALATION_WEBHOOK_TIMEOUT:-}
|
||
+ ESCALATION_WEBHOOK_URL: ${ESCALATION_WEBHOOK_URL:-}
|
||
+ GLPI_AGENT_USER_ID: ${GLPI_AGENT_USER_ID:-}
|
||
+ GLPI_ALLOWED_STATUS_IDS: ${GLPI_ALLOWED_STATUS_IDS:-}
|
||
+ GLPI_ALLOW_INSECURE_HTTP: ${GLPI_ALLOW_INSECURE_HTTP:-}
|
||
+ GLPI_API_VERSION: ${GLPI_API_VERSION:-}
|
||
+ GLPI_CHANGE_FILTER: ${GLPI_CHANGE_FILTER:-}
|
||
+ GLPI_CHANGE_LIMIT: ${GLPI_CHANGE_LIMIT:-}
|
||
+ GLPI_CHANGE_PATH: ${GLPI_CHANGE_PATH:-}
|
||
+ GLPI_CLIENT_ID: ${GLPI_CLIENT_ID:?Set GLPI_CLIENT_ID}
|
||
+ GLPI_CLIENT_SECRET: ${GLPI_CLIENT_SECRET:?Set GLPI_CLIENT_SECRET}
|
||
+ GLPI_ESCALATION_FILTER: ${GLPI_ESCALATION_FILTER:-}
|
||
+ GLPI_ESCALATION_GROUP_PATCH_FIELD: ${GLPI_ESCALATION_GROUP_PATCH_FIELD:-}
|
||
+ GLPI_ESCALATION_ITIL_LINK_BODY: ${GLPI_ESCALATION_ITIL_LINK_BODY:-}
|
||
+ GLPI_ESCALATION_ITIL_LINK_PATH: ${GLPI_ESCALATION_ITIL_LINK_PATH:-}
|
||
+ GLPI_ESCALATION_LIMIT: ${GLPI_ESCALATION_LIMIT:-}
|
||
+ GLPI_ESCALATION_USER_PATCH_FIELD: ${GLPI_ESCALATION_USER_PATCH_FIELD:-}
|
||
+ GLPI_KB_AUTO_REPLY: ${GLPI_KB_AUTO_REPLY:-}
|
||
+ GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED: ${GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED:-}
|
||
+ GLPI_KB_AUTO_REPLY_CATEGORY_IDS: ${GLPI_KB_AUTO_REPLY_CATEGORY_IDS:-}
|
||
+ GLPI_KB_AUTO_REPLY_ITIL_CATEGORY_IDS: ${GLPI_KB_AUTO_REPLY_ITIL_CATEGORY_IDS:-}
|
||
+ GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS: ${GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS:-}
|
||
+ GLPI_KB_ENABLED: ${GLPI_KB_ENABLED:-}
|
||
+ GLPI_KB_FILTER: ${GLPI_KB_FILTER:-}
|
||
+ GLPI_KB_LIMIT: ${GLPI_KB_LIMIT:-}
|
||
+ GLPI_KB_PATH: ${GLPI_KB_PATH:-}
|
||
+ GLPI_KB_SOURCE: ${GLPI_KB_SOURCE:-}
|
||
+ GLPI_KB_SYNC_INTERVAL: ${GLPI_KB_SYNC_INTERVAL:-}
|
||
+ GLPI_MAJOR_INCIDENT_FILTER: ${GLPI_MAJOR_INCIDENT_FILTER:-}
|
||
+ GLPI_MAJOR_INCIDENT_LIMIT: ${GLPI_MAJOR_INCIDENT_LIMIT:-}
|
||
+ GLPI_PASSWORD: ${GLPI_PASSWORD:?Set GLPI_PASSWORD}
|
||
+ GLPI_POLL_INTERVAL: ${GLPI_POLL_INTERVAL:-}
|
||
+ GLPI_POLL_LIMIT: ${GLPI_POLL_LIMIT:-}
|
||
+ GLPI_TICKET_FILTER: ${GLPI_TICKET_FILTER:-}
|
||
+ GLPI_TIMEOUT: ${GLPI_TIMEOUT:-}
|
||
+ GLPI_URL: ${GLPI_URL:?Set GLPI_URL}
|
||
+ GLPI_USERNAME: ${GLPI_USERNAME:?Set GLPI_USERNAME}
|
||
+ GLPI_USER_DEVICE_FILTER_TEMPLATE: ${GLPI_USER_DEVICE_FILTER_TEMPLATE:-}
|
||
+ GLPI_USER_DEVICE_LIMIT: ${GLPI_USER_DEVICE_LIMIT:-}
|
||
+ GLPI_USER_DEVICE_PATHS: ${GLPI_USER_DEVICE_PATHS:-}
|
||
+ HTTP_ADDR: :8080
|
||
+ KNOWLEDGE_ALLOWED_SOURCES: ${KNOWLEDGE_ALLOWED_SOURCES:-}
|
||
+ KNOWLEDGE_AUDIT_TOP_K: ${KNOWLEDGE_AUDIT_TOP_K:-}
|
||
+ KNOWLEDGE_AUTO_REPLY_SOURCES: ${KNOWLEDGE_AUTO_REPLY_SOURCES:-}
|
||
+ KNOWLEDGE_CANDIDATE_MAX_GAP: ${KNOWLEDGE_CANDIDATE_MAX_GAP:-}
|
||
+ KNOWLEDGE_CATEGORY_MAP_FILE: ${KNOWLEDGE_CATEGORY_MAP_FILE:-}
|
||
+ KNOWLEDGE_CATEGORY_MODE: ${KNOWLEDGE_CATEGORY_MODE:-}
|
||
+ KNOWLEDGE_CATEGORY_SOURCES: ${KNOWLEDGE_CATEGORY_SOURCES:-}
|
||
+ KNOWLEDGE_CHUNK_OVERLAP_WORDS: ${KNOWLEDGE_CHUNK_OVERLAP_WORDS:-}
|
||
+ KNOWLEDGE_CHUNK_WORDS: ${KNOWLEDGE_CHUNK_WORDS:-}
|
||
+ KNOWLEDGE_DIR: /app/knowledge
|
||
+ KNOWLEDGE_EMBEDDING_PROFILE: ${KNOWLEDGE_EMBEDDING_PROFILE:-}
|
||
+ KNOWLEDGE_EMBED_BATCH_SIZE: ${KNOWLEDGE_EMBED_BATCH_SIZE:-}
|
||
+ KNOWLEDGE_EVIDENCE_WEIGHT_AI: ${KNOWLEDGE_EVIDENCE_WEIGHT_AI:-}
|
||
+ KNOWLEDGE_EVIDENCE_WEIGHT_CATEGORY: ${KNOWLEDGE_EVIDENCE_WEIGHT_CATEGORY:-}
|
||
+ KNOWLEDGE_EVIDENCE_WEIGHT_RETRIEVAL: ${KNOWLEDGE_EVIDENCE_WEIGHT_RETRIEVAL:-}
|
||
+ KNOWLEDGE_IGNORE_GLOBS: ${KNOWLEDGE_IGNORE_GLOBS:-}
|
||
+ KNOWLEDGE_INDEX_MODE: ${KNOWLEDGE_INDEX_MODE:-}
|
||
+ KNOWLEDGE_INDEX_SCAN_INTERVAL: ${KNOWLEDGE_INDEX_SCAN_INTERVAL:-}
|
||
+ KNOWLEDGE_MAX_CHUNKS_PER_DOC: ${KNOWLEDGE_MAX_CHUNKS_PER_DOC:-}
|
||
+ KNOWLEDGE_MAX_QUERY_CHUNKS: ${KNOWLEDGE_MAX_QUERY_CHUNKS:-}
|
||
+ KNOWLEDGE_MIN_SCORE: ${KNOWLEDGE_MIN_SCORE:-}
|
||
+ KNOWLEDGE_RETRIEVAL_FLOOR: ${KNOWLEDGE_RETRIEVAL_FLOOR:-}
|
||
+ KNOWLEDGE_TOP_K: ${KNOWLEDGE_TOP_K:-}
|
||
+ KNOWLEDGE_VECTOR_BACKEND: ${KNOWLEDGE_VECTOR_BACKEND:-dual}
|
||
+ KNOWLEDGE_WEB_EDIT_ENABLED: ${AGENT_LEGACY_KNOWLEDGE_EDITOR_ENABLED:-false}
|
||
+ KNOWLEDGE_WEIGHT_CATEGORY: ${KNOWLEDGE_WEIGHT_CATEGORY:-}
|
||
+ KNOWLEDGE_WEIGHT_KEYWORDS: ${KNOWLEDGE_WEIGHT_KEYWORDS:-}
|
||
+ KNOWLEDGE_WEIGHT_LEXICAL: ${KNOWLEDGE_WEIGHT_LEXICAL:-}
|
||
+ KNOWLEDGE_WEIGHT_SEMANTIC: ${KNOWLEDGE_WEIGHT_SEMANTIC:-}
|
||
+ KNOWLEDGE_WEIGHT_TITLE: ${KNOWLEDGE_WEIGHT_TITLE:-}
|
||
+ LEARNING_ENABLED: ${LEARNING_ENABLED:-}
|
||
+ LEARNING_EXAMPLES_PER_CATEGORY: ${LEARNING_EXAMPLES_PER_CATEGORY:-}
|
||
+ LEARNING_MAX_EXAMPLES: ${LEARNING_MAX_EXAMPLES:-}
|
||
+ LOG_LEVEL: ${LOG_LEVEL:-}
|
||
+ MAJOR_INCIDENTS_ENABLED: ${MAJOR_INCIDENTS_ENABLED:-}
|
||
+ NEUROFORGE_API_KEY: ${NEUROFORGE_INTEGRATION_TOKEN:?Set the NeuroForge integration
|
||
+ token}
|
||
+ NEUROFORGE_FAIL_OPEN: ${NEUROFORGE_FAIL_OPEN:-true}
|
||
+ NEUROFORGE_NAMESPACE: ${NEUROFORGE_NAMESPACE:-glpi-agent}
|
||
+ NEUROFORGE_SEARCH_K: ${NEUROFORGE_SEARCH_K:-128}
|
||
+ NEUROFORGE_TIMEOUT: ${NEUROFORGE_TIMEOUT:-}
|
||
+ NEUROFORGE_URL: http://neuroforge:8080
|
||
+ OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-}
|
||
+ OLLAMA_FAILOVER_ATTEMPTS: ${OLLAMA_FAILOVER_ATTEMPTS:-}
|
||
+ OLLAMA_FAILOVER_ENABLED: ${OLLAMA_FAILOVER_ENABLED:-}
|
||
+ OLLAMA_JSON_RETRIES: ${OLLAMA_JSON_RETRIES:-}
|
||
+ OLLAMA_KEEP_ALIVE: ${OLLAMA_KEEP_ALIVE:-}
|
||
+ OLLAMA_MAX_CONCURRENT: ${OLLAMA_MAX_CONCURRENT:-}
|
||
+ OLLAMA_MODEL: ${OLLAMA_MODEL:-}
|
||
+ OLLAMA_NODE_FAILURE_COOLDOWN: ${OLLAMA_NODE_FAILURE_COOLDOWN:-}
|
||
+ OLLAMA_NODE_HEALTH_INTERVAL: ${OLLAMA_NODE_HEALTH_INTERVAL:-}
|
||
+ OLLAMA_NODE_MAX_INFLIGHT: ${OLLAMA_NODE_MAX_INFLIGHT:-}
|
||
+ OLLAMA_NODE_NAMES: ${OLLAMA_NODE_NAMES:-}
|
||
+ OLLAMA_NODE_REQUEST_TIMEOUT: ${OLLAMA_NODE_REQUEST_TIMEOUT:-}
|
||
+ OLLAMA_NODE_WEIGHTS: ${OLLAMA_NODE_WEIGHTS:-}
|
||
+ OLLAMA_NUM_PREDICT: ${OLLAMA_NUM_PREDICT:-}
|
||
+ OLLAMA_REQUIRE_EMBEDDING_MODEL: ${OLLAMA_REQUIRE_EMBEDDING_MODEL:-}
|
||
+ OLLAMA_REQUIRE_SAME_MODEL_DIGEST: ${OLLAMA_REQUIRE_SAME_MODEL_DIGEST:-}
|
||
+ OLLAMA_ROUTING_MODE: ${OLLAMA_ROUTING_MODE:-}
|
||
+ OLLAMA_THINK: ${OLLAMA_THINK:-}
|
||
+ OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-}
|
||
+ OLLAMA_URL: ${OLLAMA_BASE_URL:?Set Ollama URL reachable from master}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ OLLAMA_URLS: ${OLLAMA_URLS:?Set one or more Ollama URLs reachable from master}
|
||
+ OUTCOME_LEARNING_ENABLED: ${OUTCOME_LEARNING_ENABLED:-true}
|
||
+ OUTCOME_LEARNING_FAIL_OPEN: ${OUTCOME_LEARNING_FAIL_OPEN:-false}
|
||
+ OUTCOME_LEARNING_MAX_OUTCOMES: ${OUTCOME_LEARNING_MAX_OUTCOMES:-2000}
|
||
+ OUTCOME_RETRIEVAL_ENABLED: ${OUTCOME_RETRIEVAL_ENABLED:-true}
|
||
+ OUTCOME_RETRIEVAL_FAIL_OPEN: ${OUTCOME_RETRIEVAL_FAIL_OPEN:-true}
|
||
+ OUTCOME_RETRIEVAL_MIN_SIMILARITY: ${OUTCOME_RETRIEVAL_MIN_SIMILARITY:-0.58}
|
||
+ OUTCOME_RETRIEVAL_SEARCH_K: ${OUTCOME_RETRIEVAL_SEARCH_K:-6}
|
||
+ PRIORITY_ALLOWED_REASON_CODES: ${PRIORITY_ALLOWED_REASON_CODES:-}
|
||
+ PRIORITY_ANALYSIS_TIMEOUT: ${PRIORITY_ANALYSIS_TIMEOUT:-}
|
||
+ PRIORITY_CONFIDENCE: ${PRIORITY_CONFIDENCE:-}
|
||
+ PRIORITY_ENABLED: ${PRIORITY_ENABLED:-}
|
||
+ PRIORITY_MAX_INCREASE: ${PRIORITY_MAX_INCREASE:-}
|
||
+ QUEUE_SIZE: ${QUEUE_SIZE:-}
|
||
+ RAG_ENABLED: ${RAG_ENABLED:-}
|
||
+ REPLY_CONFIDENCE: ${REPLY_CONFIDENCE:-}
|
||
+ UPTIME_KUMA_API_KEY: ${UPTIME_KUMA_API_KEY:-}
|
||
+ UPTIME_KUMA_ENABLED: ${UPTIME_KUMA_ENABLED:-}
|
||
+ UPTIME_KUMA_INCLUDE_MAINTENANCE: ${UPTIME_KUMA_INCLUDE_MAINTENANCE:-}
|
||
+ UPTIME_KUMA_MAX_ISSUES: ${UPTIME_KUMA_MAX_ISSUES:-}
|
||
+ UPTIME_KUMA_MODE: ${UPTIME_KUMA_MODE:-}
|
||
+ UPTIME_KUMA_STATUS_PAGES: ${UPTIME_KUMA_STATUS_PAGES:-}
|
||
+ UPTIME_KUMA_TIMEOUT: ${UPTIME_KUMA_TIMEOUT:-}
|
||
+ UPTIME_KUMA_URL: ${UPTIME_KUMA_URL:-}
|
||
+ USER_DEVICE_CONTEXT_ENABLED: ${USER_DEVICE_CONTEXT_ENABLED:-}
|
||
+ WEBHOOK_SECRET: ${WEBHOOK_SECRET:-}
|
||
+ WEB_ALLOW_ANONYMOUS: ${WEB_ALLOW_ANONYMOUS:-}
|
||
+ WEB_PASSWORD: ${WEB_PASSWORD:-}
|
||
+ WEB_USERNAME: ${WEB_USERNAME:-}
|
||
+ WORKERS: ${WORKERS:-}
|
||
+ ports:
|
||
+ - 127.0.0.1:${AGENT_HOST_PORT:-8080}:8080
|
||
+ volumes:
|
||
+ - agent-data:/app/data
|
||
+ - ${KB_DATA_PATH:-./knowledge}:/app/knowledge:ro
|
||
+ depends_on:
|
||
+ agent-data-init:
|
||
+ condition: service_completed_successfully
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=64m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ healthcheck:
|
||
+ test:
|
||
+ - CMD
|
||
+ - /app/glpi-ai-agent
|
||
+ - healthcheck
|
||
+ interval: 15s
|
||
+ timeout: 3s
|
||
+ retries: 8
|
||
+ start_period: 10s
|
||
+ stop_grace_period: 20s
|
||
+ knowledge:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG
|
||
+ to an immutable release tag, for example 1.6.2}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ APP_MODE: ${KB_APP_MODE:-editor}
|
||
+ DATA_DIR: /data/knowledge
|
||
+ BACKUP_DIR: /data/backups
|
||
+ STAGING_DIR: /data/staging
|
||
+ LISTEN_ADDR: :8080
|
||
+ APP_TITLE: ${APP_TITLE:-}
|
||
+ APP_SUBTITLE: ${APP_SUBTITLE:-}
|
||
+ AUTO_RELOAD_INTERVAL: ${AUTO_RELOAD_INTERVAL:-}
|
||
+ BASIC_AUTH_USER: ${BASIC_AUTH_USER:?Set the Knowledge web user}
|
||
+ BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD:?Set the Knowledge web password}
|
||
+ AI_FALLBACK_ENABLED: ${AI_FALLBACK_ENABLED:-false}
|
||
+ OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:?Set Ollama URL reachable from master}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
+ OLLAMA_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
+ OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-10m}
|
||
+ OLLAMA_MAX_CONCURRENT: ${OLLAMA_MAX_CONCURRENT:-2}
|
||
+ OLLAMA_STAGING_AUTO_REPLY: ${OLLAMA_STAGING_AUTO_REPLY:-false}
|
||
+ OLLAMA_STAGING_MIN_SCORE: ${OLLAMA_STAGING_MIN_SCORE:-0.72}
|
||
+ BRAIN_ACTIVITY_URL: http://neuroforge:8080/api/v1/integrations/events
|
||
+ BRAIN_ACTIVITY_API_KEY: ${NEUROFORGE_INTEGRATION_TOKEN:?Set the NeuroForge integration
|
||
+ token}
|
||
+ KB_INTEGRATION_TOKEN: ${KB_INTEGRATION_TOKEN:?Set the Knowledge integration
|
||
+ token}
|
||
+ ports:
|
||
+ - 127.0.0.1:${KNOWLEDGE_HOST_PORT:-8081}:8080
|
||
+ volumes:
|
||
+ - ${KB_DATA_PATH:-./knowledge}:/data/knowledge:${KB_DATA_MOUNT_MODE:-rw}
|
||
+ - ${KB_STAGING_PATH:-./staging}:/data/staging:rw
|
||
+ - ${KB_BACKUP_PATH:-./backups}:/data/backups:rw
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=32m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ stop_grace_period: 35s
|
||
+ control:
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG
|
||
+ to an immutable release tag, for example 1.6.2}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ CONTROL_ADDR: :8070
|
||
+ CONTROL_BASIC_AUTH_USER: ${CONTROL_BASIC_AUTH_USER:?Set the Control Center user}
|
||
+ CONTROL_BASIC_AUTH_PASSWORD: ${CONTROL_BASIC_AUTH_PASSWORD:?Set the Control
|
||
+ Center password}
|
||
+ AGENT_URL: http://agent:8080
|
||
+ KNOWLEDGE_URL: http://knowledge:8080
|
||
+ NEUROFORGE_URL: http://neuroforge:8080
|
||
+ NEUROFORGE_API_KEY: ${NEUROFORGE_CONTROL_READ_TOKEN:?Set the NeuroForge control
|
||
+ read token}
|
||
+ CONTROL_READ_TOKEN: ${CONTROL_READ_TOKEN:?Set the Agent control read token}
|
||
+ CODEBASE_MEMORY_URL: ${CODEBASE_MEMORY_URL:-}
|
||
+ PUBLIC_CODEBASE_MEMORY_URL: ${PUBLIC_CODEBASE_MEMORY_URL:-}
|
||
+ KNOWLEDGE_VECTOR_BACKEND: ${KNOWLEDGE_VECTOR_BACKEND:-dual}
|
||
+ NEUROFORGE_SEARCH_K: ${NEUROFORGE_SEARCH_K:-128}
|
||
+ NEUROFORGE_FAIL_OPEN: ${NEUROFORGE_FAIL_OPEN:-true}
|
||
+ NEUROFORGE_CONTROLLED_LEARNING: ${NEUROFORGE_CONTROLLED_LEARNING:-true}
|
||
+ NEUROFORGE_GOAL_LEARNING_ENABLED: ${NEUROFORGE_GOAL_LEARNING_ENABLED:-false}
|
||
+ OUTCOME_LEARNING_ENABLED: ${OUTCOME_LEARNING_ENABLED:-true}
|
||
+ OUTCOME_RETRIEVAL_ENABLED: ${OUTCOME_RETRIEVAL_ENABLED:-true}
|
||
+ OUTCOME_RETRIEVAL_SEARCH_K: ${OUTCOME_RETRIEVAL_SEARCH_K:-6}
|
||
+ OUTCOME_RETRIEVAL_MIN_SIMILARITY: ${OUTCOME_RETRIEVAL_MIN_SIMILARITY:-0.58}
|
||
+ OUTCOME_RETRIEVAL_FAIL_OPEN: ${OUTCOME_RETRIEVAL_FAIL_OPEN:-true}
|
||
+ NEUROFORGE_RESEARCH_ENABLED: ${NEUROFORGE_RESEARCH_ENABLED:-false}
|
||
+ NEUROFORGE_SEARXNG_ENABLED: ${NEUROFORGE_SEARXNG_ENABLED:-false}
|
||
+ NEUROFORGE_KB_STAGING_ENABLED: ${NEUROFORGE_KB_STAGING_ENABLED:-true}
|
||
+ NEUROFORGE_AUTONOMY_ENABLED: ${NEUROFORGE_AUTONOMY_ENABLED:-false}
|
||
+ PUBLIC_AGENT_URL: http://localhost:${AGENT_HOST_PORT:-8080}
|
||
+ PUBLIC_KNOWLEDGE_URL: http://localhost:${KNOWLEDGE_HOST_PORT:-8081}
|
||
+ PUBLIC_NEUROFORGE_URL: http://localhost:${NEUROFORGE_HOST_PORT:-8090}/admin
|
||
+ ports:
|
||
+ - 127.0.0.1:${CONTROL_HOST_PORT:-8070}:8070
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=16m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ extra_hosts:
|
||
+ - host.docker.internal:host-gateway
|
||
+ stop_grace_period: 25s
|
||
+ prometheus-secrets-init:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: alpine:3.22.1
|
||
+ restart: 'no'
|
||
+ environment:
|
||
+ NEUROFORGE_METRICS_TOKEN: ${NEUROFORGE_METRICS_TOKEN:?Set NeuroForge metrics
|
||
+ token}
|
||
+ command:
|
||
+ - /bin/sh
|
||
+ - -ec
|
||
+ - printf "%s" "$${NEUROFORGE_METRICS_TOKEN}" > /secrets/neuroforge_metrics.token;
|
||
+ chown 65534:65534 /secrets/neuroforge_metrics.token; chmod 0400 /secrets/neuroforge_metrics.token
|
||
+ volumes:
|
||
+ - prometheus-secrets:/secrets
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=8m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ prometheus-config-init:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: alpine:3.22.1
|
||
+ restart: 'no'
|
||
+ environment:
|
||
+ CPU_TARGET: ${CPU_SUBAGENT_NODE_EXPORTER_TARGET:-cpu-subagent.example.invalid:9100}
|
||
+ GPU_TARGET: ${GPU_SUBAGENT_NODE_EXPORTER_TARGET:-gpu-subagent.example.invalid:9100}
|
||
+ GPU_DCGM_TARGET: ${GPU_SUBAGENT_DCGM_EXPORTER_TARGET:-gpu-subagent.example.invalid:9400}
|
||
+ command:
|
||
+ - /bin/sh
|
||
+ - -ec
|
||
+ - sed -e "s|__CPU_TARGET__|$${CPU_TARGET}|g" -e "s|__GPU_TARGET__|$${GPU_TARGET}|g"
|
||
+ -e "s|__GPU_DCGM_TARGET__|$${GPU_DCGM_TARGET}|g" /template/prometheus.yml.template
|
||
+ > /rendered/prometheus.yml
|
||
+ volumes:
|
||
+ - ./monitoring/prometheus:/template:ro
|
||
+ - prometheus-config:/rendered
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=8m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ node-exporter:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${NODE_EXPORTER_IMAGE:-prom/node-exporter:v1.12.1}
|
||
+ restart: unless-stopped
|
||
+ command:
|
||
+ - --path.procfs=/host/proc
|
||
+ - --path.sysfs=/host/sys
|
||
+ - --path.rootfs=/rootfs
|
||
+ - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
|
||
+ volumes:
|
||
+ - /proc:/host/proc:ro
|
||
+ - /sys:/host/sys:ro
|
||
+ - /:/rootfs:ro,rslave
|
||
+ read_only: true
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ prometheus:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${PROMETHEUS_IMAGE:-prom/prometheus:v3.14.0}
|
||
+ restart: unless-stopped
|
||
+ command:
|
||
+ - --config.file=/etc/prometheus/prometheus.yml
|
||
+ - --storage.tsdb.path=/prometheus
|
||
+ - --storage.tsdb.retention.time=${PROMETHEUS_RETENTION:-30d}
|
||
+ - --web.enable-lifecycle
|
||
+ ports:
|
||
+ - 127.0.0.1:${PROMETHEUS_HOST_PORT:-9090}:9090
|
||
+ volumes:
|
||
+ - prometheus-config:/etc/prometheus:ro
|
||
+ - ./monitoring/prometheus/alerts.yml:/etc/prometheus-alerts/alerts.yml:ro
|
||
+ - prometheus-data:/prometheus
|
||
+ - prometheus-secrets:/etc/prometheus-secrets:ro
|
||
+ depends_on:
|
||
+ neuroforge:
|
||
+ condition: service_started
|
||
+ prometheus-secrets-init:
|
||
+ condition: service_completed_successfully
|
||
+ prometheus-config-init:
|
||
+ condition: service_completed_successfully
|
||
+ read_only: true
|
||
+ tmpfs:
|
||
+ - /tmp:size=32m,mode=1777
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+ grafana:
|
||
+ profiles:
|
||
+ - monitoring
|
||
+ image: ${GRAFANA_IMAGE:-grafana/grafana:13.2.0}
|
||
+ restart: unless-stopped
|
||
+ environment:
|
||
+ GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
|
||
+ GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?Set Grafana admin password}
|
||
+ GF_USERS_ALLOW_SIGN_UP: 'false'
|
||
+ GF_AUTH_ANONYMOUS_ENABLED: 'false'
|
||
+ GF_SERVER_ROOT_URL: ${GRAFANA_ROOT_URL:-http://localhost:3000}
|
||
+ ports:
|
||
+ - 127.0.0.1:${GRAFANA_HOST_PORT:-3000}:3000
|
||
+ volumes:
|
||
+ - grafana-data:/var/lib/grafana
|
||
+ - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
|
||
+ - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
|
||
+ depends_on:
|
||
+ prometheus:
|
||
+ condition: service_started
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+ cap_drop:
|
||
+ - ALL
|
||
+volumes:
|
||
+ neuroforge-data: null
|
||
+ agent-data: null
|
||
+ searxng-cache: null
|
||
+ prometheus-data: null
|
||
+ prometheus-secrets: null
|
||
+ prometheus-config: null
|
||
+ grafana-data: null
|
||
diff --git ab/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json b/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json
|
||
new file mode 100644
|
||
index 0000000..4b48074
|
||
--- /dev/null
|
||
+++ b/deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json
|
||
@@ -0,0 +1,1487 @@
|
||
+{
|
||
+ "id": null,
|
||
+ "uid": "neuroforge-master-subagents",
|
||
+ "title": "NeuroForge Master / Subagents / Knowledge Graph",
|
||
+ "tags": [
|
||
+ "neuroforge",
|
||
+ "orchestrator",
|
||
+ "knowledge-graph"
|
||
+ ],
|
||
+ "timezone": "browser",
|
||
+ "schemaVersion": 41,
|
||
+ "version": 2,
|
||
+ "refresh": "15s",
|
||
+ "time": {
|
||
+ "from": "now-6h",
|
||
+ "to": "now"
|
||
+ },
|
||
+ "timepicker": {},
|
||
+ "templating": {
|
||
+ "list": []
|
||
+ },
|
||
+ "annotations": {
|
||
+ "list": []
|
||
+ },
|
||
+ "panels": [
|
||
+ {
|
||
+ "id": 1,
|
||
+ "type": "stat",
|
||
+ "title": "Master Up",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_up",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 2,
|
||
+ "type": "stat",
|
||
+ "title": "Memories",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 4,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_memories",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 3,
|
||
+ "type": "stat",
|
||
+ "title": "Synapses",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 8,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_synapses",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 4,
|
||
+ "type": "stat",
|
||
+ "title": "Online CPU Workers",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "sum(neuroforge_workers{resource=\"cpu\",status=\"online\"})",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 5,
|
||
+ "type": "stat",
|
||
+ "title": "Online GPU Workers",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 16,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "sum(neuroforge_workers{resource=\"gpu\",status=\"online\"})",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 6,
|
||
+ "type": "stat",
|
||
+ "title": "Queued Jobs",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 20,
|
||
+ "y": 0,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_jobs{status=\"queued\"}",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 7,
|
||
+ "type": "stat",
|
||
+ "title": "Linked Memories",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_memories{state=\"linked\"}",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 8,
|
||
+ "type": "stat",
|
||
+ "title": "Isolated Memories",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 4,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_memories{state=\"isolated\"}",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 9,
|
||
+ "type": "stat",
|
||
+ "title": "Multi-linked",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 8,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_memories{state=\"multi_linked\"}",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 10,
|
||
+ "type": "stat",
|
||
+ "title": "Average Degree",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_average_degree",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 11,
|
||
+ "type": "stat",
|
||
+ "title": "Connected Components",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 16,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_connected_components",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 12,
|
||
+ "type": "stat",
|
||
+ "title": "Largest Component",
|
||
+ "description": "",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 20,
|
||
+ "y": 4,
|
||
+ "w": 4,
|
||
+ "h": 4
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_largest_component",
|
||
+ "instant": true,
|
||
+ "range": false
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "mappings": [],
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "reduceOptions": {
|
||
+ "values": false,
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ],
|
||
+ "fields": ""
|
||
+ },
|
||
+ "orientation": "auto",
|
||
+ "textMode": "auto",
|
||
+ "colorMode": "value",
|
||
+ "graphMode": "area",
|
||
+ "justifyMode": "auto"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 13,
|
||
+ "type": "timeseries",
|
||
+ "title": "Orchestrator Jobs by Status",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 8,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_jobs",
|
||
+ "legendFormat": "{{status}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 14,
|
||
+ "type": "timeseries",
|
||
+ "title": "Worker Inflight",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 8,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_worker_inflight",
|
||
+ "legendFormat": "{{worker}} / {{resource}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 15,
|
||
+ "type": "timeseries",
|
||
+ "title": "Worker Capacity",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 16,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_worker_capacity",
|
||
+ "legendFormat": "{{worker}} / {{resource}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 16,
|
||
+ "type": "timeseries",
|
||
+ "title": "Graph Linkage",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 16,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_graph_memories",
|
||
+ "legendFormat": "{{state}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 17,
|
||
+ "type": "timeseries",
|
||
+ "title": "Vector Index Population",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 24,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_hnsw_nodes",
|
||
+ "legendFormat": "HNSW",
|
||
+ "range": true
|
||
+ },
|
||
+ {
|
||
+ "refId": "B",
|
||
+ "expr": "neuroforge_disk_pq_items",
|
||
+ "legendFormat": "Disk PQ",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "short",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 18,
|
||
+ "type": "timeseries",
|
||
+ "title": "HTTP Request Rate",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 24,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "sum by (code) (rate(neuroforge_http_requests_total[5m]))",
|
||
+ "legendFormat": "HTTP {{code}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "reqps",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 19,
|
||
+ "type": "timeseries",
|
||
+ "title": "HTTP P95 Latency",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 32,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "histogram_quantile(0.95, sum by (le) (rate(neuroforge_http_request_duration_seconds_bucket[5m])))",
|
||
+ "legendFormat": "p95",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "s",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 20,
|
||
+ "type": "timeseries",
|
||
+ "title": "Master Heap",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 32,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "neuroforge_runtime_heap_alloc_bytes",
|
||
+ "legendFormat": "heap alloc",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "bytes",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 21,
|
||
+ "type": "timeseries",
|
||
+ "title": "Master / CPU / GPU Host CPU",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 40,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "100 - (avg by (job) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
|
||
+ "legendFormat": "{{job}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "percent",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 22,
|
||
+ "type": "timeseries",
|
||
+ "title": "Host Memory Usage",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 12,
|
||
+ "y": 40,
|
||
+ "w": 12,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "100 * (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes))",
|
||
+ "legendFormat": "{{job}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "percent",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 23,
|
||
+ "type": "timeseries",
|
||
+ "title": "GPU Utilization",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 48,
|
||
+ "w": 8,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "avg by (gpu) (DCGM_FI_DEV_GPU_UTIL)",
|
||
+ "legendFormat": "GPU {{gpu}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "percent",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 24,
|
||
+ "type": "timeseries",
|
||
+ "title": "GPU Framebuffer Used",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 8,
|
||
+ "y": 48,
|
||
+ "w": 8,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "DCGM_FI_DEV_FB_USED",
|
||
+ "legendFormat": "GPU {{gpu}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "MiB",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 25,
|
||
+ "type": "timeseries",
|
||
+ "title": "GPU Power",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 16,
|
||
+ "y": 48,
|
||
+ "w": 8,
|
||
+ "h": 8
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "refId": "A",
|
||
+ "expr": "DCGM_FI_DEV_POWER_USAGE",
|
||
+ "legendFormat": "GPU {{gpu}}",
|
||
+ "range": true
|
||
+ }
|
||
+ ],
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "watt",
|
||
+ "custom": {
|
||
+ "drawStyle": "line",
|
||
+ "lineInterpolation": "linear",
|
||
+ "lineWidth": 1,
|
||
+ "fillOpacity": 8,
|
||
+ "showPoints": "auto",
|
||
+ "spanNulls": false
|
||
+ },
|
||
+ "thresholds": {
|
||
+ "mode": "absolute",
|
||
+ "steps": [
|
||
+ {
|
||
+ "color": "green",
|
||
+ "value": null
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "calcs": [
|
||
+ "lastNotNull"
|
||
+ ]
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "desc"
|
||
+ }
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": 26,
|
||
+ "title": "Durable Job Payload Bytes",
|
||
+ "type": "timeseries",
|
||
+ "datasource": {
|
||
+ "type": "prometheus",
|
||
+ "uid": "prometheus"
|
||
+ },
|
||
+ "gridPos": {
|
||
+ "x": 0,
|
||
+ "y": 56,
|
||
+ "w": 24,
|
||
+ "h": 8
|
||
+ },
|
||
+ "fieldConfig": {
|
||
+ "defaults": {
|
||
+ "unit": "bytes"
|
||
+ },
|
||
+ "overrides": []
|
||
+ },
|
||
+ "options": {
|
||
+ "legend": {
|
||
+ "displayMode": "list",
|
||
+ "placement": "bottom",
|
||
+ "showLegend": true
|
||
+ },
|
||
+ "tooltip": {
|
||
+ "mode": "multi",
|
||
+ "sort": "none"
|
||
+ }
|
||
+ },
|
||
+ "targets": [
|
||
+ {
|
||
+ "expr": "neuroforge_job_payload_bytes",
|
||
+ "legendFormat": "{{state}}",
|
||
+ "refId": "A"
|
||
+ }
|
||
+ ]
|
||
+ }
|
||
+ ]
|
||
+}
|
||
diff --git ab/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml b/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml
|
||
new file mode 100644
|
||
index 0000000..707ffdd
|
||
--- /dev/null
|
||
+++ b/deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml
|
||
@@ -0,0 +1,12 @@
|
||
+apiVersion: 1
|
||
+
|
||
+providers:
|
||
+ - name: NeuroForge
|
||
+ orgId: 1
|
||
+ folder: NeuroForge
|
||
+ type: file
|
||
+ disableDeletion: true
|
||
+ updateIntervalSeconds: 30
|
||
+ allowUiUpdates: false
|
||
+ options:
|
||
+ path: /var/lib/grafana/dashboards
|
||
diff --git ab/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml b/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml
|
||
new file mode 100644
|
||
index 0000000..00f9915
|
||
--- /dev/null
|
||
+++ b/deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml
|
||
@@ -0,0 +1,10 @@
|
||
+apiVersion: 1
|
||
+
|
||
+datasources:
|
||
+ - name: Prometheus
|
||
+ uid: prometheus
|
||
+ type: prometheus
|
||
+ access: proxy
|
||
+ url: http://prometheus:9090
|
||
+ isDefault: true
|
||
+ editable: false
|
||
diff --git ab/deployments/master/monitoring/prometheus/alerts.yml b/deployments/master/monitoring/prometheus/alerts.yml
|
||
new file mode 100644
|
||
index 0000000..af8bc9d
|
||
--- /dev/null
|
||
+++ b/deployments/master/monitoring/prometheus/alerts.yml
|
||
@@ -0,0 +1,74 @@
|
||
+groups:
|
||
+ - name: neuroforge-production
|
||
+ rules:
|
||
+ - alert: NeuroForgeMasterDown
|
||
+ expr: up{job="neuroforge"} == 0
|
||
+ for: 1m
|
||
+ labels: {severity: critical}
|
||
+ annotations:
|
||
+ summary: "NeuroForge Master is down"
|
||
+ description: "Prometheus cannot scrape the NeuroForge Master for more than 1 minute."
|
||
+
|
||
+ - alert: NeuroForgeCPUWorkerMissing
|
||
+ expr: sum(neuroforge_workers{resource="cpu",status="online"}) < 1
|
||
+ for: 2m
|
||
+ labels: {severity: critical}
|
||
+ annotations:
|
||
+ summary: "No online CPU subagent"
|
||
+ description: "Graph relink/backfill jobs cannot converge without a CPU worker."
|
||
+
|
||
+ - alert: NeuroForgeGPUWorkerMissing
|
||
+ expr: sum(neuroforge_workers{resource="gpu",status="online"}) < 1
|
||
+ for: 2m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "No online GPU subagent"
|
||
+ description: "Distributed chat/embed jobs have no online GPU worker."
|
||
+
|
||
+ - alert: NeuroForgeFailedJobs
|
||
+ expr: neuroforge_jobs{status="failed"} > 0
|
||
+ for: 5m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "NeuroForge has failed durable jobs"
|
||
+ description: "At least one orchestrator job has remained failed for 5 minutes."
|
||
+
|
||
+ - alert: NeuroForgeApplyWaitStuck
|
||
+ expr: neuroforge_jobs{status="apply_wait"} > 0
|
||
+ for: 10m
|
||
+ labels: {severity: critical}
|
||
+ annotations:
|
||
+ summary: "Master apply phase appears stuck"
|
||
+ description: "A durable job has remained in apply_wait for more than 10 minutes."
|
||
+
|
||
+ - alert: NeuroForgeQueueHigh
|
||
+ expr: neuroforge_jobs{status="queued"} > 1000
|
||
+ for: 10m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "NeuroForge queue is high"
|
||
+ description: "More than 1000 jobs have remained queued for 10 minutes."
|
||
+
|
||
+ - alert: NeuroForgeGraphHighlyIsolated
|
||
+ expr: (neuroforge_graph_memories{state="isolated"} / clamp_min(neuroforge_memories, 1)) > 0.80 and on() neuroforge_memories > 1000
|
||
+ for: 30m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "Knowledge graph remains highly isolated"
|
||
+ description: "More than 80% of memories are still isolated after 30 minutes."
|
||
+
|
||
+ - alert: NeuroForgePendingPayloadHigh
|
||
+ expr: neuroforge_job_payload_bytes{state="pending"} > 104857600
|
||
+ for: 5m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "NeuroForge pending job payload memory is high"
|
||
+ description: "Pending durable job payload/result bytes exceed 100 MiB for 5 minutes; backfill should remain below the v1.6.1 safety budget."
|
||
+
|
||
+ - alert: NeuroForgeTerminalPayloadHigh
|
||
+ expr: neuroforge_job_payload_bytes{state="terminal"} > 67108864
|
||
+ for: 10m
|
||
+ labels: {severity: warning}
|
||
+ annotations:
|
||
+ summary: "NeuroForge terminal job payload retention is high"
|
||
+ description: "Terminal job payload/result bytes exceed 64 MiB. Completed vector.relink jobs should be compacted automatically in v1.6.1."
|
||
diff --git ab/deployments/master/monitoring/prometheus/prometheus.yml.template b/deployments/master/monitoring/prometheus/prometheus.yml.template
|
||
new file mode 100644
|
||
index 0000000..5e5899a
|
||
--- /dev/null
|
||
+++ b/deployments/master/monitoring/prometheus/prometheus.yml.template
|
||
@@ -0,0 +1,31 @@
|
||
+global:
|
||
+ scrape_interval: 15s
|
||
+ evaluation_interval: 15s
|
||
+
|
||
+rule_files:
|
||
+ - /etc/prometheus-alerts/alerts.yml
|
||
+
|
||
+scrape_configs:
|
||
+ - job_name: neuroforge
|
||
+ metrics_path: /metrics
|
||
+ authorization:
|
||
+ type: Bearer
|
||
+ credentials_file: /etc/prometheus-secrets/neuroforge_metrics.token
|
||
+ static_configs:
|
||
+ - targets: ["neuroforge:8080"]
|
||
+
|
||
+ - job_name: master_node
|
||
+ static_configs:
|
||
+ - targets: ["node-exporter:9100"]
|
||
+
|
||
+ - job_name: cpu_subagent_node
|
||
+ static_configs:
|
||
+ - targets: ["__CPU_TARGET__"]
|
||
+
|
||
+ - job_name: gpu_subagent_node
|
||
+ static_configs:
|
||
+ - targets: ["__GPU_TARGET__"]
|
||
+
|
||
+ - job_name: gpu_dcgm
|
||
+ static_configs:
|
||
+ - targets: ["__GPU_DCGM_TARGET__"]
|
||
diff --git ab/deployments/master/preflight.sh b/deployments/master/preflight.sh
|
||
new file mode 100755
|
||
index 0000000..cb00405
|
||
--- /dev/null
|
||
+++ b/deployments/master/preflight.sh
|
||
@@ -0,0 +1,14 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+[ -f .env ] || { echo 'missing .env'; exit 1; }
|
||
+required='IMAGE_TAG NEUROFORGE_ADMIN_TOKEN NEUROFORGE_APP_API_KEY NEUROFORGE_INTEGRATION_TOKEN NEUROFORGE_CONTROL_READ_TOKEN NEUROFORGE_WORKER_TOKEN NEUROFORGE_METRICS_TOKEN KB_INTEGRATION_TOKEN CONTROL_READ_TOKEN GLPI_URL GLPI_CLIENT_ID GLPI_CLIENT_SECRET GLPI_USERNAME GLPI_PASSWORD GRAFANA_ADMIN_PASSWORD'
|
||
+for k in $required; do
|
||
+ v=$(awk -F= -v key="$k" '$1==key {sub(/^[^=]*=/,""); print; exit}' .env)
|
||
+ [ -n "$v" ] || { echo "missing: $k"; exit 1; }
|
||
+done
|
||
+if grep -Eq '(^|=)(https?://)?192\.0\.2\.|example\.invalid|CHANGE_ME|YOUR-' .env; then
|
||
+ echo 'placeholder/example values remain in .env'; exit 1
|
||
+fi
|
||
+[ "$(awk -F= '$1=="IMAGE_TAG"{print $2}' .env)" = "1.6.2" ] || { echo 'IMAGE_TAG must be 1.6.2'; exit 1; }
|
||
+docker compose --profile research --profile monitoring config >/dev/null
|
||
+echo 'master preflight: OK'
|
||
diff --git ab/deployments/ollama/.env b/deployments/ollama/.env
|
||
new file mode 100644
|
||
index 0000000..9020ebc
|
||
--- /dev/null
|
||
+++ b/deployments/ollama/.env
|
||
@@ -0,0 +1,12 @@
|
||
+###############################################################################
|
||
+# Ollama standalone
|
||
+###############################################################################
|
||
+CORE_NETWORK=glpi-ai-core
|
||
+CORE_DATA_ROOT=../../runtime
|
||
+OLLAMA_IMAGE=ollama/ollama:0.33.2
|
||
+OLLAMA_BIND_IP=127.0.0.1
|
||
+OLLAMA_HOST_PORT=11434
|
||
+OLLAMA_MODEL=gemma4
|
||
+OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
diff --git ab/deployments/ollama/docker-compose.yml b/deployments/ollama/docker-compose.yml
|
||
new file mode 100644
|
||
index 0000000..735731f
|
||
--- /dev/null
|
||
+++ b/deployments/ollama/docker-compose.yml
|
||
@@ -0,0 +1,20 @@
|
||
+name: glpi-ai-ollama-core
|
||
+services:
|
||
+ ollama:
|
||
+ image: ${OLLAMA_IMAGE:-ollama/ollama:0.33.2}
|
||
+ restart: unless-stopped
|
||
+ ports:
|
||
+ - ${OLLAMA_BIND_IP:-127.0.0.1}:${OLLAMA_HOST_PORT:-11434}:11434
|
||
+ volumes:
|
||
+ - ${CORE_DATA_ROOT:-../../runtime}/ollama:/root/.ollama
|
||
+ networks:
|
||
+ core:
|
||
+ aliases:
|
||
+ - ollama
|
||
+ security_opt:
|
||
+ - no-new-privileges:true
|
||
+
|
||
+networks:
|
||
+ core:
|
||
+ external: true
|
||
+ name: ${CORE_NETWORK:-glpi-ai-core}
|
||
diff --git ab/deployments/ollama/pull-models.sh b/deployments/ollama/pull-models.sh
|
||
new file mode 100755
|
||
index 0000000..097e592
|
||
--- /dev/null
|
||
+++ b/deployments/ollama/pull-models.sh
|
||
@@ -0,0 +1,9 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+[ -f .env ] && set -a && . ./.env && set +a
|
||
+: "${OLLAMA_MODEL:=gemma4}"
|
||
+: "${OLLAMA_EMBEDDING_MODEL:=embeddinggemma}"
|
||
+docker compose exec ollama ollama pull "$OLLAMA_MODEL"
|
||
+docker compose exec ollama ollama pull "$OLLAMA_EMBEDDING_MODEL"
|
||
+echo "Modelle vorhanden:"
|
||
+docker compose exec ollama ollama list
|
||
diff --git ab/docker-bake.hcl b/docker-bake.hcl
|
||
new file mode 100644
|
||
index 0000000..4a29258
|
||
--- /dev/null
|
||
+++ b/docker-bake.hcl
|
||
@@ -0,0 +1,56 @@
|
||
+variable "IMAGE_TAG" {
|
||
+ default = "1.6.2"
|
||
+}
|
||
+
|
||
+variable "REGISTRY" {
|
||
+ default = "git.send.nrw/sendnrw"
|
||
+}
|
||
+
|
||
+group "default" {
|
||
+ targets = ["neuroforge", "neuroforge-worker", "agent", "agent-data-init", "knowledge", "control"]
|
||
+}
|
||
+
|
||
+target "neuroforge" {
|
||
+ context = "./platform/neuroforge"
|
||
+ dockerfile = "Dockerfile"
|
||
+ target = "server"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
+
|
||
+target "neuroforge-worker" {
|
||
+ context = "./platform/neuroforge"
|
||
+ dockerfile = "Dockerfile"
|
||
+ target = "worker"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
+
|
||
+target "agent" {
|
||
+ context = "./services/agent"
|
||
+ dockerfile = "Dockerfile"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-agent:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
+
|
||
+target "agent-data-init" {
|
||
+ context = "./services/agent"
|
||
+ dockerfile = "Dockerfile"
|
||
+ target = "data-init"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
+
|
||
+target "knowledge" {
|
||
+ context = "./services/knowledge"
|
||
+ dockerfile = "Dockerfile"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-knowledge:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
+
|
||
+target "control" {
|
||
+ context = "./services/control"
|
||
+ dockerfile = "Dockerfile"
|
||
+ tags = ["${REGISTRY}/glpi-neuroforge-mega-control:${IMAGE_TAG}"]
|
||
+ platforms = ["linux/amd64"]
|
||
+}
|
||
diff --git a/docker-compose.subagent.yml b/docker-compose.subagent.yml
|
||
index 5f923e8..30228ac 100644
|
||
--- a/docker-compose.subagent.yml
|
||
+++ b/docker-compose.subagent.yml
|
||
@@ -7,7 +7,7 @@ name: neuroforge-subagents
|
||
services:
|
||
cpu-subagent:
|
||
profiles: [cpu]
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.2}
|
||
command:
|
||
- -server
|
||
- ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL}
|
||
@@ -29,7 +29,7 @@ services:
|
||
|
||
gpu-subagent:
|
||
profiles: [gpu]
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG, e.g. 1.6.2}
|
||
command:
|
||
- -server
|
||
- ${NEUROFORGE_MASTER_URL:?Set the reachable NeuroForge master URL}
|
||
@@ -43,6 +43,8 @@ services:
|
||
NEUROFORGE_WORKER_MAX_CONCURRENCY: ${NEUROFORGE_GPU_WORKER_CONCURRENCY:-1}
|
||
NEUROFORGE_WORKER_HEARTBEAT_INTERVAL: ${NEUROFORGE_WORKER_HEARTBEAT_INTERVAL:-15s}
|
||
NEUROFORGE_WORKER_OLLAMA_URL: ${NEUROFORGE_WORKER_OLLAMA_URL:?Set Ollama URL reachable from the GPU subagent}
|
||
+ NEUROFORGE_WORKER_OLLAMA_API_KEY: ${NEUROFORGE_WORKER_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
NEUROFORGE_WORKER_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
NEUROFORGE_WORKER_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
NEUROFORGE_WORKER_OLLAMA_NUM_CTX: ${NEUROFORGE_OLLAMA_NUM_CTX:-8192}
|
||
diff --git a/docker-compose.yml b/docker-compose.yml
|
||
index 61900b1..9e3936b 100644
|
||
--- a/docker-compose.yml
|
||
+++ b/docker-compose.yml
|
||
@@ -33,7 +33,7 @@ services:
|
||
cap_drop:
|
||
- ALL
|
||
neuroforge:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
command:
|
||
- -data
|
||
- /app/data
|
||
@@ -50,6 +50,8 @@ services:
|
||
NEUROFORGE_CLUSTER_TOKEN: ${NEUROFORGE_CLUSTER_TOKEN:-}
|
||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||
NEUROFORGE_OLLAMA_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||
+ NEUROFORGE_OLLAMA_API_KEY: ${NEUROFORGE_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
NEUROFORGE_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
NEUROFORGE_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
NEUROFORGE_OLLAMA_NUM_CTX: ${NEUROFORGE_OLLAMA_NUM_CTX:-8192}
|
||
@@ -143,7 +145,7 @@ services:
|
||
start_period: 15s
|
||
stop_grace_period: 35s
|
||
neuroforge-worker-cpu:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
command:
|
||
- -server
|
||
- http://neuroforge:8080
|
||
@@ -168,7 +170,7 @@ services:
|
||
- ALL
|
||
|
||
neuroforge-worker-gpu:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
command:
|
||
- -server
|
||
- http://neuroforge:8080
|
||
@@ -182,6 +184,8 @@ services:
|
||
NEUROFORGE_WORKER_MAX_CONCURRENCY: ${NEUROFORGE_GPU_WORKER_CONCURRENCY:-1}
|
||
NEUROFORGE_WORKER_HEARTBEAT_INTERVAL: ${NEUROFORGE_WORKER_HEARTBEAT_INTERVAL:-15s}
|
||
NEUROFORGE_WORKER_OLLAMA_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||
+ NEUROFORGE_WORKER_OLLAMA_API_KEY: ${NEUROFORGE_WORKER_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
NEUROFORGE_WORKER_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
NEUROFORGE_WORKER_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
NEUROFORGE_WORKER_OLLAMA_NUM_CTX: ${NEUROFORGE_OLLAMA_NUM_CTX:-8192}
|
||
@@ -200,7 +204,7 @@ services:
|
||
- ALL
|
||
|
||
agent-data-init:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
restart: 'no'
|
||
user: 0:0
|
||
volumes:
|
||
@@ -213,7 +217,7 @@ services:
|
||
- CHOWN
|
||
- FOWNER
|
||
agent:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
restart: unless-stopped
|
||
environment:
|
||
AI_CONTENT_LABEL_ENABLED: ${AI_CONTENT_LABEL_ENABLED:-}
|
||
@@ -376,6 +380,7 @@ services:
|
||
OLLAMA_THINK: ${OLLAMA_THINK:-}
|
||
OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-}
|
||
OLLAMA_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
OLLAMA_URLS: ${OLLAMA_URLS:-http://ollama:11434}
|
||
OUTCOME_LEARNING_ENABLED: ${OUTCOME_LEARNING_ENABLED:-true}
|
||
OUTCOME_LEARNING_FAIL_OPEN: ${OUTCOME_LEARNING_FAIL_OPEN:-false}
|
||
@@ -434,7 +439,7 @@ services:
|
||
start_period: 10s
|
||
stop_grace_period: 20s
|
||
knowledge:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
restart: unless-stopped
|
||
environment:
|
||
APP_MODE: ${KB_APP_MODE:-editor}
|
||
@@ -449,6 +454,7 @@ services:
|
||
BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD:?Set the Knowledge web password}
|
||
AI_FALLBACK_ENABLED: ${AI_FALLBACK_ENABLED:-false}
|
||
OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
OLLAMA_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-10m}
|
||
OLLAMA_MAX_CONCURRENT: ${OLLAMA_MAX_CONCURRENT:-2}
|
||
@@ -475,7 +481,7 @@ services:
|
||
- ALL
|
||
stop_grace_period: 35s
|
||
control:
|
||
- image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}
|
||
+ image: git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}
|
||
restart: unless-stopped
|
||
environment:
|
||
CONTROL_ADDR: :8070
|
||
diff --git ab/docs/MIGRATION-v1.6.1-to-v1.6.2.md b/docs/MIGRATION-v1.6.1-to-v1.6.2.md
|
||
new file mode 100644
|
||
index 0000000..b34632c
|
||
--- /dev/null
|
||
+++ b/docs/MIGRATION-v1.6.1-to-v1.6.2.md
|
||
@@ -0,0 +1,24 @@
|
||
+# Migration v1.6.1 -> v1.6.2
|
||
+
|
||
+No data-volume migration is required. Keep `neuroforge-data`, Agent data and Knowledge directories intact.
|
||
+
|
||
+1. Set `IMAGE_TAG=1.6.2`.
|
||
+2. Copy the new optional Ollama variables into your `.env`:
|
||
+
|
||
+ ```env
|
||
+ OLLAMA_API_KEY=
|
||
+ NEUROFORGE_OLLAMA_API_KEY=
|
||
+ NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
+ ```
|
||
+
|
||
+ Leave them empty for an unauthenticated native Ollama endpoint.
|
||
+3. If Ollama is protected by a Bearer-aware proxy, normally set only `OLLAMA_API_KEY`. Use the two NeuroForge overrides only when the Master and model worker need different credentials.
|
||
+4. Pull/recreate services without deleting volumes:
|
||
+
|
||
+ ```bash
|
||
+ docker compose --profile research pull
|
||
+ docker compose --profile research up -d --force-recreate --remove-orphans
|
||
+ ```
|
||
+5. For distributed deployments, use the complete `.env` shipped under `deployments/master`, `deployments/cpu-subagent`, and `deployments/gpu-subagent` and preserve the same `NEUROFORGE_WORKER_TOKEN` on Master and subagents.
|
||
+
|
||
+The v1.6.1 OOM/recovery fixes are part of v1.6.2 and must not be removed when merging older deployment files.
|
||
diff --git ab/docs/OLLAMA-BEARER-AUTH.md b/docs/OLLAMA-BEARER-AUTH.md
|
||
new file mode 100644
|
||
index 0000000..ff19322
|
||
--- /dev/null
|
||
+++ b/docs/OLLAMA-BEARER-AUTH.md
|
||
@@ -0,0 +1,36 @@
|
||
+# Ollama Bearer authentication
|
||
+
|
||
+v1.6.2 can call Ollama-compatible endpoints protected by an HTTP Bearer token.
|
||
+
|
||
+## Shared configuration
|
||
+
|
||
+```env
|
||
+OLLAMA_API_KEY=CHANGE_ME
|
||
+```
|
||
+
|
||
+Every supported Ollama request then carries:
|
||
+
|
||
+```http
|
||
+Authorization: Bearer CHANGE_ME
|
||
+```
|
||
+
|
||
+This applies to health/model discovery (`/api/tags`), chat (`/api/chat`) and embeddings (`/api/embed`).
|
||
+
|
||
+## NeuroForge overrides
|
||
+
|
||
+```env
|
||
+NEUROFORGE_OLLAMA_API_KEY=
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
+```
|
||
+
|
||
+`NEUROFORGE_OLLAMA_API_KEY` overrides the shared key for the NeuroForge Master/provider. `NEUROFORGE_WORKER_OLLAMA_API_KEY` overrides it for model-capable NeuroForge workers. If the override is empty, workers fall back to `OLLAMA_API_KEY`; the Master also accepts `OLLAMA_API_KEY` as a shared alias when no NeuroForge-specific key is supplied.
|
||
+
|
||
+## Agent pools
|
||
+
|
||
+The GLPI Agent uses one `OLLAMA_API_KEY` for all URLs in `OLLAMA_URLS`. This is intentional for a pool behind one common authentication boundary. Deploy separate Agent instances or a common gateway if individual nodes require unrelated credentials.
|
||
+
|
||
+## Native Ollama
|
||
+
|
||
+The environment variable configures the **clients**, not the bundled native Ollama server. Native Ollama does not gain access control from this setting alone. To require authentication, place Ollama behind an authentication-capable reverse proxy/gateway and point `OLLAMA_URL`, `OLLAMA_URLS` or `OLLAMA_BASE_URL` at that endpoint.
|
||
+
|
||
+Do not put credentials into Ollama URLs. Keep URLs credential-free and use the Bearer variable.
|
||
diff --git a/mega-project.json b/mega-project.json
|
||
index e97312f..a555b73 100644
|
||
--- a/mega-project.json
|
||
+++ b/mega-project.json
|
||
@@ -20,7 +20,8 @@
|
||
"worker_to_neuroforge": "worker_token",
|
||
"research_to_kb_staging": "kb_integration_token",
|
||
"control_to_agent_graph": "control_read_token",
|
||
- "control_to_neuroforge_graph": "app_api_key_read_only_endpoints"
|
||
+ "control_to_neuroforge_graph": "app_api_key_read_only_endpoints",
|
||
+ "ollama": "optional bearer token via OLLAMA_API_KEY; role-specific NeuroForge overrides supported"
|
||
},
|
||
"knowledge_export": {
|
||
"format": "Obsidian Markdown + YAML frontmatter + Wikilinks",
|
||
@@ -28,7 +29,7 @@
|
||
"schema": "Wiki/Schema.md",
|
||
"glpi_relations": "KnowbaseItem_Item when exposed by GLPI OpenAPI"
|
||
},
|
||
- "version": "1.5.6",
|
||
+ "version": "1.6.2",
|
||
"controlled_learning": {
|
||
"raw_chat_auto_learning": false,
|
||
"validated_outcomes": [
|
||
diff --git a/platform/neuroforge/.env.example b/platform/neuroforge/.env.example
|
||
index a4f0579..4475a6a 100644
|
||
--- a/platform/neuroforge/.env.example
|
||
+++ b/platform/neuroforge/.env.example
|
||
@@ -1,4 +1,8 @@
|
||
# Generate strong random values, e.g. openssl rand -hex 32
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
+# Optional NeuroForge GPU-worker override; falls back to OLLAMA_API_KEY.
|
||
+NEUROFORGE_WORKER_OLLAMA_API_KEY=
|
||
NEUROFORGE_ADMIN_TOKEN=replace-with-random-admin-token
|
||
NEUROFORGE_APP_API_KEY=replace-with-random-app-key
|
||
NEUROFORGE_WORKER_TOKEN=replace-with-random-worker-token
|
||
@@ -7,3 +11,6 @@ NEUROFORGE_CLUSTER_TOKEN=replace-with-shared-random-cluster-token
|
||
|
||
# Optional. You can also set this through the web interface.
|
||
OPENAI_API_KEY=
|
||
+
|
||
+# Optional NeuroForge-specific override; takes precedence over OLLAMA_API_KEY.
|
||
+NEUROFORGE_OLLAMA_API_KEY=
|
||
diff --git a/platform/neuroforge/cmd/server/main.go b/platform/neuroforge/cmd/server/main.go
|
||
index c48a86a..d9b0f56 100644
|
||
--- a/platform/neuroforge/cmd/server/main.go
|
||
+++ b/platform/neuroforge/cmd/server/main.go
|
||
@@ -234,6 +234,7 @@ func run() (retErr error) {
|
||
changed := false
|
||
for name, dst := range map[string]*string{
|
||
"OPENAI_API_KEY": &sec.OpenAIAPIKey,
|
||
+ "NEUROFORGE_OLLAMA_API_KEY": &sec.OllamaAPIKey,
|
||
"NEUROFORGE_ADMIN_TOKEN": &sec.AdminToken,
|
||
"NEUROFORGE_APP_API_KEY": &sec.AppAPIKey,
|
||
"NEUROFORGE_INTEGRATION_TOKEN": &sec.IntegrationToken,
|
||
@@ -247,6 +248,14 @@ func run() (retErr error) {
|
||
changed = true
|
||
}
|
||
}
|
||
+ // OLLAMA_API_KEY is a convenient shared alias. The NeuroForge-specific
|
||
+ // variable wins when both are set.
|
||
+ if strings.TrimSpace(os.Getenv("NEUROFORGE_OLLAMA_API_KEY")) == "" {
|
||
+ if v := strings.TrimSpace(os.Getenv("OLLAMA_API_KEY")); v != "" {
|
||
+ sec.OllamaAPIKey = v
|
||
+ changed = true
|
||
+ }
|
||
+ }
|
||
if changed {
|
||
if err := s.UpdateSecrets(sec); err != nil {
|
||
return err
|
||
diff --git a/platform/neuroforge/cmd/worker/main.go b/platform/neuroforge/cmd/worker/main.go
|
||
index d189683..7ba39d1 100644
|
||
--- a/platform/neuroforge/cmd/worker/main.go
|
||
+++ b/platform/neuroforge/cmd/worker/main.go
|
||
@@ -86,6 +86,7 @@ type workerConfig struct {
|
||
MaxConcurrency int
|
||
Hostname string
|
||
OllamaURL string
|
||
+ OllamaAPIKey string
|
||
OllamaChatModel string
|
||
OllamaEmbedModel string
|
||
OllamaNumCtx int
|
||
@@ -145,6 +146,7 @@ func main() {
|
||
Heartbeat: heartbeat, ResourceClass: resource, Capabilities: caps,
|
||
MaxConcurrency: maxConcurrency, Hostname: hostname(),
|
||
OllamaURL: strings.TrimRight(firstNonEmpty(os.Getenv("NEUROFORGE_WORKER_OLLAMA_URL"), os.Getenv("OLLAMA_BASE_URL"), os.Getenv("OLLAMA_URL")), "/"),
|
||
+ OllamaAPIKey: firstNonEmpty(os.Getenv("NEUROFORGE_WORKER_OLLAMA_API_KEY"), os.Getenv("OLLAMA_API_KEY")),
|
||
OllamaChatModel: firstNonEmpty(os.Getenv("NEUROFORGE_WORKER_OLLAMA_CHAT_MODEL"), os.Getenv("OLLAMA_MODEL")),
|
||
OllamaEmbedModel: firstNonEmpty(os.Getenv("NEUROFORGE_WORKER_OLLAMA_EMBEDDING_MODEL"), os.Getenv("OLLAMA_EMBEDDING_MODEL")),
|
||
OllamaNumCtx: envInt("NEUROFORGE_WORKER_OLLAMA_NUM_CTX", 8192),
|
||
@@ -342,7 +344,7 @@ func ollamaEmbed(ctx context.Context, c *http.Client, cfg workerConfig, p modelE
|
||
Embeddings [][]float32 `json:"embeddings"`
|
||
PromptEvalCount int64 `json:"prompt_eval_count"`
|
||
}
|
||
- if err := postOllama(ctx, c, cfg.OllamaURL+"/api/embed", body, &resp); err != nil {
|
||
+ if err := postOllama(ctx, c, cfg.OllamaURL+"/api/embed", cfg.OllamaAPIKey, body, &resp); err != nil {
|
||
return modelResult{}, err
|
||
}
|
||
if len(resp.Embeddings) == 0 || len(resp.Embeddings[0]) == 0 {
|
||
@@ -385,19 +387,22 @@ func ollamaChat(ctx context.Context, c *http.Client, cfg workerConfig, p modelCh
|
||
PromptEvalCount int64 `json:"prompt_eval_count"`
|
||
EvalCount int64 `json:"eval_count"`
|
||
}
|
||
- if err := postOllama(ctx, c, cfg.OllamaURL+"/api/chat", body, &resp); err != nil {
|
||
+ if err := postOllama(ctx, c, cfg.OllamaURL+"/api/chat", cfg.OllamaAPIKey, body, &resp); err != nil {
|
||
return modelResult{}, err
|
||
}
|
||
return modelResult{Text: strings.TrimSpace(resp.Message.Content), Usage: modelUsage{InputTokens: resp.PromptEvalCount, OutputTokens: resp.EvalCount}, Provider: "ollama", Model: model, NodeID: cfg.ID}, nil
|
||
}
|
||
|
||
-func postOllama(ctx context.Context, c *http.Client, url string, body any, out any) error {
|
||
+func postOllama(ctx context.Context, c *http.Client, url, apiKey string, body any, out any) error {
|
||
raw, _ := json.Marshal(body)
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(raw))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
req.Header.Set("Content-Type", "application/json")
|
||
+ if token := strings.TrimSpace(apiKey); token != "" {
|
||
+ req.Header.Set("Authorization", "Bearer "+token)
|
||
+ }
|
||
resp, err := c.Do(req)
|
||
if err != nil {
|
||
return err
|
||
diff --git ab/platform/neuroforge/cmd/worker/main_test.go b/platform/neuroforge/cmd/worker/main_test.go
|
||
new file mode 100644
|
||
index 0000000..90fd243
|
||
--- /dev/null
|
||
+++ b/platform/neuroforge/cmd/worker/main_test.go
|
||
@@ -0,0 +1,26 @@
|
||
+package main
|
||
+
|
||
+import (
|
||
+ "context"
|
||
+ "encoding/json"
|
||
+ "net/http"
|
||
+ "net/http/httptest"
|
||
+ "testing"
|
||
+)
|
||
+
|
||
+func TestPostOllamaSendsBearerToken(t *testing.T) {
|
||
+ const token = "ollama-secret"
|
||
+ var auth string
|
||
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
+ auth = r.Header.Get("Authorization")
|
||
+ _ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
|
||
+ }))
|
||
+ defer srv.Close()
|
||
+ var out map[string]any
|
||
+ if err := postOllama(context.Background(), srv.Client(), srv.URL, token, map[string]any{"x": 1}, &out); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if auth != "Bearer "+token {
|
||
+ t.Fatalf("authorization=%q", auth)
|
||
+ }
|
||
+}
|
||
diff --git a/platform/neuroforge/docker-compose.yml b/platform/neuroforge/docker-compose.yml
|
||
index 618d6aa..d9f9924 100644
|
||
--- a/platform/neuroforge/docker-compose.yml
|
||
+++ b/platform/neuroforge/docker-compose.yml
|
||
@@ -12,6 +12,8 @@ services:
|
||
NEUROFORGE_METRICS_TOKEN: ${NEUROFORGE_METRICS_TOKEN}
|
||
NEUROFORGE_CLUSTER_TOKEN: ${NEUROFORGE_CLUSTER_TOKEN:-}
|
||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||
+ NEUROFORGE_OLLAMA_API_KEY: ${NEUROFORGE_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
volumes:
|
||
- neuroforge-data:/app/data
|
||
restart: unless-stopped
|
||
@@ -59,6 +61,8 @@ services:
|
||
NEUROFORGE_WORKER_CAPABILITIES: gpu,model.chat,model.embed
|
||
NEUROFORGE_WORKER_MAX_CONCURRENCY: ${NEUROFORGE_GPU_WORKER_CONCURRENCY:-1}
|
||
NEUROFORGE_WORKER_OLLAMA_URL: ${OLLAMA_BASE_URL:-http://ollama:11434}
|
||
+ NEUROFORGE_WORKER_OLLAMA_API_KEY: ${NEUROFORGE_WORKER_OLLAMA_API_KEY:-}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
NEUROFORGE_WORKER_OLLAMA_CHAT_MODEL: ${OLLAMA_MODEL:-gemma3}
|
||
NEUROFORGE_WORKER_OLLAMA_EMBEDDING_MODEL: ${OLLAMA_EMBEDDING_MODEL:-embeddinggemma}
|
||
depends_on:
|
||
diff --git a/platform/neuroforge/internal/core/types.go b/platform/neuroforge/internal/core/types.go
|
||
index c75ef25..fcc08df 100644
|
||
--- a/platform/neuroforge/internal/core/types.go
|
||
+++ b/platform/neuroforge/internal/core/types.go
|
||
@@ -419,6 +419,7 @@ type Config struct {
|
||
|
||
type Secrets struct {
|
||
OpenAIAPIKey string `json:"openai_api_key"`
|
||
+ OllamaAPIKey string `json:"ollama_api_key,omitempty"`
|
||
AppAPIKey string `json:"app_api_key"`
|
||
IntegrationToken string `json:"integration_token,omitempty"`
|
||
ControlReadToken string `json:"control_read_token,omitempty"`
|
||
diff --git a/platform/neuroforge/internal/httpapi/httpapi.go b/platform/neuroforge/internal/httpapi/httpapi.go
|
||
index 254eea6..6017263 100644
|
||
--- a/platform/neuroforge/internal/httpapi/httpapi.go
|
||
+++ b/platform/neuroforge/internal/httpapi/httpapi.go
|
||
@@ -693,7 +693,7 @@ func (s *Server) adminPutModelRouting(w http.ResponseWriter, r *http.Request) {
|
||
|
||
func (s *Server) adminSecretsStatus(w http.ResponseWriter, r *http.Request) {
|
||
sec := s.store.Secrets()
|
||
- s.json(w, 200, map[string]any{"openai_configured": sec.OpenAIAPIKey != "", "app_key_configured": sec.AppAPIKey != "", "integration_token_configured": sec.IntegrationToken != "", "control_read_token_configured": sec.ControlReadToken != "", "worker_token_configured": sec.WorkerToken != "", "metrics_token_configured": sec.MetricsToken != "", "shard_tokens": len(sec.ShardAPIToken), "cluster_token_configured": sec.ClusterToken != ""})
|
||
+ s.json(w, 200, map[string]any{"openai_configured": sec.OpenAIAPIKey != "", "ollama_api_key_configured": sec.OllamaAPIKey != "", "app_key_configured": sec.AppAPIKey != "", "integration_token_configured": sec.IntegrationToken != "", "control_read_token_configured": sec.ControlReadToken != "", "worker_token_configured": sec.WorkerToken != "", "metrics_token_configured": sec.MetricsToken != "", "shard_tokens": len(sec.ShardAPIToken), "cluster_token_configured": sec.ClusterToken != ""})
|
||
}
|
||
func maskedSecret(v string) string {
|
||
if v == "" {
|
||
@@ -708,18 +708,19 @@ func (s *Server) adminGetSecrets(w http.ResponseWriter, r *http.Request) {
|
||
sec := s.store.Secrets()
|
||
reveal := r.URL.Query().Get("reveal") == "1" && s.store.Config().Security.AllowSecretReveal
|
||
if reveal {
|
||
- s.json(w, 200, map[string]any{"revealed": true, "app_api_key": sec.AppAPIKey, "integration_token": sec.IntegrationToken, "control_read_token": sec.ControlReadToken, "worker_token": sec.WorkerToken, "metrics_token": sec.MetricsToken, "shard_api_tokens": sec.ShardAPIToken, "cluster_token": sec.ClusterToken})
|
||
+ s.json(w, 200, map[string]any{"revealed": true, "ollama_api_key": sec.OllamaAPIKey, "app_api_key": sec.AppAPIKey, "integration_token": sec.IntegrationToken, "control_read_token": sec.ControlReadToken, "worker_token": sec.WorkerToken, "metrics_token": sec.MetricsToken, "shard_api_tokens": sec.ShardAPIToken, "cluster_token": sec.ClusterToken})
|
||
return
|
||
}
|
||
maskedShards := map[string]string{}
|
||
for k, v := range sec.ShardAPIToken {
|
||
maskedShards[k] = maskedSecret(v)
|
||
}
|
||
- s.json(w, 200, map[string]any{"revealed": false, "reveal_allowed": s.store.Config().Security.AllowSecretReveal, "app_api_key": maskedSecret(sec.AppAPIKey), "integration_token": maskedSecret(sec.IntegrationToken), "control_read_token": maskedSecret(sec.ControlReadToken), "worker_token": maskedSecret(sec.WorkerToken), "metrics_token": maskedSecret(sec.MetricsToken), "shard_api_tokens": maskedShards, "cluster_token": maskedSecret(sec.ClusterToken)})
|
||
+ s.json(w, 200, map[string]any{"revealed": false, "reveal_allowed": s.store.Config().Security.AllowSecretReveal, "ollama_api_key": maskedSecret(sec.OllamaAPIKey), "app_api_key": maskedSecret(sec.AppAPIKey), "integration_token": maskedSecret(sec.IntegrationToken), "control_read_token": maskedSecret(sec.ControlReadToken), "worker_token": maskedSecret(sec.WorkerToken), "metrics_token": maskedSecret(sec.MetricsToken), "shard_api_tokens": maskedShards, "cluster_token": maskedSecret(sec.ClusterToken)})
|
||
}
|
||
func (s *Server) adminPutSecrets(w http.ResponseWriter, r *http.Request) {
|
||
var q struct {
|
||
OpenAIAPIKey string `json:"openai_api_key,omitempty"`
|
||
+ OllamaAPIKey string `json:"ollama_api_key,omitempty"`
|
||
AppAPIKey string `json:"app_api_key,omitempty"`
|
||
IntegrationToken string `json:"integration_token,omitempty"`
|
||
ControlReadToken string `json:"control_read_token,omitempty"`
|
||
@@ -737,8 +738,13 @@ func (s *Server) adminPutSecrets(w http.ResponseWriter, r *http.Request) {
|
||
_, ok := os.LookupEnv(name)
|
||
return ok && strings.TrimSpace(os.Getenv(name)) != ""
|
||
}
|
||
+ if q.OllamaAPIKey != "" && (envLocked("NEUROFORGE_OLLAMA_API_KEY") || envLocked("OLLAMA_API_KEY")) {
|
||
+ s.err(w, http.StatusConflict, fmt.Errorf("Ollama API key is environment-managed and cannot be changed through the admin API"))
|
||
+ return
|
||
+ }
|
||
for name, value := range map[string]string{
|
||
"OPENAI_API_KEY": q.OpenAIAPIKey,
|
||
+ "NEUROFORGE_OLLAMA_API_KEY": q.OllamaAPIKey,
|
||
"NEUROFORGE_APP_API_KEY": q.AppAPIKey,
|
||
"NEUROFORGE_INTEGRATION_TOKEN": q.IntegrationToken,
|
||
"NEUROFORGE_CONTROL_READ_TOKEN": q.ControlReadToken,
|
||
@@ -754,6 +760,9 @@ func (s *Server) adminPutSecrets(w http.ResponseWriter, r *http.Request) {
|
||
if q.OpenAIAPIKey != "" {
|
||
sec.OpenAIAPIKey = q.OpenAIAPIKey
|
||
}
|
||
+ if q.OllamaAPIKey != "" {
|
||
+ sec.OllamaAPIKey = q.OllamaAPIKey
|
||
+ }
|
||
if q.AppAPIKey != "" {
|
||
sec.AppAPIKey = q.AppAPIKey
|
||
}
|
||
@@ -899,7 +908,7 @@ func configuredModelAvailable(models map[string]bool, configured string) bool {
|
||
return false
|
||
}
|
||
|
||
-func checkConfiguredOllamaModels(ctx context.Context, cfg core.Config) (bool, any) {
|
||
+func checkConfiguredOllamaModels(ctx context.Context, cfg core.Config, apiKey string) (bool, any) {
|
||
type tagsResponse struct {
|
||
Models []struct {
|
||
Name string `json:"name"`
|
||
@@ -917,6 +926,9 @@ func checkConfiguredOllamaModels(ctx context.Context, cfg core.Config) (bool, an
|
||
details[node.ID] = err.Error()
|
||
continue
|
||
}
|
||
+ if token := strings.TrimSpace(apiKey); token != "" {
|
||
+ req.Header.Set("Authorization", "Bearer "+token)
|
||
+ }
|
||
resp, err := http.DefaultClient.Do(req)
|
||
if err != nil {
|
||
details[node.ID] = err.Error()
|
||
@@ -977,7 +989,7 @@ func (s *Server) readyz(w http.ResponseWriter, r *http.Request) {
|
||
if s.readinessOllamaLive {
|
||
ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second)
|
||
var detail any
|
||
- ollamaLiveReady, detail = checkConfiguredOllamaModels(ctx, cfg)
|
||
+ ollamaLiveReady, detail = checkConfiguredOllamaModels(ctx, cfg, s.store.Secrets().OllamaAPIKey)
|
||
cancel()
|
||
components["ollama_live_models"] = detail
|
||
}
|
||
diff --git a/platform/neuroforge/internal/provider/provider.go b/platform/neuroforge/internal/provider/provider.go
|
||
index 806c05a..1642e2c 100644
|
||
--- a/platform/neuroforge/internal/provider/provider.go
|
||
+++ b/platform/neuroforge/internal/provider/provider.go
|
||
@@ -307,7 +307,7 @@ func (r *Router) chatOllama(ctx context.Context, o core.OllamaServer, model, ins
|
||
}
|
||
requestCtx, cancel := optionalTimeout(ctx, o.RequestTimeoutSeconds)
|
||
defer cancel()
|
||
- if err := r.doJSON(requestCtx, "POST", cleanBase(o.BaseURL)+"/api/chat", "", body, &out); err != nil {
|
||
+ if err := r.doJSON(requestCtx, "POST", cleanBase(o.BaseURL)+"/api/chat", r.store.Secrets().OllamaAPIKey, body, &out); err != nil {
|
||
return ChatResult{}, fmt.Errorf("ollama %s: %w", o.Name, err)
|
||
}
|
||
if strings.TrimSpace(out.Message.Content) == "" {
|
||
@@ -327,7 +327,7 @@ func (r *Router) embedOllama(ctx context.Context, o core.OllamaServer, model, te
|
||
}
|
||
requestCtx, cancel := optionalTimeout(ctx, o.RequestTimeoutSeconds)
|
||
defer cancel()
|
||
- if err := r.doJSON(requestCtx, "POST", cleanBase(o.BaseURL)+"/api/embed", "", body, &out); err != nil {
|
||
+ if err := r.doJSON(requestCtx, "POST", cleanBase(o.BaseURL)+"/api/embed", r.store.Secrets().OllamaAPIKey, body, &out); err != nil {
|
||
return EmbedResult{}, fmt.Errorf("ollama %s: %w", o.Name, err)
|
||
}
|
||
if len(out.Embeddings) == 0 || len(out.Embeddings[0]) == 0 {
|
||
@@ -459,6 +459,9 @@ func (r *Router) Health(ctx context.Context) []map[string]any {
|
||
}
|
||
healthCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||
req, _ := http.NewRequestWithContext(healthCtx, "GET", cleanBase(o.BaseURL)+"/api/tags", nil)
|
||
+ if token := strings.TrimSpace(r.store.Secrets().OllamaAPIKey); token != "" {
|
||
+ req.Header.Set("Authorization", "Bearer "+token)
|
||
+ }
|
||
resp, err := r.http.Do(req)
|
||
if err != nil {
|
||
cancel()
|
||
diff --git a/platform/neuroforge/internal/provider/runtime_test.go b/platform/neuroforge/internal/provider/runtime_test.go
|
||
index ee1577a..b804b95 100644
|
||
--- a/platform/neuroforge/internal/provider/runtime_test.go
|
||
+++ b/platform/neuroforge/internal/provider/runtime_test.go
|
||
@@ -111,3 +111,48 @@ func TestOllamaExplicitRequestTimeoutStillWorks(t *testing.T) {
|
||
t.Fatalf("configured timeout was not enforced promptly: %v", time.Since(start))
|
||
}
|
||
}
|
||
+
|
||
+func TestOllamaBearerTokenIsSentToChatAndHealth(t *testing.T) {
|
||
+ const token = "ollama-secret"
|
||
+ var chatAuth, tagsAuth string
|
||
+ fake := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
+ switch r.URL.Path {
|
||
+ case "/api/chat":
|
||
+ chatAuth = r.Header.Get("Authorization")
|
||
+ _ = json.NewEncoder(w).Encode(map[string]any{"message": map[string]any{"content": "ok"}})
|
||
+ case "/api/tags":
|
||
+ tagsAuth = r.Header.Get("Authorization")
|
||
+ _ = json.NewEncoder(w).Encode(map[string]any{"models": []map[string]any{{"name": "chat"}, {"name": "embed"}}})
|
||
+ default:
|
||
+ http.NotFound(w, r)
|
||
+ }
|
||
+ }))
|
||
+ defer fake.Close()
|
||
+ s, err := store.New(t.TempDir())
|
||
+ if err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ defer s.Close()
|
||
+ cfg := s.Config()
|
||
+ cfg.Ollama = []core.OllamaServer{{ID: "secure", Name: "secure", BaseURL: fake.URL, ChatModel: "chat", EmbeddingModel: "embed", Weight: 1, Enabled: true}}
|
||
+ cfg.Routing.ChatProvider = "ollama"
|
||
+ if err := s.UpdateConfig(cfg); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ sec := s.Secrets()
|
||
+ sec.OllamaAPIKey = token
|
||
+ if err := s.UpdateSecrets(sec); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ r := NewRouter(s)
|
||
+ if _, err := r.Chat(context.Background(), "ollama", "chat", "", "hello", 32); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ _ = r.Health(context.Background())
|
||
+ if chatAuth != "Bearer "+token {
|
||
+ t.Fatalf("chat authorization=%q", chatAuth)
|
||
+ }
|
||
+ if tagsAuth != "Bearer "+token {
|
||
+ t.Fatalf("tags authorization=%q", tagsAuth)
|
||
+ }
|
||
+}
|
||
diff --git ab/runtime/agent-data/.gitkeep b/runtime/agent-data/.gitkeep
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
diff --git ab/runtime/backups/.gitkeep b/runtime/backups/.gitkeep
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
diff --git ab/runtime/knowledge/.gitkeep b/runtime/knowledge/.gitkeep
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
diff --git ab/runtime/ollama/.gitkeep b/runtime/ollama/.gitkeep
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
diff --git ab/runtime/staging/.gitkeep b/runtime/staging/.gitkeep
|
||
new file mode 100644
|
||
index 0000000..e69de29
|
||
diff --git ab/scripts/create-network.sh b/scripts/create-network.sh
|
||
new file mode 100755
|
||
index 0000000..28d743e
|
||
--- /dev/null
|
||
+++ b/scripts/create-network.sh
|
||
@@ -0,0 +1,9 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+NETWORK="${CORE_NETWORK:-glpi-ai-core}"
|
||
+if docker network inspect "$NETWORK" >/dev/null 2>&1; then
|
||
+ echo "Docker-Netzwerk $NETWORK existiert bereits."
|
||
+else
|
||
+ docker network create "$NETWORK" >/dev/null
|
||
+ echo "Docker-Netzwerk $NETWORK wurde angelegt."
|
||
+fi
|
||
diff --git ab/scripts/health.sh b/scripts/health.sh
|
||
new file mode 100755
|
||
index 0000000..fefa361
|
||
--- /dev/null
|
||
+++ b/scripts/health.sh
|
||
@@ -0,0 +1,13 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+AGENT_URL="${AGENT_URL:-http://127.0.0.1:9980}"
|
||
+KNOWLEDGE_URL="${KNOWLEDGE_URL:-http://127.0.0.1:9981}"
|
||
+printf 'Agent /healthz: '
|
||
+curl -fsS "$AGENT_URL/healthz" || true
|
||
+echo
|
||
+printf 'Agent /readyz: '
|
||
+curl -fsS "$AGENT_URL/readyz" || true
|
||
+echo
|
||
+printf 'Knowledge /api/health: '
|
||
+curl -fsS "$KNOWLEDGE_URL/api/health" || true
|
||
+echo
|
||
diff --git ab/scripts/preflight-role.sh b/scripts/preflight-role.sh
|
||
new file mode 100755
|
||
index 0000000..ef560ff
|
||
--- /dev/null
|
||
+++ b/scripts/preflight-role.sh
|
||
@@ -0,0 +1,19 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+ROLE="${1:-}"
|
||
+case "$ROLE" in agent|knowledge|ollama|combined) ;; *) echo "Usage: $0 agent|knowledge|ollama|combined" >&2; exit 2;; esac
|
||
+DIR="$(CDPATH= cd -- "$(dirname -- "$0")/../deployments/$ROLE" && pwd)"
|
||
+ENVFILE="$DIR/.env"
|
||
+[ -f "$ENVFILE" ] || { echo "Fehlt: $ENVFILE" >&2; exit 1; }
|
||
+if grep -Eq '^[A-Z0-9_]+=CHANGE_ME' "$ENVFILE"; then
|
||
+ echo "Hinweis: CHANGE_ME-Werte vorhanden:"
|
||
+ grep -E '^[A-Z0-9_]+=CHANGE_ME' "$ENVFILE" | cut -d= -f1
|
||
+ if [ "$ROLE" = agent ] || [ "$ROLE" = combined ]; then exit 1; fi
|
||
+fi
|
||
+mkdir -p "$DIR/../../runtime/agent-data" "$DIR/../../runtime/knowledge" "$DIR/../../runtime/backups" "$DIR/../../runtime/staging" "$DIR/../../runtime/ollama"
|
||
+if command -v docker >/dev/null 2>&1; then
|
||
+ (cd "$DIR" && docker compose config >/dev/null)
|
||
+ echo "docker compose config: OK ($ROLE)"
|
||
+else
|
||
+ echo "Docker CLI nicht vorhanden; YAML wurde beim Release statisch geprüft."
|
||
+fi
|
||
diff --git a/scripts/secret-scan.sh b/scripts/secret-scan.sh
|
||
index f16be47..a238aed 100755
|
||
--- a/scripts/secret-scan.sh
|
||
+++ b/scripts/secret-scan.sh
|
||
@@ -23,10 +23,29 @@ fi
|
||
|
||
# Private keys and common live-token shapes must not be committed. Placeholders in
|
||
# templates/docs are intentionally allowed.
|
||
-if grep -E '(^|/)\.env$|\.pem$|\.p12$|\.pfx$|(^|/)id_rsa$|(^|/)id_ed25519$' "$FILES" >/dev/null; then
|
||
+if grep -E '(^|/)\.env$|\.pem$|\.p12$|\.pfx$|(^|/)id_rsa$|(^|/)id_ed25519$' "$FILES" \
|
||
+ | grep -Ev '^deployments/[^/]+/\.env$' >/dev/null; then
|
||
bad "private environment/key material found"
|
||
fi
|
||
|
||
+# Deployment role .env files are intentionally shipped as complete templates.
|
||
+# Reject them if a sensitive field is populated with anything other than an
|
||
+# empty value or an explicit CHANGE_ME placeholder.
|
||
+for f in deployments/*/.env; do
|
||
+ [ -f "$f" ] || continue
|
||
+ awk -F= '
|
||
+ /^[[:space:]]*#/ || NF < 2 { next }
|
||
+ $1 ~ /(PASSWORD|SECRET|TOKEN|API_KEY|CLIENT_SECRET)$/ {
|
||
+ v=$0; sub(/^[^=]*=/,"",v)
|
||
+ if (v != "" && v !~ /^CHANGE_ME/) {
|
||
+ print FILENAME ": unsafe populated secret field " $1 > "/dev/stderr"
|
||
+ bad=1
|
||
+ }
|
||
+ }
|
||
+ END { exit bad ? 1 : 0 }
|
||
+ ' "$f" || bad "deployment .env contains a populated secret: $f"
|
||
+done
|
||
+
|
||
if [ "$HAVE_GIT" = true ]; then
|
||
if git grep -nE -- '-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----' -- ':!*.example' ':!*.md' >"$KEYS" 2>/dev/null; then
|
||
cat "$KEYS" >&2; bad "private key material found"
|
||
diff --git ab/scripts/verify-package.sh b/scripts/verify-package.sh
|
||
new file mode 100755
|
||
index 0000000..8070ced
|
||
--- /dev/null
|
||
+++ b/scripts/verify-package.sh
|
||
@@ -0,0 +1,19 @@
|
||
+#!/usr/bin/env sh
|
||
+set -eu
|
||
+ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||
+cd "$ROOT"
|
||
+
|
||
+for f in \
|
||
+ services/agent/Dockerfile \
|
||
+ services/knowledge/Dockerfile \
|
||
+ .gitea/workflows/ci.yml \
|
||
+ .gitea/workflows/release.yml \
|
||
+ docker-bake.hcl; do
|
||
+ test -f "$f" || { echo "missing: $f" >&2; exit 1; }
|
||
+done
|
||
+
|
||
+grep -q '^FROM ' services/agent/Dockerfile
|
||
+grep -q 'AS data-init' services/agent/Dockerfile
|
||
+grep -q '^FROM ' services/knowledge/Dockerfile
|
||
+
|
||
+echo "OK: Dockerfiles and repository workflows are present."
|
||
diff --git a/services/agent/.env.example b/services/agent/.env.example
|
||
index f60b51f..4738654 100644
|
||
--- a/services/agent/.env.example
|
||
+++ b/services/agent/.env.example
|
||
@@ -290,6 +290,8 @@ OLLAMA_REQUIRE_EMBEDDING_MODEL=true
|
||
# OLLAMA_MODEL=qwen3:8b
|
||
# Embedding-Modell für RAG.
|
||
OLLAMA_EMBEDDING_MODEL=embeddinggemma
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
|
||
# Modellspezifisches Retrieval-Prompting.
|
||
# auto = Modell automatisch erkennen; für embeddinggemma empfohlen.
|
||
@@ -799,4 +801,4 @@ QUEUE_SIZE=256
|
||
# Ticketpipelines gleichzeitig verteilen, wie Worker aktiv sind. Für drei
|
||
# gleichartige Nodes ist WORKERS=3 ein sinnvoller Lasttest; jeder Node bleibt
|
||
# zusätzlich durch OLLAMA_NODE_MAX_INFLIGHT begrenzt.
|
||
-WORKERS=2
|
||
\ No newline at end of file
|
||
+WORKERS=2
|
||
diff --git a/services/agent/cmd/agent/main.go b/services/agent/cmd/agent/main.go
|
||
index d413c74..8b57d04 100644
|
||
--- a/services/agent/cmd/agent/main.go
|
||
+++ b/services/agent/cmd/agent/main.go
|
||
@@ -62,7 +62,7 @@ func main() {
|
||
nodes = append(nodes, ollama.NodeConfig{Name: name, URL: nodeURL, Weight: weight})
|
||
}
|
||
o, err := ollama.NewPool(ollama.PoolConfig{
|
||
- Nodes: nodes, RoutingMode: cfg.OllamaRoutingMode, NodeMaxInflight: cfg.OllamaNodeMaxInflight,
|
||
+ APIKey: cfg.OllamaAPIKey, Nodes: nodes, RoutingMode: cfg.OllamaRoutingMode, NodeMaxInflight: cfg.OllamaNodeMaxInflight,
|
||
HealthInterval: cfg.OllamaNodeHealthInterval, FailureCooldown: cfg.OllamaNodeFailureCooldown,
|
||
NodeRequestTimeout: cfg.OllamaNodeRequestTimeout, FailoverEnabled: cfg.OllamaFailoverEnabled,
|
||
FailoverAttempts: cfg.OllamaFailoverAttempts, RequireSameModelDigest: cfg.OllamaRequireSameDigest,
|
||
diff --git a/services/agent/docker-compose.registry.yml b/services/agent/docker-compose.registry.yml
|
||
index d8eee4c..b075921 100644
|
||
--- a/services/agent/docker-compose.registry.yml
|
||
+++ b/services/agent/docker-compose.registry.yml
|
||
@@ -8,6 +8,7 @@ services:
|
||
DATA_DIR: /app/data
|
||
KNOWLEDGE_DIR: /app/knowledge
|
||
OLLAMA_URL: ${OLLAMA_URL:-http://ollama:11434}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-10m}
|
||
OLLAMA_NUM_PREDICT: ${OLLAMA_NUM_PREDICT:-768}
|
||
OLLAMA_JSON_RETRIES: ${OLLAMA_JSON_RETRIES:-1}
|
||
diff --git a/services/agent/docker-compose.yml b/services/agent/docker-compose.yml
|
||
index d6b4492..92e9667 100644
|
||
--- a/services/agent/docker-compose.yml
|
||
+++ b/services/agent/docker-compose.yml
|
||
@@ -24,6 +24,7 @@ services:
|
||
DATA_DIR: /app/data
|
||
KNOWLEDGE_DIR: /app/knowledge
|
||
OLLAMA_URL: ${OLLAMA_URL:-http://ollama:11434}
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
# Local CPU inference can take several minutes on the first request.
|
||
OLLAMA_TIMEOUT: ${OLLAMA_TIMEOUT:-10m}
|
||
OLLAMA_NUM_PREDICT: ${OLLAMA_NUM_PREDICT:-768}
|
||
diff --git a/services/agent/internal/config/config.go b/services/agent/internal/config/config.go
|
||
index d660a1a..e274989 100644
|
||
--- a/services/agent/internal/config/config.go
|
||
+++ b/services/agent/internal/config/config.go
|
||
@@ -39,6 +39,7 @@ type Config struct {
|
||
GLPIAllowedStatusIDs []int64
|
||
|
||
OllamaURL string // legacy single-node value
|
||
+ OllamaAPIKey string // optional Bearer token sent to every Ollama node
|
||
OllamaURLs []string
|
||
OllamaNodeNames []string
|
||
OllamaNodeWeights []int
|
||
@@ -246,6 +247,7 @@ func Load() (Config, error) {
|
||
GLPIAllowInsecureHTTP: envBool("GLPI_ALLOW_INSECURE_HTTP", false),
|
||
GLPIAllowedStatusIDs: envInt64List("GLPI_ALLOWED_STATUS_IDS", "1"),
|
||
OllamaURL: strings.TrimRight(env("OLLAMA_URL", "http://ollama:11434"), "/"),
|
||
+ OllamaAPIKey: strings.TrimSpace(os.Getenv("OLLAMA_API_KEY")),
|
||
OllamaURLs: envStringListPreserveCase("OLLAMA_URLS", ""),
|
||
OllamaNodeNames: envStringListPreserveCase("OLLAMA_NODE_NAMES", ""),
|
||
OllamaNodeWeights: envIntListAllowEmpty("OLLAMA_NODE_WEIGHTS"),
|
||
diff --git a/services/agent/internal/ollama/pool.go b/services/agent/internal/ollama/pool.go
|
||
index 6341700..bd0579f 100644
|
||
--- a/services/agent/internal/ollama/pool.go
|
||
+++ b/services/agent/internal/ollama/pool.go
|
||
@@ -29,6 +29,7 @@ type NodeConfig struct {
|
||
}
|
||
|
||
type PoolConfig struct {
|
||
+ APIKey string
|
||
Nodes []NodeConfig
|
||
RoutingMode string
|
||
NodeMaxInflight int
|
||
@@ -390,6 +391,7 @@ func (p *Pool) checkNode(ctx context.Context, n *poolNode) (string, string, erro
|
||
if err != nil {
|
||
return "", "", err
|
||
}
|
||
+ setBearer(req, p.cfg.APIKey)
|
||
resp, err := p.http.Do(req)
|
||
if err != nil {
|
||
return "", "", err
|
||
@@ -559,6 +561,7 @@ func (p *Pool) doPost(ctx context.Context, n *poolNode, path string, body []byte
|
||
}
|
||
req.Header.Set("Content-Type", "application/json")
|
||
req.Header.Set("Accept", "application/json")
|
||
+ setBearer(req, p.cfg.APIKey)
|
||
resp, err := p.http.Do(req)
|
||
if err != nil {
|
||
return 0, nil, err
|
||
@@ -574,6 +577,13 @@ func (p *Pool) doPost(ctx context.Context, n *poolNode, path string, body []byte
|
||
return resp.StatusCode, raw, nil
|
||
}
|
||
|
||
+func setBearer(req *http.Request, token string) {
|
||
+ token = strings.TrimSpace(token)
|
||
+ if token != "" {
|
||
+ req.Header.Set("Authorization", "Bearer "+token)
|
||
+ }
|
||
+}
|
||
+
|
||
func (p *Pool) selectNode(ctx context.Context, excluded map[string]struct{}, stage string) (*poolNode, int64, error) {
|
||
for {
|
||
nodes := p.orderedCandidates(excluded, stage)
|
||
diff --git a/services/agent/internal/ollama/pool_test.go b/services/agent/internal/ollama/pool_test.go
|
||
index ed3bd03..eacf386 100644
|
||
--- a/services/agent/internal/ollama/pool_test.go
|
||
+++ b/services/agent/internal/ollama/pool_test.go
|
||
@@ -388,3 +388,41 @@ func TestPoolFastestRecentProbesUnmeasuredNodes(t *testing.T) {
|
||
t.Fatalf("fastest_recent must measure both nodes before preferring one: a=%d b=%d", callsA.Load(), callsB.Load())
|
||
}
|
||
}
|
||
+
|
||
+func TestPoolSendsBearerToHealthAndInference(t *testing.T) {
|
||
+ const token = "ollama-secret"
|
||
+ var tagsAuth, embedAuth string
|
||
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
+ switch r.URL.Path {
|
||
+ case "/api/tags":
|
||
+ tagsAuth = r.Header.Get("Authorization")
|
||
+ tagsResponse(w, "chat-digest", "embed-digest")
|
||
+ case "/api/embed":
|
||
+ embedAuth = r.Header.Get("Authorization")
|
||
+ _ = json.NewEncoder(w).Encode(map[string]any{"embeddings": [][]float64{{1, 0}}})
|
||
+ default:
|
||
+ http.NotFound(w, r)
|
||
+ }
|
||
+ }))
|
||
+ defer srv.Close()
|
||
+ c, err := NewPool(PoolConfig{
|
||
+ APIKey: token, Nodes: []NodeConfig{{Name: "secure", URL: srv.URL, Weight: 1}}, RoutingMode: "least_inflight",
|
||
+ NodeMaxInflight: 1, HealthInterval: time.Minute, FailureCooldown: time.Second, NodeRequestTimeout: time.Second,
|
||
+ FailoverEnabled: false, FailoverAttempts: 1, RequireSameModelDigest: true, RequireEmbeddingModel: true, Model: "m", EmbeddingModel: "e",
|
||
+ }, "m", "e", "de-DE", "formal", 128, time.Minute, false, 0)
|
||
+ if err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if err := c.Ping(context.Background()); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if _, err := c.Embed(context.Background(), []string{"x"}); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if tagsAuth != "Bearer "+token {
|
||
+ t.Fatalf("tags authorization=%q", tagsAuth)
|
||
+ }
|
||
+ if embedAuth != "Bearer "+token {
|
||
+ t.Fatalf("embed authorization=%q", embedAuth)
|
||
+ }
|
||
+}
|
||
diff --git a/services/control/engineering-graph.json b/services/control/engineering-graph.json
|
||
index e7c5396..77c70d6 100644
|
||
--- a/services/control/engineering-graph.json
|
||
+++ b/services/control/engineering-graph.json
|
||
@@ -2464,7 +2464,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 1063,
|
||
+ "line": 1065,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2476,7 +2476,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 440,
|
||
+ "line": 442,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2488,7 +2488,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 224,
|
||
+ "line": 225,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2500,7 +2500,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1037,
|
||
+ "line": 1039,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2512,7 +2512,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1208,
|
||
+ "line": 1210,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2524,7 +2524,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1252,
|
||
+ "line": 1254,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2536,7 +2536,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1241,
|
||
+ "line": 1243,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2548,7 +2548,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1219,
|
||
+ "line": 1221,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2560,7 +2560,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1230,
|
||
+ "line": 1232,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2572,7 +2572,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1086,
|
||
+ "line": 1088,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2584,7 +2584,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1108,
|
||
+ "line": 1110,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2596,7 +2596,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1191,
|
||
+ "line": 1193,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2608,7 +2608,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1052,
|
||
+ "line": 1054,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2620,7 +2620,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1183,
|
||
+ "line": 1185,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2632,7 +2632,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1130,
|
||
+ "line": 1132,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2644,7 +2644,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1156,
|
||
+ "line": 1158,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2656,7 +2656,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1046,
|
||
+ "line": 1048,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2668,7 +2668,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1082,
|
||
+ "line": 1084,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2680,7 +2680,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1023,
|
||
+ "line": 1025,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2692,7 +2692,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 1018,
|
||
+ "line": 1020,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -2704,7 +2704,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/config",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 993,
|
||
+ "line": 995,
|
||
"path": "services/agent/internal/config/config.go"
|
||
}
|
||
},
|
||
@@ -5716,7 +5716,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 272,
|
||
+ "line": 273,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5728,7 +5728,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 262,
|
||
+ "line": 263,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5740,7 +5740,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 243,
|
||
+ "line": 244,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5752,7 +5752,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 667,
|
||
+ "line": 677,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5764,7 +5764,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 388,
|
||
+ "line": 389,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5776,7 +5776,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 549,
|
||
+ "line": 551,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5788,7 +5788,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 596,
|
||
+ "line": 606,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5800,7 +5800,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 453,
|
||
+ "line": 455,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5812,7 +5812,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 280,
|
||
+ "line": 281,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5824,7 +5824,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 577,
|
||
+ "line": 587,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5836,7 +5836,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 680,
|
||
+ "line": 690,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5848,7 +5848,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 774,
|
||
+ "line": 784,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5860,7 +5860,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 731,
|
||
+ "line": 741,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5872,7 +5872,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 369,
|
||
+ "line": 370,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5896,7 +5896,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 709,
|
||
+ "line": 719,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5908,7 +5908,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 693,
|
||
+ "line": 703,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5920,7 +5920,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 763,
|
||
+ "line": 773,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5932,7 +5932,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 715,
|
||
+ "line": 725,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5944,7 +5944,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 438,
|
||
+ "line": 440,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5956,7 +5956,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 161,
|
||
+ "line": 162,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -5992,7 +5992,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 703,
|
||
+ "line": 713,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6004,7 +6004,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 78,
|
||
+ "line": 79,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6016,7 +6016,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 540,
|
||
+ "line": 542,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6028,7 +6028,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 92,
|
||
+ "line": 93,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6040,7 +6040,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 104,
|
||
+ "line": 105,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6052,7 +6052,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 87,
|
||
+ "line": 88,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6064,7 +6064,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 135,
|
||
+ "line": 136,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6076,7 +6076,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 745,
|
||
+ "line": 755,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6088,7 +6088,19 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 740,
|
||
+ "line": 750,
|
||
+ "path": "services/agent/internal/ollama/pool.go"
|
||
+ }
|
||
+ },
|
||
+ {
|
||
+ "id": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer",
|
||
+ "kind": "function",
|
||
+ "label": "setBearer",
|
||
+ "group": "engineering",
|
||
+ "community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
+ "meta": {
|
||
+ "exported": false,
|
||
+ "line": 580,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -6112,7 +6124,7 @@
|
||
"community": "github.com/example/glpi-ai-agent/internal/ollama",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 736,
|
||
+ "line": 746,
|
||
"path": "services/agent/internal/ollama/pool.go"
|
||
}
|
||
},
|
||
@@ -7756,7 +7768,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 292,
|
||
+ "line": 293,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7768,7 +7780,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 304,
|
||
+ "line": 305,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7804,7 +7816,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 345,
|
||
+ "line": 346,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7828,7 +7840,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 311,
|
||
+ "line": 312,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7840,7 +7852,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 284,
|
||
+ "line": 285,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7864,7 +7876,7 @@
|
||
"community": "kb-editor/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 337,
|
||
+ "line": 338,
|
||
"path": "services/knowledge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -7948,7 +7960,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 40,
|
||
+ "line": 41,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -7960,7 +7972,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 76,
|
||
+ "line": 77,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -7972,7 +7984,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 106,
|
||
+ "line": 107,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -7984,7 +7996,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 73,
|
||
+ "line": 74,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -7996,7 +8008,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 74,
|
||
+ "line": 75,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -8008,7 +8020,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 72,
|
||
+ "line": 73,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -8020,7 +8032,7 @@
|
||
"community": "kb-editor/internal/aifallback",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 110,
|
||
+ "line": 111,
|
||
"path": "services/knowledge/internal/aifallback/ollama.go"
|
||
}
|
||
},
|
||
@@ -9832,7 +9844,7 @@
|
||
"community": "neuroforge/cmd/server",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 623,
|
||
+ "line": 632,
|
||
"path": "platform/neuroforge/cmd/server/main.go"
|
||
}
|
||
},
|
||
@@ -9916,7 +9928,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 255,
|
||
+ "line": 257,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9928,7 +9940,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 416,
|
||
+ "line": 421,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9940,7 +9952,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 454,
|
||
+ "line": 459,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9952,7 +9964,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 507,
|
||
+ "line": 512,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9964,7 +9976,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 495,
|
||
+ "line": 500,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9976,7 +9988,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 486,
|
||
+ "line": 491,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -9988,7 +10000,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 468,
|
||
+ "line": 473,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10000,7 +10012,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 239,
|
||
+ "line": 241,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10012,7 +10024,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 446,
|
||
+ "line": 451,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10024,7 +10036,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 100,
|
||
+ "line": 101,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10036,7 +10048,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 105,
|
||
+ "line": 106,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10048,7 +10060,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 110,
|
||
+ "line": 111,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10060,7 +10072,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 478,
|
||
+ "line": 483,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10072,7 +10084,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 120,
|
||
+ "line": 121,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10084,7 +10096,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 354,
|
||
+ "line": 356,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10096,7 +10108,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 332,
|
||
+ "line": 334,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10108,7 +10120,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 189,
|
||
+ "line": 191,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10120,7 +10132,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 421,
|
||
+ "line": 426,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10132,7 +10144,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 394,
|
||
+ "line": 396,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10144,7 +10156,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 235,
|
||
+ "line": 237,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10156,7 +10168,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 279,
|
||
+ "line": 281,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10168,7 +10180,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 178,
|
||
+ "line": 180,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -10180,7 +10192,7 @@
|
||
"community": "neuroforge/cmd/worker",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 227,
|
||
+ "line": 229,
|
||
"path": "platform/neuroforge/cmd/worker/main.go"
|
||
}
|
||
},
|
||
@@ -12124,7 +12136,7 @@
|
||
"community": "neuroforge/internal/core",
|
||
"meta": {
|
||
"exported": true,
|
||
- "line": 741,
|
||
+ "line": 742,
|
||
"path": "platform/neuroforge/internal/core/types.go"
|
||
}
|
||
},
|
||
@@ -12340,7 +12352,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 827,
|
||
+ "line": 836,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12352,7 +12364,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 801,
|
||
+ "line": 810,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12388,7 +12400,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 823,
|
||
+ "line": 832,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12544,7 +12556,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 790,
|
||
+ "line": 799,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12616,7 +12628,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 785,
|
||
+ "line": 794,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12784,7 +12796,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 813,
|
||
+ "line": 822,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -12808,7 +12820,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 816,
|
||
+ "line": 825,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13348,7 +13360,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 884,
|
||
+ "line": 893,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13384,7 +13396,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 949,
|
||
+ "line": 961,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13396,7 +13408,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 859,
|
||
+ "line": 868,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13456,7 +13468,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 837,
|
||
+ "line": 846,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13600,7 +13612,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 902,
|
||
+ "line": 911,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -13612,7 +13624,7 @@
|
||
"community": "neuroforge/internal/httpapi",
|
||
"meta": {
|
||
"exported": false,
|
||
- "line": 888,
|
||
+ "line": 897,
|
||
"path": "platform/neuroforge/internal/httpapi/httpapi.go"
|
||
}
|
||
},
|
||
@@ -21102,7 +21114,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21113,7 +21125,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21124,7 +21136,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-control:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21135,7 +21147,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-knowledge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21146,7 +21158,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21157,7 +21169,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -21168,7 +21180,7 @@
|
||
"community": "compose",
|
||
"status": "configured",
|
||
"meta": {
|
||
- "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.1}"
|
||
+ "image": "git.send.nrw/sendnrw/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG:?Set IMAGE_TAG to an immutable release tag, for example 1.6.2}"
|
||
}
|
||
},
|
||
{
|
||
@@ -32967,6 +32979,12 @@
|
||
"to": "function:github.com/example/glpi-ai-agent/internal/ollama:requestStage",
|
||
"kind": "defines"
|
||
},
|
||
+ {
|
||
+ "id": "file:services/agent/internal/ollama/pool.go-\u003efunction:github.com/example/glpi-ai-agent/internal/ollama:setBearer:defines",
|
||
+ "from": "file:services/agent/internal/ollama/pool.go",
|
||
+ "to": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer",
|
||
+ "kind": "defines"
|
||
+ },
|
||
{
|
||
"id": "file:services/agent/internal/ollama/pool.go-\u003efunction:github.com/example/glpi-ai-agent/internal/ollama:withStage:defines",
|
||
"from": "file:services/agent/internal/ollama/pool.go",
|
||
@@ -43734,6 +43752,12 @@
|
||
"to": "function:github.com/example/glpi-ai-agent/internal/ollama:modelNameMatches",
|
||
"kind": "calls"
|
||
},
|
||
+ {
|
||
+ "id": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.checkNode-\u003efunction:github.com/example/glpi-ai-agent/internal/ollama:setBearer:calls",
|
||
+ "from": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.checkNode",
|
||
+ "to": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer",
|
||
+ "kind": "calls"
|
||
+ },
|
||
{
|
||
"id": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.checkNode-\u003epackage:encoding/json:calls_package",
|
||
"from": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.checkNode",
|
||
@@ -43769,6 +43793,12 @@
|
||
"kind": "calls_package",
|
||
"label": "TrimSpace"
|
||
},
|
||
+ {
|
||
+ "id": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.doPost-\u003efunction:github.com/example/glpi-ai-agent/internal/ollama:setBearer:calls",
|
||
+ "from": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.doPost",
|
||
+ "to": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer",
|
||
+ "kind": "calls"
|
||
+ },
|
||
{
|
||
"id": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.doPost-\u003epackage:bytes:calls_package",
|
||
"from": "function:github.com/example/glpi-ai-agent/internal/ollama:Pool.doPost",
|
||
@@ -44018,6 +44048,13 @@
|
||
"kind": "calls_package",
|
||
"label": "Now"
|
||
},
|
||
+ {
|
||
+ "id": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer-\u003epackage:strings:calls_package",
|
||
+ "from": "function:github.com/example/glpi-ai-agent/internal/ollama:setBearer",
|
||
+ "to": "package:strings",
|
||
+ "kind": "calls_package",
|
||
+ "label": "TrimSpace"
|
||
+ },
|
||
{
|
||
"id": "function:github.com/example/glpi-ai-agent/internal/ollama:uniqueStrings-\u003epackage:strings:calls_package",
|
||
"from": "function:github.com/example/glpi-ai-agent/internal/ollama:uniqueStrings",
|
||
@@ -56620,6 +56657,13 @@
|
||
"kind": "calls_package",
|
||
"label": "NewRequestWithContext"
|
||
},
|
||
+ {
|
||
+ "id": "function:neuroforge/internal/provider:Router.Health-\u003epackage:strings:calls_package",
|
||
+ "from": "function:neuroforge/internal/provider:Router.Health",
|
||
+ "to": "package:strings",
|
||
+ "kind": "calls_package",
|
||
+ "label": "TrimSpace"
|
||
+ },
|
||
{
|
||
"id": "function:neuroforge/internal/provider:Router.chatOllama-\u003efunction:neuroforge/internal/provider:Router.doJSON:calls",
|
||
"from": "function:neuroforge/internal/provider:Router.chatOllama",
|
||
@@ -66863,10 +66907,10 @@
|
||
}
|
||
],
|
||
"meta": {
|
||
- "edges": 7273,
|
||
+ "edges": 7278,
|
||
"format_version": 1,
|
||
"generator": "go-ast+compose",
|
||
"modules": 4,
|
||
- "nodes": 1845
|
||
+ "nodes": 1846
|
||
}
|
||
}
|
||
diff --git a/services/knowledge/.env.example b/services/knowledge/.env.example
|
||
index 9289ba6..e560405 100644
|
||
--- a/services/knowledge/.env.example
|
||
+++ b/services/knowledge/.env.example
|
||
@@ -1,4 +1,6 @@
|
||
# Betriebsmodus desselben Images:
|
||
+# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
|
||
+OLLAMA_API_KEY=
|
||
# editor = vollständiger Einzel-/Masseneditor mit Schreibzugriff
|
||
# google = schreibgeschützte Helpdesk-Suchmaschine mit Artikel-Viewer
|
||
APP_MODE=editor
|
||
diff --git a/services/knowledge/cmd/server/main.go b/services/knowledge/cmd/server/main.go
|
||
index c2b3233..cf4fbb1 100644
|
||
--- a/services/knowledge/cmd/server/main.go
|
||
+++ b/services/knowledge/cmd/server/main.go
|
||
@@ -269,6 +269,7 @@ func aiServiceFromEnv(mode string, st *staging.Store) (*aifallback.Service, time
|
||
}
|
||
svc, err := aifallback.New(aifallback.Config{
|
||
BaseURL: envOr("OLLAMA_BASE_URL", "http://ollama:11434"),
|
||
+ APIKey: strings.TrimSpace(os.Getenv("OLLAMA_API_KEY")),
|
||
Model: strings.TrimSpace(os.Getenv("OLLAMA_MODEL")),
|
||
Timeout: timeout,
|
||
MaxConcurrent: maxConcurrent,
|
||
diff --git a/services/knowledge/docker-compose.dual.yml b/services/knowledge/docker-compose.dual.yml
|
||
index bdcc023..7574e60 100644
|
||
--- a/services/knowledge/docker-compose.dual.yml
|
||
+++ b/services/knowledge/docker-compose.dual.yml
|
||
@@ -49,6 +49,7 @@ services:
|
||
BASIC_AUTH_PASSWORD: "${SEARCH_AUTH_PASSWORD:-}"
|
||
AI_FALLBACK_ENABLED: "${AI_FALLBACK_ENABLED:-false}"
|
||
OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://ollama:11434}"
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
OLLAMA_MODEL: "${OLLAMA_MODEL:-}"
|
||
OLLAMA_TIMEOUT: "${OLLAMA_TIMEOUT:-10m}"
|
||
OLLAMA_MAX_CONCURRENT: "${OLLAMA_MAX_CONCURRENT:-1}"
|
||
diff --git a/services/knowledge/docker-compose.yml b/services/knowledge/docker-compose.yml
|
||
index f05a373..739ec1d 100644
|
||
--- a/services/knowledge/docker-compose.yml
|
||
+++ b/services/knowledge/docker-compose.yml
|
||
@@ -18,6 +18,7 @@ services:
|
||
BASIC_AUTH_PASSWORD: "${BASIC_AUTH_PASSWORD:-}"
|
||
AI_FALLBACK_ENABLED: "${AI_FALLBACK_ENABLED:-false}"
|
||
OLLAMA_BASE_URL: "${OLLAMA_BASE_URL:-http://ollama:11434}"
|
||
+ OLLAMA_API_KEY: ${OLLAMA_API_KEY:-}
|
||
OLLAMA_MODEL: "${OLLAMA_MODEL:-}"
|
||
OLLAMA_TIMEOUT: "${OLLAMA_TIMEOUT:-10m}"
|
||
OLLAMA_MAX_CONCURRENT: "${OLLAMA_MAX_CONCURRENT:-1}"
|
||
diff --git a/services/knowledge/internal/aifallback/ollama.go b/services/knowledge/internal/aifallback/ollama.go
|
||
index 7a0c673..b8e8806 100644
|
||
--- a/services/knowledge/internal/aifallback/ollama.go
|
||
+++ b/services/knowledge/internal/aifallback/ollama.go
|
||
@@ -18,6 +18,7 @@ import (
|
||
type Config struct {
|
||
BaseURL string
|
||
Model string
|
||
+ APIKey string
|
||
Timeout time.Duration
|
||
MaxConcurrent int
|
||
AutoReply bool
|
||
@@ -148,6 +149,9 @@ func (s *Service) askOllama(ctx context.Context, query string) (staging.Draft, e
|
||
}
|
||
req.Header.Set("Content-Type", "application/json")
|
||
req.Header.Set("Accept", "application/json")
|
||
+ if token := strings.TrimSpace(s.cfg.APIKey); token != "" {
|
||
+ req.Header.Set("Authorization", "Bearer "+token)
|
||
+ }
|
||
resp, err := s.client.Do(req)
|
||
if err != nil {
|
||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||
diff --git a/services/knowledge/internal/aifallback/ollama_test.go b/services/knowledge/internal/aifallback/ollama_test.go
|
||
index 593af81..5282cdb 100644
|
||
--- a/services/knowledge/internal/aifallback/ollama_test.go
|
||
+++ b/services/knowledge/internal/aifallback/ollama_test.go
|
||
@@ -50,3 +50,27 @@ func TestGenerateUsesStructuredChatAndStoresResult(t *testing.T) {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
+
|
||
+func TestGenerateSendsOllamaBearerToken(t *testing.T) {
|
||
+ const token = "ollama-secret"
|
||
+ var auth string
|
||
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
+ auth = r.Header.Get("Authorization")
|
||
+ _ = json.NewEncoder(w).Encode(map[string]any{"message": map[string]any{"content": `{"title":"T","text":"X","answer":"A","categories":[],"keywords":[]}`}})
|
||
+ }))
|
||
+ defer server.Close()
|
||
+ st, err := staging.New(t.TempDir())
|
||
+ if err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ svc, err := New(Config{BaseURL: server.URL, Model: "test", APIKey: token, Timeout: time.Second, MaxConcurrent: 1, MinScore: 0.78}, st)
|
||
+ if err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if _, err := svc.Generate(context.Background(), "secure ollama request"); err != nil {
|
||
+ t.Fatal(err)
|
||
+ }
|
||
+ if auth != "Bearer "+token {
|
||
+ t.Fatalf("authorization=%q", auth)
|
||
+ }
|
||
+}
|