openvibers/openvibe.tools Commits
Send Billing usage readings under the tools manifest id
Changes
@@@ -304,8 +304,9 @@ reload reattaches.`GET /api/health` shows `jobs.usage` (pending, invalid).- **Usage readings to OpenVibe.Billing** (`apps/_shared/billing.js`, `apps/_shared/jobs/usage.js`, plan T5 step 7):every job that succeeds or fails (anyone's, not only a project's; a cancelled one is not billed) gets one-- `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service and source-- `openvibe.tools`, operation `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,++ `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service `tools` (the++ Contracts manifest id, so Billing keys ratings and budgets on the reading) and source `openvibe.tools`, operation++ `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,quantity 1, unit `jobs`, `at` the job's end, plus `project` and `trace_id` when the job has them and `subject`(`user:usr_…`) only for a signed-in person's job, never an app or a browser session. It is stored in`tool_job_billing_readings` in the transaction that records the end (no network call there); the pruner's timer@@@ -66,7 +66,7 @@ function readingOf(row) {const r = {id: `tools-job-${row.id}`,idempotency_key: `tools:job:${row.id}`,-- service: 'openvibe.tools',++ service: 'tools', // the Contracts manifest id (services/tools.json): Billing keys ratings and budgets on itoperation: row.tool ? 'tools.tool.run' : 'tools.job.create',resource: DIMENSION_RE.test(resource) ? resource : 'other',quantity: 1,@@@ -214,7 +214,7 @@ function define(system) {for (const p of posts) {const v = contracts.validate('platform.usage-sample@1', p);assert.ok(v.valid, JSON.stringify(v.errors));-- assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['openvibe.tools', 'openvibe.tools', 1, 'jobs']);++ assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['tools', 'openvibe.tools', 1, 'jobs']);}const [person, appJob, failJob] = posts;assert.deepStrictEqual([person.id, person.operation, person.resource, person.subject, person.trace_id, person.project], [`tools-job-${personId}`, 'tools.tool.run', 'image-resize', `user:${USER}`, TRACE, undefined]);@@@ -51,7 +51,7 @@ release wait on the same lock and find it applied.- After step 5: `GET /api/health` on img, audio or docs shows `jobs.billing` with `pending` going back to 0after a tick (5 minutes) and `posted` growing; `refused` with a `last_error` of `token: 400 invalid_scope` means the grant ismissing. `SELECT COUNT(*) FROM tool_job_billing_readings WHERE sent_at IS NULL` stays small.--- In Billing: `GET /api/v1/usage?service=openvibe.tools` (`billing.ledger.admin`) lists the readings, one per++- In Billing: `GET /api/v1/usage?service=tools` (`billing.ledger.admin`) lists the readings, one perended job, `idempotency_key` `tools:job:<job id>`.```rehearse
@@ -304,8 +304,9 @@ reload reattaches.
@@ -304,8 +304,9 @@ reload reattaches.
`GET /api/health` shows `jobs.usage` (pending, invalid).
`GET /api/health` shows `jobs.usage` (pending, invalid).
- **Usage readings to OpenVibe.Billing** (`apps/_shared/billing.js`, `apps/_shared/jobs/usage.js`, plan T5 step 7):
- **Usage readings to OpenVibe.Billing** (`apps/_shared/billing.js`, `apps/_shared/jobs/usage.js`, plan T5 step 7):
every job that succeeds or fails (anyone's, not only a project's; a cancelled one is not billed) gets one
every job that succeeds or fails (anyone's, not only a project's; a cancelled one is not billed) gets one
- `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service and source
- `openvibe.tools`, operation `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,
+ `platform.usage-sample@1` reading — id `tools-job-<id>`, `idempotency_key` `tools:job:<id>`, service `tools` (the
+ Contracts manifest id, so Billing keys ratings and budgets on the reading) and source `openvibe.tools`, operation
+ `tools.tool.run` (a tool run) or `tools.job.create`, resource the tool or job type,
quantity 1, unit `jobs`, `at` the job's end, plus `project` and `trace_id` when the job has them and `subject`
quantity 1, unit `jobs`, `at` the job's end, plus `project` and `trace_id` when the job has them and `subject`
(`user:usr_…`) only for a signed-in person's job, never an app or a browser session. It is stored in
(`user:usr_…`) only for a signed-in person's job, never an app or a browser session. It is stored in
`tool_job_billing_readings` in the transaction that records the end (no network call there); the pruner's timer
`tool_job_billing_readings` in the transaction that records the end (no network call there); the pruner's timer
@@ -66,7 +66,7 @@ function readingOf(row) {
@@ -66,7 +66,7 @@ function readingOf(row) {
const r = {
const r = {
id: `tools-job-${row.id}`,
id: `tools-job-${row.id}`,
idempotency_key: `tools:job:${row.id}`,
idempotency_key: `tools:job:${row.id}`,
- service: 'openvibe.tools',
+ service: 'tools', // the Contracts manifest id (services/tools.json): Billing keys ratings and budgets on it
operation: row.tool ? 'tools.tool.run' : 'tools.job.create',
operation: row.tool ? 'tools.tool.run' : 'tools.job.create',
resource: DIMENSION_RE.test(resource) ? resource : 'other',
resource: DIMENSION_RE.test(resource) ? resource : 'other',
quantity: 1,
quantity: 1,
@@ -214,7 +214,7 @@ function define(system) {
@@ -214,7 +214,7 @@ function define(system) {
for (const p of posts) {
for (const p of posts) {
const v = contracts.validate('platform.usage-sample@1', p);
const v = contracts.validate('platform.usage-sample@1', p);
assert.ok(v.valid, JSON.stringify(v.errors));
assert.ok(v.valid, JSON.stringify(v.errors));
- assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['openvibe.tools', 'openvibe.tools', 1, 'jobs']);
+ assert.deepStrictEqual([p.service, p.source, p.quantity, p.unit], ['tools', 'openvibe.tools', 1, 'jobs']);
}
}
const [person, appJob, failJob] = posts;
const [person, appJob, failJob] = posts;
assert.deepStrictEqual([person.id, person.operation, person.resource, person.subject, person.trace_id, person.project], [`tools-job-${personId}`, 'tools.tool.run', 'image-resize', `user:${USER}`, TRACE, undefined]);
assert.deepStrictEqual([person.id, person.operation, person.resource, person.subject, person.trace_id, person.project], [`tools-job-${personId}`, 'tools.tool.run', 'image-resize', `user:${USER}`, TRACE, undefined]);
@@ -51,7 +51,7 @@ release wait on the same lock and find it applied.
@@ -51,7 +51,7 @@ release wait on the same lock and find it applied.
- After step 5: `GET /api/health` on img, audio or docs shows `jobs.billing` with `pending` going back to 0
- After step 5: `GET /api/health` on img, audio or docs shows `jobs.billing` with `pending` going back to 0
after a tick (5 minutes) and `posted` growing; `refused` with a `last_error` of `token: 400 invalid_scope` means the grant is
after a tick (5 minutes) and `posted` growing; `refused` with a `last_error` of `token: 400 invalid_scope` means the grant is
missing. `SELECT COUNT(*) FROM tool_job_billing_readings WHERE sent_at IS NULL` stays small.
missing. `SELECT COUNT(*) FROM tool_job_billing_readings WHERE sent_at IS NULL` stays small.
-- In Billing: `GET /api/v1/usage?service=openvibe.tools` (`billing.ledger.admin`) lists the readings, one per
+- In Billing: `GET /api/v1/usage?service=tools` (`billing.ledger.admin`) lists the readings, one per
ended job, `idempotency_key` `tools:job:<job id>`.
ended job, `idempotency_key` `tools:job:<job id>`.
```rehearse
```rehearse