hyperforge-perplexity-search 1.0.0.post20__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_perplexity_search-1.0.0.post20/PKG-INFO +22 -0
- hyperforge_perplexity_search-1.0.0.post20/README.md +1 -0
- hyperforge_perplexity_search-1.0.0.post20/pyproject.toml +42 -0
- hyperforge_perplexity_search-1.0.0.post20/setup.cfg +4 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search/__init__.py +3 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search/agent.py +140 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search/config.py +35 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search/py.typed +0 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/PKG-INFO +22 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/SOURCES.txt +12 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/dependency_links.txt +1 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/requires.txt +4 -0
- hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/top_level.txt +1 -0
- hyperforge_perplexity_search-1.0.0.post20/tests/test_perplexity_search.py +90 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hyperforge_perplexity_search
|
|
3
|
+
Version: 1.0.0.post20
|
|
4
|
+
Summary: Perplexity Search 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/hyperforge
|
|
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
|
+
Requires-Dist: httpx
|
|
19
|
+
Requires-Dist: hyperforge_perplexity
|
|
20
|
+
Requires-Dist: perplexityai>=0.35.1
|
|
21
|
+
|
|
22
|
+
# Perplexity Search Hyperforge agents
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Perplexity Search Hyperforge agents
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hyperforge_perplexity_search"
|
|
7
|
+
version = "1.0.0.post20"
|
|
8
|
+
license = "Apache-2.0"
|
|
9
|
+
description = "Perplexity Search 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 = [
|
|
22
|
+
"hyperforge",
|
|
23
|
+
"httpx",
|
|
24
|
+
"hyperforge_perplexity",
|
|
25
|
+
"perplexityai>=0.35.1",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"mypy",
|
|
31
|
+
"pytest",
|
|
32
|
+
"pytest-asyncio",
|
|
33
|
+
"pytest-recording",
|
|
34
|
+
"ty",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://progress.com"
|
|
39
|
+
Repository = "https://github.com/nuclia/hyperforge"
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
from typing import Any, ClassVar, Dict, List, Optional, cast
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
from hyperforge.agent import Agent
|
|
6
|
+
from hyperforge.configure import agent
|
|
7
|
+
from hyperforge.context.agent import ContextAgent
|
|
8
|
+
from hyperforge.definition import FunctionDefinition
|
|
9
|
+
from hyperforge.manager import Manager
|
|
10
|
+
from hyperforge.memory import Chunk, Context, QuestionMemory
|
|
11
|
+
from hyperforge_perplexity.driver import PerplexityDriver
|
|
12
|
+
|
|
13
|
+
from hyperforge_perplexity_search.config import PerplexitySearchAgentConfig
|
|
14
|
+
|
|
15
|
+
SYSTEM_PROMPT = "Be precise and concise"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@agent(
|
|
19
|
+
id="perplexity_search",
|
|
20
|
+
agent_type="context",
|
|
21
|
+
title="Perplexity Search",
|
|
22
|
+
description="Use Perplexity to get information from the internet.",
|
|
23
|
+
config_schema=PerplexitySearchAgentConfig,
|
|
24
|
+
)
|
|
25
|
+
class PerplexitySearchAgent(ContextAgent, Agent[PerplexitySearchAgentConfig]):
|
|
26
|
+
driver: Optional[PerplexityDriver] = None
|
|
27
|
+
__published_functions__: ClassVar[Dict[str, FunctionDefinition]] = {
|
|
28
|
+
"internet_search": FunctionDefinition(
|
|
29
|
+
name="internet_search",
|
|
30
|
+
description="Performs an internet search using Perplexity and returns the results as context to answer questions. Does not generate an answer, only search and return the results as context.",
|
|
31
|
+
parameters={
|
|
32
|
+
"question": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "The question to search for on the internet.",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async def search(
|
|
41
|
+
self,
|
|
42
|
+
question: str,
|
|
43
|
+
memory: QuestionMemory,
|
|
44
|
+
manager: Manager,
|
|
45
|
+
question_uuid: Optional[str] = None,
|
|
46
|
+
) -> Context:
|
|
47
|
+
t0 = time()
|
|
48
|
+
|
|
49
|
+
if self.driver is None:
|
|
50
|
+
self.driver: Optional[PerplexityDriver] = cast(
|
|
51
|
+
Optional[PerplexityDriver], manager.drivers.get(self.config.source)
|
|
52
|
+
)
|
|
53
|
+
if self.driver is None:
|
|
54
|
+
raise Exception("Perplexity driver does not exist")
|
|
55
|
+
|
|
56
|
+
response = await self.driver.client.search.create(
|
|
57
|
+
query=question,
|
|
58
|
+
search_domain_filter=self.config.domain,
|
|
59
|
+
max_results=self.config.max_results,
|
|
60
|
+
max_tokens_per_page=self.config.max_tokens_per_page,
|
|
61
|
+
)
|
|
62
|
+
chunks = []
|
|
63
|
+
if response.results is not None:
|
|
64
|
+
for result in response.results:
|
|
65
|
+
text = result.snippet if result.snippet is not None else ""
|
|
66
|
+
title = (
|
|
67
|
+
result.title
|
|
68
|
+
if result.title is not None
|
|
69
|
+
else result.url
|
|
70
|
+
if result.url is not None
|
|
71
|
+
else "No title"
|
|
72
|
+
)
|
|
73
|
+
chunk = Chunk(
|
|
74
|
+
chunk_id=uuid4().hex,
|
|
75
|
+
text=text,
|
|
76
|
+
title=title,
|
|
77
|
+
labels=[],
|
|
78
|
+
url=[result.url] if result.url is not None else [],
|
|
79
|
+
origin_url=result.url if result.url is not None else "",
|
|
80
|
+
origin_agent=self.config.module,
|
|
81
|
+
)
|
|
82
|
+
chunks.append(chunk)
|
|
83
|
+
context = Context(
|
|
84
|
+
agent_id=self.config.id or self.agent_id,
|
|
85
|
+
original_question_uuid=memory.original_question_uuid,
|
|
86
|
+
actual_question_uuid=question_uuid,
|
|
87
|
+
question=question,
|
|
88
|
+
title=self.config.title
|
|
89
|
+
if self.config.title
|
|
90
|
+
else "Internet search with Perplexity",
|
|
91
|
+
source="perplexity",
|
|
92
|
+
agent=self.config.module,
|
|
93
|
+
chunks=chunks,
|
|
94
|
+
)
|
|
95
|
+
await memory.add_step(
|
|
96
|
+
step_module=self.config.module,
|
|
97
|
+
step_title=self.step_title("Search results"),
|
|
98
|
+
step_reason="",
|
|
99
|
+
step_agent_path=f"/context/{self.config.id or self.agent_id}",
|
|
100
|
+
step_value=f"{len(chunks)} results found with Perplexity"
|
|
101
|
+
if chunks
|
|
102
|
+
else "No results found",
|
|
103
|
+
timeit=time() - t0,
|
|
104
|
+
input_nuclia_tokens=0,
|
|
105
|
+
output_nuclia_tokens=0,
|
|
106
|
+
)
|
|
107
|
+
return context
|
|
108
|
+
|
|
109
|
+
async def _get_question_context(
|
|
110
|
+
self,
|
|
111
|
+
memory: QuestionMemory,
|
|
112
|
+
manager: Manager,
|
|
113
|
+
question_uuid: str,
|
|
114
|
+
question: str,
|
|
115
|
+
flow_id: str,
|
|
116
|
+
extra_context: Optional[Dict[str, Any]] = None,
|
|
117
|
+
) -> List[tuple[str, str]]:
|
|
118
|
+
if self.driver is None:
|
|
119
|
+
self.driver: Optional[PerplexityDriver] = cast(
|
|
120
|
+
Optional[PerplexityDriver], manager.drivers.get(self.config.source)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if self.driver is None:
|
|
124
|
+
raise Exception("Perplexity driver does not exist")
|
|
125
|
+
|
|
126
|
+
context = await self.search(
|
|
127
|
+
question,
|
|
128
|
+
memory,
|
|
129
|
+
manager,
|
|
130
|
+
question_uuid=question_uuid,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
missing = await self.save_ctx_and_return_missing(
|
|
134
|
+
context=context,
|
|
135
|
+
question=question,
|
|
136
|
+
memory=memory,
|
|
137
|
+
manager=manager,
|
|
138
|
+
flow_id=flow_id,
|
|
139
|
+
)
|
|
140
|
+
return [missing] if missing is not None else []
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import List, Literal
|
|
2
|
+
|
|
3
|
+
from hyperforge.context.config import ContextAgentConfig
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
from pydantic.config import ConfigDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PerplexitySearchAgentConfig(ContextAgentConfig):
|
|
9
|
+
model_config = ConfigDict(title="Perplexity Search")
|
|
10
|
+
module: Literal["perplexity_search"] = "perplexity_search"
|
|
11
|
+
domain: List[str] = Field(
|
|
12
|
+
default_factory=list,
|
|
13
|
+
title="Domain Filter",
|
|
14
|
+
description="Domains to restrict the Perplexity search to.",
|
|
15
|
+
)
|
|
16
|
+
search_context_size: Literal["low", "medium", "high"] = Field(
|
|
17
|
+
default="low",
|
|
18
|
+
title="Search Context Size",
|
|
19
|
+
description="Determines how much search context Perplexity retrieves for the model. "
|
|
20
|
+
"Options are: `low` (minimizes context for cost savings but less comprehensive answers), "
|
|
21
|
+
"`medium` (balanced approach suitable for most queries), "
|
|
22
|
+
"and `high` (maximizes context for comprehensive answers but at higher cost).",
|
|
23
|
+
)
|
|
24
|
+
max_results: int = Field(
|
|
25
|
+
default=10,
|
|
26
|
+
title="Max Results",
|
|
27
|
+
description="Maximum number of search results to return.",
|
|
28
|
+
)
|
|
29
|
+
max_tokens_per_page: int = Field(
|
|
30
|
+
default=4096,
|
|
31
|
+
title="Max Tokens per Page",
|
|
32
|
+
description="Maximum number of tokens to return per search result page.",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
source: str = "perplexity"
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hyperforge_perplexity_search
|
|
3
|
+
Version: 1.0.0.post20
|
|
4
|
+
Summary: Perplexity Search 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/hyperforge
|
|
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
|
+
Requires-Dist: httpx
|
|
19
|
+
Requires-Dist: hyperforge_perplexity
|
|
20
|
+
Requires-Dist: perplexityai>=0.35.1
|
|
21
|
+
|
|
22
|
+
# Perplexity Search Hyperforge agents
|
hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/hyperforge_perplexity_search/__init__.py
|
|
4
|
+
src/hyperforge_perplexity_search/agent.py
|
|
5
|
+
src/hyperforge_perplexity_search/config.py
|
|
6
|
+
src/hyperforge_perplexity_search/py.typed
|
|
7
|
+
src/hyperforge_perplexity_search.egg-info/PKG-INFO
|
|
8
|
+
src/hyperforge_perplexity_search.egg-info/SOURCES.txt
|
|
9
|
+
src/hyperforge_perplexity_search.egg-info/dependency_links.txt
|
|
10
|
+
src/hyperforge_perplexity_search.egg-info/requires.txt
|
|
11
|
+
src/hyperforge_perplexity_search.egg-info/top_level.txt
|
|
12
|
+
tests/test_perplexity_search.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
hyperforge_perplexity_search-1.0.0.post20/src/hyperforge_perplexity_search.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hyperforge_perplexity_search
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from hyperforge.configure import get_driver_config_instance, load_all_configurations
|
|
6
|
+
from hyperforge.manager import Manager
|
|
7
|
+
from hyperforge.memory import Context
|
|
8
|
+
from hyperforge.memory.memory import EphemeralSessionMemory
|
|
9
|
+
from hyperforge.minimal_fixtures import cassette_nua_key
|
|
10
|
+
from hyperforge.models import MemoryConfig, Rules
|
|
11
|
+
from hyperforge_perplexity_search.agent import PerplexitySearchAgent
|
|
12
|
+
from hyperforge_perplexity_search.config import PerplexitySearchAgentConfig
|
|
13
|
+
from nuclia.lib.nua import AsyncNuaClient
|
|
14
|
+
|
|
15
|
+
NUA_KEY = os.environ.get(
|
|
16
|
+
"NUA_KEY",
|
|
17
|
+
) or cassette_nua_key("https://europe-1.nuclia.cloud/")
|
|
18
|
+
|
|
19
|
+
PERPLEXITY_KEY = os.environ.get("PERPLEXITY_API_KEY", "DUMMY_PERPLEXITY_KEY")
|
|
20
|
+
|
|
21
|
+
pytestmark = [
|
|
22
|
+
pytest.mark.vcr(ignore_localhost=True),
|
|
23
|
+
pytest.mark.asyncio,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
DRIVERS = [
|
|
27
|
+
{
|
|
28
|
+
"provider": "perplexity",
|
|
29
|
+
"identifier": "perplexity-01",
|
|
30
|
+
"name": "perplexity",
|
|
31
|
+
"config": {"key": PERPLEXITY_KEY},
|
|
32
|
+
},
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def _run_question(
|
|
37
|
+
drivers: list[dict], question: str, config_overrides: dict | None = None
|
|
38
|
+
) -> list[Context]:
|
|
39
|
+
load_all_configurations("hyperforge_perplexity_search")
|
|
40
|
+
load_all_configurations("hyperforge_perplexity") # register the perplexity driver
|
|
41
|
+
# Perplexity agent doesn't use nua — construct client directly with test values
|
|
42
|
+
nua = AsyncNuaClient(region="europe-1", account="test", token=NUA_KEY)
|
|
43
|
+
manager = await Manager.from_config(
|
|
44
|
+
drivers=[get_driver_config_instance(d) for d in drivers],
|
|
45
|
+
nua=nua,
|
|
46
|
+
)
|
|
47
|
+
config = PerplexitySearchAgentConfig.model_validate(
|
|
48
|
+
{
|
|
49
|
+
"module": "perplexity_search",
|
|
50
|
+
"title": "Perplexity Search Agent",
|
|
51
|
+
"source": "perplexity-01",
|
|
52
|
+
**(config_overrides or {}),
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
agent = PerplexitySearchAgent(config=config)
|
|
56
|
+
await agent.inner_from_config(config)
|
|
57
|
+
|
|
58
|
+
session = EphemeralSessionMemory.from_config(
|
|
59
|
+
config=MemoryConfig(), agent_id="test", workflow_id="test", rules=Rules()
|
|
60
|
+
)
|
|
61
|
+
session.init("test-session")
|
|
62
|
+
memory = session.start_question(question)
|
|
63
|
+
flow_id = uuid4().hex
|
|
64
|
+
await agent.get_question_context(
|
|
65
|
+
memory=memory,
|
|
66
|
+
manager=manager,
|
|
67
|
+
question_uuid=memory.original_question_uuid,
|
|
68
|
+
question=question,
|
|
69
|
+
flow_id=flow_id,
|
|
70
|
+
)
|
|
71
|
+
return memory.get_agent_contexts(flow_id=flow_id, agent_id=agent.agent_id)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def test_perplexity_search():
|
|
75
|
+
contexts = await _run_question(
|
|
76
|
+
DRIVERS,
|
|
77
|
+
"What is Nuclia?",
|
|
78
|
+
config_overrides={"domain": ["nuclia.com"], "max_results": 3},
|
|
79
|
+
)
|
|
80
|
+
assert len(contexts) > 0
|
|
81
|
+
assert any(ctx.chunks for ctx in contexts)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def test_perplexity_search_domain():
|
|
85
|
+
contexts = await _run_question(
|
|
86
|
+
DRIVERS,
|
|
87
|
+
"What is Marklogic?",
|
|
88
|
+
config_overrides={"domain": ["progress.com"], "max_results": 5},
|
|
89
|
+
)
|
|
90
|
+
assert len(contexts) > 0
|