illuma-agents 1.0.2 → 1.0.4

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 (225) hide show
  1. package/LICENSE +25 -21
  2. package/dist/cjs/agents/AgentContext.cjs +222 -0
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -0
  4. package/dist/cjs/common/enum.cjs +5 -4
  5. package/dist/cjs/common/enum.cjs.map +1 -1
  6. package/dist/cjs/events.cjs +7 -5
  7. package/dist/cjs/events.cjs.map +1 -1
  8. package/dist/cjs/graphs/Graph.cjs +328 -207
  9. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs +507 -0
  11. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
  12. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  13. package/dist/cjs/llm/google/index.cjs.map +1 -1
  14. package/dist/cjs/llm/ollama/index.cjs.map +1 -1
  15. package/dist/cjs/llm/openai/index.cjs +35 -0
  16. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openai/utils/index.cjs +3 -1
  18. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  19. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  20. package/dist/cjs/llm/providers.cjs +0 -2
  21. package/dist/cjs/llm/providers.cjs.map +1 -1
  22. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  23. package/dist/cjs/main.cjs +12 -1
  24. package/dist/cjs/main.cjs.map +1 -1
  25. package/dist/cjs/messages/cache.cjs +123 -0
  26. package/dist/cjs/messages/cache.cjs.map +1 -0
  27. package/dist/cjs/messages/content.cjs +53 -0
  28. package/dist/cjs/messages/content.cjs.map +1 -0
  29. package/dist/cjs/messages/format.cjs +17 -29
  30. package/dist/cjs/messages/format.cjs.map +1 -1
  31. package/dist/cjs/run.cjs +119 -74
  32. package/dist/cjs/run.cjs.map +1 -1
  33. package/dist/cjs/stream.cjs +77 -73
  34. package/dist/cjs/stream.cjs.map +1 -1
  35. package/dist/cjs/tools/Calculator.cjs +45 -0
  36. package/dist/cjs/tools/Calculator.cjs.map +1 -0
  37. package/dist/cjs/tools/CodeExecutor.cjs +22 -22
  38. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  39. package/dist/cjs/tools/ToolNode.cjs +5 -3
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/handlers.cjs +20 -20
  42. package/dist/cjs/tools/handlers.cjs.map +1 -1
  43. package/dist/cjs/utils/events.cjs +31 -0
  44. package/dist/cjs/utils/events.cjs.map +1 -0
  45. package/dist/cjs/utils/handlers.cjs +70 -0
  46. package/dist/cjs/utils/handlers.cjs.map +1 -0
  47. package/dist/cjs/utils/tokens.cjs +54 -7
  48. package/dist/cjs/utils/tokens.cjs.map +1 -1
  49. package/dist/esm/agents/AgentContext.mjs +220 -0
  50. package/dist/esm/agents/AgentContext.mjs.map +1 -0
  51. package/dist/esm/common/enum.mjs +5 -4
  52. package/dist/esm/common/enum.mjs.map +1 -1
  53. package/dist/esm/events.mjs +7 -5
  54. package/dist/esm/events.mjs.map +1 -1
  55. package/dist/esm/graphs/Graph.mjs +330 -209
  56. package/dist/esm/graphs/Graph.mjs.map +1 -1
  57. package/dist/esm/graphs/MultiAgentGraph.mjs +505 -0
  58. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
  59. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  60. package/dist/esm/llm/google/index.mjs.map +1 -1
  61. package/dist/esm/llm/ollama/index.mjs.map +1 -1
  62. package/dist/esm/llm/openai/index.mjs +35 -0
  63. package/dist/esm/llm/openai/index.mjs.map +1 -1
  64. package/dist/esm/llm/openai/utils/index.mjs +3 -1
  65. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  66. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  67. package/dist/esm/llm/providers.mjs +0 -2
  68. package/dist/esm/llm/providers.mjs.map +1 -1
  69. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  70. package/dist/esm/main.mjs +7 -2
  71. package/dist/esm/main.mjs.map +1 -1
  72. package/dist/esm/messages/cache.mjs +120 -0
  73. package/dist/esm/messages/cache.mjs.map +1 -0
  74. package/dist/esm/messages/content.mjs +51 -0
  75. package/dist/esm/messages/content.mjs.map +1 -0
  76. package/dist/esm/messages/format.mjs +18 -29
  77. package/dist/esm/messages/format.mjs.map +1 -1
  78. package/dist/esm/run.mjs +119 -74
  79. package/dist/esm/run.mjs.map +1 -1
  80. package/dist/esm/stream.mjs +77 -73
  81. package/dist/esm/stream.mjs.map +1 -1
  82. package/dist/esm/tools/Calculator.mjs +24 -0
  83. package/dist/esm/tools/Calculator.mjs.map +1 -0
  84. package/dist/esm/tools/CodeExecutor.mjs +22 -22
  85. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  86. package/dist/esm/tools/ToolNode.mjs +5 -3
  87. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  88. package/dist/esm/tools/handlers.mjs +20 -20
  89. package/dist/esm/tools/handlers.mjs.map +1 -1
  90. package/dist/esm/utils/events.mjs +29 -0
  91. package/dist/esm/utils/events.mjs.map +1 -0
  92. package/dist/esm/utils/handlers.mjs +68 -0
  93. package/dist/esm/utils/handlers.mjs.map +1 -0
  94. package/dist/esm/utils/tokens.mjs +54 -8
  95. package/dist/esm/utils/tokens.mjs.map +1 -1
  96. package/dist/types/agents/AgentContext.d.ts +94 -0
  97. package/dist/types/common/enum.d.ts +7 -5
  98. package/dist/types/events.d.ts +3 -3
  99. package/dist/types/graphs/Graph.d.ts +60 -66
  100. package/dist/types/graphs/MultiAgentGraph.d.ts +47 -0
  101. package/dist/types/graphs/index.d.ts +1 -0
  102. package/dist/types/index.d.ts +1 -0
  103. package/dist/types/llm/openai/index.d.ts +10 -0
  104. package/dist/types/messages/cache.d.ts +20 -0
  105. package/dist/types/messages/content.d.ts +7 -0
  106. package/dist/types/messages/format.d.ts +1 -7
  107. package/dist/types/messages/index.d.ts +2 -0
  108. package/dist/types/messages/reducer.d.ts +9 -0
  109. package/dist/types/run.d.ts +16 -10
  110. package/dist/types/stream.d.ts +4 -3
  111. package/dist/types/tools/Calculator.d.ts +8 -0
  112. package/dist/types/tools/ToolNode.d.ts +1 -1
  113. package/dist/types/tools/handlers.d.ts +9 -7
  114. package/dist/types/tools/search/tool.d.ts +4 -4
  115. package/dist/types/types/graph.d.ts +124 -11
  116. package/dist/types/types/llm.d.ts +13 -9
  117. package/dist/types/types/messages.d.ts +4 -0
  118. package/dist/types/types/run.d.ts +46 -8
  119. package/dist/types/types/stream.d.ts +3 -2
  120. package/dist/types/utils/events.d.ts +6 -0
  121. package/dist/types/utils/handlers.d.ts +34 -0
  122. package/dist/types/utils/index.d.ts +1 -0
  123. package/dist/types/utils/tokens.d.ts +24 -0
  124. package/package.json +162 -145
  125. package/src/agents/AgentContext.ts +323 -0
  126. package/src/common/enum.ts +177 -176
  127. package/src/events.ts +197 -191
  128. package/src/graphs/Graph.ts +1058 -846
  129. package/src/graphs/MultiAgentGraph.ts +598 -0
  130. package/src/graphs/index.ts +2 -1
  131. package/src/index.ts +25 -24
  132. package/src/llm/anthropic/index.ts +413 -413
  133. package/src/llm/google/index.ts +222 -222
  134. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -88
  135. package/src/llm/ollama/index.ts +92 -92
  136. package/src/llm/openai/index.ts +894 -853
  137. package/src/llm/openai/utils/index.ts +920 -918
  138. package/src/llm/openrouter/index.ts +60 -60
  139. package/src/llm/providers.ts +55 -57
  140. package/src/llm/vertexai/index.ts +360 -360
  141. package/src/messages/cache.test.ts +461 -0
  142. package/src/messages/cache.ts +151 -0
  143. package/src/messages/content.test.ts +362 -0
  144. package/src/messages/content.ts +63 -0
  145. package/src/messages/format.ts +611 -625
  146. package/src/messages/formatAgentMessages.test.ts +1144 -917
  147. package/src/messages/index.ts +6 -4
  148. package/src/messages/reducer.ts +80 -0
  149. package/src/run.ts +447 -381
  150. package/src/scripts/abort.ts +157 -138
  151. package/src/scripts/ant_web_search.ts +158 -158
  152. package/src/scripts/cli.ts +172 -167
  153. package/src/scripts/cli2.ts +133 -125
  154. package/src/scripts/cli3.ts +184 -178
  155. package/src/scripts/cli4.ts +191 -184
  156. package/src/scripts/cli5.ts +191 -184
  157. package/src/scripts/code_exec.ts +213 -214
  158. package/src/scripts/code_exec_simple.ts +147 -129
  159. package/src/scripts/content.ts +138 -120
  160. package/src/scripts/handoff-test.ts +135 -0
  161. package/src/scripts/multi-agent-chain.ts +278 -0
  162. package/src/scripts/multi-agent-conditional.ts +220 -0
  163. package/src/scripts/multi-agent-document-review-chain.ts +197 -0
  164. package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
  165. package/src/scripts/multi-agent-parallel.ts +343 -0
  166. package/src/scripts/multi-agent-sequence.ts +212 -0
  167. package/src/scripts/multi-agent-supervisor.ts +364 -0
  168. package/src/scripts/multi-agent-test.ts +186 -0
  169. package/src/scripts/search.ts +146 -150
  170. package/src/scripts/simple.ts +225 -225
  171. package/src/scripts/stream.ts +140 -122
  172. package/src/scripts/test-custom-prompt-key.ts +145 -0
  173. package/src/scripts/test-handoff-input.ts +170 -0
  174. package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
  175. package/src/scripts/test-tools-before-handoff.ts +222 -0
  176. package/src/scripts/tools.ts +153 -155
  177. package/src/specs/agent-handoffs.test.ts +889 -0
  178. package/src/specs/anthropic.simple.test.ts +320 -317
  179. package/src/specs/azure.simple.test.ts +325 -316
  180. package/src/specs/openai.simple.test.ts +311 -316
  181. package/src/specs/openrouter.simple.test.ts +107 -0
  182. package/src/specs/prune.test.ts +758 -763
  183. package/src/specs/reasoning.test.ts +201 -165
  184. package/src/specs/thinking-prune.test.ts +769 -703
  185. package/src/specs/token-memoization.test.ts +39 -0
  186. package/src/stream.ts +664 -651
  187. package/src/tools/Calculator.test.ts +278 -0
  188. package/src/tools/Calculator.ts +25 -0
  189. package/src/tools/CodeExecutor.ts +220 -220
  190. package/src/tools/ToolNode.ts +170 -170
  191. package/src/tools/handlers.ts +341 -336
  192. package/src/types/graph.ts +372 -185
  193. package/src/types/llm.ts +141 -140
  194. package/src/types/messages.ts +4 -0
  195. package/src/types/run.ts +128 -89
  196. package/src/types/stream.ts +401 -400
  197. package/src/utils/events.ts +32 -0
  198. package/src/utils/handlers.ts +107 -0
  199. package/src/utils/index.ts +6 -5
  200. package/src/utils/llmConfig.ts +183 -183
  201. package/src/utils/tokens.ts +129 -70
  202. package/dist/types/scripts/abort.d.ts +0 -1
  203. package/dist/types/scripts/ant_web_search.d.ts +0 -1
  204. package/dist/types/scripts/args.d.ts +0 -7
  205. package/dist/types/scripts/caching.d.ts +0 -1
  206. package/dist/types/scripts/cli.d.ts +0 -1
  207. package/dist/types/scripts/cli2.d.ts +0 -1
  208. package/dist/types/scripts/cli3.d.ts +0 -1
  209. package/dist/types/scripts/cli4.d.ts +0 -1
  210. package/dist/types/scripts/cli5.d.ts +0 -1
  211. package/dist/types/scripts/code_exec.d.ts +0 -1
  212. package/dist/types/scripts/code_exec_files.d.ts +0 -1
  213. package/dist/types/scripts/code_exec_simple.d.ts +0 -1
  214. package/dist/types/scripts/content.d.ts +0 -1
  215. package/dist/types/scripts/empty_input.d.ts +0 -1
  216. package/dist/types/scripts/image.d.ts +0 -1
  217. package/dist/types/scripts/memory.d.ts +0 -1
  218. package/dist/types/scripts/search.d.ts +0 -1
  219. package/dist/types/scripts/simple.d.ts +0 -1
  220. package/dist/types/scripts/stream.d.ts +0 -1
  221. package/dist/types/scripts/thinking.d.ts +0 -1
  222. package/dist/types/scripts/tools.d.ts +0 -1
  223. package/dist/types/specs/spec.utils.d.ts +0 -1
  224. package/dist/types/tools/example.d.ts +0 -78
  225. package/src/tools/example.ts +0 -129
@@ -1,413 +1,413 @@
1
- import { AIMessageChunk } from '@langchain/core/messages';
2
- import { ChatAnthropicMessages } from '@langchain/anthropic';
3
- import { ChatGenerationChunk } from '@langchain/core/outputs';
4
- import type { BaseChatModelParams } from '@langchain/core/language_models/chat_models';
5
- import type {
6
- BaseMessage,
7
- UsageMetadata,
8
- MessageContentComplex,
9
- } from '@langchain/core/messages';
10
- import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
11
- import type { AnthropicInput } from '@langchain/anthropic';
12
- import type { Anthropic } from '@anthropic-ai/sdk';
13
- import type {
14
- AnthropicMessageCreateParams,
15
- AnthropicStreamingMessageCreateParams,
16
- AnthropicStreamUsage,
17
- AnthropicMessageStartEvent,
18
- AnthropicMessageDeltaEvent,
19
- } from '@/llm/anthropic/types';
20
- import { _makeMessageChunkFromAnthropicEvent } from './utils/message_outputs';
21
- import { _convertMessagesToAnthropicPayload } from './utils/message_inputs';
22
- import { handleToolChoice } from './utils/tools';
23
- import { TextStream } from '@/llm/text';
24
-
25
- function _toolsInParams(
26
- params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
27
- ): boolean {
28
- return !!(params.tools && params.tools.length > 0);
29
- }
30
- function _documentsInParams(
31
- params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
32
- ): boolean {
33
- for (const message of params.messages ?? []) {
34
- if (typeof message.content === 'string') {
35
- continue;
36
- }
37
- for (const block of message.content ?? []) {
38
- if (
39
- typeof block === 'object' &&
40
- block != null &&
41
- block.type === 'document' &&
42
- typeof block.citations === 'object' &&
43
- block.citations.enabled
44
- ) {
45
- return true;
46
- }
47
- }
48
- }
49
- return false;
50
- }
51
-
52
- function _thinkingInParams(
53
- params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
54
- ): boolean {
55
- return !!(params.thinking && params.thinking.type === 'enabled');
56
- }
57
-
58
- function extractToken(
59
- chunk: AIMessageChunk
60
- ): [string, 'string' | 'input' | 'content'] | [undefined] {
61
- if (typeof chunk.content === 'string') {
62
- return [chunk.content, 'string'];
63
- } else if (
64
- Array.isArray(chunk.content) &&
65
- chunk.content.length >= 1 &&
66
- 'input' in chunk.content[0]
67
- ) {
68
- return typeof chunk.content[0].input === 'string'
69
- ? [chunk.content[0].input, 'input']
70
- : [JSON.stringify(chunk.content[0].input), 'input'];
71
- } else if (
72
- Array.isArray(chunk.content) &&
73
- chunk.content.length >= 1 &&
74
- 'text' in chunk.content[0]
75
- ) {
76
- return [chunk.content[0].text, 'content'];
77
- } else if (
78
- Array.isArray(chunk.content) &&
79
- chunk.content.length >= 1 &&
80
- 'thinking' in chunk.content[0]
81
- ) {
82
- return [chunk.content[0].thinking, 'content'];
83
- }
84
- return [undefined];
85
- }
86
-
87
- function cloneChunk(
88
- text: string,
89
- tokenType: string,
90
- chunk: AIMessageChunk
91
- ): AIMessageChunk {
92
- if (tokenType === 'string') {
93
- return new AIMessageChunk(Object.assign({}, chunk, { content: text }));
94
- } else if (tokenType === 'input') {
95
- return chunk;
96
- }
97
- const content = chunk.content[0] as MessageContentComplex;
98
- if (tokenType === 'content' && content.type === 'text') {
99
- return new AIMessageChunk(
100
- Object.assign({}, chunk, {
101
- content: [Object.assign({}, content, { text })],
102
- })
103
- );
104
- } else if (tokenType === 'content' && content.type === 'text_delta') {
105
- return new AIMessageChunk(
106
- Object.assign({}, chunk, {
107
- content: [Object.assign({}, content, { text })],
108
- })
109
- );
110
- } else if (tokenType === 'content' && content.type?.startsWith('thinking')) {
111
- return new AIMessageChunk(
112
- Object.assign({}, chunk, {
113
- content: [Object.assign({}, content, { thinking: text })],
114
- })
115
- );
116
- }
117
-
118
- return chunk;
119
- }
120
-
121
- export type CustomAnthropicInput = AnthropicInput & {
122
- _lc_stream_delay?: number;
123
- } & BaseChatModelParams;
124
-
125
- /**
126
- * A type representing additional parameters that can be passed to the
127
- * Anthropic API.
128
- */
129
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
- type Kwargs = Record<string, any>;
131
-
132
- export class CustomAnthropic extends ChatAnthropicMessages {
133
- _lc_stream_delay: number;
134
- private message_start: AnthropicMessageStartEvent | undefined;
135
- private message_delta: AnthropicMessageDeltaEvent | undefined;
136
- private tools_in_params?: boolean;
137
- private emitted_usage?: boolean;
138
- top_k: number | undefined;
139
- constructor(fields?: CustomAnthropicInput) {
140
- super(fields);
141
- this.resetTokenEvents();
142
- this.setDirectFields(fields);
143
- this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
144
- }
145
-
146
- static lc_name(): 'IllumaAnthropic' {
147
- return 'IllumaAnthropic';
148
- }
149
-
150
- /**
151
- * Get the parameters used to invoke the model
152
- */
153
- override invocationParams(
154
- options?: this['ParsedCallOptions']
155
- ): Omit<
156
- AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams,
157
- 'messages'
158
- > &
159
- Kwargs {
160
- const tool_choice:
161
- | Anthropic.Messages.ToolChoiceAuto
162
- | Anthropic.Messages.ToolChoiceAny
163
- | Anthropic.Messages.ToolChoiceTool
164
- | undefined = handleToolChoice(options?.tool_choice);
165
-
166
- if (this.thinking.type === 'enabled') {
167
- if (this.top_k !== -1 && (this.top_k as number | undefined) != null) {
168
- throw new Error('topK is not supported when thinking is enabled');
169
- }
170
- if (this.topP !== -1 && (this.topP as number | undefined) != null) {
171
- throw new Error('topP is not supported when thinking is enabled');
172
- }
173
- if (
174
- this.temperature !== 1 &&
175
- (this.temperature as number | undefined) != null
176
- ) {
177
- throw new Error(
178
- 'temperature is not supported when thinking is enabled'
179
- );
180
- }
181
-
182
- return {
183
- model: this.model,
184
- stop_sequences: options?.stop ?? this.stopSequences,
185
- stream: this.streaming,
186
- max_tokens: this.maxTokens,
187
- tools: this.formatStructuredToolToAnthropic(options?.tools),
188
- tool_choice,
189
- thinking: this.thinking,
190
- ...this.invocationKwargs,
191
- };
192
- }
193
- return {
194
- model: this.model,
195
- temperature: this.temperature,
196
- top_k: this.top_k,
197
- top_p: this.topP,
198
- stop_sequences: options?.stop ?? this.stopSequences,
199
- stream: this.streaming,
200
- max_tokens: this.maxTokens,
201
- tools: this.formatStructuredToolToAnthropic(options?.tools),
202
- tool_choice,
203
- thinking: this.thinking,
204
- ...this.invocationKwargs,
205
- };
206
- }
207
-
208
- /**
209
- * Get stream usage as returned by this client's API response.
210
- * @returns The stream usage object.
211
- */
212
- getStreamUsage(): UsageMetadata | undefined {
213
- if (this.emitted_usage === true) {
214
- return;
215
- }
216
- const inputUsage = this.message_start?.message.usage as
217
- | undefined
218
- | AnthropicStreamUsage;
219
- const outputUsage = this.message_delta?.usage as
220
- | undefined
221
- | Partial<AnthropicStreamUsage>;
222
- if (!outputUsage) {
223
- return;
224
- }
225
- const totalUsage: UsageMetadata = {
226
- input_tokens: inputUsage?.input_tokens ?? 0,
227
- output_tokens: outputUsage.output_tokens ?? 0,
228
- total_tokens:
229
- (inputUsage?.input_tokens ?? 0) + (outputUsage.output_tokens ?? 0),
230
- };
231
-
232
- if (
233
- inputUsage?.cache_creation_input_tokens != null ||
234
- inputUsage?.cache_read_input_tokens != null
235
- ) {
236
- totalUsage.input_token_details = {
237
- cache_creation: inputUsage.cache_creation_input_tokens ?? 0,
238
- cache_read: inputUsage.cache_read_input_tokens ?? 0,
239
- };
240
- }
241
-
242
- this.emitted_usage = true;
243
- return totalUsage;
244
- }
245
-
246
- resetTokenEvents(): void {
247
- this.message_start = undefined;
248
- this.message_delta = undefined;
249
- this.emitted_usage = undefined;
250
- this.tools_in_params = undefined;
251
- }
252
-
253
- setDirectFields(fields?: CustomAnthropicInput): void {
254
- this.temperature = fields?.temperature ?? undefined;
255
- this.topP = fields?.topP ?? undefined;
256
- this.top_k = fields?.topK;
257
- if (this.temperature === -1 || this.temperature === 1) {
258
- this.temperature = undefined;
259
- }
260
- if (this.topP === -1) {
261
- this.topP = undefined;
262
- }
263
- if (this.top_k === -1) {
264
- this.top_k = undefined;
265
- }
266
- }
267
-
268
- private createGenerationChunk({
269
- token,
270
- chunk,
271
- usageMetadata,
272
- shouldStreamUsage,
273
- }: {
274
- token?: string;
275
- chunk: AIMessageChunk;
276
- shouldStreamUsage: boolean;
277
- usageMetadata?: UsageMetadata;
278
- }): ChatGenerationChunk {
279
- const usage_metadata = shouldStreamUsage
280
- ? (usageMetadata ?? chunk.usage_metadata)
281
- : undefined;
282
- return new ChatGenerationChunk({
283
- message: new AIMessageChunk({
284
- // Just yield chunk as it is and tool_use will be concat by BaseChatModel._generateUncached().
285
- content: chunk.content,
286
- additional_kwargs: chunk.additional_kwargs,
287
- tool_call_chunks: chunk.tool_call_chunks,
288
- response_metadata: chunk.response_metadata,
289
- usage_metadata,
290
- id: chunk.id,
291
- }),
292
- text: token ?? '',
293
- });
294
- }
295
-
296
- async *_streamResponseChunks(
297
- messages: BaseMessage[],
298
- options: this['ParsedCallOptions'],
299
- runManager?: CallbackManagerForLLMRun
300
- ): AsyncGenerator<ChatGenerationChunk> {
301
- this.resetTokenEvents();
302
- const params = this.invocationParams(options);
303
- const formattedMessages = _convertMessagesToAnthropicPayload(messages);
304
- const payload = {
305
- ...params,
306
- ...formattedMessages,
307
- stream: true,
308
- } as const;
309
- const coerceContentToString =
310
- !_toolsInParams(payload) &&
311
- !_documentsInParams(payload) &&
312
- !_thinkingInParams(payload);
313
-
314
- const stream = await this.createStreamWithRetry(payload, {
315
- headers: options.headers,
316
- });
317
-
318
- const shouldStreamUsage = this.streamUsage ?? options.streamUsage;
319
-
320
- for await (const data of stream) {
321
- if (options.signal?.aborted === true) {
322
- stream.controller.abort();
323
- throw new Error('AbortError: User aborted the request.');
324
- }
325
-
326
- if (data.type === 'message_start') {
327
- this.message_start = data as AnthropicMessageStartEvent;
328
- } else if (data.type === 'message_delta') {
329
- this.message_delta = data as AnthropicMessageDeltaEvent;
330
- }
331
-
332
- let usageMetadata: UsageMetadata | undefined;
333
- if (this.tools_in_params !== true && this.emitted_usage !== true) {
334
- usageMetadata = this.getStreamUsage();
335
- }
336
-
337
- const result = _makeMessageChunkFromAnthropicEvent(data, {
338
- streamUsage: shouldStreamUsage,
339
- coerceContentToString,
340
- });
341
- if (!result) continue;
342
-
343
- const { chunk } = result;
344
- const [token = '', tokenType] = extractToken(chunk);
345
-
346
- if (
347
- !tokenType ||
348
- tokenType === 'input' ||
349
- (token === '' && (usageMetadata != null || chunk.id != null))
350
- ) {
351
- const generationChunk = this.createGenerationChunk({
352
- token,
353
- chunk,
354
- usageMetadata,
355
- shouldStreamUsage,
356
- });
357
- yield generationChunk;
358
- await runManager?.handleLLMNewToken(
359
- token,
360
- undefined,
361
- undefined,
362
- undefined,
363
- undefined,
364
- { chunk: generationChunk }
365
- );
366
- continue;
367
- }
368
-
369
- const textStream = new TextStream(token, {
370
- delay: this._lc_stream_delay,
371
- firstWordChunk: true,
372
- minChunkSize: 4,
373
- maxChunkSize: 8,
374
- });
375
-
376
- const generator = textStream.generateText(options.signal);
377
- try {
378
- let emittedUsage = false;
379
- for await (const currentToken of generator) {
380
- if ((options.signal as AbortSignal | undefined)?.aborted === true) {
381
- break;
382
- }
383
- const newChunk = cloneChunk(currentToken, tokenType, chunk);
384
-
385
- const generationChunk = this.createGenerationChunk({
386
- token: currentToken,
387
- chunk: newChunk,
388
- usageMetadata: emittedUsage ? undefined : usageMetadata,
389
- shouldStreamUsage,
390
- });
391
-
392
- if (usageMetadata && !emittedUsage) {
393
- emittedUsage = true;
394
- }
395
- yield generationChunk;
396
-
397
- await runManager?.handleLLMNewToken(
398
- currentToken,
399
- undefined,
400
- undefined,
401
- undefined,
402
- undefined,
403
- { chunk: generationChunk }
404
- );
405
- }
406
- } finally {
407
- await generator.return();
408
- }
409
- }
410
-
411
- this.resetTokenEvents();
412
- }
413
- }
1
+ import { AIMessageChunk } from '@langchain/core/messages';
2
+ import { ChatAnthropicMessages } from '@langchain/anthropic';
3
+ import { ChatGenerationChunk } from '@langchain/core/outputs';
4
+ import type { BaseChatModelParams } from '@langchain/core/language_models/chat_models';
5
+ import type {
6
+ BaseMessage,
7
+ UsageMetadata,
8
+ MessageContentComplex,
9
+ } from '@langchain/core/messages';
10
+ import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
11
+ import type { AnthropicInput } from '@langchain/anthropic';
12
+ import type { Anthropic } from '@anthropic-ai/sdk';
13
+ import type {
14
+ AnthropicMessageCreateParams,
15
+ AnthropicStreamingMessageCreateParams,
16
+ AnthropicStreamUsage,
17
+ AnthropicMessageStartEvent,
18
+ AnthropicMessageDeltaEvent,
19
+ } from '@/llm/anthropic/types';
20
+ import { _makeMessageChunkFromAnthropicEvent } from './utils/message_outputs';
21
+ import { _convertMessagesToAnthropicPayload } from './utils/message_inputs';
22
+ import { handleToolChoice } from './utils/tools';
23
+ import { TextStream } from '@/llm/text';
24
+
25
+ function _toolsInParams(
26
+ params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
27
+ ): boolean {
28
+ return !!(params.tools && params.tools.length > 0);
29
+ }
30
+ function _documentsInParams(
31
+ params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
32
+ ): boolean {
33
+ for (const message of params.messages ?? []) {
34
+ if (typeof message.content === 'string') {
35
+ continue;
36
+ }
37
+ for (const block of message.content ?? []) {
38
+ if (
39
+ typeof block === 'object' &&
40
+ block != null &&
41
+ block.type === 'document' &&
42
+ typeof block.citations === 'object' &&
43
+ block.citations.enabled
44
+ ) {
45
+ return true;
46
+ }
47
+ }
48
+ }
49
+ return false;
50
+ }
51
+
52
+ function _thinkingInParams(
53
+ params: AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams
54
+ ): boolean {
55
+ return !!(params.thinking && params.thinking.type === 'enabled');
56
+ }
57
+
58
+ function extractToken(
59
+ chunk: AIMessageChunk
60
+ ): [string, 'string' | 'input' | 'content'] | [undefined] {
61
+ if (typeof chunk.content === 'string') {
62
+ return [chunk.content, 'string'];
63
+ } else if (
64
+ Array.isArray(chunk.content) &&
65
+ chunk.content.length >= 1 &&
66
+ 'input' in chunk.content[0]
67
+ ) {
68
+ return typeof chunk.content[0].input === 'string'
69
+ ? [chunk.content[0].input, 'input']
70
+ : [JSON.stringify(chunk.content[0].input), 'input'];
71
+ } else if (
72
+ Array.isArray(chunk.content) &&
73
+ chunk.content.length >= 1 &&
74
+ 'text' in chunk.content[0]
75
+ ) {
76
+ return [chunk.content[0].text, 'content'];
77
+ } else if (
78
+ Array.isArray(chunk.content) &&
79
+ chunk.content.length >= 1 &&
80
+ 'thinking' in chunk.content[0]
81
+ ) {
82
+ return [chunk.content[0].thinking, 'content'];
83
+ }
84
+ return [undefined];
85
+ }
86
+
87
+ function cloneChunk(
88
+ text: string,
89
+ tokenType: string,
90
+ chunk: AIMessageChunk
91
+ ): AIMessageChunk {
92
+ if (tokenType === 'string') {
93
+ return new AIMessageChunk(Object.assign({}, chunk, { content: text }));
94
+ } else if (tokenType === 'input') {
95
+ return chunk;
96
+ }
97
+ const content = chunk.content[0] as MessageContentComplex;
98
+ if (tokenType === 'content' && content.type === 'text') {
99
+ return new AIMessageChunk(
100
+ Object.assign({}, chunk, {
101
+ content: [Object.assign({}, content, { text })],
102
+ })
103
+ );
104
+ } else if (tokenType === 'content' && content.type === 'text_delta') {
105
+ return new AIMessageChunk(
106
+ Object.assign({}, chunk, {
107
+ content: [Object.assign({}, content, { text })],
108
+ })
109
+ );
110
+ } else if (tokenType === 'content' && content.type?.startsWith('thinking')) {
111
+ return new AIMessageChunk(
112
+ Object.assign({}, chunk, {
113
+ content: [Object.assign({}, content, { thinking: text })],
114
+ })
115
+ );
116
+ }
117
+
118
+ return chunk;
119
+ }
120
+
121
+ export type CustomAnthropicInput = AnthropicInput & {
122
+ _lc_stream_delay?: number;
123
+ } & BaseChatModelParams;
124
+
125
+ /**
126
+ * A type representing additional parameters that can be passed to the
127
+ * Anthropic API.
128
+ */
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ type Kwargs = Record<string, any>;
131
+
132
+ export class CustomAnthropic extends ChatAnthropicMessages {
133
+ _lc_stream_delay: number;
134
+ private message_start: AnthropicMessageStartEvent | undefined;
135
+ private message_delta: AnthropicMessageDeltaEvent | undefined;
136
+ private tools_in_params?: boolean;
137
+ private emitted_usage?: boolean;
138
+ top_k: number | undefined;
139
+ constructor(fields?: CustomAnthropicInput) {
140
+ super(fields);
141
+ this.resetTokenEvents();
142
+ this.setDirectFields(fields);
143
+ this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
144
+ }
145
+
146
+ static lc_name(): 'IllumaAnthropic' {
147
+ return 'IllumaAnthropic';
148
+ }
149
+
150
+ /**
151
+ * Get the parameters used to invoke the model
152
+ */
153
+ override invocationParams(
154
+ options?: this['ParsedCallOptions']
155
+ ): Omit<
156
+ AnthropicMessageCreateParams | AnthropicStreamingMessageCreateParams,
157
+ 'messages'
158
+ > &
159
+ Kwargs {
160
+ const tool_choice:
161
+ | Anthropic.Messages.ToolChoiceAuto
162
+ | Anthropic.Messages.ToolChoiceAny
163
+ | Anthropic.Messages.ToolChoiceTool
164
+ | undefined = handleToolChoice(options?.tool_choice);
165
+
166
+ if (this.thinking.type === 'enabled') {
167
+ if (this.top_k !== -1 && (this.top_k as number | undefined) != null) {
168
+ throw new Error('topK is not supported when thinking is enabled');
169
+ }
170
+ if (this.topP !== -1 && (this.topP as number | undefined) != null) {
171
+ throw new Error('topP is not supported when thinking is enabled');
172
+ }
173
+ if (
174
+ this.temperature !== 1 &&
175
+ (this.temperature as number | undefined) != null
176
+ ) {
177
+ throw new Error(
178
+ 'temperature is not supported when thinking is enabled'
179
+ );
180
+ }
181
+
182
+ return {
183
+ model: this.model,
184
+ stop_sequences: options?.stop ?? this.stopSequences,
185
+ stream: this.streaming,
186
+ max_tokens: this.maxTokens,
187
+ tools: this.formatStructuredToolToAnthropic(options?.tools),
188
+ tool_choice,
189
+ thinking: this.thinking,
190
+ ...this.invocationKwargs,
191
+ };
192
+ }
193
+ return {
194
+ model: this.model,
195
+ temperature: this.temperature,
196
+ top_k: this.top_k,
197
+ top_p: this.topP,
198
+ stop_sequences: options?.stop ?? this.stopSequences,
199
+ stream: this.streaming,
200
+ max_tokens: this.maxTokens,
201
+ tools: this.formatStructuredToolToAnthropic(options?.tools),
202
+ tool_choice,
203
+ thinking: this.thinking,
204
+ ...this.invocationKwargs,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * Get stream usage as returned by this client's API response.
210
+ * @returns The stream usage object.
211
+ */
212
+ getStreamUsage(): UsageMetadata | undefined {
213
+ if (this.emitted_usage === true) {
214
+ return;
215
+ }
216
+ const inputUsage = this.message_start?.message.usage as
217
+ | undefined
218
+ | AnthropicStreamUsage;
219
+ const outputUsage = this.message_delta?.usage as
220
+ | undefined
221
+ | Partial<AnthropicStreamUsage>;
222
+ if (!outputUsage) {
223
+ return;
224
+ }
225
+ const totalUsage: UsageMetadata = {
226
+ input_tokens: inputUsage?.input_tokens ?? 0,
227
+ output_tokens: outputUsage.output_tokens ?? 0,
228
+ total_tokens:
229
+ (inputUsage?.input_tokens ?? 0) + (outputUsage.output_tokens ?? 0),
230
+ };
231
+
232
+ if (
233
+ inputUsage?.cache_creation_input_tokens != null ||
234
+ inputUsage?.cache_read_input_tokens != null
235
+ ) {
236
+ totalUsage.input_token_details = {
237
+ cache_creation: inputUsage.cache_creation_input_tokens ?? 0,
238
+ cache_read: inputUsage.cache_read_input_tokens ?? 0,
239
+ };
240
+ }
241
+
242
+ this.emitted_usage = true;
243
+ return totalUsage;
244
+ }
245
+
246
+ resetTokenEvents(): void {
247
+ this.message_start = undefined;
248
+ this.message_delta = undefined;
249
+ this.emitted_usage = undefined;
250
+ this.tools_in_params = undefined;
251
+ }
252
+
253
+ setDirectFields(fields?: CustomAnthropicInput): void {
254
+ this.temperature = fields?.temperature ?? undefined;
255
+ this.topP = fields?.topP ?? undefined;
256
+ this.top_k = fields?.topK;
257
+ if (this.temperature === -1 || this.temperature === 1) {
258
+ this.temperature = undefined;
259
+ }
260
+ if (this.topP === -1) {
261
+ this.topP = undefined;
262
+ }
263
+ if (this.top_k === -1) {
264
+ this.top_k = undefined;
265
+ }
266
+ }
267
+
268
+ private createGenerationChunk({
269
+ token,
270
+ chunk,
271
+ usageMetadata,
272
+ shouldStreamUsage,
273
+ }: {
274
+ token?: string;
275
+ chunk: AIMessageChunk;
276
+ shouldStreamUsage: boolean;
277
+ usageMetadata?: UsageMetadata;
278
+ }): ChatGenerationChunk {
279
+ const usage_metadata = shouldStreamUsage
280
+ ? (usageMetadata ?? chunk.usage_metadata)
281
+ : undefined;
282
+ return new ChatGenerationChunk({
283
+ message: new AIMessageChunk({
284
+ // Just yield chunk as it is and tool_use will be concat by BaseChatModel._generateUncached().
285
+ content: chunk.content,
286
+ additional_kwargs: chunk.additional_kwargs,
287
+ tool_call_chunks: chunk.tool_call_chunks,
288
+ response_metadata: chunk.response_metadata,
289
+ usage_metadata,
290
+ id: chunk.id,
291
+ }),
292
+ text: token ?? '',
293
+ });
294
+ }
295
+
296
+ async *_streamResponseChunks(
297
+ messages: BaseMessage[],
298
+ options: this['ParsedCallOptions'],
299
+ runManager?: CallbackManagerForLLMRun
300
+ ): AsyncGenerator<ChatGenerationChunk> {
301
+ this.resetTokenEvents();
302
+ const params = this.invocationParams(options);
303
+ const formattedMessages = _convertMessagesToAnthropicPayload(messages);
304
+ const payload = {
305
+ ...params,
306
+ ...formattedMessages,
307
+ stream: true,
308
+ } as const;
309
+ const coerceContentToString =
310
+ !_toolsInParams(payload) &&
311
+ !_documentsInParams(payload) &&
312
+ !_thinkingInParams(payload);
313
+
314
+ const stream = await this.createStreamWithRetry(payload, {
315
+ headers: options.headers,
316
+ });
317
+
318
+ const shouldStreamUsage = this.streamUsage ?? options.streamUsage;
319
+
320
+ for await (const data of stream) {
321
+ if (options.signal?.aborted === true) {
322
+ stream.controller.abort();
323
+ throw new Error('AbortError: User aborted the request.');
324
+ }
325
+
326
+ if (data.type === 'message_start') {
327
+ this.message_start = data as AnthropicMessageStartEvent;
328
+ } else if (data.type === 'message_delta') {
329
+ this.message_delta = data as AnthropicMessageDeltaEvent;
330
+ }
331
+
332
+ let usageMetadata: UsageMetadata | undefined;
333
+ if (this.tools_in_params !== true && this.emitted_usage !== true) {
334
+ usageMetadata = this.getStreamUsage();
335
+ }
336
+
337
+ const result = _makeMessageChunkFromAnthropicEvent(data, {
338
+ streamUsage: shouldStreamUsage,
339
+ coerceContentToString,
340
+ });
341
+ if (!result) continue;
342
+
343
+ const { chunk } = result;
344
+ const [token = '', tokenType] = extractToken(chunk);
345
+
346
+ if (
347
+ !tokenType ||
348
+ tokenType === 'input' ||
349
+ (token === '' && (usageMetadata != null || chunk.id != null))
350
+ ) {
351
+ const generationChunk = this.createGenerationChunk({
352
+ token,
353
+ chunk,
354
+ usageMetadata,
355
+ shouldStreamUsage,
356
+ });
357
+ yield generationChunk;
358
+ await runManager?.handleLLMNewToken(
359
+ token,
360
+ undefined,
361
+ undefined,
362
+ undefined,
363
+ undefined,
364
+ { chunk: generationChunk }
365
+ );
366
+ continue;
367
+ }
368
+
369
+ const textStream = new TextStream(token, {
370
+ delay: this._lc_stream_delay,
371
+ firstWordChunk: true,
372
+ minChunkSize: 4,
373
+ maxChunkSize: 8,
374
+ });
375
+
376
+ const generator = textStream.generateText(options.signal);
377
+ try {
378
+ let emittedUsage = false;
379
+ for await (const currentToken of generator) {
380
+ if ((options.signal as AbortSignal | undefined)?.aborted === true) {
381
+ break;
382
+ }
383
+ const newChunk = cloneChunk(currentToken, tokenType, chunk);
384
+
385
+ const generationChunk = this.createGenerationChunk({
386
+ token: currentToken,
387
+ chunk: newChunk,
388
+ usageMetadata: emittedUsage ? undefined : usageMetadata,
389
+ shouldStreamUsage,
390
+ });
391
+
392
+ if (usageMetadata && !emittedUsage) {
393
+ emittedUsage = true;
394
+ }
395
+ yield generationChunk;
396
+
397
+ await runManager?.handleLLMNewToken(
398
+ currentToken,
399
+ undefined,
400
+ undefined,
401
+ undefined,
402
+ undefined,
403
+ { chunk: generationChunk }
404
+ );
405
+ }
406
+ } finally {
407
+ await generator.return();
408
+ }
409
+ }
410
+
411
+ this.resetTokenEvents();
412
+ }
413
+ }