openvibers/openvibe.ai Commits

The advisor's chat run honours its pinned offer (#431)

OP OpenVibers committed 3 days ago · 8c7791d9c7 · 1 file, +6 −5

* The advisor: a cheap run asks a stronger model mid-run, each use on its receipt

* Advice on an app's own run, with the app's ai.app.run token

* Choose an app's advisor from the offers its own run may use

* Fix the advisor tests to run, and let the advisor's ai.chat run pin its offer for an app

* Sequence the advisor migration after quota files

* Merge remote-tracking branch 'origin/main' into al3-effort-advisor

* Keep the chat route pin internal to the advisor's own call

* The advisor honours the model named on the run, and an app can read its own receipt

* Merge current main

* The advisor's chat run honours its pinned offer

Changes

ddiff --git a/server/providers/index.js b/server/providers/index.js
iindex 4054f81..7750e34 100644
---- a/server/providers/index.js
++++ b/server/providers/index.js
@@@ -1654,14 +1654,15 @@ function createProviderPool({ db, registry, config, clock = { now: () => Date.no
explain.paid_by = 'openvibe';
explain.attempts = attempts;
explain.reasons = [...(explain.reasons || []), ...(selfAttempts.length ? ['your own keys did not answer; the network is the fallback'] : [])];
-- // A first-party service's generate run may name one offer from GET /api/v1/routes. The run then
-- // tries that offer alone: no fallback to another model, so the caller gets the model it asked for
-- // or an explicit failure. server/runs.js validates the pin against this same offer list first.
-- if (ctx.routePin && operation === 'generate' && !exact) {
++ // A first-party service's generate run may name one offer from GET /api/v1/routes; the advisor's own
++ // ai.chat child run names its stronger model the same way. The run then tries that offer alone: no
++ // fallback to another model, so the caller gets the model it asked for or an explicit failure.
++ // server/runs.js validates the pin against this same offer list first.
++ if (ctx.routePin && ['generate', 'chat'].includes(operation) && !exact) {
const pin = ctx.routePin;
const chosen = list.find((c) => c.provider === pin.provider && (pin.model == null || c.model === pin.model));
if (!chosen) {
-- throw new AiError(422, 'route.not_offered', `route ${pin.model ? `${pin.provider}/${pin.model}` : pin.provider} is not offered for generate; GET /api/v1/routes?operation=generate lists the offers`);
++ throw new AiError(422, 'route.not_offered', `route ${pin.model ? `${pin.provider}/${pin.model}` : pin.provider} is not offered for ${operation}; GET /api/v1/routes?operation=${operation} lists the offers`);
}
list = [chosen];
explain.reasons = [`pinned to ${chosen.model ? `${chosen.provider}/${chosen.model}` : chosen.provider} for this run`, ...(explain.reasons || [])];