@@ -544,6 +544,19 @@ class ChatModule {
544544 return normalized ;
545545 }
546546
547+ shouldUseSummarySearch ( ) {
548+ const settingsModule = window . settingsModule ;
549+ if ( ! settingsModule || typeof settingsModule . isSummarySearchEnabled !== 'function' ) {
550+ return false ;
551+ }
552+ try {
553+ return Boolean ( settingsModule . isSummarySearchEnabled ( ) ) ;
554+ } catch ( error ) {
555+ console . warn ( '读取主题检索配置失败:' , error ) ;
556+ return false ;
557+ }
558+ }
559+
547560 normalizeModelList ( models ) {
548561 if ( ! Array . isArray ( models ) || models . length === 0 ) {
549562 return [ ] ;
@@ -1940,7 +1953,9 @@ class ChatModule {
19401953 }
19411954
19421955 renderReferenceSection ( references , metadata ) {
1943- if ( ! Array . isArray ( references ) || ! references . length ) {
1956+ const summaryCard = this . createSummaryReferenceCard ( metadata ) ;
1957+ const referenceList = Array . isArray ( references ) ? references : [ ] ;
1958+ if ( ! summaryCard && ! referenceList . length ) {
19441959 return null ;
19451960 }
19461961 const section = document . createElement ( 'div' ) ;
@@ -1954,7 +1969,11 @@ class ChatModule {
19541969 const list = document . createElement ( 'div' ) ;
19551970 list . className = 'chat-reference-list' ;
19561971
1957- references . forEach ( ( reference ) => {
1972+ if ( summaryCard ) {
1973+ list . appendChild ( summaryCard ) ;
1974+ }
1975+
1976+ referenceList . forEach ( ( reference ) => {
19581977 const item = this . createReferenceItem ( reference , metadata ) ;
19591978 if ( item ) {
19601979 list . appendChild ( item ) ;
@@ -1969,6 +1988,106 @@ class ChatModule {
19691988 return section ;
19701989 }
19711990
1991+ createSummaryReferenceCard ( metadata ) {
1992+ if ( ! metadata || typeof metadata !== 'object' ) {
1993+ return null ;
1994+ }
1995+ const useSummarySearch = Boolean ( metadata . use_summary_search ) ;
1996+ const retrievalContext = metadata . retrieval_context || { } ;
1997+ if ( ! useSummarySearch || retrievalContext . mode !== 'summary' || ! retrievalContext . summary_search_applied ) {
1998+ return null ;
1999+ }
2000+ const rawMatches = Array . isArray ( retrievalContext . summary_matches ) ? retrievalContext . summary_matches : [ ] ;
2001+ if ( ! rawMatches . length ) {
2002+ return null ;
2003+ }
2004+
2005+ const matches = rawMatches . map ( ( match , index ) => {
2006+ if ( ! match || typeof match !== 'object' ) {
2007+ return null ;
2008+ }
2009+ const summaryText = typeof match . summary_text === 'string' ? match . summary_text : ( match . summary_preview || '' ) ;
2010+ return {
2011+ name : ( match . filename || '' ) . trim ( ) || `文档-${ match . rank || index + 1 } ` ,
2012+ summary : summaryText ,
2013+ score : Number . isFinite ( match . score ) ? Number ( match . score ) : null ,
2014+ vectorScore : Number . isFinite ( match . vector_score ) ? Number ( match . vector_score ) : null ,
2015+ lexicalScore : Number . isFinite ( match . lexical_score ) ? Number ( match . lexical_score ) : null ,
2016+ modelName : ( match . summary_model_name || '' ) . trim ( ) ,
2017+ } ;
2018+ } ) . filter ( Boolean ) ;
2019+
2020+ if ( ! matches . length ) {
2021+ return null ;
2022+ }
2023+
2024+ const card = document . createElement ( 'div' ) ;
2025+ card . className = 'chat-reference-item is-summary' ;
2026+
2027+ const header = document . createElement ( 'div' ) ;
2028+ header . className = 'chat-reference-summary-header' ;
2029+ const title = document . createElement ( 'span' ) ;
2030+ title . className = 'chat-reference-summary-title' ;
2031+ title . textContent = '参考文档主题' ;
2032+ header . appendChild ( title ) ;
2033+
2034+ const summaryMeta = document . createElement ( 'span' ) ;
2035+ summaryMeta . className = 'chat-reference-summary-meta' ;
2036+ const thresholdValue = Number ( retrievalContext . summary_threshold ) ;
2037+ const threshold = Number . isFinite ( thresholdValue ) ? thresholdValue . toFixed ( 2 ) : '0.70' ;
2038+ summaryMeta . textContent = `命中 ${ matches . length } 篇 · 阈值 ≥ ${ threshold } ` ;
2039+ header . appendChild ( summaryMeta ) ;
2040+
2041+ card . appendChild ( header ) ;
2042+
2043+ const list = document . createElement ( 'div' ) ;
2044+ list . className = 'chat-reference-summary-list' ;
2045+
2046+ matches . forEach ( ( match ) => {
2047+ const entry = document . createElement ( 'div' ) ;
2048+ entry . className = 'chat-reference-summary-item' ;
2049+
2050+ const nameEl = document . createElement ( 'div' ) ;
2051+ nameEl . className = 'chat-reference-summary-name' ;
2052+ nameEl . textContent = match . name ;
2053+ entry . appendChild ( nameEl ) ;
2054+
2055+ const metaParts = [ ] ;
2056+ if ( Number . isFinite ( match . score ) ) {
2057+ metaParts . push ( `综合 ${ match . score . toFixed ( 2 ) } ` ) ;
2058+ }
2059+ if ( Number . isFinite ( match . vectorScore ) ) {
2060+ metaParts . push ( `语义 ${ match . vectorScore . toFixed ( 2 ) } ` ) ;
2061+ }
2062+ if ( Number . isFinite ( match . lexicalScore ) ) {
2063+ metaParts . push ( `词法 ${ match . lexicalScore . toFixed ( 2 ) } ` ) ;
2064+ }
2065+ if ( match . modelName ) {
2066+ metaParts . push ( `模型 ${ match . modelName } ` ) ;
2067+ }
2068+
2069+ if ( metaParts . length ) {
2070+ const metaLine = document . createElement ( 'div' ) ;
2071+ metaLine . className = 'chat-reference-summary-submeta' ;
2072+ metaLine . textContent = metaParts . join ( ' · ' ) ;
2073+ entry . appendChild ( metaLine ) ;
2074+ }
2075+
2076+ const textEl = document . createElement ( 'div' ) ;
2077+ textEl . className = 'chat-reference-summary-text' ;
2078+ const summaryContent = match . summary && match . summary . trim ( )
2079+ ? this . buildReferenceSnippet ( match . summary . trim ( ) )
2080+ : '暂无主题概述内容。' ;
2081+ textEl . textContent = summaryContent ;
2082+ entry . appendChild ( textEl ) ;
2083+
2084+ list . appendChild ( entry ) ;
2085+ } ) ;
2086+
2087+ card . appendChild ( list ) ;
2088+ return card ;
2089+ }
2090+
19722091 createReferenceItem ( reference , metadata ) {
19732092 if ( ! reference || typeof reference !== 'object' ) {
19742093 return null ;
@@ -3016,6 +3135,7 @@ class ChatModule {
30163135 top_k : 5 ,
30173136 stream : true ,
30183137 client_request_id : streamingMessage . id ,
3138+ use_summary_search : this . shouldUseSummarySearch ( ) ,
30193139 model : {
30203140 source_id : this . selectedModel . sourceId ,
30213141 model_id : this . selectedModel . modelId ,
0 commit comments