openvibers/openvibe.codes Commits
Goals: give the independent verifier room for a reasoning answer (#424)
Changes
@@@ -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 = creturn { 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) {nnew file mode 100644@@@ -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();++})();
@@ -21,6 +21,8 @@ const { verify } = require('../../engine/goal');
@@ -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). */
/** The cheap rung: one request, at most a short answer. The env may name another (CODES_GOALS_MODEL). */
const DEFAULT_MODEL = 'deepseek-chat';
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. */
/** The answer's text from either dialect OpenVibe.AI speaks. */
function answerText(doc) {
function answerText(doc) {
@@ -57,11 +59,11 @@ function createGoalVerifier({ config = {}, fetchImpl = globalThis.fetch, log = c
@@ -57,11 +59,11 @@ function createGoalVerifier({ config = {}, fetchImpl = globalThis.fetch, log = c
return { res, doc };
return { res, doc };
};
};
let { res, doc } = await ask('/v1/chat/completions', {
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) {
if (res && res.status === 404) {
({ res, doc } = await ask('/v1/messages', {
({ 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) {
if (!res || !res.ok) {
new file mode 100644
new file mode 100644
@@ -0,0 +1,62 @@
@@ -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();
+})();