from pathlib import Path import json,hashlib,tarfile root=Path('C:/kefu');name='knowledge-v5-search-20260916';old='knowledge-v4-review-20260916' release=root/'deploy'/name;release.mkdir(exist_ok=True);previous=root/'deploy'/old p=root/'wechat_rpa/KNOWLEDGE.md';s=p.read_text(encoding='utf-8') section=''' ## 检索输入与错误提示修复(2026-09-16) 旧版在空白或单字问题点击检索时,会把 FastAPI 422 校验数组直接交给消息组件,显示 `[object Object]`。本机浏览器使用旧构建复现了同样提示。 检索输入现在先去除首尾空白,按 Unicode 字符数校验 2–2000 个字符,前端拦截非法请求并给出中文说明;后端同步先去除首尾空白再校验。失败时清空旧结果并展示可读原因,不产生未处理的 Promise 错误。公共请求错误处理把字符串、FastAPI 数组及嵌套错误消息转为文字,不直接展示对象或校验响应里的原始 input。 检索仍只返回当前账号的已发布、有效知识;未发布知识不会因为此修复进入检索。当前账号没有已发布知识时展示「先审核并发布」说明,合法问题正常返回空结果。 验证:96 项后端回归,10 项前端错误格式与批量操作测试,前端类型检查和构建。隔离浏览器覆盖空白、单字、2001 字符拦截,正常问题回车检索及首尾空白处理,422 数组提示,旧结果清理与空知识库;模拟账号 3 条知识中仅 1 条已发布,正确命中 1 条。 ''' if '## 检索输入与错误提示修复(2026-09-16)' not in s: p.write_text(s+section,encoding='utf-8',newline='\n') files=[root/'wechat_rpa'/f for f in ['knowledge_api.py','test_knowledge_search.py','KNOWLEDGE.md']] files += [root/'admin-web/apps/web-antd/src'/f for f in ['api/request.ts','api/error-message.ts','api/error-message.test.ts','views/knowledge/center.vue']] files += [f for f in (root/'admin-web/apps/web-antd/dist').rglob('*') if f.is_file()] manifest=[] with tarfile.open(release/'payload.tar.gz','w:gz') as tf: for path in files: relative=path.relative_to(root).as_posix();data=path.read_bytes();tf.add(path,arcname=relative) manifest.append({'path':relative,'bytes':len(data),'sha256':hashlib.sha256(data).hexdigest()}) (release/'manifest.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2),encoding='utf-8') for filename in ['01-stage.sh','02-backup-build.sh','03-deploy.sh','04-verify.sh','Dockerfile.overlay','rollback.sh']: text=(previous/filename).read_text(encoding='utf-8').replace(old,name) if filename=='01-stage.sh': text=text.replace('sha256:b8221de05e2ab92afc66ab8b725ed24600da3e717a543998ae17a6611a011acf','sha256:5eedfd342540725f61075cf02fe94a93e94e68f06719e3f9eac6c064f204a975') text=text.replace('/home/ps/knowledge-v3-field-fix-20260916/manifest.json',f'/home/ps/{old}/manifest.json') text=text.replace('verified model-field fix release','verified full-review release') if filename=='02-backup-build.sh': text=text.replace('test_knowledge_review test_model_gateway','test_knowledge_review test_knowledge_search test_model_gateway') if filename=='03-deploy.sh': text=text.replace('Full-review version','Search-fix version') if filename=='04-verify.sh': text=text.replace("assert after['counts']['knowledge_review_job']==0 and after['counts']['knowledge_review_target']==0, 'Deployment must not submit reviews'\n",'') text=text.replace("('/api/v2/knowledge/overview',401,'GET'),", "('/api/v2/knowledge/overview',401,'GET'),('/api/v2/knowledge/search',401,'POST'),") (release/filename).write_text(text,encoding='utf-8',newline='\n') probe=(root/'deploy/knowledge-v4-review/probe_lan.py').read_text(encoding='utf-8').replace(old,name) (root/'deploy/knowledge-v5-search/probe_lan.py').write_text(probe,encoding='utf-8',newline='\n') print(json.dumps({'files':len(manifest),'payload_bytes':(release/'payload.tar.gz').stat().st_size,'release':str(release)}))