update-1.6.2
ci / test (services/knowledge) (push) Successful in 47s
ci / test (services/agent) (push) Successful in 55s
ci / test (platform/neuroforge) (push) Successful in 56s
ci / docker-build (push) Failing after 1m10s
ci / test (services/control) (push) Successful in 1m26s
release-tag / release-image (push) Successful in 6m43s

This commit is contained in:
2026-09-09 12:55:35 +02:00
parent e0bf42bf32
commit d18405ed2e
48 changed files with 8062 additions and 1111 deletions
+3 -1
View File
@@ -290,6 +290,8 @@ OLLAMA_REQUIRE_EMBEDDING_MODEL=true
# OLLAMA_MODEL=qwen3:8b
# Embedding-Modell für RAG.
OLLAMA_EMBEDDING_MODEL=embeddinggemma
# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
OLLAMA_API_KEY=
# Modellspezifisches Retrieval-Prompting.
# auto = Modell automatisch erkennen; für embeddinggemma empfohlen.
@@ -799,4 +801,4 @@ QUEUE_SIZE=256
# Ticketpipelines gleichzeitig verteilen, wie Worker aktiv sind. Für drei
# gleichartige Nodes ist WORKERS=3 ein sinnvoller Lasttest; jeder Node bleibt
# zusätzlich durch OLLAMA_NODE_MAX_INFLIGHT begrenzt.
WORKERS=2
WORKERS=2
+1 -1
View File
@@ -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,
@@ -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}
+1
View File
@@ -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}
+2
View File
@@ -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"),
+10
View File
@@ -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)
@@ -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)
}
}
+148 -104
View File
@@ -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
}
}
+2
View File
@@ -1,4 +1,6 @@
# Betriebsmodus desselben Images:
# Optional: Bearer token sent as Authorization: Bearer <token> to Ollama-compatible endpoints.
OLLAMA_API_KEY=
# editor = vollständiger Einzel-/Masseneditor mit Schreibzugriff
# google = schreibgeschützte Helpdesk-Suchmaschine mit Artikel-Viewer
APP_MODE=editor
+1
View File
@@ -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,
@@ -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}"
+1
View File
@@ -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}"
@@ -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) {
@@ -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)
}
}