Skip to content

feat(plugins): add langfuse plugin - #6577

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.47from
chirag127:feat/plugin-langfuse
Jul 7, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.47from
chirag127:feat/plugin-langfuse

Conversation

@chirag127

Copy link
Copy Markdown
Contributor

Problem

OmniRoute has no first-class Langfuse observability export today. Prior core-integration OTel PRs (#4974-#5018) were closed-not-merged; issue #5410 (Langfuse + OTel + Lunary + MLflow observability) closed with nothing merged.

Proposal

Add examples/plugins/langfuse/ — an opt-in plugin mirroring the request-logger/theme-manager template shape from #3473. Emits one Langfuse generation observation per LLM completion via onRequest/onResponse/onError hooks. Zero core file changes.

Files

  • examples/plugins/langfuse/plugin.json — manifest + configSchema (publicKey, secretKey, host, enabled, sampleRate, flushAt/Interval, redactBody)
  • examples/plugins/langfuse/index.mjs — 3 hook handlers + lazy SDK init + graceful shutdown flush
  • examples/plugins/langfuse/README.md — install, config, privacy notes

Alternatives considered

Testing

Install plugin → configure with Langfuse cloud keys → run any request → confirm trace appears at https://cloud.langfuse.com/project//traces.

Impact

  • 3 new files under examples/plugins/langfuse/, 288 LOC total
  • No changes to src/, chatCore.ts, combo.ts, or package.json runtime deps
  • Runtime langfuse npm SDK is a plugin-local dep; users npm install langfuse in the plugin dir

Thanks for the excellent OmniRoute!

Emits Langfuse generation traces (prompt, response, model, usage, latency,
errors) for every LLM completion via the existing onRequest/onResponse/onError
plugin hook system.

Follows the request-logger/theme-manager template shape from #3473. Zero core
file changes; plugin loads via the child-process plugin system, so a broken
langfuse SDK cannot crash the gateway.

Files:
- examples/plugins/langfuse/plugin.json — manifest + configSchema
- examples/plugins/langfuse/index.mjs — three hook handlers + lazy SDK init +
  graceful shutdown flush
- examples/plugins/langfuse/README.md — install, config, privacy notes

Thanks for the excellent OmniRoute!
@chirag127
chirag127 requested a review from diegosouzapw as a code owner July 7, 2026 16:57
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new Langfuse plugin for OmniRoute, enabling LLM completion tracing with support for sampling, body redaction, and latency tracking. The review feedback highlights that the onShutdown hook is not supported by OmniRoute and should be replaced with the onDeactivate hook in both plugin.json and index.mjs. Additionally, the feedback suggests resetting the langfuseClient to null upon deactivation to allow clean re-initialization, and refining the error handling in onError to prevent fallback objects from stringifying to "[object Object]".

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +10 to +14
"hooks": {
"onRequest": true,
"onResponse": true,
"onError": true
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The onShutdown hook used in index.mjs is not a recognized lifecycle event in OmniRoute. To properly flush pending events and clean up resources when the plugin is deactivated or updated, you should register and implement the onDeactivate hook instead.

  "hooks": {
    "onRequest": true,
    "onResponse": true,
    "onError": true,
    "onDeactivate": true
  },

Comment on lines +148 to +158
/**
* onShutdown — flush pending events before OmniRoute exits.
*/
export async function onShutdown() {
if (!langfuseClient) return;
try {
await langfuseClient.shutdownAsync();
} catch (err) {
console.error("[langfuse] shutdown failed:", err.message);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

OmniRoute does not support an onShutdown hook. Instead, use the onDeactivate lifecycle hook to flush pending events and clean up the client. Additionally, resetting langfuseClient to null is crucial to ensure that if the plugin is deactivated and reactivated with a new configuration (e.g., updated API keys or host), a new client is instantiated with the updated settings.

Suggested change
/**
* onShutdown — flush pending events before OmniRoute exits.
*/
export async function onShutdown() {
if (!langfuseClient) return;
try {
await langfuseClient.shutdownAsync();
} catch (err) {
console.error("[langfuse] shutdown failed:", err.message);
}
}
/**
* onDeactivate — flush pending events and clean up the client when the plugin is deactivated.
*/
export async function onDeactivate() {
if (!langfuseClient) return;
try {
await langfuseClient.shutdownAsync();
} catch (err) {
console.error("[langfuse] shutdown failed:", err.message);
} finally {
langfuseClient = null;
}
}


const start = ctx.metadata.__langfuseStart || Date.now();
const body = ctx?.body || {};
const error = ctx?.error || {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Defaulting error to {} means that if ctx?.error is undefined, error becomes an empty object. Later, String(error) will evaluate to "[object Object]". It is better to keep error as nullable and handle the fallback gracefully.

Suggested change
const error = ctx?.error || {};
const error = ctx?.error;

model: body.model || "unknown",
input: truncateBody(body.messages, config.redactBody),
level: "ERROR",
statusMessage: error.message || String(error),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Improve the error message fallback to avoid logging "[object Object]" when error is undefined or not an Error instance.

Suggested change
statusMessage: error.message || String(error),
statusMessage: error?.message || (error ? String(error) : "Unknown error"),

@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.47 July 7, 2026 21:41
@diegosouzapw

Copy link
Copy Markdown
Owner

Merged — thank you, @chirag127! 🙏 Rebased onto the active release branch (release/v3.8.47); all checks green.

@diegosouzapw
diegosouzapw merged commit 5e5fb61 into diegosouzapw:release/v3.8.47 Jul 7, 2026
9 checks passed
@chirag127
chirag127 deleted the feat/plugin-langfuse branch July 8, 2026 04:04
@diegosouzapw diegosouzapw mentioned this pull request Jul 11, 2026
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
feat(plugins): add langfuse example plugin Integrated into release/v3.8.47. (thanks @chirag127)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
feat(plugins): add langfuse example plugin Integrated into release/v3.8.47. (thanks @chirag127)
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