@@ -125,6 +125,12 @@ const SUMMARY_PROVIDER_DEFAULTS = {
125125 baseURL : "https://api.openai.com/v1" ,
126126 apiKey : "" ,
127127 } ,
128+ openrouter : {
129+ provider : "openrouter" ,
130+ model : "openai/gpt-4o-mini" ,
131+ baseURL : "https://openrouter.ai/api/v1" ,
132+ apiKey : "" ,
133+ } ,
128134 ollama : {
129135 provider : "ollama" ,
130136 model : "" ,
@@ -180,7 +186,20 @@ function notesDatabasePath() {
180186}
181187
182188function normalizeSummaryProvider ( value ) {
183- return value === "ollama" || value === "lmstudio" ? value : "openai" ;
189+ return value === "openrouter" || value === "ollama" || value === "lmstudio" ? value : "openai" ;
190+ }
191+
192+ function summaryProviderRequiresAPIKey ( provider ) {
193+ return provider === "openai" || provider === "openrouter" ;
194+ }
195+
196+ function summaryProviderDisplayName ( provider ) {
197+ return {
198+ openai : "OpenAI" ,
199+ openrouter : "OpenRouter" ,
200+ ollama : "Ollama" ,
201+ lmstudio : "LM Studio" ,
202+ } [ provider ] || "OpenAI" ;
184203}
185204
186205function builtInSummaryTemplates ( ) {
@@ -334,7 +353,7 @@ function normalizeSummaryProviderConfig(provider, value = {}, legacy = {}) {
334353 ( useLegacy ? legacy . baseURL : "" ) ||
335354 defaults . baseURL ,
336355 ) . trim ( ) . replace ( / \/ + $ / , "" ) || defaults . baseURL ,
337- apiKey : provider === "openai"
356+ apiKey : summaryProviderRequiresAPIKey ( provider )
338357 ? String ( value . apiKey || value . api_key || ( useLegacy ? legacy . apiKey : "" ) || "" ) . trim ( )
339358 : String ( value . apiKey || value . api_key || ( useLegacy ? legacy . apiKey : "" ) || defaults . apiKey ) . trim ( ) ,
340359 } ;
@@ -452,11 +471,23 @@ function ollamaTagsURL(baseURL) {
452471 return `${ parsed . origin } /api/tags` ;
453472}
454473
474+ function summaryProviderRequestHeaders ( settings , options = { } ) {
475+ const headers = {
476+ Authorization : `Bearer ${ settings . apiKey || "local" } ` ,
477+ } ;
478+ if ( options . includeContentType !== false ) {
479+ headers [ "Content-Type" ] = "application/json" ;
480+ }
481+ if ( settings . provider === "openrouter" ) {
482+ headers [ "HTTP-Referer" ] = "https://github.com/qyinm/MirrorNote" ;
483+ headers [ "X-Title" ] = APP_DISPLAY_NAME ;
484+ }
485+ return headers ;
486+ }
487+
455488async function listOpenAICompatibleModels ( settings ) {
456489 const data = await fetchJSON ( openAICompatibleModelListURL ( settings . baseURL ) , {
457- headers : {
458- Authorization : `Bearer ${ settings . apiKey || "local" } ` ,
459- } ,
490+ headers : summaryProviderRequestHeaders ( settings , { includeContentType : false } ) ,
460491 } ) ;
461492 const rawModels = Array . isArray ( data ?. data ) ? data . data : [ ] ;
462493 return sortSummaryModels ( rawModels . map ( normalizeModelOption ) ) ;
@@ -479,10 +510,10 @@ async function listSummaryModels(providerOverride) {
479510 ? persistedSettings . summary ?. model
480511 : SUMMARY_PROVIDER_DEFAULTS [ provider ] . model ,
481512 } ) ;
482- if ( provider === "openai" && ! summary . apiKey ) {
513+ if ( summaryProviderRequiresAPIKey ( provider ) && ! summary . apiKey ) {
483514 return {
484515 models : summary . model ? [ { id : summary . model , name : summary . model } ] : [ ] ,
485- message : " Add an OpenAI API key to load available models." ,
516+ message : ` Add an ${ summaryProviderDisplayName ( provider ) } API key to load available models.` ,
486517 } ;
487518 }
488519
@@ -508,8 +539,8 @@ async function listSummaryModels(providerOverride) {
508539
509540let requestChatCompletion = async function requestChatCompletion ( summarySettings , messages , options = { } ) {
510541 const settings = normalizeSummarySettings ( summarySettings ) ;
511- if ( settings . provider === "openai" && ! settings . apiKey ) {
512- throw new Error ( " Add an OpenAI API key in Settings before generating a summary." ) ;
542+ if ( summaryProviderRequiresAPIKey ( settings . provider ) && ! settings . apiKey ) {
543+ throw new Error ( ` Add an ${ summaryProviderDisplayName ( settings . provider ) } API key in Settings before generating a summary.` ) ;
513544 }
514545
515546 const body = {
@@ -524,10 +555,7 @@ let requestChatCompletion = async function requestChatCompletion(summarySettings
524555
525556 const response = await fetch ( `${ settings . baseURL } /chat/completions` , {
526557 method : "POST" ,
527- headers : {
528- "Content-Type" : "application/json" ,
529- Authorization : `Bearer ${ settings . apiKey || "local" } ` ,
530- } ,
558+ headers : summaryProviderRequestHeaders ( settings ) ,
531559 body : JSON . stringify ( body ) ,
532560 } ) ;
533561 const text = await response . text ( ) ;
@@ -928,13 +956,13 @@ function publicSummarySettings(settings) {
928956 configs [ provider ] = {
929957 model : summary . providerConfigs [ provider ] . model ,
930958 baseURL : summary . providerConfigs [ provider ] . baseURL ,
931- apiKeyConfigured : provider === "openai"
959+ apiKeyConfigured : summaryProviderRequiresAPIKey ( provider )
932960 ? summary . providerConfigs [ provider ] . apiKey . length > 0
933961 : summary . providerConfigs [ provider ] . baseURL . length > 0 ,
934962 } ;
935963 return configs ;
936964 } , { } ) ,
937- apiKeyConfigured : summary . provider === "openai" ? summary . apiKey . length > 0 : true ,
965+ apiKeyConfigured : summaryProviderRequiresAPIKey ( summary . provider ) ? summary . apiKey . length > 0 : true ,
938966 } ;
939967}
940968
@@ -3209,7 +3237,7 @@ async function generateNoteSummary(noteID, templateID) {
32093237 }
32103238
32113239 const responseFormat =
3212- summarySettings . provider === "openai"
3240+ summarySettings . provider === "openai" || summarySettings . provider === "openrouter"
32133241 ? { type : "json_schema" , json_schema : summaryJSONSchemaForTemplate ( template ) }
32143242 : null ;
32153243
@@ -5056,6 +5084,7 @@ if (process.env.MIRROR_NOTE_TEST_EXPORTS === "1") {
50565084 normalizeTranscriptSegments,
50575085 parseSTTChunkLedgerJSONL,
50585086 parseTranscriptJSONL,
5087+ publicSummarySettings,
50595088 parseSummaryResultJSON,
50605089 readNote,
50615090 replaceGeneratedSummaryBlockInMarkdown,
@@ -5071,6 +5100,7 @@ if (process.env.MIRROR_NOTE_TEST_EXPORTS === "1") {
50715100 saveTranscript,
50725101 sanitizeSummaryMarkdown,
50735102 summaryJSONSchemaForTemplate,
5103+ summaryProviderRequestHeaders,
50745104 summaryTemplateById,
50755105 summaryTemplatesForSettings,
50765106 serializeTranscriptJSONL,
0 commit comments