dsh-form-fill-agent 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/diagnostics.js +1 -0
- package/lib/http.js +2 -2
- package/lib/ui.html +8 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
|
|
|
34
34
|
- 结果筛选显示当前/总数及已选格数,下载始终包含全部确认内容。
|
|
35
35
|
- 简单 XLSX 安全解析、工作表与表头识别、企业名称或信用代码锚点识别。
|
|
36
36
|
- 确定性字段语义映射、只填空白、单元格级预览与来源追溯。
|
|
37
|
-
-
|
|
37
|
+
- 133 字段目录:工商、企业简介、联系方式、上市、开票、进出口、自身及关联风险、实际控制人。唯一完整实控人记录可填写,多名/分页/异常留空待核验。事实和确定性摘要区分来源,不生成风险结论。
|
|
38
38
|
- 五步工作台、指令回填、任务历史、可恢复单元格选择、取消/失败重试与重启恢复。
|
|
39
39
|
- 可选择工作表、表头行并修正字段映射;修改后需重新预览。
|
|
40
40
|
- 查询主体不一致时展示返回候选,人工选择或补充已核验的完整主体后重新查询,原表名称不覆盖。
|
|
@@ -43,7 +43,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
|
|
|
43
43
|
|
|
44
44
|
## 使用边界与数据保存
|
|
45
45
|
|
|
46
|
-
补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。主包为 0.2.
|
|
46
|
+
补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。主包为 0.2.13,共享内核为 0.2.9,Provider 为 0.2.1,共享字段契约为 0.1.0。
|
|
47
47
|
|
|
48
48
|
支持企业完整登记名称或 18 位信用代码;简称、主体不一致或多候选不会自动猜选。仅有信用代码的表格也可作为查询锚点。连接候选检索工具时可检索简称,仍需人工确认;缺少工具时降级人工输入。字段映射采用确定性规则及人工确认,不让模型编造事实。支持普通内部公式、固定区域下拉、基础条件格式和普通表格对象;复杂 Excel 结构和 Word 暂不支持,保真边界见下方安全说明。
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
|
|
|
51
51
|
|
|
52
52
|
显式设置 DSH_HOME 时,任务保存至其 form-fill-tasks 目录,默认 24 小时过期;未设置时使用内存。任务保存输入表格和归一化预览,不持久化原始 MCP 响应,可主动删除任务。模型及 MCP 凭据由宿主管理。
|
|
53
53
|
|
|
54
|
-
0.2.
|
|
54
|
+
0.2.13 保存任务 schema 5,兼容读取旧 schema 1–4;回退旧版本须使用升级前任务目录副本或新目录,防止丢失已确认的字段/主体设置。
|
|
55
55
|
|
|
56
56
|
## 文档与反馈
|
|
57
57
|
|
package/lib/diagnostics.js
CHANGED
package/lib/http.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID, createHash } from 'node:crypto';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { applyChangeSet, parseWorkbook } from 'form-fill-core';
|
|
4
|
-
import { FIELD_CATALOG, QCC_FIELD_CATALOG, isCompleteAnchor } from 'qcc-form-fill-provider';
|
|
4
|
+
import { FIELD_CATALOG, QCC_FIELD_CATALOG, ACTUAL_CONTROLLER_GROUP, isCompleteAnchor } from 'qcc-form-fill-provider';
|
|
5
5
|
import { previewBytes } from './workflow.js';
|
|
6
6
|
import { createTaskStore } from './task-store.js';
|
|
7
7
|
import { allCandidates, selectCandidates } from './task-model.js';
|
|
@@ -25,7 +25,7 @@ export function createFormFillHandler({ basePath = '', getPort, now = Date.now,
|
|
|
25
25
|
candidates: allCandidates(task).changes,
|
|
26
26
|
selectedIds: task.preview.changeSet.changes.map(c=>c.id),
|
|
27
27
|
catalog: FIELD_CATALOG,
|
|
28
|
-
catalogGroups: QCC_FIELD_CATALOG.map(g=>({id:g.id,label:g.label,fields:g.fields.flatMap(item=>FIELD_CATALOG.filter(f=>f.key===item.id||f.aliases?.includes(item.id)).map(f=>f.key))})),
|
|
28
|
+
catalogGroups: [...QCC_FIELD_CATALOG,ACTUAL_CONTROLLER_GROUP].map(g=>({id:g.id,label:g.label,fields:g.fields.flatMap(item=>FIELD_CATALOG.filter(f=>f.key===item.id||f.aliases?.includes(item.id)).map(f=>f.key))})),
|
|
29
29
|
structure: parseWorkbook(task.bytes).sheets.filter(s => !s.hidden).map(s => ({
|
|
30
30
|
name: s.name,
|
|
31
31
|
rows: s.rows.filter(r => !r.hidden).slice(0,30).map(r => ({ number: r.number, cells: Object.values(s.cells).filter(c => c.row === r.number && !c.hidden && c.value.trim()).map(c => ({ column: c.column, label: c.value })) })),
|
package/lib/ui.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="toolbar ff-execution-toolbar"><span id="execution-progress" role="status"></span><button id="cancel-run" disabled>取消查询</button><button id="retry-run" disabled>仅重试失败 / 未完成查询</button></div>
|
|
13
13
|
<details id="grid" hidden><summary>查看完整原表 / 结果表格</summary><p class="muted">全量行列按页查看,隐藏行列只读标注。结果视图展示当前已选填写;下载不受分页或搜索影响。</p><div class="toolbar"><label>工作表 <select id="grid-sheet"></select></label><label>视图 <select id="grid-view"><option value="original">原表</option><option value="result">结果预览</option></select></label><label>搜索全部行 <input id="grid-query" type="search" maxlength="200"></label><button id="grid-search">搜索</button></div><p id="grid-status" role="status"></p><div class="scroll"><table><thead id="grid-head"></thead><tbody id="grid-body"></tbody></table></div><div class="toolbar"><button id="grid-prev">上一页</button><button id="grid-next">下一页</button><button id="grid-left">前 16 列</button><button id="grid-right">后 16 列</button></div></details>
|
|
14
14
|
<div class="card" data-pane="import"><h2>导入表格</h2><p class="muted">支持 XLSX,最大 8 MiB。支持普通内部公式、固定文本或区域下拉、基础条件格式及普通表格对象;宏、图片、图表及复杂扩展结构暂不支持。上传仅做本地分析,不查询企查查。</p>
|
|
15
|
-
<div class="controls"><input type="file" id="file" accept=".xlsx" aria-label="选择 XLSX 文件"><
|
|
15
|
+
<div class="controls"><input type="file" id="file" accept=".xlsx" aria-label="选择 XLSX 文件"><span class="muted">选择文件后自动解析,核对结果后设置填写字段。</span></div>
|
|
16
16
|
<details><summary>体验合成模板</summary><p>下面模板只使用合成数据,不查询真实企业。</p><div class="controls" id="samples"><button class="secondary" data-name="客户台账">客户台账</button><button class="secondary" data-name="供应商准入表">供应商准入表</button><button class="secondary" data-name="合同主体信息表">合同主体信息表</button></div></details></div>
|
|
17
17
|
<section id="preview" hidden>
|
|
18
18
|
<div class="card" data-pane="rules"><h2>字段与规则</h2><p id="structure"></p><p id="anchor-alert" role="status"></p><p>默认只填空白。可选择工作表、修正表头行及字段;应用设置会清除旧预览和主体确认,请重新核验。</p><label>搜索字段 <input id="mapping-search" type="search" placeholder="按原表字段名称筛选"></label><p class="muted">未识别字段可手动选择;没有数据来源的字段选择“不填写”。规则只保存表头与映射,不含记录。</p><button id="mapping-save">保存字段规则</button><button id="mapping-load">复用字段规则</button><div id="mapping"></div><button id="configure" class="primary">应用字段设置</button><div id="mapping-next" hidden role="status"><p>设置已保存。下一步核验企业主体,确认填写范围后生成指令并发送,查询结果会进入填写预览。</p><button id="mapping-continue" class="primary">下一步:主体核验</button></div></div>
|
|
@@ -206,16 +206,16 @@ function renderSettings(data){
|
|
|
206
206
|
select.onkeydown=event=>{if(event.key==='Enter'){event.preventDefault();finish()}};
|
|
207
207
|
search.oninput=options;dimension.onchange=options;
|
|
208
208
|
if(!chosen&&!manual){
|
|
209
|
-
const hints={'原文件导入名称':['company_name'],'单位':['company_name'],'名称':['company_name'],'法定代表人':['legal_person'],'登记状态':['business_status'],'地址':['registered_address'],'网址':['contact_official_website']}[cell.label.trim()]||[];
|
|
210
|
-
const recommended=(data.catalog||[]).filter(f
|
|
209
|
+
const hints={'原文件导入名称':['company_name'],'单位':['company_name'],'名称':['company_name'],'法定代表人':['legal_person'],'登记状态':['business_status'],'地址':['registered_address','mailing_address','invoice_address'],'网址':['contact_official_website']}[cell.label.trim()]||[];
|
|
210
|
+
const recommended=[...hints.map(key=>data.catalog.find(f=>f.key===key)).filter(Boolean),...(data.catalog||[]).filter(f=>!hints.includes(f.key)&&matches(f,cell.label))].slice(0,4);
|
|
211
211
|
if(recommended.length){const hint=document.createElement('span');hint.textContent='推荐(确认后采用):';suggestions.append(hint)}
|
|
212
|
-
for(const field of recommended){const button=document.createElement('button');button.type='button';button.textContent=field.label;button.onclick=()=>{chosen=field.key;manual=true;suggestions.replaceChildren();options();$('mapping-next').hidden=true;picker.open=false;trigger.focus()};suggestions.append(button)}
|
|
212
|
+
for(const field of recommended){const button=document.createElement('button');button.type='button';button.textContent='推荐:'+field.label;button.onclick=()=>{chosen=field.key;manual=true;suggestions.replaceChildren();options();$('mapping-next').hidden=true;picker.open=false;trigger.focus()};suggestions.append(button)}
|
|
213
213
|
}
|
|
214
214
|
const filters=document.createElement('div');filters.className='ff-mapping-filters';filters.append(search,dimension);
|
|
215
215
|
select.size=8;
|
|
216
216
|
const done=document.createElement('button');done.type='button';done.textContent='确认选择';done.onclick=finish;
|
|
217
|
-
panel.append(
|
|
218
|
-
label.append(status,picker);options();fields.append(label);
|
|
217
|
+
panel.append(filters,select,count,done);
|
|
218
|
+
label.append(status,suggestions,picker);options();fields.append(label);
|
|
219
219
|
}
|
|
220
220
|
};
|
|
221
221
|
header.onchange=showFields;section.append(header,fields);showFields();$('mapping').append(section);
|
|
@@ -245,7 +245,7 @@ $('summary').textContent='可填写 '+data.changeSet.changes.length+' 格 · '+(
|
|
|
245
245
|
$('structure').textContent=data.analysis.tables.map(t=>t.sheet+':表头第 '+t.headerRow+' 行').join(';');
|
|
246
246
|
renderSettings(data);
|
|
247
247
|
for(const c of data.candidates||data.changeSet.changes){const tr=row('changes',[c.sheet+' ! '+c.cell,c.label,c.oldValue||'(空白)',c.newValue,(c.source.startsWith('qcc://')?'企查查 · '+c.source.slice(6):'合成示例')+' · 获取于 '+c.acquiredAt.slice(0,10)+' · 明确字段映射'],['位置','字段','原值','准备填写','来源']);const td=document.createElement('td'),check=document.createElement('input');check.type='checkbox';check.checked=(data.selectedIds||data.changeSet.changes.map(x=>x.id)).includes(c.id);check.value=c.id;check.disabled=data.confirmed;check.setAttribute('aria-label','填写 '+c.cell);td.append(check);tr.append(td)}
|
|
248
|
-
for(const i of data.changeSet.incomplete)row('incomplete',[i.sheet+' '+(i.cell||('第 '+i.row+' 行')),i.label||'—',data.diagnostics?.reasons.find(r=>r.code===i.reason)?.label||reasons[i.reason]||'此项需要人工处理'],['位置','字段','原因']);
|
|
248
|
+
for(const i of data.changeSet.incomplete)row('incomplete',[i.sheet+' '+(i.cell||('第 '+i.row+' 行')),i.label||'—',i.detail||data.diagnostics?.reasons.find(r=>r.code===i.reason)?.label||reasons[i.reason]||'此项需要人工处理'],['位置','字段','原因']);
|
|
249
249
|
$('field-results').replaceChildren();
|
|
250
250
|
for(const column of data.diagnostics?.columns||[])row('field-results',[column.label,(data.catalog.find(f=>f.key===column.field)?.label||'未映射 / 不填写'),column.ready,column.incomplete],['原表字段','映射字段','预览可填写格数','未完成项']);
|
|
251
251
|
$('preview').hidden=false;if(data.confirmed)downloads();$('qcc-command').textContent=command();
|
|
@@ -257,7 +257,7 @@ initWizard();filterResults();syncActions();if($('wizard').open)renderWizard();
|
|
|
257
257
|
async function restore(silent=false){if(busy||!current&& !location.hash)return;const id=new URLSearchParams(location.hash.slice(1)).get('task');if(!id)return;try{const data=await api('/task/'+encodeURIComponent(id));if(!silent||data.revision!==current?.revision){render(data);if(!silent){const target=pendingNavigation||(data.confirmed?'download':'preview');pendingNavigation=undefined;if(target==='history')$('history-tab').click();else navigate(target)}}}catch(e){if(!silent)$('status').textContent=e.message}}
|
|
258
258
|
async function preview(buffer,name,demo=false){controls(true);try{const bytes=new Uint8Array(buffer);if(bytes.length>8388608)throw Error('文件超过 8 MiB');let binary='';for(let i=0;i<bytes.length;i+=32768)binary+=String.fromCharCode(...bytes.subarray(i,i+32768));$('grid').open=true;render(await api('/preview',{base64:btoa(binary),filename:name,sessionId,analyzeOnly:!demo}));navigate(demo?'preview':'rules');$('status').textContent='预览已准备好,请检查后确认。'}catch(e){$('status').textContent=e.message;$('wizard-message').textContent=e.message}finally{controls(false)}}
|
|
259
259
|
document.querySelectorAll('[data-step]').forEach(b=>b.onclick=()=>navigate(b.dataset.step));
|
|
260
|
-
$('
|
|
260
|
+
$('file').onchange=async()=>{const input=$('file'),f=input.files[0];if(!f||busy)return;input.disabled=true;controls(true);$('status').textContent='正在自动解析表格…';try{await preview(await f.arrayBuffer(),f.name)}catch(e){$('status').textContent=e.message}finally{input.disabled=false;input.value='';controls(false)}};
|
|
261
261
|
function readMapping(){const configuration={sheets:[],headers:[],mappings:[]};for(const section of $('mapping').querySelectorAll('fieldset')){if(!section.querySelector('.sheet-enabled').checked)continue;const sheet=section.dataset.sheet;configuration.sheets.push(sheet);configuration.headers.push({sheet,row:Number(section.querySelector('.header-row').value)});for(const field of section.querySelectorAll('[data-column]'))configuration.mappings.push({sheet,column:Number(field.dataset.column),field:field.value||null})}return configuration}
|
|
262
262
|
function mappingSignature(configuration){return JSON.stringify(configuration.headers.map(h=>({sheet:h.sheet,row:h.row,cells:current.structure.find(s=>s.name===h.sheet)?.rows.find(r=>r.number===h.row)?.cells}))) }
|
|
263
263
|
$('mapping-save').onclick=()=>{try{if(!current||busy)return;const configuration=readMapping();localStorage.setItem('ff-mapping:'+owner,JSON.stringify({schema:1,signature:mappingSignature(configuration),configuration}));$('status').textContent='字段规则已保存在本浏览器。'}catch{$('status').textContent='浏览器无法保存字段规则。'}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-form-fill-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "AI填表智能体:XLSX blank-cell preview, QCC enrichment and persistent tasks for DSH.",
|
|
6
6
|
"repository": { "type": "git", "url": "git+https://github.com/duhu2000/dsh-form-fill-agent.git", "directory": "packages/dsh-form-fill-agent" },
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"node": ">=22"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"form-fill-core": "0.2.
|
|
28
|
-
"qcc-form-fill-provider": "0.2.
|
|
27
|
+
"form-fill-core": "0.2.9",
|
|
28
|
+
"qcc-form-fill-provider": "0.2.1"
|
|
29
29
|
},
|
|
30
30
|
"dsh": {
|
|
31
31
|
"client": { "inject": ["@deepseek-ai/dsh-client-ui-layout", "@deepseek-ai/dsh-client-ui-conversation"], "platform": "web" },
|