ommlds 0.0.0.dev496__py3-none-any.whl → 0.0.0.dev498__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.
Files changed (62) hide show
  1. ommlds/.omlish-manifests.json +1 -1
  2. ommlds/cli/main.py +2 -2
  3. ommlds/cli/secrets.py +1 -1
  4. ommlds/minichain/__init__.py +45 -9
  5. ommlds/minichain/_dataclasses.py +1229 -1071
  6. ommlds/minichain/_typedvalues.py +11 -4
  7. ommlds/minichain/chat/content.py +42 -0
  8. ommlds/minichain/chat/messages.py +21 -14
  9. ommlds/minichain/chat/stream/types.py +1 -1
  10. ommlds/minichain/chat/templating.py +3 -3
  11. ommlds/minichain/chat/transforms/metadata.py +3 -3
  12. ommlds/minichain/content/__init__.py +10 -1
  13. ommlds/minichain/content/_marshal.py +30 -17
  14. ommlds/minichain/content/code.py +11 -2
  15. ommlds/minichain/content/composite.py +28 -0
  16. ommlds/minichain/content/{types.py → content.py} +0 -4
  17. ommlds/minichain/content/emphasis.py +27 -0
  18. ommlds/minichain/content/images.py +1 -2
  19. ommlds/minichain/content/json.py +1 -2
  20. ommlds/minichain/content/link.py +13 -0
  21. ommlds/minichain/content/markdown.py +12 -0
  22. ommlds/minichain/content/metadata.py +10 -0
  23. ommlds/minichain/content/namespaces.py +2 -3
  24. ommlds/minichain/content/placeholders.py +2 -3
  25. ommlds/minichain/content/quote.py +12 -3
  26. ommlds/minichain/content/recursive.py +12 -0
  27. ommlds/minichain/content/section.py +16 -4
  28. ommlds/minichain/content/sequence.py +13 -4
  29. ommlds/minichain/content/standard.py +10 -15
  30. ommlds/minichain/content/tag.py +12 -3
  31. ommlds/minichain/content/templates.py +1 -2
  32. ommlds/minichain/content/text.py +1 -2
  33. ommlds/minichain/content/transform/json.py +55 -0
  34. ommlds/minichain/content/transform/markdown.py +8 -0
  35. ommlds/minichain/content/transform/materialize.py +19 -78
  36. ommlds/minichain/content/transform/metadata.py +16 -0
  37. ommlds/minichain/content/transform/prepare.py +1 -5
  38. ommlds/minichain/content/transform/recursive.py +97 -0
  39. ommlds/minichain/content/transform/standard.py +43 -0
  40. ommlds/minichain/content/transform/stringify.py +1 -1
  41. ommlds/minichain/content/transform/strings.py +16 -7
  42. ommlds/minichain/content/transform/templates.py +25 -0
  43. ommlds/minichain/content/visitors.py +231 -0
  44. ommlds/minichain/metadata.py +58 -1
  45. ommlds/minichain/tools/execution/catalog.py +1 -1
  46. ommlds/minichain/tools/execution/errorhandling.py +3 -2
  47. ommlds/minichain/tools/execution/errors.py +1 -1
  48. ommlds/minichain/tools/execution/executors.py +1 -1
  49. ommlds/minichain/tools/fns.py +1 -1
  50. ommlds/minichain/tools/reflect.py +1 -1
  51. ommlds/minichain/tools/types.py +1 -1
  52. ommlds/minichain/vectors/embeddings.py +1 -1
  53. ommlds/tools/git.py +74 -63
  54. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/METADATA +4 -4
  55. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/RECORD +59 -49
  56. ommlds/minichain/content/transform/base.py +0 -63
  57. ommlds/minichain/content/transform/interleave.py +0 -68
  58. ommlds/minichain/content/transform/squeeze.py +0 -59
  59. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/WHEEL +0 -0
  60. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/entry_points.txt +0 -0
  61. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/licenses/LICENSE +0 -0
  62. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/top_level.txt +0 -0
@@ -23,14 +23,21 @@ def _tv_field_coercer(
23
23
  ) -> ta.Callable[[ta.Sequence], tv.TypedValues]:
24
24
  if isinstance(tvc, tuple):
25
25
  check.arg(all(issubclass(e, tv.TypedValue) for e in tvc))
26
+
26
27
  else:
27
28
  check.issubclass(tvc, tv.TypedValue)
28
29
 
