mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
🧷 fix: Keep Group Identity Stable and Memoize Label Endpoint Resolution
Group remount. Tool-group identity was keyed on the first part in the block. An activity label absorbs the block's leading THINK part the moment its text lands, so the key flipped from tool:<id> to fallback:<scope>:<idx> mid-run, remounting the group and discarding whatever the user had expanded. The key now scans for the first tool call, which does not move when the block re-forms. Label endpoint resolution is memoized per response. It reads provider config and can hit the database for user keys, yet nothing it depends on changes between batches of one run — and it ran twice per batch, once for generation and once for usage accounting. The promise is cached rather than the value so concurrent batches share a single in-flight resolution, and a rejection is evicted so one transient credential failure cannot disable labels for the rest of the response.
This commit is contained in:
parent
d89f8368e8
commit
5c60f77bfb
2 changed files with 33 additions and 13 deletions
|
|
@ -358,16 +358,30 @@ class AgentClient extends BaseClient {
|
|||
* endpoint named by `activityEndpoint` (default: the agent's).
|
||||
*/
|
||||
async resolveActivityLabelLLM() {
|
||||
return resolveActivityLabelModel({
|
||||
req: this.options.req,
|
||||
agent: this.options.agent,
|
||||
ids: {
|
||||
messageId: this.responseMessageId,
|
||||
conversationId: this.conversationId,
|
||||
parentMessageId: this.parentMessageId,
|
||||
},
|
||||
db: { getUserKey: db.getUserKey, getUserKeyValues: db.getUserKeyValues },
|
||||
});
|
||||
/** Memoized per response: resolution reads provider config and can hit the
|
||||
* database for user keys, and nothing it depends on changes between
|
||||
* batches of the same run — so re-resolving on every batch (twice, with
|
||||
* usage accounting) is repeated credential work for an identical result.
|
||||
* The promise is cached rather than the value so concurrent batches share
|
||||
* one in-flight resolution. */
|
||||
this.activityLabelLLMPromise =
|
||||
this.activityLabelLLMPromise ??
|
||||
resolveActivityLabelModel({
|
||||
req: this.options.req,
|
||||
agent: this.options.agent,
|
||||
ids: {
|
||||
messageId: this.responseMessageId,
|
||||
conversationId: this.conversationId,
|
||||
parentMessageId: this.parentMessageId,
|
||||
},
|
||||
db: { getUserKey: db.getUserKey, getUserKeyValues: db.getUserKeyValues },
|
||||
}).catch((error) => {
|
||||
/** Never cache a rejection: a transient credential read failure would
|
||||
* otherwise disable labels for the rest of the response. */
|
||||
this.activityLabelLLMPromise = null;
|
||||
throw error;
|
||||
});
|
||||
return this.activityLabelLLMPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -30,9 +30,15 @@ const getToolGroupId = (parts: PartWithIndex[], fallbackScope: number): string =
|
|||
if (!firstPart) {
|
||||
return 'empty';
|
||||
}
|
||||
const toolCallId = getToolCallId(firstPart.part);
|
||||
if (toolCallId) {
|
||||
return `tool:${toolCallId}`;
|
||||
/** Keyed on the first TOOL CALL, not the first part. An activity label
|
||||
* absorbs the block's leading THINK part when its text lands, so keying on
|
||||
* `parts[0]` would flip the key mid-run — remounting the group and losing
|
||||
* whatever the user had expanded. The tool calls themselves do not move. */
|
||||
for (const { part } of parts) {
|
||||
const toolCallId = getToolCallId(part);
|
||||
if (toolCallId) {
|
||||
return `tool:${toolCallId}`;
|
||||
}
|
||||
}
|
||||
return `fallback:${fallbackScope}:${firstPart.idx}`;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue