@vibesdotdev/moderation 0.0.0-stage → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +83 -2
- package/dist/decide.d.ts +16 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +81 -0
- package/dist/decide.js.map +1 -0
- package/dist/docs/moderation.decisions.docs.descriptor.d.ts +24 -0
- package/dist/docs/moderation.decisions.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/moderation.decisions.docs.descriptor.js +94 -0
- package/dist/docs/moderation.decisions.docs.descriptor.js.map +1 -0
- package/dist/docs/moderation.docs.descriptor.d.ts +22 -0
- package/dist/docs/moderation.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/moderation.docs.descriptor.js +68 -0
- package/dist/docs/moderation.docs.descriptor.js.map +1 -0
- package/dist/docs/moderation.subjects.docs.descriptor.d.ts +23 -0
- package/dist/docs/moderation.subjects.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/moderation.subjects.docs.descriptor.js +99 -0
- package/dist/docs/moderation.subjects.docs.descriptor.js.map +1 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +36 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/kinds/subject.kind.d.ts +10 -0
- package/dist/kinds/subject.kind.d.ts.map +1 -0
- package/dist/kinds/subject.kind.js +16 -0
- package/dist/kinds/subject.kind.js.map +1 -0
- package/dist/moderate.d.ts +17 -0
- package/dist/moderate.d.ts.map +1 -0
- package/dist/moderate.js +38 -0
- package/dist/moderate.js.map +1 -0
- package/dist/moderation.plugin.d.ts +12 -0
- package/dist/moderation.plugin.d.ts.map +1 -0
- package/dist/moderation.plugin.js +23 -0
- package/dist/moderation.plugin.js.map +1 -0
- package/dist/orchestration/labels.d.ts +5 -0
- package/dist/orchestration/labels.d.ts.map +1 -0
- package/dist/orchestration/labels.js +34 -0
- package/dist/orchestration/labels.js.map +1 -0
- package/dist/orchestration/resolve.d.ts +11 -0
- package/dist/orchestration/resolve.d.ts.map +1 -0
- package/dist/orchestration/resolve.js +90 -0
- package/dist/orchestration/resolve.js.map +1 -0
- package/dist/orchestration/state.d.ts +10 -0
- package/dist/orchestration/state.d.ts.map +1 -0
- package/dist/orchestration/state.js +58 -0
- package/dist/orchestration/state.js.map +1 -0
- package/dist/schemas/decision.d.ts +131 -0
- package/dist/schemas/decision.d.ts.map +1 -0
- package/dist/schemas/decision.js +97 -0
- package/dist/schemas/decision.js.map +1 -0
- package/dist/schemas/errors.d.ts +12 -0
- package/dist/schemas/errors.d.ts.map +1 -0
- package/dist/schemas/errors.js +11 -0
- package/dist/schemas/errors.js.map +1 -0
- package/dist/schemas/evaluation.d.ts +47 -0
- package/dist/schemas/evaluation.d.ts.map +1 -0
- package/dist/schemas/evaluation.js +14 -0
- package/dist/schemas/evaluation.js.map +1 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +9 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/input.d.ts +30 -0
- package/dist/schemas/input.d.ts.map +1 -0
- package/dist/schemas/input.js +20 -0
- package/dist/schemas/input.js.map +1 -0
- package/dist/schemas/labels.d.ts +19 -0
- package/dist/schemas/labels.d.ts.map +1 -0
- package/dist/schemas/labels.js +18 -0
- package/dist/schemas/labels.js.map +1 -0
- package/dist/schemas/subject.d.ts +79 -0
- package/dist/schemas/subject.d.ts.map +1 -0
- package/dist/schemas/subject.js +66 -0
- package/dist/schemas/subject.js.map +1 -0
- package/package.json +73 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './schemas/index.ts';
|
|
2
|
+
export { MODERATION_SUBJECT_KIND_ID, moderationSubjectKind } from './kinds/subject.kind.ts';
|
|
3
|
+
export { MODERATION_ERROR_CODES, ModerationError, type ModerationErrorCode } from './errors.ts';
|
|
4
|
+
export { decideModeration } from './decide.ts';
|
|
5
|
+
export { moderateSubject, type ModerateSubjectInput, type ModerateSubjectOptions } from './moderate.ts';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACN,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @vibesdotdev/moderation: universal root. No node:, bun:, process or
|
|
2
|
+
// browser globals; everything resolves on the runtime the caller passes.
|
|
3
|
+
export * from "./schemas/index.js";
|
|
4
|
+
export { MODERATION_SUBJECT_KIND_ID, moderationSubjectKind } from "./kinds/subject.kind.js";
|
|
5
|
+
export { MODERATION_ERROR_CODES, ModerationError } from "./errors.js";
|
|
6
|
+
export { decideModeration } from "./decide.js";
|
|
7
|
+
export { moderateSubject } from "./moderate.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,yEAAyE;AAEzE,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAA4B,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACN,eAAe,EAGf,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Kind id consumers pass to `runtime.query()`. */
|
|
2
|
+
export declare const MODERATION_SUBJECT_KIND_ID: "moderation/subject";
|
|
3
|
+
/**
|
|
4
|
+
* `moderation/subject`: a moderatable content type registered by the feature
|
|
5
|
+
* that owns the content. Descriptor-only: no default implementation and no
|
|
6
|
+
* loaders. Single-owner, so a subject id cannot be re-registered by a second
|
|
7
|
+
* origin; a host that wants different thresholds registers its own id.
|
|
8
|
+
*/
|
|
9
|
+
export declare const moderationSubjectKind: import("@vibesdotdev/kernel").RuntimeKindDescriptorRecord<unknown>;
|
|
10
|
+
//# sourceMappingURL=subject.kind.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject.kind.d.ts","sourceRoot":"","sources":["../../src/kinds/subject.kind.ts"],"names":[],"mappings":"AAMA,mDAAmD;AACnD,eAAO,MAAM,0BAA0B,EAAG,oBAA6B,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,oEAIhC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRuntimeKind } from '@vibesdotdev/kernel';
|
|
2
|
+
import { ModerationSubjectDescriptorSchema } from "../schemas/subject.js";
|
|
3
|
+
/** Kind id consumers pass to `runtime.query()`. */
|
|
4
|
+
export const MODERATION_SUBJECT_KIND_ID = 'moderation/subject';
|
|
5
|
+
/**
|
|
6
|
+
* `moderation/subject`: a moderatable content type registered by the feature
|
|
7
|
+
* that owns the content. Descriptor-only: no default implementation and no
|
|
8
|
+
* loaders. Single-owner, so a subject id cannot be re-registered by a second
|
|
9
|
+
* origin; a host that wants different thresholds registers its own id.
|
|
10
|
+
*/
|
|
11
|
+
export const moderationSubjectKind = createRuntimeKind({
|
|
12
|
+
id: MODERATION_SUBJECT_KIND_ID,
|
|
13
|
+
descriptorSchema: ModerationSubjectDescriptorSchema,
|
|
14
|
+
descriptorOwnership: 'single-owner'
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=subject.kind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject.kind.js","sourceRoot":"","sources":["../../src/kinds/subject.kind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACN,iCAAiC,EAEjC,MAAM,uBAAuB,CAAC;AAE/B,mDAAmD;AACnD,MAAM,CAAC,MAAM,0BAA0B,GAAG,oBAA6B,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAuC;IAC5F,EAAE,EAAE,0BAA0B;IAC9B,gBAAgB,EAAE,iCAAiC;IACnD,mBAAmB,EAAE,cAAc;CACnC,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { VibesRuntime } from '@vibesdotdev/kernel';
|
|
2
|
+
import type { ModerationDecision } from './schemas/decision.ts';
|
|
3
|
+
import { type ModerateSubjectInput, type ModerateSubjectOptions } from './schemas/input.ts';
|
|
4
|
+
export type { ModerateSubjectInput, ModerateSubjectOptions, ModerationState, ModerationStateValue, ModerationStateInput } from './schemas/input.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Judge one subject through the passed runtime.
|
|
7
|
+
*
|
|
8
|
+
* Order: parse the ref and snapshot JSON state; resolve the subject; resolve its battery once; check
|
|
9
|
+
* the battery contract (heads equal labels, all noul, non-empty
|
|
10
|
+
* requiredState); check the state; resolve runtime/client `ai`; evaluate with
|
|
11
|
+
* the pre-resolved questions; validate every answer; apply the threshold
|
|
12
|
+
* policy. Domain preconditions fail with a named ModerationError before any
|
|
13
|
+
* inference is spent. Evaluator errors propagate unchanged. Nothing is
|
|
14
|
+
* persisted or enqueued.
|
|
15
|
+
*/
|
|
16
|
+
export declare function moderateSubject(runtime: VibesRuntime, input: ModerateSubjectInput, options?: ModerateSubjectOptions): Promise<ModerationDecision>;
|
|
17
|
+
//# sourceMappingURL=moderate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderate.d.ts","sourceRoot":"","sources":["../src/moderate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAA0D,KAAK,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAIpJ,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEpJ;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAuB7B"}
|
package/dist/moderate.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { decideModeration } from "./decide.js";
|
|
2
|
+
import { ModerationEnvelopeSchema, ModerateSubjectOptionsSchema } from "./schemas/input.js";
|
|
3
|
+
import { snapshotState } from "./orchestration/state.js";
|
|
4
|
+
import { resolveSubject, resolveBattery, assertBatteryContract, assertState, resolveEvaluator } from "./orchestration/resolve.js";
|
|
5
|
+
import { readLabels } from "./orchestration/labels.js";
|
|
6
|
+
/**
|
|
7
|
+
* Judge one subject through the passed runtime.
|
|
8
|
+
*
|
|
9
|
+
* Order: parse the ref and snapshot JSON state; resolve the subject; resolve its battery once; check
|
|
10
|
+
* the battery contract (heads equal labels, all noul, non-empty
|
|
11
|
+
* requiredState); check the state; resolve runtime/client `ai`; evaluate with
|
|
12
|
+
* the pre-resolved questions; validate every answer; apply the threshold
|
|
13
|
+
* policy. Domain preconditions fail with a named ModerationError before any
|
|
14
|
+
* inference is spent. Evaluator errors propagate unchanged. Nothing is
|
|
15
|
+
* persisted or enqueued.
|
|
16
|
+
*/
|
|
17
|
+
export async function moderateSubject(runtime, input, options) {
|
|
18
|
+
const { subject: ref, state: inputState } = ModerationEnvelopeSchema.parse(input);
|
|
19
|
+
const parsedOptions = ModerateSubjectOptionsSchema.parse(options === undefined ? {} : options);
|
|
20
|
+
const state = snapshotState(inputState);
|
|
21
|
+
const subject = await resolveSubject(runtime, ref.type);
|
|
22
|
+
const battery = await resolveBattery(runtime, subject);
|
|
23
|
+
const requiredState = assertBatteryContract(subject, battery);
|
|
24
|
+
assertState(battery.id, requiredState, state);
|
|
25
|
+
const evaluator = await resolveEvaluator(runtime);
|
|
26
|
+
const questions = {};
|
|
27
|
+
for (const [name, question] of Object.entries(battery.questions)) {
|
|
28
|
+
questions[name] = { ...question };
|
|
29
|
+
}
|
|
30
|
+
const response = await evaluator.evaluate({
|
|
31
|
+
schemaId: battery.id,
|
|
32
|
+
questions,
|
|
33
|
+
state
|
|
34
|
+
});
|
|
35
|
+
const labels = readLabels(subject, ref, response);
|
|
36
|
+
return decideModeration(subject, labels, (parsedOptions.now ?? (() => new Date()))());
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=moderate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderate.js","sourceRoot":"","sources":["../src/moderate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,wBAAwB,EAAE,4BAA4B,EAA0D,MAAM,oBAAoB,CAAC;AACpJ,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,qBAAqB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAClI,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAGvD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,OAAqB,EACrB,KAA2B,EAC3B,OAAgC;IAEhC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAClF,MAAM,aAAa,GAAG,4BAA4B,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC/F,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAExC,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9D,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;IAE9C,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,SAAS,GAA6B,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAClE,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IACnC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC;QACzC,QAAQ,EAAE,OAAO,CAAC,EAAE;QACpB,SAAS;QACT,KAAK;KACL,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAClD,OAAO,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AACvF,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moderation Plugin
|
|
3
|
+
*
|
|
4
|
+
* Registers the `moderation/subject` kind and the owner-authored `docs/topic`
|
|
5
|
+
* descriptors. Features register their own subjects and batteries from their
|
|
6
|
+
* own plugins; this plugin registers no subject, no battery and no loader.
|
|
7
|
+
* The docs topics wait in the runtime's pending queue until `docs/topic`
|
|
8
|
+
* registers, so plugin order does not matter.
|
|
9
|
+
*/
|
|
10
|
+
export declare const moderationPlugin: import("@vibesdotdev/kernel").RuntimePlugin;
|
|
11
|
+
export default moderationPlugin;
|
|
12
|
+
//# sourceMappingURL=moderation.plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderation.plugin.d.ts","sourceRoot":"","sources":["../src/moderation.plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,eAAO,MAAM,gBAAgB,6CAO3B,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moderation Plugin
|
|
3
|
+
*
|
|
4
|
+
* Registers the `moderation/subject` kind and the owner-authored `docs/topic`
|
|
5
|
+
* descriptors. Features register their own subjects and batteries from their
|
|
6
|
+
* own plugins; this plugin registers no subject, no battery and no loader.
|
|
7
|
+
* The docs topics wait in the runtime's pending queue until `docs/topic`
|
|
8
|
+
* registers, so plugin order does not matter.
|
|
9
|
+
*/
|
|
10
|
+
import { createRuntimePlugin } from '@vibesdotdev/kernel';
|
|
11
|
+
import moderationDocs from "./docs/moderation.docs.descriptor.js";
|
|
12
|
+
import moderationDecisionsDocs from "./docs/moderation.decisions.docs.descriptor.js";
|
|
13
|
+
import moderationSubjectsDocs from "./docs/moderation.subjects.docs.descriptor.js";
|
|
14
|
+
import { moderationSubjectKind } from "./kinds/subject.kind.js";
|
|
15
|
+
export const moderationPlugin = createRuntimePlugin({
|
|
16
|
+
id: 'moderation',
|
|
17
|
+
name: 'Moderation',
|
|
18
|
+
description: 'Content moderation contract: the moderation/subject kind features register their content types against, plus the label and decision records moderateSubject produces.',
|
|
19
|
+
kinds: [moderationSubjectKind],
|
|
20
|
+
descriptors: [moderationDocs, moderationSubjectsDocs, moderationDecisionsDocs]
|
|
21
|
+
});
|
|
22
|
+
export default moderationPlugin;
|
|
23
|
+
//# sourceMappingURL=moderation.plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moderation.plugin.js","sourceRoot":"","sources":["../src/moderation.plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,cAAc,MAAM,sCAAsC,CAAC;AAClE,OAAO,uBAAuB,MAAM,gDAAgD,CAAC;AACrF,OAAO,sBAAsB,MAAM,+CAA+C,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;IACnD,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,WAAW,EACV,uKAAuK;IACxK,KAAK,EAAE,CAAC,qBAAqB,CAAC;IAC9B,WAAW,EAAE,CAAC,cAAc,EAAE,sBAAsB,EAAE,uBAAuB,CAAC;CAC9E,CAAC,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type EvaluationResponse } from '../schemas/evaluation.ts';
|
|
2
|
+
import type { ModerationSubjectDescriptor, ModerationSubjectRef } from '../schemas/subject.ts';
|
|
3
|
+
import type { ModerationLabel } from '../schemas/decision.ts';
|
|
4
|
+
export declare function readLabels(subject: ModerationSubjectDescriptor, ref: ModerationSubjectRef, response: EvaluationResponse): ModerationLabel[];
|
|
5
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/orchestration/labels.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8C,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC/G,OAAO,KAAK,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,UAAU,CACzB,OAAO,EAAE,2BAA2B,EACpC,GAAG,EAAE,oBAAoB,EACzB,QAAQ,EAAE,kBAAkB,GAC1B,eAAe,EAAE,CA4CnB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { ModerationError } from "../errors.js";
|
|
3
|
+
import { EvaluationResponseSchema, NoulAnswerSchema } from "../schemas/evaluation.js";
|
|
4
|
+
export function readLabels(subject, ref, response) {
|
|
5
|
+
const batteryId = subject.batteryId;
|
|
6
|
+
const parsed = EvaluationResponseSchema.safeParse(response);
|
|
7
|
+
if (!parsed.success) {
|
|
8
|
+
throw new ModerationError('MODERATION_EVALUATION_INVALID', `battery '${batteryId}' response is not an evaluation response: ${z.prettifyError(parsed.error)}`);
|
|
9
|
+
}
|
|
10
|
+
const { answers } = parsed.data;
|
|
11
|
+
const answerSource = parsed.data.source ?? 'native';
|
|
12
|
+
const labels = [];
|
|
13
|
+
for (const value of subject.labels) {
|
|
14
|
+
if (!Object.hasOwn(answers, value)) {
|
|
15
|
+
throw new ModerationError('MODERATION_EVALUATION_INVALID', `battery '${batteryId}' answer is missing head '${value}'`);
|
|
16
|
+
}
|
|
17
|
+
const answer = NoulAnswerSchema.safeParse(answers[value]);
|
|
18
|
+
if (!answer.success) {
|
|
19
|
+
throw new ModerationError('MODERATION_EVALUATION_INVALID', `battery '${batteryId}' answer for head '${value}' is not a noul in [0, 1]`);
|
|
20
|
+
}
|
|
21
|
+
labels.push({
|
|
22
|
+
subject: ref,
|
|
23
|
+
value,
|
|
24
|
+
score: answer.data.noul,
|
|
25
|
+
source: { batteryId, answerSource }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const undeclared = Object.keys(answers).filter((head) => !subject.labels.some((label) => label === head));
|
|
29
|
+
if (undeclared.length > 0) {
|
|
30
|
+
throw new ModerationError('MODERATION_EVALUATION_INVALID', `battery '${batteryId}' answered undeclared heads [${undeclared.join(', ')}]`);
|
|
31
|
+
}
|
|
32
|
+
return labels;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/orchestration/labels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAA2B,MAAM,0BAA0B,CAAC;AAI/G,MAAM,UAAU,UAAU,CACzB,OAAoC,EACpC,GAAyB,EACzB,QAA4B;IAE5B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,MAAM,GAAG,wBAAwB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,eAAe,CACxB,+BAA+B,EAC/B,YAAY,SAAS,6CAA6C,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACjG,CAAC;IACH,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAChC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC;IACpD,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,eAAe,CACxB,+BAA+B,EAC/B,YAAY,SAAS,6BAA6B,KAAK,GAAG,CAC1D,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,eAAe,CACxB,+BAA+B,EAC/B,YAAY,SAAS,sBAAsB,KAAK,2BAA2B,CAC3E,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC;YACX,OAAO,EAAE,GAAG;YACZ,KAAK;YACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;YACvB,MAAM,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE;SACnC,CAAC,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAC7C,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CACzD,CAAC;IACF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,eAAe,CACxB,+BAA+B,EAC/B,YAAY,SAAS,gCAAgC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC7E,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type VibesRuntime } from '@vibesdotdev/kernel';
|
|
2
|
+
import * as z from 'zod/v4';
|
|
3
|
+
import { type ModerationSubjectDescriptor } from '../schemas/subject.ts';
|
|
4
|
+
import { type ModerationState } from '../schemas/input.ts';
|
|
5
|
+
import { EvaluationBatterySchema, type ModerationEvaluator } from '../schemas/evaluation.ts';
|
|
6
|
+
export declare function resolveSubject(runtime: VibesRuntime, type: string): Promise<ModerationSubjectDescriptor>;
|
|
7
|
+
export declare function resolveBattery(runtime: VibesRuntime, subject: ModerationSubjectDescriptor): Promise<z.infer<typeof EvaluationBatterySchema>>;
|
|
8
|
+
export declare function assertBatteryContract(subject: ModerationSubjectDescriptor, battery: z.infer<typeof EvaluationBatterySchema>): string[];
|
|
9
|
+
export declare function assertState(batteryId: string, requiredState: readonly string[], state: ModerationState): void;
|
|
10
|
+
export declare function resolveEvaluator(runtime: VibesRuntime): Promise<ModerationEvaluator>;
|
|
11
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/orchestration/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEzE,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAG5B,OAAO,EAAqC,KAAK,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAC5G,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAA4B,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAMvH,wBAAsB,cAAc,CACnC,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,2BAA2B,CAAC,CAyBtC;AAED,wBAAsB,cAAc,CACnC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,2BAA2B,GAClC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC,CA0BlD;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,GAC9C,MAAM,EAAE,CA2BV;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAU7G;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6B1F"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { CapabilityError } from '@vibesdotdev/kernel';
|
|
2
|
+
import { getVibesClient } from '@vibesdotdev/kernel/client';
|
|
3
|
+
import * as z from 'zod/v4';
|
|
4
|
+
import { ModerationError } from "../errors.js";
|
|
5
|
+
import { MODERATION_SUBJECT_KIND_ID } from "../kinds/subject.kind.js";
|
|
6
|
+
import { ModerationSubjectDescriptorSchema } from "../schemas/subject.js";
|
|
7
|
+
import { EmptyStateValueSchema } from "../schemas/input.js";
|
|
8
|
+
import { EvaluationBatterySchema, EvaluatorAdmissionSchema } from "../schemas/evaluation.js";
|
|
9
|
+
const BATTERY_KIND_ID = 'ai/evaluation-schema';
|
|
10
|
+
function isEvaluator(client) {
|
|
11
|
+
return EvaluatorAdmissionSchema.safeParse(client).success;
|
|
12
|
+
}
|
|
13
|
+
export async function resolveSubject(runtime, type) {
|
|
14
|
+
if (!runtime.hasKind(MODERATION_SUBJECT_KIND_ID)) {
|
|
15
|
+
throw new ModerationError('MODERATION_SUBJECT_UNKNOWN', `no moderation/subject '${type}' is registered (kind not mounted; compose the moderation plugin)`);
|
|
16
|
+
}
|
|
17
|
+
const descriptors = await runtime.query(MODERATION_SUBJECT_KIND_ID).descriptorsAsync();
|
|
18
|
+
const found = descriptors.find((descriptor) => descriptor.id === type && descriptor.enabled !== false);
|
|
19
|
+
if (!found) {
|
|
20
|
+
throw new ModerationError('MODERATION_SUBJECT_UNKNOWN', `no enabled moderation/subject '${type}' is registered`);
|
|
21
|
+
}
|
|
22
|
+
const parsed = ModerationSubjectDescriptorSchema.safeParse(found);
|
|
23
|
+
if (!parsed.success) {
|
|
24
|
+
throw new ModerationError('MODERATION_SUBJECT_UNKNOWN', `moderation/subject '${type}' is registered but is not a valid subject: ${z.prettifyError(parsed.error)}`);
|
|
25
|
+
}
|
|
26
|
+
return parsed.data;
|
|
27
|
+
}
|
|
28
|
+
export async function resolveBattery(runtime, subject) {
|
|
29
|
+
const batteryId = subject.batteryId;
|
|
30
|
+
if (!runtime.hasKind(BATTERY_KIND_ID)) {
|
|
31
|
+
throw new ModerationError('MODERATION_BATTERY_UNKNOWN', `subject '${subject.id}' names battery '${batteryId}', but no ai/evaluation-schema kind is registered`);
|
|
32
|
+
}
|
|
33
|
+
const descriptors = await runtime.query(BATTERY_KIND_ID).descriptorsAsync();
|
|
34
|
+
const found = descriptors.find((descriptor) => descriptor.id === batteryId && descriptor.enabled !== false);
|
|
35
|
+
if (!found) {
|
|
36
|
+
throw new ModerationError('MODERATION_BATTERY_UNKNOWN', `subject '${subject.id}' names battery '${batteryId}', which is not registered or is disabled`);
|
|
37
|
+
}
|
|
38
|
+
const parsed = EvaluationBatterySchema.safeParse(found);
|
|
39
|
+
if (!parsed.success) {
|
|
40
|
+
throw new ModerationError('MODERATION_BATTERY_MISMATCH', `battery '${batteryId}' is not a readable evaluation battery: ${z.prettifyError(parsed.error)}`);
|
|
41
|
+
}
|
|
42
|
+
return parsed.data;
|
|
43
|
+
}
|
|
44
|
+
export function assertBatteryContract(subject, battery) {
|
|
45
|
+
const heads = Object.keys(battery.questions);
|
|
46
|
+
const missing = subject.labels.filter((label) => !heads.includes(label));
|
|
47
|
+
const extra = heads.filter((head) => !subject.labels.some((label) => label === head));
|
|
48
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
49
|
+
throw new ModerationError('MODERATION_BATTERY_MISMATCH', `battery '${battery.id}' heads [${heads.join(', ')}] must equal subject '${subject.id}' labels [${subject.labels.join(', ')}]` +
|
|
50
|
+
(missing.length > 0 ? `; missing [${missing.join(', ')}]` : '') +
|
|
51
|
+
(extra.length > 0 ? `; undeclared [${extra.join(', ')}]` : ''));
|
|
52
|
+
}
|
|
53
|
+
const notNoul = heads.filter((head) => battery.questions[head]?.type !== 'noul');
|
|
54
|
+
if (notNoul.length > 0) {
|
|
55
|
+
throw new ModerationError('MODERATION_BATTERY_MISMATCH', `battery '${battery.id}' heads must all be 'noul'; not noul: [${notNoul.join(', ')}]`);
|
|
56
|
+
}
|
|
57
|
+
const requiredState = battery.requiredState ?? [];
|
|
58
|
+
if (requiredState.length === 0) {
|
|
59
|
+
throw new ModerationError('MODERATION_BATTERY_MISMATCH', `battery '${battery.id}' must declare a non-empty requiredState so moderation can refuse empty input`);
|
|
60
|
+
}
|
|
61
|
+
return requiredState;
|
|
62
|
+
}
|
|
63
|
+
export function assertState(batteryId, requiredState, state) {
|
|
64
|
+
const empty = requiredState.filter((key) => EmptyStateValueSchema.safeParse(Object.hasOwn(state, key) ? state[key] : undefined).success);
|
|
65
|
+
if (empty.length > 0) {
|
|
66
|
+
throw new ModerationError('MODERATION_STATE_INVALID', `battery '${batteryId}' state is missing or empty for [${empty.join(', ')}]`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export async function resolveEvaluator(runtime) {
|
|
70
|
+
// Never let getVibesClient auto-register runtime/client on a runtime the
|
|
71
|
+
// host did not compose it into: that would mint a second client graph.
|
|
72
|
+
if (!runtime.hasKind('runtime/client')) {
|
|
73
|
+
throw new ModerationError('MODERATION_EVALUATOR_UNAVAILABLE', "runtime/client is not registered, so no 'ai' client can evaluate");
|
|
74
|
+
}
|
|
75
|
+
let client;
|
|
76
|
+
try {
|
|
77
|
+
client = await getVibesClient('ai', runtime);
|
|
78
|
+
}
|
|
79
|
+
catch (cause) {
|
|
80
|
+
if (cause instanceof Error && CapabilityError.isAbsenceOf(cause, 'runtime/client', 'ai')) {
|
|
81
|
+
throw new ModerationError('MODERATION_EVALUATOR_UNAVAILABLE', 'runtime/client ai did not resolve', { cause });
|
|
82
|
+
}
|
|
83
|
+
throw cause;
|
|
84
|
+
}
|
|
85
|
+
if (!isEvaluator(client)) {
|
|
86
|
+
throw new ModerationError('MODERATION_EVALUATOR_UNAVAILABLE', "runtime/client 'ai' has no evaluate function");
|
|
87
|
+
}
|
|
88
|
+
return client;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/orchestration/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ModerationState, type ModerationCallerState } from '../schemas/input.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Take the owned snapshot of the caller's state before the first await.
|
|
4
|
+
* Accessor properties and cycles are refused by path without being read;
|
|
5
|
+
* other non-JSON values fail the schema, whose error is the cause. A failure
|
|
6
|
+
* the caller's own object raises while it is read (a proxy trap, or nesting
|
|
7
|
+
* deeper than the call stack) propagates unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export declare function snapshotState(state: ModerationCallerState): ModerationState;
|
|
10
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/orchestration/state.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,qBAAqB,EAA6B,MAAM,qBAAqB,CAAC;AAgCzI;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,qBAAqB,GAAG,eAAe,CAa3E"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ModerationError } from "../errors.js";
|
|
2
|
+
import { ModerationStateSchema } from "../schemas/input.js";
|
|
3
|
+
const NOT_PLAIN_JSON = 'moderation state must be a plain JSON object';
|
|
4
|
+
/** Plain objects and arrays: the containers the schema reads as JSON. Anything else is a leaf it judges. */
|
|
5
|
+
function isJsonContainer(value) {
|
|
6
|
+
if (value === null || value === undefined)
|
|
7
|
+
return false;
|
|
8
|
+
if (Array.isArray(value))
|
|
9
|
+
return true;
|
|
10
|
+
const prototype = Object.getPrototypeOf(value);
|
|
11
|
+
return prototype === Object.prototype || prototype === null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Name the first place the schema could not read `value` as data without
|
|
15
|
+
* running caller code or recursing without end: an accessor property, or a
|
|
16
|
+
* container that encloses itself. Reads property descriptors only, so no
|
|
17
|
+
* getter runs. Shared, non-enclosing references are plain JSON and pass.
|
|
18
|
+
*/
|
|
19
|
+
function findNonData(value, path, enclosing) {
|
|
20
|
+
if (!isJsonContainer(value))
|
|
21
|
+
return null;
|
|
22
|
+
if (enclosing.has(value))
|
|
23
|
+
return `${path} refers back to an enclosing value`;
|
|
24
|
+
enclosing.add(value);
|
|
25
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
26
|
+
const at = `${path}.${String(key)}`;
|
|
27
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
28
|
+
if (descriptor !== undefined && !('value' in descriptor))
|
|
29
|
+
return `${at} is an accessor property`;
|
|
30
|
+
const found = findNonData(descriptor?.value, at, enclosing);
|
|
31
|
+
if (found !== null)
|
|
32
|
+
return found;
|
|
33
|
+
}
|
|
34
|
+
enclosing.delete(value);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Take the owned snapshot of the caller's state before the first await.
|
|
39
|
+
* Accessor properties and cycles are refused by path without being read;
|
|
40
|
+
* other non-JSON values fail the schema, whose error is the cause. A failure
|
|
41
|
+
* the caller's own object raises while it is read (a proxy trap, or nesting
|
|
42
|
+
* deeper than the call stack) propagates unchanged.
|
|
43
|
+
*/
|
|
44
|
+
export function snapshotState(state) {
|
|
45
|
+
if (Array.isArray(state) || !isJsonContainer(state)) {
|
|
46
|
+
throw new ModerationError('MODERATION_STATE_INVALID', NOT_PLAIN_JSON);
|
|
47
|
+
}
|
|
48
|
+
const nonData = findNonData(state, 'state', new Set());
|
|
49
|
+
if (nonData !== null) {
|
|
50
|
+
throw new ModerationError('MODERATION_STATE_INVALID', `${NOT_PLAIN_JSON}; ${nonData}`);
|
|
51
|
+
}
|
|
52
|
+
const parsed = ModerationStateSchema.safeParse(state);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
throw new ModerationError('MODERATION_STATE_INVALID', NOT_PLAIN_JSON, { cause: parsed.error });
|
|
55
|
+
}
|
|
56
|
+
return parsed.data;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/orchestration/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,qBAAqB,EAA+E,MAAM,qBAAqB,CAAC;AACzI,MAAM,cAAc,GAAG,8CAA8C,CAAC;AAEtE,4GAA4G;AAC5G,SAAS,eAAe,CAAC,KAA4B;IACpD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,SAAS,GAAY,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxD,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,KAA4B,EAAE,IAAY,EAAE,SAAsB;IACtF,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,IAAI,oCAAoC,CAAC;IAC7E,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/D,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,IAAI,UAAU,CAAC;YAAE,OAAO,GAAG,EAAE,0BAA0B,CAAC;QACjG,MAAM,KAAK,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;QAC5D,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;IAClC,CAAC;IACD,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAA4B;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,eAAe,CAAC,0BAA0B,EAAE,cAAc,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;IACvD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,IAAI,eAAe,CAAC,0BAA0B,EAAE,GAAG,cAAc,KAAK,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,eAAe,CAAC,0BAA0B,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
/**
|
|
3
|
+
* Where the scores came from. `native` answers are calibrated probabilities
|
|
4
|
+
* from a structured-evaluation model; `adapted` answers come from a JSON-mode
|
|
5
|
+
* chat model running the same battery and carry no calibration.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ModerationAnswerSourceSchema: z.ZodEnum<{
|
|
8
|
+
native: "native";
|
|
9
|
+
adapted: "adapted";
|
|
10
|
+
}>;
|
|
11
|
+
export type ModerationAnswerSource = z.infer<typeof ModerationAnswerSourceSchema>;
|
|
12
|
+
/** One label score for one subject, with the battery and answer source that produced it. */
|
|
13
|
+
export declare const ModerationLabelSchema: z.ZodObject<{
|
|
14
|
+
subject: z.ZodObject<{
|
|
15
|
+
type: z.ZodString;
|
|
16
|
+
id: z.ZodString;
|
|
17
|
+
containerId: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
value: z.ZodEnum<{
|
|
20
|
+
harassment: "harassment";
|
|
21
|
+
hate: "hate";
|
|
22
|
+
sexual: "sexual";
|
|
23
|
+
sexual_minors: "sexual_minors";
|
|
24
|
+
violence: "violence";
|
|
25
|
+
self_harm: "self_harm";
|
|
26
|
+
spam: "spam";
|
|
27
|
+
scam: "scam";
|
|
28
|
+
}>;
|
|
29
|
+
score: z.ZodNumber;
|
|
30
|
+
source: z.ZodObject<{
|
|
31
|
+
batteryId: z.ZodString;
|
|
32
|
+
answerSource: z.ZodEnum<{
|
|
33
|
+
native: "native";
|
|
34
|
+
adapted: "adapted";
|
|
35
|
+
}>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export type ModerationLabel = z.infer<typeof ModerationLabelSchema>;
|
|
39
|
+
export declare const ModerationActionSchema: z.ZodEnum<{
|
|
40
|
+
allow: "allow";
|
|
41
|
+
review: "review";
|
|
42
|
+
hide: "hide";
|
|
43
|
+
}>;
|
|
44
|
+
export type ModerationAction = z.infer<typeof ModerationActionSchema>;
|
|
45
|
+
/** A label whose score crossed a threshold, and which threshold it crossed. */
|
|
46
|
+
export declare const ModerationTriggerSchema: z.ZodObject<{
|
|
47
|
+
label: z.ZodEnum<{
|
|
48
|
+
harassment: "harassment";
|
|
49
|
+
hate: "hate";
|
|
50
|
+
sexual: "sexual";
|
|
51
|
+
sexual_minors: "sexual_minors";
|
|
52
|
+
violence: "violence";
|
|
53
|
+
self_harm: "self_harm";
|
|
54
|
+
spam: "spam";
|
|
55
|
+
scam: "scam";
|
|
56
|
+
}>;
|
|
57
|
+
score: z.ZodNumber;
|
|
58
|
+
threshold: z.ZodNumber;
|
|
59
|
+
level: z.ZodEnum<{
|
|
60
|
+
review: "review";
|
|
61
|
+
hide: "hide";
|
|
62
|
+
}>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
export type ModerationTrigger = z.infer<typeof ModerationTriggerSchema>;
|
|
65
|
+
/**
|
|
66
|
+
* The outcome of moderating one subject. The refinement keeps the action,
|
|
67
|
+
* the triggers and the labels mutually consistent, so a decision that says
|
|
68
|
+
* `allow` while carrying a hide trigger cannot be constructed.
|
|
69
|
+
*/
|
|
70
|
+
export declare const ModerationDecisionSchema: z.ZodObject<{
|
|
71
|
+
subject: z.ZodObject<{
|
|
72
|
+
type: z.ZodString;
|
|
73
|
+
id: z.ZodString;
|
|
74
|
+
containerId: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
action: z.ZodEnum<{
|
|
77
|
+
allow: "allow";
|
|
78
|
+
review: "review";
|
|
79
|
+
hide: "hide";
|
|
80
|
+
}>;
|
|
81
|
+
answerSource: z.ZodEnum<{
|
|
82
|
+
native: "native";
|
|
83
|
+
adapted: "adapted";
|
|
84
|
+
}>;
|
|
85
|
+
labels: z.ZodArray<z.ZodObject<{
|
|
86
|
+
subject: z.ZodObject<{
|
|
87
|
+
type: z.ZodString;
|
|
88
|
+
id: z.ZodString;
|
|
89
|
+
containerId: z.ZodOptional<z.ZodString>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
value: z.ZodEnum<{
|
|
92
|
+
harassment: "harassment";
|
|
93
|
+
hate: "hate";
|
|
94
|
+
sexual: "sexual";
|
|
95
|
+
sexual_minors: "sexual_minors";
|
|
96
|
+
violence: "violence";
|
|
97
|
+
self_harm: "self_harm";
|
|
98
|
+
spam: "spam";
|
|
99
|
+
scam: "scam";
|
|
100
|
+
}>;
|
|
101
|
+
score: z.ZodNumber;
|
|
102
|
+
source: z.ZodObject<{
|
|
103
|
+
batteryId: z.ZodString;
|
|
104
|
+
answerSource: z.ZodEnum<{
|
|
105
|
+
native: "native";
|
|
106
|
+
adapted: "adapted";
|
|
107
|
+
}>;
|
|
108
|
+
}, z.core.$strip>;
|
|
109
|
+
}, z.core.$strip>>;
|
|
110
|
+
triggers: z.ZodArray<z.ZodObject<{
|
|
111
|
+
label: z.ZodEnum<{
|
|
112
|
+
harassment: "harassment";
|
|
113
|
+
hate: "hate";
|
|
114
|
+
sexual: "sexual";
|
|
115
|
+
sexual_minors: "sexual_minors";
|
|
116
|
+
violence: "violence";
|
|
117
|
+
self_harm: "self_harm";
|
|
118
|
+
spam: "spam";
|
|
119
|
+
scam: "scam";
|
|
120
|
+
}>;
|
|
121
|
+
score: z.ZodNumber;
|
|
122
|
+
threshold: z.ZodNumber;
|
|
123
|
+
level: z.ZodEnum<{
|
|
124
|
+
review: "review";
|
|
125
|
+
hide: "hide";
|
|
126
|
+
}>;
|
|
127
|
+
}, z.core.$strip>>;
|
|
128
|
+
decidedAt: z.ZodISODateTime;
|
|
129
|
+
}, z.core.$strip>;
|
|
130
|
+
export type ModerationDecision = z.infer<typeof ModerationDecisionSchema>;
|
|
131
|
+
//# sourceMappingURL=decision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../src/schemas/decision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAI5B;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;;EAAgC,CAAC;AAE1E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;iBAQhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,sBAAsB;;;;EAAsC,CAAC;AAE1E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;iBAKlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0DlC,CAAC;AAEJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|