@celljs/ai-anthropic 3.4.0

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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/lib/common/api/anthropic-api.d.ts +16 -0
  4. package/lib/common/api/anthropic-api.d.ts.map +1 -0
  5. package/lib/common/api/anthropic-api.js +76 -0
  6. package/lib/common/api/anthropic-api.js.map +1 -0
  7. package/lib/common/api/anthropic-api.spec.d.ts +2 -0
  8. package/lib/common/api/anthropic-api.spec.d.ts.map +1 -0
  9. package/lib/common/api/anthropic-api.spec.js +146 -0
  10. package/lib/common/api/anthropic-api.spec.js.map +1 -0
  11. package/lib/common/api/anthropic-model.d.ts +34 -0
  12. package/lib/common/api/anthropic-model.d.ts.map +1 -0
  13. package/lib/common/api/anthropic-model.js +39 -0
  14. package/lib/common/api/anthropic-model.js.map +1 -0
  15. package/lib/common/api/api-protocol.d.ts +40 -0
  16. package/lib/common/api/api-protocol.d.ts.map +1 -0
  17. package/lib/common/api/api-protocol.js +6 -0
  18. package/lib/common/api/api-protocol.js.map +1 -0
  19. package/lib/common/api/chat-request.d.ts +144 -0
  20. package/lib/common/api/chat-request.d.ts.map +1 -0
  21. package/lib/common/api/chat-request.js +151 -0
  22. package/lib/common/api/chat-request.js.map +1 -0
  23. package/lib/common/api/chat-response.d.ts +72 -0
  24. package/lib/common/api/chat-response.d.ts.map +1 -0
  25. package/lib/common/api/chat-response.js +96 -0
  26. package/lib/common/api/chat-response.js.map +1 -0
  27. package/lib/common/api/content-block.d.ts +100 -0
  28. package/lib/common/api/content-block.d.ts.map +1 -0
  29. package/lib/common/api/content-block.js +84 -0
  30. package/lib/common/api/content-block.js.map +1 -0
  31. package/lib/common/api/event.d.ts +188 -0
  32. package/lib/common/api/event.d.ts.map +1 -0
  33. package/lib/common/api/event.js +249 -0
  34. package/lib/common/api/event.js.map +1 -0
  35. package/lib/common/api/index.d.ts +10 -0
  36. package/lib/common/api/index.d.ts.map +1 -0
  37. package/lib/common/api/index.js +13 -0
  38. package/lib/common/api/index.js.map +1 -0
  39. package/lib/common/api/message.d.ts +41 -0
  40. package/lib/common/api/message.d.ts.map +1 -0
  41. package/lib/common/api/message.js +45 -0
  42. package/lib/common/api/message.js.map +1 -0
  43. package/lib/common/api/usage.d.ts +15 -0
  44. package/lib/common/api/usage.d.ts.map +1 -0
  45. package/lib/common/api/usage.js +24 -0
  46. package/lib/common/api/usage.js.map +1 -0
  47. package/lib/common/index.d.ts +3 -0
  48. package/lib/common/index.d.ts.map +1 -0
  49. package/lib/common/index.js +6 -0
  50. package/lib/common/index.js.map +1 -0
  51. package/lib/common/model/anthropic-chat-model.d.ts +20 -0
  52. package/lib/common/model/anthropic-chat-model.d.ts.map +1 -0
  53. package/lib/common/model/anthropic-chat-model.js +161 -0
  54. package/lib/common/model/anthropic-chat-model.js.map +1 -0
  55. package/lib/common/model/anthropic-chat-model.spec.d.ts +2 -0
  56. package/lib/common/model/anthropic-chat-model.spec.d.ts.map +1 -0
  57. package/lib/common/model/anthropic-chat-model.spec.js +64 -0
  58. package/lib/common/model/anthropic-chat-model.spec.js.map +1 -0
  59. package/lib/common/model/anthropic-chat-options.d.ts +55 -0
  60. package/lib/common/model/anthropic-chat-options.d.ts.map +1 -0
  61. package/lib/common/model/anthropic-chat-options.js +157 -0
  62. package/lib/common/model/anthropic-chat-options.js.map +1 -0
  63. package/lib/common/model/index.d.ts +3 -0
  64. package/lib/common/model/index.d.ts.map +1 -0
  65. package/lib/common/model/index.js +6 -0
  66. package/lib/common/model/index.js.map +1 -0
  67. package/lib/common/module.d.ts +4 -0
  68. package/lib/common/module.d.ts.map +1 -0
  69. package/lib/common/module.js +6 -0
  70. package/lib/common/module.js.map +1 -0
  71. package/lib/common/test/mock-anthropic-api.d.ts +9 -0
  72. package/lib/common/test/mock-anthropic-api.d.ts.map +1 -0
  73. package/lib/common/test/mock-anthropic-api.js +46 -0
  74. package/lib/common/test/mock-anthropic-api.js.map +1 -0
  75. package/lib/common/test/test-container.d.ts +3 -0
  76. package/lib/common/test/test-container.d.ts.map +1 -0
  77. package/lib/common/test/test-container.js +15 -0
  78. package/lib/common/test/test-container.js.map +1 -0
  79. package/lib/common/utils/index.d.ts +2 -0
  80. package/lib/common/utils/index.d.ts.map +1 -0
  81. package/lib/common/utils/index.js +5 -0
  82. package/lib/common/utils/index.js.map +1 -0
  83. package/lib/common/utils/stream-util.d.ts +9 -0
  84. package/lib/common/utils/stream-util.d.ts.map +1 -0
  85. package/lib/common/utils/stream-util.js +114 -0
  86. package/lib/common/utils/stream-util.js.map +1 -0
  87. package/lib/package.spec.d.ts +1 -0
  88. package/lib/package.spec.d.ts.map +1 -0
  89. package/lib/package.spec.js +4 -0
  90. package/lib/package.spec.js.map +1 -0
  91. package/package.json +46 -0
  92. package/src/common/api/anthropic-api.spec.ts +165 -0
  93. package/src/common/api/anthropic-api.ts +90 -0
  94. package/src/common/api/anthropic-model.ts +42 -0
  95. package/src/common/api/api-protocol.ts +45 -0
  96. package/src/common/api/chat-request.ts +280 -0
  97. package/src/common/api/chat-response.ts +161 -0
  98. package/src/common/api/content-block.ts +155 -0
  99. package/src/common/api/event.ts +376 -0
  100. package/src/common/api/index.ts +9 -0
  101. package/src/common/api/message.ts +49 -0
  102. package/src/common/api/usage.ts +24 -0
  103. package/src/common/index.ts +2 -0
  104. package/src/common/model/anthropic-chat-model.spec.ts +67 -0
  105. package/src/common/model/anthropic-chat-model.ts +187 -0
  106. package/src/common/model/anthropic-chat-options.ts +152 -0
  107. package/src/common/model/index.ts +2 -0
  108. package/src/common/module.ts +4 -0
  109. package/src/common/test/mock-anthropic-api.ts +62 -0
  110. package/src/common/test/test-container.ts +12 -0
  111. package/src/common/utils/index.ts +1 -0
  112. package/src/common/utils/stream-util.ts +142 -0
  113. package/src/package.spec.ts +4 -0
@@ -0,0 +1,187 @@
1
+ import {
2
+ AssistantMessage,
3
+ ChatGenerationMetadata,
4
+ ChatModel,
5
+ ChatResponse,
6
+ ChatResponseMetadata,
7
+ FunctionCallback,
8
+ Generation,
9
+ Media,
10
+ MessageType,
11
+ Prompt,
12
+ ToolCall,
13
+ ToolResponseMessage,
14
+ Usage,
15
+ UserMessage
16
+ } from '@celljs/ai-core';
17
+ import { Autowired, Component, IllegalArgumentError, Logger } from '@celljs/core';
18
+ import { Observable } from 'rxjs';
19
+ import { AnthropicAPI, ChatResponse as AnthriopicChatResponse, AnthropicMessage, ChatRequest, ContentBlock, ContentBlockType, Role, Source, Tool } from '../api';
20
+ import { AnthropicChatOptions } from './anthropic-chat-options';
21
+ import { map } from 'rxjs/operators';
22
+
23
+ @Component(ChatModel)
24
+ export class AnthropicChatModel implements ChatModel {
25
+ @Autowired(AnthropicChatOptions)
26
+ protected readonly defaultOptions: AnthropicChatOptions;
27
+
28
+ @Autowired(AnthropicAPI)
29
+ protected readonly chatApi: AnthropicAPI;
30
+
31
+ @Autowired(Logger)
32
+ protected readonly logger: Logger;
33
+
34
+ protected getContentBlockTypeByMedia(media: Media): ContentBlockType {
35
+ if (media.mediaType.startsWith('image')) {
36
+ return ContentBlockType.IMAGE;
37
+ } else if (media.mediaType.startsWith('pdf')) {
38
+ return ContentBlockType.DOCUMENT;
39
+ }
40
+ throw new IllegalArgumentError(`Unsupported media type: ${media.mediaType}. Supported types are: images (image/*) and PDF documents (application/pdf)`);
41
+ }
42
+
43
+ protected fromMediaData(mediaData: any): string {
44
+ if (mediaData instanceof Uint8Array) {
45
+ return btoa(String.fromCharCode(...mediaData));
46
+ } else if (typeof mediaData === 'string') {
47
+ return mediaData;
48
+ } else {
49
+ throw new IllegalArgumentError(`Unsupported media data type: ${typeof mediaData}`);
50
+ }
51
+ }
52
+
53
+ protected resolveFunctionCallbacks(functions: Set<string>): FunctionCallback[] {
54
+ return [];
55
+ }
56
+
57
+ protected getFunctionTools(functions: Set<string>): Tool[] {
58
+ return this.resolveFunctionCallbacks(functions).map(func => new Tool(func.name, func.description, JSON.parse(func.inputTypeSchema)));
59
+ }
60
+
61
+ protected createRequest(prompt: Prompt, stream: boolean): ChatRequest {
62
+ const userMessages = prompt.instructions
63
+ .filter(message => message.messageType !== MessageType.SYSTEM)
64
+ .map(message => {
65
+ if (message.messageType === MessageType.USER) {
66
+ const contents = [new ContentBlock(ContentBlockType.TEXT, undefined, message.content)];
67
+ if (message instanceof UserMessage) {
68
+ if (message.media.length > 0) {
69
+ const mediaContent = message.media.map(media => {
70
+ const contentBlockType = this.getContentBlockTypeByMedia(media);
71
+ const source = new Source('base64', media.mediaType, this.fromMediaData(media.data));
72
+ return new ContentBlock(contentBlockType, source);
73
+ });
74
+ contents.push(...mediaContent);
75
+ }
76
+ }
77
+ return new AnthropicMessage(contents, message.messageType === MessageType.USER ? Role.USER : Role.ASSISTANT);
78
+ } else if (message.messageType === MessageType.ASSISTANT) {
79
+ const assistantMessage = message as AssistantMessage;
80
+ const contentBlocks = [];
81
+ if (message.content) {
82
+ contentBlocks.push(new ContentBlock(ContentBlockType.TEXT, undefined, message.content));
83
+ }
84
+ if (assistantMessage.toolCalls.length > 0) {
85
+ for (const toolCall of assistantMessage.toolCalls) {
86
+ contentBlocks.push(
87
+ new ContentBlock(ContentBlockType.TOOL_USE, undefined, undefined, undefined, toolCall.id, toolCall.name, JSON.parse(toolCall.arguments))
88
+ );
89
+ }
90
+ }
91
+ return new AnthropicMessage(contentBlocks, Role.ASSISTANT);
92
+ } else if (message.messageType === MessageType.TOOL) {
93
+ const toolResponses = (message as ToolResponseMessage).responses.map(
94
+ toolResponse => new ContentBlock(ContentBlockType.TOOL_RESULT, undefined, undefined, undefined, toolResponse.id, toolResponse.responseData)
95
+ );
96
+ return new AnthropicMessage(toolResponses, Role.USER);
97
+ } else {
98
+ throw new IllegalArgumentError(`Unsupported message type: ${message.messageType}`);
99
+ }
100
+ });
101
+
102
+ const systemPrompt = prompt.instructions
103
+ .filter(m => m.messageType === MessageType.SYSTEM)
104
+ .map(m => m.content)
105
+ .join('\n');
106
+
107
+ let request = new ChatRequest(this.defaultOptions.model, userMessages, systemPrompt, this.defaultOptions.maxTokens, this.defaultOptions.temperature, stream);
108
+
109
+ if (prompt.options) {
110
+ request = Object.assign(request, prompt.options);
111
+ }
112
+
113
+ const functionsForThisRequest: Set<string> = new Set();
114
+
115
+ if (this.defaultOptions.functions.size > 0) {
116
+ this.defaultOptions.functions.forEach(func => functionsForThisRequest.add(func));
117
+ }
118
+
119
+ if (functionsForThisRequest.size > 0) {
120
+ const tools = this.getFunctionTools(functionsForThisRequest);
121
+ request.tools = tools;
122
+ }
123
+
124
+ return request;
125
+ }
126
+
127
+ protected from(response: AnthriopicChatResponse): ChatResponseMetadata {
128
+ return ChatResponseMetadata.builder()
129
+ .id(response.id)
130
+ .model(response.model)
131
+ .usage(Usage.from(response.usage.inputTokens, response.usage.outputTokens))
132
+ .keyValue('stop-reason', response.stopReason)
133
+ .keyValue('stop-sequence', response.stopSequence)
134
+ .keyValue('type', response.type)
135
+ .build();
136
+ }
137
+
138
+ protected toChatResponse(response?: AnthriopicChatResponse): ChatResponse {
139
+ if (response === undefined) {
140
+ this.logger.warn('Null chat completion returned');
141
+ return ChatResponse.from([]);
142
+ }
143
+
144
+ const generations = response.content
145
+ .filter(content => content.type !== ContentBlockType.TOOL_USE)
146
+ .map(content => Generation.from(new AssistantMessage(content.text), ChatGenerationMetadata.from(response.stopReason)));
147
+
148
+ const allGenerations = [...generations];
149
+
150
+ if (response.stopReason && generations.length === 0) {
151
+ const generation = Generation.from(new AssistantMessage(), ChatGenerationMetadata.from(response.stopReason));
152
+ allGenerations.push(generation);
153
+ }
154
+
155
+ const toolToUseList = response.content.filter(c => c.type === ContentBlockType.TOOL_USE);
156
+ if (toolToUseList.length > 0) {
157
+ const toolCalls: ToolCall[] = [];
158
+ for (const toolToUse of toolToUseList) {
159
+ toolCalls.push({
160
+ id: toolToUse.id!,
161
+ type: 'function',
162
+ name: toolToUse.name!,
163
+ arguments: JSON.stringify(toolToUse.input)
164
+ });
165
+ }
166
+
167
+ const assistantMessage = new AssistantMessage('', [], {}, toolCalls);
168
+ const toolCallGeneration = Generation.from(assistantMessage, ChatGenerationMetadata.from(response.stopReason));
169
+ allGenerations.push(toolCallGeneration);
170
+ }
171
+
172
+ return ChatResponse.from(allGenerations, this.from(response));
173
+ }
174
+
175
+ async call(prompt: Prompt): Promise<ChatResponse> {
176
+ const request = this.createRequest(prompt, false);
177
+ const chatResponse = await this.chatApi.chat(request);
178
+ return this.toChatResponse(chatResponse);
179
+ }
180
+
181
+ async stream(prompt: Prompt): Promise<Observable<ChatResponse>> {
182
+ const request = this.createRequest(prompt, true);
183
+ const chatResponse = await this.chatApi.streamingChat(request);
184
+ return chatResponse.pipe(map(chunk => this.toChatResponse(chunk)));
185
+ }
186
+
187
+ }
@@ -0,0 +1,152 @@
1
+ import { FunctionCallback, FunctionCallingOptions } from '@celljs/ai-core';
2
+ import { Exclude, Expose } from 'class-transformer';
3
+ import { Metadata } from '../api/chat-request';
4
+ import { Constant } from '@celljs/core';
5
+
6
+ /**
7
+ * The options to be used when sending a chat request to the Anthropic API.
8
+ */
9
+ @Constant(AnthropicChatOptions, new AnthropicChatOptions())
10
+ export class AnthropicChatOptions implements FunctionCallingOptions {
11
+
12
+ model: string;
13
+
14
+ @Expose({ name: 'max_tokens' })
15
+ maxTokens: number;
16
+
17
+ metadata: Metadata;
18
+
19
+ @Expose({ name: 'stop_sequences' })
20
+ stopSequences: string[];
21
+
22
+ @Expose({ name: 'temperature' })
23
+ temperature: number;
24
+
25
+ @Expose({ name: 'top_p' })
26
+ topP: number;
27
+
28
+ @Expose({ name: 'top_k' })
29
+ topK: number;
30
+
31
+ /**
32
+ * Tool Function Callbacks to register with the ChatModel. For Prompt
33
+ * Options the functionCallbacks are automatically enabled for the duration of the
34
+ * prompt execution. For Default Options the functionCallbacks are registered but
35
+ * disabled by default. Use the enableFunctions to set the functions from the registry
36
+ * to be used by the ChatModel chat completion requests.
37
+ */
38
+ @Exclude()
39
+ functionCallbacks: FunctionCallback[] = [];
40
+
41
+ /**
42
+ * List of functions, identified by their names, to configure for function calling in
43
+ * the chat completion requests. Functions with those names must exist in the
44
+ * functionCallbacks registry. The {@link #functionCallbacks} from the PromptOptions
45
+ * are automatically enabled for the duration of the prompt execution
46
+ *
47
+ * Note that function enabled with the default options are enabled for all chat
48
+ * completion requests. This could impact the token count and the billing. If the
49
+ * functions is set in a prompt options, then the enabled functions are only active
50
+ * for the duration of this prompt execution
51
+ */
52
+ @Exclude()
53
+ functions: Set<string> = new Set<string>();
54
+ @Exclude()
55
+ proxyToolCalls: boolean = false;
56
+
57
+ @Exclude()
58
+ toolContext: Record<string, any> = {};
59
+
60
+ static builder(): AnthropicChatOptionsBuilder {
61
+ return new AnthropicChatOptionsBuilder();
62
+ }
63
+
64
+ static fromOptions(fromOptions: AnthropicChatOptions): AnthropicChatOptions {
65
+ return AnthropicChatOptions.builder()
66
+ .model(fromOptions.model)
67
+ .maxTokens(fromOptions.maxTokens)
68
+ .metadata(fromOptions.metadata)
69
+ .stopSequences(fromOptions.stopSequences)
70
+ .temperature(fromOptions.temperature)
71
+ .topP(fromOptions.topP)
72
+ .topK(fromOptions.topK)
73
+ .functionCallbacks(fromOptions.functionCallbacks)
74
+ .functions(fromOptions.functions)
75
+ .proxyToolCalls(fromOptions.proxyToolCalls)
76
+ .toolContext(fromOptions.toolContext)
77
+ .build();
78
+ }
79
+ }
80
+
81
+ export class AnthropicChatOptionsBuilder {
82
+
83
+ private options: AnthropicChatOptions = new AnthropicChatOptions();
84
+
85
+ model(model: string): AnthropicChatOptionsBuilder {
86
+ this.options.model = model;
87
+ return this;
88
+ }
89
+
90
+ maxTokens(maxTokens: number): AnthropicChatOptionsBuilder {
91
+ this.options.maxTokens = maxTokens;
92
+ return this;
93
+ }
94
+
95
+ metadata(metadata: Metadata): AnthropicChatOptionsBuilder {
96
+ this.options.metadata = metadata;
97
+ return this;
98
+ }
99
+
100
+ stopSequences(stopSequences: string[]): AnthropicChatOptionsBuilder {
101
+ this.options.stopSequences = stopSequences;
102
+ return this;
103
+ }
104
+
105
+ temperature(temperature: number): AnthropicChatOptionsBuilder {
106
+ this.options.temperature = temperature;
107
+ return this;
108
+ }
109
+
110
+ topP(topP: number): AnthropicChatOptionsBuilder {
111
+ this.options.topP = topP;
112
+ return this;
113
+ }
114
+
115
+ topK(topK: number): AnthropicChatOptionsBuilder {
116
+ this.options.topK = topK;
117
+ return this;
118
+ }
119
+
120
+ functionCallbacks(functionCallbacks: FunctionCallback[]): AnthropicChatOptionsBuilder {
121
+ this.options.functionCallbacks = functionCallbacks;
122
+ return this;
123
+ }
124
+
125
+ functions(functions: Set<string>): AnthropicChatOptionsBuilder {
126
+ this.options.functions = functions;
127
+ return this;
128
+ }
129
+
130
+ function(functionName: string): AnthropicChatOptionsBuilder {
131
+ this.options.functions.add(functionName);
132
+ return this;
133
+ }
134
+
135
+ proxyToolCalls(proxyToolCalls: boolean): AnthropicChatOptionsBuilder {
136
+ this.options.proxyToolCalls = proxyToolCalls;
137
+ return this;
138
+ }
139
+
140
+ toolContext(toolContext: Record<string, any>): AnthropicChatOptionsBuilder {
141
+ if (this.options.toolContext === undefined) {
142
+ this.options.toolContext = toolContext;
143
+ } else {
144
+ this.options.toolContext = { ...this.options.toolContext, ...toolContext };
145
+ }
146
+ return this;
147
+ }
148
+
149
+ build(): AnthropicChatOptions {
150
+ return this.options;
151
+ }
152
+ }
@@ -0,0 +1,2 @@
1
+ export * from './anthropic-chat-model';
2
+ export * from './anthropic-chat-options';
@@ -0,0 +1,4 @@
1
+ import { autoBind } from '@celljs/core';
2
+ import './index';
3
+
4
+ export default autoBind();
@@ -0,0 +1,62 @@
1
+ import { Component } from '@celljs/core';
2
+ import { Observable, from } from 'rxjs';
3
+ import { AnthropicAPI } from '../api/api-protocol';
4
+ import { ChatRequest } from '../api/chat-request';
5
+ import { ChatResponse } from '../api/chat-response';
6
+ import { ContentBlockType } from '../api/content-block';
7
+ import { Role } from '../api/message';
8
+
9
+ @Component(AnthropicAPI)
10
+ export class MockAnthropicAPI implements AnthropicAPI {
11
+ async chat(chatRequest: ChatRequest): Promise<ChatResponse> {
12
+ if (chatRequest.stream) {
13
+ throw new Error('Request must set the stream property to false.');
14
+ }
15
+
16
+ return new ChatResponse(
17
+ 'msg_123',
18
+ 'message',
19
+ Role.ASSISTANT,
20
+ [
21
+ {
22
+ type: ContentBlockType.TEXT,
23
+ text: 'How can I assist you today?'
24
+ }
25
+ ],
26
+ chatRequest.model,
27
+ 'end_turn',
28
+ undefined,
29
+ {
30
+ inputTokens: 10,
31
+ outputTokens: 20
32
+ }
33
+ );
34
+ }
35
+
36
+ async streamingChat(chatRequest: ChatRequest): Promise<Observable<ChatResponse>> {
37
+ if (!chatRequest.stream) {
38
+ throw new Error('Request must set the stream property to true.');
39
+ }
40
+
41
+ const response = new ChatResponse(
42
+ 'msg_123',
43
+ 'message',
44
+ Role.ASSISTANT,
45
+ [
46
+ {
47
+ type: ContentBlockType.TEXT,
48
+ text: 'How can I assist you today?'
49
+ }
50
+ ],
51
+ chatRequest.model,
52
+ 'end_turn',
53
+ undefined,
54
+ {
55
+ inputTokens: 10,
56
+ outputTokens: 20
57
+ }
58
+ );
59
+
60
+ return from([response]);
61
+ }
62
+ }
@@ -0,0 +1,12 @@
1
+ import { coreTestModule } from '@celljs/core/lib/common/test/test-module';
2
+ import httpModule from '@celljs/http/lib/common/module';
3
+ import { autoBind, ContainerFactory, ContainerProvider } from '@celljs/core';
4
+ import '../index';
5
+
6
+ const ollamaTestModule = autoBind();
7
+
8
+ export const createContainer = () => {
9
+ const container = ContainerFactory.create(coreTestModule, httpModule, ollamaTestModule);
10
+ ContainerProvider.set(container);
11
+ return container;
12
+ };
@@ -0,0 +1 @@
1
+ export * from './stream-util';
@@ -0,0 +1,142 @@
1
+ import { Assert } from '@celljs/core';
2
+ import { ChatResponse, ChatResponseBuilderReference } from '../api/chat-response';
3
+ import {
4
+ ContentBlockDeltaEvent,
5
+ ContentBlockDeltaJson,
6
+ ContentBlockDeltaText,
7
+ ContentBlockStartEvent,
8
+ ContentBlockText,
9
+ ContentBlockToolUse,
10
+ EventType,
11
+ MessageDeltaEvent,
12
+ MessageStartEvent,
13
+ ToolUseAggregationEvent,
14
+ AnthropicEvent
15
+ } from '../api/event';
16
+ import { ContentBlock, ContentBlockType } from '../api/content-block';
17
+
18
+ export abstract class StreamUtil {
19
+ static isToolUseStart(event: AnthropicEvent): boolean {
20
+ if (!event || !event.type || event.type !== EventType.CONTENT_BLOCK_START) {
21
+ return false;
22
+ }
23
+ return (event as ContentBlockStartEvent).contentBlock.type === ContentBlockType.TOOL_USE;
24
+ }
25
+
26
+ static isToolUseFinish(event: AnthropicEvent): boolean {
27
+ if (!event || !event.type || event.type !== EventType.CONTENT_BLOCK_STOP) {
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+
33
+ static mergeToolUseEvents(previousEvent: AnthropicEvent, event: AnthropicEvent): AnthropicEvent {
34
+ if (!previousEvent || !event) {
35
+ return event;
36
+ }
37
+
38
+ const eventAggregator = previousEvent as ToolUseAggregationEvent;
39
+
40
+ if (event.type === EventType.CONTENT_BLOCK_START) {
41
+ const contentBlockStart = event as ContentBlockStartEvent;
42
+
43
+ if (contentBlockStart.contentBlock.type === ContentBlockType.TOOL_USE) {
44
+ const cbToolUse = contentBlockStart.contentBlock as ContentBlockToolUse;
45
+
46
+ eventAggregator.id = cbToolUse.id;
47
+ eventAggregator.name = cbToolUse.name;
48
+ eventAggregator.index = contentBlockStart.index;
49
+ eventAggregator.partialJson = '';
50
+ }
51
+ } else if (event.type === EventType.CONTENT_BLOCK_DELTA) {
52
+ const contentBlockDelta = event as ContentBlockDeltaEvent;
53
+ Assert.isTrue(
54
+ contentBlockDelta.delta.type === ContentBlockType.INPUT_JSON_DELTA,
55
+ 'The json content block delta should have been aggregated. Unsupported content block type: ' + contentBlockDelta.delta.type
56
+ );
57
+
58
+ if (contentBlockDelta.delta.type === ContentBlockType.INPUT_JSON_DELTA) {
59
+ return eventAggregator.appendPartialJson((contentBlockDelta.delta as ContentBlockDeltaJson).partialJson);
60
+ }
61
+ } else if (event.type === EventType.CONTENT_BLOCK_STOP) {
62
+ if (!eventAggregator.isEmpty()) {
63
+ eventAggregator.squashIntoContentBlock();
64
+ return eventAggregator;
65
+ }
66
+ }
67
+
68
+ return event;
69
+ }
70
+
71
+ static eventToChatResponse(event: AnthropicEvent): ChatResponse {
72
+ if (event.type === EventType.MESSAGE_START) {
73
+ ChatResponseBuilderReference.reset();
74
+ const messageStartEvent = event as MessageStartEvent;
75
+ ChatResponseBuilderReference.get()
76
+ .withType(event.type)
77
+ .withId(messageStartEvent.message.id)
78
+ .withRole(messageStartEvent.message.role)
79
+ .withModel(messageStartEvent.message.model)
80
+ .withUsage(messageStartEvent.message.usage)
81
+ .withContent([]);
82
+
83
+ } else if (event.type === EventType.TOOL_USE_AGGREGATE) {
84
+ const eventToolUseBuilder = event as ToolUseAggregationEvent;
85
+
86
+ if (eventToolUseBuilder.toolContentBlocks && eventToolUseBuilder.toolContentBlocks.length > 0) {
87
+ const content = eventToolUseBuilder.toolContentBlocks.map(
88
+ toolToUse => new ContentBlock(ContentBlockType.TOOL_USE, undefined, undefined, undefined, toolToUse.id, toolToUse.name, toolToUse.input)
89
+ );
90
+ ChatResponseBuilderReference.get().withContent(content);
91
+
92
+ }
93
+ } else if (event.type === EventType.CONTENT_BLOCK_START) {
94
+ const contentBlockStartEvent = event as ContentBlockStartEvent;
95
+
96
+ Assert.isTrue(
97
+ contentBlockStartEvent.contentBlock.type === ContentBlockType.TEXT,
98
+ 'The json content block should have been aggregated. Unsupported content block type: ' + contentBlockStartEvent.contentBlock.type
99
+ );
100
+
101
+ if (contentBlockStartEvent.contentBlock.type === ContentBlockType.TEXT) {
102
+ const contentBlockText = contentBlockStartEvent.contentBlock as ContentBlockText;
103
+ ChatResponseBuilderReference
104
+ .get()
105
+ .withType(event.type)
106
+ .withContent([new ContentBlock(ContentBlockType.TEXT, undefined, contentBlockText.text, contentBlockStartEvent.index)]);
107
+ }
108
+ } else if (event.type === EventType.CONTENT_BLOCK_DELTA) {
109
+ const contentBlockDeltaEvent = event as ContentBlockDeltaEvent;
110
+
111
+ if (contentBlockDeltaEvent.delta.type === ContentBlockType.TEXT_DELTA) {
112
+ const deltaTxt = contentBlockDeltaEvent.delta as ContentBlockDeltaText;
113
+ ChatResponseBuilderReference
114
+ .get()
115
+ .withType(event.type)
116
+ .withContent([new ContentBlock(ContentBlockType.TEXT_DELTA, undefined, deltaTxt.text, contentBlockDeltaEvent.index)]);
117
+ }
118
+ } else if (event.type === EventType.MESSAGE_DELTA) {
119
+ ChatResponseBuilderReference.get().withType(event.type);
120
+
121
+ const messageDeltaEvent = event as MessageDeltaEvent;
122
+
123
+ if (messageDeltaEvent.delta.stopReason) {
124
+ ChatResponseBuilderReference.get().withStopReason(messageDeltaEvent.delta.stopReason);
125
+ }
126
+
127
+ if (messageDeltaEvent.delta.stopSequence) {
128
+ ChatResponseBuilderReference.get().withStopSequence(messageDeltaEvent.delta.stopSequence);
129
+ }
130
+
131
+ if (messageDeltaEvent.usage) {
132
+ ChatResponseBuilderReference.get().withOutputTokens(messageDeltaEvent.usage.outputTokens);
133
+ }
134
+ } else if (event.type === EventType.MESSAGE_STOP) {
135
+ // pass through
136
+ } else {
137
+ ChatResponseBuilderReference.get().withType(event.type).withContent([]);
138
+ }
139
+
140
+ return ChatResponseBuilderReference.get().build();
141
+ }
142
+ }
@@ -0,0 +1,4 @@
1
+ describe('ai-authropic package', () => {
2
+
3
+ it('support ai-authropic coverage statistics', () => {});
4
+ });