@librechat/agents 3.3.4 → 3.3.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/common/constants.cjs +21 -0
- package/dist/cjs/common/constants.cjs.map +1 -1
- package/dist/cjs/events.cjs +10 -1
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +456 -7
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +25 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs +12 -0
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs +1 -0
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +8 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +1 -3
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +268 -3
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +132 -0
- package/dist/cjs/llm/preempt.cjs.map +1 -0
- package/dist/cjs/main.cjs +17 -3
- package/dist/cjs/messages/alternation.cjs +92 -0
- package/dist/cjs/messages/alternation.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +72 -0
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/handoffCue.cjs +63 -0
- package/dist/cjs/messages/handoffCue.cjs.map +1 -0
- package/dist/cjs/messages/index.cjs +3 -0
- package/dist/cjs/messages/injected.cjs +60 -0
- package/dist/cjs/messages/injected.cjs.map +1 -0
- package/dist/cjs/run.cjs +80 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/handlers.cjs +18 -9
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +28 -7
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +3 -23
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs +90 -0
- package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +9 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/esm/common/constants.mjs +19 -1
- package/dist/esm/common/constants.mjs.map +1 -1
- package/dist/esm/events.mjs +10 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +455 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +25 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +12 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs +1 -0
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +9 -1
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +1 -3
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +270 -6
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +131 -0
- package/dist/esm/llm/preempt.mjs.map +1 -0
- package/dist/esm/main.mjs +8 -5
- package/dist/esm/messages/alternation.mjs +91 -0
- package/dist/esm/messages/alternation.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +72 -0
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/handoffCue.mjs +61 -0
- package/dist/esm/messages/handoffCue.mjs.map +1 -0
- package/dist/esm/messages/index.mjs +3 -0
- package/dist/esm/messages/injected.mjs +60 -0
- package/dist/esm/messages/injected.mjs.map +1 -0
- package/dist/esm/run.mjs +80 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/handlers.mjs +19 -10
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +27 -8
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +3 -23
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-scraper.mjs +88 -0
- package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +9 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/types/common/constants.d.ts +18 -0
- package/dist/types/graphs/Graph.d.ts +138 -1
- package/dist/types/hooks/HookRegistry.d.ts +15 -0
- package/dist/types/hooks/index.d.ts +12 -1
- package/dist/types/hooks/types.d.ts +45 -6
- package/dist/types/index.d.ts +1 -1
- package/dist/types/llm/invoke.d.ts +7 -0
- package/dist/types/llm/preempt.d.ts +40 -0
- package/dist/types/messages/alternation.d.ts +23 -0
- package/dist/types/messages/handoffCue.d.ts +40 -0
- package/dist/types/messages/index.d.ts +3 -0
- package/dist/types/messages/injected.d.ts +3 -0
- package/dist/types/run.d.ts +7 -0
- package/dist/types/stream.d.ts +14 -0
- package/dist/types/tools/ToolNode.d.ts +0 -7
- package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
- package/dist/types/tools/search/types.d.ts +31 -2
- package/dist/types/types/graph.d.ts +8 -1
- package/dist/types/types/run.d.ts +65 -0
- package/dist/types/types/stream.d.ts +0 -25
- package/package.json +1 -1
- package/src/{splitStream.test.ts → aggregator.test.ts} +59 -666
- package/src/common/constants.ts +21 -0
- package/src/events.ts +15 -1
- package/src/graphs/Graph.ts +568 -3
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +18 -7
- package/src/graphs/__tests__/Graph.preemptSignal.test.ts +126 -0
- package/src/hooks/HookRegistry.ts +40 -0
- package/src/hooks/__tests__/preemptBoundary.test.ts +152 -0
- package/src/hooks/index.ts +16 -2
- package/src/hooks/types.ts +47 -3
- package/src/index.ts +1 -1
- package/src/langfuse.ts +26 -1
- package/src/llm/bedrock/utils/message_inputs.test.ts +82 -0
- package/src/llm/bedrock/utils/message_inputs.ts +14 -11
- package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
- package/src/llm/invoke.alternation.test.ts +87 -0
- package/src/llm/invoke.handoffCue.test.ts +112 -0
- package/src/llm/invoke.ts +417 -8
- package/src/llm/preempt.test.ts +323 -0
- package/src/llm/preempt.ts +178 -0
- package/src/messages/alternation.test.ts +212 -0
- package/src/messages/alternation.ts +112 -0
- package/src/messages/format.ts +91 -0
- package/src/messages/formatAgentMessages.steer.test.ts +267 -0
- package/src/messages/handoffCue.test.ts +96 -0
- package/src/messages/handoffCue.ts +78 -0
- package/src/messages/index.ts +3 -0
- package/src/messages/injected.test.ts +90 -0
- package/src/messages/injected.ts +74 -0
- package/src/run.ts +91 -6
- package/src/scripts/preempt-probe.ts +330 -0
- package/src/scripts/preempt-scenarios.ts +388 -0
- package/src/session/handlers.ts +32 -12
- package/src/specs/handoffCue.test.ts +165 -0
- package/src/specs/langfuse-callbacks.test.ts +352 -2
- package/src/specs/preemptSeal.test.ts +309 -0
- package/src/stream.dispatch.test.ts +63 -0
- package/src/stream.ts +36 -8
- package/src/tools/ToolNode.ts +3 -31
- package/src/tools/search/keenable-scraper.test.ts +153 -0
- package/src/tools/search/keenable-scraper.ts +137 -0
- package/src/tools/search/tool.ts +13 -2
- package/src/tools/search/types.ts +50 -3
- package/src/types/graph.ts +12 -1
- package/src/types/run.ts +67 -0
- package/src/types/stream.ts +0 -41
- package/dist/cjs/splitStream.cjs +0 -151
- package/dist/cjs/splitStream.cjs.map +0 -1
- package/dist/esm/splitStream.mjs +0 -150
- package/dist/esm/splitStream.mjs.map +0 -1
- package/dist/types/mockStream.d.ts +0 -32
- package/dist/types/splitStream.d.ts +0 -37
- package/src/mockStream.ts +0 -99
- package/src/splitStream.ts +0 -234
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes a host-supplied seal budget.
|
|
4
|
+
*
|
|
5
|
+
* Read in two places that interpret it differently — a numeric comparison in
|
|
6
|
+
* the seal gate and an addition into the recursion limit — so a value like
|
|
7
|
+
* `1.5` would permit two seals while reserving fractional headroom, `NaN`
|
|
8
|
+
* would poison the recursion limit outright, and `Infinity` would remove both
|
|
9
|
+
* bounds at once. Normalizing once keeps the two readings in agreement.
|
|
10
|
+
*
|
|
11
|
+
* `0` is honored as a deliberate "never seal"; anything not finite falls back
|
|
12
|
+
* to the default rather than silently disabling the feature.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveMaxSeals(maxSeals: number | undefined): number;
|
|
15
|
+
/**
|
|
16
|
+
* Cooperative mid-generation seal gate. Returns true ONLY when sealing here
|
|
17
|
+
* yields a message sequence valid on EVERY supported provider:
|
|
18
|
+
* - non-whitespace TEXT content, so the FIRST injected user turn is preceded
|
|
19
|
+
* by a non-empty assistant turn — no empty-content 400s. Note this says
|
|
20
|
+
* nothing about adjacency AMONG several injected turns: a boundary that
|
|
21
|
+
* drains two steers emits two consecutive user messages, which strict
|
|
22
|
+
* providers reject. That is normalized at the provider-facing hop by
|
|
23
|
+
* `coalesceAdjacentUserTurns`, not here;
|
|
24
|
+
* - no tool call in flight, so no `tool_use` can be orphaned AND no eagerly
|
|
25
|
+
* prestarted execution can be stripped out from under the model.
|
|
26
|
+
*
|
|
27
|
+
* Anthropic's server-side tools need no check of their own. Every
|
|
28
|
+
* `server_tool_use` content block also emits a `tool_call_chunk`
|
|
29
|
+
* (`_makeMessageChunkFromAnthropicEvent`), and `concat` keeps that chunk on
|
|
30
|
+
* the accumulated message for the remainder of the turn, so the tool-call
|
|
31
|
+
* gates below already cover it. The practical consequence is worth stating
|
|
32
|
+
* plainly: once a turn starts a web search it is no longer preemptible, and
|
|
33
|
+
* a queued message waits for the ordinary tool boundary instead.
|
|
34
|
+
*
|
|
35
|
+
* Nothing is stripped and nothing is repaired: when the accumulated shape is
|
|
36
|
+
* not already safe the stream simply runs on, and whatever the host queued
|
|
37
|
+
* lands at the next tool boundary instead. Chunks accumulate monotonically
|
|
38
|
+
* through `concat`, so an unsafe shape can never be observed at a seal point.
|
|
39
|
+
*/
|
|
40
|
+
export declare function canSealPreempt(chunk: AIMessageChunk | undefined): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
import { Providers } from '@/common';
|
|
3
|
+
/**
|
|
4
|
+
* Providers whose APIs specify strict user/assistant alternation. Mistral
|
|
5
|
+
* rejects consecutive user turns outright. Bedrock's Converse API documents
|
|
6
|
+
* the alternation requirement across many model families; enforcement varies
|
|
7
|
+
* by family — Claude on Converse currently tolerates adjacent user turns
|
|
8
|
+
* (verified live, 2026-07-28) — so the payload is normalized for all of them
|
|
9
|
+
* rather than betting on per-family leniency. Anthropic's own Messages API,
|
|
10
|
+
* OpenAI and Gemini all accept consecutive user turns, so they are
|
|
11
|
+
* deliberately absent.
|
|
12
|
+
*/
|
|
13
|
+
export declare const strictAlternationProviders: ReadonlySet<Providers>;
|
|
14
|
+
/**
|
|
15
|
+
* Merges runs of consecutive human turns into one, for providers that reject
|
|
16
|
+
* them. Purely a wire-shaping pass: it returns a new array of new messages,
|
|
17
|
+
* so graph state and the host's persisted messages keep the per-message
|
|
18
|
+
* identity that steer rendering and the trailing-steer anchor rely on.
|
|
19
|
+
*
|
|
20
|
+
* Tool-result turns are excluded — the converters merge those themselves, and
|
|
21
|
+
* combining one with a text turn would orphan the pairing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function coalesceAdjacentUserTurns(messages: BaseMessage[]): BaseMessage[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
/**
|
|
3
|
+
* Bracketed-meta convention, like the handoff path's
|
|
4
|
+
* `[Processed tool result and transferring to …]` bridge. The wording makes
|
|
5
|
+
* two things unambiguous to the model: the assistant turn above is FINISHED,
|
|
6
|
+
* and it belongs to a previous stage — so the successor answers as itself
|
|
7
|
+
* instead of continuing someone else's sentence.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PREDECESSOR_HANDOFF_CUE: string;
|
|
10
|
+
/**
|
|
11
|
+
* Appends a user-turn handoff cue when a payload ends with an assistant turn
|
|
12
|
+
* that THIS RUN produced — which only happens when a different agent in a
|
|
13
|
+
* multi-agent workflow produced it (an agent's own self-loops always re-enter
|
|
14
|
+
* on a tool result or an injected user turn).
|
|
15
|
+
*
|
|
16
|
+
* Why: providers with prefill semantics (Anthropic, Bedrock-Claude) treat a
|
|
17
|
+
* trailing assistant message as a prefill and CONTINUE it. A bare direct-edge
|
|
18
|
+
* successor therefore speaks in its predecessor's voice — or, when the
|
|
19
|
+
* trailing turn reads complete (a preemption steer's short resume, say),
|
|
20
|
+
* returns empty content (danny-avila/agents#345, reproduced live 3/3).
|
|
21
|
+
* Handoff edges with instructions and prompt-instruction edges already break
|
|
22
|
+
* the prefill with a user turn; this closes the same gap for bare edges.
|
|
23
|
+
*
|
|
24
|
+
* Fail-safe OFF by provenance: the trailing payload message must be one the
|
|
25
|
+
* run itself produced (`isRunProduced`, backed by the graph's run-produced id
|
|
26
|
+
* set — immune to summarization compaction, which rewrites the live array
|
|
27
|
+
* and stales index-based boundaries). Host-supplied trailing assistant
|
|
28
|
+
* turns (deliberate prefill flows) never match — the run has not produced
|
|
29
|
+
* them — so single-agent prefill behavior is untouched. Wire-only: the cue is
|
|
30
|
+
* appended to the provider projection, never to graph state or host history.
|
|
31
|
+
*/
|
|
32
|
+
export declare function appendPredecessorHandoffCue(messages: BaseMessage[], isRunProduced: ((message: BaseMessage) => boolean) | undefined): BaseMessage[];
|
|
33
|
+
/**
|
|
34
|
+
* Strips a trailing handoff cue. The counterpart for the serving-provider
|
|
35
|
+
* funnel: an Anthropic-like PRIMARY bakes the cue into its measured payload,
|
|
36
|
+
* and a tolerant fallback (OpenAI, Mistral, Bedrock-Nova) re-sending that
|
|
37
|
+
* payload must not ship the Claude-only synthetic turn. Identity on the
|
|
38
|
+
* no-op path.
|
|
39
|
+
*/
|
|
40
|
+
export declare function removePredecessorHandoffCue(messages: BaseMessage[]): BaseMessage[];
|
|
@@ -7,6 +7,9 @@ export * from './cache';
|
|
|
7
7
|
export * from './anthropicToolCache';
|
|
8
8
|
export * from './content';
|
|
9
9
|
export * from './tools';
|
|
10
|
+
export * from './injected';
|
|
11
|
+
export * from './alternation';
|
|
12
|
+
export * from './handoffCue';
|
|
10
13
|
export * from './contextPruning';
|
|
11
14
|
export * from './contextPruningSettings';
|
|
12
15
|
export * from './reducer';
|
package/dist/types/run.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class Run<_T extends t.BaseGraphState> {
|
|
|
17
17
|
private interruptingToolNames?;
|
|
18
18
|
private toolExecution?;
|
|
19
19
|
private subagentUsageSink?;
|
|
20
|
+
private preemption?;
|
|
20
21
|
private indexTokenCountMap?;
|
|
21
22
|
calibrationRatio: number;
|
|
22
23
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
@@ -109,6 +110,12 @@ export declare class Run<_T extends t.BaseGraphState> {
|
|
|
109
110
|
*/
|
|
110
111
|
getCalibrationRatio(): number;
|
|
111
112
|
getResolvedInstructionOverhead(): number | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Cooperative-seal counters for this run. `emptyBoundaries` is the one to
|
|
115
|
+
* watch: it counts seals whose `PreemptBoundary` produced nothing to
|
|
116
|
+
* inject, which ends the turn early and leaves the answer unfinished.
|
|
117
|
+
*/
|
|
118
|
+
getPreemptStats(): t.PreemptStats;
|
|
112
119
|
getToolCount(): number;
|
|
113
120
|
/**
|
|
114
121
|
* Creates a custom event callback handler that intercepts custom events
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -8,7 +8,21 @@ export declare function getChunkContent({ chunk, provider, reasoningKey, }: {
|
|
|
8
8
|
provider?: Providers;
|
|
9
9
|
reasoningKey: 'reasoning_content' | 'reasoning';
|
|
10
10
|
}): string | t.MessageContentComplex[] | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Brands a handler as one that dispatches content parts for the SDK — either
|
|
13
|
+
* `ChatModelStreamHandler` itself or a wrapper forwarding to one.
|
|
14
|
+
*
|
|
15
|
+
* Identity alone is not a usable contract here. Hosts compose and wrap
|
|
16
|
+
* handlers (`composeEventHandlers`, `createRunHandlers`), and every wrapper
|
|
17
|
+
* fails `instanceof` while still driving the same dispatch. A brand survives
|
|
18
|
+
* wrapping, so "does this handler own content-part dispatch" can be answered
|
|
19
|
+
* about a value the SDK did not construct.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SDK_STREAM_DISPATCH: unique symbol;
|
|
22
|
+
/** True when `handler` is, or forwards to, the SDK's stream dispatcher. */
|
|
23
|
+
export declare function dispatchesChatModelStream(handler?: t.EventHandler): boolean;
|
|
11
24
|
export declare class ChatModelStreamHandler implements t.EventHandler {
|
|
25
|
+
readonly [SDK_STREAM_DISPATCH] = true;
|
|
12
26
|
handle(event: string, data: t.StreamEventData, metadata?: Record<string, unknown>, graph?: StandardGraph): Promise<void>;
|
|
13
27
|
handleReasoning(chunk: Partial<AIMessageChunk>, agentContext: AgentContext): void;
|
|
14
28
|
}
|
|
@@ -401,13 +401,6 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
401
401
|
* actually dispatched so the caller can fall back to batch-time emission.
|
|
402
402
|
*/
|
|
403
403
|
private dispatchEarlyToolCompletion;
|
|
404
|
-
/**
|
|
405
|
-
* Converts InjectedMessage instances to LangChain HumanMessage objects.
|
|
406
|
-
* Both 'user' and 'system' roles become HumanMessage to avoid provider
|
|
407
|
-
* rejections (Anthropic/Google reject non-leading SystemMessages).
|
|
408
|
-
* The original role is preserved in additional_kwargs for downstream consumers.
|
|
409
|
-
*/
|
|
410
|
-
private convertInjectedMessages;
|
|
411
404
|
/**
|
|
412
405
|
* Execute a group of direct (in-process) tool calls with interrupt-safe
|
|
413
406
|
* ordering, returning outputs aligned 1:1 with `directCalls`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as t from './types';
|
|
2
|
+
export declare class KeenableScraper implements t.BaseScraper {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private apiUrl;
|
|
5
|
+
private timeout;
|
|
6
|
+
private attributionTitle;
|
|
7
|
+
private logger;
|
|
8
|
+
constructor(config?: t.KeenableScraperConfig);
|
|
9
|
+
private buildHeaders;
|
|
10
|
+
scrapeUrl(url: string, options?: t.KeenableScrapeOptions): Promise<[string, t.KeenableScrapeResponse]>;
|
|
11
|
+
scrapeUrls(urls: string[], options?: t.KeenableScrapeOptions): Promise<Array<[string, t.KeenableScrapeResponse]>>;
|
|
12
|
+
extractContent(response: t.KeenableScrapeResponse): [string, undefined | t.References];
|
|
13
|
+
extractMetadata(response: t.KeenableScrapeResponse): t.GenericScrapeMetadata;
|
|
14
|
+
}
|
|
15
|
+
export declare const createKeenableScraper: (config?: t.KeenableScraperConfig) => KeenableScraper;
|
|
@@ -3,7 +3,7 @@ import type { Logger as WinstonLogger } from 'winston';
|
|
|
3
3
|
import type { BaseReranker } from './rerankers';
|
|
4
4
|
import { DATE_RANGE } from './schema';
|
|
5
5
|
export type SearchProvider = 'serper' | 'searxng' | 'tavily' | 'keenable' | 'crw';
|
|
6
|
-
export type ScraperProvider = 'firecrawl' | 'serper' | 'tavily' | 'crw';
|
|
6
|
+
export type ScraperProvider = 'firecrawl' | 'serper' | 'tavily' | 'crw' | 'keenable';
|
|
7
7
|
export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
|
|
8
8
|
export interface Highlight {
|
|
9
9
|
score: number;
|
|
@@ -172,6 +172,34 @@ export interface KeenableSearchResult {
|
|
|
172
172
|
export interface KeenableSearchResponse {
|
|
173
173
|
results?: KeenableSearchResult[];
|
|
174
174
|
}
|
|
175
|
+
export interface KeenableScraperConfig {
|
|
176
|
+
apiKey?: string;
|
|
177
|
+
/** Override the fetch endpoint base (default: public keyless, keyed when a
|
|
178
|
+
* key is set). Env fallback: KEENABLE_FETCH_URL. */
|
|
179
|
+
apiUrl?: string;
|
|
180
|
+
timeout?: number;
|
|
181
|
+
logger?: Logger;
|
|
182
|
+
/** Sent as the X-Keenable-Title attribution header. Defaults to "LibreChat". */
|
|
183
|
+
attributionTitle?: string;
|
|
184
|
+
}
|
|
185
|
+
export type KeenableScrapeOptions = Omit<KeenableScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
|
|
186
|
+
/** Raw JSON shape returned by GET /v1/fetch{,/public}?url=... */
|
|
187
|
+
export interface KeenableFetchResult {
|
|
188
|
+
url?: string;
|
|
189
|
+
title?: string;
|
|
190
|
+
content?: string;
|
|
191
|
+
description?: string;
|
|
192
|
+
}
|
|
193
|
+
export interface KeenableScrapeResponse {
|
|
194
|
+
success: boolean;
|
|
195
|
+
data?: {
|
|
196
|
+
content: string;
|
|
197
|
+
title?: string;
|
|
198
|
+
description?: string;
|
|
199
|
+
url?: string;
|
|
200
|
+
};
|
|
201
|
+
error?: string;
|
|
202
|
+
}
|
|
175
203
|
export type References = {
|
|
176
204
|
links: MediaReference[];
|
|
177
205
|
images: MediaReference[];
|
|
@@ -272,6 +300,7 @@ export type Logger = WinstonLogger;
|
|
|
272
300
|
export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, FirecrawlConfig {
|
|
273
301
|
tavilyScraperOptions?: TavilyScraperConfig;
|
|
274
302
|
crwScraperOptions?: CrwScraperConfig;
|
|
303
|
+
keenableScraperOptions?: KeenableScraperConfig;
|
|
275
304
|
/** Max chars of highlight content this tool feeds the MODEL per search (the
|
|
276
305
|
* dominant, otherwise-unbounded part of the output). Distinct from
|
|
277
306
|
* `maxContentLength`, which caps scraped/reranked content per source — full
|
|
@@ -303,7 +332,7 @@ export type UsedReferences = {
|
|
|
303
332
|
originalIndex: number;
|
|
304
333
|
reference: MediaReference;
|
|
305
334
|
}[];
|
|
306
|
-
export type AnyScraperResponse = FirecrawlScrapeResponse | SerperScrapeResponse | TavilyScrapeResponse | CrwScrapeResponse;
|
|
335
|
+
export type AnyScraperResponse = FirecrawlScrapeResponse | SerperScrapeResponse | TavilyScrapeResponse | CrwScrapeResponse | KeenableScrapeResponse;
|
|
307
336
|
/** Base Scraper Interface */
|
|
308
337
|
export interface BaseScraper {
|
|
309
338
|
scrapeUrl(url: string, options?: unknown): Promise<[string, AnyScraperResponse]>;
|
|
@@ -7,7 +7,7 @@ import type { GoogleAIToolType } from '@langchain/google-common';
|
|
|
7
7
|
import type { SummarizationNodeInput, SummarizeCompleteEvent, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
|
|
8
8
|
import type { ToolMap, ToolEndEvent, GenericTool, LCTool, ToolExecuteBatchRequest } from '@/types/tools';
|
|
9
9
|
import type { RunStep, RunStepDeltaEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
|
|
10
|
-
import type { TokenCounter, TokenBudgetBreakdown } from '@/types/run';
|
|
10
|
+
import type { TokenCounter, StreamPreemption, TokenBudgetBreakdown } from '@/types/run';
|
|
11
11
|
import type { Providers, Callback, GraphNodeKeys } from '@/common';
|
|
12
12
|
import type { StandardGraph, MultiAgentGraph } from '@/graphs';
|
|
13
13
|
import type { ClientOptions } from '@/types/llm';
|
|
@@ -243,6 +243,13 @@ export type StandardGraphInput = {
|
|
|
243
243
|
* hook inputs carry only `executingAgentId`.
|
|
244
244
|
*/
|
|
245
245
|
subagentScope?: boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Cooperative preemption, forwarded from `RunConfig.preemption`. Only ever
|
|
248
|
+
* set on the top-level graph: a steer targets the conversation, so subagent
|
|
249
|
+
* children must run to completion and `buildChildInputs` does not propagate
|
|
250
|
+
* this field.
|
|
251
|
+
*/
|
|
252
|
+
preemption?: StreamPreemption;
|
|
246
253
|
};
|
|
247
254
|
export type GraphEdge = {
|
|
248
255
|
/** Agent ID, use a list for multiple sources */
|
|
@@ -101,6 +101,63 @@ export type StandardGraphConfig = Omit<MultiAgentGraphConfig, 'edges' | 'type'>
|
|
|
101
101
|
type?: 'standard';
|
|
102
102
|
signal?: AbortSignal;
|
|
103
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* Cooperative mid-generation preemption. Lets a host seal the live model
|
|
106
|
+
* stream at the next provider-safe token boundary — the run is never
|
|
107
|
+
* aborted, the partial assistant turn is kept, and the graph self-loops
|
|
108
|
+
* into a fresh model call once the `PreemptBoundary` hook has injected
|
|
109
|
+
* whatever the host queued.
|
|
110
|
+
*
|
|
111
|
+
* Preconditions the host MUST satisfy:
|
|
112
|
+
* - `shouldPreempt` is polled once per streamed chunk on the top-level
|
|
113
|
+
* graph. It must be synchronous, allocation-free and O(1) — never I/O.
|
|
114
|
+
* It must also be LEVEL-TRIGGERED (non-consuming): the SDK never clears
|
|
115
|
+
* the host's request, and a true result is only honored once the
|
|
116
|
+
* accumulated chunk is provider-safe, so the predicate may be polled
|
|
117
|
+
* many times before a seal. A one-shot read that clears its own pending
|
|
118
|
+
* flag would silently lose the request on an unsafe chunk (leading
|
|
119
|
+
* whitespace/reasoning, an in-flight tool call) — keep returning true
|
|
120
|
+
* until the `PreemptBoundary` drain hands over the queued injection,
|
|
121
|
+
* then disarm there.
|
|
122
|
+
* - Sealing is only honored on the SDK's own dispatch loop. A run whose
|
|
123
|
+
* registered `CHAT_MODEL_STREAM` handler IS the SDK dispatcher — or wraps
|
|
124
|
+
* it, which `composeEventHandlers` and `createRunHandlers` both do —
|
|
125
|
+
* consumes chunks through a decoupled `streamEvents` reader that can lag
|
|
126
|
+
* the accumulated chunk, so those runs never seal.
|
|
127
|
+
*
|
|
128
|
+
* Detection is by capability, not identity: the dispatcher carries
|
|
129
|
+
* `SDK_STREAM_DISPATCH` and the SDK's wrappers propagate it. A handler
|
|
130
|
+
* that merely OBSERVES the raw chunk echo — LibreChat's no-op
|
|
131
|
+
* `OpenAIChatModelStreamHandler`, say — is unbranded and does NOT disable
|
|
132
|
+
* sealing, because it assigns no content-part indices and so cannot be
|
|
133
|
+
* inverted. A host that renders from the raw feed and wants the opt-out
|
|
134
|
+
* should brand its handler with `SDK_STREAM_DISPATCH`.
|
|
135
|
+
* - `RunConfig.tokenCounter` should be set. A sealed turn ends before most
|
|
136
|
+
* providers send their usage chunk, so the synthetic `CHAT_MODEL_END`
|
|
137
|
+
* falls back to the counter to report `output_tokens`. Without one that
|
|
138
|
+
* fallback silently no-ops and the sealed turn's usage is lost — verified
|
|
139
|
+
* live: OpenAI, Azure OpenAI and DeepSeek report no usage for the sealed
|
|
140
|
+
* segment without a counter, while Anthropic streams usage incrementally
|
|
141
|
+
* and reports it either way.
|
|
142
|
+
*/
|
|
143
|
+
export interface StreamPreemption {
|
|
144
|
+
/**
|
|
145
|
+
* Polled once per streamed chunk. Synchronous, allocation-free, O(1), and
|
|
146
|
+
* level-triggered — keep returning true until the `PreemptBoundary` drain
|
|
147
|
+
* consumes the request; a self-clearing read loses it on an unsafe chunk.
|
|
148
|
+
*/
|
|
149
|
+
shouldPreempt: () => boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Max cooperative seals per run. Each seal costs one extra superstep, so
|
|
152
|
+
* this also bounds the recursion-limit headroom the run reserves.
|
|
153
|
+
*/
|
|
154
|
+
maxSeals?: number;
|
|
155
|
+
}
|
|
156
|
+
/** Seals honored and boundaries that had nothing to inject, per run. */
|
|
157
|
+
export type PreemptStats = {
|
|
158
|
+
seals: number;
|
|
159
|
+
emptyBoundaries: number;
|
|
160
|
+
};
|
|
104
161
|
export type RunConfig = {
|
|
105
162
|
runId: string;
|
|
106
163
|
graphConfig: LegacyGraphConfig | StandardGraphConfig | MultiAgentGraphConfig;
|
|
@@ -132,6 +189,14 @@ export type RunConfig = {
|
|
|
132
189
|
* block to prevent leaks.
|
|
133
190
|
*/
|
|
134
191
|
hooks?: HookRegistry;
|
|
192
|
+
/**
|
|
193
|
+
* Opt-in cooperative preemption for this run. Requires a `hooks` registry
|
|
194
|
+
* with a `PreemptBoundary` matcher — the seal only stops the stream, the
|
|
195
|
+
* hook is what supplies the messages to resume with. Omit to keep the
|
|
196
|
+
* pre-preemption behavior, where a mid-run injection can only land at a
|
|
197
|
+
* tool boundary.
|
|
198
|
+
*/
|
|
199
|
+
preemption?: StreamPreemption;
|
|
135
200
|
returnContent?: boolean;
|
|
136
201
|
tokenCounter?: TokenCounter;
|
|
137
202
|
indexTokenCountMap?: Record<string, number>;
|
|
@@ -2,7 +2,6 @@ import type { MessageContentImageUrl, MessageContentText, ToolMessage, BaseMessa
|
|
|
2
2
|
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
3
3
|
import type { LLMResult, Generation } from '@langchain/core/outputs';
|
|
4
4
|
import type { Command } from '@langchain/langgraph';
|
|
5
|
-
import type OpenAITypes from 'openai';
|
|
6
5
|
import type { AnthropicContentBlock } from '@/llm/anthropic/types';
|
|
7
6
|
import type { SummarizeCompleteEvent } from '@/types/summarize';
|
|
8
7
|
import type { ToolEndEvent } from '@/types/tools';
|
|
@@ -297,30 +296,6 @@ export interface TMessage {
|
|
|
297
296
|
[key: string]: unknown;
|
|
298
297
|
}
|
|
299
298
|
export type TPayload = Array<Partial<TMessage>>;
|
|
300
|
-
export type CustomChunkDelta = null | undefined | (Partial<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice.Delta> & {
|
|
301
|
-
reasoning?: string | null;
|
|
302
|
-
reasoning_content?: string | null;
|
|
303
|
-
});
|
|
304
|
-
export type CustomChunkChoice = Partial<Omit<OpenAITypes.Chat.Completions.ChatCompletionChunk.Choice, 'delta'> & {
|
|
305
|
-
delta?: CustomChunkDelta;
|
|
306
|
-
}>;
|
|
307
|
-
export type CustomChunk = Partial<OpenAITypes.ChatCompletionChunk> & {
|
|
308
|
-
choices?: Partial<Array<CustomChunkChoice>>;
|
|
309
|
-
};
|
|
310
|
-
export type SplitStreamHandlers = Partial<{
|
|
311
|
-
[GraphEvents.ON_RUN_STEP]: ({ event, data, }: {
|
|
312
|
-
event: GraphEvents;
|
|
313
|
-
data: RunStep;
|
|
314
|
-
}) => void;
|
|
315
|
-
[GraphEvents.ON_MESSAGE_DELTA]: ({ event, data, }: {
|
|
316
|
-
event: GraphEvents;
|
|
317
|
-
data: MessageDeltaEvent;
|
|
318
|
-
}) => void;
|
|
319
|
-
[GraphEvents.ON_REASONING_DELTA]: ({ event, data, }: {
|
|
320
|
-
event: GraphEvents;
|
|
321
|
-
data: ReasoningDeltaEvent;
|
|
322
|
-
}) => void;
|
|
323
|
-
}>;
|
|
324
299
|
export type SummarizeDeltaData = {
|
|
325
300
|
id: string;
|
|
326
301
|
delta: {
|