hyperforge-passthrough 1.0.0.post19__tar.gz

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,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperforge_passthrough
3
+ Version: 1.0.0.post19
4
+ Summary: Passthrough 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
+ # Passthrough Hyperforge agents
@@ -0,0 +1 @@
1
+ # Passthrough Hyperforge agents
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hyperforge_passthrough"
7
+ version = "1.0.0.post19"
8
+ license = "Apache-2.0"
9
+ description = "Passthrough Hyperforge agent"
10
+ authors = [{ name = "Nuclia", email = "nucliadb@nuclia.com" }]
11
+ readme = "README.md"
12
+ classifiers = [
13
+ "Programming Language :: Python",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Topic :: Software Development :: Libraries :: Python Modules",
19
+ ]
20
+ requires-python = ">=3.10, <4"
21
+ dependencies = ["hyperforge"]
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ "pytest",
26
+ "pytest-benchmark",
27
+ "pytest-docker-fixtures>=1.4.2",
28
+ "pytest-lazy-fixtures",
29
+ "pytest-recording",
30
+ "pytest-asyncio",
31
+ "pytest-cov",
32
+ "pytest-mock",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://progress.com"
37
+ Repository = "https://github.com/nuclia/forge"
38
+
39
+ [tool.pytest.ini_options]
40
+ asyncio_mode = "auto"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .agent import PassthroughAgent
2
+
3
+
4
+ __all__ = ["PassthroughAgent"]
@@ -0,0 +1,77 @@
1
+ """
2
+ Passthrough generation agent.
3
+
4
+ Returns the retrieved context chunks directly as the answer, with no LLM call.
5
+ Useful for testing and for workflows where the context agent already provides
6
+ a complete answer (e.g. the built-in ``static`` context agent).
7
+ """
8
+
9
+ from hyperforge.agent import Agent
10
+ from hyperforge.configure import agent
11
+ from hyperforge.manager import Manager
12
+ from hyperforge.memory import QuestionMemory
13
+ from hyperforge.trace import trace_agent
14
+
15
+ from hyperforge_passthrough.config import PassthroughAgentConfig
16
+
17
+
18
+ @agent(
19
+ id="passthrough",
20
+ agent_type="generation",
21
+ title="Passthrough",
22
+ description=(
23
+ "Return the retrieved context directly as the answer without any LLM call. "
24
+ "Useful for testing or when the context agent already contains the final answer."
25
+ ),
26
+ config_schema=PassthroughAgentConfig,
27
+ )
28
+ class PassthroughAgent(Agent[PassthroughAgentConfig]):
29
+ __root_agent__ = True
30
+
31
+ @trace_agent
32
+ async def __call__(
33
+ self,
34
+ memory: QuestionMemory,
35
+ manager: Manager,
36
+ ) -> None:
37
+ if self.config.rich_context:
38
+ # Gather all context data and pass it through add_answer so it
39
+ # arrives in possible_answer with all fields populated.
40
+ # convert_arag_answer_to_content will parse it thoroughly.
41
+ chunks = [
42
+ chunk for ctx in memory.contexts for chunk in ctx.chunks if chunk.text
43
+ ]
44
+ structured_data = [
45
+ s for ctx in memory.contexts for s in ctx.structured if s
46
+ ]
47
+ images = {
48
+ k: img
49
+ for ctx in memory.contexts
50
+ for k, img in (ctx.images or {}).items()
51
+ }
52
+ image_urls = [
53
+ url for ctx in memory.contexts for url in ctx.image_urls if url
54
+ ]
55
+ await memory.add_answer(
56
+ "",
57
+ "passthrough",
58
+ "/generation/passthrough",
59
+ chunks=chunks,
60
+ structured=structured_data,
61
+ images=images,
62
+ image_urls=image_urls,
63
+ )
64
+ return
65
+
66
+ # Default behaviour: concatenate all context chunks as a plain-text answer.
67
+ parts = []
68
+ for ctx in memory.contexts:
69
+ for chunk in ctx.chunks:
70
+ if chunk.text:
71
+ parts.append(chunk.text)
72
+ for structured in ctx.structured:
73
+ if structured:
74
+ parts.append(structured)
75
+
76
+ answer = "\n\n".join(parts) if parts else "(no context retrieved)"
77
+ await memory.add_answer(answer, "passthrough", "/generation/passthrough")
@@ -0,0 +1,22 @@
1
+ from typing import Literal
2
+
3
+ from hyperforge.agent import AgentConfig
4
+ from pydantic import Field
5
+ from pydantic.config import ConfigDict
6
+
7
+
8
+ class PassthroughAgentConfig(AgentConfig):
9
+ """Configuration for the passthrough generation agent."""
10
+
11
+ model_config = ConfigDict(title="Passthrough")
12
+ module: Literal["passthrough"] = "passthrough"
13
+ rich_context: bool = Field(
14
+ default=False,
15
+ title="Rich context output",
16
+ description=(
17
+ "When enabled, context results are emitted as structured MCP content blocks "
18
+ "(chunks, images, structured data) via their existing callback messages instead "
19
+ "of being concatenated into a plain-text answer. "
20
+ "Use this when the output is complex."
21
+ ),
22
+ )
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperforge_passthrough
3
+ Version: 1.0.0.post19
4
+ Summary: Passthrough 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
+ # Passthrough Hyperforge agents
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/hyperforge_passthrough/__init__.py
4
+ src/hyperforge_passthrough/agent.py
5
+ src/hyperforge_passthrough/config.py
6
+ src/hyperforge_passthrough.egg-info/PKG-INFO
7
+ src/hyperforge_passthrough.egg-info/SOURCES.txt
8
+ src/hyperforge_passthrough.egg-info/dependency_links.txt
9
+ src/hyperforge_passthrough.egg-info/requires.txt
10
+ src/hyperforge_passthrough.egg-info/top_level.txt
11
+ tests/test_passthrough.py
@@ -0,0 +1,68 @@
1
+ import pytest
2
+ from hyperforge.engine import main as arag_main
3
+ from hyperforge.interaction import AragAnswer
4
+
5
+ pytestmark = pytest.mark.asyncio
6
+
7
+
8
+ CONFIG = {
9
+ "drivers": [],
10
+ "rules": {},
11
+ "memory": {},
12
+ "workflow": {
13
+ "id": "default",
14
+ "name": "Default workflow",
15
+ "description": "Default workflow for testing",
16
+ "parameters": {},
17
+ },
18
+ "preprocess": [],
19
+ "postprocess": [],
20
+ "context": [
21
+ {
22
+ "module": "static",
23
+ "title": "Static Agent",
24
+ "context": "My data",
25
+ "structured": '{"source": "static"}',
26
+ "prune_context": False,
27
+ }
28
+ ],
29
+ "generation": [
30
+ {
31
+ "module": "passthrough",
32
+ "title": "Passthrough",
33
+ "rich_context": True,
34
+ }
35
+ ],
36
+ }
37
+
38
+
39
+ async def test_passthrough_rich_context_emits_possible_answer_from_pipeline():
40
+ answers: list[AragAnswer] = []
41
+
42
+ async def callback(obj: AragAnswer):
43
+ answers.append(obj)
44
+
45
+ await arag_main(
46
+ agent_id="default",
47
+ question="Return the static context",
48
+ config=CONFIG,
49
+ callback=callback,
50
+ loaded_modules=["hyperforge_passthrough", "hyperforge_static"],
51
+ )
52
+
53
+ context_msg = next(
54
+ (answer for answer in answers if answer.context is not None), None
55
+ )
56
+ assert context_msg is not None
57
+ assert len(context_msg.context.chunks) == 1
58
+ assert context_msg.context.chunks[0].text == "My data"
59
+ assert context_msg.context.structured == ['{"source": "static"}']
60
+
61
+ possible_answer_msg = next(
62
+ (answer for answer in answers if answer.possible_answer is not None),
63
+ None,
64
+ )
65
+ assert possible_answer_msg is not None
66
+ assert possible_answer_msg.possible_answer.answer == ""
67
+ assert possible_answer_msg.possible_answer.chunks[0].text == "My data"
68
+ assert possible_answer_msg.possible_answer.structured == ['{"source": "static"}']