@@ -294,161 +294,22 @@ private ModelWithCapabilities InferAnthropicModelCapabilities(string modelName)
294294 }
295295
296296 #endregion
297-
298- #region Chat History
299-
300- public bool IsSameSender ( DataMessage message1 , DataMessage message2 ) {
301- if ( message1 == null || message2 == null ) return false ;
302- bool msg1IsBot = message1 . FromUserId == Env . BotId ;
303- bool msg2IsBot = message2 . FromUserId == Env . BotId ;
304- return msg1IsBot == msg2IsBot ;
305- }
306-
307- /// <summary>
308- /// Build Anthropic message list from DB history. Anthropic requires alternating user/assistant roles.
309- /// Returns (systemPrompt, messages).
310- /// </summary>
311- public async Task < ( string systemPrompt , List < MessageParam > messages ) > GetChatHistory (
312- long chatId , string systemPrompt , DataMessage inputMessage = null ) {
313- return await GetChatHistory ( chatId , systemPrompt , inputMessage , false ) ;
314- }
315-
316- public async Task < ( string systemPrompt , List < MessageParam > messages ) > GetChatHistory (
317- long chatId , string systemPrompt , DataMessage inputMessage , bool supportsVision ) {
318- var dbMessages = await _dbContext . Messages . AsNoTracking ( )
319- . Where ( m => m . GroupId == chatId && m . DateTime > DateTime . UtcNow . AddHours ( - 1 ) )
320- . OrderBy ( m => m . DateTime )
321- . ToListAsync ( ) ;
322-
323- if ( dbMessages . Count < 10 ) {
324- dbMessages = await _dbContext . Messages . AsNoTracking ( )
325- . Where ( m => m . GroupId == chatId )
326- . OrderByDescending ( m => m . DateTime )
327- . Take ( 10 )
328- . OrderBy ( m => m . DateTime )
329- . ToListAsync ( ) ;
330- }
331-
332- if ( _llmVisibilityService != null ) {
333- dbMessages = await _llmVisibilityService . FilterVisibleMessagesAsync ( chatId , dbMessages ) ;
334- }
335-
336- if ( inputMessage != null &&
337- ( _llmVisibilityService == null ||
338- ! await _llmVisibilityService . IsUserInvisibleAsync ( chatId , inputMessage . FromUserId ) ) ) {
339- dbMessages . Add ( inputMessage ) ;
297+ private static string ExtractTextFromContent ( MessageParamContent content ) {
298+ if ( content . TryPickString ( out var text ) ) {
299+ return text ;
340300 }
341-
342- _logger . LogInformation ( "Anthropic GetChatHistory: Found {Count} messages for ChatId {ChatId}." , dbMessages . Count , chatId ) ;
343-
344- var result = new List < MessageParam > ( ) ;
345- var str = new StringBuilder ( ) ;
346- DataMessage previous = null ;
347- var userCache = new Dictionary < long , UserData > ( ) ;
348- var pendingImages = new List < byte [ ] > ( ) ;
349-
350- foreach ( var message in dbMessages ) {
351- // Skip leading bot messages (Anthropic messages must start with user)
352- if ( previous == null && ! result . Any ( ) && message . FromUserId == Env . BotId ) {
353- previous = message ;
354- continue ;
355- }
356-
357- if ( previous != null && ! IsSameSender ( previous , message ) ) {
358- AddMessageToHistory ( result , previous . FromUserId , str . ToString ( ) , supportsVision ? pendingImages : null ) ;
359- str . Clear ( ) ;
360- pendingImages . Clear ( ) ;
361- }
362-
363- str . Append ( $ "[{ message . DateTime : yyyy-MM-dd HH:mm:ss zzz} ]") ;
364- if ( message . FromUserId != 0 ) {
365- if ( ! userCache . TryGetValue ( message . FromUserId , out var fromUser ) ) {
366- fromUser = await _dbContext . UserData . AsNoTracking ( )
367- . FirstOrDefaultAsync ( u => u . Id == message . FromUserId ) ;
368- if ( fromUser != null ) userCache [ message . FromUserId ] = fromUser ;
369- }
370- str . Append ( fromUser != null ? $ "{ fromUser . FirstName } { fromUser . LastName } ". Trim ( ) : $ "User({ message . FromUserId } )") ;
371- } else {
372- str . Append ( "System/Unknown" ) ;
373- }
374-
375- if ( message . ReplyToMessageId != 0 ) {
376- str . Append ( $ "(Reply to msg { message . ReplyToMessageId } )") ;
377- }
378- str . Append ( ':' ) . Append ( message . Content ) . Append ( "\n " ) ;
379-
380- // Add message extensions if any
381- var extensions = await _messageExtensionService . GetByMessageDataIdAsync ( message . Id ) ;
382- if ( extensions != null && extensions . Any ( ) ) {
383- str . Append ( "[扩展信息:" ) ;
384- foreach ( var ext in extensions ) {
385- str . Append ( $ "{ ext . Name } ={ ext . Value } ; ") ;
386- }
387- str . Append ( "]\n " ) ;
388- }
389-
390- // 如果模型支持视觉,尝试加载消息关联的图片
391- if ( supportsVision && message . FromUserId != Env . BotId ) {
392- var imageBytes = TryLoadMessagePhoto ( message . GroupId , message . MessageId ) ;
393- if ( imageBytes != null ) {
394- pendingImages . Add ( imageBytes ) ;
301+ if ( content . TryPickContentBlockParams ( out var blocks ) ) {
302+ var sb = new StringBuilder ( ) ;
303+ foreach ( var block in blocks ) {
304+ if ( block . TryPickText ( out var textBlock ) ) {
305+ sb . Append ( textBlock . Text ) ;
395306 }
396307 }
397-
398- previous = message ;
399- }
400-
401- if ( previous != null && str . Length > 0 ) {
402- AddMessageToHistory ( result , previous . FromUserId , str . ToString ( ) , supportsVision ? pendingImages : null ) ;
403- }
404-
405- // Ensure messages alternate user/assistant and start with user
406- result = EnsureAlternatingRoles ( result ) ;
407-
408- return ( systemPrompt , result ) ;
409- }
410-
411- private void AddMessageToHistory ( List < MessageParam > history , long fromUserId , string content ) {
412- AddMessageToHistory ( history , fromUserId , content , null ) ;
413- }
414-
415- private void AddMessageToHistory ( List < MessageParam > history , long fromUserId , string content , List < byte [ ] > images ) {
416- if ( string . IsNullOrWhiteSpace ( content ) && ( images == null || images . Count == 0 ) ) return ;
417- if ( ! string . IsNullOrWhiteSpace ( content ) ) {
418- content = System . Text . RegularExpressions . Regex . Replace ( content . Trim ( ) , @"\n{3,}" , "\n \n " ) ;
419- }
420-
421- var role = fromUserId == Env . BotId ? Role . Assistant : Role . User ;
422-
423- if ( images != null && images . Count > 0 && role == Role . User ) {
424- var contentBlocks = new List < ContentBlockParam > ( ) ;
425- if ( ! string . IsNullOrWhiteSpace ( content ) ) {
426- contentBlocks . Add ( new TextBlockParam ( content . Trim ( ) ) ) ;
427- }
428- foreach ( var imageBytes in images ) {
429- contentBlocks . Add ( new ImageBlockParam {
430- Source = new Base64ImageSource {
431- Data = Convert . ToBase64String ( imageBytes ) ,
432- MediaType = MediaType . ImagePng
433- }
434- } ) ;
435- }
436- history . Add ( new MessageParam {
437- Role = role ,
438- Content = contentBlocks
439- } ) ;
440- } else {
441- history . Add ( new MessageParam {
442- Role = role ,
443- Content = content ? . Trim ( ) ?? ""
444- } ) ;
308+ return sb . ToString ( ) ;
445309 }
310+ return content . ToString ( ) ;
446311 }
447312
448- /// <summary>
449- /// Ensures message list starts with user and alternates between user/assistant.
450- /// Merges consecutive same-role messages.
451- /// </summary>
452313 internal static List < MessageParam > EnsureAlternatingRoles ( List < MessageParam > messages ) {
453314 if ( ! messages . Any ( ) ) return messages ;
454315
@@ -480,23 +341,6 @@ internal static List<MessageParam> EnsureAlternatingRoles(List<MessageParam> mes
480341 return result ;
481342 }
482343
483- private static string ExtractTextFromContent ( MessageParamContent content ) {
484- if ( content . TryPickString ( out var text ) ) {
485- return text ;
486- }
487- if ( content . TryPickContentBlockParams ( out var blocks ) ) {
488- var sb = new StringBuilder ( ) ;
489- foreach ( var block in blocks ) {
490- if ( block . TryPickText ( out var textBlock ) ) {
491- sb . Append ( textBlock . Text ) ;
492- }
493- }
494- return sb . ToString ( ) ;
495- }
496- return content . ToString ( ) ;
497- }
498-
499- #endregion
500344
501345 #region Vision Support
502346
0 commit comments