hyperforge-nucliadb 1.0.0.post22__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_nucliadb-1.0.0.post22/PKG-INFO +26 -0
- hyperforge_nucliadb-1.0.0.post22/README.md +6 -0
- hyperforge_nucliadb-1.0.0.post22/pyproject.toml +39 -0
- hyperforge_nucliadb-1.0.0.post22/setup.cfg +4 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/__init__.py +13 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/advanced_ask_agent.py +267 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/advanced_ask_config.py +241 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/__init__.py +3 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/analysis.py +310 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/ask.py +326 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/config.py +52 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/hydrate.py +485 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/kb_analysis.py +48 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/knowledge_scan.py +150 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/models.py +59 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/multi.py +146 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/nucliadb.py +238 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/prompt_analysis.py +50 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/query_analysis.py +83 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/rerank.py +45 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/utils.py +36 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask_utils.py +235 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/basic_ask_agent.py +1812 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/basic_ask_config.py +42 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/driver.py +428 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/driver_config.py +42 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/__init__.py +0 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/agent.py +477 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/config.py +12 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/config_driver.py +22 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/driver.py +148 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/PKG-INFO +26 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/SOURCES.txt +37 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/dependency_links.txt +1 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/requires.txt +3 -0
- hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/top_level.txt +1 -0
- hyperforge_nucliadb-1.0.0.post22/tests/test_ask_utils.py +70 -0
- hyperforge_nucliadb-1.0.0.post22/tests/test_nucliadb.py +489 -0
- hyperforge_nucliadb-1.0.0.post22/tests/test_sync.py +220 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hyperforge_nucliadb
|
|
3
|
+
Version: 1.0.0.post22
|
|
4
|
+
Summary: NucliaDB 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
|
+
Requires-Dist: lingua-language-detector
|
|
19
|
+
Requires-Dist: nucliadb_sdk
|
|
20
|
+
|
|
21
|
+
# NucliaDB Hyperforge agents
|
|
22
|
+
|
|
23
|
+
Basic ASK - Basic initial agent
|
|
24
|
+
Advanced ASK (deprecated)
|
|
25
|
+
ASK (experimental)
|
|
26
|
+
Sync (Sync Service oauth linked)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hyperforge_nucliadb"
|
|
7
|
+
version = "1.0.0.post22"
|
|
8
|
+
license = "Apache-2.0"
|
|
9
|
+
description = "NucliaDB 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", "lingua-language-detector", "nucliadb_sdk"]
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
# these dependencies are actually in the src/ folder (under
|
|
25
|
+
# src/nucliadb_utils/tests/), but only used when the module is imported
|
|
26
|
+
dev = [
|
|
27
|
+
"pytest",
|
|
28
|
+
"pytest-benchmark",
|
|
29
|
+
"pytest-docker-fixtures>=1.4.2",
|
|
30
|
+
"pytest-lazy-fixtures",
|
|
31
|
+
"hyperforge_rephrase",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://progress.com"
|
|
36
|
+
Repository = "https://github.com/nuclia/forge"
|
|
37
|
+
|
|
38
|
+
[tool.pytest.ini_options]
|
|
39
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .advanced_ask_agent import AdvancedAskAgent
|
|
2
|
+
from .ask.ask import AskAgent
|
|
3
|
+
from .basic_ask_agent import BasicAskAgent
|
|
4
|
+
from .driver import NucliaDBDriver
|
|
5
|
+
from .sync.agent import SyncAskAgent
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"NucliaDBDriver",
|
|
9
|
+
"AskAgent",
|
|
10
|
+
"AdvancedAskAgent",
|
|
11
|
+
"BasicAskAgent",
|
|
12
|
+
"SyncAskAgent",
|
|
13
|
+
]
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from time import time
|
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
|
4
|
+
|
|
5
|
+
from hyperforge.agent import Agent
|
|
6
|
+
from hyperforge.configure import agent
|
|
7
|
+
from hyperforge.context.agent import ContextAgent
|
|
8
|
+
from hyperforge.manager import Manager
|
|
9
|
+
from hyperforge.memory import Chunk, Context, QuestionMemory, Source
|
|
10
|
+
from nucliadb_models import filters as ndb_filters
|
|
11
|
+
from nucliadb_models.search import AskRequest
|
|
12
|
+
|
|
13
|
+
from hyperforge import logger
|
|
14
|
+
from hyperforge_nucliadb.advanced_ask_config import (
|
|
15
|
+
AdvancedAskAgentConfig,
|
|
16
|
+
)
|
|
17
|
+
from hyperforge_nucliadb.ask.multi import choose_source
|
|
18
|
+
from hyperforge_nucliadb.ask_utils import (
|
|
19
|
+
combine_filter_expressions,
|
|
20
|
+
get_chunk_text,
|
|
21
|
+
to_field_filter_expression,
|
|
22
|
+
)
|
|
23
|
+
from hyperforge_nucliadb.driver import (
|
|
24
|
+
NucliaDBConnection,
|
|
25
|
+
NucliaDBDriver,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@agent(
|
|
30
|
+
id="advanced_ask",
|
|
31
|
+
agent_type="context",
|
|
32
|
+
title="Knowledge Box Advanced Ask",
|
|
33
|
+
description="Ask a question to the knowledge box and retrieve relevant information",
|
|
34
|
+
config_schema=AdvancedAskAgentConfig,
|
|
35
|
+
)
|
|
36
|
+
class AdvancedAskAgent(ContextAgent, Agent[AdvancedAskAgentConfig]):
|
|
37
|
+
agent_description: str = "Agent that queries a NucliaDB Knowledge Box to get context to answer questions. It is a retrieval agent, so questions should be in a format that makes sense for retrieval."
|
|
38
|
+
|
|
39
|
+
async def _get_question_context(
|
|
40
|
+
self,
|
|
41
|
+
memory: QuestionMemory,
|
|
42
|
+
manager: Manager,
|
|
43
|
+
question_uuid: str,
|
|
44
|
+
question: str,
|
|
45
|
+
flow_id: str,
|
|
46
|
+
extra_context: Optional[Dict[str, Any]] = None,
|
|
47
|
+
) -> List[tuple[str, str]]:
|
|
48
|
+
sources = self.config.sources
|
|
49
|
+
# Choose sources based on the question/s and the in
|
|
50
|
+
chosen_sources = await choose_source(
|
|
51
|
+
memory,
|
|
52
|
+
manager,
|
|
53
|
+
sources,
|
|
54
|
+
question,
|
|
55
|
+
ident=self.config.id if self.config.id else "default",
|
|
56
|
+
step_title=self.step_title("Choose sources"),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
missing: list[tuple[str, str] | None] = await asyncio.gather(
|
|
60
|
+
*[
|
|
61
|
+
self.rag(
|
|
62
|
+
memory, manager, source, question_uuid, question, flow_id=flow_id
|
|
63
|
+
)
|
|
64
|
+
for source in chosen_sources
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
# We only want to fallback if all sources failed
|
|
68
|
+
if any(m is None for m in missing):
|
|
69
|
+
return []
|
|
70
|
+
else:
|
|
71
|
+
# XXX: We might be duplicating some results here if multiple sources return the same missing question
|
|
72
|
+
return [r for r in missing if r is not None]
|
|
73
|
+
|
|
74
|
+
async def rag(
|
|
75
|
+
self,
|
|
76
|
+
memory: QuestionMemory,
|
|
77
|
+
manager: Manager,
|
|
78
|
+
source_obj: Source,
|
|
79
|
+
question_uuid: str,
|
|
80
|
+
question: str,
|
|
81
|
+
flow_id: str,
|
|
82
|
+
) -> tuple[str, str] | None:
|
|
83
|
+
source = source_obj.id
|
|
84
|
+
|
|
85
|
+
nucliadb_driver = get_ndb_driver(manager, source)
|
|
86
|
+
|
|
87
|
+
context = Context(
|
|
88
|
+
agent_id=self.config.id,
|
|
89
|
+
original_question_uuid=memory.original_question_uuid,
|
|
90
|
+
actual_question_uuid=question_uuid,
|
|
91
|
+
question=question,
|
|
92
|
+
source=source,
|
|
93
|
+
agent="advanced_ask",
|
|
94
|
+
title=self.config.title
|
|
95
|
+
if self.config.title
|
|
96
|
+
else f"Retrieval on {source} Knowledge Box",
|
|
97
|
+
)
|
|
98
|
+
t0 = time()
|
|
99
|
+
ask_request = build_ask_request(self.config, nucliadb_driver.config, question)
|
|
100
|
+
paragraphs = await nucliadb_driver.ask(ask_request)
|
|
101
|
+
answer = None
|
|
102
|
+
if paragraphs is not None:
|
|
103
|
+
input_tokens = (
|
|
104
|
+
paragraphs.metadata.tokens.input_nuclia
|
|
105
|
+
if paragraphs.metadata and paragraphs.metadata.tokens
|
|
106
|
+
else 0
|
|
107
|
+
)
|
|
108
|
+
output_tokens = (
|
|
109
|
+
paragraphs.metadata.tokens.output_nuclia
|
|
110
|
+
if paragraphs.metadata and paragraphs.metadata.tokens
|
|
111
|
+
else 0
|
|
112
|
+
)
|
|
113
|
+
context.chunks = []
|
|
114
|
+
answer = (
|
|
115
|
+
paragraphs.answer
|
|
116
|
+
if paragraphs.answer
|
|
117
|
+
and "not enough data to answer this" not in paragraphs.answer
|
|
118
|
+
else ""
|
|
119
|
+
)
|
|
120
|
+
if paragraphs.citations != {}:
|
|
121
|
+
result_chunks = list(paragraphs.citations.keys())
|
|
122
|
+
else:
|
|
123
|
+
result_chunks = paragraphs.retrieval_results.best_matches
|
|
124
|
+
for chunk_id in result_chunks:
|
|
125
|
+
resource_id = chunk_id.split("/")[0]
|
|
126
|
+
resource = paragraphs.retrieval_results.resources[resource_id]
|
|
127
|
+
text = get_chunk_text(paragraphs, chunk_id)
|
|
128
|
+
context.chunks.append(
|
|
129
|
+
Chunk(
|
|
130
|
+
chunk_id=chunk_id,
|
|
131
|
+
title=resource.title,
|
|
132
|
+
text=text,
|
|
133
|
+
source=source,
|
|
134
|
+
origin_agent=self.config.module,
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
# TODO: Save citations properly
|
|
138
|
+
|
|
139
|
+
# XXX: This answer will be overriden by any call to save_ctx_and_return_missing below
|
|
140
|
+
if answer:
|
|
141
|
+
context.summary = answer
|
|
142
|
+
if self.fallback is None:
|
|
143
|
+
if answer is not None and answer != "":
|
|
144
|
+
missing = await self.save_ctx_and_return_missing(
|
|
145
|
+
context=context,
|
|
146
|
+
question=question,
|
|
147
|
+
memory=memory,
|
|
148
|
+
manager=manager,
|
|
149
|
+
flow_id=flow_id,
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
missing = (question_uuid, question)
|
|
153
|
+
logger.info(
|
|
154
|
+
f"No context found for question {question} in source {source}, skipping"
|
|
155
|
+
)
|
|
156
|
+
ident = self.config.id if self.config.id else "default"
|
|
157
|
+
await memory.add_step(
|
|
158
|
+
step_module=self.config.module,
|
|
159
|
+
step_title=self.step_title("RAG retrieval"),
|
|
160
|
+
step_reason="Got answer" if answer else "No answer",
|
|
161
|
+
step_value=answer if answer else "No answer",
|
|
162
|
+
timeit=time() - t0,
|
|
163
|
+
input_nuclia_tokens=input_tokens if input_tokens else 0,
|
|
164
|
+
output_nuclia_tokens=output_tokens if output_tokens else 0,
|
|
165
|
+
step_agent_path=f"/context/{ident}",
|
|
166
|
+
)
|
|
167
|
+
return missing
|
|
168
|
+
missing = await self.save_ctx_and_return_missing(
|
|
169
|
+
context=context,
|
|
170
|
+
question=question,
|
|
171
|
+
memory=memory,
|
|
172
|
+
manager=manager,
|
|
173
|
+
flow_id=flow_id,
|
|
174
|
+
)
|
|
175
|
+
return missing
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_ndb_driver(manager: Manager, source: str) -> NucliaDBDriver:
|
|
179
|
+
driver = manager.drivers.get(source)
|
|
180
|
+
if driver is None:
|
|
181
|
+
raise Exception("No NDB available")
|
|
182
|
+
return cast(NucliaDBDriver, driver)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def build_ask_request(
|
|
186
|
+
agent: AdvancedAskAgentConfig,
|
|
187
|
+
driver: NucliaDBConnection,
|
|
188
|
+
question: str,
|
|
189
|
+
) -> AskRequest:
|
|
190
|
+
filter_expression = parse_filter_expression(agent, driver)
|
|
191
|
+
ask_request = AskRequest(
|
|
192
|
+
query=question,
|
|
193
|
+
generative_model=agent.generative_model,
|
|
194
|
+
citations=True,
|
|
195
|
+
filters=driver.filters,
|
|
196
|
+
)
|
|
197
|
+
if filter_expression is not None:
|
|
198
|
+
# filters and filter_expression are mutually exclusive
|
|
199
|
+
ask_request.filter_expression = filter_expression
|
|
200
|
+
ask_request.filters = []
|
|
201
|
+
|
|
202
|
+
# Fields to copy from agent to ask_request
|
|
203
|
+
COPYABLE_FIELDS = [
|
|
204
|
+
"answer_json_schema",
|
|
205
|
+
"citation_threshold",
|
|
206
|
+
"citations",
|
|
207
|
+
"extra_context_images",
|
|
208
|
+
"extra_context",
|
|
209
|
+
"features",
|
|
210
|
+
"generate_answer",
|
|
211
|
+
"max_tokens",
|
|
212
|
+
"min_score",
|
|
213
|
+
"prompt",
|
|
214
|
+
"query_image",
|
|
215
|
+
"rag_images_strategies",
|
|
216
|
+
"rag_strategies",
|
|
217
|
+
"rank_fusion",
|
|
218
|
+
"rephrase",
|
|
219
|
+
"reranker",
|
|
220
|
+
"resource_filters",
|
|
221
|
+
"security",
|
|
222
|
+
"show_hidden",
|
|
223
|
+
"top_k",
|
|
224
|
+
"vectorset",
|
|
225
|
+
"search_configuration",
|
|
226
|
+
]
|
|
227
|
+
for field in COPYABLE_FIELDS:
|
|
228
|
+
value = getattr(agent, field, None)
|
|
229
|
+
if value is not None:
|
|
230
|
+
setattr(ask_request, field, value)
|
|
231
|
+
return ask_request
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def parse_filter_expression(
|
|
235
|
+
agent: AdvancedAskAgentConfig,
|
|
236
|
+
driver: NucliaDBConnection,
|
|
237
|
+
) -> Optional[ndb_filters.FilterExpression]:
|
|
238
|
+
"""
|
|
239
|
+
Make sure to merge agent and driver filter expressions properly if both are present.
|
|
240
|
+
"""
|
|
241
|
+
driver_filter_expression: Optional[ndb_filters.FilterExpression] = None
|
|
242
|
+
if len(driver.filters) > 0:
|
|
243
|
+
operands: list[ndb_filters.FieldFilterExpression] = []
|
|
244
|
+
for filter in driver.filters:
|
|
245
|
+
field_filter = to_field_filter_expression(filter)
|
|
246
|
+
if field_filter is not None:
|
|
247
|
+
operands.append(field_filter)
|
|
248
|
+
if len(operands) == 0:
|
|
249
|
+
driver_filter_expression = None
|
|
250
|
+
elif len(operands) == 1:
|
|
251
|
+
driver_filter_expression = operands[0] # type: ignore
|
|
252
|
+
else:
|
|
253
|
+
driver_filter_expression = ndb_filters.FilterExpression(
|
|
254
|
+
field=ndb_filters.And(operands=operands)
|
|
255
|
+
)
|
|
256
|
+
elif driver.filter_expression is not None:
|
|
257
|
+
driver_filter_expression = driver.filter_expression
|
|
258
|
+
|
|
259
|
+
if agent.filter_expression is None:
|
|
260
|
+
return driver_filter_expression
|
|
261
|
+
else:
|
|
262
|
+
if driver_filter_expression is None:
|
|
263
|
+
return agent.filter_expression
|
|
264
|
+
else:
|
|
265
|
+
return combine_filter_expressions(
|
|
266
|
+
[agent.filter_expression, driver_filter_expression], operator="and"
|
|
267
|
+
)
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from typing import Any, List, Literal, Optional, Union
|
|
2
|
+
|
|
3
|
+
from hyperforge.context.config import ContextAgentConfig
|
|
4
|
+
from hyperforge.utils import WidgetType
|
|
5
|
+
from nucliadb_models.filters import FilterExpression
|
|
6
|
+
from nucliadb_models.search import (
|
|
7
|
+
ChatOptions,
|
|
8
|
+
CitationsType,
|
|
9
|
+
CustomPrompt,
|
|
10
|
+
Image,
|
|
11
|
+
MaxTokens,
|
|
12
|
+
MinScore,
|
|
13
|
+
RagImagesStrategies,
|
|
14
|
+
RagStrategies,
|
|
15
|
+
RankFusion,
|
|
16
|
+
RankFusionName,
|
|
17
|
+
RerankerName,
|
|
18
|
+
)
|
|
19
|
+
from nucliadb_models.security import RequestSecurity
|
|
20
|
+
from pydantic import Field
|
|
21
|
+
from pydantic.config import ConfigDict
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AdvancedAskAgentConfig(ContextAgentConfig):
|
|
25
|
+
model_config = ConfigDict(title="Knowledge Box advanced Ask")
|
|
26
|
+
module: Literal["advanced_ask"] = "advanced_ask"
|
|
27
|
+
sources: List[str] = Field(
|
|
28
|
+
default_factory=list,
|
|
29
|
+
json_schema_extra={
|
|
30
|
+
"show_in_node": True,
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
generative_model: str = Field(
|
|
34
|
+
default="chatgpt-azure-4o-mini",
|
|
35
|
+
title="Generative model",
|
|
36
|
+
description="Model used to generate answers",
|
|
37
|
+
json_schema_extra={"widget": WidgetType.MODEL_SELECT},
|
|
38
|
+
)
|
|
39
|
+
features: Optional[list[ChatOptions]] = Field(
|
|
40
|
+
default=None,
|
|
41
|
+
title="Chat features",
|
|
42
|
+
description="Features enabled for the chat endpoint. Semantic search is done if `semantic` is included. If `keyword` is included, the results will include matching paragraphs from the bm25 index. If `relations` is included, a graph of entities related to the answer is returned. `paragraphs` and `vectors` are deprecated, please use `keyword` and `semantic` instead",
|
|
43
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
44
|
+
)
|
|
45
|
+
top_k: Optional[int] = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
title="Top k",
|
|
48
|
+
ge=1,
|
|
49
|
+
le=200,
|
|
50
|
+
description="The top most relevant results to fetch at the retrieval step. The maximum number of results allowed is 200.",
|
|
51
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
52
|
+
)
|
|
53
|
+
filter_expression: Optional[FilterExpression] = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
title="Filter documents by an expression",
|
|
56
|
+
description=(
|
|
57
|
+
"Returns only documents that match this filter expression."
|
|
58
|
+
"Filtering examples can be found here: https://docs.nuclia.dev/docs/rag/advanced/search-filters"
|
|
59
|
+
),
|
|
60
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
61
|
+
)
|
|
62
|
+
rephrase: Optional[bool] = Field(
|
|
63
|
+
default=None,
|
|
64
|
+
description=(
|
|
65
|
+
"Rephrase the query for a more efficient retrieval. This will consume LLM tokens and make the request slower."
|
|
66
|
+
),
|
|
67
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
68
|
+
)
|
|
69
|
+
reranker: Optional[RerankerName] = Field(
|
|
70
|
+
default=None,
|
|
71
|
+
title="Reranker",
|
|
72
|
+
description="Reranker to use for the search.",
|
|
73
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
74
|
+
)
|
|
75
|
+
security: Optional[RequestSecurity] = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
title="Security",
|
|
78
|
+
description="Security metadata for the request. If not provided, the search request is done without the security lookup phase.",
|
|
79
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
80
|
+
)
|
|
81
|
+
prompt: Optional[CustomPrompt] = Field(
|
|
82
|
+
default=None,
|
|
83
|
+
title="Prompts",
|
|
84
|
+
description="Use to customize the prompts given to the generative model. Both system and user prompts can be customized.",
|
|
85
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
86
|
+
)
|
|
87
|
+
max_tokens: Optional[MaxTokens] = Field(
|
|
88
|
+
default=None,
|
|
89
|
+
title="Maximum LLM tokens to use for the request",
|
|
90
|
+
description="Use to limit the amount of tokens used in the LLM context and/or for generating the answer. If not provided, the default maximum tokens of the generative model will be used. If an integer is provided, it is interpreted as the maximum tokens for the answer.",
|
|
91
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
92
|
+
)
|
|
93
|
+
show_hidden: Optional[bool] = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
title="Show hidden resources",
|
|
96
|
+
description="If set to false (default), excludes hidden resources from search",
|
|
97
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
98
|
+
)
|
|
99
|
+
rag_strategies: Optional[List[RagStrategies]] = Field(
|
|
100
|
+
default=None,
|
|
101
|
+
title="RAG context building strategies",
|
|
102
|
+
description=(
|
|
103
|
+
"""Options for tweaking how the context for the LLM model is crafted:
|
|
104
|
+
- `full_resource` will add the full text of the matching resources to the context. This strategy cannot be combined with `hierarchy`, `neighbouring_paragraphs`, or `field_extension`.
|
|
105
|
+
- `field_extension` will add the text of the matching resource's specified fields to the context.
|
|
106
|
+
- `hierarchy` will add the title and summary text of the parent resource to the context for each matching paragraph.
|
|
107
|
+
- `neighbouring_paragraphs` will add the sorrounding paragraphs to the context for each matching paragraph.
|
|
108
|
+
- `metadata_extension` will add the metadata of the matching paragraphs or its resources to the context.
|
|
109
|
+
- `prequeries` allows to run multiple retrieval queries before the main query and add the results to the context. The results of specific queries can be boosted by the specifying weights.
|
|
110
|
+
|
|
111
|
+
If empty, the default strategy is used, which simply adds the text of the matching paragraphs to the context.
|
|
112
|
+
"""
|
|
113
|
+
),
|
|
114
|
+
examples=[
|
|
115
|
+
[{"name": "full_resource", "count": 2}],
|
|
116
|
+
[
|
|
117
|
+
{"name": "field_extension", "fields": ["t/amend", "a/title"]},
|
|
118
|
+
],
|
|
119
|
+
[{"name": "hierarchy", "count": 2}],
|
|
120
|
+
[{"name": "neighbouring_paragraphs", "before": 2, "after": 2}],
|
|
121
|
+
[
|
|
122
|
+
{
|
|
123
|
+
"name": "metadata_extension",
|
|
124
|
+
"types": ["origin", "classification_labels"],
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
{
|
|
129
|
+
"name": "prequeries",
|
|
130
|
+
"queries": [
|
|
131
|
+
{
|
|
132
|
+
"request": {
|
|
133
|
+
"query": "What is the capital of France?",
|
|
134
|
+
"features": ["keyword"],
|
|
135
|
+
},
|
|
136
|
+
"weight": 0.5,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"request": {
|
|
140
|
+
"query": "What is the capital of Germany?",
|
|
141
|
+
},
|
|
142
|
+
"weight": 0.5,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
],
|
|
148
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
149
|
+
)
|
|
150
|
+
citations: Optional[Union[bool, CitationsType]] = Field(
|
|
151
|
+
default=None,
|
|
152
|
+
description="Whether to include citations in the response. "
|
|
153
|
+
"If set to None or False, no citations will be computed. "
|
|
154
|
+
"If set to True or 'default', citations will be computed after answer generation and send as a separate `CitationsGenerativeResponse` chunk. "
|
|
155
|
+
"If set to 'llm_footnotes', citations will be included in the LLM's response as markdown-styled footnotes. A `FootnoteCitationsGenerativeResponse` chunk will also be sent to map footnote ids to context keys in the `query_context`.",
|
|
156
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
157
|
+
)
|
|
158
|
+
citation_threshold: Optional[float] = Field(
|
|
159
|
+
default=None,
|
|
160
|
+
description="If citations is set to True or 'default', this will be the similarity threshold. Value between 0 and 1, lower values will produce more citations. If not set, it will be set to the optimized threshold found by Nuclia.",
|
|
161
|
+
ge=0.0,
|
|
162
|
+
le=1.0,
|
|
163
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
164
|
+
)
|
|
165
|
+
answer_json_schema: Optional[dict[str, Any]] = Field(
|
|
166
|
+
default=None,
|
|
167
|
+
title="Answer JSON schema",
|
|
168
|
+
description="""Desired JSON schema for the LLM answer.
|
|
169
|
+
This schema is passed to the LLM so that it answers in a scructured format following the schema. If not provided, textual response is returned.
|
|
170
|
+
Note that when using this parameter, the answer in the generative response will not be returned in chunks, the whole response text will be returned instead.
|
|
171
|
+
Using this feature also disables the `citations` parameter. For maximal accuracy, please include a `description` for each field of the schema.
|
|
172
|
+
""",
|
|
173
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
174
|
+
)
|
|
175
|
+
generate_answer: Optional[bool] = Field(
|
|
176
|
+
default=None,
|
|
177
|
+
description="Whether to generate an answer using the generative model. If set to false, the response will only contain the retrieval results.",
|
|
178
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
179
|
+
)
|
|
180
|
+
min_score: Optional[Union[float, MinScore]] = Field(
|
|
181
|
+
default=None,
|
|
182
|
+
title="Minimum score",
|
|
183
|
+
description="Minimum score to filter search results. Results with a lower score will be ignored. Accepts either a float or a dictionary with the minimum scores for the bm25 and vector indexes. If a float is provided, it is interpreted as the minimum score for vector index search.",
|
|
184
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
185
|
+
)
|
|
186
|
+
extra_context: Optional[list[str]] = Field(
|
|
187
|
+
default=None,
|
|
188
|
+
title="Extra query context",
|
|
189
|
+
description="""Additional context that is added to the retrieval context sent to the LLM.
|
|
190
|
+
It allows extending the chat feature with content that may not be in the Knowledge Box.""",
|
|
191
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
192
|
+
)
|
|
193
|
+
extra_context_images: Optional[list[Image]] = Field(
|
|
194
|
+
default=None,
|
|
195
|
+
title="Extra query context images",
|
|
196
|
+
description="""Additional images added to the retrieval context sent to the LLM."
|
|
197
|
+
It allows extending the chat feature with content that may not be in the Knowledge Box.""",
|
|
198
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
199
|
+
)
|
|
200
|
+
query_image: Optional[Image] = Field(
|
|
201
|
+
default=None,
|
|
202
|
+
title="Query image",
|
|
203
|
+
description="Image that will be used together with the query text for retrieval and then sent to the LLM as part of the context. "
|
|
204
|
+
"If a query image is provided, the `extra_context_images` and `rag_images_strategies` will be disabled.",
|
|
205
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
206
|
+
)
|
|
207
|
+
rank_fusion: Optional[Union[RankFusionName, RankFusion]] = Field(
|
|
208
|
+
default=None,
|
|
209
|
+
title="Rank fusion",
|
|
210
|
+
description="Rank fusion algorithm to use to merge results from multiple retrievers (keyword, semantic)",
|
|
211
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
212
|
+
)
|
|
213
|
+
rag_images_strategies: Optional[list[RagImagesStrategies]] = Field(
|
|
214
|
+
default=None,
|
|
215
|
+
title="RAG image context building strategies",
|
|
216
|
+
description=(
|
|
217
|
+
"Options for tweaking how the image based context for the LLM model is crafted:\n"
|
|
218
|
+
"- `page_image` will add the full page image of the matching resources to the context.\n"
|
|
219
|
+
"- `tables` will send the table images for the paragraphs that contain tables and matched the retrieval query.\n"
|
|
220
|
+
"- `paragraph_image` will add the images of the paragraphs that contain images (images for tables are not included).\n"
|
|
221
|
+
"No image strategy is used by default. Note that this is only available for LLM models that support visual inputs. If the model does not support visual inputs, the image strategies will be ignored."
|
|
222
|
+
),
|
|
223
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
224
|
+
)
|
|
225
|
+
resource_filters: Optional[list[str]] = Field(
|
|
226
|
+
default=None,
|
|
227
|
+
title="Resources filter",
|
|
228
|
+
description="List of resource ids to filter search results for. Only paragraphs from the specified resources will be returned.",
|
|
229
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
230
|
+
)
|
|
231
|
+
vectorset: Optional[str] = Field(
|
|
232
|
+
default=None,
|
|
233
|
+
title="Vectorset",
|
|
234
|
+
description="Vectors index to perform the search in. If not provided, NucliaDB will use the default one",
|
|
235
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
236
|
+
)
|
|
237
|
+
search_configuration: Optional[str] = Field(
|
|
238
|
+
default=None,
|
|
239
|
+
description="Load ask parameters from this configuration. Parameters in the request override parameters from the configuration.",
|
|
240
|
+
json_schema_extra={"widget": WidgetType.NOT_SHOWN},
|
|
241
|
+
)
|