openvibers/openvibe.codes Commits

Goals: give the independent verifier room for a reasoning answer (#424)

OP OpenVibers committed 1 days ago · 534bc175d8 · 2 files, +66 −2

Changes

ddiff --git a/server/domain/goal-verifier.js b/server/domain/goal-verifier.js
iindex 8974d9e..9879abd 100644
---- a/server/domain/goal-verifier.js
++++ b/server/domain/goal-verifier.js
@@@ -21,6 +21,8 @@ const { verify } = require('../../engine/goal');
/** The cheap rung: one request, at most a short answer. The env may name another (CODES_GOALS_MODEL). */
const DEFAULT_MODEL = 'deepseek-chat';
++/** Room for the answer plus a reasoning model's thinking: 300 left AI's reasoning models with no content. */
++const MAX_ANSWER_TOKENS = 800;
/** The answer's text from either dialect OpenVibe.AI speaks. */
function answerText(doc) {
@@@ -57,11 +59,11 @@ function createGoalVerifier({ config = {}, fetchImpl = globalThis.fetch, log = c
return { res, doc };
};
let { res, doc } = await ask('/v1/chat/completions', {
-- model, messages: [{ role: 'user', content: prompt }], temperature: 0, max_tokens: 300,
++ model, messages: [{ role: 'user', content: prompt }], temperature: 0, max_tokens: MAX_ANSWER_TOKENS,
});
if (res && res.status === 404) {
({ res, doc } = await ask('/v1/messages', {
-- model, max_tokens: 300, messages: [{ role: 'user', content: prompt }],
++ model, max_tokens: MAX_ANSWER_TOKENS, messages: [{ role: 'user', content: prompt }],
}));
}
if (!res || !res.ok) {
ddiff --git a/test/goal-verifier.test.js b/test/goal-verifier.test.js
nnew file mode 100644
iindex 0000000..d54a8c4
---- /dev/null
++++ b/test/goal-verifier.test.js
@@@ -0,0 +1,62 @@
++'use strict';
++
++/**
++ * AL1's independent verifier: one cheap model call, with room for the answer. A reasoning model needs more
++ * than 300 tokens or it finishes with `length` and no content (the 2026-11-11 canary found this), which must
++ * never become an acceptance.
++ */
++const assert = require('assert');
++const { check, done } = require('./helpers/boot');
++const { createGoalVerifier } = require('../server/domain/goal-verifier');
++
++function json(body, status = 200) {
++ return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });
++}
++
++function verifier(aiAnswer) {
++ const calls = [];
++ const fetchImpl = async (url, init = {}) => {
++ const target = String(url);
++ if (target.endsWith('/oauth/token')) {
++ calls.push({ url: target, body: null });
++ return json({ access_token: 'a-test-token', token_type: 'Bearer', expires_in: 300 });
++ }
++ const body = init.body ? JSON.parse(init.body) : null;
++ calls.push({ url: target, body });
++ if (target.endsWith('/v1/chat/completions')) return json({ choices: [{ message: { role: 'assistant', content: aiAnswer } }] });
++ return json({ error: 'unexpected call' }, 500);
++ };
++ const v = createGoalVerifier({
++ config: {
++ oauth: { clientId: 'codes', clientSecret: 'not-a-real-secret' },
++ networkInternalUrl: 'http://network.invalid',
++ ai: { internalUrl: 'http://ai.invalid', audience: 'openvibe.ai' },
++ goals: { model: 'deepseek-chat' },
++ },
++ fetchImpl,
++ log: { warn: () => {} },
++ });
++ return { v, calls };
++}
++
++(async () => {
++ await check('the verifier asks for enough answer tokens for a reasoning model', async () => {
++ const { v, calls } = verifier('{"verdict":"accept","reason":"the check passed"}');
++ const r = await v.verify({ goal: 'the check passes', check: { kind: 'command', value: 'true' }, proposal: 'done', evidence: '{"check_output":""}', checkOutput: '' });
++ assert.strictEqual(r.status, 'accepted');
++ assert.strictEqual(r.reason, 'the check passed');
++ const ask = calls.find((c) => c.url.endsWith('/v1/chat/completions'));
++ assert.ok(ask, 'the verifier called AI');
++ assert.strictEqual(ask.body.max_tokens, 800);
++ assert.strictEqual(ask.body.model, 'deepseek-chat');
++ });
++
++ await check('an empty answer is not an acceptance', async () => {
++ const { v } = verifier('');
++ const r = await v.verify({ goal: 'the check passes', check: { kind: 'command', value: 'true' }, proposal: 'done', evidence: '{}', checkOutput: '' });
++ assert.strictEqual(r.status, 'failed');
++ assert.match(r.reason, /failed to produce a verdict/);
++ });
++
++ done();
++})();