Files
xinmi/scripts/verify_real_generation.cjs
yawan f7fc992f57
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
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

118 lines
8.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const { execFileSync } = require('node:child_process')
const directory = path.resolve(__dirname, '../../acceptance')
const base = 'http://127.0.0.1:18080/api/v1'
const resumed = process.env.RESUME_REAL_GENERATION === '1' ? JSON.parse(fs.readFileSync(path.join(directory, 'real-generation-checks.json'), 'utf8')) : null
const prefix = resumed?.prefix || `real-generation-${Date.now()}`
const checks = [], actions = [], fixtures = []
let auth, agent, stage = 'login', failure
const sql = statement => execFileSync('docker', ['--context', 'colima-yawan', 'exec', 'WeKnora-postgres', 'psql', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915', '-v', 'ON_ERROR_STOP=1', '-Atc', statement], {encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']}).trim()
const literal = value => "'" + String(value).replaceAll("'", "''") + "'"
function check(name, value) {
for (let i = checks.length - 1; i >= 0; i--) if (checks[i].name === name) checks.splice(i, 1)
checks.push({name, passed: !!value}); assert.ok(value, name)
}
async function api(route, method = 'GET', body, expected = 200) {
const response = await fetch(base + route, {method, headers: {'Content-Type': 'application/json', ...(auth ? {Authorization: `Bearer ${auth.token}`, 'X-Tenant-ID': '10059'} : {})}, body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(60000)})
const result = await response.json()
assert.equal(response.status, expected, `${stage}: ${method} ${route}: HTTP ${response.status}`)
return result
}
async function create(kind, data) {
const resource = (await api(`/workbench/resources/${kind}`, 'POST', {name: `${prefix}-${kind}`, data}, 201)).data
fixtures.push(resource)
return resource
}
async function action(resource, name, args = {}) {
stage = `${resource.kind}:${name}`
const start = Date.now()
const job = (await api(`/workbench/resources/${resource.kind}/${resource.id}/actions/${name}`, 'POST', {version: resource.version, ...args}, 202)).data
let current, lastStage
do {
await new Promise(resolve => setTimeout(resolve, 1200))
current = (await api(`/workbench/jobs/${job.id}`)).data
if (current.stage !== lastStage) { console.log(JSON.stringify({action: name, stage: current.stage, elapsed_seconds: Math.round((Date.now() - start) / 1000)})); lastStage = current.stage }
} while (['queued', 'running'].includes(current.status) && Date.now() - start < 12 * 60 * 1000)
actions.push({action: name, job_id: job.id, status: current.status, elapsed_ms: Date.now() - start, error: current.error || ''})
if (current.status !== 'succeeded') throw new Error(`AI action ${name} ended ${current.status}; see private verification report`)
return (await api(`/workbench/resources/${resource.kind}/${resource.id}`)).data
}
async function main() {
const credentials = JSON.parse(fs.readFileSync(path.join(directory, 'super-admin-login.json'), 'utf8'))
auth = await api('/auth/login', 'POST', {email: credentials.email, password: credentials.password})
const models = (await api('/models')).data
const model = models.find(m => m.name === 'deepseek-flash' && m.type === 'KnowledgeQA')
assert.ok(model, 'Migrated live generation model is configured')
const kb = (await api('/knowledge-bases')).data.find(k => k.name === '销售逻辑')
assert.ok(kb, 'Migrated knowledge base exists')
const before = (await api('/workbench/balance')).data
let map, task
if (resumed) {
fixtures.push(...resumed.fixtures)
actions.push(...resumed.actions)
checks.push(...resumed.checks)
agent = (await api(`/agents/${resumed.agent_id}`)).data
map = (await api(`/workbench/resources/training-maps/${resumed.fixtures.find(r => r.kind === 'training-maps').id}`)).data
task = (await api(`/workbench/resources/content-tasks/${resumed.fixtures.find(r => r.kind === 'content-tasks').id}`)).data
} else {
agent = (await api('/agents', 'POST', {name: prefix, config: {
model_id: model.id, agent_mode: 'quick-answer', kb_selection_mode: 'selected', knowledge_bases: [kb.id],
system_prompt: '本次是普通消费者沟通课程的验收样例,只讨论需求澄清、预算与理性决策。不要输出成人性描写。课程只安排两天,每天两个主要框架节点。拆解为三个内容段,每段正文约 250 字,保持术语和逻辑一致。所有输出遵循当前任务要求的 JSON 或正文格式。', temperature: 0.2,
}}, 201)).data
const flow = await create('flows', {nodes: [{id: 'writer', label: '课程作者', agent_id: agent.id}], edges: []})
map = await create('training-maps', {topic: '成年人如何澄清购物需求并做出理性消费决策', days_count: 2, days_auto: false, agent_id: agent.id, knowledge_base_ids: [kb.id], reading_mode: 'full'})
map = await action(map, 'generate')
check('two_real_generated_days', map.data.days.length === 2)
check('native_evidence_retrieved', map.data.reading?.evidence_chunks > 0)
check('layered_reading_reports_native_documents', map.data.reading?.documents > 0)
map = await action(map, 'confirm')
map = await action(map, 'generate-day', {day: 1})
map = await action(map, 'confirm-day', {day: 1})
task = await create('content-tasks', {map_id: map.id, day: 1, flow_id: flow.id, reading_mode: 'full'})
task = await action(task, 'decompose')
check('real_decomposition_has_segments', task.data.segments.length >= 2)
check('configuration_is_frozen', !!task.data.flow_snapshot && Object.keys(task.data.agent_snapshots || {}).length > 0)
}
const selected = task.data.segments.slice(0, 3).map(s => s.id)
for (const segment of task.data.segments) {
if (['accepted', 'skipped'].includes(segment.status)) continue
if (segment.id === selected[1] || !selected.includes(segment.id)) task = await action(task, 'skip', {segment_id: segment.id})
else {
task = await action(task, 'generate', {segment_id: segment.id})
check(`generated_${segment.id}_has_evidence`, task.data.segments.find(s => s.id === segment.id).source_ids.length > 0)
task = await action(task, 'accept', {segment_id: segment.id})
}
}
if (!task.data.quality) task = await action(task, 'review')
check('structured_real_quality_report', !!task.data.quality?.manuscript_hash && Array.isArray(task.data.quality.issues))
if (!task.data.quality.passed) {
const issues = task.data.quality.issues
const ids = new Set(issues.filter(i => i.severity === 'blocking').flatMap(i => i.segment_ids))
for (const segment of task.data.segments) {
if (segment.status !== 'accepted' || !ids.has(segment.id)) continue
const advice = issues.filter(i => i.segment_ids.includes(segment.id)).map(i => `${i.problem};${i.suggestion}`).join('\n')
task = await action(task, 'revise', {segment_id: segment.id, advice})
task = await action(task, 'accept', {segment_id: segment.id})
}
for (const segment of task.data.segments) if (segment.status === 'needs_review') task = await action(task, 'accept', {segment_id: segment.id})
task = await action(task, 'review')
}
check('real_quality_passed_after_review', task.data.quality.passed)
task = await action(task, 'compose')
check('real_final_manuscript_saved', task.status === 'completed' && task.data.composed.length > 100)
const after = (await api('/workbench/balance')).data
check('real_usage_debited_once_and_holds_released', after.balance < before.balance && after.reserved === before.reserved)
const jobIDs = actions.map(a => literal(a.job_id)).join(',')
const usage = JSON.parse(sql(`SELECT coalesce(json_agg(json_build_object('action', action, 'tokens', input_tokens+output_tokens, 'points', points, 'status',status)), '[]'::json) FROM workbench_usage WHERE tenant_id=10059 AND job_id IN (${jobIDs})`))
check('live_model_usage_recorded', usage.some(r => r.tokens > 0 && r.points > 0))
fs.writeFileSync(path.join(directory, 'real-generation-manuscript.json'), JSON.stringify({map, task, usage}, null, 2), {mode: 0o600})
}
main().catch(error => { failure = error.message; process.exitCode = 1 }).finally(async () => {
// Keep generated material for inspection; mark its disposable provenance.
fs.writeFileSync(path.join(directory, 'real-generation-checks.json'), JSON.stringify({time: new Date().toISOString(), prefix, stage, checks, actions, fixtures: fixtures.map(r => ({id: r.id, kind: r.kind})), agent_id: agent?.id, failure, passed: !failure}, null, 2), {mode: 0o600})
console.log(JSON.stringify({stage, checks, failure, passed: !failure}))
})