29
30
  def inner(seq):
30
- return tv.TypedValues(*[
31
- check.isinstance(e, tvc)
32
- for e in check.isinstance(seq, ta.Sequence)
33
- ])
31
+ if isinstance(seq, tv.TypedValues):
32
+ for e in seq:
33
+ check.isinstance(e, tvc)
34
+ return seq
35
+
36
+ else:
37
+ return tv.TypedValues(*[
38
+ check.isinstance(e, tvc)
39
+ for e in check.isinstance(seq, ta.Sequence)
40
+ ])
34
41
 
35
42
  return inner
36
43
 
@@ -0,0 +1,42 @@
1
+ import typing as ta
2
+
3
+ from ..content.content import Content
4
+ from ..content.transform.strings import transform_content_strings
5
+ from .messages import AiMessage
6
+ from .messages import Message
7
+ from .messages import SystemMessage
8
+ from .messages import ToolUseMessage
9
+ from .messages import ToolUseResultMessage
10
+ from .messages import UserMessage
11
+
12
+
13
+ MessageT = ta.TypeVar('MessageT', bound=Message)
14
+
15
+
16
+ ##
17
+
18
+
19
+ def transform_message_content(fn: ta.Callable[[Content], Content], m: MessageT) -> MessageT:
20
+ if isinstance(m, UserMessage):
21
+ return m.replace(c=fn(m.c))
22
+
23
+ elif isinstance(m, AiMessage):
24
+ return m.replace(c=fn(m.c))
25
+
26
+ elif isinstance(m, SystemMessage):
27
+ return m.replace(c=fn(m.c))
28
+
29
+ elif isinstance(m, ToolUseMessage):
30
+ # TODO: m.tu.args?
31
+ return m
32
+
33
+ elif isinstance(m, ToolUseResultMessage):
34
+ # TODO: m.tur.c?
35
+ return m
36
+
37
+ else:
38
+ raise TypeError(m)
39
+
40
+
41
+ def transform_message_content_strings(fn: ta.Callable[[str], str], m: MessageT) -> MessageT:
42
+ return transform_message_content(lambda c: transform_content_strings(fn, c), m)
@@ -1,3 +1,7 @@
1
+ """
2
+ TODO:
3
+ - channel? reasoning / thinking?
4
+ """
1
5
  import operator
2
6
  import typing as ta
3
7
 
@@ -5,13 +9,12 @@ from omlish import check
5
9
  from omlish import dataclasses as dc
6
10
  from omlish import lang
7
11
  from omlish import marshal as msh
8
- from omlish import typedvalues as tv
9
12
 
10
- from .._typedvalues import _tv_field_metadata
11
- from ..content.types import Content
12
- from ..metadata import MetadataContainer
13
+ from ..content.content import Content
14
+ from ..metadata import MetadataContainerDataclass
13
15
  from ..tools.types import ToolUse
14
16
  from ..tools.types import ToolUseResult
17
+ from .metadata import MessageMetadata
15
18
  from .metadata import MessageMetadatas
16
19
 
17
20
 
@@ -23,7 +26,7 @@ msh.register_global_module_import('._marshal', __package__)
23
26
 
24
27
  @dc.dataclass(frozen=True)
25
28
  class Message( # noqa
26
- MetadataContainer[MessageMetadatas],
29
+ MetadataContainerDataclass[MessageMetadatas],
27
30
  lang.Abstract,
28
31
  lang.Sealed,
29
32
  ):
@@ -31,18 +34,14 @@ class Message( # noqa
31
34
  default=(),
32
35
  kw_only=True,
33
36
  repr=False,
34
- metadata=_tv_field_metadata(
35
- MessageMetadatas,
36
- marshal_name='metadata',
37
- ),
38
37
  )
39
38
 
40
- @property
41
- def metadata(self) -> tv.TypedValues[MessageMetadatas]:
42
- return check.isinstance(self._metadata, tv.TypedValues)
39
+ MetadataContainerDataclass._configure_metadata_field(_metadata, MessageMetadatas) # noqa
43
40
 
44
- def with_metadata(self, *mds: MessageMetadatas, override: bool = False) -> ta.Self:
45
- return dc.replace(self, _metadata=tv.TypedValues(*self._metadata, *mds, override=override))
41
+ def replace(self, **kwargs: ta.Any) -> ta.Self:
42
+ if (n := dc.replace_is_not(self, **kwargs)) is self:
43
+ return self
44
+ return n.update_metadata(MessageOriginal(self), discard=[MessageOriginal], override=True)
46
45
 
47
46
 
48
47
  Chat: ta.TypeAlias = ta.Sequence[Message]
@@ -51,6 +50,14 @@ Chat: ta.TypeAlias = ta.Sequence[Message]
51
50
  ##
52
51
 
53
52
 
53
+ @dc.dataclass(frozen=True)
54
+ class MessageOriginal(MessageMetadata, lang.Final):
55
+ c: Message
56
+
57
+
58
+ ##
59
+
60
+
54
61
  @dc.dataclass(frozen=True)
55
62
  class AnyUserMessage(Message, lang.Abstract):
56
63
  pass
@@ -4,7 +4,7 @@ from omlish import dataclasses as dc
4
4
  from omlish import lang
5
5
  from omlish import marshal as msh
6
6
 
7
- from ...content.types import Content
7
+ from ...content.content import Content
8
8
  from ...stream.services import StreamOptions
9
9
  from ...types import Option
10
10
  from ...types import Output
@@ -10,9 +10,9 @@ from omlish import dataclasses as dc
10
10
  from omlish import lang
11
11
  from omlish.text import templating as tpl
12
12
 
13
- from ..content.transform.strings import transform_content_strings
14
13
  from ..envs import Env
15
14
  from ..envs import EnvKey
15
+ from .content import transform_message_content_strings
16
16
  from .messages import Chat
17
17
  from .messages import Message
18
18
 
@@ -81,7 +81,7 @@ class ChatTemplater:
81
81
  t = self.d[s]
82
82
  return t.render(tpl.Templater.Context(env))
83
83
 
84
- return [transform_content_strings(render_content_str, self.m)]
84
+ return [transform_message_content_strings(render_content_str, self.m)]
85
85
 
86
86
  @dc.dataclass(frozen=True)
87
87
  class _PlaceholderStep(_Step):
@@ -110,7 +110,7 @@ class ChatTemplater:
110
110
  content_strs.add(s)
111
111
  return s
112
112
 
113
- transform_content_strings(visit_content_str, p.v)
113
+ transform_message_content_strings(visit_content_str, p.v)
114
114
 
