hammad-python 0.0.24__py3-none-any.whl → 0.0.26__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.
- hammad/__init__.py +49 -268
- hammad/_main.py +226 -0
- hammad/cli/__init__.py +0 -2
- hammad/data/__init__.py +4 -5
- hammad/data/types/__init__.py +37 -1
- hammad/data/types/file.py +74 -1
- hammad/data/types/multimodal/__init__.py +14 -2
- hammad/data/types/multimodal/audio.py +106 -2
- hammad/data/types/multimodal/image.py +104 -2
- hammad/data/types/text.py +242 -0
- hammad/genai/__init__.py +22 -0
- hammad/genai/a2a/__init__.py +32 -0
- hammad/genai/a2a/workers.py +552 -0
- hammad/genai/agents/__init__.py +2 -0
- hammad/genai/agents/agent.py +115 -9
- hammad/genai/agents/run.py +379 -8
- hammad/genai/graphs/__init__.py +13 -1
- hammad/genai/graphs/_utils.py +190 -0
- hammad/genai/graphs/base.py +850 -125
- hammad/genai/graphs/types.py +2 -2
- hammad/genai/models/language/__init__.py +6 -1
- hammad/genai/models/language/run.py +308 -0
- hammad/genai/models/language/types/language_model_response.py +2 -0
- hammad/logging/logger.py +53 -8
- hammad/mcp/__init__.py +3 -0
- hammad/types.py +288 -0
- {hammad_python-0.0.24.dist-info → hammad_python-0.0.26.dist-info}/METADATA +2 -1
- {hammad_python-0.0.24.dist-info → hammad_python-0.0.26.dist-info}/RECORD +30 -26
- hammad/cli/_runner.py +0 -265
- {hammad_python-0.0.24.dist-info → hammad_python-0.0.26.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.24.dist-info → hammad_python-0.0.26.dist-info}/licenses/LICENSE +0 -0
hammad/genai/agents/agent.py
CHANGED
@@ -11,6 +11,7 @@ from typing import (
|
|
11
11
|
Optional,
|
12
12
|
Union,
|
13
13
|
Dict,
|
14
|
+
TypeAlias,
|
14
15
|
overload,
|
15
16
|
TYPE_CHECKING,
|
16
17
|
)
|
@@ -49,7 +50,10 @@ from .types.agent_hooks import HookManager, HookDecorator
|
|
49
50
|
from .types.agent_messages import AgentMessages
|
50
51
|
|
51
52
|
if TYPE_CHECKING:
|
52
|
-
|
53
|
+
try:
|
54
|
+
from fasta2a import FastA2A
|
55
|
+
except ImportError:
|
56
|
+
FastA2A: TypeAlias = Any
|
53
57
|
|
54
58
|
|
55
59
|
T = TypeVar("T")
|
@@ -1058,7 +1062,8 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1058
1062
|
f"Agent '{self.name}' completed execution in {step + 1} steps"
|
1059
1063
|
)
|
1060
1064
|
# Now we can make the final call with the output_type if specified
|
1061
|
-
|
1065
|
+
# Only make structured output call for non-str types
|
1066
|
+
if output_type and output_type != str:
|
1062
1067
|
# Make a final call with the structured output type
|
1063
1068
|
final_model_kwargs = kwargs.copy()
|
1064
1069
|
final_model_kwargs["type"] = output_type
|
@@ -1129,8 +1134,8 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1129
1134
|
# Max steps reached - return last response
|
1130
1135
|
if steps:
|
1131
1136
|
final_response = steps[-1]
|
1132
|
-
# If we have an output_type, make a final structured call
|
1133
|
-
if output_type:
|
1137
|
+
# If we have an output_type, make a final structured call (but not for str)
|
1138
|
+
if output_type and output_type != str:
|
1134
1139
|
final_model_kwargs = kwargs.copy()
|
1135
1140
|
final_model_kwargs["type"] = output_type
|
1136
1141
|
if self.instructor_mode:
|
@@ -1168,7 +1173,7 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1168
1173
|
else:
|
1169
1174
|
# No steps taken, make a final call
|
1170
1175
|
final_model_kwargs = kwargs.copy()
|
1171
|
-
if output_type:
|
1176
|
+
if output_type and output_type != str:
|
1172
1177
|
final_model_kwargs["type"] = output_type
|
1173
1178
|
if self.instructor_mode:
|
1174
1179
|
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
@@ -1444,7 +1449,8 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1444
1449
|
|
1445
1450
|
# This is the final step (either no end_strategy or end_tool was called)
|
1446
1451
|
# Now we can make the final call with the output_type if specified
|
1447
|
-
|
1452
|
+
# Only make structured output call for non-str types
|
1453
|
+
if output_type and output_type != str:
|
1448
1454
|
# Make a final call with the structured output type
|
1449
1455
|
final_model_kwargs = kwargs.copy()
|
1450
1456
|
final_model_kwargs["type"] = output_type
|
@@ -1515,8 +1521,8 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1515
1521
|
# Max steps reached - return last response
|
1516
1522
|
if steps:
|
1517
1523
|
final_response = steps[-1]
|
1518
|
-
# If we have an output_type, make a final structured call
|
1519
|
-
if output_type:
|
1524
|
+
# If we have an output_type, make a final structured call (but not for str)
|
1525
|
+
if output_type and output_type != str:
|
1520
1526
|
final_model_kwargs = kwargs.copy()
|
1521
1527
|
final_model_kwargs["type"] = output_type
|
1522
1528
|
if self.instructor_mode:
|
@@ -1554,7 +1560,7 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1554
1560
|
else:
|
1555
1561
|
# No steps taken, make a final call
|
1556
1562
|
final_model_kwargs = kwargs.copy()
|
1557
|
-
if output_type:
|
1563
|
+
if output_type and output_type != str:
|
1558
1564
|
final_model_kwargs["type"] = output_type
|
1559
1565
|
if self.instructor_mode:
|
1560
1566
|
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
@@ -1621,6 +1627,106 @@ Please update the appropriate fields based on the conversation. Only update fiel
|
|
1621
1627
|
**kwargs,
|
1622
1628
|
)
|
1623
1629
|
|
1630
|
+
def as_a2a(
|
1631
|
+
self,
|
1632
|
+
*,
|
1633
|
+
# Worker configuration
|
1634
|
+
context: Optional[AgentContext] = None,
|
1635
|
+
# Storage and broker configuration
|
1636
|
+
storage: Optional[Any] = None,
|
1637
|
+
broker: Optional[Any] = None,
|
1638
|
+
# Server configuration
|
1639
|
+
host: str = "0.0.0.0",
|
1640
|
+
port: int = 8000,
|
1641
|
+
reload: bool = False,
|
1642
|
+
workers: int = 1,
|
1643
|
+
log_level: str = "info",
|
1644
|
+
# A2A configuration
|
1645
|
+
name: Optional[str] = None,
|
1646
|
+
url: Optional[str] = None,
|
1647
|
+
version: str = "1.0.0",
|
1648
|
+
description: Optional[str] = None,
|
1649
|
+
# Advanced configuration
|
1650
|
+
lifespan_timeout: int = 30,
|
1651
|
+
**uvicorn_kwargs: Any,
|
1652
|
+
) -> "FastA2A": # type: ignore
|
1653
|
+
"""
|
1654
|
+
Convert this agent to an A2A server application.
|
1655
|
+
|
1656
|
+
This method creates a FastA2A server that can handle A2A requests
|
1657
|
+
for this agent instance. It sets up the necessary Worker, Storage,
|
1658
|
+
and Broker components automatically.
|
1659
|
+
|
1660
|
+
Args:
|
1661
|
+
context: Initial context for the agent
|
1662
|
+
storage: Custom storage backend (defaults to InMemoryStorage)
|
1663
|
+
broker: Custom broker backend (defaults to InMemoryBroker)
|
1664
|
+
host: Host to bind the server to
|
1665
|
+
port: Port to bind the server to
|
1666
|
+
reload: Enable auto-reload for development
|
1667
|
+
workers: Number of worker processes
|
1668
|
+
log_level: Logging level
|
1669
|
+
name: Agent name for the A2A server (defaults to agent's name)
|
1670
|
+
url: URL where the agent is hosted
|
1671
|
+
version: API version
|
1672
|
+
description: API description for the A2A server (defaults to agent's description)
|
1673
|
+
lifespan_timeout: Timeout for lifespan events
|
1674
|
+
**uvicorn_kwargs: Additional arguments passed to uvicorn
|
1675
|
+
|
1676
|
+
Returns:
|
1677
|
+
FastA2A application instance that can be run with uvicorn
|
1678
|
+
|
1679
|
+
Examples:
|
1680
|
+
Convert agent to A2A server:
|
1681
|
+
```python
|
1682
|
+
agent = Agent(
|
1683
|
+
name="assistant",
|
1684
|
+
instructions="You are a helpful assistant",
|
1685
|
+
model="openai/gpt-4o-mini"
|
1686
|
+
)
|
1687
|
+
|
1688
|
+
app = agent.as_a2a(port=8080)
|
1689
|
+
|
1690
|
+
# Run with uvicorn
|
1691
|
+
import uvicorn
|
1692
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
1693
|
+
```
|
1694
|
+
|
1695
|
+
Or use the CLI:
|
1696
|
+
```bash
|
1697
|
+
uvicorn mymodule:agent.as_a2a() --reload
|
1698
|
+
```
|
1699
|
+
|
1700
|
+
With custom configuration:
|
1701
|
+
```python
|
1702
|
+
app = agent.as_a2a(
|
1703
|
+
name="My Assistant API",
|
1704
|
+
description="A helpful AI assistant",
|
1705
|
+
host="localhost",
|
1706
|
+
port=3000
|
1707
|
+
)
|
1708
|
+
```
|
1709
|
+
"""
|
1710
|
+
from ..a2a import as_a2a_app
|
1711
|
+
|
1712
|
+
return as_a2a_app(
|
1713
|
+
self,
|
1714
|
+
context=context,
|
1715
|
+
storage=storage,
|
1716
|
+
broker=broker,
|
1717
|
+
host=host,
|
1718
|
+
port=port,
|
1719
|
+
reload=reload,
|
1720
|
+
workers=workers,
|
1721
|
+
log_level=log_level,
|
1722
|
+
name=name or self.name,
|
1723
|
+
url=url,
|
1724
|
+
version=version,
|
1725
|
+
description=description or self.description,
|
1726
|
+
lifespan_timeout=lifespan_timeout,
|
1727
|
+
**uvicorn_kwargs,
|
1728
|
+
)
|
1729
|
+
|
1624
1730
|
def iter(
|
1625
1731
|
self,
|
1626
1732
|
messages: AgentMessages,
|
hammad/genai/agents/run.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
Standalone functions for running agents with full parameter typing.
|
4
4
|
"""
|
5
5
|
|
6
|
+
import functools
|
6
7
|
from typing import (
|
7
8
|
Any,
|
8
9
|
Callable,
|
@@ -40,6 +41,7 @@ __all__ = [
|
|
40
41
|
"async_run_agent",
|
41
42
|
"run_agent_iter",
|
42
43
|
"async_run_agent_iter",
|
44
|
+
"agent_decorator",
|
43
45
|
]
|
44
46
|
|
45
47
|
T = TypeVar("T")
|
@@ -209,8 +211,20 @@ def run_agent(
|
|
209
211
|
... context=context
|
210
212
|
... )
|
211
213
|
"""
|
212
|
-
agent
|
213
|
-
|
214
|
+
# Separate agent constructor parameters from run parameters
|
215
|
+
agent_constructor_params = {
|
216
|
+
k: v
|
217
|
+
for k, v in kwargs.items()
|
218
|
+
if k in ["name", "instructions", "description", "tools", "settings", "model"]
|
219
|
+
}
|
220
|
+
agent_run_params = {
|
221
|
+
k: v
|
222
|
+
for k, v in kwargs.items()
|
223
|
+
if k not in ["name", "instructions", "description", "tools", "settings"]
|
224
|
+
}
|
225
|
+
|
226
|
+
agent = Agent(verbose=verbose, debug=debug, **agent_constructor_params)
|
227
|
+
return agent.run(messages, verbose=verbose, debug=debug, **agent_run_params)
|
214
228
|
|
215
229
|
|
216
230
|
# Overloads for async_run_agent
|
@@ -371,8 +385,22 @@ async def async_run_agent(
|
|
371
385
|
... )
|
372
386
|
... return response.output
|
373
387
|
"""
|
374
|
-
agent
|
375
|
-
|
388
|
+
# Separate agent constructor parameters from run parameters
|
389
|
+
agent_constructor_params = {
|
390
|
+
k: v
|
391
|
+
for k, v in kwargs.items()
|
392
|
+
if k in ["name", "instructions", "description", "tools", "settings", "model"]
|
393
|
+
}
|
394
|
+
agent_run_params = {
|
395
|
+
k: v
|
396
|
+
for k, v in kwargs.items()
|
397
|
+
if k not in ["name", "instructions", "description", "tools", "settings"]
|
398
|
+
}
|
399
|
+
|
400
|
+
agent = Agent(verbose=verbose, debug=debug, **agent_constructor_params)
|
401
|
+
return await agent.async_run(
|
402
|
+
messages, verbose=verbose, debug=debug, **agent_run_params
|
403
|
+
)
|
376
404
|
|
377
405
|
|
378
406
|
# Overloads for run_agent_iter
|
@@ -545,8 +573,22 @@ def run_agent_iter(
|
|
545
573
|
... except Exception as e:
|
546
574
|
... print(f"Stream error: {e}")
|
547
575
|
"""
|
548
|
-
agent
|
549
|
-
|
576
|
+
# Separate agent constructor parameters from run parameters
|
577
|
+
agent_constructor_params = {
|
578
|
+
k: v
|
579
|
+
for k, v in kwargs.items()
|
580
|
+
if k in ["name", "instructions", "description", "tools", "settings", "model"]
|
581
|
+
}
|
582
|
+
agent_run_params = {
|
583
|
+
k: v
|
584
|
+
for k, v in kwargs.items()
|
585
|
+
if k not in ["name", "instructions", "description", "tools", "settings"]
|
586
|
+
}
|
587
|
+
|
588
|
+
agent = Agent(verbose=verbose, debug=debug, **agent_constructor_params)
|
589
|
+
return agent.run(
|
590
|
+
messages, stream=True, verbose=verbose, debug=debug, **agent_run_params
|
591
|
+
)
|
550
592
|
|
551
593
|
|
552
594
|
# Overloads for async_run_agent_iter
|
@@ -649,5 +691,334 @@ def async_run_agent_iter(
|
|
649
691
|
Returns:
|
650
692
|
An AgentStream that can be iterated over asynchronously
|
651
693
|
"""
|
652
|
-
agent
|
653
|
-
|
694
|
+
# Separate agent constructor parameters from run parameters
|
695
|
+
agent_constructor_params = {
|
696
|
+
k: v
|
697
|
+
for k, v in kwargs.items()
|
698
|
+
if k in ["name", "instructions", "description", "tools", "settings", "model"]
|
699
|
+
}
|
700
|
+
agent_run_params = {
|
701
|
+
k: v
|
702
|
+
for k, v in kwargs.items()
|
703
|
+
if k not in ["name", "instructions", "description", "tools", "settings"]
|
704
|
+
}
|
705
|
+
|
706
|
+
agent = Agent(verbose=verbose, debug=debug, **agent_constructor_params)
|
707
|
+
return agent.run(
|
708
|
+
messages, stream=True, verbose=verbose, debug=debug, **agent_run_params
|
709
|
+
)
|
710
|
+
|
711
|
+
|
712
|
+
def agent_decorator(
|
713
|
+
fn: Union[str, Callable, None] = None,
|
714
|
+
*,
|
715
|
+
# Agent settings
|
716
|
+
name: Optional[str] = None,
|
717
|
+
instructions: Optional[str] = None,
|
718
|
+
description: Optional[str] = None,
|
719
|
+
tools: Union[List["Tool"], Callable, None] = None,
|
720
|
+
settings: Optional[AgentSettings] = None,
|
721
|
+
# Context management
|
722
|
+
context: Optional["AgentContext"] = None,
|
723
|
+
context_updates: Optional[
|
724
|
+
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
725
|
+
] = None,
|
726
|
+
context_confirm: bool = False,
|
727
|
+
context_strategy: Literal["selective", "all"] = "all",
|
728
|
+
context_max_retries: int = 3,
|
729
|
+
context_confirm_instructions: Optional[str] = None,
|
730
|
+
context_selection_instructions: Optional[str] = None,
|
731
|
+
context_update_instructions: Optional[str] = None,
|
732
|
+
context_format: Literal["json", "python", "markdown"] = "json",
|
733
|
+
# Model settings
|
734
|
+
model: Optional[Union["LanguageModel", "LanguageModelName"]] = None,
|
735
|
+
max_steps: Optional[int] = None,
|
736
|
+
instructor_mode: Optional["LanguageModelInstructorMode"] = None,
|
737
|
+
return_output: bool = True,
|
738
|
+
# End strategy
|
739
|
+
end_strategy: Optional[Literal["tool"]] = None,
|
740
|
+
end_tool: Optional[Callable] = None,
|
741
|
+
# LM settings
|
742
|
+
timeout: Optional[Union[float, str, "Timeout"]] = None,
|
743
|
+
temperature: Optional[float] = None,
|
744
|
+
top_p: Optional[float] = None,
|
745
|
+
max_tokens: Optional[int] = None,
|
746
|
+
presence_penalty: Optional[float] = None,
|
747
|
+
frequency_penalty: Optional[float] = None,
|
748
|
+
seed: Optional[int] = None,
|
749
|
+
user: Optional[str] = None,
|
750
|
+
verbose: bool = False,
|
751
|
+
debug: bool = False,
|
752
|
+
):
|
753
|
+
"""Decorator that converts a function into an agent.
|
754
|
+
|
755
|
+
The function's parameters become the input to the LLM (converted to a string),
|
756
|
+
the function's return type annotation becomes the agent's output type,
|
757
|
+
and the function's docstring becomes the agent's instructions.
|
758
|
+
|
759
|
+
Works with both sync and async functions.
|
760
|
+
|
761
|
+
Can be used in multiple ways:
|
762
|
+
|
763
|
+
1. As a decorator with parameters:
|
764
|
+
@agent_decorator(name="steve", temperature=0.7)
|
765
|
+
def my_agent():
|
766
|
+
pass
|
767
|
+
|
768
|
+
2. As a decorator without parameters:
|
769
|
+
@agent_decorator
|
770
|
+
def my_agent():
|
771
|
+
pass
|
772
|
+
|
773
|
+
3. As an inline function with name as first argument:
|
774
|
+
agent = agent_decorator("steve")
|
775
|
+
# Then use: decorated_func = agent(my_function)
|
776
|
+
|
777
|
+
4. As an inline function with all parameters:
|
778
|
+
agent = agent_decorator(name="steve", temperature=0.7)
|
779
|
+
# Then use: decorated_func = agent(my_function)
|
780
|
+
"""
|
781
|
+
# Handle different calling patterns
|
782
|
+
if callable(fn):
|
783
|
+
# Case: @agent_decorator (no parentheses)
|
784
|
+
func = fn
|
785
|
+
actual_name = name or "agent"
|
786
|
+
return _create_agent_wrapper(
|
787
|
+
func,
|
788
|
+
actual_name,
|
789
|
+
instructions,
|
790
|
+
description,
|
791
|
+
tools,
|
792
|
+
settings,
|
793
|
+
context,
|
794
|
+
context_updates,
|
795
|
+
context_confirm,
|
796
|
+
context_strategy,
|
797
|
+
context_max_retries,
|
798
|
+
context_confirm_instructions,
|
799
|
+
context_selection_instructions,
|
800
|
+
context_update_instructions,
|
801
|
+
context_format,
|
802
|
+
model,
|
803
|
+
max_steps,
|
804
|
+
instructor_mode,
|
805
|
+
return_output,
|
806
|
+
end_strategy,
|
807
|
+
end_tool,
|
808
|
+
timeout,
|
809
|
+
temperature,
|
810
|
+
top_p,
|
811
|
+
max_tokens,
|
812
|
+
presence_penalty,
|
813
|
+
frequency_penalty,
|
814
|
+
seed,
|
815
|
+
user,
|
816
|
+
verbose,
|
817
|
+
debug,
|
818
|
+
)
|
819
|
+
elif isinstance(fn, str):
|
820
|
+
# Case: agent_decorator("steve") - first arg is name
|
821
|
+
actual_name = fn
|
822
|
+
else:
|
823
|
+
# Case: agent_decorator() or agent_decorator(name="steve")
|
824
|
+
actual_name = name or "agent"
|
825
|
+
|
826
|
+
def decorator(func: Callable) -> Callable:
|
827
|
+
return _create_agent_wrapper(
|
828
|
+
func,
|
829
|
+
actual_name,
|
830
|
+
instructions,
|
831
|
+
description,
|
832
|
+
tools,
|
833
|
+
settings,
|
834
|
+
context,
|
835
|
+
context_updates,
|
836
|
+
context_confirm,
|
837
|
+
context_strategy,
|
838
|
+
context_max_retries,
|
839
|
+
context_confirm_instructions,
|
840
|
+
context_selection_instructions,
|
841
|
+
context_update_instructions,
|
842
|
+
context_format,
|
843
|
+
model,
|
844
|
+
max_steps,
|
845
|
+
instructor_mode,
|
846
|
+
return_output,
|
847
|
+
end_strategy,
|
848
|
+
end_tool,
|
849
|
+
timeout,
|
850
|
+
temperature,
|
851
|
+
top_p,
|
852
|
+
max_tokens,
|
853
|
+
presence_penalty,
|
854
|
+
frequency_penalty,
|
855
|
+
seed,
|
856
|
+
user,
|
857
|
+
verbose,
|
858
|
+
debug,
|
859
|
+
)
|
860
|
+
|
861
|
+
return decorator
|
862
|
+
|
863
|
+
|
864
|
+
def _create_agent_wrapper(
|
865
|
+
func: Callable,
|
866
|
+
name: str,
|
867
|
+
instructions: Optional[str],
|
868
|
+
description: Optional[str],
|
869
|
+
tools: Union[List["Tool"], Callable, None],
|
870
|
+
settings: Optional[AgentSettings],
|
871
|
+
context: Optional["AgentContext"],
|
872
|
+
context_updates: Optional[
|
873
|
+
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
874
|
+
],
|
875
|
+
context_confirm: bool,
|
876
|
+
context_strategy: Literal["selective", "all"],
|
877
|
+
context_max_retries: int,
|
878
|
+
context_confirm_instructions: Optional[str],
|
879
|
+
context_selection_instructions: Optional[str],
|
880
|
+
context_update_instructions: Optional[str],
|
881
|
+
context_format: Literal["json", "python", "markdown"],
|
882
|
+
model: Optional[Union["LanguageModel", "LanguageModelName"]],
|
883
|
+
max_steps: Optional[int],
|
884
|
+
instructor_mode: Optional["LanguageModelInstructorMode"],
|
885
|
+
return_output: bool,
|
886
|
+
end_strategy: Optional[Literal["tool"]],
|
887
|
+
end_tool: Optional[Callable],
|
888
|
+
timeout: Optional[Union[float, str, "Timeout"]],
|
889
|
+
temperature: Optional[float],
|
890
|
+
top_p: Optional[float],
|
891
|
+
max_tokens: Optional[int],
|
892
|
+
presence_penalty: Optional[float],
|
893
|
+
frequency_penalty: Optional[float],
|
894
|
+
seed: Optional[int],
|
895
|
+
user: Optional[str],
|
896
|
+
verbose: bool,
|
897
|
+
debug: bool,
|
898
|
+
) -> Callable:
|
899
|
+
"""Helper function to create the actual agent wrapper."""
|
900
|
+
import inspect
|
901
|
+
import asyncio
|
902
|
+
from typing import get_type_hints
|
903
|
+
|
904
|
+
# Get function metadata
|
905
|
+
sig = inspect.signature(func)
|
906
|
+
type_hints = get_type_hints(func)
|
907
|
+
return_type = type_hints.get("return", str)
|
908
|
+
func_instructions = instructions or func.__doc__ or ""
|
909
|
+
|
910
|
+
# Check if function is async
|
911
|
+
is_async = asyncio.iscoroutinefunction(func)
|
912
|
+
|
913
|
+
if is_async:
|
914
|
+
|
915
|
+
@functools.wraps(func)
|
916
|
+
async def async_wrapper(*args, **kwargs):
|
917
|
+
# Convert function parameters to message string
|
918
|
+
bound_args = sig.bind(*args, **kwargs)
|
919
|
+
bound_args.apply_defaults()
|
920
|
+
|
921
|
+
# Create message from parameters
|
922
|
+
param_parts = []
|
923
|
+
for param_name, param_value in bound_args.arguments.items():
|
924
|
+
param_parts.append(f"{param_name}: {param_value}")
|
925
|
+
message = "\n".join(param_parts)
|
926
|
+
|
927
|
+
# Run agent with extracted parameters
|
928
|
+
response = await async_run_agent(
|
929
|
+
messages=message,
|
930
|
+
output_type=return_type,
|
931
|
+
name=name,
|
932
|
+
instructions=func_instructions,
|
933
|
+
description=description,
|
934
|
+
tools=tools,
|
935
|
+
settings=settings,
|
936
|
+
context=context,
|
937
|
+
context_updates=context_updates,
|
938
|
+
context_confirm=context_confirm,
|
939
|
+
context_strategy=context_strategy,
|
940
|
+
context_max_retries=context_max_retries,
|
941
|
+
context_confirm_instructions=context_confirm_instructions,
|
942
|
+
context_selection_instructions=context_selection_instructions,
|
943
|
+
context_update_instructions=context_update_instructions,
|
944
|
+
context_format=context_format,
|
945
|
+
model=model or "openai/gpt-4o-mini",
|
946
|
+
max_steps=max_steps,
|
947
|
+
instructor_mode=instructor_mode,
|
948
|
+
end_strategy=end_strategy,
|
949
|
+
end_tool=end_tool,
|
950
|
+
timeout=timeout,
|
951
|
+
temperature=temperature,
|
952
|
+
top_p=top_p,
|
953
|
+
max_tokens=max_tokens,
|
954
|
+
presence_penalty=presence_penalty,
|
955
|
+
frequency_penalty=frequency_penalty,
|
956
|
+
seed=seed,
|
957
|
+
user=user,
|
958
|
+
verbose=verbose,
|
959
|
+
debug=debug,
|
960
|
+
)
|
961
|
+
|
962
|
+
# Return just the output if return_output is True (default behavior)
|
963
|
+
if return_output:
|
964
|
+
return response.output
|
965
|
+
else:
|
966
|
+
return response
|
967
|
+
|
968
|
+
return async_wrapper
|
969
|
+
else:
|
970
|
+
|
971
|
+
@functools.wraps(func)
|
972
|
+
def sync_wrapper(*args, **kwargs):
|
973
|
+
# Convert function parameters to message string
|
974
|
+
bound_args = sig.bind(*args, **kwargs)
|
975
|
+
bound_args.apply_defaults()
|
976
|
+
|
977
|
+
# Create message from parameters
|
978
|
+
param_parts = []
|
979
|
+
for param_name, param_value in bound_args.arguments.items():
|
980
|
+
param_parts.append(f"{param_name}: {param_value}")
|
981
|
+
message = "\n".join(param_parts)
|
982
|
+
|
983
|
+
# Run agent with extracted parameters
|
984
|
+
response = run_agent(
|
985
|
+
messages=message,
|
986
|
+
output_type=return_type,
|
987
|
+
name=name,
|
988
|
+
instructions=func_instructions,
|
989
|
+
description=description,
|
990
|
+
tools=tools,
|
991
|
+
settings=settings,
|
992
|
+
context=context,
|
993
|
+
context_updates=context_updates,
|
994
|
+
context_confirm=context_confirm,
|
995
|
+
context_strategy=context_strategy,
|
996
|
+
context_max_retries=context_max_retries,
|
997
|
+
context_confirm_instructions=context_confirm_instructions,
|
998
|
+
context_selection_instructions=context_selection_instructions,
|
999
|
+
context_update_instructions=context_update_instructions,
|
1000
|
+
context_format=context_format,
|
1001
|
+
model=model or "openai/gpt-4o-mini",
|
1002
|
+
max_steps=max_steps,
|
1003
|
+
instructor_mode=instructor_mode,
|
1004
|
+
end_strategy=end_strategy,
|
1005
|
+
end_tool=end_tool,
|
1006
|
+
timeout=timeout,
|
1007
|
+
temperature=temperature,
|
1008
|
+
top_p=top_p,
|
1009
|
+
max_tokens=max_tokens,
|
1010
|
+
presence_penalty=presence_penalty,
|
1011
|
+
frequency_penalty=frequency_penalty,
|
1012
|
+
seed=seed,
|
1013
|
+
user=user,
|
1014
|
+
verbose=verbose,
|
1015
|
+
debug=debug,
|
1016
|
+
)
|
1017
|
+
|
1018
|
+
# Return just the output if return_output is True (default behavior)
|
1019
|
+
if return_output:
|
1020
|
+
return response.output
|
1021
|
+
else:
|
1022
|
+
return response
|
1023
|
+
|
1024
|
+
return sync_wrapper
|
hammad/genai/graphs/__init__.py
CHANGED
@@ -46,7 +46,16 @@ from ..._internal import create_getattr_importer
|
|
46
46
|
|
47
47
|
|
48
48
|
if TYPE_CHECKING:
|
49
|
-
from .base import
|
49
|
+
from .base import (
|
50
|
+
ActionDecorator,
|
51
|
+
ActionNode,
|
52
|
+
ActionSettings,
|
53
|
+
BaseGraph,
|
54
|
+
GraphBuilder,
|
55
|
+
action,
|
56
|
+
select,
|
57
|
+
SelectionStrategy,
|
58
|
+
)
|
50
59
|
from .types import (
|
51
60
|
GraphContext,
|
52
61
|
GraphResponse,
|
@@ -77,11 +86,14 @@ __all__ = (
|
|
77
86
|
# Core graph classes
|
78
87
|
"BaseGraph",
|
79
88
|
"GraphBuilder",
|
89
|
+
"ActionDecorator",
|
80
90
|
# Action system
|
81
91
|
"action",
|
82
92
|
"ActionNode",
|
83
93
|
"ActionSettings",
|
84
94
|
"ActionInfo",
|
95
|
+
"select",
|
96
|
+
"SelectionStrategy",
|
85
97
|
# Plugin system
|
86
98
|
"plugin",
|
87
99
|
"BasePlugin",
|