openvibers/openvibe.ai Commits
Relay AI usage samples to local Billing by default (#470)
Changes
@@@ -66,7 +66,8 @@ OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000# ── Billing (plan T5 lane F) ──# One platform.usage-sample@1 reading per accounted run, posted to billing.usage.record with AI's# service token, so Network must grant client `ai` billing.usage.record on openvibe.billing. Readings--# are queued from the start and sent while OV_BILLING_INTERNAL_URL is set; USAGE_SAMPLES=off stops queueing them.++# are queued from the start. In production they are sent to colocated Billing at 127.0.0.1:4600 by++# default; OV_BILLING_INTERNAL_URL overrides the address, and USAGE_SAMPLES=off stops queueing them.#OV_BILLING_INTERNAL_URL=http://127.0.0.1:4600#USAGE_SAMPLES=on#USAGE_SAMPLES_INTERVAL_MS=2000@@@ -537,12 +537,13 @@ function load(env = process.env) {// ── Billing (plan T5 lane F): one platform.usage-sample@1 reading per accounted run, posted by// server/usage-samples.js to billing.usage.record (POST /api/v1/usage, audience openvibe.billing).-- // Readings are queued unless USAGE_SAMPLES=off; they are sent while OV_BILLING_INTERNAL_URL is set.++ // Readings are queued unless USAGE_SAMPLES=off. Production's colocated Billing receives them by++ // default; OV_BILLING_INTERNAL_URL overrides that address when the services are split.// No `capability:` key: AI asks for billing.usage.record (named in usage-samples.js), Billing enforces it,// and openvibe-contracts-check reads a `capability: 'x'` literal as one this service enforces.billing: {enabled: bool(env.USAGE_SAMPLES, true),-- billingUrl: trimUrl(env.OV_BILLING_INTERNAL_URL),++ billingUrl: trimUrl(env.OV_BILLING_INTERNAL_URL != null ? env.OV_BILLING_INTERNAL_URL : (isProduction ? 'http://127.0.0.1:4600' : '')),audience: 'openvibe.billing',intervalMs: Math.max(100, int(env.USAGE_SAMPLES_INTERVAL_MS, 2000)),},nnew file mode 100644@@@ -0,0 +1,16 @@++'use strict';++const assert = require('assert');++const { load } = require('../server/config');++const { suite } = require('./helpers');++++const t = suite('billing-config');++++t.test('production relays usage to colocated Billing unless overridden or disabled', () => {++ assert.strictEqual(load({ NODE_ENV: 'production' }).billing.billingUrl, 'http://127.0.0.1:4600');++ assert.strictEqual(load({ NODE_ENV: 'development' }).billing.billingUrl, '');++ assert.strictEqual(load({ NODE_ENV: 'production', OV_BILLING_INTERNAL_URL: 'https://billing.example/' }).billing.billingUrl, 'https://billing.example');++ assert.strictEqual(load({ NODE_ENV: 'production', OV_BILLING_INTERNAL_URL: '' }).billing.billingUrl, '');++ assert.strictEqual(load({ NODE_ENV: 'production', USAGE_SAMPLES: 'off' }).billing.enabled, false);++});++++t.run();
@@ -66,7 +66,8 @@ OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000
@@ -66,7 +66,8 @@ OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000
# ── Billing (plan T5 lane F) ──
# ── Billing (plan T5 lane F) ──
# One platform.usage-sample@1 reading per accounted run, posted to billing.usage.record with AI's
# One platform.usage-sample@1 reading per accounted run, posted to billing.usage.record with AI's
# service token, so Network must grant client `ai` billing.usage.record on openvibe.billing. Readings
# service token, so Network must grant client `ai` billing.usage.record on openvibe.billing. Readings
-# are queued from the start and sent while OV_BILLING_INTERNAL_URL is set; USAGE_SAMPLES=off stops queueing them.
+# are queued from the start. In production they are sent to colocated Billing at 127.0.0.1:4600 by
+# default; OV_BILLING_INTERNAL_URL overrides the address, and USAGE_SAMPLES=off stops queueing them.
#OV_BILLING_INTERNAL_URL=http://127.0.0.1:4600
#OV_BILLING_INTERNAL_URL=http://127.0.0.1:4600
#USAGE_SAMPLES=on
#USAGE_SAMPLES=on
#USAGE_SAMPLES_INTERVAL_MS=2000
#USAGE_SAMPLES_INTERVAL_MS=2000
@@ -537,12 +537,13 @@ function load(env = process.env) {
@@ -537,12 +537,13 @@ function load(env = process.env) {
// ── Billing (plan T5 lane F): one platform.usage-sample@1 reading per accounted run, posted by
// ── Billing (plan T5 lane F): one platform.usage-sample@1 reading per accounted run, posted by
// server/usage-samples.js to billing.usage.record (POST /api/v1/usage, audience openvibe.billing).
// server/usage-samples.js to billing.usage.record (POST /api/v1/usage, audience openvibe.billing).
- // Readings are queued unless USAGE_SAMPLES=off; they are sent while OV_BILLING_INTERNAL_URL is set.
+ // Readings are queued unless USAGE_SAMPLES=off. Production's colocated Billing receives them by
+ // default; OV_BILLING_INTERNAL_URL overrides that address when the services are split.
// No `capability:` key: AI asks for billing.usage.record (named in usage-samples.js), Billing enforces it,
// No `capability:` key: AI asks for billing.usage.record (named in usage-samples.js), Billing enforces it,
// and openvibe-contracts-check reads a `capability: 'x'` literal as one this service enforces.
// and openvibe-contracts-check reads a `capability: 'x'` literal as one this service enforces.
billing: {
billing: {
enabled: bool(env.USAGE_SAMPLES, true),
enabled: bool(env.USAGE_SAMPLES, true),
- billingUrl: trimUrl(env.OV_BILLING_INTERNAL_URL),
+ billingUrl: trimUrl(env.OV_BILLING_INTERNAL_URL != null ? env.OV_BILLING_INTERNAL_URL : (isProduction ? 'http://127.0.0.1:4600' : '')),
audience: 'openvibe.billing',
audience: 'openvibe.billing',
intervalMs: Math.max(100, int(env.USAGE_SAMPLES_INTERVAL_MS, 2000)),
intervalMs: Math.max(100, int(env.USAGE_SAMPLES_INTERVAL_MS, 2000)),
},
},
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+'use strict';
+const assert = require('assert');
+const { load } = require('../server/config');
+const { suite } = require('./helpers');
+
+const t = suite('billing-config');
+
+t.test('production relays usage to colocated Billing unless overridden or disabled', () => {
+ assert.strictEqual(load({ NODE_ENV: 'production' }).billing.billingUrl, 'http://127.0.0.1:4600');
+ assert.strictEqual(load({ NODE_ENV: 'development' }).billing.billingUrl, '');
+ assert.strictEqual(load({ NODE_ENV: 'production', OV_BILLING_INTERNAL_URL: 'https://billing.example/' }).billing.billingUrl, 'https://billing.example');
+ assert.strictEqual(load({ NODE_ENV: 'production', OV_BILLING_INTERNAL_URL: '' }).billing.billingUrl, '');
+ assert.strictEqual(load({ NODE_ENV: 'production', USAGE_SAMPLES: 'off' }).billing.enabled, false);
+});
+
+t.run();