115
115
  d: dict[str, tpl.Templater] = {
116
116
  s: self._template_factory(s)
@@ -26,7 +26,7 @@ class UuidAddingMessageTransform(MessageTransform):
26
26
 
27
27
  def transform_message(self, m: Message) -> Chat:
28
28
  if Uuid not in m.metadata:
29
- m = m.with_metadata(Uuid(self.uuid_factory()))
29
+ m = m.update_metadata(Uuid(self.uuid_factory()))
30
30
  return [m]
31
31
 
32
32
 
@@ -36,7 +36,7 @@ class CreatedAtAddingMessageTransform(MessageTransform):
36
36
 
37
37
  def transform_message(self, m: Message) -> Chat:
38
38
  if CreatedAt not in m.metadata:
39
- m = m.with_metadata(CreatedAt(self.clock()))
39
+ m = m.update_metadata(CreatedAt(self.clock()))
40
40
  return [m]
41
41
 
42
42
 
@@ -54,6 +54,6 @@ class OriginAddingMessageTransform(MessageTransform):
54
54
 
55
55
  def transform_message(self, m: Message) -> Chat:
56
56
  return [
57
- o.with_metadata(TransformedMessageOrigin(m)) if TransformedMessageOrigin not in o.metadata else m
57
+ o.update_metadata(TransformedMessageOrigin(m)) if TransformedMessageOrigin not in o.metadata else m
58
58
  for o in self.child.transform_message(m)
59
59
  ]
@@ -8,17 +8,26 @@ _msh.register_global_module_import('._marshal', __package__)
8
8
  ##
9
9
 
10
10
 
11
- # This is everything _marshal.py references - it must all be imported before the conditional import.
11
+ # These are all of the Content subtypes - it must all be imported before the conditional import.
12
12
 
13
13
  from . import ( # noqa
14
14
  code as _code,
15
+ composite as _composite,
16
+ content as _content,
15
17
  dynamic as _dynamic,
18
+ emphasis as _emphasis,
16
19
  images as _images,
17
20
  json as _json,
21
+ link as _link,
22
+ markdown as _markdown,
23
+ namespaces as _namespaces,
24
+ placeholders as _placeholders,
18
25
  quote as _quote,
19
26
  raw as _raw,
27
+ recursive as _recursive,
20
28
  section as _section,
21
29
  sequence as _sequence,
30
+ standard as _standard,
22
31
  tag as _tag,
23
32
  templates as _templates,
24
33
  text as _text,
@@ -7,24 +7,28 @@ from omlish import lang
7
7
  from omlish import marshal as msh
8
8
  from omlish import reflect as rfl
9
9
 
10
- from .code import CodeContent # noqa
11
- from .dynamic import DynamicContent # noqa
12
- from .images import ImageContent # noqa
13
- from .json import JsonContent # noqa
14
- from .quote import QuoteContent # noqa
10
+ from .code import BlockCodeContent
11
+ from .code import InlineCodeContent
12
+ from .content import BaseContent
13
+ from .content import Content
14
+ from .emphasis import BoldContent
15
+ from .emphasis import BoldItalicContent
16
+ from .emphasis import ItalicContent
17
+ from .images import ImageContent
18
+ from .json import JsonContent
19
+ from .link import LinkContent
20
+ from .markdown import MarkdownContent
21
+ from .quote import QuoteContent
15
22
  from .raw import NON_STR_SINGLE_RAW_CONTENT_TYPES
16
23
  from .raw import NonStrSingleRawContent
17
- from .raw import RawContent # noqa
18
- from .raw import SingleRawContent # noqa
19
- from .section import SectionContent # noqa
20
- from .sequence import BlockContent # noqa
21
- from .sequence import InlineContent # noqa
22
- from .sequence import ItemListContent # noqa
23
- from .tag import TagContent # noqa
24
- from .text import TextContent # noqa
25
- from .types import CONTENT_TYPES # noqa
26
- from .types import BaseContent
27
- from .types import Content
24
+ from .raw import RawContent
25
+ from .raw import SingleRawContent
26
+ from .section import SectionContent
27
+ from .sequence import BlockContent
28
+ from .sequence import InlineContent
29
+ from .sequence import ItemListContent
30
+ from .tag import TagContent
31
+ from .text import TextContent
28
32
 
29
33
 
30
34
  ##
@@ -234,12 +238,21 @@ def _install_standard_marshaling() -> None:
234
238
  BaseContent,
235
239
  [
236
240
 
237
- msh.Impl(CodeContent, 'code'),
241
+ msh.Impl(InlineCodeContent, 'inline_code'),
242
+ msh.Impl(BlockCodeContent, 'block_code'),
243
+
244
+ msh.Impl(BoldContent, 'bold'),
245
+ msh.Impl(ItalicContent, 'italic'),
246
+ msh.Impl(BoldItalicContent, 'bold_italic'),
238
247
 
239
248
  msh.Impl(ImageContent, 'image'),
240
249
 
241
250
  msh.Impl(JsonContent, 'json'),
242
251
 
252
+ msh.Impl(LinkContent, 'link'),
253
+
254
+ msh.Impl(MarkdownContent, 'markdown'),
255
+
243
256
  msh.Impl(QuoteContent, 'quote'),
244
257
 
245
258
  msh.Impl(SectionContent, 'section'),
@@ -2,16 +2,25 @@ from omlish import dataclasses as dc
2
2
  from omlish import lang
3
3
 
4
4
  from .standard import StandardContent
5
- from .types import LeafContent
6
5
 
7
6
 
8
7
  ##
9
8
 
10
9
 
11
10
  @dc.dataclass(frozen=True)
12
- class CodeContent(StandardContent, LeafContent, lang.Final):
11
+ class CodeContent(StandardContent, lang.Abstract):
13
12
  s: str
14
13
 
15
14
  _: dc.KW_ONLY
16
15
 
17
16
  lang: str | None = None
17
+
18
+
19
+ @dc.dataclass(frozen=True)
20
+ class InlineCodeContent(CodeContent, lang.Final):
21
+ pass
22
+
23
+
24
+ @dc.dataclass(frozen=True)
25
+ class BlockCodeContent(CodeContent, lang.Final):
26
+ pass
@@ -0,0 +1,28 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import dataclasses as dc
5
+ from omlish import lang
6
+
7
+ from .content import Content
8
+ from .standard import StandardContent
9
+
10
+
11
+ ##
12
+
13
+
14
+ @dc.dataclass(frozen=True)
15
+ class CompositeContent(StandardContent, lang.Abstract):
16
+ @abc.abstractmethod
17
+ def child_content(self) -> ta.Sequence[Content]:
18
+ raise NotImplementedError
19
+
20
+ @abc.abstractmethod
21
+ def _replace_child_content(self, new_child_content: ta.Sequence[Content]) -> ta.Self:
22
+ raise NotImplementedError
23
+
24
+ def replace_child_content(self, new_child_content: ta.Sequence[Content]) -> ta.Self:
25
+ if lang.seqs_identical(new_child_content, self.child_content()):
26
+ return self
27
+
28
+ return self._replace_child_content(new_child_content)
@@ -10,10 +10,6 @@ class BaseContent(lang.Abstract, lang.PackageSealed):
10
10
  pass
11
11
 
12
12
 
13
- class LeafContent(BaseContent, lang.Abstract):
14
- pass
15
-
16
-
17
13
  ##
18
14
 
19
15
 
@@ -0,0 +1,27 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from .standard import StandardContent
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True)
11
+ class EmphasisContent(StandardContent, lang.Abstract):
12
+ s: str
13
+
14
+
15
+ @dc.dataclass(frozen=True)
16
+ class BoldContent(EmphasisContent, lang.Final):
17
+ pass
18
+
19
+
20
+ @dc.dataclass(frozen=True)
21
+ class ItalicContent(EmphasisContent, lang.Final):
22
+ pass
23
+
24
+
25
+ @dc.dataclass(frozen=True)
26
+ class BoldItalicContent(EmphasisContent, lang.Final):
27
+ pass
@@ -9,7 +9,6 @@ from omlish import dataclasses as dc
9
9
  from omlish import lang
10
10
 
11
11
  from .standard import StandardContent
12
- from .types import LeafContent
13
12
 
14
13
 
15
14
  if ta.TYPE_CHECKING:
@@ -22,5 +21,5 @@ else:
22
21
 
23
22
 
24
23
  @dc.dataclass(frozen=True)
25
- class ImageContent(StandardContent, LeafContent, lang.Final):
24
+ class ImageContent(StandardContent, lang.Final):
26
25
  i: 'pimg.Image' = dc.field()
@@ -3,12 +3,11 @@ from omlish import lang
3
3
 
4
4
  from ..json import JsonValue
5
5
  from .standard import StandardContent
6
- from .types import LeafContent
7
6
 
8
7
 
9
8
  ##
10
9
 
11
10
 
12
11
  @dc.dataclass(frozen=True)
13
- class JsonContent(StandardContent, LeafContent, lang.Final):
12
+ class JsonContent(StandardContent, lang.Final):
14
13
  v: JsonValue
@@ -0,0 +1,13 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from .standard import StandardContent
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True)
11
+ class LinkContent(StandardContent, lang.Final):
12
+ url: str
13
+ title: str | None = None
@@ -0,0 +1,12 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from .standard import StandardContent
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True)
11
+ class MarkdownContent(StandardContent, lang.Final):
12
+ s: str
@@ -1,9 +1,11 @@
1
1
  import typing as ta
2
2
 
3
+ from omlish import dataclasses as dc
3
4
  from omlish import lang
4
5
 
5
6
  from ..metadata import CommonMetadata
6
7
  from ..metadata import Metadata
8
+ from .content import Content
7
9
 
8
10
 
9
11
  ##
@@ -14,3 +16,11 @@ class ContentMetadata(Metadata, lang.Abstract):
14
16
 
15
17
 
16
18
  ContentMetadatas: ta.TypeAlias = ContentMetadata | CommonMetadata
19
+
20
+
21
+ ##
22
+
23
+
24
+ @dc.dataclass(frozen=True)
25
+ class ContentOriginal(ContentMetadata, lang.Final):
26
+ c: Content
@@ -1,8 +1,7 @@
1
1
  from omlish import dataclasses as dc
2
2
  from omlish import lang
3
3
 
4
- from .dynamic import DynamicContent
5
- from .types import LeafContent
4
+ from .recursive import RecursiveContent
6
5
 
7
6
 
8
7
  ##
@@ -13,5 +12,5 @@ class ContentNamespace(lang.Namespace, lang.Abstract):
13
12
 
14
13
 
15
14
  @dc.dataclass(frozen=True)
16
- class NamespaceContent(DynamicContent, LeafContent, lang.Final):
15
+ class NamespaceContent(RecursiveContent, lang.Final):
17
16
  ns: type[ContentNamespace]
