Skip to content

Commit 43cff98

Browse files
authored
⚙️ refactor: OAuth Flow Signal, Type Safety, Tool Progress & Updated Packages (danny-avila#6752)
* chore: bump @librechat/agents and related packages * refactor: update message state for tool calls run step, in case no tool call chunks are received * fix: avoid combining finalized args createContentAggregator for tool calls * chore: bump @librechat/agents to version 2.3.99 * feat: add support for aborting flows with AbortSignal in createFlow methods * fix: improve handling of tool call arguments in useStepHandler * chore: bump @librechat/agents to version 2.4.0 * fix: update flow identifier format for OAuth login in createActionTool to allow uniqueness per run * fix: improve error message handling for aborted flows in FlowStateManager * refactor: allow possible multi-agent cross-over for oauth login * fix: add type safety for Sandpack files in ArtifactCodeEditor
1 parent 1f345a6 commit 43cff98

File tree

6 files changed

+1667
-1695
lines changed

6 files changed

+1667
-1695
lines changed

api/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@
4444
"@googleapis/youtube": "^20.0.0",
4545
"@keyv/mongo": "^2.1.8",
4646
"@keyv/redis": "^2.8.1",
47-
"@langchain/community": "^0.3.34",
48-
"@langchain/core": "^0.3.40",
49-
"@langchain/google-genai": "^0.1.11",
50-
"@langchain/google-vertexai": "^0.2.2",
47+
"@langchain/community": "^0.3.39",
48+
"@langchain/core": "^0.3.43",
49+
"@langchain/google-genai": "^0.2.2",
50+
"@langchain/google-vertexai": "^0.2.3",
5151
"@langchain/textsplitters": "^0.1.0",
52-
"@librechat/agents": "^2.3.95",
52+
"@librechat/agents": "^2.4.0",
5353
"@librechat/data-schemas": "*",
5454
"@waylaidwanderer/fetch-event-source": "^3.0.1",
5555
"axios": "^1.8.2",

api/server/services/ActionService.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,24 +191,30 @@ async function createActionTool({
191191
};
192192
const flowManager = await getFlowStateManager(getLogStores);
193193
await flowManager.createFlowWithHandler(
194-
`${identifier}:login`,
194+
`${identifier}:oauth_login:${config.metadata.thread_id}:${config.metadata.run_id}`,
195195
'oauth_login',
196196
async () => {
197197
sendEvent(res, { event: GraphEvents.ON_RUN_STEP_DELTA, data });
198198
logger.debug('Sent OAuth login request to client', { action_id, identifier });
199199
return true;
200200
},
201+
config?.signal,
201202
);
202203
logger.debug('Waiting for OAuth Authorization response', { action_id, identifier });
203-
const result = await flowManager.createFlow(identifier, 'oauth', {
204-
state: stateToken,
205-
userId: req.user.id,
206-
client_url: metadata.auth.client_url,
207-
redirect_uri: `${process.env.DOMAIN_CLIENT}/api/actions/${action_id}/oauth/callback`,
208-
/** Encrypted values */
209-
encrypted_oauth_client_id: encrypted.oauth_client_id,
210-
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
211-
});
204+
const result = await flowManager.createFlow(
205+
identifier,
206+
'oauth',
207+
{
208+
state: stateToken,
209+
userId: req.user.id,
210+
client_url: metadata.auth.client_url,
211+
redirect_uri: `${process.env.DOMAIN_CLIENT}/api/actions/${action_id}/oauth/callback`,
212+
/** Encrypted values */
213+
encrypted_oauth_client_id: encrypted.oauth_client_id,
214+
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
215+
},
216+
config?.signal,
217+
);
212218
logger.debug('Received OAuth Authorization response', { action_id, identifier });
213219
data.delta.auth = undefined;
214220
data.delta.expires_at = undefined;
@@ -264,6 +270,7 @@ async function createActionTool({
264270
`${identifier}:refresh`,
265271
'oauth_refresh',
266272
refreshTokens,
273+
config?.signal,
267274
);
268275
metadata.oauth_access_token = refreshData.access_token;
269276
if (refreshData.refresh_token) {

client/src/components/Artifacts/ArtifactCodeEditor.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
SandpackCodeEditor,
66
SandpackProvider as StyledProvider,
77
} from '@codesandbox/sandpack-react';
8-
import { SandpackProviderProps } from '@codesandbox/sandpack-react/unstyled';
8+
import type { SandpackProviderProps } from '@codesandbox/sandpack-react/unstyled';
9+
import type { SandpackBundlerFile } from '@codesandbox/sandpack-client';
910
import type { CodeEditorRef } from '@codesandbox/sandpack-react';
1011
import type { ArtifactFiles, Artifact } from '~/common';
1112
import { useEditArtifact, useGetStartupConfig } from '~/data-provider';
@@ -66,7 +67,7 @@ const CodeEditor = ({
6667
return;
6768
}
6869

69-
const currentCode = sandpack.files['/' + fileKey].code;
70+
const currentCode = (sandpack.files['/' + fileKey] as SandpackBundlerFile | undefined)?.code;
7071

7172
if (currentCode && artifact.content != null && currentCode.trim() !== artifact.content.trim()) {
7273
setCurrentCode(currentCode);

client/src/hooks/SSE/useStepHandler.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,14 @@ export default function useStepHandler({
123123
} else if (contentType === ContentTypes.TOOL_CALL && 'tool_call' in contentPart) {
124124
const existingContent = updatedContent[index] as Agents.ToolCallContent | undefined;
125125
const existingToolCall = existingContent?.tool_call;
126-
const toolCallArgs = (contentPart.tool_call.args as unknown as string | undefined) ?? '';
127-
128-
const args = finalUpdate
129-
? contentPart.tool_call.args
130-
: (existingToolCall?.args ?? '') + toolCallArgs;
126+
const toolCallArgs = (contentPart.tool_call as Agents.ToolCall).args;
127+
/** When args are a valid object, they are likely already invoked */
128+
const args =
129+
finalUpdate ||
130+
typeof existingToolCall?.args === 'object' ||
131+
typeof toolCallArgs === 'object'
132+
? contentPart.tool_call.args
133+
: (existingToolCall?.args ?? '') + (toolCallArgs ?? '');
131134

132135
const id = getNonEmptyValue([contentPart.tool_call.id, existingToolCall?.id]) ?? '';
133136
const name = getNonEmptyValue([contentPart.tool_call.name, existingToolCall?.name]) ?? '';
@@ -195,12 +198,31 @@ export default function useStepHandler({
195198

196199
// Store tool call IDs if present
197200
if (runStep.stepDetails.type === StepTypes.TOOL_CALLS) {
198-
runStep.stepDetails.tool_calls.forEach((toolCall) => {
201+
let updatedResponse = { ...response };
202+
(runStep.stepDetails.tool_calls as Agents.ToolCall[]).forEach((toolCall) => {
199203
const toolCallId = toolCall.id ?? '';
200204
if ('id' in toolCall && toolCallId) {
201205
toolCallIdMap.current.set(runStep.id, toolCallId);
202206
}
207+
208+
const contentPart: Agents.MessageContentComplex = {
209+
type: ContentTypes.TOOL_CALL,
210+
tool_call: {
211+
name: toolCall.name ?? '',
212+
args: toolCall.args,
213+
id: toolCallId,
214+
},
215+
};
216+
217+
updatedResponse = updateContent(updatedResponse, runStep.index, contentPart);
203218
});
219+
220+
messageMap.current.set(responseMessageId, updatedResponse);
221+
const updatedMessages = messages.map((msg) =>
222+
msg.messageId === runStep.runId ? updatedResponse : msg,
223+
);
224+
225+
setMessages(updatedMessages);
204226
}
205227
} else if (event === 'on_agent_update') {
206228
const { agent_update } = data as Agents.AgentUpdate;

0 commit comments

Comments
 (0)