hammad-python 0.0.29__py3-none-any.whl → 0.0.31__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.
- ham/__init__.py +10 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/METADATA +6 -32
- hammad_python-0.0.31.dist-info/RECORD +6 -0
- hammad/__init__.py +0 -84
- hammad/_internal.py +0 -256
- hammad/_main.py +0 -226
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -867
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -634
- hammad/data/__init__.py +0 -90
- hammad/data/collections/__init__.py +0 -49
- hammad/data/collections/collection.py +0 -326
- hammad/data/collections/indexes/__init__.py +0 -37
- hammad/data/collections/indexes/qdrant/__init__.py +0 -1
- hammad/data/collections/indexes/qdrant/index.py +0 -723
- hammad/data/collections/indexes/qdrant/settings.py +0 -94
- hammad/data/collections/indexes/qdrant/utils.py +0 -210
- hammad/data/collections/indexes/tantivy/__init__.py +0 -1
- hammad/data/collections/indexes/tantivy/index.py +0 -426
- hammad/data/collections/indexes/tantivy/settings.py +0 -40
- hammad/data/collections/indexes/tantivy/utils.py +0 -176
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/models/__init__.py +0 -50
- hammad/data/models/extensions/__init__.py +0 -4
- hammad/data/models/extensions/pydantic/__init__.py +0 -42
- hammad/data/models/extensions/pydantic/converters.py +0 -759
- hammad/data/models/fields.py +0 -546
- hammad/data/models/model.py +0 -1078
- hammad/data/models/utils.py +0 -280
- hammad/data/sql/__init__.py +0 -24
- hammad/data/sql/database.py +0 -576
- hammad/data/sql/types.py +0 -127
- hammad/data/types/__init__.py +0 -75
- hammad/data/types/file.py +0 -431
- hammad/data/types/multimodal/__init__.py +0 -36
- hammad/data/types/multimodal/audio.py +0 -200
- hammad/data/types/multimodal/image.py +0 -182
- hammad/data/types/text.py +0 -1308
- hammad/formatting/__init__.py +0 -33
- hammad/formatting/json/__init__.py +0 -27
- hammad/formatting/json/converters.py +0 -158
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/genai/__init__.py +0 -217
- hammad/genai/a2a/__init__.py +0 -32
- hammad/genai/a2a/workers.py +0 -552
- hammad/genai/agents/__init__.py +0 -59
- hammad/genai/agents/agent.py +0 -1973
- hammad/genai/agents/run.py +0 -1024
- hammad/genai/agents/types/__init__.py +0 -42
- hammad/genai/agents/types/agent_context.py +0 -13
- hammad/genai/agents/types/agent_event.py +0 -128
- hammad/genai/agents/types/agent_hooks.py +0 -220
- hammad/genai/agents/types/agent_messages.py +0 -31
- hammad/genai/agents/types/agent_response.py +0 -125
- hammad/genai/agents/types/agent_stream.py +0 -327
- hammad/genai/graphs/__init__.py +0 -125
- hammad/genai/graphs/_utils.py +0 -190
- hammad/genai/graphs/base.py +0 -1828
- hammad/genai/graphs/plugins.py +0 -316
- hammad/genai/graphs/types.py +0 -638
- hammad/genai/models/__init__.py +0 -1
- hammad/genai/models/embeddings/__init__.py +0 -43
- hammad/genai/models/embeddings/model.py +0 -226
- hammad/genai/models/embeddings/run.py +0 -163
- hammad/genai/models/embeddings/types/__init__.py +0 -37
- hammad/genai/models/embeddings/types/embedding_model_name.py +0 -75
- hammad/genai/models/embeddings/types/embedding_model_response.py +0 -76
- hammad/genai/models/embeddings/types/embedding_model_run_params.py +0 -66
- hammad/genai/models/embeddings/types/embedding_model_settings.py +0 -47
- hammad/genai/models/language/__init__.py +0 -57
- hammad/genai/models/language/model.py +0 -1098
- hammad/genai/models/language/run.py +0 -878
- hammad/genai/models/language/types/__init__.py +0 -40
- hammad/genai/models/language/types/language_model_instructor_mode.py +0 -47
- hammad/genai/models/language/types/language_model_messages.py +0 -28
- hammad/genai/models/language/types/language_model_name.py +0 -239
- hammad/genai/models/language/types/language_model_request.py +0 -127
- hammad/genai/models/language/types/language_model_response.py +0 -217
- hammad/genai/models/language/types/language_model_response_chunk.py +0 -56
- hammad/genai/models/language/types/language_model_settings.py +0 -89
- hammad/genai/models/language/types/language_model_stream.py +0 -600
- hammad/genai/models/language/utils/__init__.py +0 -28
- hammad/genai/models/language/utils/requests.py +0 -421
- hammad/genai/models/language/utils/structured_outputs.py +0 -135
- hammad/genai/models/model_provider.py +0 -4
- hammad/genai/models/multimodal.py +0 -47
- hammad/genai/models/reranking.py +0 -26
- hammad/genai/types/__init__.py +0 -1
- hammad/genai/types/base.py +0 -215
- hammad/genai/types/history.py +0 -290
- hammad/genai/types/tools.py +0 -507
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -1018
- hammad/mcp/__init__.py +0 -53
- hammad/mcp/client/__init__.py +0 -35
- hammad/mcp/client/client.py +0 -624
- hammad/mcp/client/client_service.py +0 -400
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -26
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/runtime/__init__.py +0 -32
- hammad/runtime/decorators.py +0 -142
- hammad/runtime/run.py +0 -299
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -527
- hammad/service/decorators.py +0 -283
- hammad/types.py +0 -288
- hammad/typing/__init__.py +0 -435
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -275
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -1023
- hammad/web/utils.py +0 -472
- hammad_python-0.0.29.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/licenses/LICENSE +0 -0
@@ -1,723 +0,0 @@
|
|
1
|
-
"""hammad.formatting.text.converters"""
|
2
|
-
|
3
|
-
import json
|
4
|
-
import logging
|
5
|
-
import dataclasses
|
6
|
-
from dataclasses import is_dataclass, fields as dataclass_fields
|
7
|
-
from docstring_parser import parse
|
8
|
-
from typing import (
|
9
|
-
Any,
|
10
|
-
Optional,
|
11
|
-
Dict,
|
12
|
-
List,
|
13
|
-
Set,
|
14
|
-
Callable,
|
15
|
-
Union,
|
16
|
-
)
|
17
|
-
|
18
|
-
from ...typing import (
|
19
|
-
inspection,
|
20
|
-
is_pydantic_basemodel,
|
21
|
-
is_msgspec_struct,
|
22
|
-
is_dataclass,
|
23
|
-
is_pydantic_basemodel_instance,
|
24
|
-
)
|
25
|
-
from .markdown import (
|
26
|
-
markdown_bold,
|
27
|
-
markdown_italic,
|
28
|
-
markdown_code,
|
29
|
-
markdown_code_block,
|
30
|
-
markdown_heading,
|
31
|
-
markdown_list_item,
|
32
|
-
markdown_table_row,
|
33
|
-
markdown_horizontal_rule,
|
34
|
-
)
|
35
|
-
|
36
|
-
__all__ = [
|
37
|
-
"convert_type_to_text",
|
38
|
-
"convert_docstring_to_text",
|
39
|
-
"convert_dataclass_to_text",
|
40
|
-
"convert_pydantic_to_text",
|
41
|
-
"convert_function_to_text",
|
42
|
-
"convert_collection_to_text",
|
43
|
-
"convert_dict_to_text",
|
44
|
-
"convert_to_text",
|
45
|
-
]
|
46
|
-
|
47
|
-
|
48
|
-
class TextFormattingError(Exception):
|
49
|
-
"""Exception raised for errors in the markdown converters."""
|
50
|
-
|
51
|
-
def __init__(self, message: str):
|
52
|
-
self.message = message
|
53
|
-
super().__init__(self.message)
|
54
|
-
|
55
|
-
|
56
|
-
def _escape_markdown(text: str) -> str:
|
57
|
-
"""Escape special Markdown characters."""
|
58
|
-
# Only escape the most problematic characters
|
59
|
-
chars_to_escape = ["*", "_", "`", "[", "]", "(", ")", "#", "+", "-", "!"]
|
60
|
-
for char in chars_to_escape:
|
61
|
-
text = text.replace(char, f"\\{char}")
|
62
|
-
return text
|
63
|
-
|
64
|
-
|
65
|
-
def convert_type_to_text(cls: Any) -> str:
|
66
|
-
"""Converts a type into a clean & human readable text representation.
|
67
|
-
|
68
|
-
This function uses `typing_inspect` exclusively to infer nested types
|
69
|
-
within `Optional`, `Union` types, for the cleanest possible string
|
70
|
-
representation of a type.
|
71
|
-
|
72
|
-
Args:
|
73
|
-
cls: The type to convert to a text representation.
|
74
|
-
|
75
|
-
Returns:
|
76
|
-
A clean, human-readable string representation of the type.
|
77
|
-
"""
|
78
|
-
# Handle None type
|
79
|
-
if cls is None or cls is type(None):
|
80
|
-
return "None"
|
81
|
-
|
82
|
-
# Get origin and args using typing_inspect for better type handling
|
83
|
-
origin = inspection.get_origin(cls)
|
84
|
-
args = inspection.get_args(cls)
|
85
|
-
|
86
|
-
if origin is not None:
|
87
|
-
# Handle Optional (Union[T, None])
|
88
|
-
if inspection.is_optional_type(cls):
|
89
|
-
# Recursively get the name of the inner type (the one not None)
|
90
|
-
inner_type = args[0]
|
91
|
-
inner_type_name = convert_type_to_text(inner_type)
|
92
|
-
return f"Optional[{inner_type_name}]"
|
93
|
-
|
94
|
-
# Handle other Union types
|
95
|
-
if inspection.is_union_type(cls):
|
96
|
-
# Recursively get names of all arguments in the Union
|
97
|
-
args_str = ", ".join(convert_type_to_text(arg) for arg in args)
|
98
|
-
return f"Union[{args_str}]"
|
99
|
-
|
100
|
-
# Handle other generic types (List, Dict, Tuple, Set, etc.)
|
101
|
-
# Use origin.__name__ for built-in generics like list, dict, tuple, set
|
102
|
-
origin_name = getattr(origin, "__name__", str(origin).split(".")[-1])
|
103
|
-
if origin_name.startswith("_"): # Handle internal typing names like _List
|
104
|
-
origin_name = origin_name[1:]
|
105
|
-
|
106
|
-
# Convert to lowercase for built-in types to match modern Python style
|
107
|
-
if origin_name in ["List", "Dict", "Tuple", "Set"]:
|
108
|
-
origin_name = origin_name.lower()
|
109
|
-
|
110
|
-
if args: # If there are type arguments
|
111
|
-
# Recursively get names of type arguments
|
112
|
-
args_str = ", ".join(convert_type_to_text(arg) for arg in args)
|
113
|
-
return f"{origin_name}[{args_str}]"
|
114
|
-
else: # Generic without arguments (e.g., typing.List)
|
115
|
-
return origin_name
|
116
|
-
|
117
|
-
# Handle special cases with typing_inspect
|
118
|
-
if inspection.is_typevar(cls):
|
119
|
-
return str(cls)
|
120
|
-
if inspection.is_forward_ref(cls):
|
121
|
-
return str(cls)
|
122
|
-
if inspection.is_literal_type(cls):
|
123
|
-
return f"Literal[{', '.join(str(arg) for arg in args)}]"
|
124
|
-
if inspection.is_final_type(cls):
|
125
|
-
return f"Final[{convert_type_to_text(args[0])}]" if args else "Final"
|
126
|
-
if inspection.is_new_type(cls):
|
127
|
-
return str(cls)
|
128
|
-
|
129
|
-
# Handle Pydantic BaseModel types
|
130
|
-
if is_pydantic_basemodel(cls):
|
131
|
-
if hasattr(cls, "__name__"):
|
132
|
-
return cls.__name__
|
133
|
-
return "BaseModel"
|
134
|
-
|
135
|
-
# Handle msgspec Struct types
|
136
|
-
if is_msgspec_struct(cls):
|
137
|
-
if hasattr(cls, "__name__"):
|
138
|
-
return cls.__name__
|
139
|
-
return "Struct"
|
140
|
-
|
141
|
-
# Handle dataclass types
|
142
|
-
if is_dataclass(cls):
|
143
|
-
if hasattr(cls, "__name__"):
|
144
|
-
return cls.__name__
|
145
|
-
return "dataclass"
|
146
|
-
|
147
|
-
# Handle basic types with __name__ attribute
|
148
|
-
if hasattr(cls, "__name__") and cls.__name__ != "<lambda>":
|
149
|
-
return cls.__name__
|
150
|
-
|
151
|
-
# Special handling for Optional type string representation
|
152
|
-
if str(cls).startswith("typing.Optional"):
|
153
|
-
# Extract the inner type from the string representation
|
154
|
-
inner_type_str = str(cls).replace("typing.Optional[", "").rstrip("]")
|
155
|
-
return f"Optional[{inner_type_str}]"
|
156
|
-
|
157
|
-
# Fallback for any other types
|
158
|
-
# Clean up 'typing.' prefix and handle other common representations
|
159
|
-
return str(cls).replace("typing.", "").replace("__main__.", "")
|
160
|
-
|
161
|
-
|
162
|
-
def convert_docstring_to_text(
|
163
|
-
obj: Any,
|
164
|
-
*,
|
165
|
-
params_override: Optional[str] = None,
|
166
|
-
returns_override: Optional[str] = None,
|
167
|
-
raises_override: Optional[str] = None,
|
168
|
-
examples_override: Optional[str] = None,
|
169
|
-
params_prefix: Optional[str] = None,
|
170
|
-
returns_prefix: Optional[str] = None,
|
171
|
-
raises_prefix: Optional[str] = None,
|
172
|
-
exclude_params: bool = False,
|
173
|
-
exclude_returns: bool = False,
|
174
|
-
exclude_raises: bool = False,
|
175
|
-
exclude_examples: bool = False,
|
176
|
-
) -> str:
|
177
|
-
"""
|
178
|
-
Convert an object's docstring to formatted text using docstring_parser.
|
179
|
-
|
180
|
-
Args:
|
181
|
-
obj: The object to extract docstring from
|
182
|
-
params_override: Override text for parameters section
|
183
|
-
returns_override: Override text for returns section
|
184
|
-
raises_override: Override text for raises section
|
185
|
-
examples_override: Override text for examples section
|
186
|
-
params_prefix: Prefix for parameters section
|
187
|
-
returns_prefix: Prefix for returns section
|
188
|
-
raises_prefix: Prefix for raises section
|
189
|
-
exclude_params: Whether to exclude parameters section
|
190
|
-
exclude_returns: Whether to exclude returns section
|
191
|
-
exclude_raises: Whether to exclude raises section
|
192
|
-
exclude_examples: Whether to exclude examples section
|
193
|
-
|
194
|
-
Returns:
|
195
|
-
Formatted text representation of the docstring
|
196
|
-
"""
|
197
|
-
# Get the raw docstring
|
198
|
-
doc = getattr(obj, "__doc__", None)
|
199
|
-
if not doc:
|
200
|
-
return ""
|
201
|
-
|
202
|
-
try:
|
203
|
-
# Parse the docstring using docstring_parser
|
204
|
-
parsed = parse(doc)
|
205
|
-
|
206
|
-
parts = []
|
207
|
-
|
208
|
-
# Add short description
|
209
|
-
if parsed.short_description:
|
210
|
-
parts.append(parsed.short_description)
|
211
|
-
|
212
|
-
# Add long description
|
213
|
-
if parsed.long_description:
|
214
|
-
parts.append("") # Empty line separator
|
215
|
-
parts.append(parsed.long_description)
|
216
|
-
|
217
|
-
# Add parameters section
|
218
|
-
if not exclude_params and (params_override or parsed.params):
|
219
|
-
parts.append("") # Empty line separator
|
220
|
-
if params_override:
|
221
|
-
parts.append(params_override)
|
222
|
-
else:
|
223
|
-
prefix = params_prefix or "Parameters:"
|
224
|
-
parts.append(prefix)
|
225
|
-
for param in parsed.params:
|
226
|
-
param_line = f" {param.arg_name}"
|
227
|
-
if param.type_name:
|
228
|
-
param_line += f" ({param.type_name})"
|
229
|
-
if param.description:
|
230
|
-
param_line += f": {param.description}"
|
231
|
-
parts.append(param_line)
|
232
|
-
|
233
|
-
# Add returns section
|
234
|
-
if not exclude_returns and (returns_override or parsed.returns):
|
235
|
-
parts.append("") # Empty line separator
|
236
|
-
if returns_override:
|
237
|
-
parts.append(returns_override)
|
238
|
-
else:
|
239
|
-
prefix = returns_prefix or "Returns:"
|
240
|
-
parts.append(prefix)
|
241
|
-
if parsed.returns:
|
242
|
-
return_line = " "
|
243
|
-
if parsed.returns.type_name:
|
244
|
-
return_line += f"{parsed.returns.type_name}: "
|
245
|
-
if parsed.returns.description:
|
246
|
-
return_line += parsed.returns.description
|
247
|
-
parts.append(return_line)
|
248
|
-
|
249
|
-
# Add raises section
|
250
|
-
if not exclude_raises and (raises_override or parsed.raises):
|
251
|
-
parts.append("") # Empty line separator
|
252
|
-
if raises_override:
|
253
|
-
parts.append(raises_override)
|
254
|
-
else:
|
255
|
-
prefix = raises_prefix or "Raises:"
|
256
|
-
parts.append(prefix)
|
257
|
-
for exc in parsed.raises:
|
258
|
-
exc_line = f" {exc.type_name or 'Exception'}"
|
259
|
-
if exc.description:
|
260
|
-
exc_line += f": {exc.description}"
|
261
|
-
parts.append(exc_line)
|
262
|
-
|
263
|
-
# Add examples section (if available in parsed docstring)
|
264
|
-
if not exclude_examples and examples_override:
|
265
|
-
parts.append("") # Empty line separator
|
266
|
-
parts.append(examples_override)
|
267
|
-
|
268
|
-
return "\n".join(parts)
|
269
|
-
|
270
|
-
except Exception:
|
271
|
-
# Fallback to raw docstring if parsing fails
|
272
|
-
return doc.strip()
|
273
|
-
|
274
|
-
|
275
|
-
def convert_dataclass_to_text(
|
276
|
-
obj: Any,
|
277
|
-
title: Optional[str],
|
278
|
-
description: Optional[str],
|
279
|
-
table_format: bool,
|
280
|
-
show_types: bool,
|
281
|
-
show_defaults: bool,
|
282
|
-
show_values: bool,
|
283
|
-
indent_level: int,
|
284
|
-
) -> str:
|
285
|
-
"""Convert a dataclass to Markdown format."""
|
286
|
-
is_class = isinstance(obj, type)
|
287
|
-
obj_name = title or (obj.__name__ if is_class else obj.__class__.__name__)
|
288
|
-
|
289
|
-
parts = []
|
290
|
-
parts.append(markdown_heading(obj_name, min(indent_level + 1, 6)))
|
291
|
-
|
292
|
-
if description:
|
293
|
-
parts.append(f"\n{description}\n")
|
294
|
-
|
295
|
-
fields_data = []
|
296
|
-
for field in dataclass_fields(obj if is_class else obj.__class__):
|
297
|
-
field_info = {
|
298
|
-
"name": field.name,
|
299
|
-
"type": convert_type_to_text(field.type) if show_types else None,
|
300
|
-
"default": field.default
|
301
|
-
if show_defaults and field.default is not dataclasses.MISSING
|
302
|
-
else None,
|
303
|
-
"value": getattr(obj, field.name) if show_values and not is_class else None,
|
304
|
-
}
|
305
|
-
fields_data.append(field_info)
|
306
|
-
|
307
|
-
if table_format and fields_data:
|
308
|
-
# Create table headers
|
309
|
-
headers = ["Field"]
|
310
|
-
if show_types:
|
311
|
-
headers.append("Type")
|
312
|
-
if show_defaults:
|
313
|
-
headers.append("Default")
|
314
|
-
if show_values and not is_class:
|
315
|
-
headers.append("Value")
|
316
|
-
|
317
|
-
parts.append("\n" + markdown_table_row(headers, is_header=True))
|
318
|
-
|
319
|
-
# Add rows
|
320
|
-
for field_info in fields_data:
|
321
|
-
row = [markdown_code(field_info["name"])]
|
322
|
-
if show_types:
|
323
|
-
row.append(field_info["type"] or "")
|
324
|
-
if show_defaults:
|
325
|
-
row.append(
|
326
|
-
str(field_info["default"])
|
327
|
-
if field_info["default"] is not None
|
328
|
-
else ""
|
329
|
-
)
|
330
|
-
if show_values and not is_class:
|
331
|
-
row.append(
|
332
|
-
str(field_info["value"]) if field_info["value"] is not None else ""
|
333
|
-
)
|
334
|
-
parts.append(markdown_table_row(row))
|
335
|
-
else:
|
336
|
-
# List format
|
337
|
-
for field_info in fields_data:
|
338
|
-
field_desc = markdown_code(field_info["name"])
|
339
|
-
if field_info["type"]:
|
340
|
-
field_desc += f" ({field_info['type']})"
|
341
|
-
if field_info["default"] is not None:
|
342
|
-
field_desc += f" - default: {field_info['default']}"
|
343
|
-
if field_info["value"] is not None:
|
344
|
-
field_desc += f" = {field_info['value']}"
|
345
|
-
parts.append(markdown_list_item(field_desc, indent_level))
|
346
|
-
|
347
|
-
return "\n".join(parts)
|
348
|
-
|
349
|
-
|
350
|
-
def convert_pydantic_to_text(
|
351
|
-
obj: Any,
|
352
|
-
title: Optional[str],
|
353
|
-
description: Optional[str],
|
354
|
-
table_format: bool,
|
355
|
-
show_types: bool,
|
356
|
-
show_defaults: bool,
|
357
|
-
show_values: bool,
|
358
|
-
show_required: bool,
|
359
|
-
indent_level: int,
|
360
|
-
) -> str:
|
361
|
-
"""Convert a Pydantic model to Markdown format."""
|
362
|
-
is_class = isinstance(obj, type)
|
363
|
-
is_instance = is_pydantic_basemodel_instance(obj)
|
364
|
-
|
365
|
-
obj_name = title or (obj.__name__ if is_class else obj.__class__.__name__)
|
366
|
-
|
367
|
-
parts = []
|
368
|
-
parts.append(markdown_heading(obj_name, min(indent_level + 1, 6)))
|
369
|
-
|
370
|
-
if description:
|
371
|
-
parts.append(f"\n{description}\n")
|
372
|
-
|
373
|
-
model_fields = getattr(obj if is_class else obj.__class__, "model_fields", {})
|
374
|
-
fields_data = []
|
375
|
-
|
376
|
-
for field_name, field_info in model_fields.items():
|
377
|
-
field_data = {
|
378
|
-
"name": field_name,
|
379
|
-
"type": convert_type_to_text(field_info.annotation) if show_types else None,
|
380
|
-
"required": getattr(field_info, "is_required", lambda: True)()
|
381
|
-
if show_required
|
382
|
-
else None,
|
383
|
-
"default": getattr(field_info, "default", None) if show_defaults else None,
|
384
|
-
"value": getattr(obj, field_name, None)
|
385
|
-
if show_values and is_instance
|
386
|
-
else None,
|
387
|
-
"description": getattr(field_info, "description", None),
|
388
|
-
}
|
389
|
-
fields_data.append(field_data)
|
390
|
-
|
391
|
-
if table_format and fields_data:
|
392
|
-
# Create table
|
393
|
-
headers = ["Field"]
|
394
|
-
if show_types:
|
395
|
-
headers.append("Type")
|
396
|
-
if show_required:
|
397
|
-
headers.append("Required")
|
398
|
-
if show_defaults:
|
399
|
-
headers.append("Default")
|
400
|
-
if show_values and is_instance:
|
401
|
-
headers.append("Value")
|
402
|
-
|
403
|
-
parts.append("\n" + markdown_table_row(headers, is_header=True))
|
404
|
-
|
405
|
-
for field_data in fields_data:
|
406
|
-
row = [markdown_code(field_data["name"])]
|
407
|
-
if show_types:
|
408
|
-
row.append(field_data["type"] or "")
|
409
|
-
if show_required:
|
410
|
-
row.append("Yes" if field_data["required"] else "No")
|
411
|
-
if show_defaults:
|
412
|
-
row.append(
|
413
|
-
str(field_data["default"])
|
414
|
-
if field_data["default"] is not None
|
415
|
-
else ""
|
416
|
-
)
|
417
|
-
if show_values and is_instance:
|
418
|
-
row.append(
|
419
|
-
str(field_data["value"]) if field_data["value"] is not None else ""
|
420
|
-
)
|
421
|
-
parts.append(markdown_table_row(row))
|
422
|
-
else:
|
423
|
-
# List format
|
424
|
-
for field_data in fields_data:
|
425
|
-
field_desc = markdown_code(field_data["name"])
|
426
|
-
if field_data["type"]:
|
427
|
-
field_desc += f" ({field_data['type']})"
|
428
|
-
if field_data["required"] is not None:
|
429
|
-
field_desc += (
|
430
|
-
" " + markdown_bold("[Required]")
|
431
|
-
if field_data["required"]
|
432
|
-
else " " + markdown_italic("[Optional]")
|
433
|
-
)
|
434
|
-
if field_data["default"] is not None:
|
435
|
-
field_desc += f" - default: {field_data['default']}"
|
436
|
-
if field_data["value"] is not None:
|
437
|
-
field_desc += f" = {field_data['value']}"
|
438
|
-
|
439
|
-
parts.append(markdown_list_item(field_desc, indent_level))
|
440
|
-
|
441
|
-
if field_data["description"]:
|
442
|
-
parts.append(
|
443
|
-
markdown_list_item(field_data["description"], indent_level + 1)
|
444
|
-
)
|
445
|
-
|
446
|
-
return "\n".join(parts)
|
447
|
-
|
448
|
-
|
449
|
-
def convert_function_to_text(
|
450
|
-
obj: Callable,
|
451
|
-
title: Optional[str],
|
452
|
-
description: Optional[str],
|
453
|
-
show_signature: bool,
|
454
|
-
show_docstring: bool,
|
455
|
-
indent_level: int,
|
456
|
-
) -> str:
|
457
|
-
"""Convert a function to Markdown format."""
|
458
|
-
func_name = title or obj.__name__
|
459
|
-
|
460
|
-
parts = []
|
461
|
-
parts.append(markdown_heading(func_name, min(indent_level + 1, 6)))
|
462
|
-
|
463
|
-
if show_signature:
|
464
|
-
import inspect
|
465
|
-
|
466
|
-
try:
|
467
|
-
sig = inspect.signature(obj)
|
468
|
-
parts.append(f"\n{markdown_code(f'{func_name}{sig}')}\n")
|
469
|
-
except Exception:
|
470
|
-
pass
|
471
|
-
|
472
|
-
if description:
|
473
|
-
parts.append(f"\n{description}\n")
|
474
|
-
elif show_docstring and obj.__doc__:
|
475
|
-
doc_info = parse(obj.__doc__)
|
476
|
-
if doc_info.short_description:
|
477
|
-
parts.append(f"\n{doc_info.short_description}\n")
|
478
|
-
if doc_info.long_description:
|
479
|
-
parts.append(f"\n{doc_info.long_description}\n")
|
480
|
-
|
481
|
-
return "\n".join(parts)
|
482
|
-
|
483
|
-
|
484
|
-
def convert_collection_to_text(
|
485
|
-
obj: Union[List, Set, tuple],
|
486
|
-
title: Optional[str],
|
487
|
-
description: Optional[str],
|
488
|
-
compact: bool,
|
489
|
-
show_indices: bool,
|
490
|
-
indent_level: int,
|
491
|
-
visited: Set[int],
|
492
|
-
) -> str:
|
493
|
-
"""Convert a collection to Markdown format."""
|
494
|
-
obj_name = title or obj.__class__.__name__
|
495
|
-
|
496
|
-
parts = []
|
497
|
-
if not compact:
|
498
|
-
parts.append(markdown_heading(obj_name, min(indent_level + 1, 6)))
|
499
|
-
if description:
|
500
|
-
parts.append(f"\n{description}\n")
|
501
|
-
|
502
|
-
if not obj:
|
503
|
-
parts.append(markdown_italic("(empty)"))
|
504
|
-
return "\n".join(parts)
|
505
|
-
|
506
|
-
for i, item in enumerate(obj):
|
507
|
-
if show_indices:
|
508
|
-
item_text = f"[{i}] {convert_to_text(item, compact=True, _visited=visited)}"
|
509
|
-
else:
|
510
|
-
item_text = convert_to_text(item, compact=True, _visited=visited)
|
511
|
-
parts.append(markdown_list_item(item_text, indent_level))
|
512
|
-
|
513
|
-
return "\n".join(parts)
|
514
|
-
|
515
|
-
|
516
|
-
def convert_dict_to_text(
|
517
|
-
obj: Dict[Any, Any],
|
518
|
-
title: Optional[str],
|
519
|
-
description: Optional[str],
|
520
|
-
table_format: bool,
|
521
|
-
compact: bool,
|
522
|
-
indent_level: int,
|
523
|
-
visited: Set[int],
|
524
|
-
) -> str:
|
525
|
-
"""Convert a dictionary to Markdown format."""
|
526
|
-
obj_name = title or "Dictionary"
|
527
|
-
|
528
|
-
parts = []
|
529
|
-
if not compact:
|
530
|
-
parts.append(markdown_heading(obj_name, min(indent_level + 1, 6)))
|
531
|
-
if description:
|
532
|
-
parts.append(f"\n{description}\n")
|
533
|
-
|
534
|
-
if not obj:
|
535
|
-
parts.append(markdown_italic("(empty)"))
|
536
|
-
return "\n".join(parts)
|
537
|
-
|
538
|
-
if table_format and all(
|
539
|
-
isinstance(v, (str, int, float, bool, type(None))) for v in obj.values()
|
540
|
-
):
|
541
|
-
# Use table format for simple values
|
542
|
-
parts.append("\n" + markdown_table_row(["Key", "Value"], is_header=True))
|
543
|
-
for key, value in obj.items():
|
544
|
-
parts.append(markdown_table_row([markdown_code(str(key)), str(value)]))
|
545
|
-
else:
|
546
|
-
# Use list format
|
547
|
-
for key, value in obj.items():
|
548
|
-
key_str = markdown_code(str(key))
|
549
|
-
value_str = convert_to_text(value, compact=True, _visited=visited)
|
550
|
-
parts.append(markdown_list_item(f"{key_str}: {value_str}", indent_level))
|
551
|
-
|
552
|
-
return "\n".join(parts)
|
553
|
-
|
554
|
-
|
555
|
-
# -----------------------------------------------------------------------------
|
556
|
-
# Main Converter Function
|
557
|
-
# -----------------------------------------------------------------------------
|
558
|
-
|
559
|
-
|
560
|
-
def convert_to_text(
|
561
|
-
obj: Any,
|
562
|
-
*,
|
563
|
-
# Formatting options
|
564
|
-
title: Optional[str] = None,
|
565
|
-
description: Optional[str] = None,
|
566
|
-
heading_level: int = 1,
|
567
|
-
table_format: bool = False,
|
568
|
-
compact: bool = False,
|
569
|
-
code_block_language: Optional[str] = None,
|
570
|
-
# Display options
|
571
|
-
show_types: bool = True,
|
572
|
-
show_values: bool = True,
|
573
|
-
show_defaults: bool = True,
|
574
|
-
show_required: bool = True,
|
575
|
-
show_docstring: bool = True,
|
576
|
-
show_signature: bool = True,
|
577
|
-
show_indices: bool = False,
|
578
|
-
# Style options
|
579
|
-
escape_special_chars: bool = False,
|
580
|
-
add_toc: bool = False,
|
581
|
-
add_horizontal_rules: bool = False,
|
582
|
-
# Internal
|
583
|
-
_visited: Optional[Set[int]] = None,
|
584
|
-
_indent_level: int = 0,
|
585
|
-
) -> str:
|
586
|
-
"""
|
587
|
-
Converts any object into a Markdown formatted string.
|
588
|
-
|
589
|
-
Args:
|
590
|
-
obj: The object to convert to Markdown
|
591
|
-
name: Optional name/title for the object
|
592
|
-
description: Optional description to add
|
593
|
-
heading_level: Starting heading level (1-6)
|
594
|
-
table_format: Use tables for structured data when possible
|
595
|
-
compact: Minimize formatting for inline usage
|
596
|
-
code_block_language: If set, wrap entire output in code block
|
597
|
-
show_types: Include type information
|
598
|
-
show_values: Show current values (for instances)
|
599
|
-
show_defaults: Show default values
|
600
|
-
show_required: Show required/optional status
|
601
|
-
show_docstring: Include docstrings
|
602
|
-
show_signature: Show function signatures
|
603
|
-
show_indices: Show indices for collections
|
604
|
-
escape_special_chars: Escape Markdown special characters
|
605
|
-
add_toc: Add table of contents (not implemented)
|
606
|
-
add_horizontal_rules: Add separators between sections
|
607
|
-
|
608
|
-
Returns:
|
609
|
-
Markdown formatted string representation of the object
|
610
|
-
"""
|
611
|
-
# Handle circular references
|
612
|
-
visited = _visited if _visited is not None else set()
|
613
|
-
obj_id = id(obj)
|
614
|
-
|
615
|
-
if obj_id in visited:
|
616
|
-
return markdown_italic("(circular reference)")
|
617
|
-
|
618
|
-
visited_copy = visited.copy()
|
619
|
-
visited_copy.add(obj_id)
|
620
|
-
|
621
|
-
# Handle None
|
622
|
-
if obj is None:
|
623
|
-
return markdown_code("None")
|
624
|
-
|
625
|
-
# Handle primitives
|
626
|
-
if isinstance(obj, (str, int, float, bool)):
|
627
|
-
text = str(obj)
|
628
|
-
if escape_special_chars and isinstance(obj, str):
|
629
|
-
text = _escape_markdown(text)
|
630
|
-
return text if compact else markdown_code(text)
|
631
|
-
|
632
|
-
# Handle bytes
|
633
|
-
if isinstance(obj, bytes):
|
634
|
-
return markdown_code(f"b'{obj.hex()}'")
|
635
|
-
|
636
|
-
# Wrap in code block if requested
|
637
|
-
if code_block_language:
|
638
|
-
try:
|
639
|
-
if code_block_language.lower() == "json":
|
640
|
-
content = json.dumps(obj, indent=2)
|
641
|
-
else:
|
642
|
-
content = str(obj)
|
643
|
-
return markdown_code_block(content, code_block_language)
|
644
|
-
except Exception:
|
645
|
-
pass
|
646
|
-
|
647
|
-
result = ""
|
648
|
-
|
649
|
-
# Handle dataclasses
|
650
|
-
if is_dataclass(obj):
|
651
|
-
result = convert_dataclass_to_text(
|
652
|
-
obj,
|
653
|
-
title,
|
654
|
-
description,
|
655
|
-
table_format,
|
656
|
-
show_types,
|
657
|
-
show_defaults,
|
658
|
-
show_values,
|
659
|
-
_indent_level,
|
660
|
-
)
|
661
|
-
|
662
|
-
# Handle Pydantic models
|
663
|
-
elif is_pydantic_basemodel(obj):
|
664
|
-
result = convert_pydantic_to_text(
|
665
|
-
obj,
|
666
|
-
title,
|
667
|
-
description,
|
668
|
-
table_format,
|
669
|
-
show_types,
|
670
|
-
show_defaults,
|
671
|
-
show_values,
|
672
|
-
show_required,
|
673
|
-
_indent_level,
|
674
|
-
)
|
675
|
-
|
676
|
-
# Handle msgspec structs
|
677
|
-
elif is_msgspec_struct(obj):
|
678
|
-
# Similar to dataclass handling
|
679
|
-
result = convert_dataclass_to_text(
|
680
|
-
obj,
|
681
|
-
title,
|
682
|
-
description,
|
683
|
-
table_format,
|
684
|
-
show_types,
|
685
|
-
show_defaults,
|
686
|
-
show_values,
|
687
|
-
_indent_level,
|
688
|
-
)
|
689
|
-
|
690
|
-
# Handle functions
|
691
|
-
elif callable(obj) and hasattr(obj, "__name__"):
|
692
|
-
result = convert_function_to_text(
|
693
|
-
obj, title, description, show_signature, show_docstring, _indent_level
|
694
|
-
)
|
695
|
-
|
696
|
-
# Handle collections
|
697
|
-
elif isinstance(obj, (list, tuple, set)):
|
698
|
-
result = convert_collection_to_text(
|
699
|
-
obj, title, description, compact, show_indices, _indent_level, visited_copy
|
700
|
-
)
|
701
|
-
|
702
|
-
# Handle dictionaries
|
703
|
-
elif isinstance(obj, dict):
|
704
|
-
result = convert_dict_to_text(
|
705
|
-
obj, title, description, table_format, compact, _indent_level, visited_copy
|
706
|
-
)
|
707
|
-
|
708
|
-
# Default handling
|
709
|
-
else:
|
710
|
-
obj_name = title or obj.__class__.__name__
|
711
|
-
parts = []
|
712
|
-
if not compact:
|
713
|
-
parts.append(markdown_heading(obj_name, min(_indent_level + 1, 6)))
|
714
|
-
if description:
|
715
|
-
parts.append(f"\n{description}\n")
|
716
|
-
parts.append(markdown_code(str(obj)))
|
717
|
-
result = "\n".join(parts)
|
718
|
-
|
719
|
-
# Add horizontal rule if requested
|
720
|
-
if add_horizontal_rules and not compact and _indent_level == 0:
|
721
|
-
result += f"\n\n{markdown_horizontal_rule()}\n"
|
722
|
-
|
723
|
-
return result
|