4
4
using System ;
5
5
using System . Collections . Generic ;
6
6
using System . Linq ;
7
+ using System . Net . Http ;
7
8
using System . Runtime . CompilerServices ;
8
9
using System . Threading ;
9
10
using System . Threading . Tasks ;
@@ -19,7 +20,7 @@ public class AnthropicMessageConnector : IStreamingMiddleware
19
20
public async Task < IMessage > InvokeAsync ( MiddlewareContext context , IAgent agent , CancellationToken cancellationToken = default )
20
21
{
21
22
var messages = context . Messages ;
22
- var chatMessages = ProcessMessage ( messages , agent ) ;
23
+ var chatMessages = await ProcessMessageAsync ( messages , agent ) ;
23
24
var response = await agent . GenerateReplyAsync ( chatMessages , context . Options , cancellationToken ) ;
24
25
25
26
return response is IMessage < ChatCompletionResponse > chatMessage
@@ -31,7 +32,7 @@ public async IAsyncEnumerable<IStreamingMessage> InvokeAsync(MiddlewareContext c
31
32
[ EnumeratorCancellation ] CancellationToken cancellationToken = default )
32
33
{
33
34
var messages = context . Messages ;
34
- var chatMessages = ProcessMessage ( messages , agent ) ;
35
+ var chatMessages = await ProcessMessageAsync ( messages , agent ) ;
35
36
36
37
await foreach ( var reply in agent . GenerateStreamingReplyAsync ( chatMessages , context . Options , cancellationToken ) )
37
38
{
@@ -53,60 +54,78 @@ public async IAsyncEnumerable<IStreamingMessage> InvokeAsync(MiddlewareContext c
53
54
private IStreamingMessage ? ProcessChatCompletionResponse ( IStreamingMessage < ChatCompletionResponse > chatMessage ,
54
55
IStreamingAgent agent )
55
56
{
56
- Delta ? delta = chatMessage . Content . Delta ;
57
+ var delta = chatMessage . Content . Delta ;
57
58
return delta != null && ! string . IsNullOrEmpty ( delta . Text )
58
59
? new TextMessageUpdate ( role : Role . Assistant , delta . Text , from : agent . Name )
59
60
: null ;
60
61
}
61
62
62
- private IEnumerable < IMessage > ProcessMessage ( IEnumerable < IMessage > messages , IAgent agent )
63
+ private async Task < IEnumerable < IMessage > > ProcessMessageAsync ( IEnumerable < IMessage > messages , IAgent agent )
63
64
{
64
- return messages . SelectMany < IMessage , IMessage > ( m =>
65
+ var processedMessages = new List < IMessage > ( ) ;
66
+
67
+ foreach ( var message in messages )
65
68
{
66
- return m switch
69
+ var processedMessage = message switch
67
70
{
68
71
TextMessage textMessage => ProcessTextMessage ( textMessage , agent ) ,
69
- _ => [ m ] ,
72
+
73
+ ImageMessage imageMessage =>
74
+ new MessageEnvelope < ChatMessage > ( new ChatMessage ( "user" ,
75
+ new ContentBase [ ] { new ImageContent { Source = await ProcessImageSourceAsync ( imageMessage ) } }
76
+ . ToList ( ) ) ,
77
+ from : agent . Name ) ,
78
+
79
+ MultiModalMessage multiModalMessage => await ProcessMultiModalMessageAsync ( multiModalMessage , agent ) ,
80
+ _ => message ,
70
81
} ;
71
- } ) ;
82
+
83
+ processedMessages . Add ( processedMessage ) ;
84
+ }
85
+
86
+ return processedMessages ;
72
87
}
73
88
74
89
private IMessage PostProcessMessage ( ChatCompletionResponse response , IAgent from )
75
90
{
76
91
if ( response . Content is null )
92
+ {
77
93
throw new ArgumentNullException ( nameof ( response . Content ) ) ;
94
+ }
78
95
79
96
if ( response . Content . Count != 1 )
97
+ {
80
98
throw new NotSupportedException ( $ "{ nameof ( response . Content ) } != 1") ;
99
+ }
81
100
82
101
return new TextMessage ( Role . Assistant , ( ( TextContent ) response . Content [ 0 ] ) . Text ?? string . Empty , from : from . Name ) ;
83
102
}
84
103
85
- private IEnumerable < IMessage < ChatMessage > > ProcessTextMessage ( TextMessage textMessage , IAgent agent )
104
+ private IMessage < ChatMessage > ProcessTextMessage ( TextMessage textMessage , IAgent agent )
86
105
{
87
- IEnumerable < ChatMessage > messages ;
106
+ ChatMessage messages ;
88
107
89
108
if ( textMessage . From == agent . Name )
90
109
{
91
- messages = [ new ChatMessage (
92
- "assistant" , textMessage . Content ) ] ;
110
+ messages = new ChatMessage (
111
+ "assistant" , textMessage . Content ) ;
93
112
}
94
113
else if ( textMessage . From is null )
95
114
{
96
115
if ( textMessage . Role == Role . User )
97
116
{
98
- messages = [ new ChatMessage (
99
- "user" , textMessage . Content ) ] ;
117
+ messages = new ChatMessage (
118
+ "user" , textMessage . Content ) ;
100
119
}
101
120
else if ( textMessage . Role == Role . Assistant )
102
121
{
103
- messages = [ new ChatMessage (
104
- "assistant" , textMessage . Content ) ] ;
122
+ messages = new ChatMessage (
123
+ "assistant" , textMessage . Content ) ;
105
124
}
106
125
else if ( textMessage . Role == Role . System )
107
126
{
108
- messages = [ new ChatMessage (
109
- "system" , textMessage . Content ) ] ;
127
+ messages = new ChatMessage (
128
+ "system" , textMessage . Content ) ;
110
129
}
111
130
else
112
131
{
@@ -116,10 +135,61 @@ private IEnumerable<IMessage<ChatMessage>> ProcessTextMessage(TextMessage textMe
116
135
else
117
136
{
118
137
// if from is not null, then the message is from user
119
- messages = [ new ChatMessage (
120
- "user" , textMessage . Content ) ] ;
138
+ messages = new ChatMessage (
139
+ "user" , textMessage . Content ) ;
121
140
}
122
141
123
- return messages . Select ( m => new MessageEnvelope < ChatMessage > ( m , from : textMessage . From ) ) ;
142
+ return new MessageEnvelope < ChatMessage > ( messages , from : textMessage . From ) ;
143
+ }
144
+
145
+ private async Task < IMessage > ProcessMultiModalMessageAsync ( MultiModalMessage multiModalMessage , IAgent agent )
146
+ {
147
+ var content = new List < ContentBase > ( ) ;
148
+ foreach ( var message in multiModalMessage . Content )
149
+ {
150
+ switch ( message )
151
+ {
152
+ case TextMessage textMessage when textMessage . GetContent ( ) is not null :
153
+ content . Add ( new TextContent { Text = textMessage . GetContent ( ) } ) ;
154
+ break ;
155
+ case ImageMessage imageMessage :
156
+ content . Add ( new ImageContent ( ) { Source = await ProcessImageSourceAsync ( imageMessage ) } ) ;
157
+ break ;
158
+ }
159
+ }
160
+
161
+ var chatMessage = new ChatMessage ( "user" , content ) ;
162
+ return MessageEnvelope . Create ( chatMessage , agent . Name ) ;
163
+ }
164
+
165
+ private async Task < ImageSource > ProcessImageSourceAsync ( ImageMessage imageMessage )
166
+ {
167
+ if ( imageMessage . Data != null )
168
+ {
169
+ return new ImageSource
170
+ {
171
+ MediaType = imageMessage . Data . MediaType ,
172
+ Data = Convert . ToBase64String ( imageMessage . Data . ToArray ( ) )
173
+ } ;
174
+ }
175
+
176
+ if ( imageMessage . Url is null )
177
+ {
178
+ throw new InvalidOperationException ( "Invalid ImageMessage, the data or url must be provided" ) ;
179
+ }
180
+
181
+ var uri = new Uri ( imageMessage . Url ) ;
182
+ using var client = new HttpClient ( ) ;
183
+ var response = client . GetAsync ( uri ) . Result ;
184
+ if ( ! response . IsSuccessStatusCode )
185
+ {
186
+ throw new HttpRequestException ( $ "Failed to download the image from { uri } ") ;
187
+ }
188
+
189
+ return new ImageSource
190
+ {
191
+ MediaType = "image/jpeg" ,
192
+ Data = Convert . ToBase64String ( await response . Content . ReadAsByteArrayAsync ( ) )
193
+ } ;
124
194
}
125
195
}
0 commit comments