internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: lhotse-speech/lhotse

Issue:
JSONDecodeError when running feat extract after doing kaldi import
Hi!
If I run `lhotse kaldi import <kaldidir> 16000 <newdir>` and then
`lhotse feat extract <newdir>/recordings.jsonl.gz <newdir>/feat` I get an error

```
[..]
  File "/home/rudolf/.local/lib/python3.8/site-packages/lhotse/bin/modes/features.py", line 86, in extract
    recordings: RecordingSet = RecordingSet.from_json(recording_manifest)
[..]
  File "/usr/lib/python3.8/json/decoder.py", line 340, in decode
    raise JSONDecodeError("Extra data", s, end)
json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 285)
```

There's nothing wrong with the kaldi dir (it was fixed and I've validated it). This is lhotse 1.2, I tried out both `pip install lhotse` and `pip install lhotse[orjson]`.

This is what the first 3 lines of `zcat` look like:
```
{"id": "5ccae615b4e948578998a20f", "sources": [{"type": "file", "channels": [0], "source": "/path/to/wav/5ccae615b4e948578998a20f-wav.wav"}], "sampling_rate": 16000, "num_samples": 24992427, "duration": 1562.0266875}
{"id": "5ccae699b4e948578998a211", "sources": [{"type": "file", "channels": [0], "source": "/path/to/wav/wav/5ccae699b4e948578998a211-wav.wav"}], "sampling_rate": 16000, "num_samples": 25830741, "duration": 1614.4213125}
{"id": "5ccae7b2b4e948578998a215", "sources": [{"type": "file", "channels": [0], "source": "/path/to/wav/5ccae7b2b4e948578998a215-wav.wav"}], "sampling_rate": 16000, "num_samples": 11936427, "duration": 746.0266875}
```

Any ideas?

edit: Seems it's just loading it as a json when it should be reading line by line? should I be passing a specific cli flag?

