Skip to content

feat(observability): allow selecting Laminar instruments - #4434

Open
Shimada666 wants to merge 2 commits into
OpenHands:mainfrom
Shimada666:feat/laminar-instruments
Open

feat(observability): allow selecting Laminar instruments#4434
Shimada666 wants to merge 2 commits into
OpenHands:mainfrom
Shimada666:feat/laminar-instruments

Conversation

@Shimada666

@Shimada666 Shimada666 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

HUMAN:
In our E2B deferred-init setup, limiting Laminar to LiteLLM reduced median POST /api/init latency from 5.971s to 0.370s across three fresh sandboxes.


AGENT:

Why

Laminar.initialize() enables every supported auto-instrumentation by default. Applications that only use a subset still pay the package discovery and initialization cost for the full catalog during startup.

In an OpenHands Agent Server deferred-init environment, limiting initialization to LiteLLM reduced median POST /api/init latency from 5.971s to 0.370s across three fresh E2B sandboxes per configuration (93.8% reduction).

Summary

  • Add the optional LMNR_INSTRUMENTS environment variable using comma-separated Laminar instrument values.
  • Pass the selected instruments to Laminar for both Laminar and generic OTLP backends.
  • Preserve the existing initialize-all behavior when the variable is unset.

Issue Number

N/A

How to Test

Unit coverage:

uv run pytest tests/sdk/observability/test_laminar.py -q

Result: 42 passed.

Repository checks:

uv run pre-commit run --files openhands-sdk/openhands/sdk/observability/laminar.py tests/sdk/observability/test_laminar.py

Result: Ruff format, Ruff lint, pycodestyle, pyright, import rules, and tool registration checks passed.

End-to-end benchmark:

  1. Started a fresh ghcr.io/openhands/agent-server:1.41.0-python-based E2B sandbox in deferred-init mode for each run.
  2. Sent the same Laminar configuration to POST /api/init.
  3. Compared the default with LMNR_INSTRUMENTS=litellm.
Configuration Run 1 Run 2 Run 3 Median
Default instruments 5.971s 6.169s 5.527s 5.971s
litellm only 0.361s 0.370s 0.399s 0.370s

Video/Screenshots

Not applicable; this is an environment configuration change.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Companion documentation PR: OpenHands/docs#706

Review feedback addressed in commit 6425b2c.

Co-authored-by: openhands <openhands@all-hands.dev>
@all-hands-bot

Copy link
Copy Markdown
Collaborator

🤖 OpenHands is reviewing this PR.

Head commit: c6fa8b0adc121268255ddbc6ca759feda7562f70
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/4dbdaae9-92bd-4909-ae88-1c21657662bb

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Summary

Clean, well-scoped change. The LMNR_INSTRUMENTS env var is parsed into a set of Instruments and forwarded to both the Laminar and generic OTLP Laminar.initialize() branches, with the existing initialize-all behavior preserved when the variable is unset. The motivating latency improvement (5.971s to 0.370s median in deferred-init) is compelling and the implementation matches the existing env-var parsing style in the module.

Tests pass (42 passed, as described) and the parametrized test covers both backend branches.

Risk assessment: LOW

The change is additive and opt-in. When LMNR_INSTRUMENTS is unset, behavior is identical to today. The one material finding below is a robustness gap at an import-time call site rather than a correctness bug in the happy path.

Finding

Unhandled ValueError on malformed/invalid instrument values crashes startup. maybe_init_laminar() runs at module import time (agent.py:99, acp_agent.py:118), so any ValueError from Instruments(value.strip()) propagates through import and breaks agent/server startup entirely.

Concrete triggers:

  • Trailing/leading/repeated commas: LMNR_INSTRUMENTS=litellm, -> "litellm,".split(",") yields an empty entry -> Instruments("") -> ValueError.
  • Whitespace-only value: LMNR_INSTRUMENTS=" " -> Instruments("") after strip -> ValueError.
  • A typo or unsupported instrument name -> ValueError.

An operator typo in a .env or deployment manifest would therefore turn the deferred-init latency win into a hard startup failure with no actionable log line, rather than a degraded-but-running state. This is inconsistent with the sibling _get_int_env helper in the same module, which logs a warning and returns None on a malformed value rather than raising.

Suggested direction (for the author to decide): filter out empty entries after stripping, and/or guard invalid values with a logger.warning(...) + skip (mirroring _get_int_env). This keeps the fail-open behavior expected of an observability hook. The test suite would also benefit from a case covering an empty/invalid entry so the import-time path is protected against regressions.

Comment thread openhands-sdk/openhands/sdk/observability/laminar.py Outdated
Co-authored-by: openhands <openhands@all-hands.dev>
@Shimada666

Copy link
Copy Markdown
Contributor Author

Addressed in 6425b2c: empty entries are ignored, unsupported instrument values now log a warning and are skipped, and the regression test covers both cases across both initialization branches. tests/sdk/observability/test_laminar.py now passes with 44 tests, and pre-commit passes for both changed files.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

🚦 CI is currently failing on this PR's latest commit.

Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request @all-hands-bot as a reviewer to have it reviewed regardless of CI status.)

This is an automated check - no AI was used to generate this comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants