Sweeps the references that carry no runtime coupling, and fixes one that
turned out to be a real bug rather than stale branding.
Docker network: sentry_default -> cairnobs_default across 23 runbook and
test-header `docker run` commands. Compose derives the network from the
directory name, so this lands together with renaming the working copy to
cairnobs/ -- the two are only correct as one change.
Stale references corrected: four Dockerfile "repo root (sentry/)"
headers; .env pointing at the long-renamed deploy/helm/sentry/ chart;
five Helm comments describing the topic as sentry.logs.raw when all four
code paths have defaulted to cairnobs.logs.raw for some time; an
absolute /home/john/Projects/sentry/ path in the operator's package doc,
now repo-relative; the hand-written Tenant CRD description in both of
its identical copies, whose Go source already said Cairn OBS.
Migration 0043 repoints the default tenant's data source. 0026 seeded it
with ('sentry', '/var/lib/sentry-search') to match what
api/internal/config then defaulted to; the rebrand later moved those
defaults to "cairnobs" and /var/lib/cairnobs-search without moving the
already-applied row, leaving the default tenant naming a ClickHouse
database nothing writes to. Scoped to the exact stale values so it is a
no-op on any deployment that set them deliberately. 0026's comment is
annotated as superseded; its applied SQL is untouched.
Deliberately not included: the gRPC wire packages (sentry.logs.v1,
sentry.agent.v1) and proto/sentry/ import paths, which cannot change
without a lockstep agent/server upgrade; the Helm chart's
sentry_metadata database and sentry role, which need a real Postgres
migration on existing deployments; and the compliance audit records in
docs/compliance/, which are a dated historical record.
go build, go vet, and go test pass for ingest and deploy/operator.
101 lines
3.3 KiB
Go
101 lines
3.3 KiB
Go
// Command mock-ollama stands in for a real Ollama server (see
|
|
// /docs/phase-7-ai-design.md) when manually verifying Phase 7's AI
|
|
// features against a live docker-compose stack without needing model
|
|
// weights or a GPU. Matches Ollama's real POST /api/chat wire contract
|
|
// (see api/ai/provider/ollama/ollama.go's chatRequest/chatResponse)
|
|
// closely enough that api/cmd/api and enterprise/cmd/enterprise-api
|
|
// can't tell the difference -- picks a canned, deterministic response by
|
|
// inspecting the system prompt's distinctive opening line (see
|
|
// api/ai/provider/ollama/prompts.go), the same technique the
|
|
// integration tests in api/ai/aiapi/integration_test.go use for the
|
|
// same reason: no live model, deterministic output, fast.
|
|
//
|
|
// Not part of any docker-compose service by default -- run it
|
|
// standalone (or as a throwaway container on the cairnobs_default
|
|
// network) and point OLLAMA_BASE_URL at it. See
|
|
// /docs/phase-7-runbook.md for the exact recipe.
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type chatMessage struct {
|
|
Role string `json:"role"`
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
type chatRequest struct {
|
|
Model string `json:"model"`
|
|
Messages []chatMessage `json:"messages"`
|
|
}
|
|
|
|
type chatResponse struct {
|
|
Message chatMessage `json:"message"`
|
|
}
|
|
|
|
// canned responses, keyed by a distinctive substring of each
|
|
// operation's system prompt (see prompts.go's opening sentence for
|
|
// each). Content is deliberately valid, uninteresting pipe syntax --
|
|
// this tool exists to verify plumbing, not to simulate model quality.
|
|
var canned = []struct {
|
|
systemPromptContains string
|
|
response string
|
|
}{
|
|
{"translate a plain-English question", `{"query":"earliest=-1h severity=ERROR","confidence":"high"}`},
|
|
{"suggest how to continue", `{"suggestion":" severity=ERROR"}`},
|
|
{"phrase those findings", "Add a time range (e.g. earliest=-1h) to avoid scanning the entire table."},
|
|
{"fix a broken", `{"suggested_query":"earliest=-1h severity=ERROR","explanation":"added a missing time bound","confidence":"high"}`},
|
|
// Plain explain (no findings, no original-intent framing) falls
|
|
// through to this last, broadest match.
|
|
{"", "This query filters logs where severity equals ERROR from the last hour."},
|
|
}
|
|
|
|
func respond(system string) string {
|
|
for _, c := range canned {
|
|
if strings.Contains(system, c.systemPromptContains) {
|
|
return c.response
|
|
}
|
|
}
|
|
return canned[len(canned)-1].response
|
|
}
|
|
|
|
func main() {
|
|
addr := flag.String("addr", ":11434", "listen address")
|
|
flag.Parse()
|
|
|
|
http.HandleFunc("/api/chat", func(w http.ResponseWriter, r *http.Request) {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
http.Error(w, "reading body: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
var req chatRequest
|
|
if err := json.Unmarshal(body, &req); err != nil {
|
|
http.Error(w, "decoding request: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
var system string
|
|
for _, m := range req.Messages {
|
|
if m.Role == "system" {
|
|
system = m.Content
|
|
break
|
|
}
|
|
}
|
|
content := respond(system)
|
|
fmt.Printf("chat: model=%s -> %s\n", req.Model, content)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(chatResponse{Message: chatMessage{Role: "assistant", Content: content}})
|
|
})
|
|
|
|
log.Printf("mock-ollama listening on %s", *addr)
|
|
log.Fatal(http.ListenAndServe(*addr, nil))
|
|
}
|