utterplan 0.1.1__py3-none-any.whl
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.
- utterplan/__init__.py +81 -0
- utterplan/__main__.py +4 -0
- utterplan/_version.py +24 -0
- utterplan/cli.py +271 -0
- utterplan/config.py +161 -0
- utterplan/diagnostics.py +3 -0
- utterplan/directives.py +82 -0
- utterplan/exceptions.py +47 -0
- utterplan/format.py +34 -0
- utterplan/hashing.py +43 -0
- utterplan/language.py +93 -0
- utterplan/linguistics.py +179 -0
- utterplan/model.py +931 -0
- utterplan/parsers.py +272 -0
- utterplan/pauses.py +66 -0
- utterplan/planner.py +474 -0
- utterplan/preparation.py +224 -0
- utterplan/py.typed +0 -0
- utterplan/serialization.py +22 -0
- utterplan/spacy_models.py +21 -0
- utterplan/spans.py +3 -0
- utterplan/stages/__init__.py +0 -0
- utterplan/stages/document/__init__.py +0 -0
- utterplan/stages/document/plain.py +4 -0
- utterplan/stages/document/ssmd.py +3 -0
- utterplan/stages/preparation/__init__.py +0 -0
- utterplan/stages/preparation/identity.py +3 -0
- utterplan/stages/preparation/spokenform.py +3 -0
- utterplan/stages/protocols.py +25 -0
- utterplan/stages/segmentation/__init__.py +0 -0
- utterplan/stages/segmentation/phrasplit.py +35 -0
- utterplan/units.py +81 -0
- utterplan/utterplan.schema.json +260 -0
- utterplan-0.1.1.dist-info/METADATA +348 -0
- utterplan-0.1.1.dist-info/RECORD +40 -0
- utterplan-0.1.1.dist-info/WHEEL +5 -0
- utterplan-0.1.1.dist-info/entry_points.txt +2 -0
- utterplan-0.1.1.dist-info/licenses/LICENSE +201 -0
- utterplan-0.1.1.dist-info/licenses/NOTICE +1 -0
- utterplan-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .model import UtterancePlan
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def to_dict(plan: UtterancePlan) -> dict[str, Any]:
|
|
10
|
+
return plan.to_dict()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def from_dict(data: Mapping[str, Any]) -> UtterancePlan:
|
|
14
|
+
return UtterancePlan.from_dict(data)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def to_json(plan: UtterancePlan, *, indent: int | None = 2) -> str:
|
|
18
|
+
return plan.to_json(indent=indent)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def from_json(value: str) -> UtterancePlan:
|
|
22
|
+
return UtterancePlan.from_json(value)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
SpacyModelSize = Literal["sm", "md", "lg", "trf"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def normalize_spacy_language(language: str | None) -> str:
|
|
9
|
+
value = (language or "en").strip().lower().replace("_", "-")
|
|
10
|
+
return value.split("-", 1)[0] or "en"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def resolve_spacy_model(language: str, size: SpacyModelSize = "sm") -> str:
|
|
14
|
+
if size not in {"sm", "md", "lg", "trf"}:
|
|
15
|
+
raise ValueError("spacy model size must be one of sm, md, lg, trf")
|
|
16
|
+
base = normalize_spacy_language(language)
|
|
17
|
+
family = "web" if base in {"en", "zh"} else "news"
|
|
18
|
+
return f"{base}_core_{family}_{size}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ["SpacyModelSize", "normalize_spacy_language", "resolve_spacy_model"]
|
utterplan/spans.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from ..config import PlannerConfig
|
|
6
|
+
from ..model import AnnotationSpan, BoundaryEvent
|
|
7
|
+
from ..parsers import ParsedDocument
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DocumentParser(Protocol):
|
|
11
|
+
def parse(self, text: str, config: PlannerConfig) -> ParsedDocument: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TextPreparer(Protocol):
|
|
15
|
+
def prepare(
|
|
16
|
+
self,
|
|
17
|
+
text: str,
|
|
18
|
+
language: str,
|
|
19
|
+
runs: tuple[object, ...],
|
|
20
|
+
annotations: tuple[AnnotationSpan, ...] = (),
|
|
21
|
+
boundaries: tuple[BoundaryEvent, ...] = (),
|
|
22
|
+
) -> object: ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ["DocumentParser", "TextPreparer"]
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ...config import PlannerConfig
|
|
4
|
+
from ...model import PlanSegment
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def split_text(text: str, config: PlannerConfig) -> tuple[PlanSegment, ...]:
|
|
8
|
+
try:
|
|
9
|
+
import phrasplit
|
|
10
|
+
|
|
11
|
+
items = phrasplit.split_with_offsets(
|
|
12
|
+
text, mode="sentence", use_spacy=False, language=config.language
|
|
13
|
+
)
|
|
14
|
+
except (ImportError, OSError, TypeError, ValueError):
|
|
15
|
+
items = ()
|
|
16
|
+
result = []
|
|
17
|
+
for index, item in enumerate(items):
|
|
18
|
+
start, end = int(item.char_start), int(item.char_end)
|
|
19
|
+
result.append(
|
|
20
|
+
PlanSegment(
|
|
21
|
+
f"seg-{index:06d}",
|
|
22
|
+
text[start:end],
|
|
23
|
+
start,
|
|
24
|
+
end,
|
|
25
|
+
config.language,
|
|
26
|
+
int(getattr(item, "paragraph_idx", 0) or 0),
|
|
27
|
+
int(getattr(item, "sentence_idx", 0) or 0),
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
if not result and text:
|
|
31
|
+
result.append(PlanSegment("seg-000000", text, 0, len(text), config.language))
|
|
32
|
+
return tuple(result)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = ["split_text"]
|
utterplan/units.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import replace
|
|
4
|
+
|
|
5
|
+
from .hashing import semantic_hash, unit_hash_payload
|
|
6
|
+
from .model import Marker, PlanSegment, PlanUnit
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def make_units(
|
|
10
|
+
segments: tuple[PlanSegment, ...], markers: tuple[Marker, ...], kind: str
|
|
11
|
+
) -> tuple[PlanUnit, ...]:
|
|
12
|
+
if not segments:
|
|
13
|
+
return ()
|
|
14
|
+
groups: list[list[PlanSegment]] = []
|
|
15
|
+
current: list[PlanSegment] = []
|
|
16
|
+
current_key: tuple[int, int] | None = None
|
|
17
|
+
closed: set[tuple[int, int]] = set()
|
|
18
|
+
for segment in segments:
|
|
19
|
+
key = (segment.paragraph, segment.sentence if kind == "sentence" else -1)
|
|
20
|
+
if current and key != current_key:
|
|
21
|
+
groups.append(current)
|
|
22
|
+
if current_key is not None:
|
|
23
|
+
closed.add(current_key)
|
|
24
|
+
current = []
|
|
25
|
+
if key in closed:
|
|
26
|
+
raise ValueError(f"non-contiguous unit key {key}")
|
|
27
|
+
current.append(segment)
|
|
28
|
+
current_key = key
|
|
29
|
+
if current:
|
|
30
|
+
groups.append(current)
|
|
31
|
+
|
|
32
|
+
units: list[PlanUnit] = []
|
|
33
|
+
for index, group in enumerate(groups):
|
|
34
|
+
start, end = group[0].spoken_start, group[-1].spoken_end
|
|
35
|
+
marker_ids = tuple(
|
|
36
|
+
marker.id
|
|
37
|
+
for marker in markers
|
|
38
|
+
if _marker_belongs(marker.spoken_position, groups, index)
|
|
39
|
+
)
|
|
40
|
+
marker_values = tuple(marker for marker in markers if marker.id in marker_ids)
|
|
41
|
+
provisional = PlanUnit(
|
|
42
|
+
f"unit-{index:04d}",
|
|
43
|
+
index,
|
|
44
|
+
kind,
|
|
45
|
+
start,
|
|
46
|
+
end,
|
|
47
|
+
tuple(segment.id for segment in group),
|
|
48
|
+
marker_ids,
|
|
49
|
+
)
|
|
50
|
+
units.append(
|
|
51
|
+
replace(
|
|
52
|
+
provisional,
|
|
53
|
+
content_hash=semantic_hash(
|
|
54
|
+
unit_hash_payload(_UnitView(group, marker_ids, marker_values))
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
return tuple(units)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _marker_belongs(position: int, groups: list[list[PlanSegment]], index: int) -> bool:
|
|
62
|
+
group = groups[index]
|
|
63
|
+
start = group[0].spoken_start
|
|
64
|
+
end = group[-1].spoken_end
|
|
65
|
+
if index < len(groups) - 1 and end <= position <= groups[index + 1][0].spoken_start:
|
|
66
|
+
return False
|
|
67
|
+
if index > 0 and groups[index - 1][-1].spoken_end <= position <= start:
|
|
68
|
+
return True
|
|
69
|
+
return start <= position < end or (index == len(groups) - 1 and position == end)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _UnitView:
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
segments: list[PlanSegment],
|
|
76
|
+
marker_ids: tuple[str, ...],
|
|
77
|
+
marker_values: tuple[Marker, ...],
|
|
78
|
+
) -> None:
|
|
79
|
+
self.segments = tuple(segments)
|
|
80
|
+
self.marker_ids = marker_ids
|
|
81
|
+
self.marker_values = marker_values
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/buchwandler/utterplan/blob/main/spec/utterplan.schema.json",
|
|
4
|
+
"title": "UtterPlan schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"format",
|
|
8
|
+
"schema_version",
|
|
9
|
+
"producer",
|
|
10
|
+
"plan_id",
|
|
11
|
+
"source",
|
|
12
|
+
"config",
|
|
13
|
+
"texts",
|
|
14
|
+
"preparation",
|
|
15
|
+
"languages",
|
|
16
|
+
"annotations",
|
|
17
|
+
"boundaries",
|
|
18
|
+
"tokens",
|
|
19
|
+
"segments",
|
|
20
|
+
"units",
|
|
21
|
+
"markers",
|
|
22
|
+
"warnings",
|
|
23
|
+
"diagnostics"
|
|
24
|
+
],
|
|
25
|
+
"properties": {
|
|
26
|
+
"format": { "const": "utterplan" },
|
|
27
|
+
"schema_version": { "const": 1 },
|
|
28
|
+
"producer": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["name", "version"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"name": { "type": "string" },
|
|
33
|
+
"version": { "type": "string" }
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": true
|
|
36
|
+
},
|
|
37
|
+
"plan_id": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
38
|
+
"source": { "$ref": "#/$defs/source" },
|
|
39
|
+
"config": { "type": "object" },
|
|
40
|
+
"texts": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["structural", "spoken"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"structural": { "type": "string" },
|
|
45
|
+
"spoken": { "type": "string" }
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
},
|
|
49
|
+
"preparation": { "$ref": "#/$defs/preparation" },
|
|
50
|
+
"languages": { "type": "array", "items": { "$ref": "#/$defs/language" } },
|
|
51
|
+
"annotations": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "$ref": "#/$defs/annotation" }
|
|
54
|
+
},
|
|
55
|
+
"boundaries": { "type": "array", "items": { "$ref": "#/$defs/boundary" } },
|
|
56
|
+
"tokens": { "type": "array", "items": { "$ref": "#/$defs/token" } },
|
|
57
|
+
"segments": { "type": "array", "items": { "$ref": "#/$defs/segment" } },
|
|
58
|
+
"units": { "type": "array", "items": { "$ref": "#/$defs/unit" } },
|
|
59
|
+
"markers": { "type": "array", "items": { "$ref": "#/$defs/marker" } },
|
|
60
|
+
"document_metadata": { "type": "object" },
|
|
61
|
+
"warnings": { "type": "array", "items": { "type": "string" } },
|
|
62
|
+
"diagnostics": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": { "$ref": "#/$defs/diagnostic" }
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"additionalProperties": false,
|
|
68
|
+
"$defs": {
|
|
69
|
+
"source": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"required": ["format", "text"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"format": { "enum": ["plain", "ssmd"] },
|
|
74
|
+
"text": { "type": "string" }
|
|
75
|
+
},
|
|
76
|
+
"additionalProperties": false
|
|
77
|
+
},
|
|
78
|
+
"preparation": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"required": ["backend", "languages", "replacements", "warnings"],
|
|
81
|
+
"properties": {
|
|
82
|
+
"backend": { "type": "string" },
|
|
83
|
+
"version": { "type": ["string", "null"] },
|
|
84
|
+
"languages": { "type": "array", "items": { "type": "string" } },
|
|
85
|
+
"replacements": { "type": "array", "items": { "type": "object" } },
|
|
86
|
+
"warnings": { "type": "array", "items": { "type": "string" } }
|
|
87
|
+
},
|
|
88
|
+
"additionalProperties": false
|
|
89
|
+
},
|
|
90
|
+
"range": { "type": "integer", "minimum": 0 },
|
|
91
|
+
"language": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"required": ["id", "spoken_start", "spoken_end", "language", "source"],
|
|
94
|
+
"properties": {
|
|
95
|
+
"id": { "type": "string" },
|
|
96
|
+
"spoken_start": { "$ref": "#/$defs/range" },
|
|
97
|
+
"spoken_end": { "$ref": "#/$defs/range" },
|
|
98
|
+
"language": { "type": "string" },
|
|
99
|
+
"source": { "type": "string" }
|
|
100
|
+
},
|
|
101
|
+
"additionalProperties": false
|
|
102
|
+
},
|
|
103
|
+
"annotation": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"required": [
|
|
106
|
+
"id",
|
|
107
|
+
"structural_start",
|
|
108
|
+
"structural_end",
|
|
109
|
+
"spoken_start",
|
|
110
|
+
"spoken_end",
|
|
111
|
+
"kind",
|
|
112
|
+
"attrs"
|
|
113
|
+
],
|
|
114
|
+
"properties": {
|
|
115
|
+
"id": { "type": "string" },
|
|
116
|
+
"structural_start": { "$ref": "#/$defs/range" },
|
|
117
|
+
"structural_end": { "$ref": "#/$defs/range" },
|
|
118
|
+
"spoken_start": { "type": ["integer", "null"], "minimum": 0 },
|
|
119
|
+
"spoken_end": { "type": ["integer", "null"], "minimum": 0 },
|
|
120
|
+
"kind": { "type": "string" },
|
|
121
|
+
"attrs": { "type": "object" }
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"boundary": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"required": ["id", "position", "kind", "seconds", "origin", "strength"],
|
|
128
|
+
"properties": {
|
|
129
|
+
"id": { "type": "string" },
|
|
130
|
+
"position": { "$ref": "#/$defs/range" },
|
|
131
|
+
"kind": { "type": "string" },
|
|
132
|
+
"seconds": { "type": ["number", "null"], "minimum": 0 },
|
|
133
|
+
"origin": { "type": "string" },
|
|
134
|
+
"strength": { "type": ["string", "null"] },
|
|
135
|
+
"attrs": { "type": "object" }
|
|
136
|
+
},
|
|
137
|
+
"additionalProperties": false
|
|
138
|
+
},
|
|
139
|
+
"token": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"required": ["spoken_start", "spoken_end", "text"],
|
|
142
|
+
"properties": {
|
|
143
|
+
"id": { "type": "string" },
|
|
144
|
+
"spoken_start": { "$ref": "#/$defs/range" },
|
|
145
|
+
"spoken_end": { "$ref": "#/$defs/range" },
|
|
146
|
+
"text": { "type": "string" },
|
|
147
|
+
"pos": { "type": ["string", "null"] },
|
|
148
|
+
"tag": { "type": ["string", "null"] },
|
|
149
|
+
"lemma": { "type": ["string", "null"] },
|
|
150
|
+
"language": { "type": ["string", "null"] }
|
|
151
|
+
},
|
|
152
|
+
"additionalProperties": false
|
|
153
|
+
},
|
|
154
|
+
"pause": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"required": ["seconds", "events"],
|
|
157
|
+
"properties": {
|
|
158
|
+
"seconds": { "type": "number", "minimum": 0 },
|
|
159
|
+
"events": { "type": "array", "items": { "type": "string" } }
|
|
160
|
+
},
|
|
161
|
+
"additionalProperties": false
|
|
162
|
+
},
|
|
163
|
+
"directives": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": {
|
|
166
|
+
"voice": { "type": ["object", "null"] },
|
|
167
|
+
"pronunciation": { "type": ["object", "null"] },
|
|
168
|
+
"prosody": { "type": ["object", "null"] },
|
|
169
|
+
"emphasis": { "type": ["object", "null"] },
|
|
170
|
+
"audio": { "type": ["object", "null"] }
|
|
171
|
+
},
|
|
172
|
+
"additionalProperties": false
|
|
173
|
+
},
|
|
174
|
+
"segment": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"required": [
|
|
177
|
+
"id",
|
|
178
|
+
"text",
|
|
179
|
+
"spoken_start",
|
|
180
|
+
"spoken_end",
|
|
181
|
+
"language",
|
|
182
|
+
"paragraph",
|
|
183
|
+
"sentence",
|
|
184
|
+
"clause",
|
|
185
|
+
"pause_before",
|
|
186
|
+
"pause_after",
|
|
187
|
+
"directives",
|
|
188
|
+
"token_indices",
|
|
189
|
+
"annotation_ids"
|
|
190
|
+
],
|
|
191
|
+
"properties": {
|
|
192
|
+
"id": { "type": "string" },
|
|
193
|
+
"text": { "type": "string" },
|
|
194
|
+
"spoken_start": { "$ref": "#/$defs/range" },
|
|
195
|
+
"spoken_end": { "$ref": "#/$defs/range" },
|
|
196
|
+
"language": { "type": "string" },
|
|
197
|
+
"paragraph": { "type": "integer", "minimum": 0 },
|
|
198
|
+
"sentence": { "type": "integer", "minimum": 0 },
|
|
199
|
+
"clause": { "type": "integer", "minimum": 0 },
|
|
200
|
+
"structural_start": { "type": ["integer", "null"], "minimum": 0 },
|
|
201
|
+
"structural_end": { "type": ["integer", "null"], "minimum": 0 },
|
|
202
|
+
"pause_before": { "$ref": "#/$defs/pause" },
|
|
203
|
+
"pause_after": { "$ref": "#/$defs/pause" },
|
|
204
|
+
"directives": { "$ref": "#/$defs/directives" },
|
|
205
|
+
"token_indices": {
|
|
206
|
+
"type": "array",
|
|
207
|
+
"items": { "type": "integer", "minimum": 0 }
|
|
208
|
+
},
|
|
209
|
+
"annotation_ids": { "type": "array", "items": { "type": "string" } }
|
|
210
|
+
},
|
|
211
|
+
"additionalProperties": false
|
|
212
|
+
},
|
|
213
|
+
"unit": {
|
|
214
|
+
"type": "object",
|
|
215
|
+
"required": [
|
|
216
|
+
"id",
|
|
217
|
+
"index",
|
|
218
|
+
"kind",
|
|
219
|
+
"spoken_start",
|
|
220
|
+
"spoken_end",
|
|
221
|
+
"segment_ids",
|
|
222
|
+
"marker_ids",
|
|
223
|
+
"content_hash"
|
|
224
|
+
],
|
|
225
|
+
"properties": {
|
|
226
|
+
"id": { "type": "string" },
|
|
227
|
+
"index": { "type": "integer", "minimum": 0 },
|
|
228
|
+
"kind": { "enum": ["paragraph", "sentence"] },
|
|
229
|
+
"spoken_start": { "$ref": "#/$defs/range" },
|
|
230
|
+
"spoken_end": { "$ref": "#/$defs/range" },
|
|
231
|
+
"segment_ids": { "type": "array", "items": { "type": "string" } },
|
|
232
|
+
"marker_ids": { "type": "array", "items": { "type": "string" } },
|
|
233
|
+
"content_hash": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
|
|
234
|
+
},
|
|
235
|
+
"additionalProperties": false
|
|
236
|
+
},
|
|
237
|
+
"marker": {
|
|
238
|
+
"type": "object",
|
|
239
|
+
"required": ["id", "name", "spoken_position"],
|
|
240
|
+
"properties": {
|
|
241
|
+
"id": { "type": "string" },
|
|
242
|
+
"name": { "type": "string" },
|
|
243
|
+
"spoken_position": { "$ref": "#/$defs/range" },
|
|
244
|
+
"attrs": { "type": "object" }
|
|
245
|
+
},
|
|
246
|
+
"additionalProperties": false
|
|
247
|
+
},
|
|
248
|
+
"diagnostic": {
|
|
249
|
+
"type": "object",
|
|
250
|
+
"required": ["code", "message", "severity", "path"],
|
|
251
|
+
"properties": {
|
|
252
|
+
"code": { "type": "string" },
|
|
253
|
+
"message": { "type": "string" },
|
|
254
|
+
"severity": { "type": "string" },
|
|
255
|
+
"path": { "type": ["string", "null"] }
|
|
256
|
+
},
|
|
257
|
+
"additionalProperties": false
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|