@librechat/agents 3.1.86 → 3.1.88

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.
Files changed (160) hide show
  1. package/README.md +69 -0
  2. package/dist/cjs/events.cjs +23 -0
  3. package/dist/cjs/events.cjs.map +1 -1
  4. package/dist/cjs/graphs/Graph.cjs +133 -18
  5. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  8. package/dist/cjs/llm/anthropic/index.cjs +251 -53
  9. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  10. package/dist/cjs/llm/init.cjs +1 -5
  11. package/dist/cjs/llm/init.cjs.map +1 -1
  12. package/dist/cjs/llm/openai/index.cjs +113 -24
  13. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  14. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  15. package/dist/cjs/llm/openrouter/index.cjs +3 -1
  16. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  17. package/dist/cjs/main.cjs +18 -5
  18. package/dist/cjs/main.cjs.map +1 -1
  19. package/dist/cjs/openai/index.cjs +253 -0
  20. package/dist/cjs/openai/index.cjs.map +1 -0
  21. package/dist/cjs/responses/index.cjs +448 -0
  22. package/dist/cjs/responses/index.cjs.map +1 -0
  23. package/dist/cjs/run.cjs +108 -7
  24. package/dist/cjs/run.cjs.map +1 -1
  25. package/dist/cjs/session/AgentSession.cjs +1057 -0
  26. package/dist/cjs/session/AgentSession.cjs.map +1 -0
  27. package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
  28. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
  29. package/dist/cjs/session/handlers.cjs +221 -0
  30. package/dist/cjs/session/handlers.cjs.map +1 -0
  31. package/dist/cjs/session/ids.cjs +22 -0
  32. package/dist/cjs/session/ids.cjs.map +1 -0
  33. package/dist/cjs/session/messageSerialization.cjs +179 -0
  34. package/dist/cjs/session/messageSerialization.cjs.map +1 -0
  35. package/dist/cjs/stream.cjs +475 -11
  36. package/dist/cjs/stream.cjs.map +1 -1
  37. package/dist/cjs/summarization/node.cjs +1 -1
  38. package/dist/cjs/summarization/node.cjs.map +1 -1
  39. package/dist/cjs/tools/ToolNode.cjs +177 -59
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
  42. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
  43. package/dist/cjs/tools/handlers.cjs +1 -1
  44. package/dist/cjs/tools/handlers.cjs.map +1 -1
  45. package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
  46. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
  47. package/dist/esm/events.mjs +23 -1
  48. package/dist/esm/events.mjs.map +1 -1
  49. package/dist/esm/graphs/Graph.mjs +133 -18
  50. package/dist/esm/graphs/Graph.mjs.map +1 -1
  51. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  52. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  53. package/dist/esm/llm/anthropic/index.mjs +251 -53
  54. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  55. package/dist/esm/llm/init.mjs +1 -5
  56. package/dist/esm/llm/init.mjs.map +1 -1
  57. package/dist/esm/llm/openai/index.mjs +113 -25
  58. package/dist/esm/llm/openai/index.mjs.map +1 -1
  59. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  60. package/dist/esm/llm/openrouter/index.mjs +4 -2
  61. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  62. package/dist/esm/main.mjs +5 -1
  63. package/dist/esm/main.mjs.map +1 -1
  64. package/dist/esm/openai/index.mjs +246 -0
  65. package/dist/esm/openai/index.mjs.map +1 -0
  66. package/dist/esm/responses/index.mjs +440 -0
  67. package/dist/esm/responses/index.mjs.map +1 -0
  68. package/dist/esm/run.mjs +108 -7
  69. package/dist/esm/run.mjs.map +1 -1
  70. package/dist/esm/session/AgentSession.mjs +1054 -0
  71. package/dist/esm/session/AgentSession.mjs.map +1 -0
  72. package/dist/esm/session/JsonlSessionStore.mjs +422 -0
  73. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
  74. package/dist/esm/session/handlers.mjs +219 -0
  75. package/dist/esm/session/handlers.mjs.map +1 -0
  76. package/dist/esm/session/ids.mjs +17 -0
  77. package/dist/esm/session/ids.mjs.map +1 -0
  78. package/dist/esm/session/messageSerialization.mjs +173 -0
  79. package/dist/esm/session/messageSerialization.mjs.map +1 -0
  80. package/dist/esm/stream.mjs +476 -12
  81. package/dist/esm/stream.mjs.map +1 -1
  82. package/dist/esm/summarization/node.mjs +1 -1
  83. package/dist/esm/summarization/node.mjs.map +1 -1
  84. package/dist/esm/tools/ToolNode.mjs +177 -59
  85. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  86. package/dist/esm/tools/eagerEventExecution.mjs +107 -0
  87. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
  88. package/dist/esm/tools/handlers.mjs +1 -1
  89. package/dist/esm/tools/handlers.mjs.map +1 -1
  90. package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
  91. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
  92. package/dist/types/events.d.ts +1 -0
  93. package/dist/types/graphs/Graph.d.ts +24 -9
  94. package/dist/types/index.d.ts +1 -0
  95. package/dist/types/llm/openai/index.d.ts +1 -0
  96. package/dist/types/openai/index.d.ts +75 -0
  97. package/dist/types/responses/index.d.ts +97 -0
  98. package/dist/types/run.d.ts +2 -0
  99. package/dist/types/session/AgentSession.d.ts +32 -0
  100. package/dist/types/session/JsonlSessionStore.d.ts +67 -0
  101. package/dist/types/session/handlers.d.ts +8 -0
  102. package/dist/types/session/ids.d.ts +4 -0
  103. package/dist/types/session/index.d.ts +5 -0
  104. package/dist/types/session/messageSerialization.d.ts +7 -0
  105. package/dist/types/session/types.d.ts +191 -0
  106. package/dist/types/tools/ToolNode.d.ts +12 -1
  107. package/dist/types/tools/eagerEventExecution.d.ts +23 -0
  108. package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
  109. package/dist/types/types/hitl.d.ts +4 -0
  110. package/dist/types/types/run.d.ts +11 -1
  111. package/dist/types/types/tools.d.ts +36 -0
  112. package/package.json +19 -2
  113. package/src/__tests__/stream.eagerEventExecution.test.ts +2571 -0
  114. package/src/events.ts +29 -0
  115. package/src/graphs/Graph.ts +224 -50
  116. package/src/graphs/MultiAgentGraph.ts +1 -1
  117. package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
  118. package/src/index.ts +3 -0
  119. package/src/llm/anthropic/index.ts +356 -84
  120. package/src/llm/anthropic/llm.spec.ts +64 -0
  121. package/src/llm/custom-chat-models.smoke.test.ts +175 -4
  122. package/src/llm/openai/contentBlocks.test.ts +35 -0
  123. package/src/llm/openai/deepseek.test.ts +201 -2
  124. package/src/llm/openai/index.ts +171 -26
  125. package/src/llm/openai/utils/index.ts +22 -0
  126. package/src/llm/openrouter/index.ts +4 -2
  127. package/src/openai/__tests__/openai.test.ts +337 -0
  128. package/src/openai/index.ts +404 -0
  129. package/src/responses/__tests__/responses.test.ts +652 -0
  130. package/src/responses/index.ts +677 -0
  131. package/src/run.ts +158 -8
  132. package/src/scripts/compare_pi_vs_ours.ts +592 -173
  133. package/src/scripts/session_live.ts +548 -0
  134. package/src/session/AgentSession.ts +1432 -0
  135. package/src/session/JsonlSessionStore.ts +572 -0
  136. package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
  137. package/src/session/__tests__/handlers.test.ts +161 -0
  138. package/src/session/handlers.ts +272 -0
  139. package/src/session/ids.ts +17 -0
  140. package/src/session/index.ts +44 -0
  141. package/src/session/messageSerialization.ts +207 -0
  142. package/src/session/types.ts +275 -0
  143. package/src/specs/custom-event-await.test.ts +89 -0
  144. package/src/specs/summarization.test.ts +1 -1
  145. package/src/stream.ts +756 -48
  146. package/src/summarization/node.ts +1 -1
  147. package/src/tools/ToolNode.ts +299 -126
  148. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
  149. package/src/tools/__tests__/handlers.test.ts +2 -1
  150. package/src/tools/__tests__/hitl.test.ts +206 -110
  151. package/src/tools/eagerEventExecution.ts +153 -0
  152. package/src/tools/handlers.ts +8 -4
  153. package/src/tools/streamedToolCallSeals.ts +57 -0
  154. package/src/types/hitl.ts +4 -0
  155. package/src/types/run.ts +11 -0
  156. package/src/types/tools.ts +36 -0
  157. package/dist/cjs/llm/text.cjs +0 -69
  158. package/dist/cjs/llm/text.cjs.map +0 -1
  159. package/dist/esm/llm/text.mjs +0 -67
  160. package/dist/esm/llm/text.mjs.map +0 -1
@@ -0,0 +1,153 @@
1
+ import type * as t from '@/types';
2
+
3
+ export function coerceRecordArgs(
4
+ args: unknown
5
+ ): Record<string, unknown> | undefined {
6
+ if (typeof args === 'string') {
7
+ try {
8
+ const parsed = JSON.parse(args) as unknown;
9
+ return coerceRecordArgs(parsed);
10
+ } catch {
11
+ return undefined;
12
+ }
13
+ }
14
+
15
+ if (args === null || typeof args !== 'object' || Array.isArray(args)) {
16
+ return undefined;
17
+ }
18
+
19
+ return args as Record<string, unknown>;
20
+ }
21
+
22
+ export function stableStringify(value: unknown): string {
23
+ if (Array.isArray(value)) {
24
+ return `[${value.map((item) => stableStringify(item)).join(',')}]`;
25
+ }
26
+
27
+ if (value !== null && typeof value === 'object') {
28
+ const record = value as Record<string, unknown>;
29
+ const keys = Object.keys(record).sort();
30
+ return `{${keys
31
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
32
+ .join(',')}}`;
33
+ }
34
+
35
+ return JSON.stringify(value);
36
+ }
37
+
38
+ export function recordArgsEqual(
39
+ left: Record<string, unknown>,
40
+ right: Record<string, unknown>
41
+ ): boolean {
42
+ return stableStringify(left) === stableStringify(right);
43
+ }
44
+
45
+ export function normalizeError(error: unknown): Error {
46
+ return error instanceof Error ? error : new Error(String(error));
47
+ }
48
+
49
+ export type ToolExecutionPlanCall = {
50
+ id?: string;
51
+ name: string;
52
+ args: unknown;
53
+ stepId?: string;
54
+ codeSessionContext?: t.ToolCallRequest['codeSessionContext'];
55
+ };
56
+
57
+ export type ToolExecutionRequestPlan = {
58
+ allRequests: t.ToolCallRequest[];
59
+ requests: t.ToolCallRequest[];
60
+ rejectedResults: t.ToolExecuteResult[];
61
+ };
62
+
63
+ export function buildToolExecutionRequestPlan(args: {
64
+ toolCalls: ToolExecutionPlanCall[];
65
+ usageCount: Map<string, number>;
66
+ invalidArgsBehavior?: 'abort' | 'error-result';
67
+ recordTurn?: (toolName: string, turn: number, callId: string) => void;
68
+ }): ToolExecutionRequestPlan | undefined {
69
+ const invalidArgsBehavior = args.invalidArgsBehavior ?? 'abort';
70
+ const prepared: Array<{
71
+ id: string;
72
+ name: string;
73
+ args: Record<string, unknown>;
74
+ stepId?: string;
75
+ codeSessionContext?: t.ToolCallRequest['codeSessionContext'];
76
+ rejectedErrorMessage?: string;
77
+ }> = [];
78
+
79
+ for (const toolCall of args.toolCalls) {
80
+ if (
81
+ toolCall.id == null ||
82
+ toolCall.id === '' ||
83
+ toolCall.name === ''
84
+ ) {
85
+ return undefined;
86
+ }
87
+ const coercedArgs = coerceRecordArgs(toolCall.args);
88
+ if (coercedArgs == null) {
89
+ if (invalidArgsBehavior === 'abort') {
90
+ return undefined;
91
+ }
92
+ prepared.push({
93
+ id: toolCall.id,
94
+ name: toolCall.name,
95
+ args: {},
96
+ stepId: toolCall.stepId,
97
+ codeSessionContext: toolCall.codeSessionContext,
98
+ rejectedErrorMessage:
99
+ 'Invalid tool call arguments: expected a JSON object.',
100
+ });
101
+ continue;
102
+ }
103
+ prepared.push({
104
+ id: toolCall.id,
105
+ name: toolCall.name,
106
+ args: coercedArgs,
107
+ stepId: toolCall.stepId,
108
+ codeSessionContext: toolCall.codeSessionContext,
109
+ });
110
+ }
111
+
112
+ const nextUsageCount = new Map(args.usageCount);
113
+ const allRequests = prepared.map((toolCall): t.ToolCallRequest => {
114
+ const turn = nextUsageCount.get(toolCall.name) ?? 0;
115
+ nextUsageCount.set(toolCall.name, turn + 1);
116
+ const request: t.ToolCallRequest = {
117
+ id: toolCall.id,
118
+ name: toolCall.name,
119
+ args: toolCall.args,
120
+ stepId: toolCall.stepId,
121
+ turn,
122
+ };
123
+ if (toolCall.codeSessionContext != null) {
124
+ request.codeSessionContext = toolCall.codeSessionContext;
125
+ }
126
+ return request;
127
+ });
128
+ const requests = allRequests.filter(
129
+ (_, index) => prepared[index].rejectedErrorMessage == null
130
+ );
131
+ const rejectedResults = prepared.flatMap((toolCall) => {
132
+ if (toolCall.rejectedErrorMessage == null) {
133
+ return [];
134
+ }
135
+ return [
136
+ {
137
+ toolCallId: toolCall.id,
138
+ status: 'error' as const,
139
+ content: '',
140
+ errorMessage: toolCall.rejectedErrorMessage,
141
+ },
142
+ ];
143
+ });
144
+
145
+ for (const [toolName, count] of nextUsageCount) {
146
+ args.usageCount.set(toolName, count);
147
+ }
148
+ for (const request of allRequests) {
149
+ args.recordTurn?.(request.name, request.turn ?? 0, request.id);
150
+ }
151
+
152
+ return { allRequests, requests, rejectedResults };
153
+ }
@@ -118,10 +118,14 @@ export async function handleToolCallChunks({
118
118
  );
119
119
  }
120
120
 
121
- await graph.dispatchRunStepDelta(stepId, {
122
- type: StepTypes.TOOL_CALLS,
123
- tool_calls: toolCallChunks,
124
- });
121
+ await graph.dispatchRunStepDelta(
122
+ stepId,
123
+ {
124
+ type: StepTypes.TOOL_CALLS,
125
+ tool_calls: toolCallChunks,
126
+ },
127
+ metadata
128
+ );
125
129
  }
126
130
 
127
131
  export const handleToolCalls = async (
@@ -0,0 +1,57 @@
1
+ export const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY =
2
+ 'lc_streamed_tool_call_adapter';
3
+ export const STREAMED_TOOL_CALL_SEAL_METADATA_KEY =
4
+ 'lc_streamed_tool_call_seal';
5
+ export const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = 'openai_responses';
6
+
7
+ export type StreamedToolCallAdapter =
8
+ typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
9
+
10
+ export type StreamedToolCallSeal =
11
+ | {
12
+ kind: 'single';
13
+ id?: string;
14
+ index?: number;
15
+ }
16
+ | {
17
+ kind: 'all';
18
+ };
19
+
20
+ export function getStreamedToolCallAdapter(
21
+ metadata: Record<string, unknown> | undefined
22
+ ): StreamedToolCallAdapter | undefined {
23
+ if (
24
+ metadata?.[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY] ===
25
+ OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER
26
+ ) {
27
+ return OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
28
+ }
29
+ return undefined;
30
+ }
31
+
32
+ export function getStreamedToolCallSeal(
33
+ metadata: Record<string, unknown> | undefined
34
+ ): StreamedToolCallSeal | undefined {
35
+ const seal = metadata?.[STREAMED_TOOL_CALL_SEAL_METADATA_KEY];
36
+ if (seal == null || typeof seal !== 'object') {
37
+ return undefined;
38
+ }
39
+ if (!('kind' in seal)) {
40
+ return undefined;
41
+ }
42
+ if (seal.kind === 'all') {
43
+ return { kind: 'all' };
44
+ }
45
+ if (seal.kind !== 'single') {
46
+ return undefined;
47
+ }
48
+ const id = 'id' in seal && typeof seal.id === 'string' ? seal.id : undefined;
49
+ const index =
50
+ 'index' in seal && typeof seal.index === 'number'
51
+ ? seal.index
52
+ : undefined;
53
+ if (id == null && index == null) {
54
+ return undefined;
55
+ }
56
+ return { kind: 'single', id, index };
57
+ }
package/src/types/hitl.ts CHANGED
@@ -327,6 +327,10 @@ export interface RunInterruptResult<TPayload = HumanInterruptPayload> {
327
327
  interruptId: string;
328
328
  /** `thread_id` the run was bound to — required to resume. */
329
329
  threadId?: string;
330
+ /** LangGraph checkpoint id that contains the paused interrupt task. */
331
+ checkpointId?: string;
332
+ /** LangGraph checkpoint namespace for the paused interrupt task. */
333
+ checkpointNs?: string;
330
334
  /** Structured payload describing what needs human input. */
331
335
  payload: TPayload;
332
336
  }
package/src/types/run.ts CHANGED
@@ -15,6 +15,7 @@ import type {
15
15
  ToolSessionMap,
16
16
  ToolExecutionConfig,
17
17
  ToolOutputReferencesConfig,
18
+ EagerEventToolExecutionConfig,
18
19
  } from '@/types/tools';
19
20
  import type { HumanInTheLoopConfig } from '@/types/hitl';
20
21
  import type { HookRegistry } from '@/hooks';
@@ -161,6 +162,16 @@ export type RunConfig = {
161
162
  * placeholders. Disabled by default so existing runs are unaffected.
162
163
  */
163
164
  toolOutputReferences?: ToolOutputReferencesConfig;
165
+ /**
166
+ * Opt-in latency optimization for event-driven tools. When enabled,
167
+ * the streaming layer may start a tool call as soon as it sees a
168
+ * complete, parseable tool call; ToolNode still waits for the final
169
+ * assistant message before appending ToolMessages, preserving provider
170
+ * ordering. The SDK automatically falls back to normal batch dispatch
171
+ * when hooks, HITL, output references, or ambiguous stream shapes make
172
+ * eager dispatch unsafe.
173
+ */
174
+ eagerEventToolExecution?: EagerEventToolExecutionConfig;
164
175
  /**
165
176
  * Selects the execution backend for built-in code tools. Omit this to keep
166
177
  * the remote LibreChat Code API sandbox. Set `{ engine: 'local' }` to run
@@ -29,6 +29,36 @@ export type ToolRefs = {
29
29
 
30
30
  export type ToolRefGenerator = (tool_calls: ToolCall[]) => ToolRefs;
31
31
 
32
+ export type EagerEventToolExecutionConfig = {
33
+ /**
34
+ * When enabled, event-driven tool calls may be started from the
35
+ * streaming layer as soon as the SDK observes a complete tool call.
36
+ * Results are still held until ToolNode materializes the final
37
+ * ToolMessages so provider message ordering is preserved.
38
+ */
39
+ enabled?: boolean;
40
+ };
41
+
42
+ export type EagerEventToolExecutionOutcome =
43
+ | { results: ToolExecuteResult[]; error?: undefined }
44
+ | { results?: undefined; error: Error };
45
+
46
+ export type EagerEventToolExecution = {
47
+ toolCallId: string;
48
+ toolName: string;
49
+ args: Record<string, unknown>;
50
+ request: ToolCallRequest;
51
+ promise: Promise<EagerEventToolExecutionOutcome>;
52
+ };
53
+
54
+ export type EagerEventToolCallChunkState = {
55
+ id?: string;
56
+ name?: string;
57
+ argsText: string;
58
+ index?: number;
59
+ lastArgsFragment?: string;
60
+ };
61
+
32
62
  export type ToolNodeOptions = {
33
63
  name?: string;
34
64
  tags?: string[];
@@ -51,6 +81,12 @@ export type ToolNodeOptions = {
51
81
  agentId?: string;
52
82
  /** Tool names that must be executed directly (via runTool) even in event-driven mode (e.g., graph-managed handoff tools) */
53
83
  directToolNames?: Set<string>;
84
+ /** Opt-in eager execution for event-driven tool calls. */
85
+ eagerEventToolExecution?: EagerEventToolExecutionConfig;
86
+ /** Shared per-run eager execution registry populated by the stream handler. */
87
+ eagerEventToolExecutions?: Map<string, EagerEventToolExecution>;
88
+ /** Shared per-run per-tool turn counter used by eager and normal event dispatch. */
89
+ eagerEventToolUsageCount?: Map<string, number>;
54
90
  /**
55
91
  * Hook registry for PreToolUse/PostToolUse/PostToolUseFailure/
56
92
  * PermissionDenied lifecycle hooks. Fires for **every** tool the
@@ -1,69 +0,0 @@
1
- 'use strict';
2
-
3
- class TextStream {
4
- text;
5
- currentIndex;
6
- minChunkSize;
7
- maxChunkSize;
8
- delay;
9
- firstWordChunk;
10
- constructor(text, options = {}) {
11
- this.text = text;
12
- this.currentIndex = 0;
13
- this.minChunkSize = options.minChunkSize ?? 4;
14
- this.maxChunkSize = options.maxChunkSize ?? 8;
15
- this.delay = options.delay ?? 20;
16
- this.firstWordChunk = options.firstWordChunk ?? true;
17
- }
18
- randomInt(min, max) {
19
- return Math.floor(Math.random() * (max - min)) + min;
20
- }
21
- static BOUNDARIES = new Set([
22
- ' ',
23
- '.',
24
- ',',
25
- '!',
26
- '?',
27
- ';',
28
- ':',
29
- ]);
30
- findFirstWordBoundary(text, minSize) {
31
- if (minSize >= text.length)
32
- return text.length;
33
- // Ensure we meet the minimum size first
34
- let pos = minSize;
35
- // Look forward until we find a boundary
36
- while (pos < text.length) {
37
- if (TextStream.BOUNDARIES.has(text[pos])) {
38
- return pos + 1; // Include the boundary character
39
- }
40
- pos++;
41
- }
42
- return text.length; // If no boundary found, return entire remaining text
43
- }
44
- async *generateText(signal, progressCallback) {
45
- const { delay, minChunkSize, maxChunkSize } = this;
46
- while (this.currentIndex < this.text.length) {
47
- if (signal?.aborted === true) {
48
- break;
49
- }
50
- await new Promise((resolve) => setTimeout(resolve, delay));
51
- const remainingText = this.text.slice(this.currentIndex);
52
- let chunkSize;
53
- if (this.firstWordChunk) {
54
- chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);
55
- }
56
- else {
57
- const remainingChars = remainingText.length;
58
- chunkSize = Math.min(this.randomInt(minChunkSize, maxChunkSize + 1), remainingChars);
59
- }
60
- const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize);
61
- progressCallback?.(chunk);
62
- yield chunk;
63
- this.currentIndex += chunkSize;
64
- }
65
- }
66
- }
67
-
68
- exports.TextStream = TextStream;
69
- //# sourceMappingURL=text.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"text.cjs","sources":["../../../src/llm/text.ts"],"sourcesContent":["export interface TextStreamOptions {\n minChunkSize?: number;\n maxChunkSize?: number;\n delay?: number;\n firstWordChunk?: boolean;\n}\n\nexport type ProgressCallback = (chunk: string) => void;\nexport type PostChunkCallback = (chunk: string) => void;\n\nexport class TextStream {\n private text: string;\n private currentIndex: number;\n private minChunkSize: number;\n private maxChunkSize: number;\n private delay: number;\n private firstWordChunk: boolean;\n\n constructor(text: string, options: TextStreamOptions = {}) {\n this.text = text;\n this.currentIndex = 0;\n this.minChunkSize = options.minChunkSize ?? 4;\n this.maxChunkSize = options.maxChunkSize ?? 8;\n this.delay = options.delay ?? 20;\n this.firstWordChunk = options.firstWordChunk ?? true;\n }\n\n private randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min)) + min;\n }\n\n private static readonly BOUNDARIES = new Set([\n ' ',\n '.',\n ',',\n '!',\n '?',\n ';',\n ':',\n ]);\n\n private findFirstWordBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) return text.length;\n\n // Ensure we meet the minimum size first\n let pos = minSize;\n\n // Look forward until we find a boundary\n while (pos < text.length) {\n if (TextStream.BOUNDARIES.has(text[pos])) {\n return pos + 1; // Include the boundary character\n }\n pos++;\n }\n\n return text.length; // If no boundary found, return entire remaining text\n }\n\n async *generateText(\n signal?: AbortSignal,\n progressCallback?: ProgressCallback\n ): AsyncGenerator<string, void, unknown> {\n const { delay, minChunkSize, maxChunkSize } = this;\n\n while (this.currentIndex < this.text.length) {\n if (signal?.aborted === true) {\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n const remainingText = this.text.slice(this.currentIndex);\n let chunkSize: number;\n\n if (this.firstWordChunk) {\n chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);\n } else {\n const remainingChars = remainingText.length;\n chunkSize = Math.min(\n this.randomInt(minChunkSize, maxChunkSize + 1),\n remainingChars\n );\n }\n\n const chunk = this.text.slice(\n this.currentIndex,\n this.currentIndex + chunkSize\n );\n progressCallback?.(chunk);\n\n yield chunk;\n this.currentIndex += chunkSize;\n }\n }\n}\n"],"names":[],"mappings":";;MAUa,UAAU,CAAA;AACb,IAAA,IAAI;AACJ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,KAAK;AACL,IAAA,cAAc;IAEtB,WAAA,CAAY,IAAY,EAAE,OAAA,GAA6B,EAAE,EAAA;AACvD,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAChB,QAAA,IAAI,CAAC,YAAY,GAAG,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE;QAChC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI;IACtD;IAEQ,SAAS,CAAC,GAAW,EAAE,GAAW,EAAA;AACxC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG;IACtD;AAEQ,IAAA,OAAgB,UAAU,GAAG,IAAI,GAAG,CAAC;QAC3C,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;AACJ,KAAA,CAAC;IAEM,qBAAqB,CAAC,IAAY,EAAE,OAAe,EAAA;AACzD,QAAA,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM;;QAG9C,IAAI,GAAG,GAAG,OAAO;;AAGjB,QAAA,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACxB,YAAA,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AACxC,gBAAA,OAAO,GAAG,GAAG,CAAC,CAAC;YACjB;AACA,YAAA,GAAG,EAAE;QACP;AAEA,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB;AAEA,IAAA,OAAO,YAAY,CACjB,MAAoB,EACpB,gBAAmC,EAAA;QAEnC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAI;QAElD,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC3C,YAAA,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE;gBAC5B;YACF;AACA,YAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAE1D,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;AACxD,YAAA,IAAI,SAAiB;AAErB,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE;gBACvB,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC;YACrE;iBAAO;AACL,gBAAA,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM;AAC3C,gBAAA,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,CAAC,CAAC,EAC9C,cAAc,CACf;YACH;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAC3B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,YAAY,GAAG,SAAS,CAC9B;AACD,YAAA,gBAAgB,GAAG,KAAK,CAAC;AAEzB,YAAA,MAAM,KAAK;AACX,YAAA,IAAI,CAAC,YAAY,IAAI,SAAS;QAChC;IACF;;;;;"}
@@ -1,67 +0,0 @@
1
- class TextStream {
2
- text;
3
- currentIndex;
4
- minChunkSize;
5
- maxChunkSize;
6
- delay;
7
- firstWordChunk;
8
- constructor(text, options = {}) {
9
- this.text = text;
10
- this.currentIndex = 0;
11
- this.minChunkSize = options.minChunkSize ?? 4;
12
- this.maxChunkSize = options.maxChunkSize ?? 8;
13
- this.delay = options.delay ?? 20;
14
- this.firstWordChunk = options.firstWordChunk ?? true;
15
- }
16
- randomInt(min, max) {
17
- return Math.floor(Math.random() * (max - min)) + min;
18
- }
19
- static BOUNDARIES = new Set([
20
- ' ',
21
- '.',
22
- ',',
23
- '!',
24
- '?',
25
- ';',
26
- ':',
27
- ]);
28
- findFirstWordBoundary(text, minSize) {
29
- if (minSize >= text.length)
30
- return text.length;
31
- // Ensure we meet the minimum size first
32
- let pos = minSize;
33
- // Look forward until we find a boundary
34
- while (pos < text.length) {
35
- if (TextStream.BOUNDARIES.has(text[pos])) {
36
- return pos + 1; // Include the boundary character
37
- }
38
- pos++;
39
- }
40
- return text.length; // If no boundary found, return entire remaining text
41
- }
42
- async *generateText(signal, progressCallback) {
43
- const { delay, minChunkSize, maxChunkSize } = this;
44
- while (this.currentIndex < this.text.length) {
45
- if (signal?.aborted === true) {
46
- break;
47
- }
48
- await new Promise((resolve) => setTimeout(resolve, delay));
49
- const remainingText = this.text.slice(this.currentIndex);
50
- let chunkSize;
51
- if (this.firstWordChunk) {
52
- chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);
53
- }
54
- else {
55
- const remainingChars = remainingText.length;
56
- chunkSize = Math.min(this.randomInt(minChunkSize, maxChunkSize + 1), remainingChars);
57
- }
58
- const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize);
59
- progressCallback?.(chunk);
60
- yield chunk;
61
- this.currentIndex += chunkSize;
62
- }
63
- }
64
- }
65
-
66
- export { TextStream };
67
- //# sourceMappingURL=text.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"text.mjs","sources":["../../../src/llm/text.ts"],"sourcesContent":["export interface TextStreamOptions {\n minChunkSize?: number;\n maxChunkSize?: number;\n delay?: number;\n firstWordChunk?: boolean;\n}\n\nexport type ProgressCallback = (chunk: string) => void;\nexport type PostChunkCallback = (chunk: string) => void;\n\nexport class TextStream {\n private text: string;\n private currentIndex: number;\n private minChunkSize: number;\n private maxChunkSize: number;\n private delay: number;\n private firstWordChunk: boolean;\n\n constructor(text: string, options: TextStreamOptions = {}) {\n this.text = text;\n this.currentIndex = 0;\n this.minChunkSize = options.minChunkSize ?? 4;\n this.maxChunkSize = options.maxChunkSize ?? 8;\n this.delay = options.delay ?? 20;\n this.firstWordChunk = options.firstWordChunk ?? true;\n }\n\n private randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min)) + min;\n }\n\n private static readonly BOUNDARIES = new Set([\n ' ',\n '.',\n ',',\n '!',\n '?',\n ';',\n ':',\n ]);\n\n private findFirstWordBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) return text.length;\n\n // Ensure we meet the minimum size first\n let pos = minSize;\n\n // Look forward until we find a boundary\n while (pos < text.length) {\n if (TextStream.BOUNDARIES.has(text[pos])) {\n return pos + 1; // Include the boundary character\n }\n pos++;\n }\n\n return text.length; // If no boundary found, return entire remaining text\n }\n\n async *generateText(\n signal?: AbortSignal,\n progressCallback?: ProgressCallback\n ): AsyncGenerator<string, void, unknown> {\n const { delay, minChunkSize, maxChunkSize } = this;\n\n while (this.currentIndex < this.text.length) {\n if (signal?.aborted === true) {\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n const remainingText = this.text.slice(this.currentIndex);\n let chunkSize: number;\n\n if (this.firstWordChunk) {\n chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);\n } else {\n const remainingChars = remainingText.length;\n chunkSize = Math.min(\n this.randomInt(minChunkSize, maxChunkSize + 1),\n remainingChars\n );\n }\n\n const chunk = this.text.slice(\n this.currentIndex,\n this.currentIndex + chunkSize\n );\n progressCallback?.(chunk);\n\n yield chunk;\n this.currentIndex += chunkSize;\n }\n }\n}\n"],"names":[],"mappings":"MAUa,UAAU,CAAA;AACb,IAAA,IAAI;AACJ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,KAAK;AACL,IAAA,cAAc;IAEtB,WAAA,CAAY,IAAY,EAAE,OAAA,GAA6B,EAAE,EAAA;AACvD,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAChB,QAAA,IAAI,CAAC,YAAY,GAAG,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE;QAChC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI;IACtD;IAEQ,SAAS,CAAC,GAAW,EAAE,GAAW,EAAA;AACxC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG;IACtD;AAEQ,IAAA,OAAgB,UAAU,GAAG,IAAI,GAAG,CAAC;QAC3C,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;AACJ,KAAA,CAAC;IAEM,qBAAqB,CAAC,IAAY,EAAE,OAAe,EAAA;AACzD,QAAA,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM;;QAG9C,IAAI,GAAG,GAAG,OAAO;;AAGjB,QAAA,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACxB,YAAA,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AACxC,gBAAA,OAAO,GAAG,GAAG,CAAC,CAAC;YACjB;AACA,YAAA,GAAG,EAAE;QACP;AAEA,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB;AAEA,IAAA,OAAO,YAAY,CACjB,MAAoB,EACpB,gBAAmC,EAAA;QAEnC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAI;QAElD,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC3C,YAAA,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE;gBAC5B;YACF;AACA,YAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAE1D,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;AACxD,YAAA,IAAI,SAAiB;AAErB,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE;gBACvB,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC;YACrE;iBAAO;AACL,gBAAA,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM;AAC3C,gBAAA,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,CAAC,CAAC,EAC9C,cAAc,CACf;YACH;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAC3B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,YAAY,GAAG,SAAS,CAC9B;AACD,YAAA,gBAAgB,GAAG,KAAK,CAAC;AAEzB,YAAA,MAAM,KAAK;AACX,YAAA,IAAI,CAAC,YAAY,IAAI,SAAS;QAChC;IACF;;;;;"}