dify - 💡(How to fix) Fix Pass app_id to model plugins for provider-side cost attribution [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langgenius/dify#35772Fetched 2026-05-04 05:15:56
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
1
Timeline (top)
commented ×1cross-referenced ×1labeled ×1

Root Cause

None of these can be utilized from a Dify model plugin today because app_id is not passed.

Fix Action

Fix / Workaround

Meanwhile, tool plugins already receive app_id (along with conversation_id and message_id) via the session. The model plugin dispatch path simply does not include it:

# Tool plugin dispatch (core/plugin/impl/tool.py) — app_id is included
data={
    "user_id": user_id,
    "conversation_id": conversation_id,
    "app_id": app_id,
    "message_id": message_id,
    "data": { ... },
}

# Model plugin dispatch (core/plugin/impl/model.py) — app_id is missing
data=jsonable_encoder({
    "user_id": user_id,
    "data": { ... },
})

Code Example

# Tool plugin dispatch (core/plugin/impl/tool.py) — app_id is included
data={
    "user_id": user_id,
    "conversation_id": conversation_id,
    "app_id": app_id,
    "message_id": message_id,
    "data": { ... },
}

# Model plugin dispatch (core/plugin/impl/model.py) — app_id is missing
data=jsonable_encoder({
    "user_id": user_id,
    "data": { ... },
})
RAW_BUFFERClick to expand / collapse

Self Checks

  • I have read the Contributing Guide and Language Policy.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report, otherwise it will be closed.
  • Please do not modify this template :) and fill in all the required fields.

1. Is this request related to a challenge you're experiencing? Tell me about your story.

We are building a per-app cost tracking dashboard (Grafana) for a Dify + Amazon Bedrock deployment. Bedrock's Converse API supports a requestMetadata parameter that lets you tag each request with custom key-value pairs, which are then recorded in CloudWatch Logs. We built a custom Bedrock model plugin to use this, but discovered that model plugins do not receive app_id — only user_id is available. This makes it impossible to tag LLM calls with the originating app.

This is not a Bedrock-specific limitation. All major LLM providers offer similar request-level metadata for cost attribution:

  • Amazon Bedrock: requestMetadata (Converse API) → CloudWatch Logs
  • OpenAI / Azure OpenAI: metadata (Chat Completions API) → Usage Dashboard
  • Google Vertex AI: labels (generateContent API) → directly reflected in Cloud Billing
  • Anthropic: metadata (Messages API)

None of these can be utilized from a Dify model plugin today because app_id is not passed.

Meanwhile, tool plugins already receive app_id (along with conversation_id and message_id) via the session. The model plugin dispatch path simply does not include it:

# Tool plugin dispatch (core/plugin/impl/tool.py) — app_id is included
data={
    "user_id": user_id,
    "conversation_id": conversation_id,
    "app_id": app_id,
    "message_id": message_id,
    "data": { ... },
}

# Model plugin dispatch (core/plugin/impl/model.py) — app_id is missing
data=jsonable_encoder({
    "user_id": user_id,
    "data": { ... },
})

Proposal: Add app_id to the model plugin dispatch payload, as an optional field (null when called outside app context, e.g., RAG routing, title generation, suggested questions).

We are only proposing app_id — not conversation_id, message_id, or workflow_run_id. These IDs vary by app type (e.g., Workflow apps have workflow_run_id instead of conversation_id), which would make the change more complex. With app_id on the provider side, finer-grained drill-down (per-conversation, per-node, per-iteration) can be done by joining provider logs with Dify's existing database tables (messages, workflow_node_executions) using app_id as the key.

This change spans 2 repositories (langgenius/dify and langgenius/dify-plugin-sdks). The plugin daemon (dify-plugin-daemon) already uses a shared InvokePluginRequest struct with an AppID field for both tool and model dispatch, so no daemon changes are needed — once Dify includes app_id in the model dispatch payload, the daemon will pass it through to the SDK session automatically. On the SDK side, plugin_executor.py needs to forward the session to model plugin instances (it already does this for tool plugins). Fully backward compatible — existing model plugins require no changes.

2. Additional context or comments

This asymmetry was introduced in PR #13836 (Introduce Plugins, 2025-02-17). We understand the model plugin path was kept minimal because models can be invoked outside of app context (RAG indexing, utility LLM calls, etc.). Passing app_id as a nullable field preserves this design — it is simply null for those calls.

Known limitation: For Chat/Completion apps using multiple knowledge bases with "single retrieval" (LLM-based routing), the routing LLM call does not have app_id because it is not threaded through the retrieval chain. This is a minor cost (a few hundred tokens per call) and can be addressed separately.

We have prepared PRs for both repositories and can submit them once the design direction is agreed upon.

3. Can you help us with this feature?

  • I am interested in contributing to this feature.

extent analysis

TL;DR

Add app_id as an optional field to the model plugin dispatch payload to enable cost tracking and attribution for LLM calls.

Guidance

  • Review the proposed change to add app_id to the model plugin dispatch payload, ensuring it is optional and defaults to null when called outside of an app context.
  • Verify that the plugin daemon (dify-plugin-daemon) can pass the app_id field through to the SDK session without requiring changes.
  • Update the plugin_executor.py file to forward the session to model plugin instances, similar to how it is done for tool plugins.
  • Test the change to ensure it is fully backward compatible with existing model plugins.

Example

# Updated model plugin dispatch (core/plugin/impl/model.py) — app_id is included as an optional field
data=jsonable_encoder({
    "user_id": user_id,
    "app_id": app_id,  # Optional field, defaults to null when not available
    "data": { ... },
})

Notes

The proposed change spans two repositories (langgenius/dify and langgenius/dify-plugin-sdks), and the authors have prepared PRs for both repositories. However, there is a known limitation for Chat/Completion apps using multiple knowledge bases with "single retrieval" (LLM-based routing), which can be addressed separately.

Recommendation

Apply the proposed workaround by adding app_id as an optional field to the model plugin dispatch payload, as it provides a straightforward solution to enable cost tracking and attribution for LLM calls while maintaining backward compatibility with existing model plugins.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING