@@ -26,7 +26,9 @@ export interface FileAttachment {
2626
2727export interface ChatMessage {
2828 role : 'user' | 'assistant' | 'system'
29- content : string | Array < { type : 'text' | 'image_url' ; text ?: string ; image_url ?: { url : string } } >
29+ content :
30+ | string
31+ | Array < { type : 'text' | 'image_url' ; text ?: string ; image_url ?: { url : string } } >
3032 attachments ?: FileAttachment [ ]
3133}
3234
@@ -52,55 +54,64 @@ class AIHandler {
5254 /**
5355 * 准备 API 消息数组,处理 systemPrompt 和文件附件
5456 */
55- private async prepareApiMessages ( messages : ChatMessage [ ] , modelConfig : ModelConfig ) : Promise < ChatMessage [ ] > {
57+ private async prepareApiMessages (
58+ messages : ChatMessage [ ] ,
59+ modelConfig : ModelConfig
60+ ) : Promise < ChatMessage [ ] > {
5661 const attachmentsDir = path . join ( app . getPath ( 'userData' ) , 'attachments' )
5762
58- const apiMessages = await Promise . all ( messages . map ( async ( msg ) => {
59- // 如果消息有附件,需要转换为多模态格式
60- if ( msg . attachments && msg . attachments . length > 0 ) {
61- const contentParts : Array < { type : 'text' | 'image_url' ; text ?: string ; image_url ?: { url : string } } > = [ ]
62-
63- // 添加文本内容
64- if ( typeof msg . content === 'string' && msg . content . trim ( ) ) {
65- contentParts . push ( {
66- type : 'text' ,
67- text : msg . content
68- } )
69- }
63+ const apiMessages = await Promise . all (
64+ messages . map ( async ( msg ) => {
65+ // 如果消息有附件,需要转换为多模态格式
66+ if ( msg . attachments && msg . attachments . length > 0 ) {
67+ const contentParts : Array < {
68+ type : 'text' | 'image_url'
69+ text ?: string
70+ image_url ?: { url : string }
71+ } > = [ ]
72+
73+ // 添加文本内容
74+ if ( typeof msg . content === 'string' && msg . content . trim ( ) ) {
75+ contentParts . push ( {
76+ type : 'text' ,
77+ text : msg . content
78+ } )
79+ }
7080
71- // 添加图片附件 - 从文件系统读取
72- for ( const attachment of msg . attachments ) {
73- if ( attachment . type . startsWith ( 'image/' ) ) {
74- try {
75- const fullPath = path . join ( attachmentsDir , attachment . localPath )
76- const buffer = await readFile ( fullPath )
77- const base64Content = buffer . toString ( 'base64' )
78-
79- contentParts . push ( {
80- type : 'image_url' ,
81- image_url : {
82- url : `data:${ attachment . type } ;base64,${ base64Content } `
83- }
84- } )
85- } catch ( error ) {
86- console . error ( 'Failed to read attachment file:' , attachment . localPath , error )
87- // 跳过无法读取的附件
81+ // 添加图片附件 - 从文件系统读取
82+ for ( const attachment of msg . attachments ) {
83+ if ( attachment . type . startsWith ( 'image/' ) ) {
84+ try {
85+ const fullPath = path . join ( attachmentsDir , attachment . localPath )
86+ const buffer = await readFile ( fullPath )
87+ const base64Content = buffer . toString ( 'base64' )
88+
89+ contentParts . push ( {
90+ type : 'image_url' ,
91+ image_url : {
92+ url : `data:${ attachment . type } ;base64,${ base64Content } `
93+ }
94+ } )
95+ } catch ( error ) {
96+ console . error ( 'Failed to read attachment file:' , attachment . localPath , error )
97+ // 跳过无法读取的附件
98+ }
8899 }
89100 }
101+
102+ return {
103+ role : msg . role ,
104+ content : contentParts
105+ }
90106 }
91107
108+ // 普通文本消息
92109 return {
93110 role : msg . role ,
94- content : contentParts
111+ content : msg . content
95112 }
96- }
97-
98- // 普通文本消息
99- return {
100- role : msg . role ,
101- content : msg . content
102- }
103- } ) )
113+ } )
114+ )
104115
105116 // 如果有systemPrompt且第一条消息不是system消息,则插入system消息
106117 if (
@@ -298,12 +309,7 @@ class AIHandler {
298309 const fullResponse = { value : '' }
299310 const fullReasoning = { value : '' }
300311
301- const parser = this . createStreamParser (
302- event ,
303- eventChannel ,
304- fullResponse ,
305- fullReasoning
306- )
312+ const parser = this . createStreamParser ( event , eventChannel , fullResponse , fullReasoning )
307313
308314 await this . processStreamResponse (
309315 reader ,
@@ -368,7 +374,9 @@ class AIHandler {
368374 }
369375 }
370376
371- public async getModels ( config : LLMConfig ) : Promise < { success : boolean ; models ?: string [ ] ; error ?: string } > {
377+ public async getModels (
378+ config : LLMConfig
379+ ) : Promise < { success : boolean ; models ?: string [ ] ; error ?: string } > {
372380 try {
373381 const response = await fetch ( `${ config . apiHost . replace ( / \/ $ / , '' ) } /models` , {
374382 method : 'GET' ,
@@ -402,7 +410,11 @@ export function setupAIHandlers() {
402410 ipcMain . handle ( 'ai:send-message-streaming' , ( event , request : AIRequest , eventChannel : string ) =>
403411 aiHandler . sendMessageStreaming ( event , request , eventChannel )
404412 )
405- ipcMain . handle ( 'ai:stop-streaming' , ( _event , requestId : string ) => aiHandler . stopStreaming ( requestId ) )
406- ipcMain . handle ( 'ai:test-connection' , ( _event , config : LLMConfig ) => aiHandler . testConnection ( config ) )
413+ ipcMain . handle ( 'ai:stop-streaming' , ( _event , requestId : string ) =>
414+ aiHandler . stopStreaming ( requestId )
415+ )
416+ ipcMain . handle ( 'ai:test-connection' , ( _event , config : LLMConfig ) =>
417+ aiHandler . testConnection ( config )
418+ )
407419 ipcMain . handle ( 'ai:get-models' , ( _event , config : LLMConfig ) => aiHandler . getModels ( config ) )
408420}
0 commit comments