Files
memind/scripts/check-goosed-v149-cost-smoke.mjs
T
john 67220a14ea fix(goose-v149): block unattended real LLM smokes by default
Prevent phase2/phase3/all reruns from burning DashScope tokens unless
GOOSE_V149_ALLOW_REAL_LLM=1 is explicitly set with human approval.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 22:08:39 +08:00

154 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
/**
* Phase 2: verify Finish SSE carries token_state suitable for Portal billing.
* Requires DEEPSEEK_API_KEY (or GOOSE_V149_TEST_API_KEY).
*/
import fs from 'node:fs';
import path from 'node:path';
import { randomUUID } from 'node:crypto';
import { fileURLToPath } from 'node:url';
import { normalizeTokenState } from '../billing.mjs';
import { enrichTokenStateForBilling, resolveBillingTokenState } from '../billing-token-state.mjs';
import { collectReplyEvents, createV149Client } from './goose-v149-sse.mjs';
import { enforceRealLlmGate } from './goose-v149-real-llm-gate.mjs';
enforceRealLlmGate('check-goosed-v149-cost-smoke.mjs');
const memindRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
function loadEnvFile(filePath) {
if (!fs.existsSync(filePath)) return;
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const eq = trimmed.indexOf('=');
if (eq < 0) continue;
const key = trimmed.slice(0, eq).trim();
let value = trimmed.slice(eq + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"'))
|| (value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
if (!process.env[key]) process.env[key] = value;
}
}
loadEnvFile(path.join(memindRoot, '.env'));
const client = createV149Client();
const workingDir = process.env.GOOSE_V149_WORKING_DIR || memindRoot;
const providerId = process.env.GOOSE_V149_PROVIDER_ID || 'custom_tkmind_relay_deepseek';
const model = process.env.GOOSE_V149_PROVIDER_MODEL || 'deepseek-chat';
const apiKey = process.env.DEEPSEEK_API_KEY || process.env.GOOSE_V149_TEST_API_KEY || '';
async function upsertProvider() {
const body = {
provider_name: providerId,
api_key: apiKey,
api_url: process.env.GOOSE_V149_PROVIDER_API_URL
|| process.env.DEEPSEEK_API_BASE_URL
|| 'https://api.deepseek.com/v1',
models: [model],
relay_provider: 'deepseek',
};
let response = await client.apiFetch(`/config/custom-providers/${encodeURIComponent(providerId)}`, {
method: 'PUT',
body: JSON.stringify(body),
});
if (!response.ok && (response.status === 404 || /not found/i.test(await response.text()))) {
await client.apiJson('/config/custom-providers', body);
}
}
async function main() {
if (!apiKey) {
console.log('GOOSE_V149_COST_SMOKE_SKIP: no DEEPSEEK_API_KEY');
process.exit(0);
}
await upsertProvider();
const session = await client.apiJson('/agent/start', { working_dir: workingDir });
if (!session?.id) throw new Error('missing session id');
await client.apiJson('/agent/update_provider', {
session_id: session.id,
provider: providerId,
model,
});
const requestId = randomUUID();
const result = await collectReplyEvents({
client,
sessionId: session.id,
requestId,
userMessage: {
role: 'user',
created: Date.now(),
content: [{ type: 'text', text: 'Reply with exactly one word: pong' }],
metadata: { userVisible: true, agentVisible: true, displayText: 'cost smoke ping' },
},
});
if (result.outcome !== 'finish') {
throw new Error(`expected Finish, got ${result.outcome}: ${result.errorEvent?.error ?? 'timeout'}`);
}
const rawTokenState = result.finishEvent?.token_state ?? null;
if (!rawTokenState) {
throw new Error('Finish event missing token_state');
}
const normalized = normalizeTokenState(rawTokenState);
const hasTokens =
normalized.accumulatedInputTokens > 0
|| normalized.accumulatedOutputTokens > 0
|| normalized.inputTokens > 0
|| normalized.outputTokens > 0;
if (!hasTokens) {
throw new Error(`Finish token_state has no token counts: ${JSON.stringify(normalized)}`);
}
const billingState = await resolveBillingTokenState(rawTokenState, { sessionId: session.id });
const enriched = enrichTokenStateForBilling(rawTokenState, {}, {
...process.env,
H5_USE_BACKEND_COST: process.env.H5_USE_BACKEND_COST ?? '1',
H5_COST_ESTIMATE_FROM_TOKENS: process.env.H5_COST_ESTIMATE_FROM_TOKENS ?? '1',
});
const outputDir = path.join(memindRoot, 'docs', 'baselines');
fs.mkdirSync(outputDir, { recursive: true });
const evidencePath = path.join(
outputDir,
`goose-v149-cost-evidence-${new Date().toISOString().replace(/[:.]/g, '-')}.json`,
);
const evidence = {
schemaVersion: 'goose-v149-cost-evidence-v1',
capturedAt: new Date().toISOString(),
sessionId: session.id,
requestId,
finishReason: result.finishEvent?.reason ?? null,
tokenState: normalized,
billingState: {
accumulatedInputTokens: billingState.accumulatedInputTokens,
accumulatedOutputTokens: billingState.accumulatedOutputTokens,
accumulatedCost: billingState.accumulatedCost,
},
enrichedCost: enriched.accumulatedCost,
};
fs.writeFileSync(evidencePath, `${JSON.stringify(evidence, null, 2)}\n`, 'utf8');
console.log(`GOOSE_V149_COST_SMOKE_OK: session=${session.id}`);
console.log(
` tokens in=${normalized.accumulatedInputTokens} out=${normalized.accumulatedOutputTokens} cost=${billingState.accumulatedCost ?? enriched.accumulatedCost ?? 'null'}`,
);
console.log(` evidence=${evidencePath}`);
}
main().catch((error) => {
console.error(`GOOSE_V149_COST_SMOKE_FAIL: ${error.message}`);
process.exit(1);
});