interscript 3.2.0 → 3.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '047058523934ce18616d22f657269aa926232df2739ddfa87577c0753d6921dd'
4
- data.tar.gz: 7ae9aad06aac1f0c87a8581c58aedc229674a5e0113516a4532b665a7b3f69e4
3
+ metadata.gz: d563d6c5e8b2552d43b377d7c0637bc178a03bfa48ab8296c13de68269264266
4
+ data.tar.gz: f235d6abf2393cd6b7af75eb78960466f915f81a4d873c4db920ffe97a226bbd
5
5
  SHA512:
6
- metadata.gz: 35b234e18f152088a43e072f428c563564ff6b594bf7bea471f07f761f936d206b9efaa4ba885180c0bf92010a3521fe5f0e6e5673825e35d225ff1611814805
7
- data.tar.gz: 776266ef1babe566d4d95a16b637d26923ea1b1458738edcb3b5840d63aeed86b25af14203bb8d05c04733812b1e297f4c10c0f9fbbce1c2c08569ba0818f21a
6
+ metadata.gz: 494749273ef3a9d265c0ab4d5c622024f7cac93125a0a13c3f5773b24648d1a195c550371d2379e8ec2a96dbf63d9217dad24b24497df72f1afa6dfb4467c29f
7
+ data.tar.gz: 630e78cfd16a814c80b3a53441cfb48b7d82094c32be909e683727cfa4607a885455018be87bf3a8fa48c9efea7a4cd228612b8450c9f6d22bdb93e5fa9a7f8a
data/README.md CHANGED
@@ -1,18 +1,91 @@
1
1
  image:https://github.com/interscript/interscript-ruby/actions/workflows/rake.yml/badge.svg["CI status", link="https://github.com/interscript/interscript-ruby/actions/workflows/rake.yml"]
2
- # Interscript
3
2
 
4
- Documentation is available directory higher in file README.adoc.
3
+ # Interscript (Ruby)
5
4
 
6
- ## Phonological layer (optional)
5
+ The Ruby runtime for https://www.interscript.org[Interscript] —
6
+ interoperable script conversion systems. Interscript turns the world's
7
+ transliteration standards from paper tables into executable, verifiable
8
+ software: authority schemes (BGN/PCGN, ISO, UN/UNGEGN, ALA-LC, ICAO,
9
+ ODNI, ...) are encoded once in the ISC language, every runtime produces
10
+ provably identical output, and neural models ship under the same proof
11
+ regime — pinned artifacts, sha256 verification, frozen conformance
12
+ vectors.
13
+
14
+ == Install
15
+
16
+ [source,sh]
17
+ ----
18
+ gem install interscript
19
+ ----
20
+
21
+ == Transliterate
22
+
23
+ [source,sh]
24
+ ----
25
+ interscript -s odni-ara-Arab-Latn-2015 ara-Arab.txt -o out.txt
26
+ ----
27
+
28
+ [source,ruby]
29
+ ----
30
+ require "interscript"
31
+
32
+ Interscript.transliterate("odni-rus-Cyrl-Latn-2015", "привет")
33
+ # => "privet"
34
+ ----
35
+
36
+ == Neural models (Interscript::ML)
37
+
38
+ The gem embeds the IMF v1 model runtime — resolve a model id through
39
+ the interscript-models index, verify its sha256, and run ONNX
40
+ inference locally:
41
+
42
+ [source,ruby]
43
+ ----
44
+ require "interscript/ml"
45
+
46
+ Interscript::ML::Translator.new(model_file: "urd-diac-base-1.0")
47
+ .translate("...")
48
+ ----
49
+
50
+ Runtime dependencies (onnxruntime, rubyzip, numo-narray) are optional
51
+ and load lazily; a missing gem raises a `LoadError` naming it. The
52
+ artifact contract and frozen conformance vectors live in
53
+ https://github.com/interscript/interscript-models[interscript-models]
54
+ (see its `conformance/CONFORMANCE.md`).
55
+
56
+ == The phonological layer
7
57
 
8
58
  Some transliteration systems need their input vocalized before the map
9
59
  can apply (undiacritized Arabic, nikud-less Hebrew, unsegmented Thai).
10
60
  The stdlib ships adapters for this: the `secryst` function dispatches
11
- to a [secryst crystal](https://www.secryst.org) —
12
- `gem 'secryst'` (Ruby), `pip install secryst`, or `npm i secryst` —
61
+ to a crystal runtime — `pip install secryst`, or `npm i secryst` —
13
62
  which resolves a model id through the
14
- [interscript-ml `models.yaml` index](https://github.com/interscript/interscript-ml)
15
- and runs sha256-verified ONNX inference locally. The gem has no
16
- interscript dependency: a crystal can be used standalone for any G2P
17
- need, and an engine without a crystal simply cannot execute maps that
18
- declare a vocalization step.
63
+ https://github.com/interscript/interscript-models[models.yaml index]
64
+ and runs sha256-verified ONNX inference locally. An engine without a
65
+ crystal simply cannot execute maps that declare a vocalization step.
66
+
67
+ == Development
68
+
69
+ [source,sh]
70
+ ----
71
+ bundle install
72
+ bundle exec rake # spec suite
73
+ ----
74
+
75
+ `SKIP_JS=1 SKIP_PYTHON=1` exclude the cross-compiler *execution* specs
76
+ (they need the monorepo's sibling js assets); that coverage is owned by
77
+ the CI matrix that runs on every push.
78
+
79
+ == Release
80
+
81
+ Push a `v*` tag: the release workflow runs the spec suite, builds the
82
+ gem, and publishes via RubyGems.org trusted publishing (OIDC). The
83
+ `interscript-maps` dependency is pinned to the published lineage
84
+ (`~> 2.5`), not derived from the gem version.
85
+
86
+ == Project
87
+
88
+ Purpose, architecture, and direction: see the
89
+ https://github.com/interscript/interscript-models#direction-audit-2026-10[project
90
+ direction] and https://github.com/interscript/interscript[the project
91
+ monorepo].
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Interscript
4
+ # A3 (WO59): mechanical equivalence checking between two systems.
5
+ #
6
+ # Probes a sample set through both systems and reports the agreement
7
+ # ratio plus the minimal distinguishing samples. The sample set is
8
+ # caller-supplied or derived from the systems' own embedded tests.
9
+ class Equivalence
10
+ def self.check(first, second, samples:)
11
+ new(first, second).check(samples)
12
+ end
13
+
14
+ # Embedded test inputs of the given systems (the natural probe set
15
+ # for near-equivalent authorities).
16
+ def self.samples_from_tests(*codes)
17
+ codes.flat_map do |code|
18
+ Array(Interscript.parse(code).tests&.data).map(&:first).compact
19
+ end.uniq
20
+ end
21
+
22
+ def initialize(first, second)
23
+ @first = first
24
+ @second = second
25
+ end
26
+
27
+ def check(samples)
28
+ distinguishing = []
29
+ tested = 0
30
+ matched = 0
31
+ samples.each do |sample|
32
+ tested += 1
33
+ a = Interscript.transliterate(@first, sample)
34
+ b = Interscript.transliterate(@second, sample)
35
+ if a == b
36
+ matched += 1
37
+ else
38
+ distinguishing << {sample: sample, first: a, second: b}
39
+ end
40
+ end
41
+ ratio = tested.zero? ? 1.0 : matched.fdiv(tested)
42
+ {tested: tested, matched: matched, ratio: ratio,
43
+ distinguishing: distinguishing}
44
+ end
45
+ end
46
+ end
@@ -18,7 +18,7 @@ module Interscript::ML
18
18
  @pasts = @kv ? zero_pasts : {}
19
19
  end
20
20
 
21
- def translate(text, max_seq_length: 256)
21
+ def translate(text, max_seq_length: 512)
22
22
  ids = IMF.encode(text)
23
23
  return "" if ids.length == 1 # only the trailing EOS: empty input
24
24
 
@@ -38,7 +38,9 @@ module Interscript::ML
38
38
  def build_session(graphs, name)
39
39
  path = File.join(@tmpdir, name)
40
40
  File.binwrite(path, graphs.fetch(name))
41
- OnnxRuntime::Model.new(path)
41
+ # single-thread decode: the conformance contract is byte-exact
42
+ # across environments (WO79); multi-thread fp16 ties flip rows
43
+ OnnxRuntime::Model.new(path, intra_op_num_threads: 1)
42
44
  end
43
45
 
44
46
  def zero_pasts
@@ -42,7 +42,10 @@ module Interscript::ML
42
42
  next if token == PAD_ID || token == UNK_ID
43
43
  out << ((token - BYTE_OFFSET) % 256).chr
44
44
  end
45
- out.force_encoding(Encoding::UTF_8)
45
+ # the python reference decodes with errors="replace": invalid
46
+ # bytes (mid-character sequence caps) become U+FFFD -- scrub to
47
+ # match byte-for-byte (the C1 matrix catch)
48
+ out.force_encoding(Encoding::UTF_8).scrub
46
49
  end
47
50
 
48
51
  def manifest(zip_path)
@@ -10,7 +10,7 @@ module Interscript::ML
10
10
  @model = Model.from_file(model_file)
11
11
  end
12
12
 
13
- def translate(phrase, max_seq_length: 100)
13
+ def translate(phrase, max_seq_length: 512)
14
14
  return @model.translate(phrase, max_seq_length: max_seq_length) if @model.is_a?(Byt5Onnx)
15
15
 
16
16
  Interscript::ML.require_optional!("numo-narray", feature: "numo/narray")
@@ -23,6 +23,17 @@ class Interscript::Stdlib
23
23
  end
24
24
  end
25
25
 
26
+ # The 126e8e1 split lost this body but kept the call — an ML
27
+ # funcall without a provisioned model crashed with an accidental
28
+ # NoMethodError instead of a named provisioning error (A4).
29
+ def require_secryst!
30
+ require "interscript/ml/translator"
31
+ rescue LoadError => e
32
+ raise LoadError,
33
+ "the secryst funcall requires Interscript::ML — " \
34
+ "loading failed: #{e.message}"
35
+ end
36
+
26
37
  def build_translator(model_key)
27
38
  Interscript.secryst_index_locations.each do |remote|
28
39
  Interscript::ML::Provisioning.add_remote(remote)
@@ -1,3 +1,3 @@
1
1
  module Interscript
2
- VERSION = "3.2.0"
2
+ VERSION = "3.2.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: interscript
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-19 00:00:00.000000000 Z
11
+ date: 2026-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -124,6 +124,7 @@ files:
124
124
  - lib/interscript/dsl/stage.rb
125
125
  - lib/interscript/dsl/symbol_mm.rb
126
126
  - lib/interscript/dsl/tests.rb
127
+ - lib/interscript/equivalence.rb
127
128
  - lib/interscript/interpreter.rb
128
129
  - lib/interscript/isc.rb
129
130
  - lib/interscript/isc/codemod.rb