@librechat/agents 3.6.3 → 3.6.5
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/graphs/Graph.cjs +44 -19
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +7 -1
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs +1 -1
- package/dist/cjs/langchain/index.cjs +12 -0
- package/dist/cjs/langchain/messages.cjs +12 -0
- package/dist/cjs/langfuseConfig.cjs +16 -0
- package/dist/cjs/langfuseConfig.cjs.map +1 -1
- package/dist/cjs/langfuseSpanRegistry.cjs +16 -4
- package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
- package/dist/cjs/main.cjs +15 -1
- package/dist/cjs/run.cjs +4 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/SubagentTool.cjs +14 -4
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs +403 -0
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +201 -61
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/index.cjs +1 -0
- package/dist/esm/graphs/Graph.mjs +44 -19
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +7 -1
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +1 -1
- package/dist/esm/langchain/index.mjs +2 -2
- package/dist/esm/langchain/messages.mjs +2 -2
- package/dist/esm/langfuseConfig.mjs +16 -0
- package/dist/esm/langfuseConfig.mjs.map +1 -1
- package/dist/esm/langfuseSpanRegistry.mjs +16 -4
- package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
- package/dist/esm/main.mjs +4 -3
- package/dist/esm/run.mjs +4 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/SubagentTool.mjs +14 -4
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs +403 -0
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +201 -61
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/index.mjs +1 -0
- package/dist/types/graphs/Graph.d.ts +6 -3
- package/dist/types/hooks/HookRegistry.d.ts +8 -0
- package/dist/types/langchain/messages.d.ts +2 -2
- package/dist/types/langfuseSpanRegistry.d.ts +9 -4
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/SubagentTool.d.ts +5 -2
- package/dist/types/tools/subagent/InMemorySubagentTaskStore.d.ts +45 -0
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +30 -3
- package/dist/types/tools/subagent/index.d.ts +2 -0
- package/dist/types/types/graph.d.ts +22 -4
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/run.d.ts +6 -0
- package/dist/types/types/subagentTasks.d.ts +172 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +75 -30
- package/src/hooks/HookRegistry.ts +18 -0
- package/src/langchain/messages.ts +3 -0
- package/src/langfuseConfig.ts +43 -0
- package/src/langfuseSpanRegistry.ts +42 -4
- package/src/run.ts +4 -0
- package/src/tools/SubagentTool.ts +37 -3
- package/src/tools/subagent/InMemorySubagentTaskStore.ts +632 -0
- package/src/tools/subagent/SubagentExecutor.ts +411 -74
- package/src/tools/subagent/index.ts +2 -0
- package/src/types/graph.ts +22 -4
- package/src/types/index.ts +1 -0
- package/src/types/run.ts +6 -0
- package/src/types/subagentTasks.ts +162 -0
package/src/graphs/Graph.ts
CHANGED
|
@@ -1312,6 +1312,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1312
1312
|
subagentUsageSink?: t.SubagentUsageSink;
|
|
1313
1313
|
/** See {@link t.StandardGraphInput.subagentScope}. */
|
|
1314
1314
|
subagentScope: boolean;
|
|
1315
|
+
/** See {@link t.StandardGraphInput.subagentTasks}. */
|
|
1316
|
+
subagentTasks: t.SubagentTaskConfig | undefined;
|
|
1315
1317
|
/** See {@link t.StandardGraphInput.subagentExecutionContext}. */
|
|
1316
1318
|
private readonly subagentExecutionContext?: t.SubagentExecutionContext;
|
|
1317
1319
|
/** See {@link t.StandardGraphInput.preemption}. */
|
|
@@ -1445,6 +1447,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1445
1447
|
indexTokenCountMap,
|
|
1446
1448
|
calibrationRatio,
|
|
1447
1449
|
subagentUsageSink,
|
|
1450
|
+
subagentTasks,
|
|
1448
1451
|
subagentScope,
|
|
1449
1452
|
subagentExecutionContext,
|
|
1450
1453
|
preemption,
|
|
@@ -1469,6 +1472,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1469
1472
|
this.signal = signal;
|
|
1470
1473
|
this.langfuse = langfuse;
|
|
1471
1474
|
this.subagentUsageSink = subagentUsageSink;
|
|
1475
|
+
this.subagentTasks = subagentTasks;
|
|
1472
1476
|
this.subagentScope = subagentScope === true;
|
|
1473
1477
|
this.subagentExecutionContext = subagentExecutionContext;
|
|
1474
1478
|
this.preemption = preemption;
|
|
@@ -1695,8 +1699,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1695
1699
|
* budget is taken by {@link claimPreemptSeal} once the accumulated chunk is
|
|
1696
1700
|
* known to be safe, so a chunk that cannot seal never spends budget.
|
|
1697
1701
|
*
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1702
|
+
* Ordinary subagent scopes never receive `preemption`. A detached child may
|
|
1703
|
+
* receive a dedicated parent-control preemption source, in which case the
|
|
1704
|
+
* same provider-safe seal path is intentionally reused inside that child.
|
|
1700
1705
|
*/
|
|
1701
1706
|
/** Internal seal preconditions only — no host callback, no side effects. */
|
|
1702
1707
|
private canClaimPreemptSeal(): boolean {
|
|
@@ -1710,7 +1715,6 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1710
1715
|
const runId =
|
|
1711
1716
|
(this.config?.configurable?.run_id as string | undefined) ?? this.runId;
|
|
1712
1717
|
return (
|
|
1713
|
-
!this.subagentScope &&
|
|
1714
1718
|
this.preemption != null &&
|
|
1715
1719
|
!this.preemptSealInFlight &&
|
|
1716
1720
|
this.preemptSealBudgetUsed < resolveMaxSeals(this.preemption.maxSeals) &&
|
|
@@ -4769,19 +4773,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4769
4773
|
}
|
|
4770
4774
|
const getParentHandlerRegistry = (): HandlerRegistry | undefined =>
|
|
4771
4775
|
this.handlerRegistry ?? this.parentToolHandlerRegistry;
|
|
4772
|
-
const
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
const childHandlerRegistry = createChildHandlerRegistry(
|
|
4779
|
-
getParentHandlerRegistry()
|
|
4780
|
-
);
|
|
4781
|
-
// Pure execution-ordering hint (unlike `humanInTheLoop`). It only
|
|
4782
|
-
// reorders tools already in the child's direct group; it does not
|
|
4783
|
-
// force a schema-only event tool onto the direct execution path.
|
|
4784
|
-
applyGraphRuntimeConfig(childGraph, {
|
|
4776
|
+
const snapshotChildGraphFactory = (
|
|
4777
|
+
parentHandlerRegistry: HandlerRegistry | undefined
|
|
4778
|
+
): GraphFactory => {
|
|
4779
|
+
const graphFactory = this.graphFactory;
|
|
4780
|
+
const subagentModelOverride = this.subagentModelOverride;
|
|
4781
|
+
const runtimeConfig = {
|
|
4785
4782
|
hookRegistry: this.hookRegistry,
|
|
4786
4783
|
humanInTheLoop: this.humanInTheLoop,
|
|
4787
4784
|
toolOutputReferences: this.toolOutputReferences,
|
|
@@ -4789,18 +4786,33 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4789
4786
|
codeSessionToolNames: this.codeSessionToolNames,
|
|
4790
4787
|
interruptingToolNames: this.interruptingToolNames,
|
|
4791
4788
|
toolExecution: this.toolExecution,
|
|
4792
|
-
}
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
childHandlerRegistry
|
|
4801
|
-
|
|
4802
|
-
|
|
4789
|
+
};
|
|
4790
|
+
const checkpointer = this.compileOptions?.checkpointer;
|
|
4791
|
+
return (request): StandardGraph => {
|
|
4792
|
+
const childGraph = graphFactory(request);
|
|
4793
|
+
if (subagentModelOverride != null) {
|
|
4794
|
+
childGraph.overrideModel = subagentModelOverride;
|
|
4795
|
+
childGraph.setSubagentModelOverride(subagentModelOverride);
|
|
4796
|
+
}
|
|
4797
|
+
const childHandlerRegistry = createChildHandlerRegistry(
|
|
4798
|
+
parentHandlerRegistry
|
|
4799
|
+
);
|
|
4800
|
+
// Pure execution-ordering hint (unlike `humanInTheLoop`). It only
|
|
4801
|
+
// reorders tools already in the child's direct group; it does not
|
|
4802
|
+
// force a schema-only event tool onto the direct execution path.
|
|
4803
|
+
applyGraphRuntimeConfig(childGraph, runtimeConfig);
|
|
4804
|
+
if (runtimeConfig.humanInTheLoop?.enabled === true) {
|
|
4805
|
+
childGraph.compileOptions = { checkpointer };
|
|
4806
|
+
}
|
|
4807
|
+
childGraph.parentToolHandlerRegistry = childHandlerRegistry;
|
|
4808
|
+
childGraph.eventToolExecutionAvailable =
|
|
4809
|
+
childHandlerRegistry?.getHandler(GraphEvents.ON_TOOL_EXECUTE) !=
|
|
4810
|
+
null;
|
|
4811
|
+
return childGraph;
|
|
4812
|
+
};
|
|
4803
4813
|
};
|
|
4814
|
+
const createConfiguredChildGraph: GraphFactory = (request) =>
|
|
4815
|
+
snapshotChildGraphFactory(getParentHandlerRegistry())(request);
|
|
4804
4816
|
const executor = new SubagentExecutor({
|
|
4805
4817
|
configs: new Map(
|
|
4806
4818
|
executableConfigs.map((config) => [config.type, config])
|
|
@@ -4820,6 +4832,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4820
4832
|
langfuse: this.langfuse,
|
|
4821
4833
|
tokenCounter: agentContext.tokenCounter,
|
|
4822
4834
|
usageSink: this.subagentUsageSink,
|
|
4835
|
+
taskConfig: this.subagentTasks,
|
|
4823
4836
|
streamLimits: this.streamLimits,
|
|
4824
4837
|
humanInTheLoop: this.humanInTheLoop,
|
|
4825
4838
|
checkpointer: this.compileOptions?.checkpointer,
|
|
@@ -4830,6 +4843,10 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4830
4843
|
input,
|
|
4831
4844
|
}),
|
|
4832
4845
|
createChildGraphByKind: createConfiguredChildGraph,
|
|
4846
|
+
createDetachedChildGraphFactory: (
|
|
4847
|
+
parentHandlerRegistry
|
|
4848
|
+
): GraphFactory =>
|
|
4849
|
+
snapshotChildGraphFactory(parentHandlerRegistry),
|
|
4833
4850
|
});
|
|
4834
4851
|
this.registerSubagentExecutor(executor);
|
|
4835
4852
|
|
|
@@ -4837,6 +4854,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4837
4854
|
const input = rawInput as {
|
|
4838
4855
|
description?: string;
|
|
4839
4856
|
subagent_type?: string;
|
|
4857
|
+
subagent_thread_id?: string;
|
|
4858
|
+
run_in_background?: boolean;
|
|
4840
4859
|
};
|
|
4841
4860
|
const description =
|
|
4842
4861
|
typeof input.description === 'string' &&
|
|
@@ -4845,6 +4864,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4845
4864
|
: DEFAULT_SUBAGENT_DESCRIPTION;
|
|
4846
4865
|
const subagentType =
|
|
4847
4866
|
typeof input.subagent_type === 'string' ? input.subagent_type : '';
|
|
4867
|
+
const subagentThreadId =
|
|
4868
|
+
typeof input.subagent_thread_id === 'string' &&
|
|
4869
|
+
input.subagent_thread_id.trim() !== ''
|
|
4870
|
+
? input.subagent_thread_id.trim()
|
|
4871
|
+
: undefined;
|
|
4848
4872
|
const threadId = config.configurable?.thread_id as string | undefined;
|
|
4849
4873
|
/** Surface the parent call id so child checkpoints, interrupts, and
|
|
4850
4874
|
* update events remain correlated across replay and resume. */
|
|
@@ -4869,7 +4893,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4869
4893
|
const batchScope = config.configurable?.[
|
|
4870
4894
|
RUN_BREAKER_SCOPE_CONFIG_KEY
|
|
4871
4895
|
] as RunBreakerScope | undefined;
|
|
4872
|
-
const
|
|
4896
|
+
const executeParams = {
|
|
4873
4897
|
description,
|
|
4874
4898
|
subagentType,
|
|
4875
4899
|
threadId,
|
|
@@ -4885,9 +4909,30 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
4885
4909
|
parentConfigurable: config.configurable as
|
|
4886
4910
|
| Record<string, unknown>
|
|
4887
4911
|
| undefined,
|
|
4888
|
-
}
|
|
4912
|
+
};
|
|
4913
|
+
if (input.run_in_background === true) {
|
|
4914
|
+
return executor.executeInBackground({
|
|
4915
|
+
...executeParams,
|
|
4916
|
+
...(subagentThreadId == null
|
|
4917
|
+
? {}
|
|
4918
|
+
: { subagentThreadId }),
|
|
4919
|
+
});
|
|
4920
|
+
}
|
|
4921
|
+
if (subagentThreadId != null) {
|
|
4922
|
+
return JSON.stringify({
|
|
4923
|
+
status: 'rejected',
|
|
4924
|
+
tool: Constants.SUBAGENT,
|
|
4925
|
+
message:
|
|
4926
|
+
'Child-thread continuation requires run_in_background.',
|
|
4927
|
+
});
|
|
4928
|
+
}
|
|
4929
|
+
const result = await executor.execute(executeParams);
|
|
4889
4930
|
return result.content;
|
|
4890
|
-
}, buildSubagentToolParams(executableConfigs
|
|
4931
|
+
}, buildSubagentToolParams(executableConfigs, {
|
|
4932
|
+
background: this.subagentTasks != null,
|
|
4933
|
+
threadContinuation:
|
|
4934
|
+
this.subagentTasks?.store.supportsThreadContinuation === true,
|
|
4935
|
+
}));
|
|
4891
4936
|
const replayableSubagentTool = subagentTool as typeof subagentTool &
|
|
4892
4937
|
ReplayableSubagentTool;
|
|
4893
4938
|
replayableSubagentTool[SUBAGENT_REPLAY_CONTROLLER] = {
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ToolApprovalReplaySnapshot,
|
|
7
7
|
AggregatedHookResult,
|
|
8
8
|
} from './types';
|
|
9
|
+
import { HOOK_EVENTS } from './types';
|
|
9
10
|
|
|
10
11
|
function serializeApprovalKey(key: ToolApprovalReplayKey): string {
|
|
11
12
|
return JSON.stringify([key.executionScope, key.agentId, key.toolUseId]);
|
|
@@ -227,6 +228,23 @@ export class HookRegistry {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Takes an isolated policy snapshot for work that may outlive the source
|
|
233
|
+
* run. Global and source-session matchers become global to the returned
|
|
234
|
+
* task-local registry, so parent cleanup and one-shot hook consumption
|
|
235
|
+
* cannot mutate the detached child (or vice versa). Runtime halt signals
|
|
236
|
+
* and pending approvals are intentionally not copied.
|
|
237
|
+
*/
|
|
238
|
+
forkSession(sourceSessionId: string): HookRegistry {
|
|
239
|
+
const fork = new HookRegistry();
|
|
240
|
+
for (const event of HOOK_EVENTS) {
|
|
241
|
+
for (const matcher of this.getMatchers(event, sourceSessionId)) {
|
|
242
|
+
fork.register(event, matcher);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return fork;
|
|
246
|
+
}
|
|
247
|
+
|
|
230
248
|
getPendingToolApproval(
|
|
231
249
|
sessionId: string,
|
|
232
250
|
key: ToolApprovalReplayKey
|
|
@@ -10,6 +10,8 @@ export {
|
|
|
10
10
|
isAIMessage,
|
|
11
11
|
isBaseMessage,
|
|
12
12
|
isToolMessage,
|
|
13
|
+
mapChatMessagesToStoredMessages,
|
|
14
|
+
mapStoredMessagesToChatMessages,
|
|
13
15
|
} from '@langchain/core/messages';
|
|
14
16
|
|
|
15
17
|
export type {
|
|
@@ -17,5 +19,6 @@ export type {
|
|
|
17
19
|
MessageContent,
|
|
18
20
|
MessageContentText,
|
|
19
21
|
MessageContentImageUrl,
|
|
22
|
+
StoredMessage,
|
|
20
23
|
UsageMetadata,
|
|
21
24
|
} from '@langchain/core/messages';
|
package/src/langfuseConfig.ts
CHANGED
|
@@ -175,6 +175,44 @@ export function resolveToolOutputTracingConfig(
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Merges header maps case-insensitively, keeping the override's casing.
|
|
180
|
+
*
|
|
181
|
+
* A plain spread would keep both `X-Proxy-Token` and `x-proxy-token`, and
|
|
182
|
+
* filling a fetch `Headers` from that record *appends* rather than replaces —
|
|
183
|
+
* the exporter would send one comma-joined `run-token, agent-token` value, so
|
|
184
|
+
* the agent override never cleanly wins and a gateway sees a malformed
|
|
185
|
+
* credential. Matches the case-insensitive identity already used for the
|
|
186
|
+
* destination key.
|
|
187
|
+
*/
|
|
188
|
+
function mergeAdditionalHeaders(
|
|
189
|
+
base?: Record<string, string>,
|
|
190
|
+
override?: Record<string, string>
|
|
191
|
+
): Record<string, string> | undefined {
|
|
192
|
+
if (base == null && override == null) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const merged: Record<string, string> = { ...base };
|
|
197
|
+
if (override == null) {
|
|
198
|
+
return merged;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const baseKeyByLower = new Map<string, string>(
|
|
202
|
+
Object.keys(merged).map((key) => [key.toLowerCase(), key])
|
|
203
|
+
);
|
|
204
|
+
for (const [key, value] of Object.entries(override)) {
|
|
205
|
+
const lower = key.toLowerCase();
|
|
206
|
+
const existingKey = baseKeyByLower.get(lower);
|
|
207
|
+
if (existingKey != null && existingKey !== key) {
|
|
208
|
+
delete merged[existingKey];
|
|
209
|
+
}
|
|
210
|
+
merged[key] = value;
|
|
211
|
+
baseKeyByLower.set(lower, key);
|
|
212
|
+
}
|
|
213
|
+
return merged;
|
|
214
|
+
}
|
|
215
|
+
|
|
178
216
|
export function resolveLangfuseConfig(
|
|
179
217
|
runLangfuse?: t.LangfuseConfig,
|
|
180
218
|
agentLangfuse?: t.LangfuseConfig
|
|
@@ -208,6 +246,10 @@ export function resolveLangfuseConfig(
|
|
|
208
246
|
...agentLangfuse.metadata,
|
|
209
247
|
}
|
|
210
248
|
: undefined;
|
|
249
|
+
const additionalHeaders = mergeAdditionalHeaders(
|
|
250
|
+
runLangfuse.additionalHeaders,
|
|
251
|
+
agentLangfuse.additionalHeaders
|
|
252
|
+
);
|
|
211
253
|
const librechatTraceAttributes =
|
|
212
254
|
runLangfuse.librechatTraceAttributes != null ||
|
|
213
255
|
agentLangfuse.librechatTraceAttributes != null
|
|
@@ -230,6 +272,7 @@ export function resolveLangfuseConfig(
|
|
|
230
272
|
...runLangfuse,
|
|
231
273
|
...agentLangfuse,
|
|
232
274
|
...(metadata != null ? { metadata } : {}),
|
|
275
|
+
...(additionalHeaders != null ? { additionalHeaders } : {}),
|
|
233
276
|
...(librechatTraceAttributes != null ? { librechatTraceAttributes } : {}),
|
|
234
277
|
...(tags != null ? { tags } : {}),
|
|
235
278
|
...(toolNodeTracing != null ? { toolNodeTracing } : {}),
|
|
@@ -59,6 +59,12 @@ function resolveLangfuseEnvironment(
|
|
|
59
59
|
return undefined;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function hasAdditionalHeaders(
|
|
63
|
+
headers?: Record<string, string>
|
|
64
|
+
): headers is Record<string, string> {
|
|
65
|
+
return headers != null && Object.keys(headers).length > 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
export function getLangfuseSpanProcessorParams(
|
|
63
69
|
langfuse?: t.LangfuseConfig
|
|
64
70
|
): LangfuseSpanProcessorParams | undefined {
|
|
@@ -66,6 +72,9 @@ export function getLangfuseSpanProcessorParams(
|
|
|
66
72
|
return undefined;
|
|
67
73
|
}
|
|
68
74
|
const environment = resolveLangfuseEnvironment(langfuse);
|
|
75
|
+
const additionalHeaders = hasAdditionalHeaders(langfuse?.additionalHeaders)
|
|
76
|
+
? { additionalHeaders: langfuse.additionalHeaders }
|
|
77
|
+
: {};
|
|
69
78
|
if (hasLangfuseConfigCredentials(langfuse)) {
|
|
70
79
|
return {
|
|
71
80
|
publicKey: langfuse.publicKey,
|
|
@@ -75,6 +84,7 @@ export function getLangfuseSpanProcessorParams(
|
|
|
75
84
|
...(langfuse.mediaUploadEnabled != null
|
|
76
85
|
? { mediaUploadEnabled: langfuse.mediaUploadEnabled }
|
|
77
86
|
: {}),
|
|
87
|
+
...additionalHeaders,
|
|
78
88
|
};
|
|
79
89
|
}
|
|
80
90
|
if (hasLangfuseEnvConfig()) {
|
|
@@ -90,6 +100,7 @@ export function getLangfuseSpanProcessorParams(
|
|
|
90
100
|
...(langfuse?.mediaUploadEnabled != null
|
|
91
101
|
? { mediaUploadEnabled: langfuse.mediaUploadEnabled }
|
|
92
102
|
: {}),
|
|
103
|
+
...additionalHeaders,
|
|
93
104
|
};
|
|
94
105
|
}
|
|
95
106
|
if (isPresent(langfuse?.baseUrl) && hasLangfuseEnvCredentials()) {
|
|
@@ -101,6 +112,7 @@ export function getLangfuseSpanProcessorParams(
|
|
|
101
112
|
...(langfuse.mediaUploadEnabled != null
|
|
102
113
|
? { mediaUploadEnabled: langfuse.mediaUploadEnabled }
|
|
103
114
|
: {}),
|
|
115
|
+
...additionalHeaders,
|
|
104
116
|
};
|
|
105
117
|
}
|
|
106
118
|
return undefined;
|
|
@@ -112,12 +124,37 @@ function hashCacheKeyValue(value: string | undefined): string | undefined {
|
|
|
112
124
|
: undefined;
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Order- and case-insensitive digest of the custom headers sent to a
|
|
129
|
+
* destination, so header maps that differ only in key order or header-name
|
|
130
|
+
* casing resolve to one destination instead of duplicating its exporter.
|
|
131
|
+
* Hashed because these values are credentials (proxy tokens, gateway keys).
|
|
132
|
+
* Absent and empty both yield `undefined`, keeping keys stable for the
|
|
133
|
+
* overwhelmingly common no-headers case.
|
|
134
|
+
*/
|
|
135
|
+
function hashAdditionalHeaders(
|
|
136
|
+
headers: Record<string, string> | undefined
|
|
137
|
+
): string | undefined {
|
|
138
|
+
if (!hasAdditionalHeaders(headers)) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
const normalized = Object.entries(headers)
|
|
142
|
+
.map(([name, value]) => JSON.stringify([name.trim().toLowerCase(), value]))
|
|
143
|
+
.sort();
|
|
144
|
+
return hashCacheKeyValue(normalized.join('\n'));
|
|
145
|
+
}
|
|
146
|
+
|
|
115
147
|
/**
|
|
116
148
|
* Identity of an export destination (project credentials + endpoint +
|
|
117
|
-
* environment) only. Processor-level policies like
|
|
118
|
-
* deliberately excluded: two spans exporting to the
|
|
119
|
-
* different redaction settings still share a destination
|
|
120
|
-
* another.
|
|
149
|
+
* environment + custom headers) only. Processor-level policies like
|
|
150
|
+
* `toolOutputTracing` are deliberately excluded: two spans exporting to the
|
|
151
|
+
* same project under different redaction settings still share a destination
|
|
152
|
+
* and may parent one another.
|
|
153
|
+
*
|
|
154
|
+
* Custom headers are included because a gateway may route on them, making two
|
|
155
|
+
* otherwise-identical configs different projects. Treating them as part of the
|
|
156
|
+
* destination keeps a run from inheriting a parent span bound elsewhere, and
|
|
157
|
+
* keeps a rotated proxy credential from reusing the stale exporter.
|
|
121
158
|
*/
|
|
122
159
|
export function getLangfuseDestinationKey(
|
|
123
160
|
params: LangfuseSpanProcessorParams
|
|
@@ -127,6 +164,7 @@ export function getLangfuseDestinationKey(
|
|
|
127
164
|
secretKeyHash: hashCacheKeyValue(params.secretKey),
|
|
128
165
|
baseUrl: params.baseUrl,
|
|
129
166
|
environment: params.environment,
|
|
167
|
+
additionalHeadersHash: hashAdditionalHeaders(params.additionalHeaders),
|
|
130
168
|
});
|
|
131
169
|
}
|
|
132
170
|
|
package/src/run.ts
CHANGED
|
@@ -296,6 +296,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
296
296
|
private subagentUsageSink?: t.SubagentUsageSink;
|
|
297
297
|
private preemption?: t.StreamPreemption;
|
|
298
298
|
private streamLimits?: t.StreamLimits;
|
|
299
|
+
private subagentTasks?: t.SubagentTaskConfig;
|
|
299
300
|
private indexTokenCountMap?: Record<string, number>;
|
|
300
301
|
calibrationRatio: number = 1;
|
|
301
302
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
@@ -363,6 +364,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
363
364
|
this.interruptingToolNames = config.interruptingToolNames;
|
|
364
365
|
this.toolExecution = config.toolExecution;
|
|
365
366
|
this.subagentUsageSink = config.subagentUsageSink;
|
|
367
|
+
this.subagentTasks = config.subagentTasks;
|
|
366
368
|
this.preemption = config.preemption;
|
|
367
369
|
this.streamLimits = config.streamLimits;
|
|
368
370
|
|
|
@@ -455,6 +457,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
455
457
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
456
458
|
calibrationRatio: this.calibrationRatio,
|
|
457
459
|
subagentUsageSink: this.subagentUsageSink,
|
|
460
|
+
subagentTasks: this.subagentTasks,
|
|
458
461
|
preemption: this.preemption,
|
|
459
462
|
streamLimits: this.streamLimits,
|
|
460
463
|
},
|
|
@@ -493,6 +496,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
493
496
|
indexTokenCountMap: this.indexTokenCountMap,
|
|
494
497
|
calibrationRatio: this.calibrationRatio,
|
|
495
498
|
subagentUsageSink: this.subagentUsageSink,
|
|
499
|
+
subagentTasks: this.subagentTasks,
|
|
496
500
|
preemption: this.preemption,
|
|
497
501
|
streamLimits: this.streamLimits,
|
|
498
502
|
},
|
|
@@ -13,7 +13,7 @@ WHEN TO USE:
|
|
|
13
13
|
- A specialized subagent is available for the task domain.
|
|
14
14
|
|
|
15
15
|
WHAT HAPPENS:
|
|
16
|
-
- A fresh agent or configured agent graph is created with the task description as its only input.
|
|
16
|
+
- A fresh agent or configured agent graph is created with the task description as its only input, unless the host enables child-thread continuation and you provide a saved thread id.
|
|
17
17
|
- The delegated agent or team runs to completion using isolated tools and context.
|
|
18
18
|
- Only the single agent's final response or the graph's designated result-agent response is returned to you.
|
|
19
19
|
|
|
@@ -27,6 +27,12 @@ const DESCRIPTION_PROP_DESCRIPTION =
|
|
|
27
27
|
const SUBAGENT_TYPE_PROP_DESCRIPTION =
|
|
28
28
|
'Which subagent type to delegate to. Must be one of the available types.';
|
|
29
29
|
|
|
30
|
+
const RUN_IN_BACKGROUND_PROP_DESCRIPTION =
|
|
31
|
+
'Set true to start the subagent as a detached process-local task and return a background_task_id immediately. Poll the host background-task tool to collect its result. The task can outlive this turn but does not survive a process restart.';
|
|
32
|
+
|
|
33
|
+
const SUBAGENT_THREAD_PROP_DESCRIPTION =
|
|
34
|
+
'Continue a host-owned child thread using a fresh execution lease. The saved thread must belong to this scope and subagent type. Only available with run_in_background.';
|
|
35
|
+
|
|
30
36
|
export const SubagentToolSchema = {
|
|
31
37
|
type: 'object',
|
|
32
38
|
properties: {
|
|
@@ -54,7 +60,10 @@ export const SubagentToolDefinition: LCTool = {
|
|
|
54
60
|
* Used by `Graph.createAgentNode()` when constructing the runtime tool instance.
|
|
55
61
|
* Extends `SubagentToolSchema` by populating `subagent_type.enum` dynamically.
|
|
56
62
|
*/
|
|
57
|
-
export function buildSubagentToolParams(
|
|
63
|
+
export function buildSubagentToolParams(
|
|
64
|
+
configs: SubagentConfig[],
|
|
65
|
+
options: { background?: boolean; threadContinuation?: boolean } = {}
|
|
66
|
+
): {
|
|
58
67
|
name: string;
|
|
59
68
|
schema: JsonSchemaType;
|
|
60
69
|
description: string;
|
|
@@ -79,10 +88,35 @@ export function buildSubagentToolParams(configs: SubagentConfig[]): {
|
|
|
79
88
|
enum: types,
|
|
80
89
|
description: `${SUBAGENT_TYPE_PROP_DESCRIPTION} Available: ${types.join(', ')}.`,
|
|
81
90
|
},
|
|
91
|
+
...(options.background === true
|
|
92
|
+
? {
|
|
93
|
+
run_in_background: {
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
description: RUN_IN_BACKGROUND_PROP_DESCRIPTION,
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
: {}),
|
|
99
|
+
...(options.background === true &&
|
|
100
|
+
options.threadContinuation === true
|
|
101
|
+
? {
|
|
102
|
+
subagent_thread_id: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: SUBAGENT_THREAD_PROP_DESCRIPTION,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
82
108
|
},
|
|
83
109
|
required: ['description', 'subagent_type'],
|
|
84
110
|
},
|
|
85
|
-
description: `${SubagentToolDescription}
|
|
111
|
+
description: `${SubagentToolDescription}${
|
|
112
|
+
options.background === true
|
|
113
|
+
? '\n\nBACKGROUND EXECUTION:\n- Set run_in_background to true when you do not need the result immediately. The call returns a background_task_id; use the host background-task tools to poll, steer, queue, interrupt, or cancel it.'
|
|
114
|
+
: ''
|
|
115
|
+
}${
|
|
116
|
+
options.background === true && options.threadContinuation === true
|
|
117
|
+
? '\n- To continue a completed child later, set run_in_background and pass its subagent_thread_id. The host starts a fresh execution from saved history; it does not restore a completed runtime.'
|
|
118
|
+
: ''
|
|
119
|
+
}\n\nAvailable types:\n${typeDescriptions}`,
|
|
86
120
|
};
|
|
87
121
|
}
|
|
88
122
|
|