@@ -39,6 +39,7 @@ let reportsCache = [];
3939let reportFilterState = { q :'' , status :'all' , type :'all' } ;
4040let activeReportId = '' ;
4141let commandAssetsLoaded = false ;
42+ let contextPackAssetFilterState = { q :'' , type :'all' } ;
4243const CONTEXT_PACK_STORAGE_KEY = 'dap_context_pack_v1' ;
4344let contextPack = loadContextPack ( ) ;
4445
@@ -137,7 +138,7 @@ function compactTags(values,limit=4){
137138 return list . length ?list . map ( v => tag ( v ) ) . join ( '' ) :'<span class="muted">暂无标签</span>' ;
138139}
139140function defaultContextPack ( ) {
140- return { name :'默认工作包' , instructions :'' , agentId :'' , datasetIds :[ ] , reportIds :[ ] , traceIds :[ ] , sessionId :'' , toolMode :'auto' , evidenceDepth :'standard' , updatedAt :'' } ;
141+ return { name :'默认工作包' , instructions :'' , agentId :'' , datasetIds :[ ] , knowledgeBaseIds : [ ] , reportIds :[ ] , traceIds :[ ] , sessionId :'' , toolMode :'auto' , evidenceDepth :'standard' , updatedAt :'' } ;
141142}
142143function normalizeIdList ( list , limit = 6 ) {
143144 return [ ...new Set ( asList ( list ) . map ( v => String ( v || '' ) . trim ( ) ) . filter ( Boolean ) ) ] . slice ( 0 , limit ) ;
@@ -149,6 +150,7 @@ function normalizeContextPack(pack={}){
149150 next . instructions = String ( next . instructions || '' ) . slice ( 0 , 1200 ) ;
150151 next . agentId = String ( next . agentId || '' ) . slice ( 0 , 80 ) ;
151152 next . datasetIds = normalizeIdList ( next . datasetIds , 6 ) ;
153+ next . knowledgeBaseIds = normalizeIdList ( next . knowledgeBaseIds , 6 ) ;
152154 next . reportIds = normalizeIdList ( next . reportIds , 4 ) ;
153155 next . traceIds = normalizeIdList ( next . traceIds , 6 ) ;
154156 next . sessionId = String ( next . sessionId || '' ) . slice ( 0 , 90 ) ;
@@ -167,7 +169,7 @@ function loadContextPack(){
167169}
168170function contextPackHasContent ( pack = contextPack ) {
169171 const p = normalizeContextPack ( pack ) ;
170- return Boolean ( p . instructions . trim ( ) || p . agentId || p . datasetIds . length || p . reportIds . length || p . traceIds . length || p . sessionId ) ;
172+ return Boolean ( p . instructions . trim ( ) || p . agentId || p . datasetIds . length || p . knowledgeBaseIds . length || p . reportIds . length || p . traceIds . length || p . sessionId ) ;
171173}
172174function asList ( value ) {
173175 if ( Array . isArray ( value ) ) return value ;
@@ -184,6 +186,7 @@ function assetName(list,id,fallback='-'){
184186}
185187function datasetName ( id ) { return assetName ( datasets , id , id || '-' ) }
186188function metricName ( id ) { return assetName ( metrics , id , id || '-' ) }
189+ function knowledgeBaseName ( id ) { return assetName ( knowledgeBasesCache , id , id || '-' ) }
187190function cellHtml ( key , value ) {
188191 if ( value === null || value === undefined || value === '' ) return '<span class="muted">-</span>' ;
189192 if ( [ 'status' , 'risk' , 'risk_level' , 'severity' , 'mode' , 'data_classification' , 'refresh_mode' ] . includes ( key ) ) return statusTag ( value ) ;
@@ -1074,6 +1077,11 @@ function openReportCommand(reportId){
10741077 showPage ( 'reports' ) ;
10751078 setTimeout ( ( ) => openReportDetail ( reportId , null ) , 180 ) ;
10761079}
1080+ function openKnowledgeCommand ( kbId ) {
1081+ knowledgeFilterState = { q :'' , backend :'all' , type :'all' } ;
1082+ showPage ( 'knowledge' ) ;
1083+ setTimeout ( ( ) => selectKnowledgeBase ( kbId ) , 180 ) ;
1084+ }
10771085function openSessionCommand ( sessionId ) {
10781086 showPage ( 'chat' ) ;
10791087 setTimeout ( ( ) => loadChatSession ( sessionId ) , 180 ) ;
@@ -1085,6 +1093,7 @@ function buildCommandItems(q){
10851093 items . push ( ...agents . map ( a => ( { kind :'agent' , title :a . name , description :`试用 ${ displayValue ( a . type ) } · ${ displayValue ( a . risk_level || 'low' ) } ` , keywords :[ a . id , a . type , a . description , a . adapter_id ] . join ( ' ' ) , run :( ) => openAgentCommand ( a . id ) } ) ) ) ;
10861094 items . push ( ...datasets . map ( d => ( { kind :'dataset' , title :d . name , description :`打开数据画像 · ${ d . physical_table || d . id } ` , keywords :[ d . id , d . business_domain , d . description , d . data_classification ] . join ( ' ' ) , run :( ) => openDatasetCommand ( d . id ) } ) ) ) ;
10871095 items . push ( ...metrics . map ( m => ( { kind :'metric' , title :m . name , description :`解释指标口径 · ${ m . code || m . id } ` , keywords :[ m . id , m . code , m . formula , m . dataset_id ] . join ( ' ' ) , run :( ) => openMetricCommand ( m ) } ) ) ) ;
1096+ items . push ( ...knowledgeBasesCache . slice ( 0 , 80 ) . map ( k => ( { kind :'knowledge' , title :k . name || k . id , description :`打开知识库 · ${ displayValue ( k . backend_type || 'mock' ) } · ${ displayValue ( k . type || 'document' ) } ` , keywords :[ k . id , k . name , k . backend_type , k . type , k . adapter_id , k . description ] . join ( ' ' ) , run :( ) => openKnowledgeCommand ( k . id ) } ) ) ) ;
10881097 items . push ( ...reportsCache . slice ( 0 , 80 ) . map ( r => ( { kind :'report_asset' , title :r . title || r . id , description :`打开报告 Canvas · ${ reportTypeLabel ( r . report_type ) } · ${ displayValue ( r . status || 'draft' ) } ` , keywords :[ r . id , r . report_type , r . status , r . owner_id , r . agent_id , r . created_at , r . updated_at ] . join ( ' ' ) , run :( ) => openReportCommand ( r . id ) } ) ) ) ;
10891098 items . push ( ...chatSessions . slice ( 0 , 80 ) . map ( s => ( { kind :'session' , title :sessionTitle ( s ) , description :`恢复会话 · ${ displayValue ( s . status || 'active' ) } · ${ timeText ( s . updated_at ) } ` , keywords :[ s . id , s . title , s . agent_id , s . status , s . created_at , s . updated_at ] . join ( ' ' ) , run :( ) => openSessionCommand ( s . id ) } ) ) ) ;
10901099 const prompts = [ '本月收入最高的渠道有哪些?' , '按区域统计本月收入' , '客户工单根因分布是什么?' , '解释收入指标口径' , '给我生成一个经营总览面板' , '帮我创建一个 Codex 任务,开发面板导出功能' ] ;
@@ -1444,14 +1453,15 @@ function contextPackCounts(){
14441453 const p = normalizeContextPack ( contextPack ) ;
14451454 return {
14461455 datasets :p . datasetIds . length ,
1456+ knowledge :p . knowledgeBaseIds . length ,
14471457 reports :p . reportIds . length ,
14481458 traces :p . traceIds . length ,
14491459 instructions :p . instructions . trim ( ) ?1 :0
14501460 } ;
14511461}
14521462function contextPackSummaryLabel ( ) {
14531463 const counts = contextPackCounts ( ) ;
1454- const total = counts . datasets + counts . reports + counts . traces + counts . instructions + ( contextPack . agentId ?1 :0 ) + ( contextPack . sessionId ?1 :0 ) ;
1464+ const total = counts . datasets + counts . knowledge + counts . reports + counts . traces + counts . instructions + ( contextPack . agentId ?1 :0 ) + ( contextPack . sessionId ?1 :0 ) ;
14551465 return total ?`${ total } 项上下文` :'未捕获' ;
14561466}
14571467function contextPackSummaryDetail ( ) {
@@ -1460,6 +1470,7 @@ function contextPackSummaryDetail(){
14601470 if ( counts . instructions ) parts . push ( '指令' ) ;
14611471 if ( contextPack . agentId ) parts . push ( 'Agent' ) ;
14621472 if ( counts . datasets ) parts . push ( `${ counts . datasets } 数据集` ) ;
1473+ if ( counts . knowledge ) parts . push ( `${ counts . knowledge } 知识库` ) ;
14631474 if ( counts . reports ) parts . push ( `${ counts . reports } 报告` ) ;
14641475 if ( counts . traces ) parts . push ( `${ counts . traces } Trace` ) ;
14651476 if ( contextPack . sessionId ) parts . push ( '会话' ) ;
@@ -1477,21 +1488,90 @@ function contextPackPills(){
14771488 const pills = [ ] ;
14781489 if ( p . agentId ) pills . push ( [ 'Agent' , contextPackAgentName ( ) ] ) ;
14791490 p . datasetIds . forEach ( id => pills . push ( [ '数据集' , datasetName ( id ) ] ) ) ;
1491+ p . knowledgeBaseIds . forEach ( id => pills . push ( [ '知识库' , knowledgeBaseName ( id ) ] ) ) ;
14801492 p . reportIds . forEach ( id => pills . push ( [ '报告' , contextPackReportTitle ( id ) ] ) ) ;
14811493 p . traceIds . forEach ( id => pills . push ( [ 'Trace' , id ] ) ) ;
14821494 if ( p . sessionId ) pills . push ( [ '会话' , p . sessionId ] ) ;
1483- return pills . length ?`<div class="context-pack-pills">${ pills . map ( ( [ label , value ] ) => `<span><small>${ esc ( label ) } </small><b>${ esc ( short ( value , 34 ) ) } </b></span>` ) . join ( '' ) } </div>` :`<p class="context-pack-empty">捕获当前会话后 ,这里会显示 Agent、数据集、Trace、报告和会话线索。</p>` ;
1495+ return pills . length ?`<div class="context-pack-pills">${ pills . map ( ( [ label , value ] ) => `<span><small>${ esc ( label ) } </small><b>${ esc ( short ( value , 34 ) ) } </b></span>` ) . join ( '' ) } </div>` :`<p class="context-pack-empty">捕获当前会话或添加资产后 ,这里会显示 Agent、数据集、知识库 、Trace、报告和会话线索。</p>` ;
14841496}
14851497function contextPackStatusText ( ) {
14861498 return contextPack . updatedAt ?`已更新 ${ timeText ( contextPack . updatedAt ) } · 本地浏览器工作包` :'本地浏览器工作包 · 未写入服务端' ;
14871499}
1500+ function contextPackAssetItems ( ) {
1501+ const q = ( contextPackAssetFilterState . q || '' ) . trim ( ) . toLowerCase ( ) ;
1502+ const type = contextPackAssetFilterState . type || 'all' ;
1503+ const items = [
1504+ ...datasets . map ( d => ( {
1505+ kind :'dataset' ,
1506+ label :'数据集' ,
1507+ id :d . id ,
1508+ title :d . name || d . id ,
1509+ detail :[ d . business_domain , d . physical_table , displayValue ( d . data_classification || 'internal' ) ] . filter ( Boolean ) . join ( ' · ' ) ,
1510+ keywords :[ d . id , d . name , d . physical_table , d . business_domain , d . description , d . data_classification ] . join ( ' ' )
1511+ } ) ) ,
1512+ ...knowledgeBasesCache . map ( k => ( {
1513+ kind :'knowledge' ,
1514+ label :'知识库' ,
1515+ id :k . id ,
1516+ title :k . name || k . id ,
1517+ detail :[ displayValue ( k . backend_type || 'mock' ) , displayValue ( k . type || 'document' ) , k . adapter_id ] . filter ( Boolean ) . join ( ' · ' ) ,
1518+ keywords :[ k . id , k . name , k . backend_type , k . type , k . adapter_id , k . description ] . join ( ' ' )
1519+ } ) ) ,
1520+ ...reportsCache . slice ( 0 , 80 ) . map ( r => ( {
1521+ kind :'report' ,
1522+ label :'报告' ,
1523+ id :r . id ,
1524+ title :r . title || r . id ,
1525+ detail :[ reportTypeLabel ( r . report_type ) , displayValue ( r . status || 'draft' ) , timeText ( r . updated_at || r . created_at ) ] . filter ( Boolean ) . join ( ' · ' ) ,
1526+ keywords :[ r . id , r . title , r . report_type , r . status , r . owner_id , r . agent_id , r . created_at , r . updated_at ] . join ( ' ' )
1527+ } ) )
1528+ ] ;
1529+ return items
1530+ . filter ( item => ( type === 'all' || item . kind === type ) && ( ! q || [ item . id , item . title , item . detail , item . keywords ] . join ( ' ' ) . toLowerCase ( ) . includes ( q ) ) )
1531+ . slice ( 0 , 8 ) ;
1532+ }
1533+ function contextPackAssetInPack ( kind , id , pack = normalizeContextPack ( contextPack ) ) {
1534+ if ( kind === 'dataset' ) return pack . datasetIds . includes ( id ) ;
1535+ if ( kind === 'knowledge' ) return pack . knowledgeBaseIds . includes ( id ) ;
1536+ if ( kind === 'report' ) return pack . reportIds . includes ( id ) ;
1537+ return false ;
1538+ }
1539+ function contextPackAssetListHtml ( ) {
1540+ const items = contextPackAssetItems ( ) ;
1541+ const pack = normalizeContextPack ( contextPack ) ;
1542+ if ( ! items . length ) return emptyState ( '没有匹配资产' , '换一个关键词或资产类型。' ) ;
1543+ return `<div class="context-asset-list">${ items . map ( item => {
1544+ const selected = contextPackAssetInPack ( item . kind , item . id , pack ) ;
1545+ return `<article class="context-asset-item ${ selected ?'selected' :'' } ">
1546+ <div><small>${ esc ( item . label ) } </small><b>${ esc ( short ( item . title , 38 ) ) } </b><span>${ esc ( short ( item . detail || item . id , 54 ) ) } </span></div>
1547+ <button class="report-action ${ selected ?'muted-action' :'' } " ${ selected ?'disabled' :'' } onclick="addContextAssetToPack('${ jsArg ( item . kind ) } ','${ jsArg ( item . id ) } ',this)">${ selected ?'已加入' :'加入' } </button>
1548+ </article>` ;
1549+ } ) . join ( '' ) } </div>`;
1550+ }
1551+ function renderContextPackAssetPicker ( ) {
1552+ const type = contextPackAssetFilterState . type || 'all' ;
1553+ return `<div class="context-pack-picker">
1554+ <div class="context-pack-filter">
1555+ <label><span>资产</span><input id="contextPackAssetSearch" value="${ esc ( contextPackAssetFilterState . q || '' ) } " placeholder="搜索数据集、知识库、报告" oninput="contextPackAssetFilterState.q=this.value;renderContextPackAssetList()"/></label>
1556+ <label><span>类型</span><select id="contextPackAssetType" onchange="contextPackAssetFilterState.type=this.value;renderContextPackAssetList()">
1557+ ${ [ [ 'all' , '全部' ] , [ 'dataset' , '数据集' ] , [ 'knowledge' , '知识库' ] , [ 'report' , '报告' ] ] . map ( ( [ value , label ] ) => `<option value="${ value } " ${ type === value ?'selected' :'' } >${ label } </option>` ) . join ( '' ) }
1558+ </select></label>
1559+ </div>
1560+ <div id="contextPackAssetList">${ contextPackAssetListHtml ( ) } </div>
1561+ </div>` ;
1562+ }
1563+ function renderContextPackAssetList ( ) {
1564+ const list = document . getElementById ( 'contextPackAssetList' ) ;
1565+ if ( list ) list . innerHTML = contextPackAssetListHtml ( ) ;
1566+ }
14881567function renderContextPackPanel ( ) {
14891568 const active = contextPackHasContent ( ) ;
14901569 return `<section id="contextPackPanel" class="context-pack-card ${ active ?'active' :'' } " aria-label="工作包">
14911570 <div class="context-pack-head"><div><span>Context Pack</span><b>${ esc ( contextPack . name || '默认工作包' ) } </b></div>${ active ?tag ( 'active' , 'green' ) :tag ( 'empty' ) } </div>
14921571 <label class="field-label" for="contextPackInstructions">工作指令</label>
14931572 <textarea id="contextPackInstructions" rows="4" placeholder="写下这组工作要长期遵循的口径、范围或偏好。" oninput="updateContextPackInstructions(this.value)">${ esc ( contextPack . instructions ) } </textarea>
14941573 ${ contextPackPills ( ) }
1574+ ${ renderContextPackAssetPicker ( ) }
14951575 <div class="context-pack-actions">
14961576 <button class="report-action" onclick="captureContextPack()">捕获当前</button>
14971577 <button class="report-action" onclick="applyContextPackToChat()">应用</button>
@@ -1554,6 +1634,19 @@ function addReportToContextPack(reportId,btn){
15541634 persistContextPack ( { toast :'报告已加入工作包' } ) ;
15551635 setBusy ( btn , false ) ;
15561636}
1637+ function addKnowledgeToContextPack ( knowledgeBaseId , btn ) {
1638+ if ( ! knowledgeBaseId ) return ;
1639+ setBusy ( btn , true ) ;
1640+ contextPack . knowledgeBaseIds = normalizeIdList ( [ knowledgeBaseId , ...contextPack . knowledgeBaseIds ] , 6 ) ;
1641+ contextPack . updatedAt = new Date ( ) . toISOString ( ) ;
1642+ persistContextPack ( { toast :'知识库已加入工作包' } ) ;
1643+ setBusy ( btn , false ) ;
1644+ }
1645+ function addContextAssetToPack ( kind , id , btn ) {
1646+ if ( kind === 'dataset' ) return addDatasetToContextPack ( id , btn ) ;
1647+ if ( kind === 'knowledge' ) return addKnowledgeToContextPack ( id , btn ) ;
1648+ if ( kind === 'report' ) return addReportToContextPack ( id , btn ) ;
1649+ }
15571650function addTraceToContextPack ( traceId , btn ) {
15581651 if ( ! traceId ) return ;
15591652 setBusy ( btn , true ) ;
@@ -1577,6 +1670,7 @@ function contextPackPrompt(){
15771670 if ( p . instructions . trim ( ) ) lines . push ( '' , `工作指令:${ p . instructions . trim ( ) } ` ) ;
15781671 if ( p . agentId ) lines . push ( `Agent:${ contextPackAgentName ( ) || p . agentId } ` ) ;
15791672 if ( p . datasetIds . length ) lines . push ( `数据集:${ p . datasetIds . map ( datasetName ) . join ( '、' ) } ` ) ;
1673+ if ( p . knowledgeBaseIds . length ) lines . push ( `知识库:${ p . knowledgeBaseIds . map ( knowledgeBaseName ) . join ( '、' ) } ` ) ;
15801674 if ( p . reportIds . length ) lines . push ( `报告:${ p . reportIds . map ( id => short ( contextPackReportTitle ( id ) , 42 ) ) . join ( '、' ) } ` ) ;
15811675 if ( p . traceIds . length ) lines . push ( `Trace:${ p . traceIds . join ( '、' ) } ` ) ;
15821676 if ( p . sessionId ) lines . push ( `来源会话:${ p . sessionId } ` ) ;
@@ -1589,6 +1683,7 @@ function contextPackCanvasMarkdown(){
15891683 if ( p . instructions . trim ( ) ) lines . push ( '## 工作指令' , p . instructions . trim ( ) , '' ) ;
15901684 if ( p . agentId ) lines . push ( '## Agent' , `- ${ contextPackAgentName ( ) || p . agentId } ` , '' ) ;
15911685 if ( p . datasetIds . length ) lines . push ( '## 数据集' , ...p . datasetIds . map ( id => `- ${ datasetName ( id ) } (${ id } )` ) , '' ) ;
1686+ if ( p . knowledgeBaseIds . length ) lines . push ( '## 知识库' , ...p . knowledgeBaseIds . map ( id => `- ${ knowledgeBaseName ( id ) } (${ id } )` ) , '' ) ;
15921687 if ( p . reportIds . length ) lines . push ( '## 报告' , ...p . reportIds . map ( id => `- ${ contextPackReportTitle ( id ) } (${ id } )` ) , '' ) ;
15931688 if ( p . traceIds . length ) lines . push ( '## Trace' , ...p . traceIds . map ( id => `- ${ id } ` ) , '' ) ;
15941689 if ( p . sessionId ) lines . push ( '## 来源会话' , `- ${ p . sessionId } ` , '' ) ;
@@ -1601,6 +1696,7 @@ function contextPackPayload(){
16011696 instructions :p . instructions ,
16021697 agent_id :p . agentId || null ,
16031698 dataset_ids :p . datasetIds ,
1699+ knowledge_base_ids :p . knowledgeBaseIds ,
16041700 report_ids :p . reportIds ,
16051701 trace_ids :p . traceIds ,
16061702 session_id :p . sessionId || null ,
@@ -1940,12 +2036,14 @@ async function refreshChatSessions(){
19402036 renderSessionList ( ) ;
19412037}
19422038async function refreshCommandAssets ( ) {
1943- const [ sessions , reports ] = await Promise . all ( [
2039+ const [ sessions , reports , knowledgeBases ] = await Promise . all ( [
19442040 api ( '/api/sessions' ) . catch ( ( ) => chatSessions ) ,
1945- api ( '/api/reports' ) . catch ( ( ) => reportsCache )
2041+ api ( '/api/reports' ) . catch ( ( ) => reportsCache ) ,
2042+ api ( '/api/knowledge-bases' ) . catch ( ( ) => knowledgeBasesCache )
19462043 ] ) ;
19472044 chatSessions = sessions || [ ] ;
19482045 reportsCache = reports || [ ] ;
2046+ knowledgeBasesCache = knowledgeBases || [ ] ;
19492047 commandAssetsLoaded = true ;
19502048}
19512049async function updateChatSession ( id , payload , btn ) {
@@ -2946,6 +3044,7 @@ function knowledgeDetailHtml(kb,versions=[]){
29463044 const actions = contextActionStrip ( [
29473045 { label :'用知识库提问' , onclick :`setChatDraft('${ jsArg ( `基于知识库“${ kbName } ”回答:请说明它适合支撑哪些业务问题,并给出可追问方向。` ) } ','${ jsArg ( preferredAgent ) } ')` } ,
29483046 { label :'查看语义资产' , onclick :`openSemanticFiltered('${ jsArg ( kbName ) } ','')` } ,
3047+ { label :'加入工作包' , onclick :`addKnowledgeToContextPack('${ jsArg ( kb . id ) } ',this)` } ,
29493048 { label :'查看审计' , onclick :`openAuditFiltered('${ jsArg ( kb . id ) } ')` } ,
29503049 { label :'创建接入任务' , onclick :`setCodexDraft('${ jsArg ( `完善知识库接入:${ kbName } ` ) } ','${ jsArg ( `检查知识库 ${ kb . id } 的 Adapter 绑定、版本展示、Agent 引用和审计链路,保持权限、Trace 和外部知识源边界不退化。` ) } ')` }
29513050 ] ) ;
0 commit comments