[api][plan][python] Use request-scoped metric groups for chat token metrics#861
[api][plan][python] Use request-scoped metric groups for chat token metrics#861jlon wants to merge 2 commits into
Conversation
| this resource's currently bound metric group is used. | ||
| """ | ||
| metric_group = self.metric_group | ||
| if metric_group is None: |
There was a problem hiding this comment.
The two languages now document slightly different contracts for the absent-group case: Java's 4-arg recordTokenMetrics treats a null group as "skip recording" (BaseChatModelSetup.java:135), while here metric_group is None falls back to self.metric_group — the live, possibly-rebound group. Every real call site passes a captured group (chat_model_action.py:334), so there's no impact on today's flow. The part I'm less sure about: a future caller passing None expecting Java parity would record under whatever action is currently bound — the #859 scenario again on the Python side, where Java would no-op instead. Is the asymmetry intentional? If the = None default is only there to keep the param optional for the test helper, dropping it so callers must pass the captured group would match Java's no-fallback contract; alternatively a one-line javadoc note that Java's null means skip would at least make each side self-documenting.
There was a problem hiding this comment.
Updated in ecd96c5. _record_token_metrics now requires the metric group argument and no longer falls back to the resource-bound self.metric_group. The action path also skips token metric recording when no request/action metric group is available.
wenjin272
left a comment
There was a problem hiding this comment.
Thanks for taking this on @jlon.
Since Resource instances are shared across multiple Actions, reporting metrics using the metricGroup bound at creation time can lead to incorrect metric matching. This PR addresses the issue by passing the current Action’s metricGroup at reporting time, ensuring metrics are reported to the correct Action scope.
In this case, I think Resource no longer need to hold a metricGroup field.
| @@ -116,7 +116,22 @@ public void open() throws Exception { | |||
| * @param completionTokens the number of completion tokens | |||
| */ | |||
| public void recordTokenMetrics(String modelName, long promptTokens, long completionTokens) { | |||
There was a problem hiding this comment.
It appears that only tests call this method, so I think it can be removed directly.
There was a problem hiding this comment.
Updated in ecd96c5. Removed the 3-arg Java recordTokenMetrics(...) overload and updated the tests to pass an explicit request/action metric group.
| * @param completionTokens the number of completion tokens | ||
| */ | ||
| public void recordTokenMetrics( | ||
| @Nullable FlinkAgentsMetricGroup metricGroup, |
There was a problem hiding this comment.
In practice, we now require that the metricGroup of the current action be passed in when reporting metrics. Therefore, I believe metricGroup should not be nullable.
There was a problem hiding this comment.
Updated in ecd96c5. The Java recordTokenMetrics(...) metric group parameter is no longer nullable, and ChatModelAction now skips token metric recording before calling it when the action metric group is absent.
I agree. This PR fixes the concrete bug locally for chat token metrics by passing the request/action metric group at reporting time, instead of reading the mutable metric group currently bound to the cached resource. But the underlying model issue is broader than this one recording path. A cleaner abstraction would be to separate the stable resource instance from the per-action resource-use context:
One possible shape is for Under that model, This is probably a larger refactor than this PR should take on, but I agree with the direction: action-scoped metric state should not be owned by the cached |
|
Pushed ecd96c5 to address the review comments:
I kept the broader Local verification:
|
wenjin272
left a comment
There was a problem hiding this comment.
I agree with excluding the removal of metricGroup bindings from this PR. We should create a separate issue to evaluate this holistically.
I’ve left one comment regarding this PR.
| metrics are skipped. | ||
| """ | ||
| metric_group = self.metric_group | ||
| if metric_group is None: |
There was a problem hiding this comment.
One parity question: Python _record_token_metrics(..., metric_group=None) is a no-op, while Java recordTokenMetrics(null, ...) fails via checkNotNull. Should we align the lower-level helper contract?
# 影响范围 - Java/Python `BaseEmbeddingModelConnection` 与 `BaseEmbeddingModelSetup` 保留 `embedWithUsage`/`embed_with_usage` 的结果携带链路;普通 `embed` API 的返回类型不变。 - `BaseVectorStore.query`、自动补 embedding 以及 Python 对应实现恢复仅调用 `embed`,不再进入指标组或计数器。 - OpenAI、Tongyi、Bedrock 的 provider usage 提取及批量聚合能力保持不变;测试改为断言 usage 结果本身。 # 改动影响面 - 主链路:embedding provider -> `EmbeddingResult` -> 调用方显式消费 usage;本提交不再把 usage 写入 `MetricGroup`。 - 向量库扩展点:恢复 `BaseEmbeddingModelSetup.embed` 覆盖语义,并且仅在确有缺失 embedding 时延迟解析模型,避免 Mem0 无模型路径报错。 - 不受影响范围:apache#860/apache#861 的 resource metric-group 传播和 action-scoped metric 语义未改动;未引入新的 metric abstraction。 # 功能改进/开发/新增 - 类型: refactor - 触发条件: 普通资源 API 可并发调用,无法安全保证 `MetricGroup`/`SimpleCounter` 的线程模型;Python vector-store 还绕过了 `embed` 扩展点。 - 根因类别: 指标写入边界不明确、公共 API 扩展点回归、惰性资源解析边界遗漏。 - 行为变化: 有;PR 仅暴露 provider usage 返回 API,不再自动记录 embedding token metrics。 # 验证 - `mvn --batch-mode --no-transfer-progress -pl api -Dtest=BaseEmbeddingModelSetupEmbeddingResultTest test` PASS - `mvn --batch-mode --no-transfer-progress -pl integrations/embedding-models/bedrock -am -Dtest=BedrockEmbeddingModelTest -Dsurefire.failIfNoSpecifiedTests=false test` PASS - `uv run --python 3.12 --extra test pytest ...vector_stores... -q` PASS (49 passed, 4 skipped) - `./tools/lint.sh --check` PASS - `uv run --python 3.12 --extra lint ruff format --check ... && uv run --python 3.12 --extra lint ruff check ...` PASS - `git diff --check` PASS
What
Why
Cached resources can be rebound when another action retrieves the same resource before the original request records token usage. The delayed token metrics should stay under the action that initiated the request.
Closes #859.
Tests