92 lines
4.4 KiB
JavaScript
92 lines
4.4 KiB
JavaScript
const assert = require('node:assert/strict')
|
|||
|
|
const fs = require('node:fs')
|
||
|
|
const path = require('node:path')
|
||
|
|
const { execFileSync } = require('node:child_process')
|
||
|
|
const { idle } = require('./publish_public_runtime.cjs')
|
||
|
|
|
||
|
|
const root = path.resolve(__dirname, '../..')
|
||
|
|
const modelID = '404e005b-83ae-5e2e-a3b3-999f9d5cd47e'
|
||
|
|
const tenantID = 10059
|
||
|
|
const report = { checked_at: new Date().toISOString(), model_id: modelID, tenant_id: tenantID, model_calls: 0, changed: false }
|
||
|
|
let token, stage = 'snapshot'
|
||
|
|
|
||
|
|
function snapshot() {
|
||
|
|
const sql = `BEGIN READ ONLY;
|
||
|
|
SELECT json_build_object('model_id',id,'provider',parameters->>'provider',
|
||
|
|
'thinking_control',parameters->'extra_config'->>'thinking_control',
|
||
|
|
'unchanged_fields_sha256',encode(sha256(convert_to((to_jsonb(models) - 'updated_at' - 'parameters' ||
|
||
|
|
jsonb_build_object('parameters',parameters #- '{extra_config,thinking_control}'))::text,'UTF8')),'hex'))
|
||
|
|
FROM models WHERE id='${modelID}' AND tenant_id=${tenantID} AND deleted_at IS NULL;
|
||
|
|
COMMIT;`
|
||
|
|
const result = execFileSync('docker', ['--context', 'colima-yawan', 'exec', '-i', 'WeKnora-postgres',
|
||
|
|
'psql', '-X', '-qAt', '-v', 'ON_ERROR_STOP=1', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915'],
|
||
|
|
{ input: sql, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 15000 })
|
||
|
|
return JSON.parse(result)
|
||
|
|
}
|
||
|
|
|
||
|
|
async function api(route, method = 'GET', body) {
|
||
|
|
const response = await fetch(`http://127.0.0.1:18080/api/v1${route}`, {
|
||
|
|
method, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}`, 'X-Tenant-ID': String(tenantID) } : {}) },
|
||
|
|
body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(30000),
|
||
|
|
})
|
||
|
|
assert.equal(response.status, 200)
|
||
|
|
const result = await response.json()
|
||
|
|
assert.notEqual(result.success, false)
|
||
|
|
return result.data || result
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
const before = snapshot()
|
||
|
|
report.before = before
|
||
|
|
assert.equal(before.provider, 'deepseek')
|
||
|
|
assert.equal(before.thinking_control, 'none')
|
||
|
|
if (!process.argv.includes('--apply')) {
|
||
|
|
console.log(JSON.stringify({ ...report, proposed: 'thinking_type', dry_run: true }))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
stage = 'idle_check'
|
||
|
|
idle()
|
||
|
|
stage = 'login'
|
||
|
|
const credentials = JSON.parse(fs.readFileSync(path.join(root, 'acceptance/super-admin-login.json'), 'utf8'))
|
||
|
|
token = (await api('/auth/login', 'POST', credentials)).token
|
||
|
|
assert.ok(token)
|
||
|
|
stage = 'model_read'
|
||
|
|
const model = await api(`/models/${modelID}`)
|
||
|
|
assert.equal(model.tenant_id, tenantID)
|
||
|
|
assert.equal(model.name, 'deepseek-flash')
|
||
|
|
assert.equal(model.type, 'KnowledgeQA')
|
||
|
|
assert.equal(model.source, 'remote')
|
||
|
|
assert.equal(model.parameters.provider, 'deepseek')
|
||
|
|
assert.equal(new URL(model.parameters.base_url).hostname, 'api.deepseek.com')
|
||
|
|
assert.equal(model.parameters.extra_config?.thinking_control, 'none')
|
||
|
|
const receiptPath = path.join(root, 'acceptance', `graph-model-thinking-fix-${Date.now()}.json`)
|
||
|
|
fs.writeFileSync(receiptPath, JSON.stringify(report, null, 2) + '\n', { flag: 'wx', mode: 0o600 })
|
||
|
|
stage = 'compare_before_update'
|
||
|
|
assert.deepEqual(snapshot(), before)
|
||
|
|
stage = 'model_update'
|
||
|
|
await api(`/models/${modelID}`, 'PUT', {
|
||
|
|
name: model.name, display_name: model.display_name, description: model.description,
|
||
|
|
type: model.type, source: model.source,
|
||
|
|
parameters: { ...model.parameters, extra_config: { ...model.parameters.extra_config, thinking_control: 'thinking_type' } },
|
||
|
|
})
|
||
|
|
report.changed = true
|
||
|
|
stage = 'verify'
|
||
|
|
report.after = snapshot()
|
||
|
|
fs.writeFileSync(receiptPath, JSON.stringify(report, null, 2) + '\n', { mode: 0o600 })
|
||
|
|
assert.equal(report.after.thinking_control, 'thinking_type')
|
||
|
|
assert.equal(report.after.unchanged_fields_sha256, before.unchanged_fields_sha256)
|
||
|
|
const after = await api(`/models/${modelID}`)
|
||
|
|
assert.equal(after.parameters.extra_config.thinking_control, 'thinking_type')
|
||
|
|
report.passed = true
|
||
|
|
report.verified_at = new Date().toISOString()
|
||
|
|
fs.writeFileSync(receiptPath, JSON.stringify(report, null, 2) + '\n', { mode: 0o600 })
|
||
|
|
console.log(JSON.stringify({ passed: true, changed: true, before: 'none', after: 'thinking_type',
|
||
|
|
unrelated_fields_and_credentials_preserved: true, model_calls: 0, receipt_path: receiptPath }))
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch(() => {
|
||
|
|
console.error(JSON.stringify({ passed: false, stage, changed: report.changed, model_calls: 0,
|
||
|
|
error: 'Guarded model configuration update stopped; inspect the private receipt without printing credentials.' }))
|
||
|
|
process.exitCode = 1
|
||
|
|
})
|