Class: Vangrail::Rails::Watermark
- Inherits:
-
Vangrail::Rail
- Object
- Vangrail::Rail
- Vangrail::Rails::Watermark
- Defined in:
- lib/vangrail/rails/watermark.rb
Overview
Marks the answer as generated, in a format a machine can read.
The obligation is Article 50(2) of the AI Act: a provider of a generative system marks its output so it is detectable as artificially generated, and does so effectively, interoperably, robustly and reliably as far as technically feasible. Vangrail::Watermark carries the format and the argument for it; this is the rail that puts it on the output side of an engine, beside the rails that read the same text.
Vangrail::Engine.new(
output: [Vangrail::Rails::Watermark.new(key: ENV['ASK_WATERMARK_KEY'],
issuer: 'surf/handbook-ask')]
)
It runs last by convention, after the rails that might rewrite the answer: a redaction applied to marked text leaves a mark covering words that are no longer there, and the mark would then fail its own verification. Order the output list with this at the end and that cannot happen.
The result is :modified, because the rail did rewrite the text and saying
otherwise would make a rail lie about what it did. An application whose
interface flags edited answers to the reader should route on the category
rather than on the status: watermark is disclosure, not redaction.
With no key it still marks. The disclosure half of Article 50 is the magic bytes, which need no secret, and a rail that does nothing until somebody provisions a key is a rail that ships turned off. The key buys attribution on top: with one, a mark verifies as this issuer's and cannot be lifted off one answer onto another.
Instance Attribute Summary collapse
-
#issuer ⇒ Object
readonly
Returns the value of attribute issuer.
-
#key ⇒ Object
readonly
Returns the value of attribute key.
Instance Method Summary collapse
-
#cache_key(text, _context) ⇒ Object
Same text, same key, same mark, so a repeat costs one HMAC and no second run of selectors.
- #decide(text, _context) ⇒ Object
-
#incremental? ⇒ Boolean
Not mid-stream.
-
#initialize(key: nil, issuer: nil, name: 'watermark', sides: [:output]) ⇒ Watermark
constructor
A new instance of Watermark.
- #signed? ⇒ Boolean
Constructor Details
#initialize(key: nil, issuer: nil, name: 'watermark', sides: [:output]) ⇒ Watermark
Returns a new instance of Watermark.
40 41 42 43 44 |
# File 'lib/vangrail/rails/watermark.rb', line 40 def initialize(key: nil, issuer: nil, name: 'watermark', sides: [:output]) super(name: name, sides: sides) @key = key @issuer = issuer end |
Instance Attribute Details
#issuer ⇒ Object (readonly)
Returns the value of attribute issuer.
38 39 40 |
# File 'lib/vangrail/rails/watermark.rb', line 38 def issuer @issuer end |
#key ⇒ Object (readonly)
Returns the value of attribute key.
38 39 40 |
# File 'lib/vangrail/rails/watermark.rb', line 38 def key @key end |
Instance Method Details
#cache_key(text, _context) ⇒ Object
Same text, same key, same mark, so a repeat costs one HMAC and no second run of selectors.
60 61 62 |
# File 'lib/vangrail/rails/watermark.rb', line 60 def cache_key(text, _context) text end |
#decide(text, _context) ⇒ Object
64 65 66 67 68 69 |
# File 'lib/vangrail/rails/watermark.rb', line 64 def decide(text, _context) marked = Vangrail::Watermark.mark(text, key: key, issuer: issuer) return pass if marked == text modify(marked, categories: categories, reason: reason) end |
#incremental? ⇒ Boolean
Not mid-stream. The mark covers a finished paragraph, and a paragraph
that is still arriving would be marked once per chunk, each mark
covering different words and each one rewriting text the reader can
already see. StreamGuard marks at finish, where the answer is whole.
54 55 56 |
# File 'lib/vangrail/rails/watermark.rb', line 54 def incremental? false end |
#signed? ⇒ Boolean
46 47 48 |
# File 'lib/vangrail/rails/watermark.rb', line 46 def signed? !key.nil? && !key.to_s.empty? end |