@@ -3,8 +3,7 @@ import typing as ta
3
3
  from omlish import dataclasses as dc
4
4
  from omlish import lang
5
5
 
6
- from .dynamic import DynamicContent
7
- from .types import LeafContent
6
+ from .recursive import RecursiveContent
8
7
 
9
8
 
10
9
  ##
@@ -18,5 +17,5 @@ PlaceholderContentKey: ta.TypeAlias = str | type[ContentPlaceholder]
18
17
 
19
18
 
20
19
  @dc.dataclass(frozen=True)
21
- class PlaceholderContent(DynamicContent, LeafContent, lang.Final):
20
+ class PlaceholderContent(RecursiveContent, lang.Final):
22
21
  k: PlaceholderContentKey
@@ -2,16 +2,25 @@
2
2
  TODO:
3
3
  - attribution
4
4
  """
5
+ import typing as ta
6
+
7
+ from omlish import check
5
8
  from omlish import dataclasses as dc
6
9
  from omlish import lang
7
10
 
8
- from .standard import StandardContent
9
- from .types import Content
11
+ from .composite import CompositeContent
12
+ from .content import Content
10
13
 
11
14
 
12
15
  ##
13
16
 
14
17
 
15
18
  @dc.dataclass(frozen=True)
16
- class QuoteContent(StandardContent, lang.Final):
19
+ class QuoteContent(CompositeContent, lang.Final):
17
20
  body: Content
21
+
22
+ def child_content(self) -> ta.Sequence[Content]:
23
+ return [self.body]
24
+
25
+ def _replace_child_content(self, new_child_content: ta.Sequence[Content]) -> ta.Self:
26
+ return self.replace(body=check.single(new_child_content))
@@ -0,0 +1,12 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from .dynamic import DynamicContent
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True)
11
+ class RecursiveContent(DynamicContent, lang.Abstract):
12
+ pass
@@ -1,14 +1,26 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
1
4
  from omlish import dataclasses as dc
2
5
  from omlish import lang
3
6
 
4
- from .standard import StandardContent
5
- from .types import Content
7
+ from .composite import CompositeContent
8
+ from .content import Content
6
9
 
7
10
 
8
11
  ##
9
12
 
10
13
 
11
14
  @dc.dataclass(frozen=True)
12
- class SectionContent(StandardContent, lang.Final):
13
- header: str
15
+ class SectionContent(CompositeContent, lang.Final):
14
16
  body: Content
17
+
18
+ _: dc.KW_ONLY
19
+
20
+ header: str | None
21
+
22
+ def child_content(self) -> ta.Sequence[Content]:
23
+ return [self.body]
24
+
25
+ def _replace_child_content(self, new_child_content: ta.Sequence[Content]) -> ta.Self:
26
+ return self.replace(body=check.single(new_child_content))
@@ -1,5 +1,6 @@
1
1
  """
2
2
  TODO:
3
+ - ... does inline have *any* separator? generic whitespace when content is stripped?
3
4
  - track BlockContent nesting depth?
4
5
  - section names? dedicated 'section' content with header and body?
5
6
  """
@@ -8,17 +9,23 @@ import typing as ta
8
9
  from omlish import dataclasses as dc
9
10
  from omlish import lang
10
11
 
11
- from .standard import StandardContent
12
- from .types import Content
12
+ from .composite import CompositeContent
13
+ from .content import Content
13
14
 
14
15
 
15
16
  ##
16
17
 
17
18
 
18
19
  @dc.dataclass(frozen=True)
19
- class SequenceContent(StandardContent, lang.Abstract):
20
+ class SequenceContent(CompositeContent, lang.Abstract):
20
21
  l: ta.Sequence[Content]
21
22
 
23
+ def child_content(self) -> ta.Sequence[Content]:
24
+ return self.l
25
+
26
+ def _replace_child_content(self, new_child_content: ta.Sequence[Content]) -> ta.Self:
27
+ return self.replace(l=new_child_content)
28
+
22
29
 
23
30
  @dc.dataclass(frozen=True)
24
31
  class InlineContent(SequenceContent, lang.Final):
@@ -32,4 +39,6 @@ class BlockContent(SequenceContent, lang.Final):
32
39
 
33
40
  @dc.dataclass(frozen=True)
34
41
  class ItemListContent(SequenceContent, lang.Final):
35
- pass
42
+ _: dc.KW_ONLY
43
+
44
+ style: ta.Literal['-', '#'] = '-'