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.
Files changed (39) hide show
  1. hyperforge_nucliadb-1.0.0.post22/PKG-INFO +26 -0
  2. hyperforge_nucliadb-1.0.0.post22/README.md +6 -0
  3. hyperforge_nucliadb-1.0.0.post22/pyproject.toml +39 -0
  4. hyperforge_nucliadb-1.0.0.post22/setup.cfg +4 -0
  5. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/__init__.py +13 -0
  6. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/advanced_ask_agent.py +267 -0
  7. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/advanced_ask_config.py +241 -0
  8. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/__init__.py +3 -0
  9. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/analysis.py +310 -0
  10. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/ask.py +326 -0
  11. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/config.py +52 -0
  12. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/hydrate.py +485 -0
  13. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/kb_analysis.py +48 -0
  14. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/knowledge_scan.py +150 -0
  15. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/models.py +59 -0
  16. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/multi.py +146 -0
  17. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/nucliadb.py +238 -0
  18. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/prompt_analysis.py +50 -0
  19. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/query_analysis.py +83 -0
  20. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/rerank.py +45 -0
  21. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask/utils.py +36 -0
  22. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/ask_utils.py +235 -0
  23. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/basic_ask_agent.py +1812 -0
  24. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/basic_ask_config.py +42 -0
  25. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/driver.py +428 -0
  26. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/driver_config.py +42 -0
  27. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/__init__.py +0 -0
  28. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/agent.py +477 -0
  29. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/config.py +12 -0
  30. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/config_driver.py +22 -0
  31. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb/sync/driver.py +148 -0
  32. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/PKG-INFO +26 -0
  33. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/SOURCES.txt +37 -0
  34. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/dependency_links.txt +1 -0
  35. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/requires.txt +3 -0
  36. hyperforge_nucliadb-1.0.0.post22/src/hyperforge_nucliadb.egg-info/top_level.txt +1 -0
  37. hyperforge_nucliadb-1.0.0.post22/tests/test_ask_utils.py +70 -0
  38. hyperforge_nucliadb-1.0.0.post22/tests/test_nucliadb.py +489 -0
  39. 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,6 @@
1
+ # NucliaDB Hyperforge agents
2
+
3
+ Basic ASK - Basic initial agent
4
+ Advanced ASK (deprecated)
5
+ ASK (experimental)
6
+ 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ )
@@ -0,0 +1,3 @@
1
+ from .ask import AskAgent
2
+
3
+ __all__ = ["AskAgent"]