From d18405ed2ebe5d8325d0a8ebc84bd584a8ae6575 Mon Sep 17 00:00:00 2001 From: groot Date: Wed, 9 Sep 2026 12:55:35 +0200 Subject: [PATCH] update-1.6.2 --- .env.example | 6 + .env.subagent.example | 9 +- .gitea/workflows/ci.yml | 63 +- .gitea/workflows/release.yml | 135 +- BUILD-AND-RELEASE.md | 47 +- DISTRIBUTED-DEPLOYMENT-BUNDLE.md | 14 +- MANIFEST.sha256 | 1285 ++-- README.md | 38 +- RELEASE-NOTES-v1.6.2.md | 77 +- deployments/README.md | 35 +- deployments/agent/docker-compose.yml | 1 + deployments/combined/docker-compose.yml | 1 + deployments/gpu-subagent/docker-compose.yml | 2 + deployments/knowledge/docker-compose.yml | 1 + deployments/master/docker-compose.yml | 4 + .../master/monitoring/prometheus/alerts.yml | 4 +- docker-bake.hcl | 45 +- docker-compose.subagent.yml | 6 +- docker-compose.yml | 6 + docs/MIGRATION-v1.6.1-to-v1.6.2.md | 28 +- docs/OLLAMA-BEARER-AUTH.md | 36 + mega-project.json | 5 +- patches/SHA256SUMS | 1 + patches/v1.6.1-to-v1.6.2.diff | 6802 +++++++++++++++++ platform/neuroforge/.env.example | 7 + platform/neuroforge/cmd/server/main.go | 9 + platform/neuroforge/cmd/worker/main.go | 11 +- platform/neuroforge/cmd/worker/main_test.go | 26 + platform/neuroforge/docker-compose.yml | 4 + platform/neuroforge/internal/core/types.go | 1 + .../neuroforge/internal/httpapi/httpapi.go | 22 +- .../neuroforge/internal/provider/provider.go | 7 +- .../internal/provider/runtime_test.go | 45 + scripts/secret-scan.sh | 47 +- services/agent/.env.example | 4 +- services/agent/cmd/agent/main.go | 2 +- services/agent/docker-compose.registry.yml | 1 + services/agent/docker-compose.yml | 1 + services/agent/internal/config/config.go | 2 + services/agent/internal/ollama/pool.go | 10 + services/agent/internal/ollama/pool_test.go | 38 + services/control/engineering-graph.json | 252 +- services/knowledge/.env.example | 2 + services/knowledge/cmd/server/main.go | 1 + services/knowledge/docker-compose.dual.yml | 1 + services/knowledge/docker-compose.yml | 1 + .../knowledge/internal/aifallback/ollama.go | 4 + .../internal/aifallback/ollama_test.go | 24 + 48 files changed, 8062 insertions(+), 1111 deletions(-) create mode 100644 docs/OLLAMA-BEARER-AUTH.md create mode 100644 patches/v1.6.1-to-v1.6.2.diff create mode 100644 platform/neuroforge/cmd/worker/main_test.go diff --git a/.env.example b/.env.example index 81a1bac..d3c6f7d 100644 --- a/.env.example +++ b/.env.example @@ -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 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 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 a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 1c80f04..5c5d928 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,4 +1,4 @@ -name: mega-ci +name: ci on: push: @@ -7,7 +7,7 @@ on: branches: [main] jobs: - go-quality: + test: runs-on: ubuntu-latest strategy: fail-fast: false @@ -18,69 +18,22 @@ jobs: - services/knowledge - services/control steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: go-version-file: ${{ matrix.module }}/go.mod - cache-dependency-path: ${{ matrix.module }}/go.mod - + cache: false - name: Test working-directory: ${{ matrix.module }} run: go test ./... - - name: Vet working-directory: ${{ matrix.module }} run: go vet ./... - - name: Build - working-directory: ${{ matrix.module }} - run: go build ./... - - static-release-gates: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install PyYAML - run: python3 -m pip install --user PyYAML - - - name: Static production preflight - run: ./scripts/preflight.sh --static - - - name: Compose environment isolation - run: ./scripts/check-compose-env.py - - - name: Secret scan - run: ./scripts/secret-scan.sh - - - name: YAML syntax check - shell: bash - run: | - python3 - <<'PY' - from pathlib import Path - import yaml - files = list(Path('.').rglob('*.yml')) + list(Path('.').rglob('*.yaml')) - for p in files: - if '.git' in p.parts: - continue - with p.open(encoding='utf-8') as f: - yaml.safe_load(f) - print(f'parsed {len(files)} YAML files') - PY - docker-build: runs-on: ubuntu-latest - needs: [go-quality, static-release-gates] steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build all project images + - 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.yml b/.gitea/workflows/release.yml index d69e1d5..d63343f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,4 +1,4 @@ -name: mega-release +name: release on: push: @@ -10,136 +10,33 @@ jobs: runs-on: ubuntu-latest env: REGISTRY: git.send.nrw/sendnrw + RUNNER_TOOL_CACHE: /toolcache steps: - - name: Checkout - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Validate immutable release version - id: meta - shell: bash - run: | - set -euo pipefail - REF_NAME="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" - VERSION="${REF_NAME#v}" - test -n "$VERSION" - FILE_VERSION="$(tr -d '\r\n' < VERSION)" - test "$VERSION" = "$FILE_VERSION" || { - echo "Tag version $VERSION does not match VERSION=$FILE_VERSION" >&2 - exit 1 - } - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 with: config-inline: | [registry."git.send.nrw"] http = true insecure = true - - - name: Login to registry - uses: docker/login-action@v3 + - uses: docker/login-action@v3 with: registry: git.send.nrw username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - - - name: Push NeuroForge server - uses: docker/build-push-action@v6 - with: - context: ./platform/neuroforge - file: ./platform/neuroforge/Dockerfile - target: server - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-neuroforge:${{ steps.meta.outputs.version }} - - - name: Push NeuroForge worker - uses: docker/build-push-action@v6 - with: - context: ./platform/neuroforge - file: ./platform/neuroforge/Dockerfile - target: worker - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-neuroforge-worker:${{ steps.meta.outputs.version }} - - - name: Push Agent - uses: docker/build-push-action@v6 - with: - context: ./services/agent - file: ./services/agent/Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-agent:${{ steps.meta.outputs.version }} - - - name: Push Agent data-init - uses: docker/build-push-action@v6 - with: - context: ./services/agent - file: ./services/agent/Dockerfile - target: data-init - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-agent-data-init:${{ steps.meta.outputs.version }} - - - name: Push Knowledge - uses: docker/build-push-action@v6 - with: - context: ./services/knowledge - file: ./services/knowledge/Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-knowledge:${{ steps.meta.outputs.version }} - - - name: Push Control - uses: docker/build-push-action@v6 - with: - context: ./services/control - file: ./services/control/Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/glpi-neuroforge-mega-control:${{ steps.meta.outputs.version }} - - release-archive: - needs: release-images - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Validate and package + - name: Resolve immutable release version + id: meta shell: bash run: | set -euo pipefail - REF_NAME="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" - VERSION="${REF_NAME#v}" - test "$VERSION" = "$(tr -d '\r\n' < VERSION)" - ./scripts/secret-scan.sh - find . -type f ! -path './.git/*' ! -name MANIFEST.sha256 -print0 \ - | sort -z \ - | xargs -0 sha256sum > MANIFEST.sha256 - ROOT_DIR="$(pwd)" - PKG_DIR="../glpi-neuroforge-mega-v${VERSION}-gitea" - rm -rf "$PKG_DIR" - mkdir -p "$PKG_DIR" - tar --exclude=.git -cf - . | (cd "$PKG_DIR" && tar -xf -) - cd .. - zip -qr "glpi-neuroforge-mega-v${VERSION}-gitea.zip" "glpi-neuroforge-mega-v${VERSION}-gitea" - sha256sum "glpi-neuroforge-mega-v${VERSION}-gitea.zip" > "glpi-neuroforge-mega-v${VERSION}-gitea.zip.sha256" - - - name: Upload release archive - uses: actions/upload-artifact@v4 - with: - name: glpi-neuroforge-mega-${{ gitea.ref_name }} - path: | - ../glpi-neuroforge-mega-*.zip - ../glpi-neuroforge-mega-*.zip.sha256 + 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 a/BUILD-AND-RELEASE.md b/BUILD-AND-RELEASE.md index 8aa6a12..325daa8 100644 --- a/BUILD-AND-RELEASE.md +++ b/BUILD-AND-RELEASE.md @@ -1,10 +1,8 @@ -# Build & Release — GLPI NeuroForge Mega v1.6.2 +# Build and Release -The repository has one canonical image/release pipeline. Nested service workflows are intentionally not used. +## Repository image set -## Project images - -`docker buildx bake` builds all six immutable project images: +The repository publishes six immutable project images: - `glpi-neuroforge-mega-neuroforge` - `glpi-neuroforge-mega-neuroforge-worker` @@ -13,42 +11,33 @@ The repository has one canonical image/release pipeline. Nested service workflow - `glpi-neuroforge-mega-knowledge` - `glpi-neuroforge-mega-control` -Default registry/tag: +The default registry is `git.send.nrw/sendnrw`. -```text -git.send.nrw/sendnrw/:1.6.2 -``` +## Local build -Override without editing the bake file: +From repository root: ```bash -IMAGE_TAG=1.6.2 REGISTRY=git.send.nrw/sendnrw docker buildx bake +docker buildx bake ``` -## Gitea Actions +Override registry/tag when required: -`.gitea/workflows/ci.yml` runs test/vet/build for all four Go modules, static release checks and Docker builds. - -`.gitea/workflows/release.yml` runs only on immutable `v*` tags. The tag must exactly match the root `VERSION` file. It pushes all six project images with the version tag only; no production dependency on `latest` is introduced. - -Required registry secrets: - -```text -DOCKER_USERNAME -DOCKER_PASSWORD +```bash +REGISTRY=registry.example/org IMAGE_TAG=1.6.2 docker buildx bake ``` -Release example: +## 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 ``` -## Local source gate - -```bash -./scripts/release-gate.sh -``` - -This executes static production checks, Compose environment isolation, secret scanning, graph reproducibility, test/vet/build for every module, and targeted race checks. +`.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 a/DISTRIBUTED-DEPLOYMENT-BUNDLE.md b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md index 812078f..a031908 100644 --- a/DISTRIBUTED-DEPLOYMENT-BUNDLE.md +++ b/DISTRIBUTED-DEPLOYMENT-BUNDLE.md @@ -1,13 +1,5 @@ -# Distributed Deployment — v1.6.2 +# v1.6.2 Distributed Deployment Bundle -The distributed deployment is part of the canonical release, not a separate code fork. +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. -- `deployments/master`: authoritative NeuroForge Master, GLPI Agent, Knowledge, Control, optional Research, Prometheus and Grafana. -- `deployments/cpu-subagent`: remote CPU execution node for graph/relink work. -- `deployments/gpu-subagent`: remote GPU execution node plus Ollama for chat/embedding work. - -All three roles ship with complete `.env` templates containing `CHANGE_ME_...` placeholders. The same real `NEUROFORGE_WORKER_TOKEN` must be configured on Master and both worker roles. - -Recommended startup order: GPU subagent, CPU subagent, then Master. Keep the NeuroForge data volume authoritative on the Master only. - -The v1.6.1 recovery/OOM fixes are included unchanged in v1.6.2. Do not delete the existing `neuroforge-data` volume when upgrading. +See `deployments/README.md`. diff --git a/MANIFEST.sha256 b/MANIFEST.sha256 index 1031d68..94c9f34 100644 --- a/MANIFEST.sha256 +++ b/MANIFEST.sha256 @@ -1,640 +1,645 @@ -27dc46be5cbb1b171deff7fbd2f28bff1be802dff403797535fd8968bb98c8eb .cbmignore -2b175ba266562ae5c6b9176e69287f1e4ad843f85da0ee0ca62a9ccc6fb0236e .env.example -e5ae1e5c6c25efc5dd362aec2056b6b354c7044a30f1a43704b157b4b512dee6 .env.subagent.example -4ff38172ec186fe954432741b4311a8a9cac0f89144fb9dca2ec3523e4321210 .gitea/workflows/ci.yml -2fe4f14b9dd2e20e50dc12866d163fdfae7f21267e99aaa00ec65523f0cbbd94 .gitea/workflows/release.yml -e1ff71187cc3411a85067b964264011db7bd109a585ef7b9ea5b08bda039d813 .gitignore -cdbdf1b5b66e81ea25cd6a7ebb707f4e638925202d6b3db10dc02cea1683d484 BUILD-AND-RELEASE.md -6fe6ff56b5370b68a99cc968ca63ce8863b22a001a9d714794abc19ee051b3b2 DISTRIBUTED-DEPLOYMENT-BUNDLE.md -ca915a34965137f3b709a862672d26077c17fb79f7e5c838cda8663ddfeac05a MIGRATION-FROM-MEGA.md -ccfc4c139345a69d05cd2b809b0d09b4332d98e5b4f79aace4db0b648e3ea814 Makefile -335d334606432df08ff1b85683161814ec0ddd8c6dcbd0ec1dfda0eba4c6ff12 README.md -4858caa52c0fb6cf302a1c581d07d448e5e90e0daa797e5819610fd6223bd348 RELEASE-NOTES-v1.1.0.md -01163462f46314f57660677fdef407c6c2884412ea850aab13a4f650e8c29f50 RELEASE-NOTES-v1.2.0.md -4da388ce660aa3b7a0d8075ec066a025b5437960973397360fcb9a5d4cb58c96 RELEASE-NOTES-v1.3.0.md -257d6d3ea1d22b8ab97adb5d35ca5ac806702d75cea66982dc711754040fc7a2 RELEASE-NOTES-v1.4.0.md -0723a0151b72d492899a5c5409e1fef37de1fe4c458c3053f2f7eccd4b6e70c5 RELEASE-NOTES-v1.4.1.md -58923d3292f11487526b8dc4d6c0992025c0f946df8435fde5c91ef4581d46fd RELEASE-NOTES-v1.4.2.md -548f3168f8f29add4f510c4c8048a74465f311dbc3ba2436e5b8db5cc898c552 RELEASE-NOTES-v1.4.3.md -61ceebe5a891388336795fda2fd0d1ad10373b4ebaae7d2e670f35766115c604 RELEASE-NOTES-v1.4.4.md -15a3defdd5bbf07ebaec1a2e8626347a027a3622f10afb508f7a21c9b0bdfa71 RELEASE-NOTES-v1.4.5.md -d10b1dbd87585d72144d816449800dc17b6d7f8faba2dfb4eca93ae3def2c631 RELEASE-NOTES-v1.5.0.md -d069259c99322966b525ab451defd6386ea182bdbdba58122e287df395e2396e RELEASE-NOTES-v1.6.0.md -cd02749b3f18cf635e2ee78f8895c50a397317ad8ccb416311f07a653126233d RELEASE-NOTES-v1.6.1.md -7f4e6dab2bca1c45677c958e71b18797efe3b4d36ebb0230cf995adc0f17623d RELEASE-NOTES-v1.6.2.md -a2d5e268fe70979caccd895156b3d33bd61a3cc7e660ab7a5b07066849b3e248 VERSION -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 backups/.gitkeep -8127e9db5e5e0af1d88770dc8fa60b381de45dbcc843262698cf9501409b4d58 deploy/searxng/settings.yml -4111b49c5e3eb1d1f455d7907b22460fb2ab990ab40888c2462f73a84f7f5731 deployments/README.md -dd91e3bec5c38d1b962e3d5e5511fbc4a298f0434a58f17cc41cb750b6ebc8d1 deployments/agent/.env -0df71e0cca461b82f4991368c6f53c11bcaf62fdbdb3fda0f3aad1555e1bad36 deployments/agent/docker-compose.build.yml -7de0310fc57f7459a176a2106406038207ef2907d9c307e0db106af93ea6bcad deployments/agent/docker-compose.yml -88be11f84b4db7533c5a2ecd1b53b01a27f86eea576592a29f46136fdc550c69 deployments/combined/.env -1cdd0d8f851d7f7d8533443d02496a9f368e4a4194faf39408ea67487b5f77d7 deployments/combined/docker-compose.build.yml -90068015004bc5d08f0ce1f4992a630147341e110fb606230ca603fa6f552b61 deployments/combined/docker-compose.yml -8e0f2dc98203b807798e3bb4bb0b88710f4a0838badbce099acf1ea8ea87b73f deployments/cpu-subagent/.env -fa1f4ebcf753f7d3ba7b49a1bf9b9738d2f9f3cf1b88b3ed4403a819de196b4a deployments/cpu-subagent/docker-compose.yml -dead361b179b6f761b160bd302f304b62941781cd542aba29eb5cfca8ec0497c deployments/cpu-subagent/preflight.sh -6f97ad0b9585efd18f604984987cfe67e4424cd6ae703ff9fd34aba4b56c22de deployments/gpu-subagent/.env -89dbaa1fa401a02ac9242450fa3119caf3d4f63e66d5b25af4cb100f1a8be351 deployments/gpu-subagent/docker-compose.yml -36d44f3bde10d228881273f04a309cbddba394c8bc559cb48f7d37ee8f2f4e3a deployments/gpu-subagent/preflight.sh -9614f2cee637b847ba1506335971652b6b6bf601254d3d9e098b4a9b54fffcde deployments/knowledge/.env -81013b617610a4903ac7e237c231d62e0aa5caa083d73a5a838316db31d9dc55 deployments/knowledge/docker-compose.build.yml -d5ddeb7e193e735b50422771ab580610de82a5099650c6153f9f092cfdbbbc97 deployments/knowledge/docker-compose.yml -05a2a5c066a5474988e50e45a84623060162e58f908eae09d83fbc36b4e3886e deployments/master/.env -ce3d84ef0ff7fc8df89223e29d746b8b6700f69969b2f3750a646f0b2cadd04b deployments/master/docker-compose.yml -0c2fc23f8f6deced618e025cc66fa5711349439814d8a2c6d0f347990b4632e1 deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json -083e25e212b70a4c0fb5cd8d2cd5a5735f8ae230809c7fac40187f785a8eb43d deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml -72305a9fded6b40fe740ec82dac495f006990e6e37de754d1fcd01d93da4834a deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml -0026163136fe3a51e140c73bdad1fa227bd2b38c9f6a69c391cd9d0647ad4e48 deployments/master/monitoring/prometheus/alerts.yml -aac90d7e75a79256f77d730ca80fc1824e86f4299192526910b6f0226cedcbb9 deployments/master/monitoring/prometheus/prometheus.yml.template -57fa3c3be7f476c791da0fd7e9985b38be82f1d9a816d13d75f645f29de57e86 deployments/master/preflight.sh -a3df591bb129f55e3860d36201936dd8ce2080c82dfe59094ad79387ce27557f deployments/ollama/.env -15a8680873d1dbde2b7deca0a30b786f49f4498f499269e9cd24c33e0b243333 deployments/ollama/docker-compose.yml -8d4943a3e023ed3b24312ff0b406298026b4d32aac6873b0c8488812b20dba4b deployments/ollama/pull-models.sh -750138d7d6a4ba4f8a0cbf572722b949f5ebc31d97ff0c33314fafa451ced3a1 docker-bake.hcl -58db2a87b703a5bbddc20b140907d187f8388e7db87b079ae9f6ffaac9c121fc docker-compose.dev.yml -91123a501c0cf14a57c8c7fe3742a366a4cac3ed8229225f797d00282db160fd docker-compose.subagent.yml -d8891ae37a17269f0e4a8eb7eb09c0824271bd537e9fde48f31015d7d9f3df75 docker-compose.yml -e4c516375e450c43b5193b1257880c02f2132b05a16e19ab1c96d57182fa070c docs/ARCHITECTURE.md -34e2c7ac5992389cc51b40f740c1e206f20bffc67bdf19cb59d94b5df67a7082 docs/BACKUP-RESTORE-v1.5.0.md -9467a3c0796a87bada0eab6ad191913e628b4abc05520cbb4b73fac76d334481 docs/CODEBASE-MEMORY-MCP.md -74cab9b3ce3f6ed2bb5f1de99e1fe5c6f311e90550d078e10e140b726674957e docs/CONTROL-CENTER.md -88955b0ea289ccc63cbd3404f5b00bb446b38270408dcee23a6103a698f1f76b docs/CONTROL-MATRIX.md -323402668da4d8e1d97a29c45db753834f6c3a16739eebebf7e016f06af160bf docs/CONTROLLED-AUTONOMY.md -51b4a8d7daff787f099b77bd5a33ebf8038c0812bd3056f0ee6afe17e103681d docs/ENV-MIGRATION-FROM-LEGACY.md -a19522149d097ede5751dd6b9db515146ac47d14aaa7a7b49515e495388d0e0e docs/ENVIRONMENT.md -9c49332c6d0e23cd7f82adf23f61bcbd795ad30401990ae99b23295b2ab4cd09 docs/GO-LIVE-v1.5.0.md -6a9862c2ffa95420a3f8d2c3901a46f00c77a38826913280bd0e367e3133498f docs/IMPLEMENTED.md -65ab928c3a0c283131e48322685d240507a085c1fb059d4c114465774473c335 docs/MASTER-SUBAGENT-ORCHESTRATOR.md -9c38807cc12fba6f94cadc4694996de58e6d2d5396e55fc0c4bd404bb13f53ad docs/MIGRATION-CUTOVER.md -a71d28353529906cee08cf90f35e0b0cf97bbc94ccfcef830145a2d561b21a9c docs/MIGRATION-MANIFEST.md -4330b3adefd8c40d174e1e878ff7cf075d99a44ec51251953d1a58d3a0fa8ca3 docs/MIGRATION-v1.1.0-to-v1.2.0.md -46672e5984cdcf5f31e88dccc273041aa277552cc9c5ecbf59a46b3de133da7b docs/MIGRATION-v1.2.0-to-v1.3.0.md -3738dc79be0598316dc397f1fcc71cedda67b604a3038b116c2ea001dc3105ce docs/MIGRATION-v1.3.0-to-v1.4.0.md -b285d050223844f5fd05014c2278199eaccd7525fbbf8a6dae724ed87ced0ee8 docs/MIGRATION-v1.4.4-to-v1.4.5.md -57858f4f2b6670106edf5a88ce705649d547c65c26407d6117040296122a0e26 docs/MIGRATION-v1.4.5-to-v1.5.0.md -c905084e03788887894e37c040492b8ce2386ecc25ac3ab2e10bfd4b0a7cd167 docs/MIGRATION-v1.5.0-to-v1.5.1.md -12619874bce206ed6e817685d2def62244d6bcceb6eeccbca9b0936fed74b8a2 docs/MIGRATION-v1.5.1-to-v1.5.2.md -44219c5fe32a0d6d2c32719e243793a54f3b7624cb45a497cda78c4a443774ae docs/MIGRATION-v1.5.2-to-v1.5.3.md -fd78b77e18277a8bb81cf6ad33e67c0e7fae3977dc1bbf74787b0cb0cb9a4b4e docs/MIGRATION-v1.5.3-to-v1.5.4.md -4e2e595c0c1b78271a971f6e039c48416114ad9916ad7afc5293c2cff4ec8f9d docs/MIGRATION-v1.5.4-to-v1.5.5.md -b38aaf9d352ad70a9701566fc24d73d26c50c407291fe1c38b08b1afad9435bc docs/MIGRATION-v1.5.5-to-v1.5.6.md -b57e66a589cff7331d547afc0e031accd1d576c46c013143f76311a84eacbcfe docs/MIGRATION-v1.5.6-to-v1.5.7.md -6c234e8bfcfa63f879f0748ccf9ca006595f5ee905b97d316f685154a4a5a2b9 docs/MIGRATION-v1.5.7-to-v1.5.8.md -91e8541a86f3b6b2080439b3177309b584f5d6c637baa286d7ad9d40afbbdba9 docs/MIGRATION-v1.5.8-to-v1.5.9.md -1d114256ef855284dd5a11b40682ce67ad27e17b41045625cae251a16531d0bd docs/MIGRATION-v1.5.9-to-v1.6.0.md -c23f60d75e790ab7317d6bc06c42ccbdb303bce1c11ace2410326257312058b9 docs/MIGRATION-v1.6.0-to-v1.6.1.md -a58ff9a9c9f0842716a7a532556850487d4aacc6032b69c0ffb69df8a4ffff9f docs/MIGRATION-v1.6.1-to-v1.6.2.md -2a01fb10a3e04eae1800a7c7e0aafc31e9bbb23584cea54d849b004716ef81b4 docs/OBSIDIAN-EXPORT.md -040010a807178d33797106e04716822f8d147d7d5833c1ba70865a1827f8484a docs/OPERATIONS.md -69ea49bc76690ac489aea908f4e784a14293e92d62478a240eabea53c5f90820 docs/QUALITY-REPLAY-example.json -bd2d3c43a09a89fefd0e844433064d7d7384a414179a754c6de94232d738fb04 docs/QUALITY-REPLAY.md -6d72b49b5b0ce93b94597cf5688403c468cd3b82f4d87470d80f108c5a7ec531 docs/RELEASE-v1.5.1.md -84b12cafd6320d781b99970cb48b7252da5aff94797d45af164f8a56dee94e5c docs/RELEASE-v1.5.2.md -f0ab4fa6b353059acf6b41a1e5a484ebd698684102724eddf8f8b91d7ff321a3 docs/RELEASE-v1.5.3.md -bc9067063b13788393fc3389ac60c0db50e35cc9d7d733311fca2fc6923834f1 docs/RELEASE-v1.5.4.md -677bf7eacb64adce080dcc1184dca0d7c088727b1c49242df14a7694eabbb593 docs/RELEASE-v1.5.5.md -4cde69f2875d5f97544fb85ff066eb671a40d9670302c81b2a9d9b9a95a9bee3 docs/RELEASE-v1.5.6.md -f7bbf78e76592762fe6e2a08eff9110a273962ae3145da287c5875355672db7f docs/RELEASE-v1.5.7.md -5757dd833f1f8b7b0a7b90b09324cb8989496ea6326d652d659a59f215a3172d docs/RELEASE-v1.5.8.md -245f942b72da17cb6c953720bd9c6742f71723be06bdb842c512851aca351e3e docs/RELEASE-v1.5.9.md -85160313c5dd89faea2e7404f090a6cef13a9b33758124dfc4d748a119375c32 docs/RELEASE-v1.6.0.md -be749a09ddbcd4cf427316c6fe531f138e9bfaa6c104f4c11a08f7555231e8b2 docs/UNIFIED-GRAPH.md -77d91565660789c621b05bd69fecfa3c2ef736f93855eca1f58b017e61c76f90 docs/VALIDATION-v1.5.0.md -3ea1953b2b4eb8f966310a65a12a681fb44e4eb3857b843312e235759aacfce5 docs/VALIDATION.md -be4810451750abb676eee0edcc6f164d86f372f32b185f6f730bf84538cef4a8 exports/knowledge-obsidian-snapshot.zip -dfa65e55e9ccf642ae5ef8ef91c62f4220b31ce4304ea4e1061f2e7d8c1b0fb4 exports/knowledge-obsidian-snapshot.zip.sha256 -99ffd5f497239a5e17b4e4bc79c7a1b5e1e41fdaf9b3f07beb7179050bbddcc4 go.work -5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a knowledge/01_active-directory.json -70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc knowledge/01_arbeitsplatzdrucker.json -96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 knowledge/01_fachanwendung-storung.json -521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 knowledge/01_festnetztelefon.json -88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 knowledge/01_hardwarebeschaffung.json -4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d knowledge/01_kennwort-zurucksetzen.json -5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 knowledge/01_lan.json -95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 knowledge/01_neue-it-anforderung.json -ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 knowledge/01_padagogisches-netzwerk.json -f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 knowledge/01_pc-und-notebook.json -ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc knowledge/01_sonstiges-und-unklare-zuordnung.json -5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 knowledge/01_verdachtige-e-mail-und-phishing.json -5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json -ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 knowledge/02_fachanwendung-bedienung-und-beratung.json -0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 knowledge/02_gruppenrichtlinien.json -4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 knowledge/02_monitor-und-dockingstation.json -bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 knowledge/02_netzwerkdrucker.json -7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 knowledge/02_rufnummer-und-nebenstelle.json -efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 knowledge/02_schadsoftware-und-virenfund.json -2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 knowledge/02_schulverwaltungsnetz.json -830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 knowledge/02_softwarebeschaffung.json -fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 knowledge/02_wlan.json -26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 knowledge/03_ad-gruppen-und-gruppenrichtlinien.json -b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a knowledge/03_dateiablagen-und-netzlaufwerke.json -0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c knowledge/03_digitale-tafeln-und-prasentationstechnik.json -a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 knowledge/03_fachanwendung-berechtigung.json -f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 knowledge/03_internetzugang.json -5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 knowledge/03_lizenzbestellung.json -d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b knowledge/03_multifunktionsgerat-und-kopierer.json -c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 knowledge/03_rufgruppe-und-weiterleitung.json -b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f knowledge/03_sicherheitsvorfall.json -ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b knowledge/03_tastatur-maus-und-zubehor.json -7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 knowledge/04_anwendungsberechtigung.json -6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 knowledge/04_computerraume.json -36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 knowledge/04_fachanwendung-konfiguration.json -a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 knowledge/04_lizenzverwaltung.json -e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb knowledge/04_mobile-endgerate.json -adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 knowledge/04_mobilfunk.json -1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc knowledge/04_scanner.json -658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c knowledge/04_schwachstelle.json -5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 knowledge/04_serverbetrieb.json -ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d knowledge/04_standortanbindung.json -9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 knowledge/04_standorteroffnung-und-umzug.json -8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed knowledge/05_dienstliches-smartphone.json -47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 knowledge/05_digitalisierungsvorhaben.json -4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 knowledge/05_druckertreiber.json -74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json -a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 knowledge/05_hypervisor.json -0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e knowledge/05_remotezugriff-und-vpn.json -9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 knowledge/05_schuler-und-lehrkraftekonten.json -2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 knowledge/05_softwareinstallation-am-arbeitsplatz.json -9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 knowledge/05_telekommunikationsvertrag.json -fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 knowledge/05_virenschutz.json -e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 knowledge/05_vpn.json -153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 knowledge/06_betriebssystem-am-arbeitsplatz.json -d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 knowledge/06_dns-und-dhcp.json -b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 knowledge/06_fachanwendung-bericht-und-auswertung.json -77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 knowledge/06_mobile-schulgerate.json -e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 knowledge/06_scan-to-mail-und-scan-to-folder.json -cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a knowledge/06_sicherheitsupdate.json -ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b knowledge/06_videokonferenz.json -e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c knowledge/06_virtuelle-maschinen.json -71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 knowledge/06_wartungs-und-supportvertrag.json -ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json -2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a knowledge/07_datensicherung.json -46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e knowledge/07_fachanwendung-update-und-release.json -8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 knowledge/07_firewall-freischaltung.json -03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 knowledge/07_funktions-und-sammelpostfacher.json -a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f knowledge/07_geratewechsel-und-umzug.json -9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 knowledge/07_mobile-device-management-fur-schulen.json -48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 knowledge/07_projektunterstutzung.json -ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 knowledge/07_storage.json -2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 knowledge/07_telefonkonferenz-und-softphone.json -748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c knowledge/07_verbrauchsmaterial.json -7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b knowledge/08_datenbanken-plattformbetrieb.json -59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c knowledge/08_datenwiederherstellung.json -7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 knowledge/08_fachanwendung-neue-anforderung.json -f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab knowledge/08_hardware-neubeschaffung.json -b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 knowledge/08_netzwerksegment-und-vlan.json -1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f knowledge/08_neues-drucksystem.json -b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 knowledge/08_padagogische-lernplattformen.json -ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 knowledge/08_rechnung-und-kostenstelle.json -3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 knowledge/08_telekommunikationsbeschaffung.json -0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 knowledge/09_container-und-docker.json -4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json -91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 knowledge/09_inventarisierung.json -d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb knowledge/09_protokollierung-und-auswertung.json -267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 knowledge/09_prufungs-und-klausursysteme.json -27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e knowledge/09_ruckgabe-und-aussonderung.json -eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 knowledge/10_haushalts-und-budgetplanung.json -523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a knowledge/10_kubernetes.json -1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 knowledge/10_microsoft-word.json -8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 knowledge/10_schulverwaltungsanwendungen.json -813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d knowledge/11_devops-und-automatisierung.json -213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a knowledge/11_microsoft-excel.json -a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de knowledge/12_microsoft-powerpoint.json -e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 knowledge/12_monitoring.json -2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c knowledge/13_outlook-client.json -cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b knowledge/14_office-vorlagen.json -9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d knowledge/15_office-add-ins-und-makros.json -7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 knowledge/16_office-aktivierung-und-lizenzierung.json -f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e knowledge/17_serienbriefe-und-dokumentfunktionen.json -5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 knowledge/example-vpn.json -49c48fb45c8387b020bb6a4bde2c81e544ac82b74403e453e28a85f987052978 mega-project.json -489b906fb8469562a110fcb2c47b425abed7f1fbf705da0ef51be2c8881ef12d patches/SHA256SUMS -47a6fa2c79bbba0c04af86dfa65d58529f492c698060fe586456c22a4eadb877 patches/glpi-agent-mega.diff -9b411c90d96a86c80f088ee4637046eeefaf31c62059d11aa1a999d6eb08b5b4 patches/glpi-knowledge-mega.diff -f0491de3cb6201f98ca6be8e865237adbba4772471f7fc7165d030e0c045fdb6 patches/neuroforge-mega.diff -576e75ca9191bbef7f136abde90c10a1c6bf2e36450a421fd3796e988bd2af00 patches/v1.1.0-to-v1.2.0.diff -7a5e0ae1d09b268d3ac62a51f92bfedf6771be26b909605a347f26d47d6fa8cb patches/v1.2.0-to-v1.3.0.diff -163a8a990aacd986bacb385494c2b2062ac10a9a8f0a456d19a6d1746c2fc500 patches/v1.3.0-to-v1.4.0.diff -654de4aac7cf318680872897c3de192afbafb61c7169acb8e7514116df86efde patches/v1.4.0-envfix-to-v1.4.1.diff -1ebc306bd6870389500c86cf07dcd677b5bd0448c762812c7e1c0f8732f36eb1 patches/v1.4.1-to-v1.4.2.diff -651c883facddfd0316f89e3cf3c24fd913371fcec2a5f447b8d105fa5dec463e patches/v1.4.2-to-v1.4.3.diff -d90be604acf6ecf5d821890d5864307853caf0e0678a3249eeaa1d29748a6ada patches/v1.4.3-to-v1.4.4.diff -01ba8cb6ea6ab0abd84295fb969fcfb93d841cd46ae1813fb0de9b23b2d5b22e patches/v1.4.4-to-v1.4.5.diff -617b434dcdc0dd0be5cdb15740661f0a513b330b99835364b508e21467f86ae4 patches/v1.4.5-to-v1.5.0.diff -b6bd8c6c04b5fca8d768279a5471c32e15dbcc57449b7d020f3e228f47adc310 patches/v1.5.0-to-v1.5.1.diff -b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 patches/v1.5.1-to-v1.5.2.diff -5c799db6701dce8019aeead5bccef6fc5fdc869e30785853eaae8d0eaf7ea43b patches/v1.5.2-to-v1.5.3.diff -27fd7fe12e9d774ced67cbcbf0f2e2e385736b48d01d655a1a9cf4b14b39bdcb patches/v1.5.3-to-v1.5.4.diff -7d0c4c85ee6cb89c6200cf35f23ea92c93ff5fdd9f2887c179c83fd5e5657281 patches/v1.5.4-to-v1.5.5.diff -405b41cbbd1ec48f316b1d935e67bc8d002fa1a3bd583fb813bf0875bd30a2e0 patches/v1.5.5-to-v1.5.6.diff -257a78b8f6613f92086a04bf53455f12e9bd1ab94775add31819ada465582163 patches/v1.5.6-to-v1.5.7.diff -53ab438b31a31590edbfe00eea6ba9d9ebff2b4c6255d7ac2056c86757f65c3a patches/v1.5.7-to-v1.5.8.diff -24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 patches/v1.5.8-to-v1.5.9.diff -e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 patches/v1.5.9-to-v1.6.0.diff -0cdce94c11a931e11c3c908f2fa3dfa733c5c602ac00f7f89e41b8c301574766 patches/v1.6.0-to-v1.6.1.diff -564817f8edabde0c4e4a1a427a3aa5418aae7bf12e9463044a7e6e0f13973657 platform/neuroforge/.env.example -39319b6f2058e4c8d6656a9cf01675374f81a04075b956b093a2befb5e05ada4 platform/neuroforge/.gitignore -189486a885c7fb78e0eb878d93cda0c70ca6d7ff9bfdfb3f5f32487cf03a9688 platform/neuroforge/BENCHMARK-v0.5.0.md -b9cfe0119f316b8143ebd33e9f66a8da36493a2c6c17acf2519ecba023223424 platform/neuroforge/BENCHMARK-v0.5.1.md -fc3aaadc0675ebb6dfeb42d9612a68c1905ac575e2c33299ca6e1522d8bdbeb9 platform/neuroforge/CHANGELOG-v0.7.0.md -a2908a0931e63244849016898c26eb023200bb9c1b1f7a8e62f5858000c94fd3 platform/neuroforge/CHANGELOG.md -2b286a4066da5b6bf7ce83c42ba7c97df6d4c6f8b71aa3f28d1ff824a7814391 platform/neuroforge/Dockerfile -9a9fd7708b61027e3677044ab1cbf7177119eac796df4d8786d39011da28729f platform/neuroforge/IMPLEMENTATION-NOTES-v0.6.0-dev.md -3146656e702607501cb348c77dadff608f9c152c4104647230848004e2692251 platform/neuroforge/MIGRATION-SQAR-VECTOR-JOURNAL.md -ee7f52ec20dcce87b751637229fe26c65d5cb0a926ac32915f46bf1b5eedad3d platform/neuroforge/MIGRATION-v0.2-to-v0.3.md -3095ca2e4512744ccfb2c3c71d80dfdbd42fe24e556a374061a76b80cbc33213 platform/neuroforge/MIGRATION-v0.3-to-v0.4.md -680eb77c8d645c3a2afa4e92e3062d1dadaa079425ed8c3f0d73393931354b59 platform/neuroforge/MIGRATION-v0.4-to-v0.5.md -0a21148446a057c773700fcfb026230fa771e970482bd6bdebb9cb9a3c11f4d9 platform/neuroforge/MIGRATION-v0.5.0-to-v0.5.1.md -59d223ec40991ef152f32b8bec26fa283bfa6318942e33a1a2169a85b361d9da platform/neuroforge/MIGRATION-v0.6-to-v0.7.md -4fc6e6f43fa70f4fa8a085225ecdc7ecd460bbcebca01dde70fed48a4c933c68 platform/neuroforge/MIGRATION-v0.7.2-to-v0.7.3.md -02bc0ac5316d063304b17bc51780f2fe55fbdfc44abbb151bac2397a642174ba platform/neuroforge/MIGRATION-v0.7.3-to-v0.8.0.md -9dc61428742d401d226d2eb8a74577b881309e67bb5c609d01b5152a18cb0bb1 platform/neuroforge/MIGRATION-v0.8.0-to-v0.8.1.md -7bbec66a7539fa9f65d8448e2c59c2f76797679f24d3b96691dc8c68e81d560f platform/neuroforge/MIGRATION-v0.8.1-to-v0.8.2.md -5b9714a45f9a5d31a536eca9b2461f408751882aa6909bd2bb6398b8d8907fbc platform/neuroforge/PRODUCTION.md -3d80eb1d37e9f6bd828d3d499ecc88e011a9f34b44d5fff5a89fd517a39907c7 platform/neuroforge/README.md -38bdb771c428636d18cb4640653ff7028715d5bfc5ca06d5ebc59f8ddcd095b2 platform/neuroforge/VALIDATION-v0.7.0.txt -73b7f0b287ae15f230a7ad2996ec305e787e7a8b096f7654fc69cf4daee12673 platform/neuroforge/VALIDATION-v0.7.3.txt -c3d34504b1b8a4ef73382df061c7b272a55c2a3a7a720435c6050254da3f22e0 platform/neuroforge/VALIDATION-v0.8.0.txt -0371dc02e4ac40b3197104f0aea41fb1b7be9b60caad4fc203eae1de1ac8191a platform/neuroforge/VALIDATION-v0.8.1.txt -b4896439112f9ca0f0a43d55d43cb3a2c4c802c568b65a96d04a3ca9d841c358 platform/neuroforge/VALIDATION-v0.8.2.txt -780363b8ef02ff7e83eb72709f6396b09dec21a26403bb44f58ea8d6757fe33d platform/neuroforge/VERSION -0c5308f5a3d37ac23dce162fd5fab78ce598e41671db5dd50c9c4ae7f215d49f platform/neuroforge/cmd/bench/main.go -6d315373348d82175d0666c709e4f0eec992f1349bdefabc968c0c2e8afe398a platform/neuroforge/cmd/server/main.go -3613379f9f9c3df373b6d686501e3e0290451cded5eae3ca307e5e14ddc829d7 platform/neuroforge/cmd/server/main_test.go -9884e323599a328e16cfe08b52473cc9346b17c3f5969fce4425d20fd0f42a93 platform/neuroforge/cmd/worker/main.go -e3dacdaec3c629dd432f24218a50fa4de5e1698ac7f27e84b14f776b50beeb83 platform/neuroforge/deploy/learning-policy.example.json -3765a5faea1faeb72aad7878ff0159a56fa1b0ca1fc94a348a8c599e460b61dd platform/neuroforge/deploy/model-routing.example.json -dcb7f80239cf955941ede9bcdc3bda8f39d81db5e5b0e0962b373036b40a0df5 platform/neuroforge/deploy/neuroforge.service.example -72443642fd4c498554ad6e46db2a096c97530fba1ba20b842a478345fe54c46c platform/neuroforge/deploy/prometheus-alerts.yml -c30e5b2fd39e72894db22499259b6f97d225c5929a7f3472277853a348cab9be platform/neuroforge/deploy/prometheus.yml.example -dbbaa7fd4430b9cdb5302144b6d40f7ac4ab9f73e81da4ddb51e788dd373d95e platform/neuroforge/deploy/searxng/settings.yml.example -a8962d0a1adca3a80b91d1eeddeab36b91c1cf7d84f39c84bfcedb85ccf55a52 platform/neuroforge/docker-compose.yml -fc993dc95fa49802ecb62994e4140dff18a27438e8a4f3c6352229c79b041710 platform/neuroforge/go.mod -1dc1d58bf4761f2aac3f2436f78c5942bf3a5968ef88840693dd6042960f7b7f platform/neuroforge/internal/brain/brain.go -976288422c0c4116d8c98af8a9b164ac670f9caf03eddc2d5e2e48747456d3b4 platform/neuroforge/internal/brain/consolidation_test.go -cbf267375414612ce2d4a697606576f756ce3611df543ca8cb63b6c2fcc64e44 platform/neuroforge/internal/brain/distributed.go -0eb3cb86d1c235ac21527aae79de7c26ce204412814c5ea84dd9389e0b0fca2b platform/neuroforge/internal/brain/goal_progress.go -6a1e33b71d88f963f244a5a00ea45ee65bcff4882a562148d8ba925b8692e083 platform/neuroforge/internal/brain/goal_progress_test.go -bb4cec696bc7e5fc2b1f2fab0b5c01552f523a7dc2bfa1cb5be5e5374b304162 platform/neuroforge/internal/brain/orchestrator.go -03ba39168194068f59e4be837de1c152e0d10e316461b235c61704571cfc0e08 platform/neuroforge/internal/brain/orchestrator_test.go -359955653c647125559afd6dc3ebe69aa5ca19ff7e825ce801b7bc24e5fbcfcb platform/neuroforge/internal/brain/policy.go -27e87af473d2d71ba94ffb9bf7a70934776f8c23ce45496ca0998ad3000fc156 platform/neuroforge/internal/brain/policy_test.go -9022a797aa1d49af20ced5d8c3e0b903be740fbaa7251e8a80d43cd04d2f68fe platform/neuroforge/internal/brain/research_quality.go -0a3c8f7d149e814e091595982dbaa69467f6bf11eec631471d133a9b21585ae4 platform/neuroforge/internal/brain/research_trace.go -f90474aa355d3c4565e61662e863bceb089795af01802e9b228c1b6c545bb7d7 platform/neuroforge/internal/brain/staging.go -4d7715622739a43b043a1f08ae7165db6ee6bbc22f3ab94945f56f226dc4037d platform/neuroforge/internal/brain/staging_quality.go -d4fed8b68d31f6fbd1992bde76abd4e7b2f9d211e53bf8b234369a6a53692fe8 platform/neuroforge/internal/brain/v3.go -3ae13251512ecad1423a33ce09889f961130fefaed9342170b2d5cc6b3b51893 platform/neuroforge/internal/brain/v3_test.go -4bc58463b659bd7e51db4c7dbeba053de90fcb41f392a7d6e62a8cd84ddaa092 platform/neuroforge/internal/brain/v4.go -a9619d9571f6ab6363b36af9fdf1690f9f88e9333eb6f2773ae68580b19666d8 platform/neuroforge/internal/brain/v4_cluster_test.go -76319080d3faaf856e9fe5e1aac5e06be433d2f06cc6b152bac5f05dcc943fff platform/neuroforge/internal/brain/v5.go -816b725594ea5f6938999799394eaea9dd619addaeec5447d8e7707da4c28c69 platform/neuroforge/internal/brain/v5_cluster_test.go -cd8f1281e25ce42ae8918abbed16d4bf57ada9c3a3cc1212edd28f77ccc9328f platform/neuroforge/internal/brain/v6.go -312e7578b5800ccb06755c19d7ef7026a362443804ff78cb7a11a7443af2aab5 platform/neuroforge/internal/brain/v8.go -31420af7145686a2c7eb42fc18ca1299604afcb5942cfdfd132bef5c5f05d980 platform/neuroforge/internal/brain/v8_test.go -e8e3defcef9ca18adb00494f85df463968cf3a9db37149d97ef02a7a2719db57 platform/neuroforge/internal/core/types.go -65a8b8196343e7cfd9444ca314da4a83c81bc046d9478b8b93217d9cc68ba562 platform/neuroforge/internal/cost/cost.go -b9bb2934e01ed2bfb6b16e4139e10e51f1387112eb8654f7f692779a2fd2d273 platform/neuroforge/internal/cost/cost_test.go -fb66ce4ab760b979eacd4f7f17dedc41916f93c53ba582be572d250f99654695 platform/neuroforge/internal/httpapi/admin_app_auth_test.go -eb2b8c599e4e0838d920296391ecaacdba1578c4f3a6a0a9e8469fdba31217ed platform/neuroforge/internal/httpapi/goal_duplicate_test.go -8f67172078339d35e61a812991dd3e48d9bb00febc6935a8be212d59d320c8c3 platform/neuroforge/internal/httpapi/httpapi.go -680b7fdcb20ac76c9305dfb94e4ec7474526be7a44b56afb3db7e4a66c8e2c38 platform/neuroforge/internal/httpapi/index.html -20f4a6cc30d5ce84ceed4fbdfc9f5c57ab5f82274e0c54464817d571e0e881b0 platform/neuroforge/internal/httpapi/integration.go -c346dd0205548f7cdb00ea026f1afd82d59f87824eb983ebe84abfa7cb078974 platform/neuroforge/internal/httpapi/integration_api_test.go -f8cfcc7a2bc781394231c25e36e75c41563f5250a1387e5687880d2591efd93b platform/neuroforge/internal/httpapi/integration_graph.go -c289fe8748d28d4c1207b22924f7d1a7b2df25703d6df5bd1869acb325211b2e platform/neuroforge/internal/httpapi/integration_graph_test.go -29c365a797f1f162f4d7a5d32833123f7930b73ca3b649fe31e117453dcb9197 platform/neuroforge/internal/httpapi/knowledge.go -409dd8d7c5ca93cc82ce2b7bf17a12655c97dd27ad9767aa40619502830a0ab2 platform/neuroforge/internal/httpapi/knowledge_integration_test.go -cf285170ff175ce2bb223b4ffd87a35da604ec73b5fe386ac35402af1f9c3531 platform/neuroforge/internal/httpapi/knowledge_policy_test.go -3bca3bf2f3d486892cee5d7e41920517f2df504ae729ddbce7d840556fe08625 platform/neuroforge/internal/httpapi/metrics.go -c40412c77dd8635bbd4bf9c6d7eb11b0d7902aee896bdd1ee99e5cdfb72c7453 platform/neuroforge/internal/httpapi/metrics_test.go -83033a18a319ffb162c7cdefb2efe108a47dba81095ac34662349e232f8c22da platform/neuroforge/internal/httpapi/model_routing_test.go -38a9388b6907975aec933697ad87ddeb9e7016a279523f2526db3810df70b006 platform/neuroforge/internal/httpapi/outcomes.go -0b0b111e6b47f38dd3ca331a0c1b79b3de95012820f1c04cbe37c79460c09e63 platform/neuroforge/internal/httpapi/outcomes_test.go -b3b1f5cfd6b0ec04341978898d72856af03a95b8922daf9864caeb27e37f1925 platform/neuroforge/internal/httpapi/research_live.go -b37f7ec3be867666999a9ec314139dd1bc4c74efb53edfa224b4fe8119858aac platform/neuroforge/internal/httpapi/research_live_test.go -4e65ab0a760369a6d6c4932c180ff6c67f0768d91e08ccf471f53c6ecd5d09be platform/neuroforge/internal/httpapi/status_dashboard_test.go -3eb47f1b666336efe952961c19a1f46a7475acff8fe4dd7a59ade000aa1b9466 platform/neuroforge/internal/httpapi/v3.go -b1587e066b56ef72f17162583698614d31c413e2ac7e75359b731c2e882a50ee platform/neuroforge/internal/httpapi/v4.go -1ef5cacb0652f2a6301c57f8987dd848fbe590abb73cab44899d9152ccce45d0 platform/neuroforge/internal/httpapi/v5.go -1d534726d7a75f21f7ad28a959f73a3402c021f7f4a1b554f0600cf3abf669d1 platform/neuroforge/internal/httpapi/v6.go -fdf937aae569dd48d09da63085fa2656299a3ab2f55c7fc7f718c46df39a2683 platform/neuroforge/internal/httpapi/v8.go -0c8c389b085e4546c3269b1bf0c539414f3603522ddfd0665261a6448f31b514 platform/neuroforge/internal/httpapi/v8_1_test.go -889b82a7f90be0590d0087ccac06aad330710c19f31c43b12c28480a94659be5 platform/neuroforge/internal/httpapi/v8_test.go -60fd100c9057c14d78f91d636f7f23aaf2767088c46b7670b4266ce22cbb5ae1 platform/neuroforge/internal/ingest/extract.go -7576df6ad6f8b692f14db739958fabe583cc5bb547c6068739fd47c45910d2ce platform/neuroforge/internal/ingest/extract_test.go -38a02bd5df61d1e7b257bcee8120380d38bf8c70ec329b6222f1092f74495629 platform/neuroforge/internal/provider/provider.go -f9cfbd0bab443faf39973e6fe1777d7137ae5dcf58fc1b0d6295099e1b3fadc9 platform/neuroforge/internal/provider/routing_test.go -baace266205a584a53463af76038e97771f088a38e42fe333bb548e43856e719 platform/neuroforge/internal/provider/runtime_test.go -2907860f1b4a30df0522f4808c58808f5b184b7b6a3733a959b4619c528379e3 platform/neuroforge/internal/research/searxng.go -e890897c662b070997c3b18134bafbee5bf9b3fa5220045ac09dc6e3dfd4ac41 platform/neuroforge/internal/research/searxng_test.go -fe35f46c0b6dee7283142dc818f484006fdde6d71906d5f5fb3bc05c424ddd4f platform/neuroforge/internal/store/batch.go -c3bd0bb04e951181c72976c246673f3b7c80e57b9f49fda47ce73702df01a5c4 platform/neuroforge/internal/store/checkpoint_order_test.go -8e7a029a904ca1c22821c5e4871c72958f0d2645786871d75f3e00c186b00778 platform/neuroforge/internal/store/cluster.go -6637e541f61a58f624baca31d972095ea01a95491ddb30492d9ac055740213c5 platform/neuroforge/internal/store/diskann.go -1858ed8045b16282de815a6668ff6027c7a3a3795e38659c753c504d0f2a8383 platform/neuroforge/internal/store/diskann_test.go -366e3b2f29b4abb1997aceb9c2b934ebe8f4ee44605b54167c70b8ddad683c7c platform/neuroforge/internal/store/goal_duplicate_test.go -38f24189c76652f12da9eef7a617455dc923499e2f21cd3c29d94231dd0d7460 platform/neuroforge/internal/store/graph_index.go -bda98567153542e9119757ce5f8f6014334ac9f80ad79a26ff83fdfaccc1ca8e platform/neuroforge/internal/store/graph_multihop_test.go -95640acc25c6d815f47a9fb390b71e58205d3d6118d73258031faad778418490 platform/neuroforge/internal/store/index_segments.go -478efaeb260cb5e970af8b3bb99f232ef846f54c6e4ff9deb5499df6e008e1c1 platform/neuroforge/internal/store/index_segments_test.go -eef4aad176d7f6e740dff4ffd81f3dbacbcbe6646e724331ae010ab50db04b30 platform/neuroforge/internal/store/knowledge.go -e9aaedbf4d5e9a43f4a4a5dbe2e9e0f0257b952fb59e82bf729aa5e761955c1e platform/neuroforge/internal/store/knowledge_test.go -43de0f541fb47633b02b25686544d44887c17b47e8d01cb23f8e57567cebdcd7 platform/neuroforge/internal/store/mmap_linux.go -9ce033d691157037fc6df719b5ecd8fb29d6d881d1862e2d50dc0c6c573b5f66 platform/neuroforge/internal/store/mmap_linux_test.go -ef6135a9039fb45361e57107d3968272b4b365ac833e9e2e58b0875566bf5a07 platform/neuroforge/internal/store/mmap_other.go -fe98dca9f919cb52140a916de08e76b88bd0d08b6b83fc8b4e5220411998b0fc platform/neuroforge/internal/store/observability.go -fcd87a843b6dfd3520185bdacf9d732303ff39befa31b513225618560eea1567 platform/neuroforge/internal/store/orchestrator.go -13ae3b3d7b797cd4d6d6775cda15975428ff4cec96738ae53594763ddd6a9ef0 platform/neuroforge/internal/store/orchestrator_test.go -66d612efe2462d76d79cc51b8b6835b5990d4a79a2dc036c26b040554357ae29 platform/neuroforge/internal/store/pagecache.go -5df97ac71adcea7627756a52d295d682900d3b812a977dbe9a4911e4c4118809 platform/neuroforge/internal/store/raftlog.go -fd6769bdcf1f7d22ced9ea426fd4dbc52b50b5483b4be42ac3b6adf637c926ab platform/neuroforge/internal/store/raftstate.go -fa892de65ab1c6ef8f4f811784cc3852854bf7e7de7e0f9c63a34363a6e8f28f platform/neuroforge/internal/store/recovery_hardening_test.go -f811ffd393ec7e72a3dd704eeebef7da1a78446169f451a44769bb586d7f0049 platform/neuroforge/internal/store/research_runs.go -c5c976d068317c688611bce153453133ebd7cf19b835955c77115b450793fe9b platform/neuroforge/internal/store/segment.go -ea968456538367ed536c212707209a173098ec65823152a0813422bc8071c952 platform/neuroforge/internal/store/segment_test.go -1d8071889b7a2be9faf029a1dd2981c69a9914798869bfe217f6db105340323e platform/neuroforge/internal/store/source_index.go -4d41586c15bfdd13139e19bfa102613f18ddc6120e4214a1f45dd9e2eaa195af platform/neuroforge/internal/store/sources.go -0ff464ef7fab01324d77ee4e406a8c15451b3333b75d4afa444be3c3c94f016b platform/neuroforge/internal/store/sources_test.go -61cd82b106e74f2aa0a7591d757a0872491a635dc1fccc78c66d41c052d0c9d2 platform/neuroforge/internal/store/sqar_vector.go -7dae4f397a98afb0fca66e59420a7799e69e8b704818d79b281dbca999ea8070 platform/neuroforge/internal/store/startup_compact.go -4ec1d8893f80f52980ff045a3a142dc9f161b3e061318755d2179950b7071956 platform/neuroforge/internal/store/startup_compact_test.go -b631e8b2fb6b5576653ea8a54672638e4e05e4ad509ea853f90b25ccec4ac5a4 platform/neuroforge/internal/store/store.go -f86b648e8b4845ab83e672ae3fcfb0bcb0dc1bf891abf4bd30d380304f0b259a platform/neuroforge/internal/store/tiering.go -2138e467981a3c814d1e88842dfbd470b76333898614543cafb4f93ea8b9beaa platform/neuroforge/internal/store/v3.go -091e4c9cacfc76786a76f6afc4eb2b697e5a641e611475b6dab97e99371360ea platform/neuroforge/internal/store/v3_test.go -7f0bca5419a895258b7921e940c3fd69cce04dedcb0b9d8f1127cd24b9c9e201 platform/neuroforge/internal/store/v5_test.go -b3a0115907719a8a7aa9e6b244eec55be71a2535ba20db65907987d5fb78dc77 platform/neuroforge/internal/store/vector_journal.go -bcc12eb91682f3c0a626f5baeea64cb3809e56f087b9f1178a6c2546f5e301b3 platform/neuroforge/internal/store/vector_journal_test.go -158125b5585b59fc1d2af7ab92c9c86ecffa2a7e3cd5147a19840ac103d348a4 platform/neuroforge/internal/store/wal.go -b369c68fbfb7bde75ba37c40948651789fe486f7c651ac245c8fdf8573c7ecf5 platform/neuroforge/internal/vector/hnsw.go -09ac0fa04dff1446893e846a95dc1131d6d1a7a54b77b48cdab8e46fc6124b7b platform/neuroforge/internal/vector/hnsw_delta_test.go -4bfd79da525c6d7901db01bc09c823608b641970e40aff9fdaac33da15d4432f platform/neuroforge/internal/vector/hnsw_test.go -6eac7fabc0f8a3cab53c758502895576fae158e3d672f8c87ad58588523919c2 platform/neuroforge/internal/vector/pq.go -275b0dd3b8cc3589541e3cfb52a2f1a372e40446e2e0c9c902388fa832e4f0dd platform/neuroforge/internal/vector/pq_test.go -c854f70f4141344dc2c6fbec4419afe5dfe25fd1bc2da11e41781730a21c8834 platform/neuroforge/internal/vector/recall_test.go -32f55e82419e4b043d15682c82ade1c4bc9694bfa372f925c131c2611f48d744 platform/neuroforge/internal/vector/snapshot_test.go -c5463c525f5ea703ea1f6df74cabc30f937f3846b1f75f237adeee2700baa977 platform/neuroforge/internal/vector/vector.go -276cb34ddd9f87abdac17be110e2bf15135ab83ba66bf91663d59d61fbd81125 platform/neuroforge/neuroforge-v0.7.3-rtx4090-example.json -6088a896352046ce9e04b1a3f8ea30aaa98e5681a4d393fa5b6bfd5288a3ee2e platform/neuroforge/openapi.yaml -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/agent-data/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/backups/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/knowledge/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/ollama/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 runtime/staging/.gitkeep -02ebd40234435473fa5e4bdb03f035f22564049a6f43243d7f4e175c7239ab78 scripts/backup-data.sh -df684b1b487762248516aebb82742e5777395b504cbde42ebda85b999ae353be scripts/check-compose-env.py -6f1d21b7aea3265a088256801aa37a00931157523d8bd834aa4f473e99511f04 scripts/codebase-memory-ui.sh -bfc2c31612e57904efefd44dd6fd2f66acdca03e7228b4a31da2a7fb2a5b28a3 scripts/create-network.sh -5abbc6b60bca94fcb880abebae19c85c6a220c9eabb9865928b23ae86246705b scripts/export-obsidian.sh -01611fcaef13df301147ad539f38e96e09d32c8f0f17c0fc9a7e3dae38de6292 scripts/generate-secrets.sh -8b0a4be74d87cf42c98360819c6f797b9caaf10528e729159412daaaffba0771 scripts/go-live.sh -3df6e58552fbe5e29f77a66c872339bb39a13329e8d63e34b3b2afb41585e84a scripts/health.sh -02cd54884b3ee16095a7c111579361b9af5920ec55349627423db96dadc63854 scripts/preflight-role.sh -0d28cf37186f9faeff2a40e506e3a3e5b8fe3a399c9c426e369bd26c9021f7b3 scripts/preflight.sh -fe35fa5db6dabf06e6266e0de4c372604160180366d4c0a8042c7ee54b7c6dcd scripts/propose-draft.sh -1744be0629e845c8d94dc2a06e91ca2cc9ce5928db00300b7fb894445aeda9ff scripts/quality-replay.py -8b745865c41ae3fcc89280c861f78918ee947c1fae4973ca95d00a507fc3afcb scripts/release-gate.sh -76358a00fbc9698879d363dfca6624a13e7633181cec2ce05368cb01811b2580 scripts/research-up.sh -697891580c9713e6451c1e0f10509b009cfa3d3c5af4a1347f841e6f05339cea scripts/restore-data.sh -e504b53162490545302b8835bc1b8f22625421a4e5d91449828837488fb82ab9 scripts/secret-scan.sh -a2c56eb668137e058cb78bb592ac359ef7e608cb9bedc7eaa930b3f4f66411ca scripts/status.sh -2970d5f5049323b14a3f3983c2f4eb5392d745b4fea34742e3cf56e52d740f17 scripts/validate.sh -cc0fc4433db0964ed172a82f2910e55c0f3dd6d59b1ec04fe0541b8fe5a912bb scripts/verify-package.sh -17d7149607bf40ccc6f64a3c4cf3e4023a8325675918c18df0be309ea9731a36 services/agent/.dockerignore -d1d8c18c6f7c7c6f0b2d72f421837d010afaafc931596ae48442a826e50f0bc6 services/agent/.env.example -9ee46f2156ee805c4ff40e418b004ead5cfbc3a9b6df3968a1b772fe1b313cdc services/agent/.gitignore -4c893d6049499eb70ca755a333e97f0a726dd212e4ded4a9d4acabf0a25d9bd8 services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT.md -9339ce57dfc58d4731ea02c8d0ab9f2835e5966a29c071062fc6a2effd17da2c services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT_OLLAMA_POOL.md -81c91a44c5d3b7eca3051d8cee35f231ef283563da7a636c56c98b61da60218b services/agent/Dockerfile -2ec93f81187ebf92665a6a15a6672b2f7dfce23bfea1546f2d4188f49de5dce0 services/agent/EMERGENCY-HOTFIX-TICKETVERARBEITUNG.md -2bce6e10780004bc12042a416b8a05b417130db2d730d01a95d9d86a564e01cd services/agent/ESCALATION.md -bc344ff4486bd38356fab0d2da46590d9f738ece0f712d043597012f215bae49 services/agent/HOTFIX-GLPI-KB-AUTO-REPLY.md -3f51b802804e9004d17cb74a7e0b8fc668537e75bc95e7344923e77d5f05f13b services/agent/HOTFIX-GLPI-KB-SIMPLE-AUTO-REPLY.md -65408599c64a4364f41fe0876f6531b861c352eaee4afabfeae52a5a72f28f6b services/agent/HOTFIX-GLPI-KB-UNCATEGORIZED-AUTO-REPLY.md -dacf2060df228b5c137fb5ac5c5f7377226f937aadaae4b01b662fc54abea11e services/agent/HOTFIX-POLL-DIAGNOSE.md -6b2188c579db7c47cf7be54328f2671a8fe7aa17f21556483f751fe2457db1c5 services/agent/HOTFIX-PRIORITAET.md -30a29a59cf3c409bf33e3adc96789b9281a19fa86f961c727dcdfab07ff6a327 services/agent/HOTFIX-TRIAGE-KONSISTENZ.md -c0e13c6703cf3feda57a9eaaa718e501280fbb526f466040eb685a27700b5797 services/agent/IMPLEMENTATION.md -1126322e2cc8d165adc4c792eeb195717de2bcc7b39be1ce77959d78e87ef685 services/agent/LICENSE -0ed7ee0e4846be09994dd5f3b49ffdca6b59177b9018b1947388949f0dd49a14 services/agent/Makefile -80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 services/agent/OLLAMA-POOL.md -80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 services/agent/OLLAMA_POOL_BETRIEB.md -a7a3f969150005ef062e7a77cf852371931bc2fa5ca4be01cb0e9eb07d35335c services/agent/README.md -aeac85d1adc92a8b34ffa5a95f7f19c2c6fb94742a7359a7fd452529d59ad87b services/agent/SECURITY.md -edb92cdf2d1863df53ea8045657f64e415f206d503c8da3b45ea96b7f7f0960d services/agent/UPGRADE.md -68cfcbf3bd8eb0c24f12e41cb3d95c1654b98fb7cd8b9606844d68e409428c23 services/agent/cmd/agent/main.go -98be0fd63dd4fde52342c5385da113c926ac29195aedb657c0ca0f98e1593fff services/agent/compose_local.yml -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/agent/data/.gitkeep -b74f901cca99eae4bed1b168f1f077a28bf1134a71c867d263c7834f6d896684 services/agent/deploy/glpi-ai-agent.service -e8327a246f048c70f3b2b426f5f942bf16c41e49ac385d49c296eb994dc0cfd8 services/agent/docker-compose.registry.yml -29134dbd1bb19a0fd87b242a184b989a70f3a4c784254d86e2a0f5fbdaaa0497 services/agent/docker-compose.yml -a0105475dc054977223fac36618b8cd8137c55be1d11fddcd24e9a4d3074c170 services/agent/glpi-ai-agent-neural-brain.patch -8a7805be0da45875d45a5e5b26780fe659f671edd2d6b72c0d7da9606bf15755 services/agent/go.mod -e34ac0033c53effcf654689641a15a92deae1a987e3d92e9921b7b030efab3d0 services/agent/internal/agent/agent.go -789e55accdbfbb99ef8179f5680aa340e67a055c71686533302558389dabfb8a services/agent/internal/agent/agent_test.go -a8eedc80f57b174fbad3850d787346f7b370236b4c2eef67905ef6b60a1ced64 services/agent/internal/agent/analysis_runs.go -46a8dfe5e80569aac48f9a098acb2c1089ea4ab9521ed6c92f53b020e0c76ccd services/agent/internal/agent/analysis_runs_test.go -05ce267b4827cc5b89332b72fa6ecbcd80bd3796b5143bc40398b932f5f45867 services/agent/internal/agent/candidates_test.go -76fd850dc9bedf73db32e9b2d49edfb04624709507e0b67fea1f88730e87720c services/agent/internal/agent/category_mapping_audit_test.go -d8aab6fa743e6ed8246ac6e20f993fc3642c635a1cb349c4fb6579b863a2bd63 services/agent/internal/agent/escalation.go -c4bf8fcb4ba6eb7962ec55cacff806a8a99561d134ab5238abd012763a2f9c74 services/agent/internal/agent/escalation_actions.go -471cf47af1220f50fce99ea41c59adf257cd29417ce23fafb4728f0037d33842 services/agent/internal/agent/escalation_actions_test.go -7a65f676bacd5a7b879862e4c1a157632f541a570a71bd8b57864e603bb4cd60 services/agent/internal/agent/outcome_learning_test.go -5175e4fa9c394c0a8df94c29c69c5f896b3227a7d8de67d214aea4634ba0fad8 services/agent/internal/agent/policy.go -5434e1c4dc61eee33d6dceaff69156a5b9ab4a9fbb555cd056c8cf0444601606 services/agent/internal/agent/policy_test.go -6b70a29176602e499935a85ac8831dbe36bf75fddb130a56cda376e124ecb7db services/agent/internal/agent/status_reply.go -6f95bd3c85b42f1b2b098f05828c0b9e3dbb2e57f1f89b3dc4d1806453e0c4ce services/agent/internal/agent/status_reply_test.go -9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 services/agent/internal/brainactivity/client.go -cbb14913455ead81f9fe85c3dc185a29431e28271f8babe840759bc93417c0a3 services/agent/internal/config/config.go -fa1717032ad536bff867a07d3fdf0ae2e0a9d5d7637a27b43274832d51322eb6 services/agent/internal/config/config_test.go -6fe7335b0e66b60d5f6f27583b1ac11a0948719cfa657d51e77de0b337aab5d5 services/agent/internal/contextdata/collector.go -12e9eb6fd567b5d4173a63fa2b89be913b6d1c97e17b69048f46d119e88dce26 services/agent/internal/contextdata/collector_test.go -5eee0e130a9cb8452a07e9f5d4c76048ad2eb62aa411ea61c9dc6066fadff934 services/agent/internal/glpi/client.go -6499b2e19547d7d874dbaa64f0c905643ffe4d822329c6cd710df0a30a3a9755 services/agent/internal/glpi/client_test.go -247324ba41f3cd991a3582ed270d0b0d6c4c92c36f11e7742c20d3e59b982928 services/agent/internal/glpikb/sync.go -9836e2bf9153df638ead57d92c152b87f831e5a3696bcda4b1f650ff38e9ac70 services/agent/internal/glpikb/sync_test.go -787ab6b53623aa36ef6628f4eada11dcb4ed3011b34effd691bf07ba5d8f214d services/agent/internal/knowledge/category_mapping.go -d3a9909672ea420db87be29ce669f3cd973125ff15bb46ca90a54c3aa98d9e73 services/agent/internal/knowledge/neuroforge_backend.go -7bcd33bfcfb99fb06762fe75f4646308428957ed7eca26a266891ab580c3a929 services/agent/internal/knowledge/persistent_index.go -df1ac214ebb88bf3ce7f68112129cf9ffc300dd725380c38661b85b04908ecdf services/agent/internal/knowledge/store.go -63e47d86bcc89178fdc0f91e07ecb30bd01ee4b84bf29bfc660debbf26f8a40b services/agent/internal/knowledge/store_test.go -807996228b38e1cd97ec2516cb1ecfc4bd945b638fcde465a0991051df2f1a00 services/agent/internal/learning/outcomes.go -2917b3dab63783af4c92c48f269f097dc94b989008a5451f46d6c1b309ccdc9b services/agent/internal/learning/outcomes_test.go -cd8c7379d6ca50fed77a7eafb6ab34b12b9ce2fe56025da534f54e6db246430d services/agent/internal/learning/store.go -c540f2e554d66f36d79d2ce32c74a18b27b2940e96a40e89eae10abccd0bd2a5 services/agent/internal/learning/store_test.go -5030ec3f51830f7decfb24c27f9b243cb64f97302a6a51df096bc957f24b3fae services/agent/internal/metrics/metrics.go -809c3f2833ed2093bf1f9e4466eb3d855f2a84d827cb21deb698dd0e7b63b403 services/agent/internal/model/model.go -975d5846fa38bf2478fa3ab9696660740c8d9a0549ce8d4f3443d4ca917c4aba services/agent/internal/model/reason_codes.go -821850d649c7661f1665dc6be030ebeeacb60e4e007e1e84891d4b779b9a5d0d services/agent/internal/model/reason_codes_test.go -c89248224adf72720289215684b916eb209387c1d38b757d64804a8d3148011c services/agent/internal/obsidian/export.go -b75ffec53e9a94505ee57ecb9361bffbb6805cfd06ea05a7bf68b6b28b268c09 services/agent/internal/obsidian/export_test.go -f4e32c5653b5fc3e046f1536513d376a7c0ac11b392210a107e12bd0ee63e179 services/agent/internal/ollama/client.go -3522e7687e38db771dcf7e05b8dc52ad99a2fbc53d98dbdf07ded781d68b248e services/agent/internal/ollama/client_test.go -efbee1deb5b452da5ba8a9005cfc8b0f74dbdebddf8d251c71d63265c9ccfe9e services/agent/internal/ollama/pool.go -401db60148c8307516c091a56cf8acd21a95230c69fc50a6999bbb0292f2b06f services/agent/internal/ollama/pool_test.go -ab29c5808a06f6b21a0776355eeaa030f69be5eee251e7b1dbd7e00c71045c61 services/agent/internal/prioritysignals/signals.go -149bfefd67291ee288beed89a27235f58d3c33d87a1eff98cb2b7cc312fbd6b2 services/agent/internal/prioritysignals/signals_test.go -93f31d9e7062a03990f96bb950cae54a3fc24176a723a2f634312653a0692434 services/agent/internal/queue/queue.go -147146bf5fa222d54812d84ff799555fd4c2d57ccbf8337e960708524f0ecd86 services/agent/internal/queue/queue_test.go -171f346e674ac5ebfa562a55e47060a462a0460db86e741085e2ee85533d3193 services/agent/internal/state/store.go -592b00ad638b2e523d5e24d0d73286e547ee09c5027da4f6c13fe3885daed020 services/agent/internal/state/store_test.go -209c5ce33b17403289c897932ecfa14b1e8f036f89746715d41cca3c607cf405 services/agent/internal/uptimekuma/client.go -c849df7e346e1bd1e122037a4eafc30fe461add9b1876d7b9d63a02e33d831ad services/agent/internal/uptimekuma/client_test.go -5f0375f33c4c3f0804e58bb7b3a8699a450a05100274fbb19762d59f4d5d0034 services/agent/internal/web/control_graph.go -cb68a23bef549186d251766a3d3820b611defd55e8f7386b8b87490a78b9e11d services/agent/internal/web/control_graph_test.go -b4f7d72022d5009fce43b5879483de92dacfee2b3bf527306902187c3d26486b services/agent/internal/web/server.go -1f517bcb30f95831a06dc2fe0fd9cd633a0c42fc795f67e5ce53de7a2eebd2b1 services/agent/internal/web/server_test.go -21b32b0b863ed62ae352011d7dd85f0134bf9909c23ee80b1c7e3c3605ef8cc4 services/agent/internal/web/templates/category-mappings.html -d0561b87acd8fe8c0ab7f1f0bb82e1df840db8342f91798e8cb0a0408064abcc services/agent/internal/web/templates/dashboard.html -fb5a4e2e1846046032bb878f1e878eb792497e321df6a8ce16209c07a925fdcd services/agent/internal/web/templates/diagnostics.html -5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a services/agent/knowledge/01_active-directory.json -70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc services/agent/knowledge/01_arbeitsplatzdrucker.json -96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 services/agent/knowledge/01_fachanwendung-storung.json -521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 services/agent/knowledge/01_festnetztelefon.json -88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 services/agent/knowledge/01_hardwarebeschaffung.json -4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d services/agent/knowledge/01_kennwort-zurucksetzen.json -5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 services/agent/knowledge/01_lan.json -95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 services/agent/knowledge/01_neue-it-anforderung.json -ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 services/agent/knowledge/01_padagogisches-netzwerk.json -f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 services/agent/knowledge/01_pc-und-notebook.json -ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc services/agent/knowledge/01_sonstiges-und-unklare-zuordnung.json -5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 services/agent/knowledge/01_verdachtige-e-mail-und-phishing.json -5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb services/agent/knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json -ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 services/agent/knowledge/02_fachanwendung-bedienung-und-beratung.json -0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 services/agent/knowledge/02_gruppenrichtlinien.json -4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 services/agent/knowledge/02_monitor-und-dockingstation.json -bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 services/agent/knowledge/02_netzwerkdrucker.json -7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 services/agent/knowledge/02_rufnummer-und-nebenstelle.json -efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 services/agent/knowledge/02_schadsoftware-und-virenfund.json -2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 services/agent/knowledge/02_schulverwaltungsnetz.json -830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 services/agent/knowledge/02_softwarebeschaffung.json -fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 services/agent/knowledge/02_wlan.json -26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 services/agent/knowledge/03_ad-gruppen-und-gruppenrichtlinien.json -b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a services/agent/knowledge/03_dateiablagen-und-netzlaufwerke.json -0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c services/agent/knowledge/03_digitale-tafeln-und-prasentationstechnik.json -a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 services/agent/knowledge/03_fachanwendung-berechtigung.json -f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 services/agent/knowledge/03_internetzugang.json -5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 services/agent/knowledge/03_lizenzbestellung.json -d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b services/agent/knowledge/03_multifunktionsgerat-und-kopierer.json -c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 services/agent/knowledge/03_rufgruppe-und-weiterleitung.json -b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f services/agent/knowledge/03_sicherheitsvorfall.json -ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b services/agent/knowledge/03_tastatur-maus-und-zubehor.json -7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 services/agent/knowledge/04_anwendungsberechtigung.json -6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 services/agent/knowledge/04_computerraume.json -36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 services/agent/knowledge/04_fachanwendung-konfiguration.json -a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 services/agent/knowledge/04_lizenzverwaltung.json -e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb services/agent/knowledge/04_mobile-endgerate.json -adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 services/agent/knowledge/04_mobilfunk.json -1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc services/agent/knowledge/04_scanner.json -658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c services/agent/knowledge/04_schwachstelle.json -5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 services/agent/knowledge/04_serverbetrieb.json -ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d services/agent/knowledge/04_standortanbindung.json -9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 services/agent/knowledge/04_standorteroffnung-und-umzug.json -8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed services/agent/knowledge/05_dienstliches-smartphone.json -47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 services/agent/knowledge/05_digitalisierungsvorhaben.json -4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 services/agent/knowledge/05_druckertreiber.json -74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 services/agent/knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json -a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 services/agent/knowledge/05_hypervisor.json -0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e services/agent/knowledge/05_remotezugriff-und-vpn.json -9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 services/agent/knowledge/05_schuler-und-lehrkraftekonten.json -2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 services/agent/knowledge/05_softwareinstallation-am-arbeitsplatz.json -9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 services/agent/knowledge/05_telekommunikationsvertrag.json -fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 services/agent/knowledge/05_virenschutz.json -e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 services/agent/knowledge/05_vpn.json -153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 services/agent/knowledge/06_betriebssystem-am-arbeitsplatz.json -d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 services/agent/knowledge/06_dns-und-dhcp.json -b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 services/agent/knowledge/06_fachanwendung-bericht-und-auswertung.json -77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 services/agent/knowledge/06_mobile-schulgerate.json -e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 services/agent/knowledge/06_scan-to-mail-und-scan-to-folder.json -cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a services/agent/knowledge/06_sicherheitsupdate.json -ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b services/agent/knowledge/06_videokonferenz.json -e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c services/agent/knowledge/06_virtuelle-maschinen.json -71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 services/agent/knowledge/06_wartungs-und-supportvertrag.json -ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 services/agent/knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json -2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a services/agent/knowledge/07_datensicherung.json -46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e services/agent/knowledge/07_fachanwendung-update-und-release.json -8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 services/agent/knowledge/07_firewall-freischaltung.json -03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 services/agent/knowledge/07_funktions-und-sammelpostfacher.json -a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f services/agent/knowledge/07_geratewechsel-und-umzug.json -9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 services/agent/knowledge/07_mobile-device-management-fur-schulen.json -48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 services/agent/knowledge/07_projektunterstutzung.json -ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 services/agent/knowledge/07_storage.json -2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 services/agent/knowledge/07_telefonkonferenz-und-softphone.json -748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c services/agent/knowledge/07_verbrauchsmaterial.json -7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b services/agent/knowledge/08_datenbanken-plattformbetrieb.json -59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c services/agent/knowledge/08_datenwiederherstellung.json -7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 services/agent/knowledge/08_fachanwendung-neue-anforderung.json -f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab services/agent/knowledge/08_hardware-neubeschaffung.json -b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 services/agent/knowledge/08_netzwerksegment-und-vlan.json -1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f services/agent/knowledge/08_neues-drucksystem.json -b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 services/agent/knowledge/08_padagogische-lernplattformen.json -ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 services/agent/knowledge/08_rechnung-und-kostenstelle.json -3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 services/agent/knowledge/08_telekommunikationsbeschaffung.json -0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 services/agent/knowledge/09_container-und-docker.json -4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 services/agent/knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json -91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 services/agent/knowledge/09_inventarisierung.json -d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb services/agent/knowledge/09_protokollierung-und-auswertung.json -267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 services/agent/knowledge/09_prufungs-und-klausursysteme.json -27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e services/agent/knowledge/09_ruckgabe-und-aussonderung.json -eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 services/agent/knowledge/10_haushalts-und-budgetplanung.json -523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a services/agent/knowledge/10_kubernetes.json -1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 services/agent/knowledge/10_microsoft-word.json -8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 services/agent/knowledge/10_schulverwaltungsanwendungen.json -813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d services/agent/knowledge/11_devops-und-automatisierung.json -213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a services/agent/knowledge/11_microsoft-excel.json -a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de services/agent/knowledge/12_microsoft-powerpoint.json -e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 services/agent/knowledge/12_monitoring.json -2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c services/agent/knowledge/13_outlook-client.json -cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b services/agent/knowledge/14_office-vorlagen.json -9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d services/agent/knowledge/15_office-add-ins-und-makros.json -7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 services/agent/knowledge/16_office-aktivierung-und-lizenzierung.json -f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e services/agent/knowledge/17_serienbriefe-und-dokumentfunktionen.json -5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 services/agent/knowledge/example-vpn.json -7418ad39aa0899a85e69a9910d99ff1cb01757d5ae2edaa885c5ff7a815cb580 services/agent/knowledge-category-map.example.json -895f8400aaad550ff2b96262eaa54e954d603129682e97aa4964539eb19bd7c2 services/agent/run.ps1 -d1d4ab8145a7131c36df51d3a9fbdcdeb0cda3de134fe24c0e8d52370862f3b2 services/control/Dockerfile -f30ddf9251860d92717276483f7a2c2d7405f0516940ca62abc21d97ff3f3ad2 services/control/cmd/engineering-graph/main.go -d989c1706c42eb841d9a719243246f5c04059489969188a2cfbe0f6ac03c4987 services/control/engineering-graph.json -ba44c599b9faf861eca647614c4abd18548e2708233cf462b11eb277e96449b8 services/control/go.mod -da37393e58ff53847f26b1051f6d3a5270571d290b33d79360bae0dcb3513829 services/control/graph.go -e6a8021a44219219fe37ab5edc45d1908fc6579f653aa80c9966810d891ad27d services/control/graph_test.go -d373caddd318992fe25be3879f3606b81bf393d55a2cb8457d3984b8af499950 services/control/index.html -a42b3daf9b816503801f666e322668b3ad106ff8c1e1b83855e801e7db4eabe5 services/control/main.go -f0ec671cd3f00083d9b9d69c82ffcaa8efe08122837671e812f2a510f3c4e321 services/control/security_test.go -457fa6b4c81a62197eb1e76a5472cd571d5c69659110fcd9bc016582f1b5dfa1 services/knowledge/.dockerignore -e82cbdb6336e424c4b9d8db5d149016d606579292184582788a9bf295895cda4 services/knowledge/.env.example -e45bab225ede9bacef0b53f84fb0577235d9a81967ae2a53ab7c7f4d66bd4ccd services/knowledge/.gitignore -d3becb2b7565ba0b1d32bd8d3b085dca6adec2d2b153583c8c6855e0eea43b66 services/knowledge/CHANGELOG.md -d3c5e5636f96c641b0181fd91bb2cf4802529a7355ba0065cea49356057e402f services/knowledge/Dockerfile -31f5067cf8e45271081ff08bf0d9afffef74d300628ddc1bd1db6e87e6d4e922 services/knowledge/Makefile -d50e9892824dc9e2f86b7f4fbfdc81165a8c6b403e4b2b9f6fd5bd2f22c295f0 services/knowledge/QUALITY_REPORT.txt -bcafd8739157e677416bd4e7c3693eafb1572d72384830ab839dd4dcef74d3ee services/knowledge/README.md -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/backups/.gitkeep -cbaed4e619df99e83eef694b12b660757b749bf64c0a340143bab523a121c7f6 services/knowledge/cmd/server/app.go -ba6569476482d21ee6eb4852d6f1a8dbe052335f3be4669ef96298d0f5a5028b services/knowledge/cmd/server/app_test.go -fdaeb96aea39074522e75c2aeae8036ada253167d2e44bba156b10a7b8cf7c61 services/knowledge/cmd/server/main.go -1867f5c83786628441a1c9e3bb96d90d496f5cab24b6dd62a80376f256bfa497 services/knowledge/cmd/server/viewer/app.js -9537e946b22208a5a78f11a29a92c8f379805f51f3de3d3c74a7cd068d876418 services/knowledge/cmd/server/viewer/index.html -58f16f955f1287bc077933980e50eaae238ab2eb40599242abd820a31aca79f5 services/knowledge/cmd/server/viewer/style.css -0269039e6baffbf8ecc423fe021e91827924a9c47decbd0816257aa052702303 services/knowledge/cmd/server/web/app.js -7b1cdf4c71867e0563eb0eca460c60be34ab839099793e0ad9faf4a84b29c5e7 services/knowledge/cmd/server/web/index.html -c6e48f5098243eee4de816d515cd71d8f8904deeedb335966b1db320d574e2e1 services/knowledge/cmd/server/web/style.css -6e2430ead0897eba9ffb7b3df18e61136f54a8da6b4fb9e711490623fca31a12 services/knowledge/docker-compose.dual.yml -f5d6b8c7891ac8ab237423914f67bff5b91f7e3abd255622eeef26ec83feda37 services/knowledge/docker-compose.yml -5a49eb8794bdb76d14c29060ed6c4a9f0126d4daa2f2ae45b11b8d1e7ccaba45 services/knowledge/go.mod -06893c64974dac010903569e7554b1a245a8862538e3dc1b10375932eae6e5b9 services/knowledge/internal/aifallback/ollama.go -60727530c541fdce632fe071173c4846c3a4029929619caf9389f31f27e3c1b0 services/knowledge/internal/aifallback/ollama_test.go -9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 services/knowledge/internal/brainactivity/client.go -ff6d65a0a4648464a89c67e06f5c33a4ec87d7a41e84d894725e8ed582d64acb services/knowledge/internal/obsidian/export.go -e8e91a8ce16c1905c963ac57a156006e92323be80615637a7b1b477b89464012 services/knowledge/internal/obsidian/export_test.go -7fa9c38a719f2f46bd169438aec3c6e8595b047d63236d2a5e78db9cde6924f0 services/knowledge/internal/staging/staging.go -3c1f5726489a773536e430152b41944bd623ed0f88939688744611371462dd6c services/knowledge/internal/staging/staging_test.go -3bcb39124c72cb46efc7cf794254ab5821dbd7da57e7a20b1366862a71b874a9 services/knowledge/internal/store/store.go -c98bd048680978d06d34303db9729b130eb7cfc0750f15ee1a4e7abfe582dca1 services/knowledge/internal/store/store_test.go -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/knowledge/.gitkeep -81893d2cba93d7a30c5c728559e81a7b7e2eca87cae8f9e239aeefcaaf604d60 services/knowledge/run.ps1 -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 services/knowledge/staging/.gitkeep -e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 staging/.gitkeep +27dc46be5cbb1b171deff7fbd2f28bff1be802dff403797535fd8968bb98c8eb ./.cbmignore +079e174cd38e79421469cb5fc79ff4d886015cd5ec97c538466f97d70ce5331a ./.env.example +813c6a53b389380997fc71835ef72d3b593e1808c4fcc16dc0c309608a389f40 ./.env.subagent.example +2d49d2ee955bf352cc7e76068ec842808c0bf7bffaacb6a129b641a78bbde0e5 ./.gitea/workflows/ci.yml +cefe3681ecaca9bbbf7fcc18e668c0208e12a2260b8527561d50818916cbf77a ./.gitea/workflows/release.yml +e1ff71187cc3411a85067b964264011db7bd109a585ef7b9ea5b08bda039d813 ./.gitignore +0451d06da4395dff53c5f5085bd5a78eb54a5a22ff2dbf9a717dddd13babf91a ./BUILD-AND-RELEASE.md +3fff38aaf7e2ac300714f4ce5a6a1303f6a4e2b7e809769e9e1cb1997d1721f1 ./DISTRIBUTED-DEPLOYMENT-BUNDLE.md +ca915a34965137f3b709a862672d26077c17fb79f7e5c838cda8663ddfeac05a ./MIGRATION-FROM-MEGA.md +ccfc4c139345a69d05cd2b809b0d09b4332d98e5b4f79aace4db0b648e3ea814 ./Makefile +a0f69a743e32a43229c486495e59fdecd9d47ed8723cb6edea1fe02d32bb5098 ./README.md +4858caa52c0fb6cf302a1c581d07d448e5e90e0daa797e5819610fd6223bd348 ./RELEASE-NOTES-v1.1.0.md +01163462f46314f57660677fdef407c6c2884412ea850aab13a4f650e8c29f50 ./RELEASE-NOTES-v1.2.0.md +4da388ce660aa3b7a0d8075ec066a025b5437960973397360fcb9a5d4cb58c96 ./RELEASE-NOTES-v1.3.0.md +257d6d3ea1d22b8ab97adb5d35ca5ac806702d75cea66982dc711754040fc7a2 ./RELEASE-NOTES-v1.4.0.md +0723a0151b72d492899a5c5409e1fef37de1fe4c458c3053f2f7eccd4b6e70c5 ./RELEASE-NOTES-v1.4.1.md +58923d3292f11487526b8dc4d6c0992025c0f946df8435fde5c91ef4581d46fd ./RELEASE-NOTES-v1.4.2.md +548f3168f8f29add4f510c4c8048a74465f311dbc3ba2436e5b8db5cc898c552 ./RELEASE-NOTES-v1.4.3.md +61ceebe5a891388336795fda2fd0d1ad10373b4ebaae7d2e670f35766115c604 ./RELEASE-NOTES-v1.4.4.md +15a3defdd5bbf07ebaec1a2e8626347a027a3622f10afb508f7a21c9b0bdfa71 ./RELEASE-NOTES-v1.4.5.md +d10b1dbd87585d72144d816449800dc17b6d7f8faba2dfb4eca93ae3def2c631 ./RELEASE-NOTES-v1.5.0.md +d069259c99322966b525ab451defd6386ea182bdbdba58122e287df395e2396e ./RELEASE-NOTES-v1.6.0.md +cd02749b3f18cf635e2ee78f8895c50a397317ad8ccb416311f07a653126233d ./RELEASE-NOTES-v1.6.1.md +b819acb6838b5afcc8aae8ac93b08b15c12f86583cca5b5ad41a4d82faed71a4 ./RELEASE-NOTES-v1.6.2.md +a2d5e268fe70979caccd895156b3d33bd61a3cc7e660ab7a5b07066849b3e248 ./VERSION +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./backups/.gitkeep +8127e9db5e5e0af1d88770dc8fa60b381de45dbcc843262698cf9501409b4d58 ./deploy/searxng/settings.yml +316c40e929f7341b9a639d9351438d8da9d62e40783c1f05ade80ee2540ee08f ./deployments/README.md +30e63c048256d33c46931b9d9746652fd078845f446b8e395e7eb362f88b67eb ./deployments/agent/.env +0df71e0cca461b82f4991368c6f53c11bcaf62fdbdb3fda0f3aad1555e1bad36 ./deployments/agent/docker-compose.build.yml +c6c255cb0f19421618a8cc8fce3fdcefd707d39e8a371f4136b1445e3ecb7ca3 ./deployments/agent/docker-compose.yml +8a8ed00d937a8d2620e8a9713754e2b0e9c048fd2bb9b0d014edf8978bf7a961 ./deployments/combined/.env +1cdd0d8f851d7f7d8533443d02496a9f368e4a4194faf39408ea67487b5f77d7 ./deployments/combined/docker-compose.build.yml +67697cc388446999f21528e19df491d86aaf1141c2cbfbc3182d0149a4958458 ./deployments/combined/docker-compose.yml +2284148fdb09db7cabba462eadfa9ba10f88a2c458279d774c5c28cb5ea31b94 ./deployments/cpu-subagent/.env +fa1f4ebcf753f7d3ba7b49a1bf9b9738d2f9f3cf1b88b3ed4403a819de196b4a ./deployments/cpu-subagent/docker-compose.yml +dead361b179b6f761b160bd302f304b62941781cd542aba29eb5cfca8ec0497c ./deployments/cpu-subagent/preflight.sh +28180b0dc3cc22d90b84ef3f2dffa30f05e4255b8dd15824d3d200d6628fbd5e ./deployments/gpu-subagent/.env +d38f8433fb34c0d9b88b788cc95219c8b8ca96e6de93243c9a9eb8fc64946e40 ./deployments/gpu-subagent/docker-compose.yml +36d44f3bde10d228881273f04a309cbddba394c8bc559cb48f7d37ee8f2f4e3a ./deployments/gpu-subagent/preflight.sh +aee3a1b054e2f84b4f3d148067960d7d19b2edaf9eeb5dd8b02fbee4703481af ./deployments/knowledge/.env +81013b617610a4903ac7e237c231d62e0aa5caa083d73a5a838316db31d9dc55 ./deployments/knowledge/docker-compose.build.yml +6686e18e07453dcfdb5e57c0d456833ddf1e7a921fdd5f05abf324719fc69d5a ./deployments/knowledge/docker-compose.yml +966a910f90a25f5b434f386b0ea9828c5b4e270166f7101efcd582a37f9d13fe ./deployments/master/.env +a7d49be8630c475230585db69ad977339d6b3e95c36379e75682f6ff124ab588 ./deployments/master/docker-compose.yml +0c2fc23f8f6deced618e025cc66fa5711349439814d8a2c6d0f347990b4632e1 ./deployments/master/monitoring/grafana/dashboards/neuroforge-master-subagents.json +083e25e212b70a4c0fb5cd8d2cd5a5735f8ae230809c7fac40187f785a8eb43d ./deployments/master/monitoring/grafana/provisioning/dashboards/neuroforge.yml +72305a9fded6b40fe740ec82dac495f006990e6e37de754d1fcd01d93da4834a ./deployments/master/monitoring/grafana/provisioning/datasources/prometheus.yml +3e42499e55aa2d5c211728809e22d713162c88a493610c2107da17891c3567ad ./deployments/master/monitoring/prometheus/alerts.yml +aac90d7e75a79256f77d730ca80fc1824e86f4299192526910b6f0226cedcbb9 ./deployments/master/monitoring/prometheus/prometheus.yml.template +57fa3c3be7f476c791da0fd7e9985b38be82f1d9a816d13d75f645f29de57e86 ./deployments/master/preflight.sh +8dc996e535cdb7867421a02536f8b4ed0eb5d0dfdad0959ddd9db90fc04c7d4e ./deployments/ollama/.env +15a8680873d1dbde2b7deca0a30b786f49f4498f499269e9cd24c33e0b243333 ./deployments/ollama/docker-compose.yml +8d4943a3e023ed3b24312ff0b406298026b4d32aac6873b0c8488812b20dba4b ./deployments/ollama/pull-models.sh +5d2ca8625ccb84cb868c3f7450a0c63fc128967aab5677363dfeab8f214f92c3 ./docker-bake.hcl +58db2a87b703a5bbddc20b140907d187f8388e7db87b079ae9f6ffaac9c121fc ./docker-compose.dev.yml +1360e44c867fdb9b754fe4585d57a02daf0f4820a7602a4e588c94c45eeb28e8 ./docker-compose.subagent.yml +0b5325d5e86ff75292031316d8ea2837e8c3eecbf77dcb67db39407edc1189fe ./docker-compose.yml +e4c516375e450c43b5193b1257880c02f2132b05a16e19ab1c96d57182fa070c ./docs/ARCHITECTURE.md +34e2c7ac5992389cc51b40f740c1e206f20bffc67bdf19cb59d94b5df67a7082 ./docs/BACKUP-RESTORE-v1.5.0.md +9467a3c0796a87bada0eab6ad191913e628b4abc05520cbb4b73fac76d334481 ./docs/CODEBASE-MEMORY-MCP.md +74cab9b3ce3f6ed2bb5f1de99e1fe5c6f311e90550d078e10e140b726674957e ./docs/CONTROL-CENTER.md +88955b0ea289ccc63cbd3404f5b00bb446b38270408dcee23a6103a698f1f76b ./docs/CONTROL-MATRIX.md +323402668da4d8e1d97a29c45db753834f6c3a16739eebebf7e016f06af160bf ./docs/CONTROLLED-AUTONOMY.md +51b4a8d7daff787f099b77bd5a33ebf8038c0812bd3056f0ee6afe17e103681d ./docs/ENV-MIGRATION-FROM-LEGACY.md +a19522149d097ede5751dd6b9db515146ac47d14aaa7a7b49515e495388d0e0e ./docs/ENVIRONMENT.md +9c49332c6d0e23cd7f82adf23f61bcbd795ad30401990ae99b23295b2ab4cd09 ./docs/GO-LIVE-v1.5.0.md +6a9862c2ffa95420a3f8d2c3901a46f00c77a38826913280bd0e367e3133498f ./docs/IMPLEMENTED.md +65ab928c3a0c283131e48322685d240507a085c1fb059d4c114465774473c335 ./docs/MASTER-SUBAGENT-ORCHESTRATOR.md +9c38807cc12fba6f94cadc4694996de58e6d2d5396e55fc0c4bd404bb13f53ad ./docs/MIGRATION-CUTOVER.md +a71d28353529906cee08cf90f35e0b0cf97bbc94ccfcef830145a2d561b21a9c ./docs/MIGRATION-MANIFEST.md +4330b3adefd8c40d174e1e878ff7cf075d99a44ec51251953d1a58d3a0fa8ca3 ./docs/MIGRATION-v1.1.0-to-v1.2.0.md +46672e5984cdcf5f31e88dccc273041aa277552cc9c5ecbf59a46b3de133da7b ./docs/MIGRATION-v1.2.0-to-v1.3.0.md +3738dc79be0598316dc397f1fcc71cedda67b604a3038b116c2ea001dc3105ce ./docs/MIGRATION-v1.3.0-to-v1.4.0.md +b285d050223844f5fd05014c2278199eaccd7525fbbf8a6dae724ed87ced0ee8 ./docs/MIGRATION-v1.4.4-to-v1.4.5.md +57858f4f2b6670106edf5a88ce705649d547c65c26407d6117040296122a0e26 ./docs/MIGRATION-v1.4.5-to-v1.5.0.md +c905084e03788887894e37c040492b8ce2386ecc25ac3ab2e10bfd4b0a7cd167 ./docs/MIGRATION-v1.5.0-to-v1.5.1.md +12619874bce206ed6e817685d2def62244d6bcceb6eeccbca9b0936fed74b8a2 ./docs/MIGRATION-v1.5.1-to-v1.5.2.md +44219c5fe32a0d6d2c32719e243793a54f3b7624cb45a497cda78c4a443774ae ./docs/MIGRATION-v1.5.2-to-v1.5.3.md +fd78b77e18277a8bb81cf6ad33e67c0e7fae3977dc1bbf74787b0cb0cb9a4b4e ./docs/MIGRATION-v1.5.3-to-v1.5.4.md +4e2e595c0c1b78271a971f6e039c48416114ad9916ad7afc5293c2cff4ec8f9d ./docs/MIGRATION-v1.5.4-to-v1.5.5.md +b38aaf9d352ad70a9701566fc24d73d26c50c407291fe1c38b08b1afad9435bc ./docs/MIGRATION-v1.5.5-to-v1.5.6.md +b57e66a589cff7331d547afc0e031accd1d576c46c013143f76311a84eacbcfe ./docs/MIGRATION-v1.5.6-to-v1.5.7.md +6c234e8bfcfa63f879f0748ccf9ca006595f5ee905b97d316f685154a4a5a2b9 ./docs/MIGRATION-v1.5.7-to-v1.5.8.md +91e8541a86f3b6b2080439b3177309b584f5d6c637baa286d7ad9d40afbbdba9 ./docs/MIGRATION-v1.5.8-to-v1.5.9.md +1d114256ef855284dd5a11b40682ce67ad27e17b41045625cae251a16531d0bd ./docs/MIGRATION-v1.5.9-to-v1.6.0.md +c23f60d75e790ab7317d6bc06c42ccbdb303bce1c11ace2410326257312058b9 ./docs/MIGRATION-v1.6.0-to-v1.6.1.md +b6f7dd05f7d86b5c22828c043d88d7cae22a0d759d7745719c211ca5f042f396 ./docs/MIGRATION-v1.6.1-to-v1.6.2.md +2a01fb10a3e04eae1800a7c7e0aafc31e9bbb23584cea54d849b004716ef81b4 ./docs/OBSIDIAN-EXPORT.md +d53949698d69a71334f62f02f8b53d8637cd9a3dca8c67a0cdfbc6e56cff8d2d ./docs/OLLAMA-BEARER-AUTH.md +040010a807178d33797106e04716822f8d147d7d5833c1ba70865a1827f8484a ./docs/OPERATIONS.md +69ea49bc76690ac489aea908f4e784a14293e92d62478a240eabea53c5f90820 ./docs/QUALITY-REPLAY-example.json +bd2d3c43a09a89fefd0e844433064d7d7384a414179a754c6de94232d738fb04 ./docs/QUALITY-REPLAY.md +6d72b49b5b0ce93b94597cf5688403c468cd3b82f4d87470d80f108c5a7ec531 ./docs/RELEASE-v1.5.1.md +84b12cafd6320d781b99970cb48b7252da5aff94797d45af164f8a56dee94e5c ./docs/RELEASE-v1.5.2.md +f0ab4fa6b353059acf6b41a1e5a484ebd698684102724eddf8f8b91d7ff321a3 ./docs/RELEASE-v1.5.3.md +bc9067063b13788393fc3389ac60c0db50e35cc9d7d733311fca2fc6923834f1 ./docs/RELEASE-v1.5.4.md +677bf7eacb64adce080dcc1184dca0d7c088727b1c49242df14a7694eabbb593 ./docs/RELEASE-v1.5.5.md +4cde69f2875d5f97544fb85ff066eb671a40d9670302c81b2a9d9b9a95a9bee3 ./docs/RELEASE-v1.5.6.md +f7bbf78e76592762fe6e2a08eff9110a273962ae3145da287c5875355672db7f ./docs/RELEASE-v1.5.7.md +5757dd833f1f8b7b0a7b90b09324cb8989496ea6326d652d659a59f215a3172d ./docs/RELEASE-v1.5.8.md +245f942b72da17cb6c953720bd9c6742f71723be06bdb842c512851aca351e3e ./docs/RELEASE-v1.5.9.md +85160313c5dd89faea2e7404f090a6cef13a9b33758124dfc4d748a119375c32 ./docs/RELEASE-v1.6.0.md +be749a09ddbcd4cf427316c6fe531f138e9bfaa6c104f4c11a08f7555231e8b2 ./docs/UNIFIED-GRAPH.md +77d91565660789c621b05bd69fecfa3c2ef736f93855eca1f58b017e61c76f90 ./docs/VALIDATION-v1.5.0.md +3ea1953b2b4eb8f966310a65a12a681fb44e4eb3857b843312e235759aacfce5 ./docs/VALIDATION.md +be4810451750abb676eee0edcc6f164d86f372f32b185f6f730bf84538cef4a8 ./exports/knowledge-obsidian-snapshot.zip +dfa65e55e9ccf642ae5ef8ef91c62f4220b31ce4304ea4e1061f2e7d8c1b0fb4 ./exports/knowledge-obsidian-snapshot.zip.sha256 +99ffd5f497239a5e17b4e4bc79c7a1b5e1e41fdaf9b3f07beb7179050bbddcc4 ./go.work +5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a ./knowledge/01_active-directory.json +70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc ./knowledge/01_arbeitsplatzdrucker.json +96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 ./knowledge/01_fachanwendung-storung.json +521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 ./knowledge/01_festnetztelefon.json +88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 ./knowledge/01_hardwarebeschaffung.json +4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d ./knowledge/01_kennwort-zurucksetzen.json +5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 ./knowledge/01_lan.json +95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 ./knowledge/01_neue-it-anforderung.json +ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 ./knowledge/01_padagogisches-netzwerk.json +f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 ./knowledge/01_pc-und-notebook.json +ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc ./knowledge/01_sonstiges-und-unklare-zuordnung.json +5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 ./knowledge/01_verdachtige-e-mail-und-phishing.json +5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb ./knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json +ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 ./knowledge/02_fachanwendung-bedienung-und-beratung.json +0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 ./knowledge/02_gruppenrichtlinien.json +4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 ./knowledge/02_monitor-und-dockingstation.json +bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 ./knowledge/02_netzwerkdrucker.json +7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 ./knowledge/02_rufnummer-und-nebenstelle.json +efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 ./knowledge/02_schadsoftware-und-virenfund.json +2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 ./knowledge/02_schulverwaltungsnetz.json +830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 ./knowledge/02_softwarebeschaffung.json +fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 ./knowledge/02_wlan.json +26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 ./knowledge/03_ad-gruppen-und-gruppenrichtlinien.json +b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a ./knowledge/03_dateiablagen-und-netzlaufwerke.json +0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c ./knowledge/03_digitale-tafeln-und-prasentationstechnik.json +a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 ./knowledge/03_fachanwendung-berechtigung.json +f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 ./knowledge/03_internetzugang.json +5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 ./knowledge/03_lizenzbestellung.json +d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b ./knowledge/03_multifunktionsgerat-und-kopierer.json +c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 ./knowledge/03_rufgruppe-und-weiterleitung.json +b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f ./knowledge/03_sicherheitsvorfall.json +ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b ./knowledge/03_tastatur-maus-und-zubehor.json +7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 ./knowledge/04_anwendungsberechtigung.json +6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 ./knowledge/04_computerraume.json +36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 ./knowledge/04_fachanwendung-konfiguration.json +a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 ./knowledge/04_lizenzverwaltung.json +e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb ./knowledge/04_mobile-endgerate.json +adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 ./knowledge/04_mobilfunk.json +1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc ./knowledge/04_scanner.json +658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c ./knowledge/04_schwachstelle.json +5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 ./knowledge/04_serverbetrieb.json +ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d ./knowledge/04_standortanbindung.json +9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 ./knowledge/04_standorteroffnung-und-umzug.json +8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed ./knowledge/05_dienstliches-smartphone.json +47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 ./knowledge/05_digitalisierungsvorhaben.json +4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 ./knowledge/05_druckertreiber.json +74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 ./knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json +a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 ./knowledge/05_hypervisor.json +0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e ./knowledge/05_remotezugriff-und-vpn.json +9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 ./knowledge/05_schuler-und-lehrkraftekonten.json +2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 ./knowledge/05_softwareinstallation-am-arbeitsplatz.json +9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 ./knowledge/05_telekommunikationsvertrag.json +fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 ./knowledge/05_virenschutz.json +e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 ./knowledge/05_vpn.json +153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 ./knowledge/06_betriebssystem-am-arbeitsplatz.json +d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 ./knowledge/06_dns-und-dhcp.json +b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 ./knowledge/06_fachanwendung-bericht-und-auswertung.json +77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 ./knowledge/06_mobile-schulgerate.json +e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 ./knowledge/06_scan-to-mail-und-scan-to-folder.json +cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a ./knowledge/06_sicherheitsupdate.json +ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b ./knowledge/06_videokonferenz.json +e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c ./knowledge/06_virtuelle-maschinen.json +71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 ./knowledge/06_wartungs-und-supportvertrag.json +ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 ./knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json +2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a ./knowledge/07_datensicherung.json +46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e ./knowledge/07_fachanwendung-update-und-release.json +8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 ./knowledge/07_firewall-freischaltung.json +03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 ./knowledge/07_funktions-und-sammelpostfacher.json +a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f ./knowledge/07_geratewechsel-und-umzug.json +9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 ./knowledge/07_mobile-device-management-fur-schulen.json +48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 ./knowledge/07_projektunterstutzung.json +ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 ./knowledge/07_storage.json +2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 ./knowledge/07_telefonkonferenz-und-softphone.json +748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c ./knowledge/07_verbrauchsmaterial.json +7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b ./knowledge/08_datenbanken-plattformbetrieb.json +59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c ./knowledge/08_datenwiederherstellung.json +7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 ./knowledge/08_fachanwendung-neue-anforderung.json +f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab ./knowledge/08_hardware-neubeschaffung.json +b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 ./knowledge/08_netzwerksegment-und-vlan.json +1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f ./knowledge/08_neues-drucksystem.json +b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 ./knowledge/08_padagogische-lernplattformen.json +ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 ./knowledge/08_rechnung-und-kostenstelle.json +3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 ./knowledge/08_telekommunikationsbeschaffung.json +0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 ./knowledge/09_container-und-docker.json +4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 ./knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json +91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 ./knowledge/09_inventarisierung.json +d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb ./knowledge/09_protokollierung-und-auswertung.json +267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 ./knowledge/09_prufungs-und-klausursysteme.json +27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e ./knowledge/09_ruckgabe-und-aussonderung.json +eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 ./knowledge/10_haushalts-und-budgetplanung.json +523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a ./knowledge/10_kubernetes.json +1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 ./knowledge/10_microsoft-word.json +8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 ./knowledge/10_schulverwaltungsanwendungen.json +813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d ./knowledge/11_devops-und-automatisierung.json +213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a ./knowledge/11_microsoft-excel.json +a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de ./knowledge/12_microsoft-powerpoint.json +e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 ./knowledge/12_monitoring.json +2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c ./knowledge/13_outlook-client.json +cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b ./knowledge/14_office-vorlagen.json +9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d ./knowledge/15_office-add-ins-und-makros.json +7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 ./knowledge/16_office-aktivierung-und-lizenzierung.json +f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e ./knowledge/17_serienbriefe-und-dokumentfunktionen.json +5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 ./knowledge/example-vpn.json +158438592dbbfc770975a09234e17d4b48b41ae5a0a6b1b55c448539d86e12a6 ./mega-project.json +fece435eff951e86c8383277367e42c4132d104d28db9176eb8ecb026ff3140b ./patches/SHA256SUMS +47a6fa2c79bbba0c04af86dfa65d58529f492c698060fe586456c22a4eadb877 ./patches/glpi-agent-mega.diff +9b411c90d96a86c80f088ee4637046eeefaf31c62059d11aa1a999d6eb08b5b4 ./patches/glpi-knowledge-mega.diff +f0491de3cb6201f98ca6be8e865237adbba4772471f7fc7165d030e0c045fdb6 ./patches/neuroforge-mega.diff +576e75ca9191bbef7f136abde90c10a1c6bf2e36450a421fd3796e988bd2af00 ./patches/v1.1.0-to-v1.2.0.diff +7a5e0ae1d09b268d3ac62a51f92bfedf6771be26b909605a347f26d47d6fa8cb ./patches/v1.2.0-to-v1.3.0.diff +163a8a990aacd986bacb385494c2b2062ac10a9a8f0a456d19a6d1746c2fc500 ./patches/v1.3.0-to-v1.4.0.diff +654de4aac7cf318680872897c3de192afbafb61c7169acb8e7514116df86efde ./patches/v1.4.0-envfix-to-v1.4.1.diff +1ebc306bd6870389500c86cf07dcd677b5bd0448c762812c7e1c0f8732f36eb1 ./patches/v1.4.1-to-v1.4.2.diff +651c883facddfd0316f89e3cf3c24fd913371fcec2a5f447b8d105fa5dec463e ./patches/v1.4.2-to-v1.4.3.diff +d90be604acf6ecf5d821890d5864307853caf0e0678a3249eeaa1d29748a6ada ./patches/v1.4.3-to-v1.4.4.diff +01ba8cb6ea6ab0abd84295fb969fcfb93d841cd46ae1813fb0de9b23b2d5b22e ./patches/v1.4.4-to-v1.4.5.diff +617b434dcdc0dd0be5cdb15740661f0a513b330b99835364b508e21467f86ae4 ./patches/v1.4.5-to-v1.5.0.diff +b6bd8c6c04b5fca8d768279a5471c32e15dbcc57449b7d020f3e228f47adc310 ./patches/v1.5.0-to-v1.5.1.diff +b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 ./patches/v1.5.1-to-v1.5.2.diff +5c799db6701dce8019aeead5bccef6fc5fdc869e30785853eaae8d0eaf7ea43b ./patches/v1.5.2-to-v1.5.3.diff +27fd7fe12e9d774ced67cbcbf0f2e2e385736b48d01d655a1a9cf4b14b39bdcb ./patches/v1.5.3-to-v1.5.4.diff +7d0c4c85ee6cb89c6200cf35f23ea92c93ff5fdd9f2887c179c83fd5e5657281 ./patches/v1.5.4-to-v1.5.5.diff +405b41cbbd1ec48f316b1d935e67bc8d002fa1a3bd583fb813bf0875bd30a2e0 ./patches/v1.5.5-to-v1.5.6.diff +257a78b8f6613f92086a04bf53455f12e9bd1ab94775add31819ada465582163 ./patches/v1.5.6-to-v1.5.7.diff +53ab438b31a31590edbfe00eea6ba9d9ebff2b4c6255d7ac2056c86757f65c3a ./patches/v1.5.7-to-v1.5.8.diff +24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 ./patches/v1.5.8-to-v1.5.9.diff +e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 ./patches/v1.5.9-to-v1.6.0.diff +0cdce94c11a931e11c3c908f2fa3dfa733c5c602ac00f7f89e41b8c301574766 ./patches/v1.6.0-to-v1.6.1.diff +0b09d93f115608f4ab6ee47e0a085f254eaeb8914fe1415fe73dccea69e5d166 ./patches/v1.6.1-to-v1.6.2.diff +117b36c44bb995154ffaa3a8c6aa8ae32a669f976ce4b7c41c5cc8d258ccb51e ./platform/neuroforge/.env.example +39319b6f2058e4c8d6656a9cf01675374f81a04075b956b093a2befb5e05ada4 ./platform/neuroforge/.gitignore +189486a885c7fb78e0eb878d93cda0c70ca6d7ff9bfdfb3f5f32487cf03a9688 ./platform/neuroforge/BENCHMARK-v0.5.0.md +b9cfe0119f316b8143ebd33e9f66a8da36493a2c6c17acf2519ecba023223424 ./platform/neuroforge/BENCHMARK-v0.5.1.md +fc3aaadc0675ebb6dfeb42d9612a68c1905ac575e2c33299ca6e1522d8bdbeb9 ./platform/neuroforge/CHANGELOG-v0.7.0.md +a2908a0931e63244849016898c26eb023200bb9c1b1f7a8e62f5858000c94fd3 ./platform/neuroforge/CHANGELOG.md +2b286a4066da5b6bf7ce83c42ba7c97df6d4c6f8b71aa3f28d1ff824a7814391 ./platform/neuroforge/Dockerfile +9a9fd7708b61027e3677044ab1cbf7177119eac796df4d8786d39011da28729f ./platform/neuroforge/IMPLEMENTATION-NOTES-v0.6.0-dev.md +3146656e702607501cb348c77dadff608f9c152c4104647230848004e2692251 ./platform/neuroforge/MIGRATION-SQAR-VECTOR-JOURNAL.md +ee7f52ec20dcce87b751637229fe26c65d5cb0a926ac32915f46bf1b5eedad3d ./platform/neuroforge/MIGRATION-v0.2-to-v0.3.md +3095ca2e4512744ccfb2c3c71d80dfdbd42fe24e556a374061a76b80cbc33213 ./platform/neuroforge/MIGRATION-v0.3-to-v0.4.md +680eb77c8d645c3a2afa4e92e3062d1dadaa079425ed8c3f0d73393931354b59 ./platform/neuroforge/MIGRATION-v0.4-to-v0.5.md +0a21148446a057c773700fcfb026230fa771e970482bd6bdebb9cb9a3c11f4d9 ./platform/neuroforge/MIGRATION-v0.5.0-to-v0.5.1.md +59d223ec40991ef152f32b8bec26fa283bfa6318942e33a1a2169a85b361d9da ./platform/neuroforge/MIGRATION-v0.6-to-v0.7.md +4fc6e6f43fa70f4fa8a085225ecdc7ecd460bbcebca01dde70fed48a4c933c68 ./platform/neuroforge/MIGRATION-v0.7.2-to-v0.7.3.md +02bc0ac5316d063304b17bc51780f2fe55fbdfc44abbb151bac2397a642174ba ./platform/neuroforge/MIGRATION-v0.7.3-to-v0.8.0.md +9dc61428742d401d226d2eb8a74577b881309e67bb5c609d01b5152a18cb0bb1 ./platform/neuroforge/MIGRATION-v0.8.0-to-v0.8.1.md +7bbec66a7539fa9f65d8448e2c59c2f76797679f24d3b96691dc8c68e81d560f ./platform/neuroforge/MIGRATION-v0.8.1-to-v0.8.2.md +5b9714a45f9a5d31a536eca9b2461f408751882aa6909bd2bb6398b8d8907fbc ./platform/neuroforge/PRODUCTION.md +3d80eb1d37e9f6bd828d3d499ecc88e011a9f34b44d5fff5a89fd517a39907c7 ./platform/neuroforge/README.md +38bdb771c428636d18cb4640653ff7028715d5bfc5ca06d5ebc59f8ddcd095b2 ./platform/neuroforge/VALIDATION-v0.7.0.txt +73b7f0b287ae15f230a7ad2996ec305e787e7a8b096f7654fc69cf4daee12673 ./platform/neuroforge/VALIDATION-v0.7.3.txt +c3d34504b1b8a4ef73382df061c7b272a55c2a3a7a720435c6050254da3f22e0 ./platform/neuroforge/VALIDATION-v0.8.0.txt +0371dc02e4ac40b3197104f0aea41fb1b7be9b60caad4fc203eae1de1ac8191a ./platform/neuroforge/VALIDATION-v0.8.1.txt +b4896439112f9ca0f0a43d55d43cb3a2c4c802c568b65a96d04a3ca9d841c358 ./platform/neuroforge/VALIDATION-v0.8.2.txt +780363b8ef02ff7e83eb72709f6396b09dec21a26403bb44f58ea8d6757fe33d ./platform/neuroforge/VERSION +0c5308f5a3d37ac23dce162fd5fab78ce598e41671db5dd50c9c4ae7f215d49f ./platform/neuroforge/cmd/bench/main.go +2a6da493d109574a13e4d40a4c46a4eb6c5b7da0be9e31dbd2ced162ea318f04 ./platform/neuroforge/cmd/server/main.go +3613379f9f9c3df373b6d686501e3e0290451cded5eae3ca307e5e14ddc829d7 ./platform/neuroforge/cmd/server/main_test.go +3fdc069ebfda289a997c4411e19da05fa175dfc0b8d411d3eb5dde9cc0012a5f ./platform/neuroforge/cmd/worker/main.go +a664f412a7c984d758e81b8d602ff5695789323067c1de7e8787dc485014dfd4 ./platform/neuroforge/cmd/worker/main_test.go +e3dacdaec3c629dd432f24218a50fa4de5e1698ac7f27e84b14f776b50beeb83 ./platform/neuroforge/deploy/learning-policy.example.json +3765a5faea1faeb72aad7878ff0159a56fa1b0ca1fc94a348a8c599e460b61dd ./platform/neuroforge/deploy/model-routing.example.json +dcb7f80239cf955941ede9bcdc3bda8f39d81db5e5b0e0962b373036b40a0df5 ./platform/neuroforge/deploy/neuroforge.service.example +72443642fd4c498554ad6e46db2a096c97530fba1ba20b842a478345fe54c46c ./platform/neuroforge/deploy/prometheus-alerts.yml +c30e5b2fd39e72894db22499259b6f97d225c5929a7f3472277853a348cab9be ./platform/neuroforge/deploy/prometheus.yml.example +dbbaa7fd4430b9cdb5302144b6d40f7ac4ab9f73e81da4ddb51e788dd373d95e ./platform/neuroforge/deploy/searxng/settings.yml.example +913a064a5ee736ac268e9e367a5fd0e4081622afe0a9f4be1a554a41c269338a ./platform/neuroforge/docker-compose.yml +fc993dc95fa49802ecb62994e4140dff18a27438e8a4f3c6352229c79b041710 ./platform/neuroforge/go.mod +1dc1d58bf4761f2aac3f2436f78c5942bf3a5968ef88840693dd6042960f7b7f ./platform/neuroforge/internal/brain/brain.go +976288422c0c4116d8c98af8a9b164ac670f9caf03eddc2d5e2e48747456d3b4 ./platform/neuroforge/internal/brain/consolidation_test.go +cbf267375414612ce2d4a697606576f756ce3611df543ca8cb63b6c2fcc64e44 ./platform/neuroforge/internal/brain/distributed.go +0eb3cb86d1c235ac21527aae79de7c26ce204412814c5ea84dd9389e0b0fca2b ./platform/neuroforge/internal/brain/goal_progress.go +6a1e33b71d88f963f244a5a00ea45ee65bcff4882a562148d8ba925b8692e083 ./platform/neuroforge/internal/brain/goal_progress_test.go +bb4cec696bc7e5fc2b1f2fab0b5c01552f523a7dc2bfa1cb5be5e5374b304162 ./platform/neuroforge/internal/brain/orchestrator.go +03ba39168194068f59e4be837de1c152e0d10e316461b235c61704571cfc0e08 ./platform/neuroforge/internal/brain/orchestrator_test.go +359955653c647125559afd6dc3ebe69aa5ca19ff7e825ce801b7bc24e5fbcfcb ./platform/neuroforge/internal/brain/policy.go +27e87af473d2d71ba94ffb9bf7a70934776f8c23ce45496ca0998ad3000fc156 ./platform/neuroforge/internal/brain/policy_test.go +9022a797aa1d49af20ced5d8c3e0b903be740fbaa7251e8a80d43cd04d2f68fe ./platform/neuroforge/internal/brain/research_quality.go +0a3c8f7d149e814e091595982dbaa69467f6bf11eec631471d133a9b21585ae4 ./platform/neuroforge/internal/brain/research_trace.go +f90474aa355d3c4565e61662e863bceb089795af01802e9b228c1b6c545bb7d7 ./platform/neuroforge/internal/brain/staging.go +4d7715622739a43b043a1f08ae7165db6ee6bbc22f3ab94945f56f226dc4037d ./platform/neuroforge/internal/brain/staging_quality.go +d4fed8b68d31f6fbd1992bde76abd4e7b2f9d211e53bf8b234369a6a53692fe8 ./platform/neuroforge/internal/brain/v3.go +3ae13251512ecad1423a33ce09889f961130fefaed9342170b2d5cc6b3b51893 ./platform/neuroforge/internal/brain/v3_test.go +4bc58463b659bd7e51db4c7dbeba053de90fcb41f392a7d6e62a8cd84ddaa092 ./platform/neuroforge/internal/brain/v4.go +a9619d9571f6ab6363b36af9fdf1690f9f88e9333eb6f2773ae68580b19666d8 ./platform/neuroforge/internal/brain/v4_cluster_test.go +76319080d3faaf856e9fe5e1aac5e06be433d2f06cc6b152bac5f05dcc943fff ./platform/neuroforge/internal/brain/v5.go +816b725594ea5f6938999799394eaea9dd619addaeec5447d8e7707da4c28c69 ./platform/neuroforge/internal/brain/v5_cluster_test.go +cd8f1281e25ce42ae8918abbed16d4bf57ada9c3a3cc1212edd28f77ccc9328f ./platform/neuroforge/internal/brain/v6.go +312e7578b5800ccb06755c19d7ef7026a362443804ff78cb7a11a7443af2aab5 ./platform/neuroforge/internal/brain/v8.go +31420af7145686a2c7eb42fc18ca1299604afcb5942cfdfd132bef5c5f05d980 ./platform/neuroforge/internal/brain/v8_test.go +8fbfdff13f20a44c16c0bae061e33e06bf34171f80256950d2e3dd2cc1f183ac ./platform/neuroforge/internal/core/types.go +65a8b8196343e7cfd9444ca314da4a83c81bc046d9478b8b93217d9cc68ba562 ./platform/neuroforge/internal/cost/cost.go +b9bb2934e01ed2bfb6b16e4139e10e51f1387112eb8654f7f692779a2fd2d273 ./platform/neuroforge/internal/cost/cost_test.go +fb66ce4ab760b979eacd4f7f17dedc41916f93c53ba582be572d250f99654695 ./platform/neuroforge/internal/httpapi/admin_app_auth_test.go +eb2b8c599e4e0838d920296391ecaacdba1578c4f3a6a0a9e8469fdba31217ed ./platform/neuroforge/internal/httpapi/goal_duplicate_test.go +2b1f124b31cff1f18379e3f3e7f2f54cc1dc3f18efb2a3a7b4679f06127a6003 ./platform/neuroforge/internal/httpapi/httpapi.go +680b7fdcb20ac76c9305dfb94e4ec7474526be7a44b56afb3db7e4a66c8e2c38 ./platform/neuroforge/internal/httpapi/index.html +20f4a6cc30d5ce84ceed4fbdfc9f5c57ab5f82274e0c54464817d571e0e881b0 ./platform/neuroforge/internal/httpapi/integration.go +c346dd0205548f7cdb00ea026f1afd82d59f87824eb983ebe84abfa7cb078974 ./platform/neuroforge/internal/httpapi/integration_api_test.go +f8cfcc7a2bc781394231c25e36e75c41563f5250a1387e5687880d2591efd93b ./platform/neuroforge/internal/httpapi/integration_graph.go +c289fe8748d28d4c1207b22924f7d1a7b2df25703d6df5bd1869acb325211b2e ./platform/neuroforge/internal/httpapi/integration_graph_test.go +29c365a797f1f162f4d7a5d32833123f7930b73ca3b649fe31e117453dcb9197 ./platform/neuroforge/internal/httpapi/knowledge.go +409dd8d7c5ca93cc82ce2b7bf17a12655c97dd27ad9767aa40619502830a0ab2 ./platform/neuroforge/internal/httpapi/knowledge_integration_test.go +cf285170ff175ce2bb223b4ffd87a35da604ec73b5fe386ac35402af1f9c3531 ./platform/neuroforge/internal/httpapi/knowledge_policy_test.go +3bca3bf2f3d486892cee5d7e41920517f2df504ae729ddbce7d840556fe08625 ./platform/neuroforge/internal/httpapi/metrics.go +c40412c77dd8635bbd4bf9c6d7eb11b0d7902aee896bdd1ee99e5cdfb72c7453 ./platform/neuroforge/internal/httpapi/metrics_test.go +83033a18a319ffb162c7cdefb2efe108a47dba81095ac34662349e232f8c22da ./platform/neuroforge/internal/httpapi/model_routing_test.go +38a9388b6907975aec933697ad87ddeb9e7016a279523f2526db3810df70b006 ./platform/neuroforge/internal/httpapi/outcomes.go +0b0b111e6b47f38dd3ca331a0c1b79b3de95012820f1c04cbe37c79460c09e63 ./platform/neuroforge/internal/httpapi/outcomes_test.go +b3b1f5cfd6b0ec04341978898d72856af03a95b8922daf9864caeb27e37f1925 ./platform/neuroforge/internal/httpapi/research_live.go +b37f7ec3be867666999a9ec314139dd1bc4c74efb53edfa224b4fe8119858aac ./platform/neuroforge/internal/httpapi/research_live_test.go +4e65ab0a760369a6d6c4932c180ff6c67f0768d91e08ccf471f53c6ecd5d09be ./platform/neuroforge/internal/httpapi/status_dashboard_test.go +3eb47f1b666336efe952961c19a1f46a7475acff8fe4dd7a59ade000aa1b9466 ./platform/neuroforge/internal/httpapi/v3.go +b1587e066b56ef72f17162583698614d31c413e2ac7e75359b731c2e882a50ee ./platform/neuroforge/internal/httpapi/v4.go +1ef5cacb0652f2a6301c57f8987dd848fbe590abb73cab44899d9152ccce45d0 ./platform/neuroforge/internal/httpapi/v5.go +1d534726d7a75f21f7ad28a959f73a3402c021f7f4a1b554f0600cf3abf669d1 ./platform/neuroforge/internal/httpapi/v6.go +fdf937aae569dd48d09da63085fa2656299a3ab2f55c7fc7f718c46df39a2683 ./platform/neuroforge/internal/httpapi/v8.go +0c8c389b085e4546c3269b1bf0c539414f3603522ddfd0665261a6448f31b514 ./platform/neuroforge/internal/httpapi/v8_1_test.go +889b82a7f90be0590d0087ccac06aad330710c19f31c43b12c28480a94659be5 ./platform/neuroforge/internal/httpapi/v8_test.go +60fd100c9057c14d78f91d636f7f23aaf2767088c46b7670b4266ce22cbb5ae1 ./platform/neuroforge/internal/ingest/extract.go +7576df6ad6f8b692f14db739958fabe583cc5bb547c6068739fd47c45910d2ce ./platform/neuroforge/internal/ingest/extract_test.go +e52e639864e90d82496677a303a54f3b4f72a6fc33e19720c751c0fd67cc2eea ./platform/neuroforge/internal/provider/provider.go +f9cfbd0bab443faf39973e6fe1777d7137ae5dcf58fc1b0d6295099e1b3fadc9 ./platform/neuroforge/internal/provider/routing_test.go +0538904936c00579cd7905aeafb7b7419c0c4a6e1841b6596bc8a07ceac64b6d ./platform/neuroforge/internal/provider/runtime_test.go +2907860f1b4a30df0522f4808c58808f5b184b7b6a3733a959b4619c528379e3 ./platform/neuroforge/internal/research/searxng.go +e890897c662b070997c3b18134bafbee5bf9b3fa5220045ac09dc6e3dfd4ac41 ./platform/neuroforge/internal/research/searxng_test.go +fe35f46c0b6dee7283142dc818f484006fdde6d71906d5f5fb3bc05c424ddd4f ./platform/neuroforge/internal/store/batch.go +c3bd0bb04e951181c72976c246673f3b7c80e57b9f49fda47ce73702df01a5c4 ./platform/neuroforge/internal/store/checkpoint_order_test.go +8e7a029a904ca1c22821c5e4871c72958f0d2645786871d75f3e00c186b00778 ./platform/neuroforge/internal/store/cluster.go +6637e541f61a58f624baca31d972095ea01a95491ddb30492d9ac055740213c5 ./platform/neuroforge/internal/store/diskann.go +1858ed8045b16282de815a6668ff6027c7a3a3795e38659c753c504d0f2a8383 ./platform/neuroforge/internal/store/diskann_test.go +366e3b2f29b4abb1997aceb9c2b934ebe8f4ee44605b54167c70b8ddad683c7c ./platform/neuroforge/internal/store/goal_duplicate_test.go +38f24189c76652f12da9eef7a617455dc923499e2f21cd3c29d94231dd0d7460 ./platform/neuroforge/internal/store/graph_index.go +bda98567153542e9119757ce5f8f6014334ac9f80ad79a26ff83fdfaccc1ca8e ./platform/neuroforge/internal/store/graph_multihop_test.go +95640acc25c6d815f47a9fb390b71e58205d3d6118d73258031faad778418490 ./platform/neuroforge/internal/store/index_segments.go +478efaeb260cb5e970af8b3bb99f232ef846f54c6e4ff9deb5499df6e008e1c1 ./platform/neuroforge/internal/store/index_segments_test.go +eef4aad176d7f6e740dff4ffd81f3dbacbcbe6646e724331ae010ab50db04b30 ./platform/neuroforge/internal/store/knowledge.go +e9aaedbf4d5e9a43f4a4a5dbe2e9e0f0257b952fb59e82bf729aa5e761955c1e ./platform/neuroforge/internal/store/knowledge_test.go +43de0f541fb47633b02b25686544d44887c17b47e8d01cb23f8e57567cebdcd7 ./platform/neuroforge/internal/store/mmap_linux.go +9ce033d691157037fc6df719b5ecd8fb29d6d881d1862e2d50dc0c6c573b5f66 ./platform/neuroforge/internal/store/mmap_linux_test.go +ef6135a9039fb45361e57107d3968272b4b365ac833e9e2e58b0875566bf5a07 ./platform/neuroforge/internal/store/mmap_other.go +fe98dca9f919cb52140a916de08e76b88bd0d08b6b83fc8b4e5220411998b0fc ./platform/neuroforge/internal/store/observability.go +fcd87a843b6dfd3520185bdacf9d732303ff39befa31b513225618560eea1567 ./platform/neuroforge/internal/store/orchestrator.go +13ae3b3d7b797cd4d6d6775cda15975428ff4cec96738ae53594763ddd6a9ef0 ./platform/neuroforge/internal/store/orchestrator_test.go +66d612efe2462d76d79cc51b8b6835b5990d4a79a2dc036c26b040554357ae29 ./platform/neuroforge/internal/store/pagecache.go +5df97ac71adcea7627756a52d295d682900d3b812a977dbe9a4911e4c4118809 ./platform/neuroforge/internal/store/raftlog.go +fd6769bdcf1f7d22ced9ea426fd4dbc52b50b5483b4be42ac3b6adf637c926ab ./platform/neuroforge/internal/store/raftstate.go +fa892de65ab1c6ef8f4f811784cc3852854bf7e7de7e0f9c63a34363a6e8f28f ./platform/neuroforge/internal/store/recovery_hardening_test.go +f811ffd393ec7e72a3dd704eeebef7da1a78446169f451a44769bb586d7f0049 ./platform/neuroforge/internal/store/research_runs.go +c5c976d068317c688611bce153453133ebd7cf19b835955c77115b450793fe9b ./platform/neuroforge/internal/store/segment.go +ea968456538367ed536c212707209a173098ec65823152a0813422bc8071c952 ./platform/neuroforge/internal/store/segment_test.go +1d8071889b7a2be9faf029a1dd2981c69a9914798869bfe217f6db105340323e ./platform/neuroforge/internal/store/source_index.go +4d41586c15bfdd13139e19bfa102613f18ddc6120e4214a1f45dd9e2eaa195af ./platform/neuroforge/internal/store/sources.go +0ff464ef7fab01324d77ee4e406a8c15451b3333b75d4afa444be3c3c94f016b ./platform/neuroforge/internal/store/sources_test.go +61cd82b106e74f2aa0a7591d757a0872491a635dc1fccc78c66d41c052d0c9d2 ./platform/neuroforge/internal/store/sqar_vector.go +7dae4f397a98afb0fca66e59420a7799e69e8b704818d79b281dbca999ea8070 ./platform/neuroforge/internal/store/startup_compact.go +4ec1d8893f80f52980ff045a3a142dc9f161b3e061318755d2179950b7071956 ./platform/neuroforge/internal/store/startup_compact_test.go +b631e8b2fb6b5576653ea8a54672638e4e05e4ad509ea853f90b25ccec4ac5a4 ./platform/neuroforge/internal/store/store.go +f86b648e8b4845ab83e672ae3fcfb0bcb0dc1bf891abf4bd30d380304f0b259a ./platform/neuroforge/internal/store/tiering.go +2138e467981a3c814d1e88842dfbd470b76333898614543cafb4f93ea8b9beaa ./platform/neuroforge/internal/store/v3.go +091e4c9cacfc76786a76f6afc4eb2b697e5a641e611475b6dab97e99371360ea ./platform/neuroforge/internal/store/v3_test.go +7f0bca5419a895258b7921e940c3fd69cce04dedcb0b9d8f1127cd24b9c9e201 ./platform/neuroforge/internal/store/v5_test.go +b3a0115907719a8a7aa9e6b244eec55be71a2535ba20db65907987d5fb78dc77 ./platform/neuroforge/internal/store/vector_journal.go +bcc12eb91682f3c0a626f5baeea64cb3809e56f087b9f1178a6c2546f5e301b3 ./platform/neuroforge/internal/store/vector_journal_test.go +158125b5585b59fc1d2af7ab92c9c86ecffa2a7e3cd5147a19840ac103d348a4 ./platform/neuroforge/internal/store/wal.go +b369c68fbfb7bde75ba37c40948651789fe486f7c651ac245c8fdf8573c7ecf5 ./platform/neuroforge/internal/vector/hnsw.go +09ac0fa04dff1446893e846a95dc1131d6d1a7a54b77b48cdab8e46fc6124b7b ./platform/neuroforge/internal/vector/hnsw_delta_test.go +4bfd79da525c6d7901db01bc09c823608b641970e40aff9fdaac33da15d4432f ./platform/neuroforge/internal/vector/hnsw_test.go +6eac7fabc0f8a3cab53c758502895576fae158e3d672f8c87ad58588523919c2 ./platform/neuroforge/internal/vector/pq.go +275b0dd3b8cc3589541e3cfb52a2f1a372e40446e2e0c9c902388fa832e4f0dd ./platform/neuroforge/internal/vector/pq_test.go +c854f70f4141344dc2c6fbec4419afe5dfe25fd1bc2da11e41781730a21c8834 ./platform/neuroforge/internal/vector/recall_test.go +32f55e82419e4b043d15682c82ade1c4bc9694bfa372f925c131c2611f48d744 ./platform/neuroforge/internal/vector/snapshot_test.go +c5463c525f5ea703ea1f6df74cabc30f937f3846b1f75f237adeee2700baa977 ./platform/neuroforge/internal/vector/vector.go +276cb34ddd9f87abdac17be110e2bf15135ab83ba66bf91663d59d61fbd81125 ./platform/neuroforge/neuroforge-v0.7.3-rtx4090-example.json +6088a896352046ce9e04b1a3f8ea30aaa98e5681a4d393fa5b6bfd5288a3ee2e ./platform/neuroforge/openapi.yaml +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./runtime/agent-data/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./runtime/backups/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./runtime/knowledge/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./runtime/ollama/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./runtime/staging/.gitkeep +02ebd40234435473fa5e4bdb03f035f22564049a6f43243d7f4e175c7239ab78 ./scripts/backup-data.sh +df684b1b487762248516aebb82742e5777395b504cbde42ebda85b999ae353be ./scripts/check-compose-env.py +6f1d21b7aea3265a088256801aa37a00931157523d8bd834aa4f473e99511f04 ./scripts/codebase-memory-ui.sh +bfc2c31612e57904efefd44dd6fd2f66acdca03e7228b4a31da2a7fb2a5b28a3 ./scripts/create-network.sh +5abbc6b60bca94fcb880abebae19c85c6a220c9eabb9865928b23ae86246705b ./scripts/export-obsidian.sh +01611fcaef13df301147ad539f38e96e09d32c8f0f17c0fc9a7e3dae38de6292 ./scripts/generate-secrets.sh +8b0a4be74d87cf42c98360819c6f797b9caaf10528e729159412daaaffba0771 ./scripts/go-live.sh +3df6e58552fbe5e29f77a66c872339bb39a13329e8d63e34b3b2afb41585e84a ./scripts/health.sh +02cd54884b3ee16095a7c111579361b9af5920ec55349627423db96dadc63854 ./scripts/preflight-role.sh +0d28cf37186f9faeff2a40e506e3a3e5b8fe3a399c9c426e369bd26c9021f7b3 ./scripts/preflight.sh +fe35fa5db6dabf06e6266e0de4c372604160180366d4c0a8042c7ee54b7c6dcd ./scripts/propose-draft.sh +1744be0629e845c8d94dc2a06e91ca2cc9ce5928db00300b7fb894445aeda9ff ./scripts/quality-replay.py +8b745865c41ae3fcc89280c861f78918ee947c1fae4973ca95d00a507fc3afcb ./scripts/release-gate.sh +76358a00fbc9698879d363dfca6624a13e7633181cec2ce05368cb01811b2580 ./scripts/research-up.sh +697891580c9713e6451c1e0f10509b009cfa3d3c5af4a1347f841e6f05339cea ./scripts/restore-data.sh +989d0041eeedc4df8d98b92c5bf13ac72be7aa907285d45d9dd69fa03ed17aaa ./scripts/secret-scan.sh +a2c56eb668137e058cb78bb592ac359ef7e608cb9bedc7eaa930b3f4f66411ca ./scripts/status.sh +2970d5f5049323b14a3f3983c2f4eb5392d745b4fea34742e3cf56e52d740f17 ./scripts/validate.sh +cc0fc4433db0964ed172a82f2910e55c0f3dd6d59b1ec04fe0541b8fe5a912bb ./scripts/verify-package.sh +17d7149607bf40ccc6f64a3c4cf3e4023a8325675918c18df0be309ea9731a36 ./services/agent/.dockerignore +68f757ffe33c55b622f0f2f3d907530419ac649e8510c47eaa8a05e08788e602 ./services/agent/.env.example +236713daf159ff0a8067e80a442ae3404fa28a5251ae6f24782f263bcfc17005 ./services/agent/.gitea/workflows/registry.yml +9ee46f2156ee805c4ff40e418b004ead5cfbc3a9b6df3968a1b772fe1b313cdc ./services/agent/.gitignore +4c893d6049499eb70ca755a333e97f0a726dd212e4ded4a9d4acabf0a25d9bd8 ./services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT.md +9339ce57dfc58d4731ea02c8d0ab9f2835e5966a29c071062fc6a2effd17da2c ./services/agent/BETRIEBSANLEITUNG_GLPI_AI_AGENT_OLLAMA_POOL.md +81c91a44c5d3b7eca3051d8cee35f231ef283563da7a636c56c98b61da60218b ./services/agent/Dockerfile +2ec93f81187ebf92665a6a15a6672b2f7dfce23bfea1546f2d4188f49de5dce0 ./services/agent/EMERGENCY-HOTFIX-TICKETVERARBEITUNG.md +2bce6e10780004bc12042a416b8a05b417130db2d730d01a95d9d86a564e01cd ./services/agent/ESCALATION.md +bc344ff4486bd38356fab0d2da46590d9f738ece0f712d043597012f215bae49 ./services/agent/HOTFIX-GLPI-KB-AUTO-REPLY.md +3f51b802804e9004d17cb74a7e0b8fc668537e75bc95e7344923e77d5f05f13b ./services/agent/HOTFIX-GLPI-KB-SIMPLE-AUTO-REPLY.md +65408599c64a4364f41fe0876f6531b861c352eaee4afabfeae52a5a72f28f6b ./services/agent/HOTFIX-GLPI-KB-UNCATEGORIZED-AUTO-REPLY.md +dacf2060df228b5c137fb5ac5c5f7377226f937aadaae4b01b662fc54abea11e ./services/agent/HOTFIX-POLL-DIAGNOSE.md +6b2188c579db7c47cf7be54328f2671a8fe7aa17f21556483f751fe2457db1c5 ./services/agent/HOTFIX-PRIORITAET.md +30a29a59cf3c409bf33e3adc96789b9281a19fa86f961c727dcdfab07ff6a327 ./services/agent/HOTFIX-TRIAGE-KONSISTENZ.md +c0e13c6703cf3feda57a9eaaa718e501280fbb526f466040eb685a27700b5797 ./services/agent/IMPLEMENTATION.md +1126322e2cc8d165adc4c792eeb195717de2bcc7b39be1ce77959d78e87ef685 ./services/agent/LICENSE +0ed7ee0e4846be09994dd5f3b49ffdca6b59177b9018b1947388949f0dd49a14 ./services/agent/Makefile +80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 ./services/agent/OLLAMA-POOL.md +80b20d2c2c78badc0dcc47e485e46bfd647877445c35faf261fb677ad58e8359 ./services/agent/OLLAMA_POOL_BETRIEB.md +a7a3f969150005ef062e7a77cf852371931bc2fa5ca4be01cb0e9eb07d35335c ./services/agent/README.md +aeac85d1adc92a8b34ffa5a95f7f19c2c6fb94742a7359a7fd452529d59ad87b ./services/agent/SECURITY.md +edb92cdf2d1863df53ea8045657f64e415f206d503c8da3b45ea96b7f7f0960d ./services/agent/UPGRADE.md +a499188fd29230a52949f2d5252d64d859b727e9d94a00889a025f30a1fa8981 ./services/agent/cmd/agent/main.go +98be0fd63dd4fde52342c5385da113c926ac29195aedb657c0ca0f98e1593fff ./services/agent/compose_local.yml +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/agent/data/.gitkeep +b74f901cca99eae4bed1b168f1f077a28bf1134a71c867d263c7834f6d896684 ./services/agent/deploy/glpi-ai-agent.service +1457cb04cfd6bd05d45f7820f778c681c1a63cd2f8e7e500954c1b74c94a1b20 ./services/agent/docker-compose.registry.yml +cfc570de023b80f9a775d82a8ad5398521a4f377a85bb86642e60d0f22333d94 ./services/agent/docker-compose.yml +a0105475dc054977223fac36618b8cd8137c55be1d11fddcd24e9a4d3074c170 ./services/agent/glpi-ai-agent-neural-brain.patch +8a7805be0da45875d45a5e5b26780fe659f671edd2d6b72c0d7da9606bf15755 ./services/agent/go.mod +e34ac0033c53effcf654689641a15a92deae1a987e3d92e9921b7b030efab3d0 ./services/agent/internal/agent/agent.go +789e55accdbfbb99ef8179f5680aa340e67a055c71686533302558389dabfb8a ./services/agent/internal/agent/agent_test.go +a8eedc80f57b174fbad3850d787346f7b370236b4c2eef67905ef6b60a1ced64 ./services/agent/internal/agent/analysis_runs.go +46a8dfe5e80569aac48f9a098acb2c1089ea4ab9521ed6c92f53b020e0c76ccd ./services/agent/internal/agent/analysis_runs_test.go +05ce267b4827cc5b89332b72fa6ecbcd80bd3796b5143bc40398b932f5f45867 ./services/agent/internal/agent/candidates_test.go +76fd850dc9bedf73db32e9b2d49edfb04624709507e0b67fea1f88730e87720c ./services/agent/internal/agent/category_mapping_audit_test.go +d8aab6fa743e6ed8246ac6e20f993fc3642c635a1cb349c4fb6579b863a2bd63 ./services/agent/internal/agent/escalation.go +c4bf8fcb4ba6eb7962ec55cacff806a8a99561d134ab5238abd012763a2f9c74 ./services/agent/internal/agent/escalation_actions.go +471cf47af1220f50fce99ea41c59adf257cd29417ce23fafb4728f0037d33842 ./services/agent/internal/agent/escalation_actions_test.go +7a65f676bacd5a7b879862e4c1a157632f541a570a71bd8b57864e603bb4cd60 ./services/agent/internal/agent/outcome_learning_test.go +5175e4fa9c394c0a8df94c29c69c5f896b3227a7d8de67d214aea4634ba0fad8 ./services/agent/internal/agent/policy.go +5434e1c4dc61eee33d6dceaff69156a5b9ab4a9fbb555cd056c8cf0444601606 ./services/agent/internal/agent/policy_test.go +6b70a29176602e499935a85ac8831dbe36bf75fddb130a56cda376e124ecb7db ./services/agent/internal/agent/status_reply.go +6f95bd3c85b42f1b2b098f05828c0b9e3dbb2e57f1f89b3dc4d1806453e0c4ce ./services/agent/internal/agent/status_reply_test.go +9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 ./services/agent/internal/brainactivity/client.go +c8da5784acc4f28d632f357dadbfc61209a37b698525dae40473d27625d9c8e2 ./services/agent/internal/config/config.go +fa1717032ad536bff867a07d3fdf0ae2e0a9d5d7637a27b43274832d51322eb6 ./services/agent/internal/config/config_test.go +6fe7335b0e66b60d5f6f27583b1ac11a0948719cfa657d51e77de0b337aab5d5 ./services/agent/internal/contextdata/collector.go +12e9eb6fd567b5d4173a63fa2b89be913b6d1c97e17b69048f46d119e88dce26 ./services/agent/internal/contextdata/collector_test.go +5eee0e130a9cb8452a07e9f5d4c76048ad2eb62aa411ea61c9dc6066fadff934 ./services/agent/internal/glpi/client.go +6499b2e19547d7d874dbaa64f0c905643ffe4d822329c6cd710df0a30a3a9755 ./services/agent/internal/glpi/client_test.go +247324ba41f3cd991a3582ed270d0b0d6c4c92c36f11e7742c20d3e59b982928 ./services/agent/internal/glpikb/sync.go +9836e2bf9153df638ead57d92c152b87f831e5a3696bcda4b1f650ff38e9ac70 ./services/agent/internal/glpikb/sync_test.go +787ab6b53623aa36ef6628f4eada11dcb4ed3011b34effd691bf07ba5d8f214d ./services/agent/internal/knowledge/category_mapping.go +d3a9909672ea420db87be29ce669f3cd973125ff15bb46ca90a54c3aa98d9e73 ./services/agent/internal/knowledge/neuroforge_backend.go +7bcd33bfcfb99fb06762fe75f4646308428957ed7eca26a266891ab580c3a929 ./services/agent/internal/knowledge/persistent_index.go +df1ac214ebb88bf3ce7f68112129cf9ffc300dd725380c38661b85b04908ecdf ./services/agent/internal/knowledge/store.go +63e47d86bcc89178fdc0f91e07ecb30bd01ee4b84bf29bfc660debbf26f8a40b ./services/agent/internal/knowledge/store_test.go +807996228b38e1cd97ec2516cb1ecfc4bd945b638fcde465a0991051df2f1a00 ./services/agent/internal/learning/outcomes.go +2917b3dab63783af4c92c48f269f097dc94b989008a5451f46d6c1b309ccdc9b ./services/agent/internal/learning/outcomes_test.go +cd8c7379d6ca50fed77a7eafb6ab34b12b9ce2fe56025da534f54e6db246430d ./services/agent/internal/learning/store.go +c540f2e554d66f36d79d2ce32c74a18b27b2940e96a40e89eae10abccd0bd2a5 ./services/agent/internal/learning/store_test.go +5030ec3f51830f7decfb24c27f9b243cb64f97302a6a51df096bc957f24b3fae ./services/agent/internal/metrics/metrics.go +809c3f2833ed2093bf1f9e4466eb3d855f2a84d827cb21deb698dd0e7b63b403 ./services/agent/internal/model/model.go +975d5846fa38bf2478fa3ab9696660740c8d9a0549ce8d4f3443d4ca917c4aba ./services/agent/internal/model/reason_codes.go +821850d649c7661f1665dc6be030ebeeacb60e4e007e1e84891d4b779b9a5d0d ./services/agent/internal/model/reason_codes_test.go +c89248224adf72720289215684b916eb209387c1d38b757d64804a8d3148011c ./services/agent/internal/obsidian/export.go +b75ffec53e9a94505ee57ecb9361bffbb6805cfd06ea05a7bf68b6b28b268c09 ./services/agent/internal/obsidian/export_test.go +f4e32c5653b5fc3e046f1536513d376a7c0ac11b392210a107e12bd0ee63e179 ./services/agent/internal/ollama/client.go +3522e7687e38db771dcf7e05b8dc52ad99a2fbc53d98dbdf07ded781d68b248e ./services/agent/internal/ollama/client_test.go +0e4e98a3db5ad781a4f7022bd74bbc4b434e64187afee077daf9174165509ea6 ./services/agent/internal/ollama/pool.go +4da6a7f953bfce654dbd8ab2267d1d519057fb230773e37538ce81aa049f9751 ./services/agent/internal/ollama/pool_test.go +ab29c5808a06f6b21a0776355eeaa030f69be5eee251e7b1dbd7e00c71045c61 ./services/agent/internal/prioritysignals/signals.go +149bfefd67291ee288beed89a27235f58d3c33d87a1eff98cb2b7cc312fbd6b2 ./services/agent/internal/prioritysignals/signals_test.go +93f31d9e7062a03990f96bb950cae54a3fc24176a723a2f634312653a0692434 ./services/agent/internal/queue/queue.go +147146bf5fa222d54812d84ff799555fd4c2d57ccbf8337e960708524f0ecd86 ./services/agent/internal/queue/queue_test.go +171f346e674ac5ebfa562a55e47060a462a0460db86e741085e2ee85533d3193 ./services/agent/internal/state/store.go +592b00ad638b2e523d5e24d0d73286e547ee09c5027da4f6c13fe3885daed020 ./services/agent/internal/state/store_test.go +209c5ce33b17403289c897932ecfa14b1e8f036f89746715d41cca3c607cf405 ./services/agent/internal/uptimekuma/client.go +c849df7e346e1bd1e122037a4eafc30fe461add9b1876d7b9d63a02e33d831ad ./services/agent/internal/uptimekuma/client_test.go +5f0375f33c4c3f0804e58bb7b3a8699a450a05100274fbb19762d59f4d5d0034 ./services/agent/internal/web/control_graph.go +cb68a23bef549186d251766a3d3820b611defd55e8f7386b8b87490a78b9e11d ./services/agent/internal/web/control_graph_test.go +b4f7d72022d5009fce43b5879483de92dacfee2b3bf527306902187c3d26486b ./services/agent/internal/web/server.go +1f517bcb30f95831a06dc2fe0fd9cd633a0c42fc795f67e5ce53de7a2eebd2b1 ./services/agent/internal/web/server_test.go +21b32b0b863ed62ae352011d7dd85f0134bf9909c23ee80b1c7e3c3605ef8cc4 ./services/agent/internal/web/templates/category-mappings.html +d0561b87acd8fe8c0ab7f1f0bb82e1df840db8342f91798e8cb0a0408064abcc ./services/agent/internal/web/templates/dashboard.html +fb5a4e2e1846046032bb878f1e878eb792497e321df6a8ce16209c07a925fdcd ./services/agent/internal/web/templates/diagnostics.html +7418ad39aa0899a85e69a9910d99ff1cb01757d5ae2edaa885c5ff7a815cb580 ./services/agent/knowledge-category-map.example.json +5a81ef73b2de4ff60c440a7a5e42da68e8ca30fab7bc62de63757157d498d72a ./services/agent/knowledge/01_active-directory.json +70b176be0117d51a35a699ab50c2ad9dc944dec807e4a193e1adbbf0ece888fc ./services/agent/knowledge/01_arbeitsplatzdrucker.json +96cf6b96e462db5e4be8345e9b18afee4e7dd5d51bcbd59e6817266725b92318 ./services/agent/knowledge/01_fachanwendung-storung.json +521a4e7d6b1362bacc8d3b8dafe73d081fad9ed06825376b57460d163db31aa6 ./services/agent/knowledge/01_festnetztelefon.json +88610d1d8b75017251e040f7627a141865b7f45a874073355bb6b30ba68c63a8 ./services/agent/knowledge/01_hardwarebeschaffung.json +4e2310d9d3fc5dba392f1869887a7ab61ce4af69822dffae5892a3797252b39d ./services/agent/knowledge/01_kennwort-zurucksetzen.json +5eb15ac8d8c6525d6eb21c29189d1561b8b6e23d81c096d012fe5f9015534c53 ./services/agent/knowledge/01_lan.json +95a46a2f250c94a380be3c259f98e83a9042201d9e3fcf2f310f7e87a293af14 ./services/agent/knowledge/01_neue-it-anforderung.json +ee98f46f39bbd60cd4fdd2b97ca6037a89719fd7a6936ebffb356b633ef2d1a6 ./services/agent/knowledge/01_padagogisches-netzwerk.json +f64d5d7bbbc67d3f958f59c94ff9a99f48801f4d31a041ed12f503c923f4a7a6 ./services/agent/knowledge/01_pc-und-notebook.json +ae9d1634baaa057f8b043558874a6d91b49d06edf8a101d0bb94096e51b062bc ./services/agent/knowledge/01_sonstiges-und-unklare-zuordnung.json +5116a7096a289685d4a320a72a99beeaa6dffa0bd640013061a9a973446d8ea8 ./services/agent/knowledge/01_verdachtige-e-mail-und-phishing.json +5b414f4fccc87f0a9abd3bc4e66a2deeadf0dad87752de19496ed9df217364bb ./services/agent/knowledge/02_benutzerkonto-anlegen-andern-oder-loschen.json +ff7275f53dee116e4194c08b482ff014e86ed31f3d127aed8e2b8dd67a1eed68 ./services/agent/knowledge/02_fachanwendung-bedienung-und-beratung.json +0377ef73fe676606dfb70605a4547b7484c3fa7109474955db6566c7c1812212 ./services/agent/knowledge/02_gruppenrichtlinien.json +4f3fa6ec2a9e15ef5b002bde7b1caa9f6a0352fdd15bfe6638a14ab7f4ea8220 ./services/agent/knowledge/02_monitor-und-dockingstation.json +bd4de92a6dcb5cb79fc1c6eb5d47f54b4585da5b586fecb74dbe50d52d8f7fe7 ./services/agent/knowledge/02_netzwerkdrucker.json +7b31d78a0019c00e360012ba1ba7257f18862297f2fdfeb173dc86154ff16191 ./services/agent/knowledge/02_rufnummer-und-nebenstelle.json +efc4eb64f60404796106fa20770b1b426698c602f2665cb2c458d30fab3eb206 ./services/agent/knowledge/02_schadsoftware-und-virenfund.json +2b1b802004ce185a6d4222848f84fe13bbe993946946e1c9119e5fa590f5ac99 ./services/agent/knowledge/02_schulverwaltungsnetz.json +830bf169fab29b06ff5f6d208c3df4cd0431fe49b0ad6a7e6fa574ac71cc2555 ./services/agent/knowledge/02_softwarebeschaffung.json +fb45cf8cacb1e9c9a8dc9f61cde9d399c607129f9bb1171b214b829c0cced2d5 ./services/agent/knowledge/02_wlan.json +26a059eac1654315c9efe982d3e441d3d667558be5d3d6643ea17efe756f96a9 ./services/agent/knowledge/03_ad-gruppen-und-gruppenrichtlinien.json +b0348c630524118be4e6efcfac879f0d7ec9494e0e85651088200012ad10669a ./services/agent/knowledge/03_dateiablagen-und-netzlaufwerke.json +0d3fee8511a1958a5be1a0c1fdbb7e13821ac8ec6fa842e75931a426186b6d9c ./services/agent/knowledge/03_digitale-tafeln-und-prasentationstechnik.json +a57924a695e382796ef15960e371cee6ebc61bf282402f69e07fd59072aa5ce4 ./services/agent/knowledge/03_fachanwendung-berechtigung.json +f75f00b6f15ce8a6211b953efb224540f6d8bac2da19570fb27c0e72d6ed24d0 ./services/agent/knowledge/03_internetzugang.json +5a76dfb0abd1f3927b070ed5e3a035d6c7c95ffe7a63b1c9a4dbf7c22aef33b6 ./services/agent/knowledge/03_lizenzbestellung.json +d4b595bb8069dde2ca4f241180178a82253a8beaa958811c18f3ae94c26ab02b ./services/agent/knowledge/03_multifunktionsgerat-und-kopierer.json +c190b86667330c1d2926cdb98ee9bf437efcce6dcbbcb63d3987230a3e3c2f54 ./services/agent/knowledge/03_rufgruppe-und-weiterleitung.json +b0d830dcd42919754fb73db0b17b2b3211be6a21b01734daba571dafde9ed01f ./services/agent/knowledge/03_sicherheitsvorfall.json +ca1b3b608c0506457e607f09e4daf827df4ac340d528eb296190822ac846cf5b ./services/agent/knowledge/03_tastatur-maus-und-zubehor.json +7bf663850628f0046edde52a80c2e32362fa5060e7a541162a63e78b3385f0f6 ./services/agent/knowledge/04_anwendungsberechtigung.json +6d0205eff7ba230918fe72d19c833eee32de4de50d4e3657b1849cb576afdfa2 ./services/agent/knowledge/04_computerraume.json +36eacce8dbf33a7e061b3baea0d43a5f3468bef003dc18d8e972f8844ca04043 ./services/agent/knowledge/04_fachanwendung-konfiguration.json +a45d9b3c16c0342a9f7cb12cee286f7f34348929d54644aa82779084da41c3a2 ./services/agent/knowledge/04_lizenzverwaltung.json +e7d40406ed649ae4764e26f3dc14170d5b9910810f8f304025cb030424205aeb ./services/agent/knowledge/04_mobile-endgerate.json +adad80ff68baa3a998b80926e03956e8200d714a2aed9a1fe6aa72228c31faa8 ./services/agent/knowledge/04_mobilfunk.json +1cc331f2d03971b6cc92202b6fac1eccf031acc81d8f3c60549b36e1628fcefc ./services/agent/knowledge/04_scanner.json +658ea20f369985931ef590bbfacbe365da24ab06c7a7433bc546511306bde33c ./services/agent/knowledge/04_schwachstelle.json +5feb14bdd59e0237832561b2d542afcdb877637bdfe6c9c2063e367fd0460025 ./services/agent/knowledge/04_serverbetrieb.json +ab7d1a11ab1e66fa6d26eb1c8783ad11a319ce77f99be48ebc71d39f3b017c3d ./services/agent/knowledge/04_standortanbindung.json +9c97b4ae44a0f0d14292a7a0bf882f2da62dcce0535f37ae36173acd31fda937 ./services/agent/knowledge/04_standorteroffnung-und-umzug.json +8dafc6d816dc06e9e384fca9d504021c2a3ab3b25f67f3556b93f98be955e5ed ./services/agent/knowledge/05_dienstliches-smartphone.json +47d689c3c7f3d5be8de0ee31e781664ed9e230dd6f9d10a287d3f5d462981f43 ./services/agent/knowledge/05_digitalisierungsvorhaben.json +4edd62233b8784ffc0e7271300d7ce96709bfa8bb73f41f945d6420ec989d838 ./services/agent/knowledge/05_druckertreiber.json +74bbb7b99036c7f9311fe912f73aea78d426fefc0ce37d841d907c0dd731e8b0 ./services/agent/knowledge/05_fachanwendung-schnittstelle-und-datenaustausch.json +a9a244145670b544f578a49c72f5ccc10bfcdf11b67080659f3c305a2d48d2b5 ./services/agent/knowledge/05_hypervisor.json +0bdd46fe6c5f13d562299b07ff9f2e18ba3a6a90f8d124c0934197245c59ba3e ./services/agent/knowledge/05_remotezugriff-und-vpn.json +9c124626a953b7c9812b576f9f22f5fd075a7b9ce591bd20b06b717f29ab87d7 ./services/agent/knowledge/05_schuler-und-lehrkraftekonten.json +2dcae54e57b9acd92c681be31a3a0f2386844322c4bdb1d5d01f85216ef6b703 ./services/agent/knowledge/05_softwareinstallation-am-arbeitsplatz.json +9178eb0fc81af7823503f87f6b63d9c57bef29490b264895f6ae2cb20cf8e075 ./services/agent/knowledge/05_telekommunikationsvertrag.json +fbc426c129b187cd0943f4924b08d127a9f8c2ef0977a1c05062d2b912f90e33 ./services/agent/knowledge/05_virenschutz.json +e9833109731fab66468e6fa1b5c22ded747b477edc0e852336691ba60cecc560 ./services/agent/knowledge/05_vpn.json +153150769b692cff7cf506978ff47aa929c01423fcf10abaaac48a22f8ae0793 ./services/agent/knowledge/06_betriebssystem-am-arbeitsplatz.json +d33e8db3bb4f32e30ede4d4cd604b90159bc9dbb66fe1f9baea441dda3168ce9 ./services/agent/knowledge/06_dns-und-dhcp.json +b3e9b7646617aedf3beb955dc94aaa99ce931bdf4a8749e118c2d5eb78dd45c9 ./services/agent/knowledge/06_fachanwendung-bericht-und-auswertung.json +77f0f12f2f216cb52c5d6d394eba485e690fd0e2c74bff8f38213b6310779108 ./services/agent/knowledge/06_mobile-schulgerate.json +e96d1285c9be2cd4a151fcbdb4745181fc2ab20e42dff97f54fc4e9ff9857a53 ./services/agent/knowledge/06_scan-to-mail-und-scan-to-folder.json +cadd71e26b643ce1fd7276fb72ef9d2e43e8ba57ddf01d44337fcefb79c9800a ./services/agent/knowledge/06_sicherheitsupdate.json +ef09af49f08090c9a82bbd9e94c735426a371f31002cb1d461e1f7f94da2c21b ./services/agent/knowledge/06_videokonferenz.json +e496efbe113881d38d97c20c199206e52ff9b936d7cdacfc186c585bb9aaa68c ./services/agent/knowledge/06_virtuelle-maschinen.json +71802acd7b2287d3edb968636a05b2651f6386faca3a820e47ac4476244e4874 ./services/agent/knowledge/06_wartungs-und-supportvertrag.json +ad1cd6d0728da647a4e819b4cd74b5a2302398a3a7becaed147e9e8942080c30 ./services/agent/knowledge/07_angebot-und-wirtschaftlichkeitsprufung.json +2924cfc43a83c7fb4856096f5005ba8bdbdf2179e3e94b0fa81fc36a09dcc06a ./services/agent/knowledge/07_datensicherung.json +46883e5655761c31bbe1a55a9e5d9cd3929b660ed44ae827ea54ef5c1f761e4e ./services/agent/knowledge/07_fachanwendung-update-und-release.json +8001bb3da012ad43f87f8606610e8144999b740ac247050a7f100c2ed17aa2a4 ./services/agent/knowledge/07_firewall-freischaltung.json +03d25806ed7a3098d4dd004e05d3fbcd81c338de7a7ff4a3d888705a96128541 ./services/agent/knowledge/07_funktions-und-sammelpostfacher.json +a87af380b0d3e3afe7e1c63b4a164ca6565259c7b5936c4227b11e7b7ef7cf8f ./services/agent/knowledge/07_geratewechsel-und-umzug.json +9b5723928527fd21fba2be5f5fa51cba6722563465f74e498478b1b2e334c955 ./services/agent/knowledge/07_mobile-device-management-fur-schulen.json +48f71d04c5e20d9c2aa2976569ac9912da2fb4af3f949fbc896915d2de701c24 ./services/agent/knowledge/07_projektunterstutzung.json +ae521cc69bd2a957189486a85bc4a9ad16bf960d5d91ca3e40757ba6b1304515 ./services/agent/knowledge/07_storage.json +2058ccf0315975a480de2b1dcdb4a480445144abe330855ba04a8a9122849ce2 ./services/agent/knowledge/07_telefonkonferenz-und-softphone.json +748b64297c5c59f20a78f0405bf798b1462934a1f05a411634133225c1c56b3c ./services/agent/knowledge/07_verbrauchsmaterial.json +7fe889fcff1c0745f1e6dc1f01f84a7580eb8b75a667ed0c60e0c529f24a0b6b ./services/agent/knowledge/08_datenbanken-plattformbetrieb.json +59e1e03acd3b87b41132a047acf14afb190e5752066c6a3c2f39831d132ca30c ./services/agent/knowledge/08_datenwiederherstellung.json +7e9fa4d0803f8c7075cfeb6db447b63bf99119dae2c2bb232bc82037c5af6943 ./services/agent/knowledge/08_fachanwendung-neue-anforderung.json +f17afbc9c495993c1dba9ef99b70ef448c9b863202e5875772d281d7624358ab ./services/agent/knowledge/08_hardware-neubeschaffung.json +b2a11fdacec61937d4d5efa717c31b71b991f46e935341de2e6317292253b5b8 ./services/agent/knowledge/08_netzwerksegment-und-vlan.json +1aebfa9a90184e3006709550e55bf538bc7d1a5fcd3bf7abb3902878db8ea13f ./services/agent/knowledge/08_neues-drucksystem.json +b1393289055a6afa0f08ac1705fae9b12a1f74eb6d91d19d6c35045575a2df72 ./services/agent/knowledge/08_padagogische-lernplattformen.json +ee49a806543115536081ae2cda509400e095253f9f4145b2e4f7bb58984127c8 ./services/agent/knowledge/08_rechnung-und-kostenstelle.json +3e0ea96e6eb9d1c78e74c34fcb07fd10147a00cdbd099cc2b6fbf2f7d0bc0967 ./services/agent/knowledge/08_telekommunikationsbeschaffung.json +0fd6d1281879b334b2f5f2afc8b40bb4f2ce2e3484f92362ad682a9b522f19c4 ./services/agent/knowledge/09_container-und-docker.json +4c50cad1c6116b42b1149aaeb24a117eb97e6a0ab7df2590dfe1ccb797b567a8 ./services/agent/knowledge/09_fachanwendung-einfuhrung-einer-anwendung.json +91b7be1bc88abd9631802a12eca2cb08a4bfb6f5890929512253deae443f81c7 ./services/agent/knowledge/09_inventarisierung.json +d4df65a13c1554e630351ebd8b4c0f6a088893c20fd894165010417ddef6d4fb ./services/agent/knowledge/09_protokollierung-und-auswertung.json +267d66313420fef443e9645b696e9fe6f7aba60af4e3b26f0f6b2df06f36c979 ./services/agent/knowledge/09_prufungs-und-klausursysteme.json +27e88b61055a44007777bd4318ad19b3e5b6f017974cb75fca968ef928206a8e ./services/agent/knowledge/09_ruckgabe-und-aussonderung.json +eb721fd2e91e5198cca4862f980c6692846405fe43107e49f2d85dfd080a8a52 ./services/agent/knowledge/10_haushalts-und-budgetplanung.json +523b27e6fdbae8de9c680fffb538e23082463028b5f5329a2cd7610358467f4a ./services/agent/knowledge/10_kubernetes.json +1e43fcd4bf900b2118d7977425fc7c55ac426eade471a8656841313e390eec62 ./services/agent/knowledge/10_microsoft-word.json +8b708ca4da157dd73a77ae051716efce330662fcff64a073fc625d68eb034935 ./services/agent/knowledge/10_schulverwaltungsanwendungen.json +813c045cacc6a0e3cdbceea69436d65cb8ac5b6009b060039488eefdfcc0007d ./services/agent/knowledge/11_devops-und-automatisierung.json +213d466c3e0a3f864268bc1b4ddcdc117f7b8dbcdf5e8a50032913de706a606a ./services/agent/knowledge/11_microsoft-excel.json +a615dc9c1ee5754c2ffa8eae113311cd857dd1422104eced8c41ffc42eb716de ./services/agent/knowledge/12_microsoft-powerpoint.json +e3a2d2154b2c9adde57874aa0c515b99c2c4fa8171da188162de421f309ab7b6 ./services/agent/knowledge/12_monitoring.json +2231a0a90369fc5208f721d91bbda702c9f58aea7f1bcccd0becf5f79a89fb8c ./services/agent/knowledge/13_outlook-client.json +cb3cde795233c0d4842dc7451c5855d1c071e459efbde3be0cabac32a837ba1b ./services/agent/knowledge/14_office-vorlagen.json +9b345483a41489babc35aca19a147c343a895eae40702bbd86075eb86538bb5d ./services/agent/knowledge/15_office-add-ins-und-makros.json +7f1d67faf4a6cea0c41c84d7b275d8a8979b19420b52db4925d8e2cd71ead3e0 ./services/agent/knowledge/16_office-aktivierung-und-lizenzierung.json +f1eab883370e0a40ef52a6b6d785a510d8ed48a19d25e0a2bc95f4f2cc8e329e ./services/agent/knowledge/17_serienbriefe-und-dokumentfunktionen.json +5a0b3d5d5bc712e30a67a4de3432f69070f7e43dd99931f675e72c213006b363 ./services/agent/knowledge/example-vpn.json +895f8400aaad550ff2b96262eaa54e954d603129682e97aa4964539eb19bd7c2 ./services/agent/run.ps1 +d1d4ab8145a7131c36df51d3a9fbdcdeb0cda3de134fe24c0e8d52370862f3b2 ./services/control/Dockerfile +f30ddf9251860d92717276483f7a2c2d7405f0516940ca62abc21d97ff3f3ad2 ./services/control/cmd/engineering-graph/main.go +10a365d00d56b812d3300fe08f33d1176d6819909235bc8e275606005e1b371f ./services/control/engineering-graph.json +ba44c599b9faf861eca647614c4abd18548e2708233cf462b11eb277e96449b8 ./services/control/go.mod +da37393e58ff53847f26b1051f6d3a5270571d290b33d79360bae0dcb3513829 ./services/control/graph.go +e6a8021a44219219fe37ab5edc45d1908fc6579f653aa80c9966810d891ad27d ./services/control/graph_test.go +d373caddd318992fe25be3879f3606b81bf393d55a2cb8457d3984b8af499950 ./services/control/index.html +a42b3daf9b816503801f666e322668b3ad106ff8c1e1b83855e801e7db4eabe5 ./services/control/main.go +f0ec671cd3f00083d9b9d69c82ffcaa8efe08122837671e812f2a510f3c4e321 ./services/control/security_test.go +457fa6b4c81a62197eb1e76a5472cd571d5c69659110fcd9bc016582f1b5dfa1 ./services/knowledge/.dockerignore +6d356215b598107a340a124f7ac8821ba1677f18a4da7425ad91b053ce1e76ad ./services/knowledge/.env.example +236713daf159ff0a8067e80a442ae3404fa28a5251ae6f24782f263bcfc17005 ./services/knowledge/.gitea/workflows/registry.yml +e45bab225ede9bacef0b53f84fb0577235d9a81967ae2a53ab7c7f4d66bd4ccd ./services/knowledge/.gitignore +d3becb2b7565ba0b1d32bd8d3b085dca6adec2d2b153583c8c6855e0eea43b66 ./services/knowledge/CHANGELOG.md +d3c5e5636f96c641b0181fd91bb2cf4802529a7355ba0065cea49356057e402f ./services/knowledge/Dockerfile +31f5067cf8e45271081ff08bf0d9afffef74d300628ddc1bd1db6e87e6d4e922 ./services/knowledge/Makefile +d50e9892824dc9e2f86b7f4fbfdc81165a8c6b403e4b2b9f6fd5bd2f22c295f0 ./services/knowledge/QUALITY_REPORT.txt +bcafd8739157e677416bd4e7c3693eafb1572d72384830ab839dd4dcef74d3ee ./services/knowledge/README.md +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/backups/.gitkeep +cbaed4e619df99e83eef694b12b660757b749bf64c0a340143bab523a121c7f6 ./services/knowledge/cmd/server/app.go +ba6569476482d21ee6eb4852d6f1a8dbe052335f3be4669ef96298d0f5a5028b ./services/knowledge/cmd/server/app_test.go +b2efd948315c373bec0610e9d31031f46169645b4b78df0747c6bf46444af042 ./services/knowledge/cmd/server/main.go +1867f5c83786628441a1c9e3bb96d90d496f5cab24b6dd62a80376f256bfa497 ./services/knowledge/cmd/server/viewer/app.js +9537e946b22208a5a78f11a29a92c8f379805f51f3de3d3c74a7cd068d876418 ./services/knowledge/cmd/server/viewer/index.html +58f16f955f1287bc077933980e50eaae238ab2eb40599242abd820a31aca79f5 ./services/knowledge/cmd/server/viewer/style.css +0269039e6baffbf8ecc423fe021e91827924a9c47decbd0816257aa052702303 ./services/knowledge/cmd/server/web/app.js +7b1cdf4c71867e0563eb0eca460c60be34ab839099793e0ad9faf4a84b29c5e7 ./services/knowledge/cmd/server/web/index.html +c6e48f5098243eee4de816d515cd71d8f8904deeedb335966b1db320d574e2e1 ./services/knowledge/cmd/server/web/style.css +8e056d0589099fd4aeeb920a4dc7116b54a3f73470e36441417f6f403cb60ee8 ./services/knowledge/docker-compose.dual.yml +1add2c016097a8a1cb2e49bb9ac38f39db68ec17c00ae6fccc1336f6cd9844e2 ./services/knowledge/docker-compose.yml +5a49eb8794bdb76d14c29060ed6c4a9f0126d4daa2f2ae45b11b8d1e7ccaba45 ./services/knowledge/go.mod +4f9b6060851d3c73b0fa1e91b164b7363938f358d378df1e8eb0841825300e71 ./services/knowledge/internal/aifallback/ollama.go +c2dd1e22001c23c9f558468b9d588644b20b32dd1420a16e2c1fe438fcb022fc ./services/knowledge/internal/aifallback/ollama_test.go +9057a742c3fe8e0fa76efd6dd9bcdf9fcdf7ab2c37ed823822808946e8649566 ./services/knowledge/internal/brainactivity/client.go +ff6d65a0a4648464a89c67e06f5c33a4ec87d7a41e84d894725e8ed582d64acb ./services/knowledge/internal/obsidian/export.go +e8e91a8ce16c1905c963ac57a156006e92323be80615637a7b1b477b89464012 ./services/knowledge/internal/obsidian/export_test.go +7fa9c38a719f2f46bd169438aec3c6e8595b047d63236d2a5e78db9cde6924f0 ./services/knowledge/internal/staging/staging.go +3c1f5726489a773536e430152b41944bd623ed0f88939688744611371462dd6c ./services/knowledge/internal/staging/staging_test.go +3bcb39124c72cb46efc7cf794254ab5821dbd7da57e7a20b1366862a71b874a9 ./services/knowledge/internal/store/store.go +c98bd048680978d06d34303db9729b130eb7cfc0750f15ee1a4e7abfe582dca1 ./services/knowledge/internal/store/store_test.go +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/knowledge/.gitkeep +81893d2cba93d7a30c5c728559e81a7b7e2eca87cae8f9e239aeefcaaf604d60 ./services/knowledge/run.ps1 +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./services/knowledge/staging/.gitkeep +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ./staging/.gitkeep diff --git a/README.md b/README.md index 5d1db75..1869f73 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,13 @@ -# GLPI NeuroForge Mega v1.6.2 +# GLPI NeuroForge Mega v1.6.1 -> Release: **v1.6.2** · kanonischer Vollrelease mit Full-Mega-, Distributed- und Standalone-Betriebsmodi. Die v1.6.1 Recovery/OOM-Härtung ist vollständig enthalten. - -v1.6.2 konsolidiert die zuvor getrennten Pakete wieder in **ein vollständiges Monorepo**: NeuroForge Master, CPU/GPU-Subagents, GLPI Agent, Knowledge, Control, Ollama, Research sowie eigenständig betreibbare Agent-/Knowledge-/Ollama-Core-Kits und Prometheus/Grafana-Beispiele. +> Release: **v1.6.1** · Crash-/Recovery-Hardening für große Knowledge-Korpora auf Basis der v1.6.0 Master/Subagent- und n:m-Graph-Architektur. Ein kontrolliertes Monorepo aus **GLPI AI Agent**, **GLPI AI Knowledgebase** und **NeuroForge + SQAR**. Ziel ist nicht ein untrennbarer Monolith, sondern eine gemeinsame Plattform mit klaren Zuständigkeiten, getrennten Credentials und nachvollziehbaren Failure-Modi. -## Crash-/Recovery-Hardening (seit v1.6.1, in v1.6.2 enthalten) +## Crash-/Recovery-Hardening (v1.6.1) -Die in v1.6.1 eingeführte Härtung behebt einen RAM-/Recovery-Fehler, der bei großen Graph-Backfills nach mehreren Stunden Laufzeit auftreten konnte. Erfolgreiche `vector.relink`-Jobs verwerfen ihre großen transienten Vector-Payloads unmittelbar nach dem autoritativen Master-Apply; ein Queue-Payload-Budget verhindert neue ungebremste Speicherbelegung. Beim ersten Start migriert NeuroForge alte v1.6.0-Checkpoints streaming, bevor `state.json` vollständig in den RAM geladen wird. +v1.6.1 behebt einen RAM-/Recovery-Fehler, der bei großen Graph-Backfills nach mehreren Stunden Laufzeit auftreten konnte. Erfolgreiche `vector.relink`-Jobs verwerfen ihre großen transienten Vector-Payloads unmittelbar nach dem autoritativen Master-Apply; ein Queue-Payload-Budget verhindert neue ungebremste Speicherbelegung. Beim ersten Start migriert NeuroForge alte v1.6.0-Checkpoints streaming, bevor `state.json` vollständig in den RAM geladen wird. HNSW-Deltas werden nicht mehr über einen vollständigen Deep-Copy aller Vektoren erzeugt. Checkpoints persistieren den Index vor `state.json`, sodass ein Crash während der Indexpersistenz den autoritativen Checkpoint nicht vor den verwendbaren Index ziehen kann. Bei Containerstarts mit explizitem `-listen` ist sofort eine Bootstrap-Liveness-/Startup-Seite erreichbar; Logs zeigen die aktuelle Recovery-Phase (`checkpoint.precompact`, `memory-segments.scan`, `wal.replay`, `hnsw.snapshot.load`, `hnsw.rebuild`, ...). @@ -34,22 +32,6 @@ Lokale Standardrollen: Zusätzliche Hosts können mit `docker-compose.subagent.yml` angebunden werden. Das Control Center bleibt read-only und zeigt Master-, Worker-, Queue- und Graphzustand. Details: [`docs/MASTER-SUBAGENT-ORCHESTRATOR.md`](docs/MASTER-SUBAGENT-ORCHESTRATOR.md). - -## Deployment-Modi - -Das Repository enthält bewusst mehrere, voneinander entkoppelte Betriebsformen: - -- `docker-compose.yml`: vollständiger Mega-Stack mit lokalem CPU- und GPU-Worker. -- `deployments/master`: autoritativer Master ohne lokale Worker, für getrennte CPU-/GPU-Hosts. -- `deployments/cpu-subagent`: abgesetzter CPU-Worker für `cpu,vector.relink`. -- `deployments/gpu-subagent`: abgesetzter GPU-Worker plus Ollama für `gpu,model.chat,model.embed`. -- `deployments/agent`: GLPI Agent standalone mit lokalem Vector-Backend, ohne NeuroForge-Zwang. -- `deployments/knowledge`: Knowledge standalone. -- `deployments/ollama`: Ollama standalone. -- `deployments/combined`: Agent + Knowledge + Ollama ohne NeuroForge. - -Details: [`deployments/README.md`](deployments/README.md). - ## Unified Graph Explorer (v1.4.0) Das read-only Control Center visualisiert Runtime/Trust, Ticket-Evidence, Learning-Lineage, Research-Provenance, einen redigierten NeuroForge-Brain-Graph sowie einen reproduzierbaren Engineering-Graph aus Go-AST und Compose. Für Dateien/Symbole/Routen gibt es zusätzlich eine statische Change-Impact-/Blast-Radius-Sicht. 2D ist der operative Default; 3D ist ein optionaler, gebundener Explorer. @@ -270,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 a/RELEASE-NOTES-v1.6.2.md b/RELEASE-NOTES-v1.6.2.md index aa1c0ae..e7e060a 100644 --- a/RELEASE-NOTES-v1.6.2.md +++ b/RELEASE-NOTES-v1.6.2.md @@ -1,38 +1,59 @@ # GLPI NeuroForge Mega v1.6.2 -## Consolidated production release +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. -v1.6.2 is the canonical full repository release. It keeps the v1.6.1 runtime/data format and recovery/OOM hotfixes, and consolidates all previously split deployment variants into one coherent tree. +## Complete deployment set -### Included deployment modes +The repository now ships all supported operating modes together: -- Full Mega stack with local CPU/GPU workers. -- Distributed Master with remote CPU and GPU subagents. -- Standalone GLPI Agent using a local vector backend. -- Standalone Knowledge service. -- Standalone Ollama. -- Combined Agent + Knowledge + Ollama core stack without NeuroForge. -- Prometheus/Grafana example monitoring on the distributed Master, including payload/queue/graph metrics and alerts. +- 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. -### Packaging / CI corrections +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. -- Restores the complete v1.6.1 NeuroForge recovery hardening and graph/orchestrator source tree. -- Includes complete `.env` files with safe placeholders for Master, CPU subagent, GPU subagent and standalone roles. -- Removes embedded real credentials from deployment examples. -- Replaces conflicting release workflows with one root CI workflow and one immutable tag release workflow. -- CI covers NeuroForge, Agent, Knowledge and Control plus all six project images. -- `docker-bake.hcl` builds NeuroForge server/worker, Agent, Agent data-init, Knowledge and Control. -- Removes nested/legacy workflow copies and stale duplicate compose files. -- Regenerates a single repository manifest for the exact final archive. +## Ollama Bearer authentication -### Runtime safety inherited from v1.6.1 +A shared optional variable is now supported: -- Streaming compaction of legacy terminal `vector.relink` payloads before normal state loading. -- Completed relink payload/result blobs are discarded after successful Master apply and during WAL recovery. -- Bounded pending durable job payload bytes. -- HNSW delta checkpoints avoid full graph/vector deep copies. -- Index-first checkpoint ordering and fail-closed authoritative JSON recovery. -- `/livez` and a bootstrap `/admin` page are available while store recovery is still running. -- Conservative graph backfill and job-retention defaults for large Knowledge corpora. +```env +OLLAMA_API_KEY= +``` -There is no intentional persisted-data-format break from v1.6.1 to v1.6.2. +When non-empty, clients send: + +```http +Authorization: Bearer +``` + +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/deployments/README.md b/deployments/README.md index 13b543c..6ee8c5a 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -1,29 +1,16 @@ -# Deployment Kits — GLPI NeuroForge Mega v1.6.2 +# Distributed Deployment Kits (v1.6.2) -This repository intentionally supports both the complete NeuroForge platform and stripped standalone operation. +This directory contains three independent deployment kits: -## Complete / distributed platform +- `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`. -- `master/` — authoritative NeuroForge Master plus GLPI Agent, Knowledge, Control, optional SearXNG, Prometheus and Grafana. Remote CPU/GPU workers connect to this node. -- `cpu-subagent/` — disposable CPU worker with `cpu,vector.relink` capabilities. -- `gpu-subagent/` — disposable GPU worker plus Ollama with `gpu,model.chat,model.embed` capabilities. +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. -The Master is the only authoritative owner of NeuroForge state. CPU/GPU workers use leases, heartbeats and fenced job completion. +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` -## Standalone core operation - -- `agent/` — GLPI Agent with local Knowledge vector backend; no NeuroForge, Control or Research dependency. -- `knowledge/` — standalone Knowledge editor/service. -- `ollama/` — standalone Ollama runtime. -- `combined/` — Agent + Knowledge + Ollama on one host, still without NeuroForge. - -The standalone Agent and Knowledge kits share `runtime/knowledge` by default. The Agent mounts it read-only; Knowledge mounts it read-write. - -## Release rules - -- Project images are pinned by `IMAGE_TAG=1.6.2`; production compose files do not require `latest`. -- Replace every `CHANGE_ME_...` placeholder before startup. -- Never use `docker compose down -v` during an in-place upgrade unless loss of persistent state is intended. -- For a v1.6.0/v1.6.1 NeuroForge data volume, keep the volume: v1.6.2 includes the v1.6.1 recovery/OOM hotfixes and startup compaction path. - -See each role's README/preflight and the root `README.md` for startup order. +Only the Master holds authoritative NeuroForge state. Workers are disposable execution nodes. diff --git a/deployments/agent/docker-compose.yml b/deployments/agent/docker-compose.yml index 879e41c..856b750 100644 --- a/deployments/agent/docker-compose.yml +++ b/deployments/agent/docker-compose.yml @@ -24,6 +24,7 @@ services: 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: "" diff --git a/deployments/combined/docker-compose.yml b/deployments/combined/docker-compose.yml index 1412e46..7f4b170 100644 --- a/deployments/combined/docker-compose.yml +++ b/deployments/combined/docker-compose.yml @@ -33,6 +33,7 @@ services: 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" diff --git a/deployments/gpu-subagent/docker-compose.yml b/deployments/gpu-subagent/docker-compose.yml index aa17baf..1787d81 100644 --- a/deployments/gpu-subagent/docker-compose.yml +++ b/deployments/gpu-subagent/docker-compose.yml @@ -39,6 +39,8 @@ services: 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} diff --git a/deployments/knowledge/docker-compose.yml b/deployments/knowledge/docker-compose.yml index 3b5d238..b0b7e05 100644 --- a/deployments/knowledge/docker-compose.yml +++ b/deployments/knowledge/docker-compose.yml @@ -11,6 +11,7 @@ services: 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: diff --git a/deployments/master/docker-compose.yml b/deployments/master/docker-compose.yml index a49cb8d..9bd0c2b 100644 --- a/deployments/master/docker-compose.yml +++ b/deployments/master/docker-compose.yml @@ -47,6 +47,8 @@ services: 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} @@ -317,6 +319,7 @@ services: 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} @@ -389,6 +392,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:?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} diff --git a/deployments/master/monitoring/prometheus/alerts.yml b/deployments/master/monitoring/prometheus/alerts.yml index 4a8667d..af8bc9d 100644 --- a/deployments/master/monitoring/prometheus/alerts.yml +++ b/deployments/master/monitoring/prometheus/alerts.yml @@ -63,7 +63,7 @@ groups: 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.2 safety budget." + 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 @@ -71,4 +71,4 @@ groups: 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.2." + description: "Terminal job payload/result bytes exceed 64 MiB. Completed vector.relink jobs should be compacted automatically in v1.6.1." diff --git a/docker-bake.hcl b/docker-bake.hcl index b508934..4a29258 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -7,51 +7,50 @@ variable "REGISTRY" { } group "default" { - targets = [ - "neuroforge", - "neuroforge-worker", - "agent", - "agent-data-init", - "knowledge", - "control", - ] + targets = ["neuroforge", "neuroforge-worker", "agent", "agent-data-init", "knowledge", "control"] } target "neuroforge" { - context = "./platform/neuroforge" + context = "./platform/neuroforge" dockerfile = "Dockerfile" - target = "server" - tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG}"] + target = "server" + tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge:${IMAGE_TAG}"] + platforms = ["linux/amd64"] } target "neuroforge-worker" { - context = "./platform/neuroforge" + context = "./platform/neuroforge" dockerfile = "Dockerfile" - target = "worker" - tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG}"] + target = "worker" + tags = ["${REGISTRY}/glpi-neuroforge-mega-neuroforge-worker:${IMAGE_TAG}"] + platforms = ["linux/amd64"] } target "agent" { - context = "./services/agent" + context = "./services/agent" dockerfile = "Dockerfile" - tags = ["${REGISTRY}/glpi-neuroforge-mega-agent:${IMAGE_TAG}"] + tags = ["${REGISTRY}/glpi-neuroforge-mega-agent:${IMAGE_TAG}"] + platforms = ["linux/amd64"] } target "agent-data-init" { - context = "./services/agent" + context = "./services/agent" dockerfile = "Dockerfile" - target = "data-init" - tags = ["${REGISTRY}/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG}"] + target = "data-init" + tags = ["${REGISTRY}/glpi-neuroforge-mega-agent-data-init:${IMAGE_TAG}"] + platforms = ["linux/amd64"] } target "knowledge" { - context = "./services/knowledge" + context = "./services/knowledge" dockerfile = "Dockerfile" - tags = ["${REGISTRY}/glpi-neuroforge-mega-knowledge:${IMAGE_TAG}"] + tags = ["${REGISTRY}/glpi-neuroforge-mega-knowledge:${IMAGE_TAG}"] + platforms = ["linux/amd64"] } target "control" { - context = "./services/control" + context = "./services/control" dockerfile = "Dockerfile" - tags = ["${REGISTRY}/glpi-neuroforge-mega-control:${IMAGE_TAG}"] + 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 e777fcd..9e3936b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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} @@ -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} @@ -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} @@ -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} diff --git a/docs/MIGRATION-v1.6.1-to-v1.6.2.md b/docs/MIGRATION-v1.6.1-to-v1.6.2.md index b855c96..b34632c 100644 --- a/docs/MIGRATION-v1.6.1-to-v1.6.2.md +++ b/docs/MIGRATION-v1.6.1-to-v1.6.2.md @@ -1,12 +1,24 @@ # Migration v1.6.1 -> v1.6.2 -v1.6.2 is primarily a packaging/deployment consolidation release. It retains the v1.6.1 NeuroForge recovery and persisted-state behavior. +No data-volume migration is required. Keep `neuroforge-data`, Agent data and Knowledge directories intact. -1. Back up the persistent data volumes/directories. -2. Set `IMAGE_TAG=1.6.2` in the selected deployment `.env`. -3. Keep existing `neuroforge-data`, Agent data and Knowledge files; do not delete volumes. -4. Replace deployment examples with the v1.6.2 variants, preserving real secrets locally. -5. Recreate project containers so changed environment/configuration is applied. -6. Check `/livez`, `/readyz`, Agent `/api/status`, worker status, graph convergence and Prometheus alerts. +1. Set `IMAGE_TAG=1.6.2`. +2. Copy the new optional Ollama variables into your `.env`: -The distributed roles now live alongside the standalone Agent/Knowledge/Ollama roles in the same canonical repository. + ```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 a/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/patches/SHA256SUMS b/patches/SHA256SUMS index d4d88c5..9684f8d 100644 --- a/patches/SHA256SUMS +++ b/patches/SHA256SUMS @@ -21,3 +21,4 @@ b031ea42356a022d7bb03e7e2bda23c4ceddb66ccc59a5d1f0ebe787a790b829 v1.5.1-to-v1.5 24bb0da9bf77a0e8b692989b05c00b623ed9a558b9b5050c24e396e1da41d0f8 v1.5.8-to-v1.5.9.diff e579d88ed2846cf5bc306cad45502af3edc61bd1c5bb5a2272979feff23dca62 v1.5.9-to-v1.6.0.diff 0cdce94c11a931e11c3c908f2fa3dfa733c5c602ac00f7f89e41b8c301574766 v1.6.0-to-v1.6.1.diff +0b09d93f115608f4ab6ee47e0a085f254eaeb8914fe1415fe73dccea69e5d166 v1.6.1-to-v1.6.2.diff diff --git a/patches/v1.6.1-to-v1.6.2.diff b/patches/v1.6.1-to-v1.6.2.diff new file mode 100644 index 0000000..915d246 --- /dev/null +++ b/patches/v1.6.1-to-v1.6.2.diff @@ -0,0 +1,6802 @@ +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 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 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 ++``` ++ ++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 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 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 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 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 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 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 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 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 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 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) ++ } ++} 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 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 a/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 a/scripts/secret-scan.sh b/scripts/secret-scan.sh index c60c531..a238aed 100755 --- a/scripts/secret-scan.sh +++ b/scripts/secret-scan.sh @@ -23,14 +23,29 @@ fi # Private keys and common live-token shapes must not be committed. Placeholders in # templates/docs are intentionally allowed. -# Distributed deployment bundles intentionally contain three complete `.env` -# templates. They must remain placeholder-only; every other private env/key file -# is still forbidden. if grep -E '(^|/)\.env$|\.pem$|\.p12$|\.pfx$|(^|/)id_rsa$|(^|/)id_ed25519$' "$FILES" \ - | grep -Ev '^deployments/(master|cpu-subagent|gpu-subagent|agent|knowledge|ollama|combined)/\.env$' >/dev/null; then + | 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" @@ -51,30 +66,6 @@ else fi fi -# Checked-in deployment .env files are templates, never live configuration. -for envf in \ - deployments/master/.env \ - deployments/cpu-subagent/.env \ - deployments/gpu-subagent/.env \ - deployments/agent/.env \ - deployments/knowledge/.env \ - deployments/ollama/.env \ - deployments/combined/.env; do - [ -f "$envf" ] || continue - if awk -F= ' - /^[[:space:]]*#/ || NF < 2 { next } - { - key=$1; sub(/^[[:space:]]+/, "", key); sub(/[[:space:]]+$/, "", key) - val=$0; sub(/^[^=]*=/, "", val) - if (key ~ /(TOKEN|PASSWORD|SECRET|CLIENT_ID|CLIENT_SECRET|API_KEY)$/ && val != "" && val !~ /^CHANGE_ME/) { - print FILENAME ":" NR ": live-looking secret in " key > "/dev/stderr"; - bad=1 - } - } - END { exit bad ? 1 : 0 } - ' "$envf"; then :; else bad "deployment template contains a non-placeholder secret: $envf"; fi -done - # Reject accidental binary blobs outside explicitly expected assets. while IFS= read -r f; do [ -f "$f" ] || continue 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 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 9502d59..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" } }, @@ -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 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) + } +}