Make a Langfuse trace worth opening: question, answer, session, no probe noise

This commit is contained in:
2026-08-21 14:45:32 +07:00
parent 53582b6030
commit f3eaab0948
16 changed files with 733 additions and 5 deletions
@@ -22,6 +22,9 @@ data:
OTEL_SERVICE_NAME: ai-service
OTEL_EXPORTER_OTLP_ENDPOINT: {{ printf "http://%s-otel-collector:4318/v1/traces" (include "medical-chatbot.fullname" .) | quote }}
OTEL_SAMPLE_RATIO: {{ .Values.aiService.config.otelSampleRatio | quote }}
{{- if .Values.aiService.config.langfuseBaseUrl }}
LANGFUSE_BASE_URL: {{ .Values.aiService.config.langfuseBaseUrl | quote }}
{{- end }}
MAX_WALL_CLOCK_MS: {{ .Values.aiService.config.maxWallClockMs | quote }}
MAX_LLM_CALLS_PER_TURN: {{ .Values.aiService.config.maxLlmCallsPerTurn | quote }}
---
@@ -81,6 +84,25 @@ spec:
secretKeyRef:
name: {{ include "medical-chatbot.secretName" . }}
key: postgres-dsn
{{- if .Values.aiService.config.langfuseBaseUrl }}
# Langfuse keys are real credentials, so they come from the
# Secret, never the ConfigMap above (which is world-readable to
# anyone with namespace get access). Both are marked optional so
# a cluster that sets the base URL before creating the Secret
# starts anyway, with the exporter simply not added.
- name: LANGFUSE_PUBLIC_KEY
valueFrom:
secretKeyRef:
name: {{ include "medical-chatbot.secretName" . }}
key: langfuse-public-key
optional: true
- name: LANGFUSE_SECRET_KEY
valueFrom:
secretKeyRef:
name: {{ include "medical-chatbot.secretName" . }}
key: langfuse-secret-key
optional: true
{{- end }}
{{- if .Values.aws.region }}
- name: AWS_REGION
value: {{ .Values.aws.region | quote }}
@@ -14,6 +14,13 @@ stringData:
aws-access-key-id: {{ .Values.aws.staticCredentials.accessKeyId | quote }}
aws-secret-access-key: {{ .Values.aws.staticCredentials.secretAccessKey | quote }}
{{- end }}
{{- if .Values.secret.langfusePublicKey }}
{{/* Both keys or neither: a public key without its secret authenticates
nothing, and ai-service already treats a partial config as "not
configured" (see rag/telemetry.py's _langfuse_exporters). */}}
langfuse-public-key: {{ .Values.secret.langfusePublicKey | quote }}
langfuse-secret-key: {{ required "secret.langfuseSecretKey is required when secret.langfusePublicKey is set" .Values.secret.langfuseSecretKey | quote }}
{{- end }}
{{- if or .Values.authService.enabled .Values.apiGateway.enabled }}
{{/* Required (not defaulted) once either service is turned on — same
fail-closed posture as the image-tag guard above: a guessable or empty
@@ -52,6 +52,22 @@ aiService:
answerProvider: bedrock-converse
answerModelId: qwen.qwen3-next-80b-a3b
rerankEnabled: true
# Second trace destination alongside Tempo, for the LLM-shaped view
# (per-trace answers + eval scores). Self-hosted on this same cluster by
# the separate `langfuse` ArgoCD Application. The keys it needs are
# secrets and stay inline on the Application, like jwtSecret does —
# setting this URL alone changes nothing until they exist.
#
# The in-cluster Service, NOT https://langfuse.realvuxbaro.me. That public
# name resolves to the Elastic IP of the very node these Pods run on, so a
# Pod reaching it has to hairpin out and back through the node's own
# public address — which silently never completes here. Spans were created
# and dropped with no error in ai-service's log and nothing arriving on
# Langfuse's otel-ingestion-queue; an identical span sent from a laptop
# over the public URL ingested fine, which is what isolated it to the
# cluster-internal hop. Cross-namespace is fine: Services resolve
# cluster-wide, unlike Secrets.
langfuseBaseUrl: http://langfuse-web.medical-chatbot-data.svc.cluster.local:3000
# Auth is live in production (2026-08-19). These stayed false here for a day
# while the live Application carried `enabled: true` inline, so Git and the
+10
View File
@@ -17,6 +17,11 @@ secret:
# Set when postgres runs in a different Application/release (the
# data/app split pattern) — overrides the default in-release host.
postgresHost: ""
# Langfuse project API keys. Real credentials -- set these inline on the
# ArgoCD Application or a pre-created Secret, never in a tracked values
# file. Leaving them empty simply means no Langfuse export.
langfusePublicKey: ""
langfuseSecretKey: ""
grafanaAdminPassword: change-me
# Required (chart render fails without it) once authService or apiGateway
# is enabled — signs/verifies every JWT. Must be the same value both
@@ -76,6 +81,11 @@ aiService:
metricsEnabled: true
otelEnabled: true
otelSampleRatio: 1.0
# Empty by default: Langfuse is an optional second trace destination.
# Setting this alone is inert -- the keys live in the Secret (see
# `secret.langfusePublicKey`), and ai-service adds the exporter only when
# URL + both keys are present.
langfuseBaseUrl: ""
maxWallClockMs: 40000
maxLlmCallsPerTurn: 8
migration: