Skip to content

Navigation Menu

Sign in
Sign up

feat(ai): add LangChain v1 agent middleware - #945

Open
gouveags wants to merge 2 commits into
PostHog:main from
gouveags:feat/langchain-v1-middleware
Open

feat(ai): add LangChain v1 agent middleware #945
gouveags wants to merge 2 commits into
PostHog:main from
gouveags:feat/langchain-v1-middleware

Conversation

@gouveags

@gouveags gouveags commented Sep 11, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

LangChain v1 supports middleware as the main way to package agent integrations, but the Python SDK currently only supports callbacks.

This adds posthog.ai.langchain.middleware.PostHogMiddleware, the Python counterpart to PostHog/posthog-js#4556.

Closes #901.

What changed

  • Added sync and async middleware hooks for agent, model, and tool activity.
  • Kept middleware support in a separate optional module, so the base SDK and existing callback-only imports do not require langchain.
  • Reused the existing LangChain callback conversion and capture logic.
  • Preserved model and tool return values and exceptions exactly; PostHog capture remains fail-open.
  • Kept synchronous delivery off the event-loop thread when Client(sync_mode=True).
  • Added private per-invocation state for trace correlation, privacy-safe checkpoint handling, and cleanup after completed runs.
  • Added per-tool schema fallback so one unsupported tool does not prevent the remaining telemetry from being captured.
  • Added a runnable example and usage guidance.

Use either PostHogMiddleware or CallbackHandler for an agent invocation, not both. Put PostHogMiddleware last in the middleware list so it sees the final model selection and each retry attempt.

On terminal agent failure, LangChain does not call after_agent. In that case, the failed model or tool call is captured without a parent, and no dangling root trace is emitted.

💚 How did you test it?

  • LangChain callback and middleware suites: 105 passed, 8 skipped
  • Full test suite: 2,712 passed, 15 skipped, 38 subtests passed
  • Python 3.10 compatibility
  • Built-wheel smoke test with the minimum supported langchain==1.3.9
  • Ruff formatting and lint
  • Mypy baseline and strict type checks
  • Public API snapshot check
  • Package build and Twine validation
  • Base-package import smoke test without LangChain installed

The middleware tests cover sync and async agent runs, non-blocking async capture, model retries, model and tool failures, returned tool errors, Command results, concurrent invocations, privacy mode, checkpoint cleanup, tool-schema fallback, capture failures, and compatibility with the existing callback integration.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Added a Sampo minor changeset.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Gabriel directed the scope and is the DRI (@gouveags). Codex assisted with implementation, tests, validation, and independent review. No shareable session transcript is available.

The main design choice was to keep middleware support optional and reuse the existing callback capture logic instead of maintaining a second telemetry implementation. Human review is required.

Copy link
Copy Markdown
Contributor Author

@greptileai review

greptile-apps[bot] reacted with thumbs up emoji

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
### Issue 1
posthog/ai/langchain/middleware.py:100
**Recovered failures lose parents**
When an outer retry or recovery middleware handles a model exception and the agent later completes, this call captures the failed attempt without `$ai_parent_id` even though it retains the root trace ID. The failed attempt therefore appears as root-level work instead of a child of the agent. The same issue affects async model failures and sync or async tool failures. Preserve the root relationship for recovered failures, and suppress it only when the invocation is known to have terminated.
### Issue 2
posthog/ai/langchain/middleware.py:114-116
**Async capture blocks execution**
If the supplied client uses `sync_mode=True`, this async wrapper calls synchronous telemetry capture on the event-loop thread after awaiting the model handler. That capture performs HTTP delivery inline, so every model event can block other coroutines; the same applies to agent and tool completion hooks. Run blocking capture work off the event-loop thread or provide an async capture path. The repository directive requires blocking synchronous HTTP calls in async Python to be flagged and avoided.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(ai): add LangChain v1 agent middlew..." | Re-trigger Greptile

Comment thread posthog/ai/langchain/middleware.py
Comment thread posthog/ai/langchain/middleware.py Outdated
gouveags marked this pull request as ready for review September 11, 2026 22:19
gouveags requested a review from a team as a code owner September 11, 2026 22:19

Copy link
Copy Markdown
Contributor Author

@greptileai review

greptile-apps[bot] reacted with thumbs up emoji

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Copy link
Copy Markdown
Contributor Author

@greptileai review

greptile-apps[bot] reacted with thumbs up emoji

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

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

Reviewers

@greptile-apps greptile-apps[bot] greptile-apps[bot] left review comments

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Support LangChain v1 middleware in the Python SDK

1 participant

AltStyle によって変換されたページ (->オリジナル) /