@librechat/agents 3.2.65 → 3.2.67
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 +15 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/createToolPolicyHook.cjs +5 -5
- package/dist/cjs/hooks/createToolPolicyHook.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +15 -3
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +1 -2
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +51 -24
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +8 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +8 -0
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +9 -8
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +9 -7
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +59 -14
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +5 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +15 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/createToolPolicyHook.mjs +5 -5
- package/dist/esm/hooks/createToolPolicyHook.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +15 -3
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +1 -2
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +51 -24
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +8 -0
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +8 -0
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +9 -8
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +9 -7
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +59 -14
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +5 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +2 -0
- package/dist/types/hooks/createToolPolicyHook.d.ts +12 -15
- package/dist/types/langfuseTraceShaping.d.ts +12 -6
- package/dist/types/tools/BashExecutor.d.ts +7 -6
- package/dist/types/tools/CodeExecutor.d.ts +7 -5
- package/dist/types/tools/search/types.d.ts +2 -0
- package/dist/types/types/graph.d.ts +10 -3
- package/package.json +1 -1
- package/src/graphs/Graph.ts +21 -3
- package/src/hooks/__tests__/createToolPolicyHook.test.ts +13 -4
- package/src/hooks/createToolPolicyHook.ts +16 -19
- package/src/instrumentation.ts +20 -0
- package/src/langfuseToolOutputTracing.ts +2 -4
- package/src/langfuseTraceShaping.ts +73 -20
- package/src/llm/anthropic/utils/cross-provider-server-tools.test.ts +110 -0
- package/src/llm/anthropic/utils/message_inputs.ts +15 -0
- package/src/llm/bedrock/utils/cross-provider-server-tools.test.ts +122 -0
- package/src/llm/bedrock/utils/message_inputs.ts +13 -0
- package/src/specs/langfuse-instrumentation.test.ts +64 -0
- package/src/specs/langfuse-routing.integration.test.ts +49 -4
- package/src/specs/langfuse-tool-output-tracing.test.ts +5 -4
- package/src/specs/langfuse-trace-shaping.test.ts +80 -9
- package/src/specs/subagent.test.ts +180 -0
- package/src/tools/BashExecutor.ts +9 -8
- package/src/tools/CodeExecutor.ts +9 -7
- package/src/tools/ToolSearch.ts +81 -23
- package/src/tools/__tests__/BashExecutor.test.ts +16 -5
- package/src/tools/__tests__/CodeExecutor.stateful.test.ts +17 -6
- package/src/tools/__tests__/ToolSearch.test.ts +151 -0
- package/src/tools/__tests__/hitl.test.ts +114 -1
- package/src/tools/search/source-processing.test.ts +57 -0
- package/src/tools/search/tool.ts +13 -1
- package/src/tools/search/types.ts +2 -0
- package/src/types/graph.ts +10 -3
|
@@ -360,6 +360,52 @@ describe('ToolSearch', () => {
|
|
|
360
360
|
expect(result.tool_references[0].match_score).toBeGreaterThan(0);
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
+
it('does not match tool names when searching only parameters', () => {
|
|
364
|
+
const tools: ToolMetadata[] = [
|
|
365
|
+
{
|
|
366
|
+
name: 'query',
|
|
367
|
+
description: 'A tool whose name matches but parameters do not',
|
|
368
|
+
parameters: {
|
|
369
|
+
type: 'object',
|
|
370
|
+
properties: { value: { type: 'string' } },
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
name: 'run_database_query',
|
|
375
|
+
description: 'Run a database query',
|
|
376
|
+
parameters: {
|
|
377
|
+
type: 'object',
|
|
378
|
+
properties: { query: { type: 'string' } },
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
const result = performLocalSearch(tools, 'query', ['parameters'], 1);
|
|
384
|
+
|
|
385
|
+
expect(result.tool_references).toHaveLength(1);
|
|
386
|
+
expect(result.tool_references[0].tool_name).toBe('run_database_query');
|
|
387
|
+
expect(result.tool_references[0].matched_field).toBe('parameters');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('returns no matches when every selected field is empty', () => {
|
|
391
|
+
const tools: ToolMetadata[] = [
|
|
392
|
+
{
|
|
393
|
+
name: 'get_weather',
|
|
394
|
+
description: 'Get the weather',
|
|
395
|
+
parameters: undefined,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
name: 'send_email',
|
|
399
|
+
description: 'Send an email',
|
|
400
|
+
parameters: undefined,
|
|
401
|
+
},
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
const result = performLocalSearch(tools, 'query', ['parameters'], 10);
|
|
405
|
+
|
|
406
|
+
expect(result.tool_references).toEqual([]);
|
|
407
|
+
});
|
|
408
|
+
|
|
363
409
|
it('prioritizes name matches over description matches', () => {
|
|
364
410
|
const result = performLocalSearch(
|
|
365
411
|
mockTools,
|
|
@@ -830,6 +876,111 @@ describe('ToolSearch', () => {
|
|
|
830
876
|
},
|
|
831
877
|
];
|
|
832
878
|
|
|
879
|
+
it('finds camelCase tools by exact name', () => {
|
|
880
|
+
const tools: ToolMetadata[] = [
|
|
881
|
+
{
|
|
882
|
+
name: 'addActivity',
|
|
883
|
+
description: 'Create an activity',
|
|
884
|
+
parameters: undefined,
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
name: 'addPerson',
|
|
888
|
+
description: 'Create a person',
|
|
889
|
+
parameters: undefined,
|
|
890
|
+
},
|
|
891
|
+
];
|
|
892
|
+
|
|
893
|
+
const result = performLocalSearch(tools, 'addPerson', ['name'], 10);
|
|
894
|
+
|
|
895
|
+
expect(result.tool_references[0].tool_name).toBe('addPerson');
|
|
896
|
+
expect(result.tool_references[0].match_score).toBe(1.0);
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it('finds camelCase tools by a lowercased exact name', () => {
|
|
900
|
+
const tools: ToolMetadata[] = [
|
|
901
|
+
{
|
|
902
|
+
name: 'addActivity',
|
|
903
|
+
description: 'Create an activity',
|
|
904
|
+
parameters: undefined,
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
name: 'addPerson',
|
|
908
|
+
description: 'Create a person',
|
|
909
|
+
parameters: undefined,
|
|
910
|
+
},
|
|
911
|
+
];
|
|
912
|
+
|
|
913
|
+
const result = performLocalSearch(tools, 'addperson', ['name'], 10);
|
|
914
|
+
|
|
915
|
+
expect(result.tool_references).toHaveLength(1);
|
|
916
|
+
expect(result.tool_references[0].tool_name).toBe('addPerson');
|
|
917
|
+
expect(result.tool_references[0].match_score).toBe(1.0);
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it('prioritizes an exact full camelCase MCP tool ID', () => {
|
|
921
|
+
const tools: ToolMetadata[] = [
|
|
922
|
+
{
|
|
923
|
+
name: 'add_person_mcp_pipedrive',
|
|
924
|
+
description: 'Create a person with a separator variant',
|
|
925
|
+
parameters: undefined,
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
name: 'addActivity_mcp_pipedrive',
|
|
929
|
+
description: 'Create an activity',
|
|
930
|
+
parameters: undefined,
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
name: 'addPerson_mcp_pipedrive',
|
|
934
|
+
description: 'Create a person',
|
|
935
|
+
parameters: undefined,
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: 'updatePerson_mcp_pipedrive',
|
|
939
|
+
description: 'Update a person',
|
|
940
|
+
parameters: undefined,
|
|
941
|
+
},
|
|
942
|
+
];
|
|
943
|
+
|
|
944
|
+
const result = performLocalSearch(
|
|
945
|
+
tools,
|
|
946
|
+
'addPerson_mcp_pipedrive',
|
|
947
|
+
['name'],
|
|
948
|
+
1
|
|
949
|
+
);
|
|
950
|
+
|
|
951
|
+
expect(result.tool_references[0].tool_name).toBe(
|
|
952
|
+
'addPerson_mcp_pipedrive'
|
|
953
|
+
);
|
|
954
|
+
expect(result.tool_references[0].match_score).toBe(1.0);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
it.each([
|
|
958
|
+
['OAuthToken', 'close_order'],
|
|
959
|
+
['iOSApp', 'inspect_inventory'],
|
|
960
|
+
])(
|
|
961
|
+
'does not return tools matching only an acronym fragment from %s',
|
|
962
|
+
(matchingTool, unrelatedTool) => {
|
|
963
|
+
const tools: ToolMetadata[] = [
|
|
964
|
+
{
|
|
965
|
+
name: matchingTool,
|
|
966
|
+
description: 'Expected acronym tool',
|
|
967
|
+
parameters: undefined,
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
name: unrelatedTool,
|
|
971
|
+
description: 'Unrelated tool',
|
|
972
|
+
parameters: undefined,
|
|
973
|
+
},
|
|
974
|
+
];
|
|
975
|
+
|
|
976
|
+
const result = performLocalSearch(tools, matchingTool, ['name'], 10);
|
|
977
|
+
|
|
978
|
+
expect(
|
|
979
|
+
result.tool_references.map(({ tool_name }) => tool_name)
|
|
980
|
+
).toEqual([matchingTool]);
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
|
|
833
984
|
it('searches across all tools including MCP tools', () => {
|
|
834
985
|
const result = performLocalSearch(
|
|
835
986
|
mcpTools,
|
|
@@ -34,7 +34,7 @@ import type {
|
|
|
34
34
|
import type * as t from '@/types';
|
|
35
35
|
import { Providers as providers, GraphEvents } from '@/common';
|
|
36
36
|
import * as events from '@/utils/events';
|
|
37
|
-
import { HookRegistry } from '@/hooks';
|
|
37
|
+
import { HookRegistry, createToolPolicyHook } from '@/hooks';
|
|
38
38
|
import { ToolNode } from '../ToolNode';
|
|
39
39
|
|
|
40
40
|
async function flushAsyncWork(): Promise<void> {
|
|
@@ -255,6 +255,119 @@ describe('ToolNode HITL — `ask` decision raises interrupt() when humanInTheLoo
|
|
|
255
255
|
]);
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
+
it('waits for approval before executing an explicit ask rule in bypass mode', async () => {
|
|
259
|
+
let toolExecuted = false;
|
|
260
|
+
jest
|
|
261
|
+
.spyOn(events, 'safeDispatchCustomEvent')
|
|
262
|
+
.mockImplementation(async (event, data) => {
|
|
263
|
+
if (event !== 'on_tool_execute') {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
toolExecuted = true;
|
|
267
|
+
const request = data as {
|
|
268
|
+
resolve: (results: t.ToolExecuteResult[]) => void;
|
|
269
|
+
};
|
|
270
|
+
request.resolve([
|
|
271
|
+
{ toolCallId: 'call_1', content: 'deleted', status: 'success' },
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
const registry = new HookRegistry();
|
|
275
|
+
registry.register('PreToolUse', {
|
|
276
|
+
hooks: [
|
|
277
|
+
createToolPolicyHook({
|
|
278
|
+
mode: 'bypass',
|
|
279
|
+
ask: ['dangerous_*'],
|
|
280
|
+
}),
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
const node = new ToolNode({
|
|
284
|
+
tools: [createSchemaStub('dangerous_tool')],
|
|
285
|
+
eventDrivenMode: true,
|
|
286
|
+
agentId: 'agent-x',
|
|
287
|
+
toolCallStepIds: new Map([['call_1', 'step_call_1']]),
|
|
288
|
+
hookRegistry: registry,
|
|
289
|
+
humanInTheLoop: { enabled: true },
|
|
290
|
+
});
|
|
291
|
+
const graph = buildHITLGraph(node, [
|
|
292
|
+
{
|
|
293
|
+
id: 'call_1',
|
|
294
|
+
name: 'dangerous_tool',
|
|
295
|
+
args: { command: 'delete data' },
|
|
296
|
+
},
|
|
297
|
+
]);
|
|
298
|
+
const config = {
|
|
299
|
+
configurable: { thread_id: 'thread-bypass-explicit-ask' },
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const interrupted = await graph.invoke({ messages: [] }, config);
|
|
303
|
+
|
|
304
|
+
expect(isInterrupted<t.HumanInterruptPayload>(interrupted)).toBe(true);
|
|
305
|
+
expect(toolExecuted).toBe(false);
|
|
306
|
+
|
|
307
|
+
const resumed = (await resumeGraph(
|
|
308
|
+
graph,
|
|
309
|
+
interrupted,
|
|
310
|
+
[{ type: 'approve' }],
|
|
311
|
+
config
|
|
312
|
+
)) as { messages: BaseMessage[] };
|
|
313
|
+
|
|
314
|
+
expect(toolExecuted).toBe(true);
|
|
315
|
+
expect(
|
|
316
|
+
resumed.messages.some(
|
|
317
|
+
(message) =>
|
|
318
|
+
message._getType() === 'tool' &&
|
|
319
|
+
(message as ToolMessage).tool_call_id === 'call_1' &&
|
|
320
|
+
message.content === 'deleted'
|
|
321
|
+
)
|
|
322
|
+
).toBe(true);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('executes an unmatched tool without interruption in bypass mode', async () => {
|
|
326
|
+
let toolExecuted = false;
|
|
327
|
+
jest
|
|
328
|
+
.spyOn(events, 'safeDispatchCustomEvent')
|
|
329
|
+
.mockImplementation(async (event, data) => {
|
|
330
|
+
if (event !== 'on_tool_execute') {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
toolExecuted = true;
|
|
334
|
+
const request = data as {
|
|
335
|
+
resolve: (results: t.ToolExecuteResult[]) => void;
|
|
336
|
+
};
|
|
337
|
+
request.resolve([
|
|
338
|
+
{ toolCallId: 'call_1', content: 'read result', status: 'success' },
|
|
339
|
+
]);
|
|
340
|
+
});
|
|
341
|
+
const registry = new HookRegistry();
|
|
342
|
+
registry.register('PreToolUse', {
|
|
343
|
+
hooks: [
|
|
344
|
+
createToolPolicyHook({
|
|
345
|
+
mode: 'bypass',
|
|
346
|
+
ask: ['dangerous_*'],
|
|
347
|
+
}),
|
|
348
|
+
],
|
|
349
|
+
});
|
|
350
|
+
const node = new ToolNode({
|
|
351
|
+
tools: [createSchemaStub('read_tool')],
|
|
352
|
+
eventDrivenMode: true,
|
|
353
|
+
agentId: 'agent-x',
|
|
354
|
+
toolCallStepIds: new Map([['call_1', 'step_call_1']]),
|
|
355
|
+
hookRegistry: registry,
|
|
356
|
+
humanInTheLoop: { enabled: true },
|
|
357
|
+
});
|
|
358
|
+
const graph = buildHITLGraph(node, [
|
|
359
|
+
{ id: 'call_1', name: 'read_tool', args: { command: 'read data' } },
|
|
360
|
+
]);
|
|
361
|
+
|
|
362
|
+
const result = await graph.invoke(
|
|
363
|
+
{ messages: [] },
|
|
364
|
+
{ configurable: { thread_id: 'thread-bypass-unmatched' } }
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
expect(isInterrupted(result)).toBe(false);
|
|
368
|
+
expect(toolExecuted).toBe(true);
|
|
369
|
+
});
|
|
370
|
+
|
|
258
371
|
it('resume with approve runs the tool through the host event path', async () => {
|
|
259
372
|
mockEventDispatch([
|
|
260
373
|
{ toolCallId: 'call_1', content: 'host-result', status: 'success' },
|
|
@@ -141,6 +141,63 @@ describe('expandHighlights content stripping', () => {
|
|
|
141
141
|
expect(result.organic?.[0].content).toBeUndefined();
|
|
142
142
|
expect(result.organic?.[0].references).toBeUndefined();
|
|
143
143
|
});
|
|
144
|
+
|
|
145
|
+
test('honors a custom mainExpandBy when expanding highlights', () => {
|
|
146
|
+
const highlightText = 'KEYFACT';
|
|
147
|
+
// Boundary-free filler so expansion is governed purely by mainExpandBy,
|
|
148
|
+
// not by where natural separators happen to fall.
|
|
149
|
+
const content = `${'x'.repeat(1000)}${highlightText}${'y'.repeat(1000)}`;
|
|
150
|
+
const makeData = (): t.SearchResultData => ({
|
|
151
|
+
organic: [
|
|
152
|
+
{
|
|
153
|
+
...makeOrganic('https://a.com'),
|
|
154
|
+
content,
|
|
155
|
+
highlights: [{ text: highlightText, score: 0.9 }],
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// separatorExpandBy 0 isolates the mainExpandBy effect.
|
|
161
|
+
const narrow = expandHighlights(makeData(), 50, 0).organic?.[0]
|
|
162
|
+
.highlights?.[0];
|
|
163
|
+
const wide = expandHighlights(makeData(), 500, 0).organic?.[0]
|
|
164
|
+
.highlights?.[0];
|
|
165
|
+
|
|
166
|
+
expect(narrow?.text).toContain(highlightText);
|
|
167
|
+
expect(wide?.text).toContain(highlightText);
|
|
168
|
+
// 50 chars of context each side vs. 500 → wide must be markedly longer.
|
|
169
|
+
expect(wide!.text.length).toBeGreaterThan(narrow!.text.length);
|
|
170
|
+
expect(narrow!.text.length).toBe(highlightText.length + 100);
|
|
171
|
+
expect(wide!.text.length).toBe(highlightText.length + 1000);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('honors a custom separatorExpandBy when seeking boundaries', () => {
|
|
175
|
+
const highlightText = 'KEYFACT';
|
|
176
|
+
// The main window lands inside a boundary-free 'y' run; the only natural
|
|
177
|
+
// boundary ('. ') sits 250 chars past the main window's end. A small
|
|
178
|
+
// separator range can't reach it; a large one can.
|
|
179
|
+
const content = `${'x'.repeat(100)}${highlightText}${'y'.repeat(300)}. ${'z'.repeat(300)}`;
|
|
180
|
+
const makeData = (): t.SearchResultData => ({
|
|
181
|
+
organic: [
|
|
182
|
+
{
|
|
183
|
+
...makeOrganic('https://a.com'),
|
|
184
|
+
content,
|
|
185
|
+
highlights: [{ text: highlightText, score: 0.9 }],
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Same mainExpandBy; only the separator search range differs.
|
|
191
|
+
const narrow = expandHighlights(makeData(), 50, 100).organic?.[0]
|
|
192
|
+
.highlights?.[0];
|
|
193
|
+
const wide = expandHighlights(makeData(), 50, 400).organic?.[0]
|
|
194
|
+
.highlights?.[0];
|
|
195
|
+
|
|
196
|
+
expect(narrow?.text).toContain(highlightText);
|
|
197
|
+
expect(wide?.text).toContain(highlightText);
|
|
198
|
+
// Only the wider separator range reaches the trailing sentence boundary.
|
|
199
|
+
expect(wide!.text.length).toBeGreaterThan(narrow!.text.length);
|
|
200
|
+
});
|
|
144
201
|
});
|
|
145
202
|
|
|
146
203
|
describe('createSourceProcessor content capping', () => {
|
package/src/tools/search/tool.ts
CHANGED
|
@@ -203,6 +203,8 @@ function createSearchProcessor({
|
|
|
203
203
|
supportsNews,
|
|
204
204
|
sourceProcessor,
|
|
205
205
|
onGetHighlights,
|
|
206
|
+
mainExpandBy,
|
|
207
|
+
separatorExpandBy,
|
|
206
208
|
logger,
|
|
207
209
|
}: {
|
|
208
210
|
safeSearch: t.SearchToolConfig['safeSearch'];
|
|
@@ -212,6 +214,8 @@ function createSearchProcessor({
|
|
|
212
214
|
searchAPI: ReturnType<typeof createSearchAPI>;
|
|
213
215
|
sourceProcessor: ReturnType<typeof createSourceProcessor>;
|
|
214
216
|
onGetHighlights: t.SearchToolConfig['onGetHighlights'];
|
|
217
|
+
mainExpandBy: t.SearchToolConfig['mainExpandBy'];
|
|
218
|
+
separatorExpandBy: t.SearchToolConfig['separatorExpandBy'];
|
|
215
219
|
logger: t.Logger;
|
|
216
220
|
}) {
|
|
217
221
|
return async function ({
|
|
@@ -260,7 +264,11 @@ function createSearchProcessor({
|
|
|
260
264
|
numElements: maxSources,
|
|
261
265
|
});
|
|
262
266
|
|
|
263
|
-
return expandHighlights(
|
|
267
|
+
return expandHighlights(
|
|
268
|
+
processedSources,
|
|
269
|
+
mainExpandBy,
|
|
270
|
+
separatorExpandBy
|
|
271
|
+
);
|
|
264
272
|
} catch (error) {
|
|
265
273
|
logger.error('Error in search:', error);
|
|
266
274
|
return {
|
|
@@ -376,6 +384,8 @@ export const createSearchTool = (
|
|
|
376
384
|
maxContentLength,
|
|
377
385
|
chunkSize,
|
|
378
386
|
chunkOverlap,
|
|
387
|
+
mainExpandBy,
|
|
388
|
+
separatorExpandBy,
|
|
379
389
|
maxOutputChars,
|
|
380
390
|
strategies = ['no_extraction'],
|
|
381
391
|
filterContent = true,
|
|
@@ -525,6 +535,8 @@ export const createSearchTool = (
|
|
|
525
535
|
supportsNews: searchProvider !== 'keenable',
|
|
526
536
|
sourceProcessor,
|
|
527
537
|
onGetHighlights,
|
|
538
|
+
mainExpandBy,
|
|
539
|
+
separatorExpandBy,
|
|
528
540
|
logger,
|
|
529
541
|
});
|
|
530
542
|
|
|
@@ -227,6 +227,8 @@ export interface ProcessSourcesConfig {
|
|
|
227
227
|
* configurable via the `SEARCH_CHUNK_OVERLAP` env var. Clamped below
|
|
228
228
|
* `chunkSize`. */
|
|
229
229
|
chunkOverlap?: number;
|
|
230
|
+
mainExpandBy?: number;
|
|
231
|
+
separatorExpandBy?: number;
|
|
230
232
|
strategies?: string[];
|
|
231
233
|
filterContent?: boolean;
|
|
232
234
|
reranker?: BaseReranker;
|
package/src/types/graph.ts
CHANGED
|
@@ -534,9 +534,9 @@ export type LangfuseToolOutputTracingConfig = {
|
|
|
534
534
|
|
|
535
535
|
export type LangfuseToolNodeTracingConfig = {
|
|
536
536
|
/**
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
537
|
+
* Opts into the internal ToolNode batch observation. Graph tool-dispatch
|
|
538
|
+
* and individual tool observations are exported without this wrapper, so
|
|
539
|
+
* the default is false to avoid a redundant hierarchy level.
|
|
540
540
|
*/
|
|
541
541
|
enabled?: boolean;
|
|
542
542
|
};
|
|
@@ -546,6 +546,13 @@ export interface LangfuseConfig {
|
|
|
546
546
|
publicKey?: string;
|
|
547
547
|
secretKey?: string;
|
|
548
548
|
baseUrl?: string;
|
|
549
|
+
/**
|
|
550
|
+
* Environment identifier attached to exported traces (Langfuse
|
|
551
|
+
* `environment`). When unset, falls back to `LANGFUSE_TRACING_ENVIRONMENT`
|
|
552
|
+
* then `NODE_ENV`, so production traces are not collapsed under the
|
|
553
|
+
* `default` environment.
|
|
554
|
+
*/
|
|
555
|
+
environment?: string;
|
|
549
556
|
metadata?: Record<string, string | number | boolean | null | undefined>;
|
|
550
557
|
/**
|
|
551
558
|
* Internal OTLP span attributes to attach to Langfuse observations before
|