edit2: I think the **fix** is to change `from_json` to `from_file` [here](https://github.com/lhotse-speech/lhotse/blob/e400cb7ca8f2ecfe9c67b0589b208574c3f997da/lhotse/bin/modes/features.py#L86), I could make a PR?


Discussion:
Yes, you’re correct, please make a PR. Thanks! 

Resolving patch:
diff --git a/lhotse/bin/modes/features.py b/lhotse/bin/modes/features.py
--- a/lhotse/bin/modes/features.py
+++ b/lhotse/bin/modes/features.py
@@ -83,7 +83,7 @@ def extract(
     Extract features for recordings in a given AUDIO_MANIFEST. The features are stored in OUTPUT_DIR,
     with one file per recording (or segment).
     """
-    recordings: RecordingSet = RecordingSet.from_json(recording_manifest)
+    recordings: RecordingSet = RecordingSet.from_file(recording_manifest)
     if root_dir is not None:
         recordings = recordings.with_path_prefix(root_dir)
 


Test patch:
Repository: lhotse-speech/lhotse

Issue:
"'SupervisionSet' object has no attribute 'lazy'"
When running `lhotse cut simple` I get the above error, which comes from [this line](https://github.com/lhotse-speech/lhotse/blob/f31aa5464c368d141a1ae57c1ba0aa5fe554c9ce/lhotse/bin/modes/cut.py#L56):
```
if all(m is None or m.lazy for m in (supervision_set, feature_set, recording_set)):
```

Looking at SupervisionSet I don't see an attribute `lazy`, so I guess there was a code change that removed it without also modifying the above code?

edit: I fixed it by putting `if False` so it goes into the non lazy branch and making `load_manifest_lazy_or_eager` always load non-lazily


Discussion:

Resolving patch:
diff --git a/lhotse/bin/modes/cut.py b/lhotse/bin/modes/cut.py
--- a/lhotse/bin/modes/cut.py
+++ b/lhotse/bin/modes/cut.py
@@ -53,7 +53,9 @@ def simple(
         for p in (supervision_manifest, feature_manifest, recording_manifest)
     ]
 
-    if all(m is None or m.lazy for m in (supervision_set, feature_set, recording_set)):
+    if all(
+        m is None or m.is_lazy for m in (supervision_set, feature_set, recording_set)
+    ):
         # Create the CutSet lazily; requires sorting by recording_id
         CutSet.from_manifests(
             recordings=recording_set,


Test patch:
Repository: lhotse-speech/lhotse

Issue:
"'SupervisionSet' object has no attribute 'lazy'"
When running `lhotse cut simple` I get the above error, which comes from [this line](https://github.com/lhotse-speech/lhotse/blob/f31aa5464c368d141a1ae57c1ba0aa5fe554c9ce/lhotse/bin/modes/cut.py#L56):
```
if all(m is None or m.lazy for m in (supervision_set, feature_set, recording_set)):
```

Looking at SupervisionSet I don't see an attribute `lazy`, so I guess there was a code change that removed it without also modifying the above code?

edit: I fixed it by putting `if False` so it goes into the non lazy branch and making `load_manifest_lazy_or_eager` always load non-lazily


Discussion:
Thanks, please check out https://github.com/lhotse-speech/lhotse/pull/759 for a fix. 

nice, but without changing `load_manifest_lazy_or_eager` that still results in an error @pzelasko :
```
$ lhotse cut simple -r manifest_de_ch/mobi_test_import/recordings.jsonl.gz -f manifest_de_ch/mobi_test_import/feats/feature_manifest.json.gz -s manifest_de_ch/mobi_test_import/supervisions.jsonl.gz bla.jsonl.gz
[...]
  File "/home/rudolf/git/lhotse/lhotse/bin/modes/cut.py", line 68, in simple
    cut_set = CutSet.from_manifests(
  File "/home/rudolf/git/lhotse/lhotse/cut.py", line 3599, in from_manifests
    return create_cut_set_eager(
  File "/home/rudolf/git/lhotse/lhotse/cut.py", line 5589, in create_cut_set_eager
    cuts = CutSet.from_cuts(
  File "/home/rudolf/git/lhotse/lhotse/cut.py", line 3557, in from_cuts
    return CutSet(cuts=index_by_id_and_check(cuts))
  File "/home/rudolf/git/lhotse/lhotse/utils.py", line 566, in index_by_id_and_check
    for m in manifests:
  File "/home/rudolf/git/lhotse/lhotse/cut.py", line 5598, in <genexpr>
    recording=recordings[feats.recording_id] if rec_ok else None,
  File "/home/rudolf/git/lhotse/lhotse/audio.py", line 1149, in __getitem__
    return self.recordings[recording_id_or_index]
TypeError: 'LazyJsonlIterator' object is not subscriptable
```
I haven't tested it with a feature manifest input, I'll follow up on this.
same problem here, for the moment I just putting recordings=None when importing features from Kaldi
i don't understand because the type of recording is definitely "lhotse.audio.RecordingSet" which can be certainly accessed like a dict

Resolving patch:
diff --git a/lhotse/cut.py b/lhotse/cut.py
--- a/lhotse/cut.py
+++ b/lhotse/cut.py
@@ -5583,9 +5583,13 @@ def create_cut_set_eager(
         features is not None,
         recordings is not None,
     )
+    if sup_ok:
+        supervisions = supervisions.to_eager()  # must be eager to use .find()
     if feat_ok:
         # Case I: Features are provided.
         # Use features to determine the cut boundaries and attach recordings and supervisions as available.
+        if rec_ok:
+            recordings = recordings.to_eager()  # ensure it can be indexed with cut.id
         cuts = CutSet.from_cuts(
             MonoCut(
                 id=str(uuid4())


Test patch:
Repository: lhotse-speech/lhotse

Issue:
Whisper workflow supervision `end` may be incorrect
So I ran the model on one of the AMI headset recordings (~5000s) and it seems like the "end" actually shows the end of the segment, not the duration. Here is the JSON containing the `results["segments"]`: https://drive.google.com/file/d/169igkcDY2SmMs5k3hOhHip89T4MQDnKs/view?usp=sharing

_Originally posted by @desh2608 in https://github.com/lhotse-speech/lhotse/pull/834#discussion_r988376898_
      


Discussion:
That might explain why I was getting weird results that needed the truncation post processing step. Thanks for finding the issue! I'll fix it.

Resolving patch:
diff --git a/lhotse/workflows/whisper.py b/lhotse/workflows/whisper.py
--- a/lhotse/workflows/whisper.py
+++ b/lhotse/workflows/whisper.py
@@ -3,7 +3,14 @@
 
 import torch
 
-from lhotse import CutSet, MonoCut, Recording, RecordingSet, SupervisionSegment
+from lhotse import (
+    CutSet,
+    MonoCut,
+    Recording,
+    RecordingSet,
+    SupervisionSegment,
+    add_durations,
+)
 from lhotse.qa import trim_supervisions_to_recordings
 from lhotse.utils import fastcopy, is_module_available
 
@@ -68,7 +75,9 @@ def _annotate_recordings(
                 id=f"{recording.id}-{segment['id']:06d}",
                 recording_id=recording.id,
                 start=round(segment["start"], ndigits=8),
-                duration=round(segment["end"], ndigits=8),
+                duration=add_durations(
+                    segment["end"], -segment["start"], sampling_rate=16000
+                ),
                 text=segment["text"].strip(),
                 language=result["language"],
             )
@@ -107,7 +116,12 @@ def _annotate_cuts(cuts: CutSet, language: str, model_name: str, device: str):
                 id=f"{cut.id}-{segment['id']:06d}",
                 recording_id=cut.recording_id,
                 start=round(segment["start"], ndigits=8),
-                duration=max(cut.duration, round(segment["end"], ndigits=8)),
+                duration=max(
+                    cut.duration,
+                    add_durations(
+                        segment["end"], -segment["start"], sampling_rate=16000
+                    ),
+                ),
                 text=segment["text"].strip(),
                 language=result["language"],
             )


Test patch:
Repository: lhotse-speech/lhotse

Issue:
Do not discard custom arrays in to_mono()
Calling `MultiCut.to_mono` discards custom field from the cut. https://github.com/lhotse-speech/lhotse/blob/8082876f153bd32a3046a66f17c8f0028dcabe4f/lhotse/cut/multi.py#L372

It would be great to have a support for splitting the custom field arrays to mono as well (assuming that custom feats and audio have the same number of channels as declared in the MultiCut)


Discussion:

Resolving patch:
diff --git a/lhotse/cut/multi.py b/lhotse/cut/multi.py
--- a/lhotse/cut/multi.py
+++ b/lhotse/cut/multi.py
@@ -387,6 +387,7 @@ def to_mono(self) -> List["DataCut"]:
                     for s in self.supervisions
                     if is_equal_or_contains(s.channel, channel)
                 ],
+                custom=self.custom,
             )
             for channel in to_list(self.channel)
         ]


Test patch:
Repository: lhotse-speech/lhotse

Issue:
The 'speaker' field in the stcmds dataset is incorrect
The file 20170001P00001I0001.wav is spoken by a female, 
and the file 20170001P00001A0001.wav is spoken by a male.
But they have the same speaker ID P00001


Discussion:
CC @shanguanma could look into it as the author of the recipe?

Resolving patch:
diff --git a/lhotse/recipes/stcmds.py b/lhotse/recipes/stcmds.py
--- a/lhotse/recipes/stcmds.py
+++ b/lhotse/recipes/stcmds.py
@@ -104,7 +104,7 @@ def prepare_stcmds(
         supervisions = []
         for audio_path in path.rglob("**/*.wav"):
             idx = audio_path.stem
-            speaker = "".join(list(idx)[8:14])
+            speaker = "".join(list(idx)[8:15])
             if idx not in transcript_dict:
                 logging.warning(f"No transcript: {idx}")
                 logging.warning(f"{audio_path} has no transcript")


Test patch:
Repository: lhotse-speech/lhotse

Issue:
Question about the returns of CutSet.mix() ?
`CutSet.mix()` returns only `MixedCut` when `p=1`, but returns both `MonoCut` and `MixedCut` when `p <1.0`;
```
from lhotse import Recording, CutSet

 wav_file = "a.wav"
 noise_file = "b.wav"

 wav_cut = Recording.from_file(wav_file).to_cut()
 noise_cut = Recording.from_file(noise_file).to_cut()

 wav_cuts = CutSet.from_cuts([wav_cut])
 noise_cuts = CutSet.from_cuts([noise_cut])

 for p in [0.1, 1.0]:
     mixed_cuts = wav_cuts.mix(
         cuts=noise_cuts,
         duration=None,
         snr=10,
         mix_prob=p,
         preserve_id=None,
         seed=42,
         random_mix_offset=True,
     ).to_eager()

     print(f"Mixed cuts lengths: {len(mixed_cuts)}")
```
outputs 
```
Mixed cuts lengths: 2
Mixed cuts lengths: 1
```

Hi, @pzelasko  Is it a bug here ? As what i want is only the mixed cut `MixedCut` when training ASR model. And when training ASR model with the `lhotse.dataset.CutMix` transform, the time costs 2x compared to the training without `lhotse.dataset.CutMix` transform.


Discussion:

Resolving patch:
diff --git a/lhotse/cut/set.py b/lhotse/cut/set.py
--- a/lhotse/cut/set.py
+++ b/lhotse/cut/set.py
@@ -3391,6 +3391,7 @@ def __iter__(self):
             # or pass it through unchanged.
             if rng.uniform(0.0, 1.0) > self.mix_prob:
                 yield cut
+                continue
             to_mix = next(mix_in_cuts)
             # Determine the SNR - either it's specified or we need to sample one.
             cut_snr = (


Test patch:
diff --git a/test/cut/test_cut_mixing.py b/test/cut/test_cut_mixing.py
--- a/test/cut/test_cut_mixing.py
+++ b/test/cut/test_cut_mixing.py
@@ -474,3 +474,20 @@ def test_cut_set_mix_is_lazy():
     mixed = cuts.mix(cuts, snr=10, mix_prob=1.0, seed=0)
 
     assert mixed.is_lazy
+
+
+def test_cut_set_mix_size_is_not_growing():
+    cuts = DummyManifest(CutSet, begin_id=0, end_id=100)
+    noise_cuts = DummyManifest(CutSet, begin_id=10, end_id=20)
+
+    mixed_cuts = cuts.mix(
+        cuts=noise_cuts,
+        duration=None,
+        snr=10,
+        mix_prob=0.1,
+        preserve_id=None,
+        seed=42,
+        random_mix_offset=True,
+    ).to_eager()
+
+    assert len(mixed_cuts) == len(cuts)
Repository: lhotse-speech/lhotse

Issue:
AttributeError: No such attribute: to_eager
Hi there,

Thank you for your excellent work. I've found the tool extremely helpful for data filtering and analysis. I am attempting to merge consecutive segments into one long segment to perform further statistics, but I encountered an error. Here's the situation:

This code works fine:
`cuts.filter_supervisions(lambda sup: sup.duration > 10).describe()`

However, when I try to merge segments using the following code, I run into an issue:
`cuts.filter_supervisions(lambda sup: sup.duration > 10).trim_to_supervision_groups(max_pause=100).describe()`

Below is the error trace screenshot:
![截屏2024-04-15 16 29 06](https://github.com/lhotse-speech/lhotse/assets/24587336/f94fac27-8dcb-40e7-abb4-ea731620f9a5)

Could you please advise on how I can successfully merge these segments and proceed with the data analysis? Thank you for your assistance!



Discussion:

Resolving patch:
diff --git a/lhotse/cut/base.py b/lhotse/cut/base.py
--- a/lhotse/cut/base.py
+++ b/lhotse/cut/base.py
@@ -677,7 +677,7 @@ def trim_to_supervision_groups(
         from .set import CutSet
 
         if not self.supervisions:
-            return self
+            return CutSet([self])
         supervisions = sorted(self.supervisions, key=lambda s: s.start)
         supervision_group = [supervisions[0]]
         cur_end = supervisions[0].end


Test patch:
Repository: lhotse-speech/lhotse

Issue:
Describe on cuts does not display supervision custom info
As in line 178:

https://github.com/lhotse-speech/lhotse/blob/ddde5bd15a769962ba7ec872fd9212bc89b08b5d/lhotse/cut/describe.py#L170-L178


Discussion:

Resolving patch:
diff --git a/lhotse/cut/describe.py b/lhotse/cut/describe.py
--- a/lhotse/cut/describe.py
+++ b/lhotse/cut/describe.py
@@ -175,7 +175,7 @@ def time_as_str_(seconds: Seconds) -> str:
         if self.sup_custom:
             print("SUPERVISION custom fields:")
             for key, val in self.sup_custom.most_common():
-                cut_stats.append(f"- {key} (in {val} cuts)")
+                print(f"- {key} (in {val} cuts)")
 
         total_speech = np.array(self.speech_durations).sum()
         total_speaking_time = np.array(self.speaking_time_durations).sum()


Test patch:
Repository: oppia/oppia

Issue:
Cannot push code due to pre push failure 
I'm trying to push some code and I get this error from the pre push (although I got no errors from running the presubmit script)

``` ref_list:
[GitRef(local_ref='refs/heads/expression-syntax-tree', local_sha1='7df913917134283dce95c61cf7395b2375127399', remote_ref='refs/heads/expression-syntax-tree', remote_sha1='0000000000000000000000000000000000000000')]
Traceback (most recent call last):
  File ".git/hooks/pre-push", line 243, in <module>
    main()
  File ".git/hooks/pre-push", line 228, in main
    modified_files, files_to_lint = _collect_files_being_pushed(refs, remote)
  File ".git/hooks/pre-push", line 150, in _collect_files_being_pushed
    remote_branch='develop')
  File ".git/hooks/pre-push", line 103, in _compare_to_remote
    return _git_diff_name_status(git_remote, local_branch)
  File ".git/hooks/pre-push", line 82, in _git_diff_name_status
    file_list = [FileDiff(*line.split()) for line in out.splitlines()]
TypeError: __new__() takes exactly 3 arguments (4 given)
```

This doesn't read an error from any file/function that I modified in the commits in this branch



Discussion:
/cc @wxyxinyu, @brunnre8 

There seem to be two issues here:
1. When creating a new branch, it considers everything to be a "changed file", which may or may not be the desired behavior. I don't know the script well enough, but it definitely looks weird to me.
2. When looking at the files, it seems that it does not handle file names that contain white space like static/images/gallery/thumbnails/Graph Theory.svg correctly. It handles them as two strings rather than as one, thus leading to it thinking it has too many arguments for the function.

OK, so issue 1 seems to be an issue with my development process that is somehow updating local but not updating my branch. They are in some ways legitimately changes, so there isn't an issue with the scripts findings here.


Resolving patch:
diff --git a/scripts/pre_push_hook.py b/scripts/pre_push_hook.py
--- a/scripts/pre_push_hook.py
+++ b/scripts/pre_push_hook.py
@@ -79,7 +79,8 @@ def _git_diff_name_status(left, right, diff_filter=''):
     git_cmd.extend([left, right])
     out, err = _start_subprocess_for_result(git_cmd)
     if not err:
-        file_list = [FileDiff(*line.split()) for line in out.splitlines()]
+        # 1st char is status, 2nd char is a tab, rest is filename
+        file_list = [FileDiff(line[0], line[2:]) for line in out.splitlines()]
         return file_list
     else:
         raise ValueError(err)


Test patch:
Repository: oppia/oppia

Issue:
Code review request
```
Branch name: tutorial-fixes

Link to the relevant commit(s): 
https://code.google.com/p/oppia/source/detail?r=2b45d6690489da66af089a830d86a9a2
f447734c

Purpose of code changes on this branch: show tutorial immediately for 
first-time editors

When reviewing my code changes, please focus on: UI and functionality

After the review, I won't merge this branch yet -- I want to fix a few other 
things before merging into develop (see comment on the commit above).

```

Original issue reported on code.google.com by `s...@google.com` on 6 Nov 2014 at 2:13



Discussion:
Original comment by `s...@seanlip.org` on 19 Nov 2014 at 1:29
- Changed state: **Done**


Resolving patch:
diff --git a/extensions/objects/models/objects.py b/extensions/objects/models/objects.py
--- a/extensions/objects/models/objects.py
+++ b/extensions/objects/models/objects.py
@@ -566,6 +566,21 @@ def normalize(cls, raw):
         return raw
 
 
+class GraphProperty(BaseObject):
+    """A string from a list of possible graph properties"""
+
+    description = 'One of the possible properties possessed by a graph.'
+    edit_html_filename = 'graph_property_editor'
+    edit_js_filename = 'GraphPropertyEditor'
+
+    SCHEMA = {
+        'type': 'unicode',
+        'choices': [
+            'strongly_connected', 'weakly_connected', 'acyclic', 'regular'
+        ]
+    }
+
+
 class ListOfGraph(BaseObject):
     """Class for lists of Graphs."""
 
diff --git a/extensions/rules/graph.py b/extensions/rules/graph.py
--- a/extensions/rules/graph.py
+++ b/extensions/rules/graph.py
@@ -19,6 +19,10 @@
 from extensions.rules import base
 
 
+class HasGraphProperty(base.GraphRule):
+    description = 'is {{p|GraphProperty}}'
+
+
 class IsIsomorphicTo(base.GraphRule):
     description = 'is isomorphic to {{g|Graph}}, including matching labels'
 


Test patch:
diff --git a/extensions/objects/models/objects_test.py b/extensions/objects/models/objects_test.py
--- a/extensions/objects/models/objects_test.py
+++ b/extensions/objects/models/objects_test.py
@@ -368,6 +368,19 @@ def test_graph(self):
         self.check_normalization(
             objects.Graph, mappings, invalid_values)
 
+    def test_graph_property_validation(self):
+        """Tests objects of type GraphProperty"""
+
+        mappings = [
+            ('acyclic', 'acyclic'), ('regular', 'regular'),
+            ('strongly_connected', 'strongly_connected'),
+            ('weakly_connected', 'weakly_connected')]
+
+        invalid_values = [None, 2, 'string', 'item']
+
+        self.check_normalization(
+            objects.GraphProperty, mappings, invalid_values)
+
     def test_set_of_html_string(self):
         """Tests objects of the type StringList"""
 
@@ -387,4 +400,4 @@ def test_schemas_used_to_define_objects_are_valid(self):
                     schema_utils_test.validate_schema(member.SCHEMA)
                     count += 1
 
-        self.assertEquals(count, 29)
+        self.assertEquals(count, 30)
Repository: oppia/oppia

Issue:
Latex RTE Component shows black vertical bar on the right border
When inserting the Latex component in the rich text editor, an unexpected black vertical bar appears in the right border of the Mathjax component, and renders in the preview as well. (I'm using Chrome 48.0.2564.82)

<img width="638" alt="screenshot 2016-01-21 22 35 54" src="https://cloud.githubusercontent.com/assets/4048170/12504088/598e07c0-c08f-11e5-8d74-0b79a86457e0.png">

<img width="758" alt="screenshot 2016-01-21 22 36 27" src="https://cloud.githubusercontent.com/assets/4048170/12504098/6c958172-c08f-11e5-92d4-ca197b356c46.png">



Discussion:
Looking at this: http://stackoverflow.com/a/34283306 it appears we just need to upgrade our version of MathJax.

Thanks, @AllanYangZhou! Changing this to 'TODO: code' since the solution is known.


Resolving patch:
diff --git a/scripts/install_third_party.py b/scripts/install_third_party.py
--- a/scripts/install_third_party.py
+++ b/scripts/install_third_party.py
@@ -304,7 +304,7 @@ def download_manifest_files(filepath):
                     dependency_tar_root_name, dependency_target_root_name)
 
 
-MATHJAX_REV = '2.4-latest'
+MATHJAX_REV = '2.6.0'
 MATHJAX_ROOT_NAME = 'MathJax-%s' % MATHJAX_REV
 MATHJAX_TARGET_ROOT_NAME = MATHJAX_ROOT_NAME
 MATHJAX_DIR_PREFIX = os.path.join(


Test patch:
Repository: oppia/oppia

Issue:
In the rich-text editor, auto-prepend "https://" to links which don't specify a protocol
```
Currently the non-interactive link widget will only accept links that begin 
with either "http://" or "https://". I propose that whenever a link does not, 
e.g. "www.google.com" we automatically prepend "http://www.google.com" to the 
link string that is stored.
```

Original issue reported on code.google.com by `jacobdav...@gmail.com` on 24 Aug 2014 at 9:43



Discussion:
Original comment by `jacobdav...@gmail.com` on 29 Sep 2014 at 4:37

It might be better to prepend https:// automatically, actually. If the user explicitly modifies that to http:// we can leave it as http://.

/cc @jacobdavis11 (original reporter)

This check is in extensions/rich_text_components/Link/Link.js
There is also a description of this restriction in extensions/rich_text_components/Link/Link.py which should also be changed.


Resolving patch:
diff --git a/extensions/rich_text_components/Link/Link.py b/extensions/rich_text_components/Link/Link.py
--- a/extensions/rich_text_components/Link/Link.py
+++ b/extensions/rich_text_components/Link/Link.py
@@ -29,7 +29,7 @@ class Link(base.BaseRichTextComponent):
     _customization_arg_specs = [{
         'name': 'url',
         'description': (
-            'The link URL. It must start with http:// or https://'),
+            'The link URL. If no protocol is specified, HTTPS will be used.'),
         'schema': {
             'type': 'custom',
             'obj_type': 'SanitizedUrl',


Test patch: