@librechat/agents 3.1.67 → 3.1.68-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +23 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +16 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +91 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +36 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +162 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -0
- package/dist/cjs/hooks/executeHooks.cjs +276 -0
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -0
- package/dist/cjs/hooks/matchers.cjs +256 -0
- package/dist/cjs/hooks/matchers.cjs.map +1 -0
- package/dist/cjs/hooks/types.cjs +27 -0
- package/dist/cjs/hooks/types.cjs.map +1 -0
- package/dist/cjs/main.cjs +54 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +74 -12
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +111 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs +41 -0
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +165 -19
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +165 -0
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +287 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/CodeExecutor.cjs +0 -9
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +7 -23
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +43 -0
- package/dist/cjs/tools/ReadFile.cjs.map +1 -0
- package/dist/cjs/tools/SkillTool.cjs +50 -0
- package/dist/cjs/tools/SkillTool.cjs.map +1 -0
- package/dist/cjs/tools/SubagentTool.cjs +92 -0
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +304 -140
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +2 -13
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/skillCatalog.cjs +84 -0
- package/dist/cjs/tools/skillCatalog.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +511 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +23 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +15 -2
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +91 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +36 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +160 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -0
- package/dist/esm/hooks/executeHooks.mjs +273 -0
- package/dist/esm/hooks/executeHooks.mjs.map +1 -0
- package/dist/esm/hooks/matchers.mjs +251 -0
- package/dist/esm/hooks/matchers.mjs.map +1 -0
- package/dist/esm/hooks/types.mjs +25 -0
- package/dist/esm/hooks/types.mjs.map +1 -0
- package/dist/esm/main.mjs +13 -2
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +66 -4
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +111 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs +41 -1
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +165 -19
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +159 -0
- package/dist/esm/tools/BashExecutor.mjs.map +1 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +278 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/CodeExecutor.mjs +0 -9
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +8 -24
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +38 -0
- package/dist/esm/tools/ReadFile.mjs.map +1 -0
- package/dist/esm/tools/SkillTool.mjs +45 -0
- package/dist/esm/tools/SkillTool.mjs.map +1 -0
- package/dist/esm/tools/SubagentTool.mjs +85 -0
- package/dist/esm/tools/SubagentTool.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +306 -142
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +3 -14
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/skillCatalog.mjs +82 -0
- package/dist/esm/tools/skillCatalog.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +505 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +6 -0
- package/dist/types/common/enum.d.ts +10 -2
- package/dist/types/graphs/Graph.d.ts +2 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +12 -0
- package/dist/types/hooks/HookRegistry.d.ts +56 -0
- package/dist/types/hooks/executeHooks.d.ts +79 -0
- package/dist/types/hooks/index.d.ts +6 -0
- package/dist/types/hooks/matchers.d.ts +95 -0
- package/dist/types/hooks/types.d.ts +320 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/messages/format.d.ts +2 -1
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/index.d.ts +2 -0
- package/dist/types/summarization/node.d.ts +2 -0
- package/dist/types/tools/BashExecutor.d.ts +45 -0
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +72 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +4 -9
- package/dist/types/tools/ReadFile.d.ts +28 -0
- package/dist/types/tools/SkillTool.d.ts +40 -0
- package/dist/types/tools/SubagentTool.d.ts +36 -0
- package/dist/types/tools/ToolNode.d.ts +24 -2
- package/dist/types/tools/ToolSearch.d.ts +2 -2
- package/dist/types/tools/skillCatalog.d.ts +19 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +137 -0
- package/dist/types/tools/subagent/index.d.ts +2 -0
- package/dist/types/types/graph.d.ts +61 -2
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/run.d.ts +20 -0
- package/dist/types/types/skill.d.ts +9 -0
- package/dist/types/types/tools.d.ts +38 -10
- package/package.json +5 -1
- package/src/agents/AgentContext.ts +26 -2
- package/src/common/enum.ts +15 -1
- package/src/graphs/Graph.ts +113 -0
- package/src/graphs/MultiAgentGraph.ts +39 -0
- package/src/graphs/__tests__/MultiAgentGraph.test.ts +91 -0
- package/src/hooks/HookRegistry.ts +208 -0
- package/src/hooks/__tests__/HookRegistry.test.ts +190 -0
- package/src/hooks/__tests__/compactHooks.test.ts +214 -0
- package/src/hooks/__tests__/executeHooks.test.ts +1013 -0
- package/src/hooks/__tests__/integration.test.ts +337 -0
- package/src/hooks/__tests__/matchers.test.ts +238 -0
- package/src/hooks/__tests__/toolHooks.test.ts +669 -0
- package/src/hooks/executeHooks.ts +375 -0
- package/src/hooks/index.ts +57 -0
- package/src/hooks/matchers.ts +280 -0
- package/src/hooks/types.ts +404 -0
- package/src/index.ts +10 -0
- package/src/messages/format.ts +74 -4
- package/src/messages/formatAgentMessages.skills.test.ts +334 -0
- package/src/run.ts +126 -0
- package/src/scripts/multi-agent-subagent.ts +246 -0
- package/src/scripts/programmatic_exec.ts +1 -10
- package/src/scripts/subagent-event-driven-debug.ts +190 -0
- package/src/scripts/subagent-tools-debug.ts +160 -0
- package/src/scripts/test_code_api.ts +0 -7
- package/src/scripts/tool_search.ts +1 -10
- package/src/specs/subagent.test.ts +305 -0
- package/src/summarization/__tests__/node.test.ts +42 -0
- package/src/summarization/__tests__/trigger.test.ts +100 -1
- package/src/summarization/index.ts +47 -0
- package/src/summarization/node.ts +202 -24
- package/src/tools/BashExecutor.ts +193 -0
- package/src/tools/BashProgrammaticToolCalling.ts +381 -0
- package/src/tools/CodeExecutor.ts +0 -11
- package/src/tools/ProgrammaticToolCalling.ts +4 -29
- package/src/tools/ReadFile.ts +39 -0
- package/src/tools/SkillTool.ts +46 -0
- package/src/tools/SubagentTool.ts +100 -0
- package/src/tools/ToolNode.ts +391 -169
- package/src/tools/ToolSearch.ts +3 -19
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +7 -8
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -1
- package/src/tools/__tests__/ReadFile.test.ts +44 -0
- package/src/tools/__tests__/SkillTool.test.ts +442 -0
- package/src/tools/__tests__/SubagentExecutor.test.ts +1148 -0
- package/src/tools/__tests__/SubagentTool.test.ts +149 -0
- package/src/tools/__tests__/ToolNode.session.test.ts +12 -12
- package/src/tools/__tests__/ToolSearch.integration.test.ts +7 -8
- package/src/tools/__tests__/skillCatalog.test.ts +161 -0
- package/src/tools/__tests__/subagentHooks.test.ts +215 -0
- package/src/tools/skillCatalog.ts +126 -0
- package/src/tools/subagent/SubagentExecutor.ts +676 -0
- package/src/tools/subagent/index.ts +13 -0
- package/src/types/graph.ts +80 -1
- package/src/types/index.ts +1 -0
- package/src/types/run.ts +20 -0
- package/src/types/skill.ts +11 -0
- package/src/types/tools.ts +41 -10
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
8
8
|
import type { UsageMetadata, BaseMessage } from '@langchain/core/messages';
|
|
9
9
|
import type { AgentContext } from '@/agents/AgentContext';
|
|
10
|
+
import type { HookRegistry } from '@/hooks';
|
|
10
11
|
import type { OnChunk } from '@/llm/invoke';
|
|
11
12
|
import type * as t from '@/types';
|
|
12
13
|
import { ContentTypes, GraphEvents, StepTypes, Providers } from '@/common';
|
|
@@ -17,6 +18,7 @@ import { getMaxOutputTokensKey } from '@/llm/request';
|
|
|
17
18
|
import { addCacheControl } from '@/messages/cache';
|
|
18
19
|
import { initializeModel } from '@/llm/init';
|
|
19
20
|
import { getChunkContent } from '@/stream';
|
|
21
|
+
import { executeHooks } from '@/hooks';
|
|
20
22
|
|
|
21
23
|
const SUMMARIZATION_PARAM_KEYS = new Set(['maxSummaryTokens']);
|
|
22
24
|
|
|
@@ -364,6 +366,122 @@ type LogFn = (
|
|
|
364
366
|
data?: Record<string, unknown>
|
|
365
367
|
) => void;
|
|
366
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Extracts an HTTP status code from a thrown LLM-provider error. Returns
|
|
371
|
+
* `undefined` for non-object values (including `null` or `undefined`, both
|
|
372
|
+
* valid `throw` targets in JS) so callers never dereference a nullish
|
|
373
|
+
* value.
|
|
374
|
+
*/
|
|
375
|
+
function extractHttpStatus(err: unknown): number | undefined {
|
|
376
|
+
if (err == null || typeof err !== 'object') {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
const errRecord = err as Record<string, unknown>;
|
|
380
|
+
const direct = errRecord.status;
|
|
381
|
+
if (typeof direct === 'number') {
|
|
382
|
+
return direct;
|
|
383
|
+
}
|
|
384
|
+
const statusCode = errRecord.statusCode;
|
|
385
|
+
if (typeof statusCode === 'number') {
|
|
386
|
+
return statusCode;
|
|
387
|
+
}
|
|
388
|
+
const response = errRecord.response;
|
|
389
|
+
if (response != null && typeof response === 'object') {
|
|
390
|
+
const nested = (response as Record<string, unknown>).status;
|
|
391
|
+
if (typeof nested === 'number') {
|
|
392
|
+
return nested;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Formats a provider-level error for logging. Returns both a human-readable
|
|
400
|
+
* suffix (safe to include in the message string so it survives any host-side
|
|
401
|
+
* formatter) and a structured metadata bag for rich log backends.
|
|
402
|
+
*/
|
|
403
|
+
function describeProviderError(
|
|
404
|
+
err: unknown,
|
|
405
|
+
provider: string,
|
|
406
|
+
modelName?: string
|
|
407
|
+
): { suffix: string; data: Record<string, unknown> } {
|
|
408
|
+
const providerLabel = `${provider}/${modelName ?? '(no-model)'}`;
|
|
409
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
410
|
+
|
|
411
|
+
const data: Record<string, unknown> = {
|
|
412
|
+
provider,
|
|
413
|
+
model: modelName,
|
|
414
|
+
};
|
|
415
|
+
if (err instanceof Error) {
|
|
416
|
+
data.errorName = err.name;
|
|
417
|
+
data.errorStack = err.stack;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const status = extractHttpStatus(err);
|
|
421
|
+
const statusSuffix = status != null ? ` (HTTP ${status})` : '';
|
|
422
|
+
if (status != null) {
|
|
423
|
+
data.status = status;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
suffix: `[${providerLabel}]${statusSuffix}: ${errMsg}`,
|
|
428
|
+
data,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Formats an exhausted-fallback error. `tryFallbackProviders` throws the
|
|
434
|
+
* last fallback provider's error, which may be from any of the configured
|
|
435
|
+
* fallbacks — not the primary — so we label the log with the list of
|
|
436
|
+
* fallback providers attempted rather than mis-attributing to the primary.
|
|
437
|
+
*
|
|
438
|
+
* Entries in `fallbacks` are normally strongly typed, but we defend against
|
|
439
|
+
* malformed runtime config (null/undefined entries, missing `provider`
|
|
440
|
+
* field) so a recoverable summarization failure is never promoted to an
|
|
441
|
+
* uncaught exception from inside the logging path.
|
|
442
|
+
*/
|
|
443
|
+
function describeFallbackError(
|
|
444
|
+
err: unknown,
|
|
445
|
+
fallbacks: unknown
|
|
446
|
+
): { suffix: string; data: Record<string, unknown> } {
|
|
447
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
448
|
+
const list: ReadonlyArray<unknown> = Array.isArray(fallbacks)
|
|
449
|
+
? fallbacks
|
|
450
|
+
: [];
|
|
451
|
+
const providerNames = list
|
|
452
|
+
.map((f) => {
|
|
453
|
+
if (f == null || typeof f !== 'object') {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
const raw = (f as { provider?: unknown }).provider;
|
|
457
|
+
return raw != null ? String(raw) : undefined;
|
|
458
|
+
})
|
|
459
|
+
.filter((p): p is string => typeof p === 'string');
|
|
460
|
+
const label =
|
|
461
|
+
providerNames.length > 0
|
|
462
|
+
? `fallbacks=[${providerNames.join(',')}]`
|
|
463
|
+
: 'no-fallbacks';
|
|
464
|
+
|
|
465
|
+
const data: Record<string, unknown> = {
|
|
466
|
+
fallbackProviders: providerNames,
|
|
467
|
+
fallbackCount: list.length,
|
|
468
|
+
};
|
|
469
|
+
if (err instanceof Error) {
|
|
470
|
+
data.errorName = err.name;
|
|
471
|
+
data.errorStack = err.stack;
|
|
472
|
+
}
|
|
473
|
+
const status = extractHttpStatus(err);
|
|
474
|
+
const statusSuffix = status != null ? ` (HTTP ${status})` : '';
|
|
475
|
+
if (status != null) {
|
|
476
|
+
data.status = status;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
suffix: `[${label}]${statusSuffix}: ${errMsg}`,
|
|
481
|
+
data,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
367
485
|
/**
|
|
368
486
|
* Runs the summarization LLM call with primary + fallback providers,
|
|
369
487
|
* falling back to a metadata stub when all calls fail.
|
|
@@ -387,18 +505,23 @@ async function executeSummarizationWithFallback(params: {
|
|
|
387
505
|
log,
|
|
388
506
|
} = params;
|
|
389
507
|
|
|
390
|
-
const summarizationModel = initializeModel({
|
|
391
|
-
provider: clientConfig.provider as Providers,
|
|
392
|
-
clientOptions: clientConfig.clientOptions as t.ClientOptions,
|
|
393
|
-
tools: agentContext.getToolsForBinding(),
|
|
394
|
-
}) as t.ChatModel;
|
|
395
|
-
|
|
396
508
|
const priorSummaryText = agentContext.getSummaryText()?.trim() ?? '';
|
|
397
509
|
|
|
398
510
|
let summaryText = '';
|
|
399
511
|
let summaryUsage: Partial<UsageMetadata> | undefined;
|
|
400
512
|
|
|
401
513
|
try {
|
|
514
|
+
/**
|
|
515
|
+
* Initialize inside the try so that a misconfigured provider
|
|
516
|
+
* (e.g. an unrecognized summarization.provider) surfaces through the
|
|
517
|
+
* `log('error', ...)` path below rather than bubbling up silently.
|
|
518
|
+
*/
|
|
519
|
+
const summarizationModel = initializeModel({
|
|
520
|
+
provider: clientConfig.provider as Providers,
|
|
521
|
+
clientOptions: clientConfig.clientOptions as t.ClientOptions,
|
|
522
|
+
tools: agentContext.getToolsForBinding(),
|
|
523
|
+
}) as t.ChatModel;
|
|
524
|
+
|
|
402
525
|
const result = await summarizeWithCacheHit({
|
|
403
526
|
model: summarizationModel,
|
|
404
527
|
messages,
|
|
@@ -415,19 +538,20 @@ async function executeSummarizationWithFallback(params: {
|
|
|
415
538
|
summaryText = result.text;
|
|
416
539
|
summaryUsage = result.usage;
|
|
417
540
|
} catch (primaryError) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
541
|
+
const primaryDescribed = describeProviderError(
|
|
542
|
+
primaryError,
|
|
543
|
+
clientConfig.provider,
|
|
544
|
+
clientConfig.modelName
|
|
545
|
+
);
|
|
546
|
+
log('error', `Summarization LLM call failed ${primaryDescribed.suffix}`, {
|
|
547
|
+
...primaryDescribed.data,
|
|
425
548
|
messagesToRefineCount: messages.length,
|
|
426
549
|
});
|
|
427
550
|
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
551
|
+
const rawFallbacks = (
|
|
552
|
+
clientConfig.clientOptions as unknown as t.LLMConfig | undefined
|
|
553
|
+
)?.fallbacks;
|
|
554
|
+
const fallbacks = Array.isArray(rawFallbacks) ? rawFallbacks : [];
|
|
431
555
|
if (fallbacks.length > 0) {
|
|
432
556
|
try {
|
|
433
557
|
const onChunk = createSummarizationChunkHandler({
|
|
@@ -460,18 +584,21 @@ async function executeSummarizationWithFallback(params: {
|
|
|
460
584
|
);
|
|
461
585
|
}
|
|
462
586
|
} catch (fbErr) {
|
|
463
|
-
|
|
464
|
-
|
|
587
|
+
const fbDescribed = describeFallbackError(fbErr, fallbacks);
|
|
588
|
+
log('warn', `Fallback providers also failed ${fbDescribed.suffix}`, {
|
|
589
|
+
...fbDescribed.data,
|
|
465
590
|
});
|
|
466
591
|
}
|
|
467
592
|
}
|
|
468
593
|
if (!summaryText) {
|
|
469
|
-
log(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
594
|
+
log(
|
|
595
|
+
'warn',
|
|
596
|
+
`Summarization failed, falling back to metadata stub ${primaryDescribed.suffix}`,
|
|
597
|
+
{
|
|
598
|
+
...primaryDescribed.data,
|
|
599
|
+
messagesToRefineCount: messages.length,
|
|
600
|
+
}
|
|
601
|
+
);
|
|
475
602
|
summaryText = generateMetadataStub(messages);
|
|
476
603
|
}
|
|
477
604
|
}
|
|
@@ -530,6 +657,35 @@ async function dispatchCompletionEvents(params: {
|
|
|
530
657
|
);
|
|
531
658
|
}
|
|
532
659
|
|
|
660
|
+
const sessionId = graph.runId ?? '';
|
|
661
|
+
if (graph.hookRegistry?.hasHookFor('PostCompact', sessionId) === true) {
|
|
662
|
+
const threadId = (
|
|
663
|
+
runnableConfig?.configurable as Record<string, unknown> | undefined
|
|
664
|
+
)?.thread_id as string | undefined;
|
|
665
|
+
const firstBlock = summaryBlock.content?.[0];
|
|
666
|
+
const summaryText =
|
|
667
|
+
firstBlock != null &&
|
|
668
|
+
typeof firstBlock === 'object' &&
|
|
669
|
+
'text' in firstBlock &&
|
|
670
|
+
typeof firstBlock.text === 'string'
|
|
671
|
+
? firstBlock.text
|
|
672
|
+
: '';
|
|
673
|
+
await executeHooks({
|
|
674
|
+
registry: graph.hookRegistry,
|
|
675
|
+
input: {
|
|
676
|
+
hook_event_name: 'PostCompact',
|
|
677
|
+
runId: sessionId,
|
|
678
|
+
threadId,
|
|
679
|
+
agentId,
|
|
680
|
+
summary: summaryText,
|
|
681
|
+
messagesAfterCount: 0,
|
|
682
|
+
},
|
|
683
|
+
sessionId,
|
|
684
|
+
}).catch(() => {
|
|
685
|
+
/* PostCompact is observational — swallow errors */
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
533
689
|
agentContext.rebuildTokenMapAfterSummarization({});
|
|
534
690
|
}
|
|
535
691
|
|
|
@@ -545,6 +701,7 @@ interface CreateSummarizeNodeParams {
|
|
|
545
701
|
config?: RunnableConfig;
|
|
546
702
|
runId?: string;
|
|
547
703
|
isMultiAgent: boolean;
|
|
704
|
+
hookRegistry?: HookRegistry;
|
|
548
705
|
dispatchRunStep: (
|
|
549
706
|
runStep: t.RunStep,
|
|
550
707
|
config?: RunnableConfig
|
|
@@ -650,6 +807,27 @@ export function createSummarizeNode({
|
|
|
650
807
|
);
|
|
651
808
|
}
|
|
652
809
|
|
|
810
|
+
const sessionId = graph.runId ?? '';
|
|
811
|
+
if (graph.hookRegistry?.hasHookFor('PreCompact', sessionId) === true) {
|
|
812
|
+
const threadId = (
|
|
813
|
+
runnableConfig?.configurable as Record<string, unknown> | undefined
|
|
814
|
+
)?.thread_id as string | undefined;
|
|
815
|
+
await executeHooks({
|
|
816
|
+
registry: graph.hookRegistry,
|
|
817
|
+
input: {
|
|
818
|
+
hook_event_name: 'PreCompact',
|
|
819
|
+
runId: sessionId,
|
|
820
|
+
threadId,
|
|
821
|
+
agentId: request.agentId,
|
|
822
|
+
messagesBeforeCount: messagesToRefine.length,
|
|
823
|
+
trigger: agentContext.summarizationConfig?.trigger?.type ?? 'default',
|
|
824
|
+
},
|
|
825
|
+
sessionId,
|
|
826
|
+
}).catch(() => {
|
|
827
|
+
/* PreCompact is observational — swallow errors */
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
|
|
653
831
|
const isSelfSummarizeModel =
|
|
654
832
|
clientConfig.provider === (agentContext.provider as string);
|
|
655
833
|
const hasPromptCache =
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
import fetch, { RequestInit } from 'node-fetch';
|
|
3
|
+
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
4
|
+
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
5
|
+
import type * as t from '@/types';
|
|
6
|
+
import { imageExtRegex, getCodeBaseURL } from './CodeExecutor';
|
|
7
|
+
import { Constants } from '@/common';
|
|
8
|
+
|
|
9
|
+
config();
|
|
10
|
+
|
|
11
|
+
const imageMessage = 'Image is already displayed to the user';
|
|
12
|
+
const otherMessage = 'File is already downloaded by the user';
|
|
13
|
+
const accessMessage =
|
|
14
|
+
'Note: Files from previous executions are automatically available and can be modified.';
|
|
15
|
+
const emptyOutputMessage =
|
|
16
|
+
'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
17
|
+
|
|
18
|
+
const baseEndpoint = getCodeBaseURL();
|
|
19
|
+
const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
20
|
+
|
|
21
|
+
export const BashExecutionToolSchema = {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
command: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: `The bash command or script to execute.
|
|
27
|
+
- The environment is stateless; variables and state don't persist between executions.
|
|
28
|
+
- Generated files from previous executions are automatically available in "/mnt/data/".
|
|
29
|
+
- Files from previous executions are automatically available and can be modified in place.
|
|
30
|
+
- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.
|
|
31
|
+
- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.
|
|
32
|
+
- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.
|
|
33
|
+
- Use \`echo\`, \`printf\`, or \`cat\` for all outputs.`,
|
|
34
|
+
},
|
|
35
|
+
args: {
|
|
36
|
+
type: 'array',
|
|
37
|
+
items: { type: 'string' },
|
|
38
|
+
description:
|
|
39
|
+
'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ['command'],
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
export const BashExecutionToolDescription = `
|
|
46
|
+
Runs bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
|
|
47
|
+
|
|
48
|
+
Usage:
|
|
49
|
+
- No network access available.
|
|
50
|
+
- Generated files are automatically delivered; **DO NOT** provide download links.
|
|
51
|
+
- NEVER use this tool to execute malicious commands.
|
|
52
|
+
`.trim();
|
|
53
|
+
|
|
54
|
+
export const BashExecutionToolName = Constants.BASH_TOOL;
|
|
55
|
+
|
|
56
|
+
export const BashExecutionToolDefinition = {
|
|
57
|
+
name: BashExecutionToolName,
|
|
58
|
+
description: BashExecutionToolDescription,
|
|
59
|
+
schema: BashExecutionToolSchema,
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
62
|
+
function createBashExecutionTool(
|
|
63
|
+
params: t.BashExecutionToolParams = {}
|
|
64
|
+
): DynamicStructuredTool {
|
|
65
|
+
return tool(
|
|
66
|
+
async (rawInput, config) => {
|
|
67
|
+
const { command, ...rest } = rawInput as {
|
|
68
|
+
command: string;
|
|
69
|
+
args?: string[];
|
|
70
|
+
};
|
|
71
|
+
const { session_id, _injected_files } = (config.toolCall ?? {}) as {
|
|
72
|
+
session_id?: string;
|
|
73
|
+
_injected_files?: t.CodeEnvFile[];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const postData: Record<string, unknown> = {
|
|
77
|
+
lang: 'bash',
|
|
78
|
+
code: command,
|
|
79
|
+
...rest,
|
|
80
|
+
...params,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (_injected_files && _injected_files.length > 0) {
|
|
84
|
+
postData.files = _injected_files;
|
|
85
|
+
} else if (session_id != null && session_id.length > 0) {
|
|
86
|
+
try {
|
|
87
|
+
const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;
|
|
88
|
+
const fetchOptions: RequestInit = {
|
|
89
|
+
method: 'GET',
|
|
90
|
+
headers: {
|
|
91
|
+
'User-Agent': 'LibreChat/1.0',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
96
|
+
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Failed to fetch files for session: ${response.status}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const files = await response.json();
|
|
107
|
+
if (Array.isArray(files) && files.length > 0) {
|
|
108
|
+
const fileReferences: t.CodeEnvFile[] = files.map((file) => {
|
|
109
|
+
const nameParts = file.name.split('/');
|
|
110
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
session_id,
|
|
114
|
+
id,
|
|
115
|
+
name: file.metadata['original-filename'],
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
postData.files = fileReferences;
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// eslint-disable-next-line no-console
|
|
123
|
+
console.warn(`Failed to fetch files for session: ${session_id}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const fetchOptions: RequestInit = {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'User-Agent': 'LibreChat/1.0',
|
|
133
|
+
},
|
|
134
|
+
body: JSON.stringify(postData),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (process.env.PROXY != null && process.env.PROXY !== '') {
|
|
138
|
+
fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);
|
|
139
|
+
}
|
|
140
|
+
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const result: t.ExecuteResult = await response.json();
|
|
146
|
+
let formattedOutput = '';
|
|
147
|
+
if (result.stdout) {
|
|
148
|
+
formattedOutput += `stdout:\n${result.stdout}\n`;
|
|
149
|
+
} else {
|
|
150
|
+
formattedOutput += emptyOutputMessage;
|
|
151
|
+
}
|
|
152
|
+
if (result.stderr) formattedOutput += `stderr:\n${result.stderr}\n`;
|
|
153
|
+
if (result.files && result.files.length > 0) {
|
|
154
|
+
formattedOutput += 'Generated files:\n';
|
|
155
|
+
|
|
156
|
+
const fileCount = result.files.length;
|
|
157
|
+
for (let i = 0; i < fileCount; i++) {
|
|
158
|
+
const file = result.files[i];
|
|
159
|
+
const isImage = imageExtRegex.test(file.name);
|
|
160
|
+
formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;
|
|
161
|
+
|
|
162
|
+
if (i < fileCount - 1) {
|
|
163
|
+
formattedOutput += fileCount <= 3 ? ', ' : ',\n';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
formattedOutput += `\n\n${accessMessage}`;
|
|
168
|
+
return [
|
|
169
|
+
formattedOutput.trim(),
|
|
170
|
+
{
|
|
171
|
+
session_id: result.session_id,
|
|
172
|
+
files: result.files,
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return [formattedOutput.trim(), { session_id: result.session_id }];
|
|
178
|
+
} catch (error) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Execution error:\n\n${(error as Error | undefined)?.message}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: BashExecutionToolName,
|
|
186
|
+
description: BashExecutionToolDescription,
|
|
187
|
+
schema: BashExecutionToolSchema,
|
|
188
|
+
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { createBashExecutionTool };
|