hammad-python 0.0.23__py3-none-any.whl → 0.0.25__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 (40) hide show
  1. hammad/__init__.py +62 -14
  2. hammad/_main.py +226 -0
  3. hammad/cli/__init__.py +0 -2
  4. hammad/cli/plugins.py +3 -1
  5. hammad/data/__init__.py +4 -5
  6. hammad/data/types/__init__.py +37 -1
  7. hammad/data/types/file.py +74 -1
  8. hammad/data/types/multimodal/__init__.py +14 -2
  9. hammad/data/types/multimodal/audio.py +106 -2
  10. hammad/data/types/multimodal/image.py +104 -2
  11. hammad/data/types/text.py +242 -0
  12. hammad/genai/__init__.py +73 -0
  13. hammad/genai/a2a/__init__.py +32 -0
  14. hammad/genai/a2a/workers.py +552 -0
  15. hammad/genai/agents/__init__.py +8 -0
  16. hammad/genai/agents/agent.py +747 -214
  17. hammad/genai/agents/run.py +421 -12
  18. hammad/genai/agents/types/agent_response.py +2 -1
  19. hammad/genai/graphs/__init__.py +125 -0
  20. hammad/genai/graphs/base.py +1786 -0
  21. hammad/genai/graphs/plugins.py +316 -0
  22. hammad/genai/graphs/types.py +638 -0
  23. hammad/genai/models/language/__init__.py +6 -1
  24. hammad/genai/models/language/model.py +46 -0
  25. hammad/genai/models/language/run.py +330 -4
  26. hammad/genai/models/language/types/language_model_response.py +1 -1
  27. hammad/genai/types/tools.py +1 -1
  28. hammad/logging/logger.py +60 -5
  29. hammad/mcp/__init__.py +3 -0
  30. hammad/types.py +288 -0
  31. {hammad_python-0.0.23.dist-info → hammad_python-0.0.25.dist-info}/METADATA +6 -1
  32. {hammad_python-0.0.23.dist-info → hammad_python-0.0.25.dist-info}/RECORD +34 -32
  33. hammad/_main/__init__.py +0 -4
  34. hammad/_main/_fn.py +0 -20
  35. hammad/_main/_new.py +0 -52
  36. hammad/_main/_run.py +0 -50
  37. hammad/_main/_to.py +0 -19
  38. hammad/cli/_runner.py +0 -265
  39. {hammad_python-0.0.23.dist-info → hammad_python-0.0.25.dist-info}/WHEEL +0 -0
  40. {hammad_python-0.0.23.dist-info → hammad_python-0.0.25.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,19 @@
1
1
  """hammad.data.types.multimodal.image"""
2
2
 
3
3
  import httpx
4
+ import mimetypes
5
+ from pathlib import Path
4
6
  from typing import Self
5
7
 
6
- from ...types.file import File, FileSource
8
+ from ...types.file import File, FileSource, _FILE_SIGNATURES
7
9
  from ...models.fields import field
8
10
 
9
- __all__ = ("Image",)
11
+
12
+ __all__ = (
13
+ "Image",
14
+ "read_image_from_path",
15
+ "read_image_from_url",
16
+ )
10
17
 
11
18
 
12
19
  class Image(File):
@@ -78,3 +85,98 @@ class Image(File):
78
85
  size=size,
79
86
  ),
80
87
  )
88
+
89
+ @classmethod
90
+ def from_path(
91
+ cls,
92
+ path: str | Path,
93
+ ) -> Self:
94
+ """Create an image from a file path.
95
+
96
+ Args:
97
+ path: The path to the image file.
98
+
99
+ Returns:
100
+ A new Image instance.
101
+
102
+ Raises:
103
+ FileNotFoundError: If the file does not exist.
104
+ ValueError: If the file is not a valid image.
105
+ """
106
+ path_obj = Path(path)
107
+
108
+ if not path_obj.exists():
109
+ raise FileNotFoundError(f"Image file not found: {path}")
110
+
111
+ if not path_obj.is_file():
112
+ raise ValueError(f"Path is not a file: {path}")
113
+
114
+ # Read the file data
115
+ data = path_obj.read_bytes()
116
+
117
+ # Detect MIME type
118
+ type = None
119
+ for sig, mime in _FILE_SIGNATURES.items():
120
+ if data.startswith(sig):
121
+ type = mime
122
+ break
123
+
124
+ # Fallback to mimetypes module
125
+ if not type:
126
+ type, _ = mimetypes.guess_type(str(path))
127
+
128
+ # Validate it's an image
129
+ if type and not type.startswith("image/"):
130
+ raise ValueError(f"File is not an image: {type}")
131
+
132
+ return cls(
133
+ data=data,
134
+ type=type,
135
+ source=FileSource(
136
+ is_file=True,
137
+ path=path_obj,
138
+ size=len(data),
139
+ ),
140
+ )
141
+
142
+
143
+ def read_image_from_url(
144
+ url: str,
145
+ *,
146
+ lazy: bool = True,
147
+ timeout: float = 30.0,
148
+ ) -> Image:
149
+ """Download and create an image from a URL.
150
+
151
+ Args:
152
+ url: The URL to download from.
153
+ lazy: If True, defer loading content until needed.
154
+ timeout: Request timeout in seconds.
155
+
156
+ Returns:
157
+ A new Image instance.
158
+
159
+ Raises:
160
+ httpx.RequestError: If the request fails.
161
+ httpx.HTTPStatusError: If the response has an error status code.
162
+ ValueError: If the URL does not point to an image.
163
+ """
164
+ return Image.from_url(url, lazy=lazy, timeout=timeout)
165
+
166
+
167
+ def read_image_from_path(
168
+ path: str | Path,
169
+ ) -> Image:
170
+ """Create an image from a file path.
171
+
172
+ Args:
173
+ path: The path to the image file.
174
+
175
+ Returns:
176
+ A new Image instance.
177
+
178
+ Raises:
179
+ FileNotFoundError: If the file does not exist.
180
+ ValueError: If the file is not a valid image.
181
+ """
182
+ return Image.from_path(path)
hammad/data/types/text.py CHANGED
@@ -1054,6 +1054,242 @@ class Text(BaseText, Generic[T]):
1054
1054
  return self.render(OutputFormat.ANY)
1055
1055
 
1056
1056
 
1057
+ def convert_to_simple_text(
1058
+ obj: Any,
1059
+ *,
1060
+ title: Optional[str] = None,
1061
+ description: Optional[str] = None,
1062
+ type: str = "simple",
1063
+ heading_level: int = 2,
1064
+ show_in_toc: bool = True,
1065
+ collapsible: bool = False,
1066
+ metadata: Optional[Dict[str, Any]] = None,
1067
+ sections: Optional[List["BaseText"]] = None,
1068
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1069
+ **kwargs,
1070
+ ) -> "SimpleText":
1071
+ """Convert any object to a SimpleText instance (fully parameterized)."""
1072
+ content = convert_to_text(obj)
1073
+ return SimpleText(
1074
+ content=content,
1075
+ title=title,
1076
+ description=description,
1077
+ type=type,
1078
+ heading_level=heading_level,
1079
+ show_in_toc=show_in_toc,
1080
+ collapsible=collapsible,
1081
+ metadata=metadata or {},
1082
+ sections=sections or [],
1083
+ format_config=format_config or {},
1084
+ **kwargs,
1085
+ )
1086
+
1087
+
1088
+ def convert_to_output_text(
1089
+ obj: Any,
1090
+ *,
1091
+ title: Optional[str] = None,
1092
+ description: Optional[str] = None,
1093
+ type: str = "output",
1094
+ heading_level: int = 2,
1095
+ show_in_toc: bool = True,
1096
+ collapsible: bool = False,
1097
+ metadata: Optional[Dict[str, Any]] = None,
1098
+ sections: Optional[List["BaseText"]] = None,
1099
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1100
+ output_schema: Optional[Any] = None,
1101
+ examples: Optional[List[Any]] = None,
1102
+ validation_rules: Optional[List[str]] = None,
1103
+ error_cases: Optional[List[Dict[str, Any]]] = None,
1104
+ **kwargs,
1105
+ ) -> "OutputText":
1106
+ """Convert any object to an OutputText instance (fully parameterized)."""
1107
+ content = convert_to_text(obj)
1108
+ return OutputText(
1109
+ content=content,
1110
+ title=title,
1111
+ description=description,
1112
+ type=type,
1113
+ heading_level=heading_level,
1114
+ show_in_toc=show_in_toc,
1115
+ collapsible=collapsible,
1116
+ metadata=metadata or {},
1117
+ sections=sections or [],
1118
+ format_config=format_config or {},
1119
+ output_schema=output_schema,
1120
+ examples=examples or [],
1121
+ validation_rules=validation_rules or [],
1122
+ error_cases=error_cases or [],
1123
+ **kwargs,
1124
+ )
1125
+
1126
+
1127
+ def convert_to_output_instructions(
1128
+ obj: Any,
1129
+ *,
1130
+ title: Optional[str] = None,
1131
+ description: Optional[str] = None,
1132
+ type: str = "output",
1133
+ heading_level: int = 2,
1134
+ show_in_toc: bool = True,
1135
+ collapsible: bool = False,
1136
+ metadata: Optional[Dict[str, Any]] = None,
1137
+ sections: Optional[List["BaseText"]] = None,
1138
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1139
+ output_schema: Optional[Any] = None,
1140
+ examples: Optional[List[Any]] = None,
1141
+ validation_rules: Optional[List[str]] = None,
1142
+ error_cases: Optional[List[Dict[str, Any]]] = None,
1143
+ as_message: bool = False,
1144
+ role: str = "user",
1145
+ as_message_content: bool = False,
1146
+ **kwargs,
1147
+ ) -> Union[str, Dict[str, Any]]:
1148
+ """
1149
+ Convert any object to output instructions, returning either:
1150
+ - a string (default)
1151
+ - a chat message dict (if as_message=True)
1152
+ - a chat message content param dict (if as_message_content=True)
1153
+
1154
+ Only one of as_message or as_message_content can be True.
1155
+ """
1156
+ if as_message and as_message_content:
1157
+ raise ValueError("Only one of as_message or as_message_content can be True.")
1158
+
1159
+ content = convert_to_text(obj)
1160
+ output_text = OutputText(
1161
+ content=content,
1162
+ title=title,
1163
+ description=description,
1164
+ type=type,
1165
+ heading_level=heading_level,
1166
+ show_in_toc=show_in_toc,
1167
+ collapsible=collapsible,
1168
+ metadata=metadata or {},
1169
+ sections=sections or [],
1170
+ format_config=format_config or {},
1171
+ output_schema=output_schema,
1172
+ examples=examples or [],
1173
+ validation_rules=validation_rules or [],
1174
+ error_cases=error_cases or [],
1175
+ **kwargs,
1176
+ )
1177
+ text_str = str(output_text)
1178
+
1179
+ if as_message:
1180
+ # Return a chat message dict
1181
+ return {"role": role, "content": text_str}
1182
+ elif as_message_content:
1183
+ # Return a chat message content param dict
1184
+ return {"type": "text", "text": text_str}
1185
+ else:
1186
+ # Return as plain string
1187
+ return text_str
1188
+
1189
+
1190
+ def convert_to_code_section(
1191
+ obj: Any,
1192
+ *,
1193
+ language: str = "python",
1194
+ title: Optional[str] = None,
1195
+ description: Optional[str] = None,
1196
+ type: str = "code",
1197
+ heading_level: int = 2,
1198
+ show_in_toc: bool = True,
1199
+ collapsible: bool = False,
1200
+ metadata: Optional[Dict[str, Any]] = None,
1201
+ sections: Optional[List["BaseText"]] = None,
1202
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1203
+ line_numbers: bool = False,
1204
+ **kwargs,
1205
+ ) -> "CodeSection":
1206
+ """Convert any object to a CodeSection instance (fully parameterized)."""
1207
+ content = convert_to_text(obj)
1208
+ return CodeSection(
1209
+ content=content,
1210
+ language=language,
1211
+ title=title,
1212
+ description=description,
1213
+ type=type,
1214
+ heading_level=heading_level,
1215
+ show_in_toc=show_in_toc,
1216
+ collapsible=collapsible,
1217
+ metadata=metadata or {},
1218
+ sections=sections or [],
1219
+ format_config=format_config or {},
1220
+ line_numbers=line_numbers,
1221
+ **kwargs,
1222
+ )
1223
+
1224
+
1225
+ def convert_to_schema_section(
1226
+ obj: Any,
1227
+ *,
1228
+ title: Optional[str] = None,
1229
+ description: Optional[str] = None,
1230
+ type: str = "schema",
1231
+ heading_level: int = 2,
1232
+ show_in_toc: bool = True,
1233
+ collapsible: bool = False,
1234
+ metadata: Optional[Dict[str, Any]] = None,
1235
+ sections: Optional[List["BaseText"]] = None,
1236
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1237
+ schema_object: Optional[Any] = None,
1238
+ show_examples: bool = True,
1239
+ table_format: bool = True,
1240
+ **kwargs,
1241
+ ) -> "SchemaSection":
1242
+ """Convert any object to a SchemaSection instance (fully parameterized)."""
1243
+ content = convert_to_text(obj)
1244
+ return SchemaSection(
1245
+ content=content,
1246
+ title=title,
1247
+ description=description,
1248
+ type=type,
1249
+ heading_level=heading_level,
1250
+ show_in_toc=show_in_toc,
1251
+ collapsible=collapsible,
1252
+ metadata=metadata or {},
1253
+ sections=sections or [],
1254
+ format_config=format_config or {},
1255
+ schema_object=schema_object or obj,
1256
+ show_examples=show_examples,
1257
+ table_format=table_format,
1258
+ **kwargs,
1259
+ )
1260
+
1261
+
1262
+ def convert_to_base_text(
1263
+ obj: Any,
1264
+ *,
1265
+ title: Optional[str] = None,
1266
+ description: Optional[str] = None,
1267
+ type: str = "base",
1268
+ heading_level: int = 2,
1269
+ show_in_toc: bool = True,
1270
+ collapsible: bool = False,
1271
+ metadata: Optional[Dict[str, Any]] = None,
1272
+ sections: Optional[List["BaseText"]] = None,
1273
+ format_config: Optional[Dict["OutputFormat", Dict[str, Any]]] = None,
1274
+ **kwargs,
1275
+ ) -> "BaseText":
1276
+ """Convert any object to a BaseText instance (fully parameterized)."""
1277
+ content = convert_to_text(obj)
1278
+ return BaseText(
1279
+ content=content,
1280
+ title=title,
1281
+ description=description,
1282
+ type=type,
1283
+ heading_level=heading_level,
1284
+ show_in_toc=show_in_toc,
1285
+ collapsible=collapsible,
1286
+ metadata=metadata or {},
1287
+ sections=sections or [],
1288
+ format_config=format_config or {},
1289
+ **kwargs,
1290
+ )
1291
+
1292
+
1057
1293
  __all__ = (
1058
1294
  "OutputFormat",
1059
1295
  "HeadingStyle",
@@ -1063,4 +1299,10 @@ __all__ = (
1063
1299
  "SimpleText",
1064
1300
  "OutputText",
1065
1301
  "Text",
1302
+ "convert_to_simple_text",
1303
+ "convert_to_output_text",
1304
+ "convert_to_output_instructions",
1305
+ "convert_to_code_section",
1306
+ "convert_to_schema_section",
1307
+ "convert_to_base_text",
1066
1308
  )
hammad/genai/__init__.py CHANGED
@@ -4,10 +4,17 @@ from typing import TYPE_CHECKING
4
4
  from .._internal import create_getattr_importer
5
5
 
6
6
 
7
+ if TYPE_CHECKING:
8
+ from .a2a import (
9
+ as_a2a_app,
10
+ GraphWorker,
11
+ AgentWorker,
12
+ )
7
13
  if TYPE_CHECKING:
8
14
  from .agents import (
9
15
  Agent,
10
16
  AgentEvent,
17
+ AgentSettings,
11
18
  AgentResponse,
12
19
  AgentStream,
13
20
  AgentContext,
@@ -20,6 +27,36 @@ if TYPE_CHECKING:
20
27
  run_agent_iter,
21
28
  async_run_agent,
22
29
  async_run_agent_iter,
30
+ agent_decorator,
31
+ )
32
+ from .graphs import (
33
+ GraphBuilder,
34
+ GraphContext,
35
+ GraphEnd,
36
+ GraphEvent,
37
+ GraphHistoryEntry,
38
+ GraphNode,
39
+ GraphState,
40
+ GraphResponse,
41
+ GraphStream,
42
+ GraphResponseChunk,
43
+ BaseGraph,
44
+ BasePlugin,
45
+ PluginDecorator,
46
+ PydanticGraphContext,
47
+ AudioPlugin,
48
+ ServePlugin,
49
+ MemoryPlugin,
50
+ HistoryPlugin,
51
+ SettingsPlugin,
52
+ ActionNode,
53
+ ActionInfo,
54
+ ActionSettings,
55
+ action,
56
+ plugin,
57
+ select,
58
+ SelectionStrategy,
59
+ ActionDecorator,
23
60
  )
24
61
  from .models.embeddings import (
25
62
  Embedding,
@@ -42,6 +79,7 @@ if TYPE_CHECKING:
42
79
  LanguageModelStream,
43
80
  run_language_model,
44
81
  async_run_language_model,
82
+ language_model_decorator,
45
83
  create_language_model,
46
84
  )
47
85
  from .models.reranking import run_reranking_model, async_run_reranking_model
@@ -73,9 +111,14 @@ if TYPE_CHECKING:
73
111
 
74
112
 
75
113
  __all__ = [
114
+ # hammad.genai.a2a
115
+ "as_a2a_app",
116
+ "GraphWorker",
117
+ "AgentWorker",
76
118
  # hammad.genai.agents.agent
77
119
  "Agent",
78
120
  "AgentEvent",
121
+ "AgentSettings",
79
122
  "AgentResponse",
80
123
  "AgentStream",
81
124
  "AgentContext",
@@ -87,6 +130,35 @@ __all__ = [
87
130
  "run_agent_iter",
88
131
  "async_run_agent",
89
132
  "async_run_agent_iter",
133
+ "agent_decorator",
134
+ # hammad.genai.graphs
135
+ "GraphBuilder",
136
+ "GraphContext",
137
+ "GraphEnd",
138
+ "GraphEvent",
139
+ "GraphHistoryEntry",
140
+ "GraphNode",
141
+ "GraphState",
142
+ "GraphResponse",
143
+ "GraphStream",
144
+ "GraphResponseChunk",
145
+ "BaseGraph",
146
+ "BasePlugin",
147
+ "PluginDecorator",
148
+ "PydanticGraphContext",
149
+ "AudioPlugin",
150
+ "ServePlugin",
151
+ "MemoryPlugin",
152
+ "HistoryPlugin",
153
+ "SettingsPlugin",
154
+ "ActionNode",
155
+ "ActionInfo",
156
+ "ActionSettings",
157
+ "action",
158
+ "plugin",
159
+ "select",
160
+ "SelectionStrategy",
161
+ "ActionDecorator",
90
162
  # hammad.genai.models.embeddings
91
163
  "Embedding",
92
164
  "EmbeddingModel",
@@ -108,6 +180,7 @@ __all__ = [
108
180
  "run_language_model",
109
181
  "async_run_language_model",
110
182
  "create_language_model",
183
+ "language_model_decorator",
111
184
  # hammad.genai.models.reranking
112
185
  "run_reranking_model",
113
186
  "async_run_reranking_model",
@@ -0,0 +1,32 @@
1
+ """hammad.genai.a2a"""
2
+
3
+ from typing import TYPE_CHECKING
4
+ from ..._internal import create_getattr_importer
5
+
6
+
7
+ if TYPE_CHECKING:
8
+ from fasta2a import (
9
+ FastA2A
10
+ )
11
+ from .workers import (
12
+ as_a2a_app,
13
+ GraphWorker,
14
+ AgentWorker,
15
+ )
16
+
17
+
18
+ __all__ = (
19
+ # fasta2a
20
+ "FastA2A",
21
+ # hammad.genai.a2a.workers
22
+ "as_a2a_app",
23
+ "GraphWorker",
24
+ "AgentWorker",
25
+ )
26
+
27
+
28
+ __getattr__ = create_getattr_importer(__all__)
29
+
30
+
31
+ def __dir__() -> list[str]:
32
+ return list(__all__)