Skip to content

Pass app_id to model plugins for provider-side cost attribution #35772

@ryuta-kobayashi-ug

Description

@ryuta-kobayashi-ug

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions