hyperforge-external 1.0.0.post21__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.
@@ -0,0 +1,3 @@
1
+ from .agent import ExternalCallAgent
2
+
3
+ __all__ = ["ExternalCallAgent"]
@@ -0,0 +1,136 @@
1
+ from copy import deepcopy
2
+ from time import time
3
+
4
+ from hyperforge.agent import Agent
5
+ from hyperforge.configure import agent
6
+ from hyperforge.manager import Manager
7
+ from hyperforge.memory import QuestionMemory
8
+ from hyperforge.trace import trace_agent
9
+ from hyperforge.utils import check_dns
10
+ from hyperforge.utils.http import safe_http_client
11
+
12
+ from hyperforge import PROMPT_ENVIRONMENT, logger
13
+ from hyperforge_external.config import ExternalCallAgentConfig
14
+
15
+ EXTERNAL_CALL_PROMPT = """
16
+ Collect the parameters to call the query based on the schema and the context:
17
+ {{prompt}}
18
+
19
+
20
+ [START OF CONTEXT]
21
+ {{context}}
22
+ [END OF CONTEXT]
23
+
24
+ MAIN QUESTION: {{question}}
25
+
26
+ MAIN ANSWER: {{answer}}
27
+ """
28
+
29
+ EXTERNAL_CALL_PROMPT_TEMPLATE = PROMPT_ENVIRONMENT.from_string(EXTERNAL_CALL_PROMPT)
30
+
31
+
32
+ @agent(
33
+ id="external",
34
+ agent_type="postprocess",
35
+ title="External Call",
36
+ description="Agent that performs External Call.",
37
+ config_schema=ExternalCallAgentConfig,
38
+ )
39
+ class ExternalCallAgent(Agent[ExternalCallAgentConfig]):
40
+ config: ExternalCallAgentConfig
41
+
42
+ @trace_agent
43
+ async def __call__(
44
+ self,
45
+ memory: QuestionMemory,
46
+ manager: Manager,
47
+ ):
48
+ t0 = time()
49
+
50
+ prompt = EXTERNAL_CALL_PROMPT_TEMPLATE.render(
51
+ question=memory.original_question,
52
+ context=memory.contexts_minimal(),
53
+ answer=memory.final_answer,
54
+ prompt=self.config.prompt,
55
+ )
56
+ t0 = time()
57
+
58
+ resp = None
59
+
60
+ input_nuclia_tokens: float = 0.0
61
+ output_nuclia_tokens: float = 0.0
62
+
63
+ evaluation = None
64
+ async with safe_http_client() as client:
65
+ url = await check_dns(self.config.url)
66
+
67
+ if self.config.call_schema:
68
+ logger.debug(
69
+ f"Calling external API with schema: {self.config.call_schema}, description: {self.config.description}"
70
+ )
71
+ evaluation, input_tokens, output_tokens = await manager.execute_json(
72
+ user_id="external_call_agent",
73
+ model=self.config.model,
74
+ schema={
75
+ "title": "external_call_agent",
76
+ "description": self.config.description
77
+ or "Choose the parameters to call an external API",
78
+ "parameters": self.config.call_schema,
79
+ },
80
+ prompt=prompt,
81
+ tracking=memory.get_tracking_info(),
82
+ )
83
+ output_nuclia_tokens += output_tokens
84
+ input_nuclia_tokens += input_tokens
85
+ logger.debug(f"Json to do the call: {evaluation}")
86
+ resp = await client.request(
87
+ method=self.config.method.value,
88
+ url=url,
89
+ json=evaluation,
90
+ headers=self.config.headers,
91
+ )
92
+ elif self.config.context:
93
+ logger.debug(
94
+ f"Calling external API with generated context: {memory.contexts}"
95
+ )
96
+ resp = await client.request(
97
+ method=self.config.method.value,
98
+ url=url,
99
+ json=memory.contexts,
100
+ headers=self.config.headers,
101
+ )
102
+ else:
103
+ logger.debug(
104
+ f"Calling external API with call object: {self.config.call_obj}"
105
+ )
106
+ if self.config.call_obj is not None:
107
+ evaluation = deepcopy(self.config.call_obj)
108
+ else:
109
+ evaluation = {}
110
+ evaluation["answer"] = memory.final_answer
111
+ evaluation["question"] = memory.original_question
112
+ resp = await client.request(
113
+ method=self.config.method.value,
114
+ url=self.config.url,
115
+ json=evaluation,
116
+ headers=self.config.headers,
117
+ )
118
+ error_resp = None
119
+ if resp is None:
120
+ error_resp = "No response from external API"
121
+ elif resp.status_code != 200:
122
+ error_resp = f"Error calling external API: {resp.status_code} - {resp.content.decode()}"
123
+ if error_resp:
124
+ logger.error(error_resp)
125
+ raise Exception(error_resp)
126
+ logger.info(f"Response from external API: {resp.content.decode()}")
127
+ await memory.add_step(
128
+ step_module="external",
129
+ step_title=self.step_title(self.config.method.value),
130
+ step_value=self.config.url,
131
+ step_reason=resp.content.decode(),
132
+ timeit=time() - t0,
133
+ step_agent_path=f"/postprocess/{self.config.id if self.config.id else 'default'}",
134
+ input_nuclia_tokens=input_nuclia_tokens,
135
+ output_nuclia_tokens=output_nuclia_tokens,
136
+ )
@@ -0,0 +1,71 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, Literal, Optional
3
+
4
+ from hyperforge.agent import AgentConfig
5
+ from hyperforge.utils import WidgetType, sync_dns_validation
6
+ from pydantic import Field, field_validator
7
+ from pydantic.config import ConfigDict
8
+
9
+
10
+ class Method(str, Enum):
11
+ POST = "POST"
12
+ GET = "GET"
13
+ PATCH = "PATCH"
14
+
15
+
16
+ class ExternalCallAgentConfig(AgentConfig):
17
+ model_config = ConfigDict(title="External call")
18
+ module: Literal["external"] = "external"
19
+ prompt: Optional[str] = Field(
20
+ None,
21
+ title="Extra Prompt",
22
+ description="Extra prompt to provide more clues to extract parameters",
23
+ json_schema_extra={
24
+ "show_in_node": True,
25
+ "widget": WidgetType.EXPANDABLE_TEXTAREA,
26
+ },
27
+ )
28
+ method: Method = Field(
29
+ Method.POST,
30
+ title="Request method",
31
+ description="POST, GET and PATCH are supported",
32
+ )
33
+ description: Optional[str] = Field(
34
+ None,
35
+ title="Description of the operation ",
36
+ description="Description to help the LLM to extract the parameters",
37
+ )
38
+ call_schema: Optional[Dict[str, Any]] = Field(
39
+ None,
40
+ title="JSON Schema to compute parameters ",
41
+ description="Valid JSON Schema to define the parameters to call the URL Its incompatible with call_obj and context",
42
+ )
43
+ call_obj: Optional[Dict[str, Any]] = Field(
44
+ None,
45
+ title="Object to call the endpoint ",
46
+ description="Object that will be used to call the endpoint adding the answer and the question to it. Its incompatible with call_schema and context",
47
+ )
48
+ headers: Dict[str, str] = Field(
49
+ {},
50
+ title="Headers to use on the API call ",
51
+ )
52
+ model: str = Field(
53
+ default="chatgpt-o3-mini",
54
+ title="Generative model",
55
+ description="Model used to extract the parameters to call the URL",
56
+ json_schema_extra={"widget": WidgetType.MODEL_SELECT},
57
+ )
58
+ context: bool = Field(
59
+ False,
60
+ title="Use context as payload",
61
+ description="Use the context as payload. Its incompatible with call_schema and call_obj",
62
+ )
63
+ url: str = Field(
64
+ ...,
65
+ title="URL to call",
66
+ description="",
67
+ )
68
+
69
+ @field_validator("url")
70
+ def validate_url(cls, v):
71
+ return sync_dns_validation(v)
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperforge_external
3
+ Version: 1.0.0.post21
4
+ Summary: External Hyperforge agent
5
+ Author-email: Nuclia <nucliadb@nuclia.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://progress.com
8
+ Project-URL: Repository, https://github.com/nuclia/forge
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: <4,>=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: hyperforge
18
+
19
+ # External/Webhook Hyperforge agents
@@ -0,0 +1,7 @@
1
+ hyperforge_external/__init__.py,sha256=XP1agyAfPTWHolRYnIY7M65zDxPrBlhuVMD5YYXJUto,70
2
+ hyperforge_external/agent.py,sha256=x6F1eeeMOuH3lYpIF7GBHyLXa3qiwLyH3LuZht6pCX8,4881
3
+ hyperforge_external/config.py,sha256=17D7FWNcTBSo6tGmbt5sdlHjd7O4AxOtkQ35FR3FfE8,2339
4
+ hyperforge_external-1.0.0.post21.dist-info/METADATA,sha256=9OPp1uX8f-j_VM5mCbkBYL0ArZmfbXX51Rl-M1Dn9Lo,736
5
+ hyperforge_external-1.0.0.post21.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ hyperforge_external-1.0.0.post21.dist-info/top_level.txt,sha256=2lPfd_Xd8E3r3hJ9A4xfdHqmTl5wZ_dOVV1j_J-iHT4,20
7
+ hyperforge_external-1.0.0.post21.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ hyperforge_external