WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const assert = require('node:assert/strict')
|
||||
const { execFileSync } = require('node:child_process')
|
||||
|
||||
const root = path.resolve(__dirname, '../..')
|
||||
const directory = path.join(root, 'acceptance')
|
||||
const tenant = 10059
|
||||
const businessIDs = ['16c3e59c-4bb6-41cf-ae0a-6d0941b3d032', '20b9280e-ed73-4700-8fa5-944d6ae6dd5f', 'bb86b44a-576b-4bf8-a6bf-8f1baca3a845', 'badd084b-f229-4005-8fb3-196fbe139fe0', '5d28a7e5-e00e-44a7-a0e1-c72342cdcf68', '3ae9257d-928a-4886-8101-7cfd0ed8c27c']
|
||||
const model = '404e005b-83ae-5e2e-a3b3-999f9d5cd47e'
|
||||
const origin = 'http://127.0.0.1:18080/api/v1'
|
||||
const statePath = path.join(directory, 'business-wiki-backfill.json')
|
||||
const literal = s => `'${String(s).replaceAll("'", "''")}'`
|
||||
const docker = (args, input) => execFileSync('docker', ['--context', 'colima-yawan', ...args], {input, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: 8 * 1024 * 1024})
|
||||
const sql = statement => docker(['exec', '-i', 'WeKnora-postgres', 'psql', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915', '-At', '-v', 'ON_ERROR_STOP=1'], statement).trim()
|
||||
let auth
|
||||
async function api(route, method = 'GET', body) {
|
||||
const response = await fetch(origin + route, {method, headers: {'Content-Type': 'application/json', ...(auth ? {Authorization: `Bearer ${auth.token}`, 'X-Tenant-ID': String(tenant)} : {})}, body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(30000)})
|
||||
const value = await response.json()
|
||||
assert.ok(response.ok && value.success !== false, `${method} ${route}: HTTP ${response.status}`)
|
||||
return value
|
||||
}
|
||||
function inventory() {
|
||||
return JSON.parse(sql(`SELECT coalesce(json_agg(row_to_json(t)),'[]'::json) FROM (
|
||||
SELECT k.id,k.name,k.type,k.indexing_strategy,k.wiki_config,k.embedding_model_id,
|
||||
(SELECT count(*) FROM knowledges n WHERE n.knowledge_base_id=k.id AND n.deleted_at IS NULL) AS documents,
|
||||
(SELECT count(*) FROM chunks c WHERE c.knowledge_base_id=k.id AND c.deleted_at IS NULL) AS chunks,
|
||||
(SELECT count(*) FROM wiki_pages p WHERE p.knowledge_base_id=k.id AND p.deleted_at IS NULL) AS wiki_pages,
|
||||
(SELECT count(*) FROM task_pending_ops o WHERE o.scope_id=k.id AND o.task_type='wiki:ingest') AS pending_documents,
|
||||
(SELECT count(*) FROM task_pending_ops o WHERE o.scope_id=k.id AND o.task_type='wiki:finalize') AS pending_finalization,
|
||||
(SELECT count(*) FROM task_dead_letters d WHERE d.scope_id=k.id AND d.task_type='wiki:ingest') AS failed_documents
|
||||
FROM knowledge_bases k WHERE k.tenant_id=${tenant} AND k.id IN (${businessIDs.map(literal).join(',')}) AND k.deleted_at IS NULL AND k.is_temporary=false ORDER BY k.name
|
||||
) t;`))
|
||||
}
|
||||
async function main() {
|
||||
const credentials = JSON.parse(fs.readFileSync(path.join(directory, 'super-admin-login.json'), 'utf8'))
|
||||
auth = await api('/auth/login', 'POST', {username: credentials.username, password: credentials.password})
|
||||
assert.equal(auth.user.is_system_admin, true)
|
||||
const before = inventory()
|
||||
assert.equal(before.length, businessIDs.length, 'All six business knowledge bases must exist')
|
||||
let state = fs.existsSync(statePath) ? JSON.parse(fs.readFileSync(statePath, 'utf8')) : {started_at: new Date().toISOString(), tenant_id: tenant, before}
|
||||
if (process.argv.includes('--apply')) {
|
||||
const modelRow = sql(`SELECT count(*) FROM models WHERE id=${literal(model)} AND tenant_id=${tenant} AND deleted_at IS NULL AND type='KnowledgeQA';`)
|
||||
assert.equal(modelRow, '1')
|
||||
fs.writeFileSync(path.join(directory, `business-wiki-config-before-${Date.now()}.json`), JSON.stringify(before, null, 2), {mode: 0o600, flag: 'wx'})
|
||||
for (const row of before) {
|
||||
assert.equal(row.type, 'document', 'Only document knowledge bases are supported')
|
||||
assert.equal(row.indexing_strategy.vector_enabled, true, 'Existing vector index is required')
|
||||
assert.equal(row.indexing_strategy.keyword_enabled, true, 'Existing keyword index is required')
|
||||
const kb = (await api(`/knowledge-bases/${row.id}`)).data
|
||||
const updated = await api(`/knowledge-bases/${row.id}`, 'PUT', {name: kb.name, description: kb.description, config: {
|
||||
chunking_config: kb.chunking_config, image_processing_config: kb.image_processing_config,
|
||||
faq_config: kb.faq_config, auto_tag_config: kb.auto_tag_config,
|
||||
indexing_strategy: {...kb.indexing_strategy, wiki_enabled: true},
|
||||
wiki_config: {...kb.wiki_config, synthesis_model_id: model, extraction_granularity: 'standard', max_pages_per_ingest: 0,
|
||||
ingest_batch_size: 1, ingest_map_parallel: 1, ingest_reduce_parallel: 1, ingest_max_inflight: 1},
|
||||
}})
|
||||
assert.equal(updated.data.indexing_strategy.wiki_enabled, true)
|
||||
assert.equal(updated.data.embedding_model_id, row.embedding_model_id)
|
||||
}
|
||||
const ids = before.map(k => literal(k.id)).join(',')
|
||||
// Backfill the Wiki queue without resetting document status or rebuilding RAG indexes.
|
||||
state.enqueued = Number(sql(`WITH added AS (
|
||||
INSERT INTO task_pending_ops (tenant_id,task_type,scope,scope_id,op,dedup_key,payload,fail_count,enqueued_at)
|
||||
SELECT n.tenant_id,'wiki:ingest','knowledge_base',n.knowledge_base_id,'ingest',n.id,
|
||||
jsonb_build_object('op','ingest','knowledge_id',n.id,'language','zh-CN'),0,now()
|
||||
FROM knowledges n JOIN knowledge_bases k ON k.id=n.knowledge_base_id AND k.tenant_id=n.tenant_id
|
||||
WHERE n.tenant_id=${tenant} AND n.knowledge_base_id IN (${ids}) AND n.deleted_at IS NULL
|
||||
AND k.deleted_at IS NULL AND k.indexing_strategy->>'wiki_enabled'='true'
|
||||
AND n.parse_status='completed' AND n.pending_subtasks_count=0
|
||||
AND EXISTS (SELECT 1 FROM chunks c WHERE c.knowledge_id=n.id AND c.deleted_at IS NULL)
|
||||
AND NOT EXISTS (SELECT 1 FROM task_pending_ops o WHERE o.tenant_id=n.tenant_id AND o.scope_id=n.knowledge_base_id AND o.task_type='wiki:ingest' AND o.dedup_key=n.id)
|
||||
AND NOT EXISTS (SELECT 1 FROM wiki_pages p WHERE p.knowledge_base_id=n.knowledge_base_id AND p.deleted_at IS NULL AND p.source_refs @> jsonb_build_array(n.id))
|
||||
RETURNING id
|
||||
) SELECT count(*) FROM added;`))
|
||||
state.configured_at = new Date().toISOString()
|
||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), {mode: 0o600})
|
||||
const scopes = before.map(k => ({tenant_id: tenant, knowledge_base_id: k.id, language: 'zh-CN'}))
|
||||
docker(['cp', path.join(directory, 'wiki-backfill-trigger'), 'yawan-native-acceptance:/tmp/wiki-backfill-trigger'])
|
||||
state.trigger_result = JSON.parse(docker(['exec', '-i', 'yawan-native-acceptance', '/tmp/wiki-backfill-trigger'], JSON.stringify(scopes)))
|
||||
}
|
||||
state.checked_at = new Date().toISOString()
|
||||
state.current = inventory()
|
||||
for (const prior of state.before) {
|
||||
const current = state.current.find(k => k.id === prior.id)
|
||||
assert.equal(current.documents, prior.documents)
|
||||
assert.equal(current.chunks, prior.chunks)
|
||||
assert.equal(current.embedding_model_id, prior.embedding_model_id)
|
||||
}
|
||||
state.rag_preserved = true
|
||||
state.all_wiki_enabled = state.current.every(k => k.indexing_strategy.wiki_enabled && k.indexing_strategy.vector_enabled && k.indexing_strategy.keyword_enabled)
|
||||
if (process.argv.includes('--apply')) assert.equal(state.all_wiki_enabled, true)
|
||||
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), {mode: 0o600})
|
||||
console.log(JSON.stringify({checked_at: state.checked_at, all_wiki_enabled: state.all_wiki_enabled, rag_preserved: true, enqueued: state.enqueued, knowledge_bases: state.current.map(k => ({name: k.name,documents: k.documents,chunks: k.chunks,wiki_pages: k.wiki_pages,pending_documents:k.pending_documents,pending_finalization:k.pending_finalization,failed_documents:k.failed_documents}))}))
|
||||
}
|
||||
main().catch(error => {console.error(error.message); process.exitCode = 1})
|
||||
Reference in New Issue
Block a user