hammad-python 0.0.14__py3-none-any.whl → 0.0.15__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 (101) hide show
  1. hammad_python-0.0.15.dist-info/METADATA +184 -0
  2. hammad_python-0.0.15.dist-info/RECORD +4 -0
  3. hammad/__init__.py +0 -1
  4. hammad/ai/__init__.py +0 -1
  5. hammad/ai/_utils.py +0 -142
  6. hammad/ai/completions/__init__.py +0 -45
  7. hammad/ai/completions/client.py +0 -684
  8. hammad/ai/completions/create.py +0 -710
  9. hammad/ai/completions/settings.py +0 -100
  10. hammad/ai/completions/types.py +0 -792
  11. hammad/ai/completions/utils.py +0 -486
  12. hammad/ai/embeddings/__init__.py +0 -35
  13. hammad/ai/embeddings/client/__init__.py +0 -1
  14. hammad/ai/embeddings/client/base_embeddings_client.py +0 -26
  15. hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +0 -200
  16. hammad/ai/embeddings/client/litellm_embeddings_client.py +0 -288
  17. hammad/ai/embeddings/create.py +0 -159
  18. hammad/ai/embeddings/types.py +0 -69
  19. hammad/cache/__init__.py +0 -40
  20. hammad/cache/base_cache.py +0 -181
  21. hammad/cache/cache.py +0 -169
  22. hammad/cache/decorators.py +0 -261
  23. hammad/cache/file_cache.py +0 -80
  24. hammad/cache/ttl_cache.py +0 -74
  25. hammad/cli/__init__.py +0 -33
  26. hammad/cli/animations.py +0 -573
  27. hammad/cli/plugins.py +0 -781
  28. hammad/cli/styles/__init__.py +0 -55
  29. hammad/cli/styles/settings.py +0 -139
  30. hammad/cli/styles/types.py +0 -358
  31. hammad/cli/styles/utils.py +0 -480
  32. hammad/data/__init__.py +0 -56
  33. hammad/data/collections/__init__.py +0 -34
  34. hammad/data/collections/base_collection.py +0 -58
  35. hammad/data/collections/collection.py +0 -452
  36. hammad/data/collections/searchable_collection.py +0 -556
  37. hammad/data/collections/vector_collection.py +0 -596
  38. hammad/data/configurations/__init__.py +0 -35
  39. hammad/data/configurations/configuration.py +0 -564
  40. hammad/data/databases/__init__.py +0 -21
  41. hammad/data/databases/database.py +0 -902
  42. hammad/data/models/__init__.py +0 -44
  43. hammad/data/models/base/__init__.py +0 -35
  44. hammad/data/models/base/fields.py +0 -546
  45. hammad/data/models/base/model.py +0 -1078
  46. hammad/data/models/base/utils.py +0 -280
  47. hammad/data/models/pydantic/__init__.py +0 -55
  48. hammad/data/models/pydantic/converters.py +0 -632
  49. hammad/data/models/pydantic/models/__init__.py +0 -28
  50. hammad/data/models/pydantic/models/arbitrary_model.py +0 -46
  51. hammad/data/models/pydantic/models/cacheable_model.py +0 -79
  52. hammad/data/models/pydantic/models/fast_model.py +0 -318
  53. hammad/data/models/pydantic/models/function_model.py +0 -176
  54. hammad/data/models/pydantic/models/subscriptable_model.py +0 -63
  55. hammad/data/types/__init__.py +0 -41
  56. hammad/data/types/file.py +0 -358
  57. hammad/data/types/multimodal/__init__.py +0 -24
  58. hammad/data/types/multimodal/audio.py +0 -96
  59. hammad/data/types/multimodal/image.py +0 -80
  60. hammad/data/types/text.py +0 -1066
  61. hammad/formatting/__init__.py +0 -38
  62. hammad/formatting/json/__init__.py +0 -21
  63. hammad/formatting/json/converters.py +0 -152
  64. hammad/formatting/text/__init__.py +0 -63
  65. hammad/formatting/text/converters.py +0 -723
  66. hammad/formatting/text/markdown.py +0 -131
  67. hammad/formatting/yaml/__init__.py +0 -26
  68. hammad/formatting/yaml/converters.py +0 -5
  69. hammad/logging/__init__.py +0 -35
  70. hammad/logging/decorators.py +0 -834
  71. hammad/logging/logger.py +0 -954
  72. hammad/mcp/__init__.py +0 -50
  73. hammad/mcp/client/__init__.py +0 -1
  74. hammad/mcp/client/client.py +0 -523
  75. hammad/mcp/client/client_service.py +0 -393
  76. hammad/mcp/client/settings.py +0 -178
  77. hammad/mcp/servers/__init__.py +0 -1
  78. hammad/mcp/servers/launcher.py +0 -1161
  79. hammad/performance/__init__.py +0 -36
  80. hammad/performance/imports.py +0 -231
  81. hammad/performance/runtime/__init__.py +0 -32
  82. hammad/performance/runtime/decorators.py +0 -142
  83. hammad/performance/runtime/run.py +0 -299
  84. hammad/py.typed +0 -0
  85. hammad/service/__init__.py +0 -49
  86. hammad/service/create.py +0 -532
  87. hammad/service/decorators.py +0 -285
  88. hammad/typing/__init__.py +0 -407
  89. hammad/web/__init__.py +0 -43
  90. hammad/web/http/__init__.py +0 -1
  91. hammad/web/http/client.py +0 -944
  92. hammad/web/models.py +0 -245
  93. hammad/web/openapi/__init__.py +0 -1
  94. hammad/web/openapi/client.py +0 -740
  95. hammad/web/search/__init__.py +0 -1
  96. hammad/web/search/client.py +0 -988
  97. hammad/web/utils.py +0 -472
  98. hammad_python-0.0.14.dist-info/METADATA +0 -70
  99. hammad_python-0.0.14.dist-info/RECORD +0 -99
  100. {hammad_python-0.0.14.dist-info → hammad_python-0.0.15.dist-info}/WHEEL +0 -0
  101. {hammad_python-0.0.14.dist-info → hammad_python-0.0.15.dist-info}/licenses/LICENSE +0 -0
hammad/data/types/text.py DELETED
@@ -1,1066 +0,0 @@
1
- """hammad.types.text
2
-
3
- Contains the `BaseText` type, which is a functional type & object
4
- for created intelligently rendered strings and markdown strings
5
- from various input types and objects."""
6
-
7
- from __future__ import annotations
8
-
9
- import json
10
- from abc import ABC
11
- from dataclasses import dataclass, field
12
- from enum import Enum
13
- from typing import (
14
- Any,
15
- Dict,
16
- List,
17
- Literal,
18
- Optional,
19
- Union,
20
- Callable,
21
- Type,
22
- ClassVar,
23
- overload,
24
- TypeVar,
25
- Generic,
26
- )
27
-
28
- from ...formatting.text.markdown import (
29
- markdown_heading,
30
- markdown_code_block,
31
- )
32
- from ...formatting.text.converters import convert_to_text
33
-
34
-
35
- # -----------------------------------------------------------------------------
36
- # Enums and Types
37
- # -----------------------------------------------------------------------------
38
-
39
-
40
- class OutputFormat(Enum):
41
- """Supported output formats for text conversion."""
42
-
43
- TEXT = "text"
44
- MARKDOWN = "markdown"
45
- JSON = "json"
46
- TYPE = "type"
47
- ANY = "any"
48
-
49
-
50
- class HeadingStyle(Enum):
51
- """Heading styles for different formats."""
52
-
53
- # Text formats
54
- HASH = "#"
55
- BRACKET = "[]"
56
- ANGLE = "<>"
57
- BRACE = "{}"
58
- # Markdown specific
59
- UNDERLINE = "="
60
-
61
-
62
- # -----------------------------------------------------------------------------
63
- # Base Text Class (Unified Type System)
64
- # -----------------------------------------------------------------------------
65
-
66
-
67
- @dataclass(repr=False, eq=False)
68
- class BaseText(ABC):
69
- """
70
- Abstract base class for structured text conversion.
71
-
72
- This class provides a unified interface for converting objects
73
- to various text formats with extensive customization options.
74
- All sections are also BaseText instances, creating a unified type system.
75
- """
76
-
77
- # Class-level configuration
78
- DEFAULT_FORMAT: ClassVar[OutputFormat] = OutputFormat.TEXT
79
- SUPPORTED_FORMATS: ClassVar[List[OutputFormat]] = [
80
- OutputFormat.TEXT,
81
- OutputFormat.MARKDOWN,
82
- OutputFormat.JSON,
83
- OutputFormat.TYPE,
84
- OutputFormat.ANY,
85
- ]
86
-
87
- # Core attributes (both for documents and sections)
88
- type: str = "base"
89
- """The type identifier for this text object/section."""
90
-
91
- title: Optional[str] = None
92
- """Title for the document or section."""
93
-
94
- description: Optional[str] = None
95
- """Description for the document or section."""
96
-
97
- content: Optional[Union[str, Any]] = ""
98
- """The main content (for sections)."""
99
-
100
- metadata: Dict[str, Any] = field(default_factory=dict)
101
- """Metadata for the document/section."""
102
-
103
- language: Optional[str] = None
104
- """Programming language for code sections (if applicable)."""
105
-
106
- # Hierarchical structure
107
- sections: List[BaseText] = field(default_factory=list)
108
- """Child sections (all are BaseText instances)."""
109
-
110
- # Formatting options
111
- heading_level: int = 2
112
- """Heading level (1-6) for this section."""
113
-
114
- show_in_toc: bool = True
115
- """Whether to include in table of contents."""
116
-
117
- collapsible: bool = False
118
- """Whether this section should be collapsible (for supported formats)."""
119
-
120
- format_config: Dict[OutputFormat, Dict[str, Any]] = field(default_factory=dict)
121
- """Format-specific configuration options."""
122
-
123
- def build_sections(self) -> List[BaseText]:
124
- """
125
- Build and return the sections for this text object.
126
- Default implementation returns existing sections.
127
- Subclasses can override to dynamically build sections.
128
- """
129
- return self.sections
130
-
131
- def add_section(self, section: BaseText) -> BaseText:
132
- """Add a section to this text object."""
133
- self.sections.append(section)
134
- return self
135
-
136
- def get_format_config(self, format: OutputFormat) -> Dict[str, Any]:
137
- """Get configuration for a specific format."""
138
- default_config = {
139
- OutputFormat.TEXT: {
140
- "compact": False,
141
- "show_types": True,
142
- "title_style": "##",
143
- "bullet_style": "-",
144
- },
145
- OutputFormat.MARKDOWN: {
146
- "table_format": False,
147
- "escape_special_chars": False,
148
- "add_toc": False,
149
- "add_horizontal_rules": False,
150
- },
151
- OutputFormat.JSON: {
152
- "indent": 2,
153
- "sort_keys": False,
154
- },
155
- OutputFormat.TYPE: {
156
- "show_full_path": True,
157
- "include_module": True,
158
- },
159
- OutputFormat.ANY: {
160
- "fallback_format": OutputFormat.TEXT,
161
- "auto_detect": True,
162
- },
163
- }
164
-
165
- config = default_config.get(format, {}).copy()
166
- config.update(self.format_config.get(format, {}))
167
- return config
168
-
169
- def to_format(self, format: OutputFormat = None, **kwargs) -> str:
170
- """
171
- Convert to the specified format.
172
-
173
- Args:
174
- format: The output format (defaults to DEFAULT_FORMAT)
175
- **kwargs: Additional format-specific options
176
-
177
- Returns:
178
- Formatted string representation
179
- """
180
- if format is None:
181
- format = self.DEFAULT_FORMAT
182
-
183
- if format not in self.SUPPORTED_FORMATS:
184
- raise ValueError(f"Unsupported format: {format}")
185
-
186
- # Ensure sections are built
187
- if not self.sections:
188
- self.sections = self.build_sections()
189
-
190
- # Merge configurations
191
- config = self.get_format_config(format)
192
- config.update(kwargs)
193
-
194
- # Convert based on format
195
- if format == OutputFormat.TEXT:
196
- return self._to_text(**config)
197
- elif format == OutputFormat.MARKDOWN:
198
- return self._to_markdown(**config)
199
- elif format == OutputFormat.JSON:
200
- return self._to_json(**config)
201
- elif format == OutputFormat.TYPE:
202
- return self._to_type(**config)
203
- elif format == OutputFormat.ANY:
204
- return self._to_any(**config)
205
- else:
206
- raise NotImplementedError(f"Format {format} not implemented")
207
-
208
- def _to_text(self, **kwargs) -> str:
209
- """Convert to plain text format."""
210
- parts = []
211
-
212
- # Handle title
213
- if self.title:
214
- title_style = kwargs.get("title_style", "##")
215
- if title_style == "#":
216
- parts.append("#" * self.heading_level + " " + self.title)
217
- elif title_style == "[]":
218
- parts.append(f"[{self.title}]")
219
- elif title_style == "<>":
220
- parts.append(f"<{self.title}>")
221
- elif title_style == "{}":
222
- parts.append(f"{{{self.title}}}")
223
- else:
224
- parts.append(self.title)
225
-
226
- # Handle description
227
- if self.description:
228
- parts.append(self.description)
229
-
230
- # Handle content (for sections)
231
- if self.content:
232
- if isinstance(self.content, str):
233
- parts.append(self.content)
234
- else:
235
- parts.append(convert_to_text(self.content, **kwargs))
236
-
237
- # Handle subsections
238
- for section in self.sections:
239
- sub_kwargs = kwargs.copy()
240
- sub_kwargs["indent"] = kwargs.get("indent", 0) + 1
241
- parts.append(section.to_format(OutputFormat.TEXT, **sub_kwargs))
242
-
243
- return "\n\n".join(filter(None, parts))
244
-
245
- def _to_markdown(self, **kwargs) -> str:
246
- """Convert to Markdown format."""
247
- parts = []
248
-
249
- # Handle title
250
- if self.title:
251
- parts.append(markdown_heading(self.title, self.heading_level))
252
-
253
- # Handle description
254
- if self.description:
255
- parts.append(self.description)
256
-
257
- # Handle content (for sections)
258
- if self.content:
259
- if isinstance(self.content, str):
260
- parts.append(self.content)
261
- else:
262
- parts.append(convert_to_text(self.content, **kwargs))
263
-
264
- # Add table of contents if requested (only for top-level documents)
265
- if kwargs.get("add_toc", False) and self.heading_level == 1:
266
- toc_headings = []
267
- for section in self.sections:
268
- if section.show_in_toc and section.title:
269
- toc_headings.append((section.heading_level, section.title))
270
-
271
- if toc_headings:
272
- from ...formatting.text.markdown import markdown_table
273
-
274
- parts.append(markdown_table(toc_headings))
275
-
276
- # Handle subsections
277
- for section in self.sections:
278
- sub_kwargs = kwargs.copy()
279
- sub_kwargs["_indent_level"] = self.heading_level
280
- parts.append(section.to_format(OutputFormat.MARKDOWN, **sub_kwargs))
281
-
282
- return "\n\n".join(filter(None, parts))
283
-
284
- def _to_json(self, **kwargs) -> str:
285
- """Convert to JSON format."""
286
- data = {
287
- "type": self.type,
288
- "title": self.title,
289
- "description": self.description,
290
- "content": self.content,
291
- "metadata": self.metadata,
292
- "sections": [
293
- json.loads(s.to_format(OutputFormat.JSON, **kwargs))
294
- for s in self.sections
295
- ],
296
- }
297
-
298
- indent = kwargs.get("indent", 2)
299
- sort_keys = kwargs.get("sort_keys", False)
300
-
301
- return json.dumps(data, indent=indent, sort_keys=sort_keys)
302
-
303
- def _to_type(self, **kwargs) -> str:
304
- """Convert to type annotation format."""
305
- show_full_path = kwargs.get("show_full_path", True)
306
- include_module = kwargs.get("include_module", True)
307
-
308
- type_info = self.__class__.__name__
309
-
310
- if include_module:
311
- module = self.__class__.__module__
312
- if module != "__main__" and show_full_path:
313
- type_info = f"{module}.{type_info}"
314
-
315
- # Include key attributes in the type representation
316
- attrs = []
317
- if self.type != "base":
318
- attrs.append(f"type={self.type!r}")
319
- if self.title:
320
- attrs.append(f"title={self.title!r}")
321
- if self.sections:
322
- attrs.append(f"sections={len(self.sections)}")
323
-
324
- if attrs:
325
- type_info += f"({', '.join(attrs)})"
326
-
327
- return type_info
328
-
329
- def _to_any(self, **kwargs) -> str:
330
- """Convert using automatic format detection or fallback."""
331
- fallback_format = kwargs.get("fallback_format", OutputFormat.TEXT)
332
- auto_detect = kwargs.get("auto_detect", True)
333
-
334
- if auto_detect:
335
- # Simple heuristics for format detection
336
- if self.content and isinstance(self.content, str):
337
- content_lower = self.content.lower().strip()
338
-
339
- # Check for JSON content
340
- if content_lower.startswith("{") and content_lower.endswith("}"):
341
- return self.to_format(OutputFormat.JSON, **kwargs)
342
-
343
- # Check for code content
344
- if any(
345
- keyword in content_lower
346
- for keyword in ["def ", "class ", "import ", "from "]
347
- ):
348
- return self.to_format(OutputFormat.MARKDOWN, **kwargs)
349
-
350
- # Check if this looks like schema documentation
351
- if hasattr(self, "output_schema") or self.type in ["schema", "output"]:
352
- return self.to_format(OutputFormat.MARKDOWN, **kwargs)
353
-
354
- # Use fallback format
355
- return self.to_format(fallback_format, **kwargs)
356
-
357
- def to_dict(self) -> Dict[str, Any]:
358
- """Convert to dictionary representation."""
359
- return {
360
- "type": self.type,
361
- "title": self.title,
362
- "description": self.description,
363
- "content": self.content,
364
- "metadata": self.metadata,
365
- "sections": [s.to_dict() for s in self.sections],
366
- }
367
-
368
- # Convenience methods
369
- def __str__(self) -> str:
370
- """String representation using default format."""
371
- return self.to_format()
372
-
373
- def __repr__(self) -> str:
374
- """Developer-friendly representation."""
375
- section_count = len(self.sections)
376
- section_text = "section" if section_count == 1 else "sections"
377
- if section_count > 0:
378
- return f"{self.__class__.__name__}(type={self.type!r}, title={self.title!r}, {section_count} {section_text})"
379
- else:
380
- return f"{self.__class__.__name__}(type={self.type!r}, title={self.title!r}, sections={section_count})"
381
-
382
- @property
383
- def text(self) -> str:
384
- """Quick access to text format."""
385
- return self.to_format(OutputFormat.TEXT)
386
-
387
- @property
388
- def markdown(self) -> str:
389
- """Quick access to markdown format."""
390
- return self.to_format(OutputFormat.MARKDOWN)
391
-
392
- @property
393
- def json(self) -> str:
394
- """Quick access to JSON format."""
395
- return self.to_format(OutputFormat.JSON)
396
-
397
- @property
398
- def type_info(self) -> str:
399
- """Quick access to type format."""
400
- return self.to_format(OutputFormat.TYPE)
401
-
402
- @property
403
- def any_format(self) -> str:
404
- """Quick access to any format (auto-detected)."""
405
- return self.to_format(OutputFormat.ANY)
406
-
407
- def __len__(self) -> int:
408
- """Return the length of the content."""
409
- if self.content is None:
410
- return 0
411
- return len(str(self.content))
412
-
413
- def __eq__(self, other) -> bool:
414
- """Check equality based on content."""
415
- if not isinstance(other, BaseText):
416
- return False
417
- return str(self.content or "") == str(other.content or "")
418
-
419
-
420
- # -----------------------------------------------------------------------------
421
- # Specialized Section Classes (Now BaseText Subclasses)
422
- # -----------------------------------------------------------------------------
423
-
424
-
425
- @dataclass(repr=False, eq=False)
426
- class CodeSection(BaseText):
427
- """Section specifically for code content."""
428
-
429
- type: str = "code"
430
- line_numbers: bool = False
431
- highlight_lines: Optional[List[int]] = None
432
-
433
- def _to_markdown(self, **kwargs) -> str:
434
- """Convert to Markdown with code block."""
435
- parts = []
436
-
437
- if self.title:
438
- parts.append(markdown_heading(self.title, self.heading_level))
439
-
440
- if self.description:
441
- parts.append(self.description)
442
-
443
- if self.content:
444
- parts.append(markdown_code_block(str(self.content), self.language or ""))
445
-
446
- # Handle subsections
447
- for section in self.sections:
448
- sub_kwargs = kwargs.copy()
449
- sub_kwargs["_indent_level"] = self.heading_level
450
- parts.append(section.to_format(OutputFormat.MARKDOWN, **sub_kwargs))
451
-
452
- return "\n\n".join(filter(None, parts))
453
-
454
-
455
- @dataclass(repr=False, eq=False)
456
- class SchemaSection(BaseText):
457
- """Section for schema/model documentation."""
458
-
459
- type: str = "schema"
460
- schema_object: Optional[Any] = None
461
- show_examples: bool = True
462
- table_format: bool = True
463
-
464
- def _to_markdown(self, **kwargs) -> str:
465
- """Convert schema to Markdown documentation."""
466
- if self.schema_object:
467
- return convert_to_text(
468
- self.schema_object,
469
- name=self.title,
470
- description=self.description,
471
- table_format=self.table_format,
472
- **kwargs,
473
- )
474
- return super()._to_markdown(**kwargs)
475
-
476
-
477
- # -----------------------------------------------------------------------------
478
- # Concrete Implementation Classes
479
- # -----------------------------------------------------------------------------
480
-
481
-
482
- @dataclass(repr=False, eq=False)
483
- class SimpleText(BaseText):
484
- """Simple concrete implementation of BaseText for basic use cases."""
485
-
486
- type: str = "simple"
487
-
488
- def build_sections(self) -> List[BaseText]:
489
- """Simple text doesn't build sections dynamically."""
490
- return self.sections
491
-
492
-
493
- @dataclass(repr=False, eq=False)
494
- class OutputText(BaseText):
495
- """
496
- Implementation for structured output documentation.
497
- """
498
-
499
- DEFAULT_FORMAT: ClassVar[OutputFormat] = OutputFormat.MARKDOWN
500
- type: str = "output"
501
-
502
- # Specific attributes for output documentation
503
- output_schema: Optional[Any] = None
504
- """The schema/model to document."""
505
-
506
- examples: List[Dict[str, Any]] = field(default_factory=list)
507
- """Example outputs."""
508
-
509
- validation_rules: List[str] = field(default_factory=list)
510
- """Validation rules for the output."""
511
-
512
- error_cases: List[Dict[str, str]] = field(default_factory=list)
513
- """Common error cases and messages."""
514
-
515
- def build_sections(self) -> List[BaseText]:
516
- """Build sections for output documentation."""
517
- sections = []
518
-
519
- # Schema section
520
- if self.output_schema:
521
- sections.append(
522
- SchemaSection(
523
- title="Output Schema",
524
- schema_object=self.output_schema,
525
- description="The following schema defines the structure of the output:",
526
- table_format=True,
527
- )
528
- )
529
-
530
- # Examples section
531
- if self.examples:
532
- examples_section = SimpleText(
533
- type="examples",
534
- title="Examples",
535
- description="Here are some example outputs:",
536
- )
537
-
538
- for i, example in enumerate(self.examples, 1):
539
- examples_section.add_section(
540
- CodeSection(
541
- title=f"Example {i}",
542
- content=json.dumps(example, indent=2),
543
- language="json",
544
- heading_level=3,
545
- )
546
- )
547
-
548
- sections.append(examples_section)
549
-
550
- # Validation rules section
551
- if self.validation_rules:
552
- rules_content = "\n".join(f"- {rule}" for rule in self.validation_rules)
553
- sections.append(
554
- SimpleText(
555
- type="validation",
556
- title="Validation Rules",
557
- content=rules_content,
558
- )
559
- )
560
-
561
- # Error cases section
562
- if self.error_cases:
563
- error_section = SimpleText(
564
- type="errors",
565
- title="Common Errors",
566
- description="The following errors may occur:",
567
- )
568
-
569
- for error in self.error_cases:
570
- error_content = f"**Error**: {error.get('error', 'Unknown')}\n"
571
- error_content += f"**Message**: {error.get('message', 'No message')}\n"
572
- if "solution" in error:
573
- error_content += f"**Solution**: {error['solution']}"
574
-
575
- error_section.add_section(
576
- SimpleText(
577
- type="error",
578
- title=error.get("code", "ERROR"),
579
- content=error_content,
580
- heading_level=3,
581
- )
582
- )
583
-
584
- sections.append(error_section)
585
-
586
- return sections
587
-
588
- @classmethod
589
- def from_function(
590
- cls, func: Callable, include_examples: bool = True, **kwargs
591
- ) -> OutputText:
592
- """
593
- Create OutputText from a function's return type and docstring.
594
-
595
- Args:
596
- func: The function to document
597
- include_examples: Whether to parse examples from docstring
598
- **kwargs: Additional arguments for OutputText
599
-
600
- Returns:
601
- OutputText instance
602
- """
603
- from typing import get_type_hints
604
-
605
- # Extract function information
606
- func_name = func.__name__
607
- hints = get_type_hints(func)
608
- return_type = hints.get("return", Any)
609
-
610
- # Create instance
611
- output_text = cls(
612
- title=kwargs.get("title", f"Output for {func_name}"),
613
- description=kwargs.get("description", None),
614
- output_schema=return_type,
615
- **kwargs,
616
- )
617
-
618
- return output_text
619
-
620
-
621
- # -----------------------------------------------------------------------------
622
- # Example Usage
623
- # -----------------------------------------------------------------------------
624
-
625
- if __name__ == "__main__":
626
- from dataclasses import dataclass
627
- from typing import Optional
628
-
629
- # Define a sample schema
630
- @dataclass
631
- class UserResponse:
632
- """User information response."""
633
-
634
- id: int
635
- username: str
636
- email: str
637
- is_active: bool = True
638
- role: Optional[str] = None
639
-
640
- # Create output documentation
641
- output_doc = OutputText(
642
- title="User API Response",
643
- description="Documentation for the user endpoint response format.",
644
- output_schema=UserResponse,
645
- examples=[
646
- {
647
- "id": 123,
648
- "username": "john_doe",
649
- "email": "john@example.com",
650
- "is_active": True,
651
- "role": "admin",
652
- },
653
- {
654
- "id": 456,
655
- "username": "jane_smith",
656
- "email": "jane@example.com",
657
- "is_active": False,
658
- "role": None,
659
- },
660
- ],
661
- validation_rules=[
662
- "ID must be a positive integer",
663
- "Username must be unique and contain only alphanumeric characters and underscores",
664
- "Email must be a valid email address",
665
- "Role must be one of: admin, user, guest (or null)",
666
- ],
667
- error_cases=[
668
- {
669
- "code": "USER_NOT_FOUND",
670
- "error": "User not found",
671
- "message": "The requested user ID does not exist",
672
- "solution": "Verify the user ID and try again",
673
- },
674
- {
675
- "code": "INVALID_EMAIL",
676
- "error": "Invalid email format",
677
- "message": "The provided email address is not valid",
678
- "solution": "Ensure the email follows the format: user@domain.com",
679
- },
680
- ],
681
- )
682
-
683
- # Get different formats
684
- print("=== MARKDOWN FORMAT ===")
685
- print(output_doc.markdown)
686
-
687
- print("\n\n=== TEXT FORMAT ===")
688
- print(output_doc.text)
689
-
690
- print("\n\n=== JSON FORMAT ===")
691
- print(output_doc.json)
692
-
693
- print("\n\n=== TYPE FORMAT ===")
694
- print(output_doc.type_info)
695
-
696
- print("\n\n=== ANY FORMAT (auto-detected) ===")
697
- print(output_doc.any_format)
698
-
699
-
700
- # -----------------------------------------------------------------------------
701
- # Unified Text Class - Main Entry Point
702
- # -----------------------------------------------------------------------------
703
-
704
- T = TypeVar("T")
705
-
706
-
707
- @dataclass(repr=False, eq=False)
708
- class Text(BaseText, Generic[T]):
709
- """
710
- Unified Text class - the main entry point for all text operations.
711
-
712
- This class provides a clean, fully-typed interface for creating and managing
713
- structured text content with support for multiple output formats.
714
- """
715
-
716
- DEFAULT_FORMAT: ClassVar[OutputFormat] = OutputFormat.MARKDOWN
717
- type: str = "text"
718
-
719
- # Enhanced typing for content
720
- content: Optional[Union[str, T, Any]] = None
721
-
722
- def __init__(
723
- self,
724
- content: Optional[Union[str, T, Any]] = None,
725
- *,
726
- title: Optional[str] = None,
727
- description: Optional[str] = None,
728
- type: str = "text",
729
- format: Optional[OutputFormat] = None,
730
- heading_level: int = 1,
731
- show_in_toc: bool = True,
732
- collapsible: bool = False,
733
- metadata: Optional[Dict[str, Any]] = None,
734
- sections: Optional[List[BaseText]] = None,
735
- format_config: Optional[Dict[OutputFormat, Dict[str, Any]]] = None,
736
- **kwargs,
737
- ) -> None:
738
- """
739
- Initialize a Text instance.
740
-
741
- Args:
742
- content: The main content (string, object, or any serializable type)
743
- title: Optional title for the text
744
- description: Optional description
745
- type: Type identifier (default: "text")
746
- format: Default output format
747
- heading_level: Heading level (1-6)
748
- show_in_toc: Whether to show in table of contents
749
- collapsible: Whether the section should be collapsible
750
- metadata: Additional metadata
751
- sections: Child sections
752
- format_config: Format-specific configuration
753
- **kwargs: Additional arguments
754
- """
755
- super().__init__(
756
- type=type,
757
- title=title,
758
- description=description,
759
- content=content,
760
- metadata=metadata or {},
761
- sections=sections or [],
762
- heading_level=heading_level,
763
- show_in_toc=show_in_toc,
764
- collapsible=collapsible,
765
- format_config=format_config or {},
766
- )
767
-
768
- # Set default format if provided
769
- if format is not None:
770
- self.DEFAULT_FORMAT = format
771
-
772
- @classmethod
773
- def from_string(
774
- cls,
775
- text: str,
776
- *,
777
- title: Optional[str] = None,
778
- format: OutputFormat = OutputFormat.TEXT,
779
- **kwargs,
780
- ) -> "Text[str]":
781
- """Create Text from a simple string."""
782
- return cls(content=text, title=title, format=format, **kwargs)
783
-
784
- @classmethod
785
- def from_markdown(
786
- cls, markdown: str, *, title: Optional[str] = None, **kwargs
787
- ) -> "Text[str]":
788
- """Create Text from markdown content."""
789
- return cls(
790
- content=markdown, title=title, format=OutputFormat.MARKDOWN, **kwargs
791
- )
792
-
793
- @classmethod
794
- def from_object(
795
- cls,
796
- obj: T,
797
- *,
798
- title: Optional[str] = None,
799
- format: OutputFormat = OutputFormat.MARKDOWN,
800
- **kwargs,
801
- ) -> "Text[T]":
802
- """Create Text from any object."""
803
- return cls(
804
- content=obj,
805
- title=title or f"{type(obj).__name__} Documentation",
806
- format=format,
807
- **kwargs,
808
- )
809
-
810
- @classmethod
811
- def from_schema(
812
- cls,
813
- schema: Type[T],
814
- *,
815
- title: Optional[str] = None,
816
- examples: Optional[List[Dict[str, Any]]] = None,
817
- **kwargs,
818
- ) -> "Text[Type[T]]":
819
- """Create Text from a schema/dataclass type."""
820
- output_text = OutputText(
821
- title=title or f"{schema.__name__} Schema",
822
- output_schema=schema,
823
- examples=examples or [],
824
- **kwargs,
825
- )
826
- return cls(
827
- content=output_text,
828
- title=output_text.title,
829
- format=OutputFormat.MARKDOWN,
830
- type="schema",
831
- )
832
-
833
- @classmethod
834
- def from_function(
835
- cls,
836
- func: Callable[..., T],
837
- *,
838
- title: Optional[str] = None,
839
- include_examples: bool = True,
840
- **kwargs,
841
- ) -> "Text[Callable[..., T]]":
842
- """Create Text from a function's documentation."""
843
- output_text = OutputText.from_function(
844
- func, title=title, include_examples=include_examples, **kwargs
845
- )
846
- return cls(
847
- content=output_text,
848
- title=output_text.title,
849
- format=OutputFormat.MARKDOWN,
850
- type="function",
851
- )
852
-
853
- def add_code_section(
854
- self,
855
- code: str,
856
- *,
857
- language: str = "python",
858
- title: Optional[str] = None,
859
- description: Optional[str] = None,
860
- line_numbers: bool = False,
861
- **kwargs,
862
- ) -> "Text[T]":
863
- """Add a code section to this text."""
864
- code_section = CodeSection(
865
- content=code,
866
- language=language,
867
- title=title,
868
- description=description,
869
- line_numbers=line_numbers,
870
- heading_level=self.heading_level + 1,
871
- **kwargs,
872
- )
873
- self.add_section(code_section)
874
- return self
875
-
876
- def add_text_section(
877
- self,
878
- content: Union[str, Any],
879
- *,
880
- title: Optional[str] = None,
881
- description: Optional[str] = None,
882
- **kwargs,
883
- ) -> "Text[T]":
884
- """Add a text section to this text."""
885
- text_section = SimpleText(
886
- content=content,
887
- title=title,
888
- description=description,
889
- heading_level=self.heading_level + 1,
890
- **kwargs,
891
- )
892
- self.add_section(text_section)
893
- return self
894
-
895
- def add_schema_section(
896
- self,
897
- schema: Any,
898
- *,
899
- title: Optional[str] = None,
900
- description: Optional[str] = None,
901
- table_format: bool = True,
902
- **kwargs,
903
- ) -> "Text[T]":
904
- """Add a schema documentation section."""
905
- schema_section = SchemaSection(
906
- schema_object=schema,
907
- title=title,
908
- description=description,
909
- table_format=table_format,
910
- heading_level=self.heading_level + 1,
911
- **kwargs,
912
- )
913
- self.add_section(schema_section)
914
- return self
915
-
916
- @overload
917
- def render(self, format: Literal[OutputFormat.TEXT]) -> str: ...
918
-
919
- @overload
920
- def render(self, format: Literal[OutputFormat.MARKDOWN]) -> str: ...
921
-
922
- @overload
923
- def render(self, format: Literal[OutputFormat.JSON]) -> str: ...
924
-
925
- @overload
926
- def render(self, format: Literal[OutputFormat.TYPE]) -> str: ...
927
-
928
- @overload
929
- def render(self, format: Literal[OutputFormat.ANY]) -> str: ...
930
-
931
- def render(self, format: Optional[OutputFormat] = None, **kwargs) -> str:
932
- """
933
- Render the text in the specified format.
934
-
935
- Args:
936
- format: Output format (uses DEFAULT_FORMAT if None)
937
- **kwargs: Format-specific options
938
-
939
- Returns:
940
- Formatted string representation
941
- """
942
- return self.to_format(format, **kwargs)
943
-
944
- def save(
945
- self,
946
- filepath: str,
947
- *,
948
- format: Optional[OutputFormat] = None,
949
- encoding: str = "utf-8",
950
- **kwargs,
951
- ) -> None:
952
- """
953
- Save the text to a file.
954
-
955
- Args:
956
- filepath: Path to save the file
957
- format: Output format (auto-detected from extension if None)
958
- encoding: File encoding
959
- **kwargs: Format-specific options
960
- """
961
- import os
962
-
963
- # Auto-detect format from file extension if not provided
964
- if format is None:
965
- ext = os.path.splitext(filepath)[1].lower()
966
- format_map = {
967
- ".md": OutputFormat.MARKDOWN,
968
- ".markdown": OutputFormat.MARKDOWN,
969
- ".json": OutputFormat.JSON,
970
- ".txt": OutputFormat.TEXT,
971
- ".text": OutputFormat.TEXT,
972
- }
973
- format = format_map.get(ext, self.DEFAULT_FORMAT)
974
-
975
- # Render content
976
- content = self.render(format, **kwargs)
977
-
978
- # Write to file
979
- with open(filepath, "w", encoding=encoding) as f:
980
- f.write(content)
981
-
982
- def chain(self, other: "Text") -> "Text[T]":
983
- """Chain another Text instance as a section."""
984
- self.add_section(other)
985
- return self
986
-
987
- def __add__(self, other: Union["Text", str, BaseText]) -> "Text[T]":
988
- """Add operator for chaining texts."""
989
- if isinstance(other, str):
990
- return self.add_text_section(other)
991
- elif isinstance(other, BaseText):
992
- return self.add_section(other)
993
- else:
994
- raise TypeError(f"Cannot add {type(other)} to Text")
995
-
996
- def __or__(self, format: OutputFormat) -> str:
997
- """Pipe operator for format conversion."""
998
- return self.render(format)
999
-
1000
- def __getitem__(self, key: Union[int, str]) -> BaseText:
1001
- """Access sections by index or title."""
1002
- if isinstance(key, int):
1003
- return self.sections[key]
1004
- elif isinstance(key, str):
1005
- for section in self.sections:
1006
- if section.title == key:
1007
- return section
1008
- raise KeyError(f"Section with title '{key}' not found")
1009
- else:
1010
- raise TypeError(f"Invalid key type: {type(key)}")
1011
-
1012
- def __len__(self) -> int:
1013
- """Return total character count of all sections."""
1014
- if not self.sections:
1015
- return 0
1016
- total_length = 0
1017
- for section in self.sections:
1018
- total_length += len(section)
1019
- # Add separators between sections (2 chars for \n\n)
1020
- return total_length + (len(self.sections) - 1) * 2
1021
-
1022
- def __iter__(self):
1023
- """Iterate over sections."""
1024
- return iter(self.sections)
1025
-
1026
- def __bool__(self) -> bool:
1027
- """Check if text has content or sections."""
1028
- return bool(self.content or self.sections or self.title)
1029
-
1030
- # Enhanced property access with type hints
1031
- @property
1032
- def text(self) -> str:
1033
- """Get text format representation."""
1034
- return self.render(OutputFormat.TEXT)
1035
-
1036
- @property
1037
- def markdown(self) -> str:
1038
- """Get markdown format representation."""
1039
- return self.render(OutputFormat.MARKDOWN)
1040
-
1041
- @property
1042
- def json(self) -> str:
1043
- """Get JSON format representation."""
1044
- return self.render(OutputFormat.JSON)
1045
-
1046
- @property
1047
- def type_info(self) -> str:
1048
- """Get type format representation."""
1049
- return self.render(OutputFormat.TYPE)
1050
-
1051
- @property
1052
- def auto(self) -> str:
1053
- """Get auto-detected format representation."""
1054
- return self.render(OutputFormat.ANY)
1055
-
1056
-
1057
- __all__ = (
1058
- "OutputFormat",
1059
- "HeadingStyle",
1060
- "BaseText",
1061
- "CodeSection",
1062
- "SchemaSection",
1063
- "SimpleText",
1064
- "OutputText",
1065
- "Text",
1066
- )