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.
- hyperforge_passthrough-1.0.0.post19/PKG-INFO +19 -0
- hyperforge_passthrough-1.0.0.post19/README.md +1 -0
- hyperforge_passthrough-1.0.0.post19/pyproject.toml +40 -0
- hyperforge_passthrough-1.0.0.post19/setup.cfg +4 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough/__init__.py +4 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough/agent.py +77 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough/config.py +22 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough.egg-info/PKG-INFO +19 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough.egg-info/SOURCES.txt +11 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough.egg-info/dependency_links.txt +1 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough.egg-info/requires.txt +1 -0
- hyperforge_passthrough-1.0.0.post19/src/hyperforge_passthrough.egg-info/top_level.txt +1 -0
- hyperforge_passthrough-1.0.0.post19/tests/test_passthrough.py +68 -0
|
@@ -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,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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hyperforge
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hyperforge_passthrough
|
|
@@ -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"}']
|