From 22c924d3b05c644259b1ecb2302c610ff78cded8 Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Thu, 16 Apr 2026 00:56:39 -0300 Subject: [PATCH] move Langfuse to shared lng cluster, add trace instrumentation --- api/main.py | 14 ++-- ctrl/Dockerfile.api | 6 +- ctrl/Tiltfile | 4 +- ctrl/k8s/base/clickhouse.yaml | 48 -------------- ctrl/k8s/base/configmap.yaml | 6 +- ctrl/k8s/base/kustomization.yaml | 2 - ctrl/k8s/base/langfuse.yaml | 77 ---------------------- ctrl/k8s/kind-config.yaml | 5 -- uv.lock | 108 +++++++++++++++++++++++++++++-- 9 files changed, 119 insertions(+), 151 deletions(-) delete mode 100644 ctrl/k8s/base/clickhouse.yaml delete mode 100644 ctrl/k8s/base/langfuse.yaml diff --git a/api/main.py b/api/main.py index 3c01ae9..3b9cf0a 100644 --- a/api/main.py +++ b/api/main.py @@ -23,19 +23,25 @@ logging.basicConfig( ) +_langfuse_client = None + def _get_langfuse(): - """Lazy Langfuse client — returns None if not configured.""" + """Singleton Langfuse client — returns None if not configured.""" + global _langfuse_client + if _langfuse_client is not None: + return _langfuse_client try: from api.config import get_settings s = get_settings() if not s.langfuse_public_key or not s.langfuse_secret_key: return None from langfuse import Langfuse - return Langfuse( + _langfuse_client = Langfuse( public_key=s.langfuse_public_key, secret_key=s.langfuse_secret_key, host=s.langfuse_host, ) + return _langfuse_client except Exception: return None @@ -175,7 +181,7 @@ async def trigger_fce(req: FCERequest): logger.error("agent_error agent=fce run_id=%s error=%s", run_id, e) finally: if lf: - lf.shutdown() + lf.flush() logger.info("agent_start agent=fce run_id=%s flight=%s", run_id, req.flight_id) asyncio.create_task(_run()) @@ -223,7 +229,7 @@ async def trigger_handover(req: HandoverRequest): logger.error("agent_error agent=handover run_id=%s error=%s", run_id, e) finally: if lf: - lf.shutdown() + lf.flush() logger.info("agent_start agent=handover run_id=%s hubs=%s", run_id, req.hubs) asyncio.create_task(_run()) diff --git a/ctrl/Dockerfile.api b/ctrl/Dockerfile.api index 441f7ff..f75ee82 100644 --- a/ctrl/Dockerfile.api +++ b/ctrl/Dockerfile.api @@ -4,12 +4,12 @@ WORKDIR /app RUN pip install uv -COPY pyproject.toml ./ -RUN uv sync --no-dev --no-install-project +COPY pyproject.toml uv.lock ./ +RUN uv sync --no-dev --no-install-project --frozen COPY mcp_servers/ mcp_servers/ COPY agents/ agents/ COPY api/ api/ -CMD ["uv", "run", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] +CMD ["uv", "run", "uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/ctrl/Tiltfile b/ctrl/Tiltfile index f73ee78..684d61e 100644 --- a/ctrl/Tiltfile +++ b/ctrl/Tiltfile @@ -23,7 +23,6 @@ docker_build( sync('../mcp_servers', '/app/mcp_servers'), sync('../agents', '/app/agents'), sync('../api', '/app/api'), - sync('../irrop', '/app/irrop'), ], ) @@ -43,8 +42,7 @@ docker_build( # --- Resources --- k8s_resource('postgres') -k8s_resource('langfuse', resource_deps=['postgres'], port_forwards=['3000:3000']) -k8s_resource('api', resource_deps=['langfuse']) +k8s_resource('api', resource_deps=['postgres']) k8s_resource('ui', resource_deps=['api'], port_forwards=['8040:80']) # Group infra resources diff --git a/ctrl/k8s/base/clickhouse.yaml b/ctrl/k8s/base/clickhouse.yaml deleted file mode 100644 index 6d26f66..0000000 --- a/ctrl/k8s/base/clickhouse.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: clickhouse - namespace: unt -spec: - replicas: 1 - selector: - matchLabels: - app: clickhouse - template: - metadata: - labels: - app: clickhouse - spec: - containers: - - name: clickhouse - image: clickhouse/clickhouse-server:24-alpine - ports: - - containerPort: 8123 - - containerPort: 9000 - env: - - name: CLICKHOUSE_USER - value: "default" - - name: CLICKHOUSE_PASSWORD - value: "clickhouse" - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 1Gi ---- -apiVersion: v1 -kind: Service -metadata: - name: clickhouse - namespace: unt -spec: - selector: - app: clickhouse - ports: - - name: http - port: 8123 - targetPort: 8123 - - name: native - port: 9000 - targetPort: 9000 diff --git a/ctrl/k8s/base/configmap.yaml b/ctrl/k8s/base/configmap.yaml index 602d1fd..5fb7faf 100644 --- a/ctrl/k8s/base/configmap.yaml +++ b/ctrl/k8s/base/configmap.yaml @@ -8,7 +8,7 @@ data: LLM_PROVIDER: "groq" GROQ_API_KEY: "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" GROQ_MODEL: "llama-3.3-70b-versatile" - LANGFUSE_HOST: "http://langfuse:3000" - LANGFUSE_PUBLIC_KEY: "pk-lf-6642fd68-e09b-4aa7-828d-5b13265703d8" - LANGFUSE_SECRET_KEY: "sk-lf-d6574b94-e63c-4bb8-8993-1f2db41a1bc0" + LANGFUSE_HOST: "http://172.19.0.4:30030" + LANGFUSE_PUBLIC_KEY: "pk-lf-96332815-783c-4448-8bca-e3df7b15e259" + LANGFUSE_SECRET_KEY: "sk-lf-14ab52ee-337f-48a5-9355-a9e726401f58" KONG_PROXY_URL: "" diff --git a/ctrl/k8s/base/kustomization.yaml b/ctrl/k8s/base/kustomization.yaml index 96dc4c5..5759707 100644 --- a/ctrl/k8s/base/kustomization.yaml +++ b/ctrl/k8s/base/kustomization.yaml @@ -7,7 +7,5 @@ resources: - namespace.yaml - configmap.yaml - postgres.yaml - - clickhouse.yaml - - langfuse.yaml - api.yaml - ui.yaml diff --git a/ctrl/k8s/base/langfuse.yaml b/ctrl/k8s/base/langfuse.yaml deleted file mode 100644 index a224c39..0000000 --- a/ctrl/k8s/base/langfuse.yaml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: langfuse - namespace: unt -spec: - replicas: 1 - selector: - matchLabels: - app: langfuse - template: - metadata: - labels: - app: langfuse - spec: - containers: - - name: langfuse - image: langfuse/langfuse:3 - ports: - - containerPort: 3000 - env: - - name: DATABASE_URL - value: "postgresql://langfuse:langfuse@postgres:5432/langfuse" - - name: CLICKHOUSE_URL - value: "http://clickhouse:8123" - - name: CLICKHOUSE_MIGRATION_URL - value: "clickhouse://clickhouse:9000" - - name: CLICKHOUSE_USER - value: "default" - - name: CLICKHOUSE_PASSWORD - value: "clickhouse" - - name: CLICKHOUSE_CLUSTER_ENABLED - value: "false" - - name: LANGFUSE_S3_EVENT_UPLOAD_ENABLED - value: "false" - - name: LANGFUSE_S3_EVENT_UPLOAD_BUCKET - value: "unused" - - name: LANGFUSE_S3_MEDIA_UPLOAD_ENABLED - value: "false" - - name: LANGFUSE_S3_MEDIA_UPLOAD_BUCKET - value: "unused" - - name: NEXTAUTH_SECRET - value: "unt-dev-secret" - - name: NEXTAUTH_URL - value: "http://localhost:3000" - - name: HOSTNAME - value: "0.0.0.0" - - name: AUTH_DISABLE_SIGNUP - value: "true" - - name: SALT - value: "unt-dev-salt-not-for-production-use" - readinessProbe: - httpGet: - path: /api/public/health - port: 3000 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 10 - resources: - requests: - memory: 256Mi - cpu: 200m - limits: - memory: 1Gi ---- -apiVersion: v1 -kind: Service -metadata: - name: langfuse - namespace: unt -spec: - selector: - app: langfuse - ports: - - port: 3000 - targetPort: 3000 diff --git a/ctrl/k8s/kind-config.yaml b/ctrl/k8s/kind-config.yaml index 68d47c8..efd9dad 100644 --- a/ctrl/k8s/kind-config.yaml +++ b/ctrl/k8s/kind-config.yaml @@ -9,8 +9,3 @@ nodes: hostPort: 8040 listenAddress: "0.0.0.0" protocol: TCP - # Langfuse observability - - containerPort: 30030 - hostPort: 3000 - listenAddress: "0.0.0.0" - protocol: TCP diff --git a/uv.lock b/uv.lock index 2d03ab6..d97f4e1 100644 --- a/uv.lock +++ b/uv.lock @@ -498,6 +498,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/48/84b6dcba793178a44b9d99b4def6cd62f870dcfc5bb7b9153ac390135812/fastmcp-3.2.3-py3-none-any.whl", hash = "sha256:cc50af6eed1f62ed8b6ebf4987286d8d1d006f08d5bec739d5c7fb76160e0911", size = 707260, upload-time = "2026-04-09T22:05:01.225Z" }, ] +[[package]] +name = "googleapis-common-protos" +version = "1.74.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/18/a746c8344152d368a5aac738d4c857012f2c5d1fd2eac7e17b647a7861bd/googleapis_common_protos-1.74.0.tar.gz", hash = "sha256:57971e4eeeba6aad1163c1f0fc88543f965bb49129b8bb55b2b7b26ecab084f1", size = 151254, upload-time = "2026-04-02T21:23:26.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/b0/be5d3329badb9230b765de6eea66b73abd5944bdeb5afb3562ddcd80ae84/googleapis_common_protos-1.74.0-py3-none-any.whl", hash = "sha256:702216f78610bb510e3f12ac3cafd281b7ac45cc5d86e90ad87e4d301a3426b5", size = 300743, upload-time = "2026-04-02T21:22:49.108Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -864,21 +876,21 @@ wheels = [ [[package]] name = "langfuse" -version = "2.60.10" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio" }, { name = "backoff" }, { name = "httpx" }, - { name = "idna" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-sdk" }, { name = "packaging" }, { name = "pydantic" }, - { name = "requests" }, { name = "wrapt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/45/77fdf53c9e9f49bb78f72eba3f992f2f3d8343e05976aabfe1fca276a640/langfuse-2.60.10.tar.gz", hash = "sha256:a26d0d927a28ee01b2d12bb5b862590b643cc4e60a28de6e2b0c2cfff5dbfc6a", size = 152648, upload-time = "2025-09-16T15:08:12.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/45/9c/b912a00ffae92ff9955cdd9b74fb839be58f631d4329ae2a8a0376f697f2/langfuse-4.2.0.tar.gz", hash = "sha256:d0bd26d5065cf6a59d7d1093b08d8910e2458dc3da7ed8ccec160db114c18342", size = 275582, upload-time = "2026-04-10T11:55:25.21Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/69/08584fbd69e14398d3932a77d0c8d7e20389da3e6470210d6719afba2801/langfuse-2.60.10-py3-none-any.whl", hash = "sha256:815c6369194aa5b2a24f88eb9952f7c3fc863272c41e90642a71f3bc76f4a11f", size = 275568, upload-time = "2025-09-16T15:08:10.166Z" }, + { url = "https://files.pythonhosted.org/packages/be/0a/b84e3e68a690ccfe6d64953c572772c685fcb0915b7f2ee3a87c22e388ab/langfuse-4.2.0-py3-none-any.whl", hash = "sha256:bfd760bf10fd0228f297f6369436620f76d16b589de46393d65706b27e4e4082", size = 475449, upload-time = "2026-04-10T11:55:23.624Z" }, ] [[package]] @@ -1123,6 +1135,75 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/58/ee/99ab786653b3bda9c37ade7e24a7b607a1b1f696063172768417539d876d/opentelemetry_api-1.41.0-py3-none-any.whl", hash = "sha256:0e77c806e6a89c9e4f8d372034622f3e1418a11bdbe1c80a50b3d3397ad0fa4f", size = 69007, upload-time = "2026-04-09T14:38:11.833Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/28/e8eca94966fe9a1465f6094dc5ddc5398473682180279c94020bc23b4906/opentelemetry_exporter_otlp_proto_common-1.41.0.tar.gz", hash = "sha256:966bbce537e9edb166154779a7c4f8ab6b8654a03a28024aeaf1a3eacb07d6ee", size = 20411, upload-time = "2026-04-09T14:38:36.572Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/c4/78b9bf2d9c1d5e494f44932988d9d91c51a66b9a7b48adf99b62f7c65318/opentelemetry_exporter_otlp_proto_common-1.41.0-py3-none-any.whl", hash = "sha256:7a99177bf61f85f4f9ed2072f54d676364719c066f6d11f515acc6c745c7acf0", size = 18366, upload-time = "2026-04-09T14:38:15.135Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/63/d9f43cd75f3fabb7e01148c89cfa9491fc18f6580a6764c554ff7c953c46/opentelemetry_exporter_otlp_proto_http-1.41.0.tar.gz", hash = "sha256:dcd6e0686f56277db4eecbadd5262124e8f2cc739cadbc3fae3d08a12c976cf5", size = 24139, upload-time = "2026-04-09T14:38:38.128Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b5/a214cd907eedc17699d1c2d602288ae17cb775526df04db3a3b3585329d2/opentelemetry_exporter_otlp_proto_http-1.41.0-py3-none-any.whl", hash = "sha256:a9c4ee69cce9c3f4d7ee736ad1b44e3c9654002c0816900abbafd9f3cf289751", size = 22673, upload-time = "2026-04-09T14:38:18.349Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/d9/08e3dc6156878713e8c811682bc76151f5fe1a3cb7f3abda3966fd56e71e/opentelemetry_proto-1.41.0.tar.gz", hash = "sha256:95d2e576f9fb1800473a3e4cfcca054295d06bdb869fda4dc9f4f779dc68f7b6", size = 45669, upload-time = "2026-04-09T14:38:45.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/8c/65ef7a9383a363864772022e822b5d5c6988e6f9dabeebb9278f5b86ebc3/opentelemetry_proto-1.41.0-py3-none-any.whl", hash = "sha256:b970ab537309f9eed296be482c3e7cca05d8aca8165346e929f658dbe153b247", size = 72074, upload-time = "2026-04-09T14:38:29.38Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/0e/a586df1186f9f56b5a0879d52653effc40357b8e88fc50fe300038c3c08b/opentelemetry_sdk-1.41.0.tar.gz", hash = "sha256:7bddf3961131b318fc2d158947971a8e37e38b1cd23470cfb72b624e7cc108bd", size = 230181, upload-time = "2026-04-09T14:38:47.225Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/13/a7825118208cb32e6a4edcd0a99f925cbef81e77b3b0aedfd9125583c543/opentelemetry_sdk-1.41.0-py3-none-any.whl", hash = "sha256:a596f5687964a3e0d7f8edfdcf5b79cbca9c93c7025ebf5fb00f398a9443b0bd", size = 180214, upload-time = "2026-04-09T14:38:30.657Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.62b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/b0/c14f723e86c049b7bf8ff431160d982519b97a7be2857ed2247377397a24/opentelemetry_semantic_conventions-0.62b0.tar.gz", hash = "sha256:cbfb3c8fc259575cf68a6e1b94083cc35adc4a6b06e8cf431efa0d62606c0097", size = 145753, upload-time = "2026-04-09T14:38:48.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/6c/5e86fa1759a525ef91c2d8b79d668574760ff3f900d114297765eb8786cb/opentelemetry_semantic_conventions-0.62b0-py3-none-any.whl", hash = "sha256:0ddac1ce59eaf1a827d9987ab60d9315fb27aea23304144242d1fcad9e16b489", size = 231619, upload-time = "2026-04-09T14:38:32.394Z" }, +] + [[package]] name = "orjson" version = "3.11.8" @@ -1251,6 +1332,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "protobuf" +version = "6.33.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, +] + [[package]] name = "py-key-value-aio" version = "0.4.4"