Self Checks
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?
Self Checks
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
requestMetadataparameter 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 receiveapp_id— onlyuser_idis 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:
requestMetadata(Converse API) → CloudWatch Logsmetadata(Chat Completions API) → Usage Dashboardlabels(generateContent API) → directly reflected in Cloud Billingmetadata(Messages API)None of these can be utilized from a Dify model plugin today because
app_idis not passed.Meanwhile, tool plugins already receive
app_id(along withconversation_idandmessage_id) via the session. The model plugin dispatch path simply does not include it:Proposal: Add
app_idto the model plugin dispatch payload, as an optional field (nullwhen called outside app context, e.g., RAG routing, title generation, suggested questions).We are only proposing
app_id— notconversation_id,message_id, orworkflow_run_id. These IDs vary by app type (e.g., Workflow apps haveworkflow_run_idinstead ofconversation_id), which would make the change more complex. Withapp_idon 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) usingapp_idas 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_idas a nullable field preserves this design — it is simplynullfor 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_idbecause 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?