hammad-python 0.0.30__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.30.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.30.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.31.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.31.dist-info}/licenses/LICENSE +0 -0
hammad/cli/plugins.py
DELETED
@@ -1,867 +0,0 @@
|
|
1
|
-
"""hammad.cli.plugins
|
2
|
-
|
3
|
-
Contains the following 'builtin' plugins or extensions:
|
4
|
-
|
5
|
-
- `print()`
|
6
|
-
- `input()`
|
7
|
-
- `animate()`
|
8
|
-
"""
|
9
|
-
|
10
|
-
from __future__ import annotations
|
11
|
-
|
12
|
-
import builtins
|
13
|
-
import json
|
14
|
-
from typing import (
|
15
|
-
Optional,
|
16
|
-
IO,
|
17
|
-
overload,
|
18
|
-
Any,
|
19
|
-
Dict,
|
20
|
-
Literal,
|
21
|
-
List,
|
22
|
-
Union,
|
23
|
-
Callable,
|
24
|
-
TYPE_CHECKING,
|
25
|
-
)
|
26
|
-
|
27
|
-
if TYPE_CHECKING:
|
28
|
-
from rich import get_console
|
29
|
-
from rich.console import (
|
30
|
-
JustifyMethod,
|
31
|
-
OverflowMethod,
|
32
|
-
Console,
|
33
|
-
RenderableType,
|
34
|
-
)
|
35
|
-
from rich.panel import PaddingDimensions
|
36
|
-
from rich.prompt import Prompt, Confirm
|
37
|
-
from prompt_toolkit import prompt as pt_prompt
|
38
|
-
from prompt_toolkit.completion import WordCompleter
|
39
|
-
from .animations import (
|
40
|
-
CLIFlashingAnimation,
|
41
|
-
CLIPulsingAnimation,
|
42
|
-
CLIShakingAnimation,
|
43
|
-
CLITypingAnimation,
|
44
|
-
CLISpinningAnimation,
|
45
|
-
CLIRainbowAnimation,
|
46
|
-
RainbowPreset,
|
47
|
-
)
|
48
|
-
from .styles.types import (
|
49
|
-
CLIStyleType,
|
50
|
-
CLIStyleBackgroundType,
|
51
|
-
CLIStyleColorName,
|
52
|
-
CLIStyleBoxName,
|
53
|
-
)
|
54
|
-
from .styles.settings import (
|
55
|
-
CLIStyleRenderableSettings,
|
56
|
-
CLIStyleBackgroundSettings,
|
57
|
-
CLIStyleLiveSettings,
|
58
|
-
)
|
59
|
-
from .styles.utils import (
|
60
|
-
live_render,
|
61
|
-
style_renderable,
|
62
|
-
)
|
63
|
-
|
64
|
-
# Lazy import cache
|
65
|
-
_IMPORT_CACHE = {}
|
66
|
-
|
67
|
-
|
68
|
-
def _get_rich_console():
|
69
|
-
"""Lazy import for rich.get_console"""
|
70
|
-
if "get_console" not in _IMPORT_CACHE:
|
71
|
-
from rich import get_console
|
72
|
-
|
73
|
-
_IMPORT_CACHE["get_console"] = get_console
|
74
|
-
return _IMPORT_CACHE["get_console"]
|
75
|
-
|
76
|
-
|
77
|
-
def _get_rich_console_classes():
|
78
|
-
"""Lazy import for rich.console classes"""
|
79
|
-
if "console_classes" not in _IMPORT_CACHE:
|
80
|
-
from rich.console import Console, RenderableType
|
81
|
-
|
82
|
-
_IMPORT_CACHE["console_classes"] = (Console, RenderableType)
|
83
|
-
return _IMPORT_CACHE["console_classes"]
|
84
|
-
|
85
|
-
|
86
|
-
def _get_rich_prompts():
|
87
|
-
"""Lazy import for rich.prompt classes"""
|
88
|
-
if "prompts" not in _IMPORT_CACHE:
|
89
|
-
from rich.prompt import Prompt, Confirm
|
90
|
-
|
91
|
-
_IMPORT_CACHE["prompts"] = (Prompt, Confirm)
|
92
|
-
return _IMPORT_CACHE["prompts"]
|
93
|
-
|
94
|
-
|
95
|
-
def _get_prompt_toolkit():
|
96
|
-
"""Lazy import for prompt_toolkit"""
|
97
|
-
if "prompt_toolkit" not in _IMPORT_CACHE:
|
98
|
-
from prompt_toolkit import prompt as pt_prompt
|
99
|
-
from prompt_toolkit.completion import WordCompleter
|
100
|
-
|
101
|
-
_IMPORT_CACHE["prompt_toolkit"] = (pt_prompt, WordCompleter)
|
102
|
-
return _IMPORT_CACHE["prompt_toolkit"]
|
103
|
-
|
104
|
-
|
105
|
-
def _get_style_utils():
|
106
|
-
"""Lazy import for style utilities"""
|
107
|
-
if "style_utils" not in _IMPORT_CACHE:
|
108
|
-
from .styles.utils import live_render, style_renderable
|
109
|
-
|
110
|
-
_IMPORT_CACHE["style_utils"] = (live_render, style_renderable)
|
111
|
-
return _IMPORT_CACHE["style_utils"]
|
112
|
-
|
113
|
-
|
114
|
-
def _get_animation_classes():
|
115
|
-
"""Lazy import for animation classes"""
|
116
|
-
if "animations" not in _IMPORT_CACHE:
|
117
|
-
from .animations import (
|
118
|
-
CLIFlashingAnimation,
|
119
|
-
CLIPulsingAnimation,
|
120
|
-
CLIShakingAnimation,
|
121
|
-
CLITypingAnimation,
|
122
|
-
CLISpinningAnimation,
|
123
|
-
CLIRainbowAnimation,
|
124
|
-
RainbowPreset,
|
125
|
-
)
|
126
|
-
|
127
|
-
_IMPORT_CACHE["animations"] = {
|
128
|
-
"CLIFlashingAnimation": CLIFlashingAnimation,
|
129
|
-
"CLIPulsingAnimation": CLIPulsingAnimation,
|
130
|
-
"CLIShakingAnimation": CLIShakingAnimation,
|
131
|
-
"CLITypingAnimation": CLITypingAnimation,
|
132
|
-
"CLISpinningAnimation": CLISpinningAnimation,
|
133
|
-
"CLIRainbowAnimation": CLIRainbowAnimation,
|
134
|
-
"RainbowPreset": RainbowPreset,
|
135
|
-
}
|
136
|
-
return _IMPORT_CACHE["animations"]
|
137
|
-
|
138
|
-
|
139
|
-
def print(
|
140
|
-
*values: object,
|
141
|
-
sep: str = " ",
|
142
|
-
end: str = "\n",
|
143
|
-
file: Optional[IO[str]] = None,
|
144
|
-
flush: bool = False,
|
145
|
-
style: "CLIStyleType | None" = None,
|
146
|
-
style_settings: "CLIStyleRenderableSettings | None" = None,
|
147
|
-
bg: "CLIStyleBackgroundType | None" = None,
|
148
|
-
bg_settings: "CLIStyleBackgroundSettings | None" = None,
|
149
|
-
justify: Optional["JustifyMethod"] = None,
|
150
|
-
overflow: Optional["OverflowMethod"] = None,
|
151
|
-
no_wrap: Optional[bool] = None,
|
152
|
-
emoji: Optional[bool] = None,
|
153
|
-
markup: Optional[bool] = None,
|
154
|
-
highlight: Optional[bool] = None,
|
155
|
-
width: Optional[int] = None,
|
156
|
-
height: Optional[int] = None,
|
157
|
-
border: Optional["CLIStyleBoxName"] = None,
|
158
|
-
padding: Optional["PaddingDimensions"] = None,
|
159
|
-
title: Optional[str] = None,
|
160
|
-
expand: Optional[bool] = None,
|
161
|
-
live: "CLIStyleLiveSettings | int | None" = None,
|
162
|
-
duration: Optional[float] = None,
|
163
|
-
transient: bool = False,
|
164
|
-
new_line_start: bool = False,
|
165
|
-
) -> None:
|
166
|
-
"""
|
167
|
-
Stylized print function built with `rich`. This method maintains
|
168
|
-
all standard functionality of the print function, with no overhead
|
169
|
-
unless the styled parameters are provided.
|
170
|
-
|
171
|
-
Args:
|
172
|
-
*values : The values to print.
|
173
|
-
sep : The separator between values.
|
174
|
-
end : The end character.
|
175
|
-
file : The file to write to.
|
176
|
-
flush : Whether to flush the file.
|
177
|
-
style : A color or style name to apply to the content.
|
178
|
-
style_settings : A dictionary of style settings to apply to the content.
|
179
|
-
bg : A color or box name to apply to the background.
|
180
|
-
bg_settings : A dictionary of background settings to apply to the content.
|
181
|
-
justify : Text justification method ("left", "center", "right", "full").
|
182
|
-
overflow : Text overflow method ("fold", "crop", "ellipsis", "ignore").
|
183
|
-
no_wrap : Disable text wrapping.
|
184
|
-
emoji : Enable/disable emoji rendering.
|
185
|
-
markup : Enable/disable Rich markup rendering.
|
186
|
-
highlight : Enable/disable automatic highlighting.
|
187
|
-
width : Override the width of the output.
|
188
|
-
height : Override the height of the output.
|
189
|
-
border : Border style for panel rendering.
|
190
|
-
padding : Padding dimensions for panel rendering.
|
191
|
-
title : Title for panel rendering.
|
192
|
-
expand : Whether to expand panel to full width.
|
193
|
-
live : A dictionary of live settings or an integer in seconds to run the print in a live renderable.
|
194
|
-
duration : The duration of the live renderable.
|
195
|
-
transient : Whether to clear the output after completion.
|
196
|
-
new_line_start : Start with a new line before printing.
|
197
|
-
|
198
|
-
NOTE: If `live` is set as an integer, transient is True.
|
199
|
-
|
200
|
-
Returns:
|
201
|
-
None
|
202
|
-
|
203
|
-
Raises:
|
204
|
-
PrintError : If the renderable is not a RenderableType.
|
205
|
-
"""
|
206
|
-
|
207
|
-
# If no styling parameters are provided, use built-in print to avoid rich's default styling
|
208
|
-
if (
|
209
|
-
style is None
|
210
|
-
and style_settings is None
|
211
|
-
and bg is None
|
212
|
-
and bg_settings is None
|
213
|
-
and live is None
|
214
|
-
and justify is None
|
215
|
-
and overflow is None
|
216
|
-
and no_wrap is None
|
217
|
-
and emoji is None
|
218
|
-
and markup is None
|
219
|
-
and highlight is None
|
220
|
-
and width is None
|
221
|
-
and height is None
|
222
|
-
and border is None
|
223
|
-
and padding is None
|
224
|
-
and title is None
|
225
|
-
and expand is None
|
226
|
-
and not transient
|
227
|
-
):
|
228
|
-
builtins.print(*values, sep=sep, end=end, file=file, flush=flush)
|
229
|
-
return
|
230
|
-
|
231
|
-
# Convert values to string for styling
|
232
|
-
content = sep.join(str(value) for value in values)
|
233
|
-
|
234
|
-
# Apply styling and background
|
235
|
-
live_render, style_renderable = _get_style_utils()
|
236
|
-
styled_content = style_renderable(
|
237
|
-
content,
|
238
|
-
style=style,
|
239
|
-
style_settings=style_settings,
|
240
|
-
bg=bg,
|
241
|
-
bg_settings=bg_settings,
|
242
|
-
border=border,
|
243
|
-
padding=padding,
|
244
|
-
title=title,
|
245
|
-
expand=expand,
|
246
|
-
)
|
247
|
-
|
248
|
-
# Handle live rendering
|
249
|
-
if live is not None:
|
250
|
-
if isinstance(live, int):
|
251
|
-
# If live is an integer, treat it as duration in seconds
|
252
|
-
from .styles.settings import CLIStyleLiveSettings
|
253
|
-
|
254
|
-
live_settings: CLIStyleLiveSettings = {
|
255
|
-
"duration": float(live),
|
256
|
-
"transient": transient, # Use the transient parameter
|
257
|
-
}
|
258
|
-
else:
|
259
|
-
live_settings = live
|
260
|
-
|
261
|
-
# For very short durations or testing, just print normally
|
262
|
-
duration = live if isinstance(live, int) else live_settings.get("duration", 2.0)
|
263
|
-
if duration <= 1:
|
264
|
-
get_console = _get_rich_console()
|
265
|
-
Console, _ = _get_rich_console_classes()
|
266
|
-
console = get_console() if file is None else Console(file=file)
|
267
|
-
console.print(
|
268
|
-
styled_content,
|
269
|
-
end=end,
|
270
|
-
justify=justify,
|
271
|
-
overflow=overflow,
|
272
|
-
no_wrap=no_wrap,
|
273
|
-
emoji=emoji,
|
274
|
-
markup=markup,
|
275
|
-
highlight=highlight,
|
276
|
-
width=width,
|
277
|
-
height=height,
|
278
|
-
new_line_start=new_line_start,
|
279
|
-
)
|
280
|
-
else:
|
281
|
-
live_render(styled_content, live_settings)
|
282
|
-
else:
|
283
|
-
# Regular print with styling
|
284
|
-
get_console = _get_rich_console()
|
285
|
-
Console, _ = _get_rich_console_classes()
|
286
|
-
console = get_console() if file is None else Console(file=file)
|
287
|
-
|
288
|
-
if transient:
|
289
|
-
# Use Rich's Live with transient for temporary output
|
290
|
-
import time
|
291
|
-
from rich.live import Live
|
292
|
-
|
293
|
-
# Auto-set duration to 2.5 when transient=True and duration is None
|
294
|
-
display_duration = duration if duration is not None else 2.5
|
295
|
-
|
296
|
-
with Live(
|
297
|
-
styled_content,
|
298
|
-
console=console,
|
299
|
-
refresh_per_second=1,
|
300
|
-
transient=True,
|
301
|
-
auto_refresh=False,
|
302
|
-
) as live:
|
303
|
-
live.update(styled_content)
|
304
|
-
live.refresh()
|
305
|
-
time.sleep(
|
306
|
-
display_duration
|
307
|
-
) # Use duration parameter for transient content
|
308
|
-
else:
|
309
|
-
console.print(
|
310
|
-
styled_content,
|
311
|
-
end=end,
|
312
|
-
justify=justify,
|
313
|
-
overflow=overflow,
|
314
|
-
no_wrap=no_wrap,
|
315
|
-
emoji=emoji,
|
316
|
-
markup=markup,
|
317
|
-
highlight=highlight,
|
318
|
-
width=width,
|
319
|
-
height=height,
|
320
|
-
new_line_start=new_line_start,
|
321
|
-
)
|
322
|
-
|
323
|
-
|
324
|
-
class InputError(Exception):
|
325
|
-
"""Exception raised for errors in the Input module."""
|
326
|
-
|
327
|
-
def __init__(self, message: str) -> None:
|
328
|
-
self.message = message
|
329
|
-
super().__init__(self.message)
|
330
|
-
|
331
|
-
|
332
|
-
def _validate_against_schema(value: str, schema: Any) -> Any:
|
333
|
-
"""Validate and convert input value against a schema.
|
334
|
-
|
335
|
-
Args:
|
336
|
-
value: The input value as a string.
|
337
|
-
schema: The schema to validate against.
|
338
|
-
|
339
|
-
Returns:
|
340
|
-
The converted/validated value.
|
341
|
-
|
342
|
-
Raises:
|
343
|
-
InputError: If validation fails.
|
344
|
-
"""
|
345
|
-
if schema is None:
|
346
|
-
return value
|
347
|
-
|
348
|
-
try:
|
349
|
-
# Handle basic types
|
350
|
-
if schema == str:
|
351
|
-
return value
|
352
|
-
elif schema == int:
|
353
|
-
return int(value)
|
354
|
-
elif schema == float:
|
355
|
-
return float(value)
|
356
|
-
elif schema == bool:
|
357
|
-
return value.lower() in ("true", "t", "yes", "y", "1", "on")
|
358
|
-
|
359
|
-
# Handle dict - expect JSON input
|
360
|
-
elif schema == dict or (
|
361
|
-
hasattr(schema, "__origin__") and schema.__origin__ is dict
|
362
|
-
):
|
363
|
-
try:
|
364
|
-
return json.loads(value)
|
365
|
-
except json.JSONDecodeError:
|
366
|
-
raise InputError(f"Invalid JSON format for dictionary input")
|
367
|
-
|
368
|
-
# Handle list - expect JSON input
|
369
|
-
elif schema == list or (
|
370
|
-
hasattr(schema, "__origin__") and schema.__origin__ is list
|
371
|
-
):
|
372
|
-
try:
|
373
|
-
result = json.loads(value)
|
374
|
-
if not isinstance(result, list):
|
375
|
-
raise InputError("Expected a list")
|
376
|
-
return result
|
377
|
-
except json.JSONDecodeError:
|
378
|
-
raise InputError(f"Invalid JSON format for list input")
|
379
|
-
|
380
|
-
# Handle Union types (including Optional)
|
381
|
-
elif hasattr(schema, "__origin__") and schema.__origin__ is Union:
|
382
|
-
args = schema.__args__
|
383
|
-
if len(args) == 2 and type(None) in args:
|
384
|
-
# This is Optional[T]
|
385
|
-
if not value or value.lower() == "none":
|
386
|
-
return None
|
387
|
-
non_none_type = args[0] if args[1] is type(None) else args[1]
|
388
|
-
return _validate_against_schema(value, non_none_type)
|
389
|
-
|
390
|
-
# Handle Pydantic models
|
391
|
-
elif hasattr(schema, "model_validate_json"):
|
392
|
-
try:
|
393
|
-
return schema.model_validate_json(value)
|
394
|
-
except Exception as e:
|
395
|
-
raise InputError(f"Invalid input for {schema.__name__}: {e}")
|
396
|
-
|
397
|
-
# Handle BasedModels
|
398
|
-
elif hasattr(schema, "model_validate_json") or (
|
399
|
-
hasattr(schema, "__bases__")
|
400
|
-
and any("BasedModel" in str(base) for base in schema.__bases__)
|
401
|
-
):
|
402
|
-
try:
|
403
|
-
return schema.model_validate_json(value)
|
404
|
-
except Exception as e:
|
405
|
-
raise InputError(f"Invalid input for {schema.__name__}: {e}")
|
406
|
-
|
407
|
-
# Handle dataclasses
|
408
|
-
elif hasattr(schema, "__dataclass_fields__"):
|
409
|
-
try:
|
410
|
-
data = json.loads(value)
|
411
|
-
return schema(**data)
|
412
|
-
except Exception as e:
|
413
|
-
raise InputError(f"Invalid input for {schema.__name__}: {e}")
|
414
|
-
|
415
|
-
# Handle TypedDict
|
416
|
-
elif hasattr(schema, "__annotations__") and hasattr(schema, "__total__"):
|
417
|
-
try:
|
418
|
-
return json.loads(value)
|
419
|
-
except json.JSONDecodeError:
|
420
|
-
raise InputError(f"Invalid JSON format for {schema.__name__}")
|
421
|
-
|
422
|
-
# Fallback - try to parse as JSON
|
423
|
-
else:
|
424
|
-
try:
|
425
|
-
return json.loads(value)
|
426
|
-
except json.JSONDecodeError:
|
427
|
-
return value
|
428
|
-
|
429
|
-
except InputError:
|
430
|
-
raise
|
431
|
-
except Exception as e:
|
432
|
-
raise InputError(f"Validation error: {e}")
|
433
|
-
|
434
|
-
|
435
|
-
def _collect_fields_sequentially(schema: Any, console) -> Dict[str, Any]:
|
436
|
-
"""Collect field values sequentially for structured schemas.
|
437
|
-
|
438
|
-
Args:
|
439
|
-
schema: The schema to collect fields for.
|
440
|
-
console: The console to use for output.
|
441
|
-
|
442
|
-
Returns:
|
443
|
-
Dictionary of field names to values.
|
444
|
-
"""
|
445
|
-
result = {}
|
446
|
-
|
447
|
-
try:
|
448
|
-
# Handle Pydantic models
|
449
|
-
if hasattr(schema, "model_fields"):
|
450
|
-
fields_info = schema.model_fields
|
451
|
-
console.print(
|
452
|
-
f"\n[bold blue]Entering data for {schema.__name__}:[/bold blue]"
|
453
|
-
)
|
454
|
-
|
455
|
-
for field_name, field_info in fields_info.items():
|
456
|
-
field_type = (
|
457
|
-
field_info.annotation if hasattr(field_info, "annotation") else str
|
458
|
-
)
|
459
|
-
default = getattr(field_info, "default", None)
|
460
|
-
|
461
|
-
prompt_text = f" {field_name}"
|
462
|
-
if default is not None and default != "...":
|
463
|
-
prompt_text += f" (default: {default})"
|
464
|
-
prompt_text += ": "
|
465
|
-
|
466
|
-
Prompt, _ = _get_rich_prompts()
|
467
|
-
value = Prompt.ask(prompt_text)
|
468
|
-
if not value and default is not None and default != "...":
|
469
|
-
result[field_name] = default
|
470
|
-
else:
|
471
|
-
try:
|
472
|
-
result[field_name] = _validate_against_schema(value, field_type)
|
473
|
-
except InputError as e:
|
474
|
-
console.print(f"[red]Error: {e}[/red]")
|
475
|
-
result[field_name] = value
|
476
|
-
|
477
|
-
# Handle BasedModels
|
478
|
-
elif hasattr(schema, "_get_fields_info"):
|
479
|
-
fields_info = schema._get_fields_info()
|
480
|
-
console.print(
|
481
|
-
f"\n[bold blue]Entering data for {schema.__name__}:[/bold blue]"
|
482
|
-
)
|
483
|
-
|
484
|
-
for field_name, field_info in fields_info.items():
|
485
|
-
field_type = field_info.get("type", str)
|
486
|
-
default = field_info.get("default")
|
487
|
-
required = field_info.get("required", True)
|
488
|
-
|
489
|
-
prompt_text = f" {field_name}"
|
490
|
-
if not required and default is not None:
|
491
|
-
prompt_text += f" (default: {default})"
|
492
|
-
elif not required:
|
493
|
-
prompt_text += " (optional)"
|
494
|
-
prompt_text += ": "
|
495
|
-
|
496
|
-
Prompt, _ = _get_rich_prompts()
|
497
|
-
value = Prompt.ask(prompt_text)
|
498
|
-
if not value and not required and default is not None:
|
499
|
-
result[field_name] = default
|
500
|
-
elif not value and not required:
|
501
|
-
continue
|
502
|
-
else:
|
503
|
-
try:
|
504
|
-
result[field_name] = _validate_against_schema(value, field_type)
|
505
|
-
except InputError as e:
|
506
|
-
console.print(f"[red]Error: {e}[/red]")
|
507
|
-
result[field_name] = value
|
508
|
-
|
509
|
-
# Handle dataclasses
|
510
|
-
elif hasattr(schema, "__dataclass_fields__"):
|
511
|
-
from ..typing import get_type_description
|
512
|
-
import dataclasses
|
513
|
-
|
514
|
-
fields_info = schema.__dataclass_fields__
|
515
|
-
console.print(
|
516
|
-
f"\n[bold blue]Entering data for {schema.__name__}:[/bold blue]"
|
517
|
-
)
|
518
|
-
|
519
|
-
for field_name, field_info in fields_info.items():
|
520
|
-
field_type = field_info.type
|
521
|
-
default = getattr(field_info, "default", None)
|
522
|
-
|
523
|
-
prompt_text = f" {field_name}"
|
524
|
-
if default is not None and default is not dataclasses.MISSING:
|
525
|
-
prompt_text += f" (default: {default})"
|
526
|
-
elif hasattr(field_type, "__name__"):
|
527
|
-
prompt_text += f" ({field_type.__name__})"
|
528
|
-
else:
|
529
|
-
prompt_text += f" ({get_type_description(field_type)})"
|
530
|
-
prompt_text += ""
|
531
|
-
|
532
|
-
Prompt, _ = _get_rich_prompts()
|
533
|
-
value = Prompt.ask(prompt_text)
|
534
|
-
if (
|
535
|
-
not value
|
536
|
-
and default is not None
|
537
|
-
and default is not dataclasses.MISSING
|
538
|
-
):
|
539
|
-
result[field_name] = default
|
540
|
-
else:
|
541
|
-
try:
|
542
|
-
result[field_name] = _validate_against_schema(value, field_type)
|
543
|
-
except InputError as e:
|
544
|
-
console.print(f"[red]Error: {e}[/red]")
|
545
|
-
result[field_name] = value
|
546
|
-
|
547
|
-
# Handle TypedDict
|
548
|
-
elif hasattr(schema, "__annotations__"):
|
549
|
-
annotations = getattr(schema, "__annotations__", {})
|
550
|
-
console.print(
|
551
|
-
f"\n[bold blue]Entering data for {schema.__name__}:[/bold blue]"
|
552
|
-
)
|
553
|
-
|
554
|
-
for field_name, field_type in annotations.items():
|
555
|
-
prompt_text = f" {field_name}: "
|
556
|
-
Prompt, _ = _get_rich_prompts()
|
557
|
-
value = Prompt.ask(prompt_text)
|
558
|
-
|
559
|
-
if value:
|
560
|
-
try:
|
561
|
-
result[field_name] = _validate_against_schema(value, field_type)
|
562
|
-
except InputError as e:
|
563
|
-
console.print(f"[red]Error: {e}[/red]")
|
564
|
-
result[field_name] = value
|
565
|
-
|
566
|
-
except Exception as e:
|
567
|
-
console.print(f"[red]Error collecting fields: {e}[/red]")
|
568
|
-
|
569
|
-
return result
|
570
|
-
|
571
|
-
|
572
|
-
@overload
|
573
|
-
def input(
|
574
|
-
prompt: str = "",
|
575
|
-
schema: Any = None,
|
576
|
-
sequential: bool = True,
|
577
|
-
style: "CLIStyleType | None" = None,
|
578
|
-
style_settings: "CLIStyleRenderableSettings | None" = None,
|
579
|
-
bg: "CLIStyleBackgroundType | None" = None,
|
580
|
-
bg_settings: "CLIStyleBackgroundSettings | None" = None,
|
581
|
-
multiline: bool = False,
|
582
|
-
password: bool = False,
|
583
|
-
complete: Optional[List[str]] = None,
|
584
|
-
validate: Optional[callable] = None,
|
585
|
-
) -> Any: ...
|
586
|
-
|
587
|
-
|
588
|
-
def input(
|
589
|
-
prompt: str = "",
|
590
|
-
schema: Any = None,
|
591
|
-
sequential: bool = True,
|
592
|
-
style: "CLIStyleType | None" = None,
|
593
|
-
style_settings: "CLIStyleRenderableSettings | None" = None,
|
594
|
-
bg: "CLIStyleBackgroundType | None" = None,
|
595
|
-
bg_settings: "CLIStyleBackgroundSettings | None" = None,
|
596
|
-
multiline: bool = False,
|
597
|
-
password: bool = False,
|
598
|
-
complete: Optional[List[str]] = None,
|
599
|
-
validate: Optional[callable] = None,
|
600
|
-
) -> Any:
|
601
|
-
"""
|
602
|
-
Stylized input function built with `rich` and `prompt_toolkit`. This method maintains
|
603
|
-
compatibility with the standard input function while adding advanced features like
|
604
|
-
schema validation, styling, and structured data input.
|
605
|
-
|
606
|
-
Args:
|
607
|
-
prompt: The prompt message to display.
|
608
|
-
schema: A type, model class, or schema to validate against. Supports:
|
609
|
-
- Basic types (str, int, float, bool)
|
610
|
-
- Collections (dict, list)
|
611
|
-
- Pydantic models
|
612
|
-
- BasedModels
|
613
|
-
- Dataclasses
|
614
|
-
- TypedDict
|
615
|
-
sequential: For schemas with multiple fields, request one field at a time.
|
616
|
-
style: A color or dictionary of style settings to apply to the prompt.
|
617
|
-
bg: A color or dictionary of background settings to apply to the prompt.
|
618
|
-
multiline: Whether to allow multiline input.
|
619
|
-
password: Whether to hide the input (password mode).
|
620
|
-
complete: List of completion options.
|
621
|
-
validate: Custom validation function.
|
622
|
-
|
623
|
-
Returns:
|
624
|
-
The validated input value, converted to the appropriate type based on schema.
|
625
|
-
|
626
|
-
Raises:
|
627
|
-
InputError: If validation fails or input is invalid.
|
628
|
-
"""
|
629
|
-
get_console = _get_rich_console()
|
630
|
-
console = get_console()
|
631
|
-
|
632
|
-
try:
|
633
|
-
# If no special features are requested, use built-in input for compatibility
|
634
|
-
if (
|
635
|
-
schema is None
|
636
|
-
and style is None
|
637
|
-
and style_settings is None
|
638
|
-
and bg is None
|
639
|
-
and bg_settings is None
|
640
|
-
and not multiline
|
641
|
-
and not password
|
642
|
-
and complete is None
|
643
|
-
and validate is None
|
644
|
-
):
|
645
|
-
return builtins.input(prompt)
|
646
|
-
|
647
|
-
# Apply styling to prompt if provided
|
648
|
-
_, style_renderable = _get_style_utils()
|
649
|
-
styled_prompt = style_renderable(
|
650
|
-
prompt,
|
651
|
-
style=style,
|
652
|
-
style_settings=style_settings,
|
653
|
-
bg=bg,
|
654
|
-
bg_settings=bg_settings,
|
655
|
-
)
|
656
|
-
|
657
|
-
# Handle schema-based input
|
658
|
-
if schema is not None:
|
659
|
-
# Handle bool schema with Confirm.ask
|
660
|
-
if schema == bool:
|
661
|
-
Prompt, Confirm = _get_rich_prompts()
|
662
|
-
return Confirm.ask(styled_prompt)
|
663
|
-
|
664
|
-
# Handle structured schemas with multiple fields
|
665
|
-
if sequential and (
|
666
|
-
hasattr(schema, "__annotations__")
|
667
|
-
or hasattr(schema, "model_fields")
|
668
|
-
or hasattr(schema, "_get_fields_info")
|
669
|
-
or hasattr(schema, "__dataclass_fields__")
|
670
|
-
):
|
671
|
-
field_data = _collect_fields_sequentially(schema, console)
|
672
|
-
|
673
|
-
try:
|
674
|
-
# Create instance from collected data
|
675
|
-
if hasattr(schema, "model_validate"):
|
676
|
-
# Pydantic model
|
677
|
-
return schema.model_validate(field_data)
|
678
|
-
elif hasattr(schema, "__call__"):
|
679
|
-
# BasedModel, dataclass, or other callable
|
680
|
-
return schema(**field_data)
|
681
|
-
else:
|
682
|
-
# TypedDict or similar - return the dict
|
683
|
-
return field_data
|
684
|
-
except Exception as e:
|
685
|
-
console.print(f"[red]Error creating {schema.__name__}: {e}[/red]")
|
686
|
-
return field_data
|
687
|
-
|
688
|
-
# Handle single value input
|
689
|
-
Prompt, Confirm = _get_rich_prompts()
|
690
|
-
if password:
|
691
|
-
value = Prompt.ask(styled_prompt, password=True)
|
692
|
-
elif complete:
|
693
|
-
# Use prompt_toolkit for completion
|
694
|
-
pt_prompt, WordCompleter = _get_prompt_toolkit()
|
695
|
-
completer = WordCompleter(complete)
|
696
|
-
value = pt_prompt(str(styled_prompt), completer=completer)
|
697
|
-
elif multiline:
|
698
|
-
console.print(styled_prompt, end="")
|
699
|
-
lines = []
|
700
|
-
console.print("[dim](Enter empty line to finish)[/dim]")
|
701
|
-
pt_prompt, _ = _get_prompt_toolkit()
|
702
|
-
while True:
|
703
|
-
line = pt_prompt("... ")
|
704
|
-
if not line:
|
705
|
-
break
|
706
|
-
lines.append(line)
|
707
|
-
value = "\n".join(lines)
|
708
|
-
else:
|
709
|
-
# Regular input with Rich prompt
|
710
|
-
value = Prompt.ask(styled_prompt)
|
711
|
-
|
712
|
-
# Apply custom validation
|
713
|
-
if validate:
|
714
|
-
try:
|
715
|
-
if not validate(value):
|
716
|
-
raise InputError("Custom validation failed")
|
717
|
-
except Exception as e:
|
718
|
-
raise InputError(f"Validation error: {e}")
|
719
|
-
|
720
|
-
# Apply schema validation
|
721
|
-
if schema is not None:
|
722
|
-
return _validate_against_schema(value, schema)
|
723
|
-
|
724
|
-
return value
|
725
|
-
|
726
|
-
except KeyboardInterrupt:
|
727
|
-
console.print("\n[yellow]Input cancelled by user[/yellow]")
|
728
|
-
raise
|
729
|
-
except InputError:
|
730
|
-
raise
|
731
|
-
except Exception as e:
|
732
|
-
raise InputError(f"Input error: {e}")
|
733
|
-
|
734
|
-
|
735
|
-
def animate(
|
736
|
-
renderable: "RenderableType | str",
|
737
|
-
type: Literal[
|
738
|
-
"flashing", "pulsing", "shaking", "typing", "spinning", "rainbow"
|
739
|
-
] = "pulsing",
|
740
|
-
duration: Optional[float] = None,
|
741
|
-
# Animation parameters (defaults are handled by the specific animation classes)
|
742
|
-
speed: Optional[float] = None,
|
743
|
-
colors: "Optional[List[CLIStyleColorName]]" = None,
|
744
|
-
on_color: "Optional[CLIStyleColorName]" = None,
|
745
|
-
off_color: "Optional[CLIStyleColorName]" = None,
|
746
|
-
min_opacity: Optional[float] = None,
|
747
|
-
max_opacity: Optional[float] = None,
|
748
|
-
color: "Optional[CLIStyleColorName]" = None,
|
749
|
-
intensity: Optional[int] = None,
|
750
|
-
typing_speed: Optional[float] = None,
|
751
|
-
cursor: Optional[str] = None,
|
752
|
-
show_cursor: Optional[bool] = None,
|
753
|
-
frames: Optional[List[str]] = None,
|
754
|
-
prefix: Optional[bool] = None,
|
755
|
-
# Rich.Live parameters
|
756
|
-
refresh_rate: int = 20,
|
757
|
-
transient: bool = True,
|
758
|
-
auto_refresh: bool = True,
|
759
|
-
console: Optional["Console"] = None,
|
760
|
-
screen: bool = False,
|
761
|
-
vertical_overflow: str = "ellipsis",
|
762
|
-
) -> None:
|
763
|
-
"""Create and run an animation based on the specified type.
|
764
|
-
|
765
|
-
Args:
|
766
|
-
renderable: The object to animate (text, panel, etc.)
|
767
|
-
type: The type of animation to create
|
768
|
-
duration: Duration of the animation in seconds (defaults to 2.0)
|
769
|
-
speed: Animation speed (defaults to the specific animation class's default)
|
770
|
-
colors: Color list (used by flashing, rainbow) (defaults to the specific animation class's default)
|
771
|
-
on_color: Color when flashing "on" (used by flashing) (defaults to the specific animation class's default)
|
772
|
-
off_color: Color when flashing "off" (used by flashing) (defaults to the specific animation class's default)
|
773
|
-
min_opacity: Minimum opacity for pulsing animation (defaults to the specific animation class's default)
|
774
|
-
max_opacity: Maximum opacity for pulsing animation (defaults to the specific animation class's default)
|
775
|
-
color: Color for pulsing animation (defaults to the specific animation class's default)
|
776
|
-
intensity: Shaking intensity for shaking animation (defaults to the specific animation class's default)
|
777
|
-
typing_speed: Speed for typing animation (used by typing) (defaults to the specific animation class's default)
|
778
|
-
cursor: Cursor character for typing animation (used by typing) (defaults to the specific animation class's default)
|
779
|
-
show_cursor: Whether to show cursor for typing animation (used by typing) (defaults to the specific animation class's default)
|
780
|
-
frames: Custom frames for spinning animation (defaults to the specific animation class's default)
|
781
|
-
prefix: Whether to show spinner as prefix for spinning animation (defaults to the specific animation class's default)
|
782
|
-
refresh_rate: Refresh rate per second for Live rendering
|
783
|
-
transient: Whether to clear animation after completion
|
784
|
-
auto_refresh: Whether to auto-refresh the display
|
785
|
-
console: Console to use for rendering
|
786
|
-
screen: Whether to use alternate screen buffer
|
787
|
-
vertical_overflow: How to handle vertical overflow
|
788
|
-
|
789
|
-
Examples:
|
790
|
-
>>> animate("Hello!", type="flashing", duration=3.0, speed=0.3)
|
791
|
-
>>> animate(Panel("Loading"), type="pulsing", min_opacity=0.1)
|
792
|
-
>>> animate("Hello World!", type="typing", typing_speed=0.1)
|
793
|
-
>>> animate("Colorful!", type="rainbow", colors=["red", "blue"])
|
794
|
-
"""
|
795
|
-
animations = _get_animation_classes()
|
796
|
-
|
797
|
-
if type == "flashing":
|
798
|
-
animation = animations["CLIFlashingAnimation"](
|
799
|
-
renderable,
|
800
|
-
speed=speed if speed is not None else 0.5,
|
801
|
-
colors=colors, # Class handles default if None
|
802
|
-
on_color=on_color if on_color is not None else "white",
|
803
|
-
off_color=off_color if off_color is not None else "dim white",
|
804
|
-
duration=duration, # Base class handles default if None
|
805
|
-
)
|
806
|
-
elif type == "pulsing":
|
807
|
-
animation = animations["CLIPulsingAnimation"](
|
808
|
-
renderable,
|
809
|
-
speed=speed if speed is not None else 2.0,
|
810
|
-
min_opacity=min_opacity if min_opacity is not None else 0.3,
|
811
|
-
max_opacity=max_opacity if max_opacity is not None else 1.0,
|
812
|
-
color=color if color is not None else "white",
|
813
|
-
duration=duration, # Base class handles default if None
|
814
|
-
)
|
815
|
-
elif type == "shaking":
|
816
|
-
animation = animations["CLIShakingAnimation"](
|
817
|
-
renderable,
|
818
|
-
intensity=intensity if intensity is not None else 1,
|
819
|
-
speed=speed if speed is not None else 0.1,
|
820
|
-
duration=duration, # Base class handles default if None
|
821
|
-
)
|
822
|
-
elif type == "typing":
|
823
|
-
# Note: CLITypingAnimation expects 'text', assuming renderable is a string here.
|
824
|
-
animation = animations[
|
825
|
-
"CLITypingAnimation"
|
826
|
-
](
|
827
|
-
renderable,
|
828
|
-
speed=speed
|
829
|
-
if speed is not None
|
830
|
-
else 0.05, # Pass animate's speed, using CLITypingAnimation's speed default
|
831
|
-
typing_speed=typing_speed, # Pass animate's typing_speed, CLITypingAnimation handles its None default
|
832
|
-
cursor=cursor if cursor is not None else "█",
|
833
|
-
show_cursor=show_cursor if show_cursor is not None else True,
|
834
|
-
duration=duration, # Base class handles default if None
|
835
|
-
)
|
836
|
-
elif type == "spinning":
|
837
|
-
animation = animations["CLISpinningAnimation"](
|
838
|
-
renderable,
|
839
|
-
frames=frames, # Class handles default if None
|
840
|
-
speed=speed if speed is not None else 0.1,
|
841
|
-
prefix=prefix if prefix is not None else True,
|
842
|
-
duration=duration, # Base class handles default if None
|
843
|
-
)
|
844
|
-
elif type == "rainbow":
|
845
|
-
animation = animations["CLIRainbowAnimation"](
|
846
|
-
renderable,
|
847
|
-
speed=speed if speed is not None else 0.5,
|
848
|
-
colors=colors, # Class handles default if None
|
849
|
-
duration=duration, # Base class handles default if None
|
850
|
-
)
|
851
|
-
else:
|
852
|
-
raise ValueError(f"Unknown animation type: {type}")
|
853
|
-
|
854
|
-
# The animation object's animate method handles its own duration default
|
855
|
-
# and the Live parameters are passed directly from the function args
|
856
|
-
animation.animate(
|
857
|
-
duration=duration,
|
858
|
-
refresh_rate=refresh_rate,
|
859
|
-
transient=transient,
|
860
|
-
auto_refresh=auto_refresh,
|
861
|
-
console=console,
|
862
|
-
screen=screen,
|
863
|
-
vertical_overflow=vertical_overflow,
|
864
|
-
)
|
865
|
-
|
866
|
-
|
867
|
-
__all__ = ("print", "input", "animate")
|