diff --git a/js/app/store/slices/contentMetadataSlice.ts b/js/app/store/slices/contentMetadataSlice.ts index f0dbf54e..297ca759 100644 --- a/js/app/store/slices/contentMetadataSlice.ts +++ b/js/app/store/slices/contentMetadataSlice.ts @@ -14,7 +14,7 @@ export interface ContentMetadataSlice { // actions createContentMetadata: (params: { contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; + distributionPolicy?: { deleteAfter?: number; allowGenAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -27,7 +27,7 @@ export interface ContentMetadataSlice { rkey?: string; livestreamRef?: { uri: string; cid: string }; contentWarnings?: string[]; - distributionPolicy?: { deleteAfter?: number; allowAiTraining?: boolean }; + distributionPolicy?: { deleteAfter?: number; allowGenAiTraining?: boolean }; contentRights?: { creator?: string; copyrightNotice?: string; @@ -81,7 +81,7 @@ export const createContentMetadataSlice: StateCreator< contentWarnings: { warnings: contentWarnings }, }), ...((distributionPolicy.deleteAfter || - distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy.allowGenAiTraining !== undefined) && { distributionPolicy, }), ...(contentRights && @@ -146,7 +146,7 @@ export const createContentMetadataSlice: StateCreator< contentWarnings: { warnings: contentWarnings }, }), ...((distributionPolicy.deleteAfter || - distributionPolicy.allowAiTraining !== undefined) && { + distributionPolicy.allowGenAiTraining !== undefined) && { distributionPolicy, }), ...(contentRights && diff --git a/js/components/src/components/content-metadata/content-metadata-form.tsx b/js/components/src/components/content-metadata/content-metadata-form.tsx index f4d19a24..26a3e3dc 100644 --- a/js/components/src/components/content-metadata/content-metadata-form.tsx +++ b/js/components/src/components/content-metadata/content-metadata-form.tsx @@ -198,18 +198,18 @@ export const ContentMetadataForm = forwardRef( ({ deleteAfter, allowedBroadcasters, - allowAiTraining, + allowGenAiTraining, }: { deleteAfter?: string; allowedBroadcasters?: string; - allowAiTraining?: boolean; + allowGenAiTraining?: boolean; }) => { let newDistributionPolicy: place.stream.metadata.distributionPolicy.Main = { ...distributionPolicy, }; - if (typeof allowAiTraining === "boolean") { - newDistributionPolicy.allowAiTraining = allowAiTraining; + if (typeof allowGenAiTraining === "boolean") { + newDistributionPolicy.allowGenAiTraining = allowGenAiTraining; } if (typeof deleteAfter === "string") { let duration = parseInt(deleteAfter, 10); @@ -312,7 +312,7 @@ export const ContentMetadataForm = forwardRef( // AI training is opt-in: absent an explicit choice, save the record // with training disallowed. metadata.distributionPolicy = { - allowAiTraining: false, + allowGenAiTraining: false, ...distributionPolicy, }; @@ -774,10 +774,10 @@ export const ContentMetadataForm = forwardRef( position="top" > handleDistributionPolicyChange({ - allowAiTraining: checked, + allowGenAiTraining: checked, }) } label={ diff --git a/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md b/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md index 908471d6..ed55ad6c 100644 --- a/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md +++ b/js/docs/src/content/docs/lex-reference/metadata/place-stream-metadata-distributionpolicy.md @@ -17,11 +17,11 @@ Distribution and rebroadcast policy. **Properties:** -| Name | Type | Req'd | Description | Constraints | -| --------------------- | ----------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| `deleteAfter` | `integer` | ❌ | Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival. | | -| `allowedBroadcasters` | Array of `string` | ❌ | List of did:webs of the broadcasters you want to allow to distribute your content. "\*" allows anyone. Starting a line with a "!" bans that broadcaster. | | -| `allowAiTraining` | `boolean` | ❌ | Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. | | +| Name | Type | Req'd | Description | Constraints | +| --------------------- | ----------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `deleteAfter` | `integer` | ❌ | Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival. | | +| `allowedBroadcasters` | Array of `string` | ❌ | List of did:webs of the broadcasters you want to allow to distribute your content. "\*" allows anyone. Starting a line with a "!" bans that broadcaster. | | +| `allowGenAiTraining` | `boolean` | ❌ | Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag. | | --- @@ -47,9 +47,9 @@ Distribution and rebroadcast policy. "type": "string" } }, - "allowAiTraining": { + "allowGenAiTraining": { "type": "boolean", - "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment." + "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag." } } } diff --git a/lexicons/place/stream/metadata/distributionPolicy.json b/lexicons/place/stream/metadata/distributionPolicy.json index eec3c1e4..9965e671 100644 --- a/lexicons/place/stream/metadata/distributionPolicy.json +++ b/lexicons/place/stream/metadata/distributionPolicy.json @@ -17,9 +17,9 @@ "type": "string" } }, - "allowAiTraining": { + "allowGenAiTraining": { "type": "boolean", - "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment." + "description": "Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag." } } } diff --git a/pkg/localdb/segment.go b/pkg/localdb/segment.go index 2e137ae5..30c0aad9 100644 --- a/pkg/localdb/segment.go +++ b/pkg/localdb/segment.go @@ -86,7 +86,7 @@ func (c ContentRights) Value() (driver.Value, error) { // DistributionPolicy represents distribution policy information type DistributionPolicy struct { DeleteAfterSeconds *int64 `json:"deleteAfterSeconds,omitempty"` - AllowAiTraining *bool `json:"allowAiTraining,omitempty"` + AllowGenAiTraining *bool `json:"allowGenAiTraining,omitempty"` } // Scan scan value into DistributionPolicy, implements sql.Scanner interface @@ -185,10 +185,10 @@ func (s *Segment) ToStreamplaceSegment() (*placestream.Segment, error) { } var distributionPolicy *placestream.MetadataDistributionPolicy - if s.DistributionPolicy != nil && (s.DistributionPolicy.DeleteAfterSeconds != nil || s.DistributionPolicy.AllowAiTraining != nil) { + if s.DistributionPolicy != nil && (s.DistributionPolicy.DeleteAfterSeconds != nil || s.DistributionPolicy.AllowGenAiTraining != nil) { distributionPolicy = &placestream.MetadataDistributionPolicy{ - DeleteAfter: s.DistributionPolicy.DeleteAfterSeconds, - AllowAiTraining: s.DistributionPolicy.AllowAiTraining, + DeleteAfter: s.DistributionPolicy.DeleteAfterSeconds, + AllowGenAiTraining: s.DistributionPolicy.AllowGenAiTraining, } } diff --git a/pkg/media/distribution_policy_test.go b/pkg/media/distribution_policy_test.go index f1efefc5..3d195c44 100644 --- a/pkg/media/distribution_policy_test.go +++ b/pkg/media/distribution_policy_test.go @@ -23,38 +23,38 @@ func manifestWithDistributionPolicy(policy map[string]any) *c2patypes.Manifest { } } -// extractDistributionPolicy carries allowAiTraining out of the manifest, alone +// extractDistributionPolicy carries allowGenAiTraining out of the manifest, alone // or alongside deleteAfter, and stays nil when neither is declared. -func TestExtractDistributionPolicyAllowAiTraining(t *testing.T) { +func TestExtractDistributionPolicyAllowGenAiTraining(t *testing.T) { now := aqtime.FromTime(time.Now()) policy := extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ - "allowAiTraining": false, + "allowGenAiTraining": false, }), now) require.NotNil(t, policy) - require.NotNil(t, policy.AllowAiTraining) - require.False(t, *policy.AllowAiTraining) + require.NotNil(t, policy.AllowGenAiTraining) + require.False(t, *policy.AllowGenAiTraining) require.Nil(t, policy.DeleteAfterSeconds) policy = extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ - "allowAiTraining": true, - "deleteAfter": 300, + "allowGenAiTraining": true, + "deleteAfter": 300, }), now) require.NotNil(t, policy) - require.NotNil(t, policy.AllowAiTraining) - require.True(t, *policy.AllowAiTraining) + require.NotNil(t, policy.AllowGenAiTraining) + require.True(t, *policy.AllowGenAiTraining) require.NotNil(t, policy.DeleteAfterSeconds) require.Equal(t, int64(300), *policy.DeleteAfterSeconds) policy = extractDistributionPolicy(manifestWithDistributionPolicy(map[string]any{ "allowedBroadcasters": []string{"*"}, }), now) - require.Nil(t, policy, "a policy with neither deleteAfter nor allowAiTraining stays nil") + require.Nil(t, policy, "a policy with neither deleteAfter nor allowGenAiTraining stays nil") } -// The minted place.stream.segment record carries allowAiTraining even when +// The minted place.stream.segment record carries allowGenAiTraining even when // deleteAfter is unset. -func TestSegmentRecordCarriesAllowAiTraining(t *testing.T) { +func TestSegmentRecordCarriesAllowGenAiTraining(t *testing.T) { notAllowed := false seg := &localdb.Segment{ ID: "test-segment", @@ -65,13 +65,13 @@ func TestSegmentRecordCarriesAllowAiTraining(t *testing.T) { Audio: []*localdb.SegmentMediadataAudio{{Rate: 48000, Channels: 2}}, }, DistributionPolicy: &localdb.DistributionPolicy{ - AllowAiTraining: ¬Allowed, + AllowGenAiTraining: ¬Allowed, }, } record, err := seg.ToStreamplaceSegment() require.NoError(t, err) require.NotNil(t, record.DistributionPolicy) - require.NotNil(t, record.DistributionPolicy.AllowAiTraining) - require.False(t, *record.DistributionPolicy.AllowAiTraining) + require.NotNil(t, record.DistributionPolicy.AllowGenAiTraining) + require.False(t, *record.DistributionPolicy.AllowGenAiTraining) require.Nil(t, record.DistributionPolicy.DeleteAfter) } diff --git a/pkg/media/manifest_builder.go b/pkg/media/manifest_builder.go index 26662659..1463d9b7 100644 --- a/pkg/media/manifest_builder.go +++ b/pkg/media/manifest_builder.go @@ -159,9 +159,9 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin if streamplaceMetadata.DistributionPolicy == nil { streamplaceMetadata.DistributionPolicy = &placestream.MetadataDistributionPolicy{} } - if streamplaceMetadata.DistributionPolicy.AllowAiTraining == nil { + if streamplaceMetadata.DistributionPolicy.AllowGenAiTraining == nil { notAllowed := false - streamplaceMetadata.DistributionPolicy.AllowAiTraining = ¬Allowed + streamplaceMetadata.DistributionPolicy.AllowGenAiTraining = ¬Allowed } metadataObj, err := toObj(streamplaceMetadata) if err != nil { @@ -172,7 +172,7 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin "label": "place.stream.metadata.configuration", "data": metadataObj, }, - trainingMiningAssertion(*streamplaceMetadata.DistributionPolicy.AllowAiTraining), + trainingMiningAssertion(*streamplaceMetadata.DistributionPolicy.AllowGenAiTraining), ) // Update the manifest title with the retrieved livestream title @@ -199,9 +199,9 @@ func (mb *ManifestBuilder) BuildManifest(ctx context.Context, streamerName strin // trainingMiningAssertion builds the CAWG training and data mining assertion // (https://cawg.io/training-and-data-mining/1.1/), the standard machine-readable // way to declare whether content may be used to train generative AI models. -func trainingMiningAssertion(allowAiTraining bool) obj { +func trainingMiningAssertion(allowGenAiTraining bool) obj { use := "notAllowed" - if allowAiTraining { + if allowGenAiTraining { use = "allowed" } return obj{ diff --git a/pkg/media/manifest_builder_test.go b/pkg/media/manifest_builder_test.go index b423eb14..57d8f803 100644 --- a/pkg/media/manifest_builder_test.go +++ b/pkg/media/manifest_builder_test.go @@ -57,7 +57,7 @@ func buildManifestAssertions(t *testing.T, metadata *model.MetadataConfiguration return out } -func aiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { +func genAiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { data, ok := assertions["cawg.training-mining"] require.True(t, ok, "manifest must carry the cawg.training-mining assertion") entries, ok := data["entries"].(map[string]any) @@ -69,20 +69,20 @@ func aiTrainingUse(t *testing.T, assertions map[string]map[string]any) string { return use } -func allowAiTrainingField(t *testing.T, assertions map[string]map[string]any) any { +func allowGenAiTrainingField(t *testing.T, assertions map[string]map[string]any) any { data, ok := assertions["place.stream.metadata.configuration"] require.True(t, ok, "manifest must carry the metadata configuration assertion") policy, ok := data["distributionPolicy"].(map[string]any) require.True(t, ok, "metadata configuration must carry a distributionPolicy") - return policy["allowAiTraining"] + return policy["allowGenAiTraining"] } // A streamer with no metadata configuration at all still gets an explicit // "AI training not allowed" stamped into every minted segment. func TestBuildManifestAiTrainingDefaultDeny(t *testing.T) { assertions := buildManifestAssertions(t, nil) - require.Equal(t, "notAllowed", aiTrainingUse(t, assertions)) - require.Equal(t, false, allowAiTrainingField(t, assertions)) + require.Equal(t, "notAllowed", genAiTrainingUse(t, assertions)) + require.Equal(t, false, allowGenAiTrainingField(t, assertions)) } // A metadata configuration that never mentions AI training also defaults to @@ -95,8 +95,8 @@ func TestBuildManifestAiTrainingUndeclaredDeny(t *testing.T) { }, }) assertions := buildManifestAssertions(t, row) - require.Equal(t, "notAllowed", aiTrainingUse(t, assertions)) - require.Equal(t, false, allowAiTrainingField(t, assertions)) + require.Equal(t, "notAllowed", genAiTrainingUse(t, assertions)) + require.Equal(t, false, allowGenAiTrainingField(t, assertions)) // the rest of the distribution policy survives the stamping policy := assertions["place.stream.metadata.configuration"]["distributionPolicy"].(map[string]any) @@ -108,10 +108,10 @@ func TestBuildManifestAiTrainingExplicitAllow(t *testing.T) { allow := true row := metadataConfigurationRow(t, &placestream.MetadataConfiguration{ DistributionPolicy: &placestream.MetadataDistributionPolicy{ - AllowAiTraining: &allow, + AllowGenAiTraining: &allow, }, }) assertions := buildManifestAssertions(t, row) - require.Equal(t, "allowed", aiTrainingUse(t, assertions)) - require.Equal(t, true, allowAiTrainingField(t, assertions)) + require.Equal(t, "allowed", genAiTrainingUse(t, assertions)) + require.Equal(t, true, allowGenAiTrainingField(t, assertions)) } diff --git a/pkg/media/media.go b/pkg/media/media.go index 8893f0b1..81096c8a 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -428,7 +428,7 @@ func extractDistributionPolicy(mani *c2patypes.Manifest, segmentStart aqtime.AQT } policy := &localdb.DistributionPolicy{ - AllowAiTraining: metadataConfig.DistributionPolicy.AllowAiTraining, + AllowGenAiTraining: metadataConfig.DistributionPolicy.AllowGenAiTraining, } if metadataConfig.DistributionPolicy.DeleteAfter != nil { @@ -437,7 +437,7 @@ func extractDistributionPolicy(mani *c2patypes.Manifest, segmentStart aqtime.AQT policy.DeleteAfterSeconds = &deleteAfterSeconds } - if policy.DeleteAfterSeconds == nil && policy.AllowAiTraining == nil { + if policy.DeleteAfterSeconds == nil && policy.AllowGenAiTraining == nil { return nil } diff --git a/pkg/placestream/metadatadistributionpolicy.go b/pkg/placestream/metadatadistributionpolicy.go index 4598ab67..6449793f 100644 --- a/pkg/placestream/metadatadistributionpolicy.go +++ b/pkg/placestream/metadatadistributionpolicy.go @@ -19,8 +19,8 @@ func init() { // Distribution and rebroadcast policy. type MetadataDistributionPolicy struct { LexiconTypeID string `json:"$type,omitempty"` - // allowAiTraining: Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. - AllowAiTraining *bool `json:"allowAiTraining,omitempty"` + // allowGenAiTraining: Whether this content may be used as training data for generative AI models. Absent means undeclared; Streamplace treats undeclared as false and stamps an explicit value into every minted segment. Covers generative AI only — non-generative machine learning (e.g. moderation, accessibility, speech models) is not governed by this flag. + AllowGenAiTraining *bool `json:"allowGenAiTraining,omitempty"` // allowedBroadcasters: List of did:webs of the broadcasters you want to allow to distribute your content. "*" allows anyone. Starting a line with a "!" bans that broadcaster. AllowedBroadcasters []string `json:"allowedBroadcasters,omitempty"` // deleteAfter: Duration in seconds after which segments should be deleted. Each segment will expire N seconds after its creation time. -1 to allow indefinite archival.