hyperforge-nucliadb-agentic 1.0.0.post64__py3-none-any.whl
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_agentic/__init__.py +3 -0
- hyperforge_nucliadb_agentic/agent.py +1642 -0
- hyperforge_nucliadb_agentic/ask/__init__.py +5 -0
- hyperforge_nucliadb_agentic/ask/audit.py +439 -0
- hyperforge_nucliadb_agentic/ask/exceptions.py +50 -0
- hyperforge_nucliadb_agentic/ask/lifespan.py +21 -0
- hyperforge_nucliadb_agentic/ask/model.py +1299 -0
- hyperforge_nucliadb_agentic/ask/predict.py +431 -0
- hyperforge_nucliadb_agentic/ask/predict_models.py +78 -0
- hyperforge_nucliadb_agentic/ask/search/__init__.py +0 -0
- hyperforge_nucliadb_agentic/ask/search/ask.py +1182 -0
- hyperforge_nucliadb_agentic/ask/search/graph_strategy.py +1138 -0
- hyperforge_nucliadb_agentic/ask/search/highlight.py +93 -0
- hyperforge_nucliadb_agentic/ask/search/hydrator.py +29 -0
- hyperforge_nucliadb_agentic/ask/search/metrics.py +112 -0
- hyperforge_nucliadb_agentic/ask/search/parsers/__init__.py +0 -0
- hyperforge_nucliadb_agentic/ask/search/parsers/ask.py +70 -0
- hyperforge_nucliadb_agentic/ask/search/parsers/fetcher.py +192 -0
- hyperforge_nucliadb_agentic/ask/search/parsers/find.py +729 -0
- hyperforge_nucliadb_agentic/ask/search/prompt.py +1298 -0
- hyperforge_nucliadb_agentic/ask/search/rank_fusion.py +157 -0
- hyperforge_nucliadb_agentic/ask/search/rerankers.py +161 -0
- hyperforge_nucliadb_agentic/ask/search/retrieval.py +750 -0
- hyperforge_nucliadb_agentic/ask/search/rpc.py +192 -0
- hyperforge_nucliadb_agentic/ask/settings.py +9 -0
- hyperforge_nucliadb_agentic/ask/utils/ids.py +188 -0
- hyperforge_nucliadb_agentic/ask/utils/proto.py +6 -0
- hyperforge_nucliadb_agentic/ask/utils/responses.py +6 -0
- hyperforge_nucliadb_agentic/ask/utils/text_blocks.py +51 -0
- hyperforge_nucliadb_agentic/config.py +47 -0
- hyperforge_nucliadb_agentic/internal_driver.py +87 -0
- hyperforge_nucliadb_agentic/py.typed +0 -0
- hyperforge_nucliadb_agentic-1.0.0.post64.dist-info/METADATA +24 -0
- hyperforge_nucliadb_agentic-1.0.0.post64.dist-info/RECORD +35 -0
- hyperforge_nucliadb_agentic-1.0.0.post64.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1299 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Annotated, Any, Literal, Self # type: ignore
|
|
3
|
+
|
|
4
|
+
from nuclia_models.common.consumption import Consumption
|
|
5
|
+
from nucliadb_models import (
|
|
6
|
+
DateTime,
|
|
7
|
+
FieldTypeName,
|
|
8
|
+
FilterExpression,
|
|
9
|
+
RequestSecurity,
|
|
10
|
+
SearchParamDefaults,
|
|
11
|
+
)
|
|
12
|
+
from nucliadb_models.resource import ExtractedDataTypeName
|
|
13
|
+
from nucliadb_models.search import (
|
|
14
|
+
ANSWER_JSON_SCHEMA_EXAMPLE,
|
|
15
|
+
AuditMetadataBase,
|
|
16
|
+
ChatOptions,
|
|
17
|
+
Filter,
|
|
18
|
+
FindRequest,
|
|
19
|
+
Image,
|
|
20
|
+
KnowledgeboxFindResults,
|
|
21
|
+
MinScore,
|
|
22
|
+
RankFusion,
|
|
23
|
+
RankFusionName,
|
|
24
|
+
Relations,
|
|
25
|
+
Reranker,
|
|
26
|
+
RerankerName,
|
|
27
|
+
ResourceProperties,
|
|
28
|
+
TextPosition,
|
|
29
|
+
_validate_resource_filter,
|
|
30
|
+
)
|
|
31
|
+
from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator
|
|
32
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Author(str, Enum):
|
|
36
|
+
NUCLIA = "NUCLIA"
|
|
37
|
+
USER = "USER"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ChatContextMessage(BaseModel):
|
|
41
|
+
author: Author
|
|
42
|
+
text: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# For bw compatibility
|
|
46
|
+
Message = ChatContextMessage
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class UserPrompt(BaseModel):
|
|
50
|
+
prompt: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class MaxTokens(BaseModel):
|
|
54
|
+
context: int | None = Field(
|
|
55
|
+
default=None,
|
|
56
|
+
title="Maximum context tokens",
|
|
57
|
+
description="Use to limit the amount of tokens used in the LLM context",
|
|
58
|
+
)
|
|
59
|
+
answer: int | None = Field(
|
|
60
|
+
default=None,
|
|
61
|
+
title="Maximum answer tokens",
|
|
62
|
+
description="Use to limit the amount of tokens used in the LLM answer",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Reasoning(BaseModel):
|
|
67
|
+
display: bool = Field(
|
|
68
|
+
default=True,
|
|
69
|
+
description="Whether to display the reasoning steps in the response.",
|
|
70
|
+
)
|
|
71
|
+
effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"] = Field(
|
|
72
|
+
default="medium",
|
|
73
|
+
description=(
|
|
74
|
+
"Level of reasoning effort. Used by OpenAI models to control the depth of reasoning. "
|
|
75
|
+
"This parameter will be automatically mapped to budget_tokens "
|
|
76
|
+
"if the chosen model does not support effort."
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
budget_tokens: int = Field(
|
|
80
|
+
default=15_000,
|
|
81
|
+
description=(
|
|
82
|
+
"Token budget for reasoning. Used by Anthropic or Google models to limit the number of "
|
|
83
|
+
"tokens used for reasoning. This parameter will be automatically mapped to effort "
|
|
84
|
+
"if the chosen model does not support budget_tokens."
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class CitationsType(str, Enum):
|
|
90
|
+
NONE = "none"
|
|
91
|
+
DEFAULT = "default"
|
|
92
|
+
LLM_FOOTNOTES = "llm_footnotes"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ChatModel(BaseModel):
|
|
96
|
+
"""
|
|
97
|
+
This is the model for the predict request payload on the chat endpoint
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
question: str = Field(description="Question to ask the generative model")
|
|
101
|
+
user_id: str
|
|
102
|
+
retrieval: bool = True
|
|
103
|
+
system: str | None = Field(
|
|
104
|
+
default=None,
|
|
105
|
+
title="System prompt",
|
|
106
|
+
description="Optional system prompt input by the user",
|
|
107
|
+
)
|
|
108
|
+
query_context: dict[str, str] = Field(
|
|
109
|
+
default={},
|
|
110
|
+
description="The information retrieval context for the current query",
|
|
111
|
+
)
|
|
112
|
+
query_context_order: dict[str, int] | None = Field(
|
|
113
|
+
default=None,
|
|
114
|
+
description="The order of the query context elements. This is used to sort the context elements by relevance before sending them to the generative model",
|
|
115
|
+
)
|
|
116
|
+
chat_history: list[ChatContextMessage] = Field(
|
|
117
|
+
default=[], description="The chat conversation history"
|
|
118
|
+
)
|
|
119
|
+
truncate: bool = Field(
|
|
120
|
+
default=True,
|
|
121
|
+
description="Truncate the chat context in case it doesn't fit the generative input",
|
|
122
|
+
)
|
|
123
|
+
user_prompt: UserPrompt | None = Field(
|
|
124
|
+
default=None, description="Optional custom prompt input by the user"
|
|
125
|
+
)
|
|
126
|
+
citations: bool | None | CitationsType = Field(
|
|
127
|
+
default=None,
|
|
128
|
+
description="Whether to include citations in the response. "
|
|
129
|
+
"If set to None or False, no citations will be computed. "
|
|
130
|
+
"If set to True or 'default', citations will be computed after answer generation and send as a separate `CitationsGenerativeResponse` chunk. "
|
|
131
|
+
"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`.",
|
|
132
|
+
)
|
|
133
|
+
citation_threshold: float | None = Field(
|
|
134
|
+
default=None,
|
|
135
|
+
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.",
|
|
136
|
+
ge=0.0,
|
|
137
|
+
le=1.0,
|
|
138
|
+
)
|
|
139
|
+
generative_model: str | None = Field(
|
|
140
|
+
default=None,
|
|
141
|
+
title="Generative model",
|
|
142
|
+
description="The generative model to use for the predict chat endpoint. If not provided, the model configured for the Knowledge Box is used.",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
max_tokens: int | None = Field(
|
|
146
|
+
default=None, description="Maximum characters to generate"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
query_context_images: dict[str, Image] = Field(
|
|
150
|
+
default={},
|
|
151
|
+
description="The information retrieval context for the current query, each image is a base64 encoded string",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
prefer_markdown: bool = Field(
|
|
155
|
+
default=False,
|
|
156
|
+
description="If set to true, the response will be in markdown format",
|
|
157
|
+
)
|
|
158
|
+
json_schema: dict[str, Any] | None = Field(
|
|
159
|
+
default=None,
|
|
160
|
+
description="The JSON schema to use for the generative model answers",
|
|
161
|
+
)
|
|
162
|
+
rerank_context: bool = Field(
|
|
163
|
+
default=False,
|
|
164
|
+
description="Whether to reorder the query context based on a reranker",
|
|
165
|
+
)
|
|
166
|
+
top_k: int | None = Field(
|
|
167
|
+
default=None, description="Number of best elements to get from"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
format_prompt: bool = Field(
|
|
171
|
+
default=True,
|
|
172
|
+
description="If set to false, the prompt given as `user_prompt` will be used as is, without any formatting for question or context. If set to true, the prompt must contain the placeholders {question} and {context} to be replaced by the question and context respectively",
|
|
173
|
+
)
|
|
174
|
+
seed: int | None = Field(
|
|
175
|
+
default=None,
|
|
176
|
+
description="Seed use for the generative model for a deterministic output.",
|
|
177
|
+
)
|
|
178
|
+
reasoning: Reasoning | bool = Field(
|
|
179
|
+
title="Reasoning options",
|
|
180
|
+
default=False,
|
|
181
|
+
description=(
|
|
182
|
+
"Reasoning options for the generative model. "
|
|
183
|
+
"Set to True to enable default reasoning, False to disable, or provide a Reasoning object for custom options."
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class RephraseModel(BaseModel):
|
|
189
|
+
question: str
|
|
190
|
+
chat_history: list[ChatContextMessage] = []
|
|
191
|
+
user_id: str
|
|
192
|
+
user_context: list[str] = []
|
|
193
|
+
generative_model: str | None = Field(
|
|
194
|
+
default=None,
|
|
195
|
+
title="Generative model",
|
|
196
|
+
description="The generative model to use for the rephrase endpoint. If not provided, the model configured for the Knowledge Box is used.",
|
|
197
|
+
)
|
|
198
|
+
chat_history_relevance_threshold: (
|
|
199
|
+
Annotated[
|
|
200
|
+
float,
|
|
201
|
+
Field(
|
|
202
|
+
ge=0.0,
|
|
203
|
+
le=1.0,
|
|
204
|
+
description="Threshold to determine if the past chat history is relevant to rephrase the user's question. "
|
|
205
|
+
"0 - Always treat previous messages as relevant (always rephrase)."
|
|
206
|
+
"1 - Always treat previous messages as irrelevant (never rephrase)."
|
|
207
|
+
"Values in between adjust the sensitivity.",
|
|
208
|
+
),
|
|
209
|
+
]
|
|
210
|
+
| None
|
|
211
|
+
) = None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class RagStrategyName:
|
|
215
|
+
FIELD_EXTENSION = "field_extension"
|
|
216
|
+
FULL_RESOURCE = "full_resource"
|
|
217
|
+
HIERARCHY = "hierarchy"
|
|
218
|
+
NEIGHBOURING_PARAGRAPHS = "neighbouring_paragraphs"
|
|
219
|
+
METADATA_EXTENSION = "metadata_extension"
|
|
220
|
+
PREQUERIES = "prequeries"
|
|
221
|
+
CONVERSATION = "conversation"
|
|
222
|
+
GRAPH = "graph_beta"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class ImageRagStrategyName:
|
|
226
|
+
PAGE_IMAGE = "page_image"
|
|
227
|
+
TABLES = "tables"
|
|
228
|
+
PARAGRAPH_IMAGE = "paragraph_image"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class RagStrategy(BaseModel):
|
|
232
|
+
name: Any
|
|
233
|
+
|
|
234
|
+
@model_validator(mode="after")
|
|
235
|
+
def set_discriminator(self) -> Self:
|
|
236
|
+
# Ensure discriminator is explicitly set so it's always serialized
|
|
237
|
+
self.name = self.name
|
|
238
|
+
return self
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ImageRagStrategy(BaseModel):
|
|
242
|
+
name: Any
|
|
243
|
+
|
|
244
|
+
@model_validator(mode="after")
|
|
245
|
+
def set_discriminator(self) -> Self:
|
|
246
|
+
# Ensure discriminator is explicitly set so it's always serialized
|
|
247
|
+
self.name = self.name
|
|
248
|
+
return self
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
ALLOWED_FIELD_TYPES: dict[str, str] = {
|
|
252
|
+
"t": "text",
|
|
253
|
+
"f": "file",
|
|
254
|
+
"u": "link",
|
|
255
|
+
"c": "conversation",
|
|
256
|
+
"a": "generic",
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class FieldExtensionStrategy(RagStrategy):
|
|
261
|
+
name: Literal["field_extension"] = "field_extension"
|
|
262
|
+
fields: list[str] = Field(
|
|
263
|
+
default=[],
|
|
264
|
+
title="Fields",
|
|
265
|
+
description="List of field ids to extend the context with. It will try to extend the retrieval context with the specified fields in the matching resources. The field ids have to be in the format `{field_type}/{field_name}`, like 'a/title', 'a/summary' for title and summary fields or 't/amend' for a text field named 'amend'.",
|
|
266
|
+
)
|
|
267
|
+
data_augmentation_field_prefixes: list[str] = Field(
|
|
268
|
+
default=[],
|
|
269
|
+
description="List of prefixes for data augmentation added fields to extend the context with. For example, if the prefix is 'simpson', all fields that are a result of data augmentation with that prefix will be used to extend the context.",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
@model_validator(mode="after")
|
|
273
|
+
def field_extension_strategy_validator(self) -> Self:
|
|
274
|
+
# We also accept /{field_type}/{field_name} for legacy/usability but we
|
|
275
|
+
# remove it so we don't deal with it
|
|
276
|
+
self.fields = [field.strip("/") for field in self.fields]
|
|
277
|
+
|
|
278
|
+
# Check that the fields are in the format {field_type}/{field_name}
|
|
279
|
+
for field in self.fields:
|
|
280
|
+
try:
|
|
281
|
+
field_type, _ = field.split("/")
|
|
282
|
+
except ValueError:
|
|
283
|
+
raise ValueError(
|
|
284
|
+
f"Field '{field}' is not in the format {{field_type}}/{{field_name}}"
|
|
285
|
+
)
|
|
286
|
+
if field_type not in ALLOWED_FIELD_TYPES:
|
|
287
|
+
allowed_field_types_part = ", ".join(
|
|
288
|
+
[
|
|
289
|
+
f"'{fid}' for '{fname}' fields"
|
|
290
|
+
for fid, fname in ALLOWED_FIELD_TYPES.items()
|
|
291
|
+
]
|
|
292
|
+
)
|
|
293
|
+
raise ValueError(
|
|
294
|
+
f"Field '{field}' does not have a valid field type. "
|
|
295
|
+
f"Valid field types are: {allowed_field_types_part}."
|
|
296
|
+
)
|
|
297
|
+
return self
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class FullResourceApplyTo(BaseModel):
|
|
301
|
+
exclude: list[str] = Field(
|
|
302
|
+
default_factory=list,
|
|
303
|
+
title="Labels to exclude from full resource expansion",
|
|
304
|
+
description="Resources from matches containing any of these labels won't expand to the full resource. This may be useful to exclude long and not interesting resources and expend less tokens",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class FullResourceStrategy(RagStrategy):
|
|
309
|
+
name: Literal["full_resource"] = "full_resource"
|
|
310
|
+
count: int | None = Field(
|
|
311
|
+
default=None,
|
|
312
|
+
title="Count",
|
|
313
|
+
description="Maximum number of full documents to retrieve. If not specified, all matching documents are retrieved.",
|
|
314
|
+
ge=1,
|
|
315
|
+
)
|
|
316
|
+
include_remaining_text_blocks: bool = Field(
|
|
317
|
+
default=False,
|
|
318
|
+
title="Include remaining text blocks",
|
|
319
|
+
description="Whether to include the remaining text blocks after the maximum number of resources has been reached.",
|
|
320
|
+
)
|
|
321
|
+
apply_to: FullResourceApplyTo | None = Field(
|
|
322
|
+
default=None,
|
|
323
|
+
title="Apply to certain resources only",
|
|
324
|
+
description="Define which resources to exclude from serialization",
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class HierarchyResourceStrategy(RagStrategy):
|
|
329
|
+
name: Literal["hierarchy"] = "hierarchy"
|
|
330
|
+
count: int = Field(
|
|
331
|
+
default=0,
|
|
332
|
+
title="Count",
|
|
333
|
+
description="Number of extra characters that are added to each matching paragraph when adding to the context.",
|
|
334
|
+
ge=0,
|
|
335
|
+
le=1024,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class NeighbouringParagraphsStrategy(RagStrategy):
|
|
340
|
+
name: Literal["neighbouring_paragraphs"] = "neighbouring_paragraphs"
|
|
341
|
+
before: int = Field(
|
|
342
|
+
default=2,
|
|
343
|
+
title="Before",
|
|
344
|
+
description="Number of previous neighbouring paragraphs to add to the context, for each matching paragraph in the retrieval step.",
|
|
345
|
+
ge=0,
|
|
346
|
+
)
|
|
347
|
+
after: int = Field(
|
|
348
|
+
default=2,
|
|
349
|
+
title="After",
|
|
350
|
+
description="Number of following neighbouring paragraphs to add to the context, for each matching paragraph in the retrieval step.",
|
|
351
|
+
ge=0,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class MetadataExtensionType(str, Enum):
|
|
356
|
+
ORIGIN = "origin"
|
|
357
|
+
CLASSIFICATION_LABELS = "classification_labels"
|
|
358
|
+
NERS = "ners"
|
|
359
|
+
EXTRA_METADATA = "extra_metadata"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class MetadataExtensionStrategy(RagStrategy):
|
|
363
|
+
"""
|
|
364
|
+
RAG strategy to enrich the context with metadata of the matching paragraphs or its resources.
|
|
365
|
+
This strategy can be combined with any of the other strategies.
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
name: Literal["metadata_extension"] = "metadata_extension"
|
|
369
|
+
types: list[MetadataExtensionType] = Field(
|
|
370
|
+
min_length=1,
|
|
371
|
+
title="Types",
|
|
372
|
+
description="""
|
|
373
|
+
List of resource metadata types to add to the context.
|
|
374
|
+
- 'origin': origin metadata of the resource.
|
|
375
|
+
- 'classification_labels': classification labels of the resource.
|
|
376
|
+
- 'ner': Named Entity Recognition entities detected for the resource.
|
|
377
|
+
- 'extra_metadata': extra metadata of the resource.
|
|
378
|
+
|
|
379
|
+
Types for which the metadata is not found at the resource are ignored and not added to the context.
|
|
380
|
+
""",
|
|
381
|
+
examples=[
|
|
382
|
+
["origin", "classification_labels"],
|
|
383
|
+
["ners"],
|
|
384
|
+
],
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class ConversationalStrategy(RagStrategy):
|
|
389
|
+
name: Literal["conversation"] = "conversation"
|
|
390
|
+
attachments_text: bool = Field(
|
|
391
|
+
default=False,
|
|
392
|
+
title="Add attachments on context",
|
|
393
|
+
description="Add attachments on context retrieved on conversation",
|
|
394
|
+
)
|
|
395
|
+
attachments_images: bool = Field(
|
|
396
|
+
default=False,
|
|
397
|
+
title="Add attachments images on context",
|
|
398
|
+
description="Add attachments images on context retrieved on conversation if they are mime type image and using a visual LLM",
|
|
399
|
+
)
|
|
400
|
+
full: bool = Field(
|
|
401
|
+
default=False,
|
|
402
|
+
title="Add all conversation",
|
|
403
|
+
description="Add all conversation fields on matched blocks",
|
|
404
|
+
)
|
|
405
|
+
max_messages: int = Field(
|
|
406
|
+
default=15,
|
|
407
|
+
title="Max messages",
|
|
408
|
+
description="Max messages to append in case its not full field",
|
|
409
|
+
ge=0,
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
class PreQuery(BaseModel):
|
|
414
|
+
request: "FindRequest" = Field(
|
|
415
|
+
title="Request",
|
|
416
|
+
description="The request to be executed before the main query.",
|
|
417
|
+
)
|
|
418
|
+
weight: float = Field(
|
|
419
|
+
default=1.0,
|
|
420
|
+
title="Weight",
|
|
421
|
+
description=(
|
|
422
|
+
"Weight of the prequery in the context. The weight is used to scale the results of the prequery before adding them to the context."
|
|
423
|
+
"The weight should be a positive number, and they are normalized so that the sum of all weights for all prequeries is 1."
|
|
424
|
+
),
|
|
425
|
+
ge=0,
|
|
426
|
+
)
|
|
427
|
+
id: str | None = Field(
|
|
428
|
+
default=None,
|
|
429
|
+
title="Prequery id",
|
|
430
|
+
min_length=1,
|
|
431
|
+
max_length=100,
|
|
432
|
+
description="Identifier of the prequery. If not specified, it is autogenerated based on the index of the prequery in the list (prequery_0, prequery_1, ...).",
|
|
433
|
+
examples=[
|
|
434
|
+
"title_prequery",
|
|
435
|
+
"summary_prequery",
|
|
436
|
+
"prequery_1",
|
|
437
|
+
],
|
|
438
|
+
)
|
|
439
|
+
prefilter: bool = Field(
|
|
440
|
+
default=False,
|
|
441
|
+
title="Prefilter",
|
|
442
|
+
description=(
|
|
443
|
+
"If set to true, the prequery results are used to filter the scope of the remaining queries. "
|
|
444
|
+
"The resources of the most relevant paragraphs of the prefilter queries are used as resource "
|
|
445
|
+
"filters for the main query and other prequeries with the prefilter flag set to false."
|
|
446
|
+
),
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class PreQueriesStrategy(RagStrategy):
|
|
451
|
+
"""
|
|
452
|
+
This strategy allows to run a set of queries before the main query and add the results to the context.
|
|
453
|
+
It allows to give more importance to some queries over others by setting the weight of each query.
|
|
454
|
+
The weight of the main query can also be set with the `main_query_weight` parameter.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
name: Literal["prequeries"] = "prequeries"
|
|
458
|
+
queries: list[PreQuery] = Field(
|
|
459
|
+
title="Queries",
|
|
460
|
+
description="List of queries to run before the main query. The results are added to the context with the specified weights for each query. There is a limit of 10 prequeries per request.",
|
|
461
|
+
min_length=1,
|
|
462
|
+
max_length=15,
|
|
463
|
+
)
|
|
464
|
+
main_query_weight: float = Field(
|
|
465
|
+
default=1.0,
|
|
466
|
+
title="Main query weight",
|
|
467
|
+
description="Weight of the main query in the context. Use this to control the importance of the main query in the context.",
|
|
468
|
+
ge=0,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
PreQueryResult = tuple[PreQuery, "KnowledgeboxFindResults"]
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class RelationRanking(str, Enum):
|
|
476
|
+
RERANKER = "reranker"
|
|
477
|
+
GENERATIVE = "generative"
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class QueryEntityDetection(str, Enum):
|
|
481
|
+
PREDICT = "predict"
|
|
482
|
+
SUGGEST = "suggest"
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class GraphStrategy(RagStrategy):
|
|
486
|
+
"""
|
|
487
|
+
This strategy retrieves context pieces by exploring the Knowledge Graph, starting from the entities present in the query.
|
|
488
|
+
It works best if the Knowledge Box has a user-defined Graph Extraction agent enabled.
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
name: Literal["graph_beta"] = "graph_beta"
|
|
492
|
+
hops: int = Field(
|
|
493
|
+
default=3,
|
|
494
|
+
title="Number of hops",
|
|
495
|
+
description="""Number of hops to take when exploring the graph for relevant context.
|
|
496
|
+
For example,
|
|
497
|
+
- hops=1 will explore the neighbors of the starting entities.
|
|
498
|
+
- hops=2 will explore the neighbors of the neighbors of the starting entities.
|
|
499
|
+
And so on.
|
|
500
|
+
Bigger values will discover more intricate relationships but will also take more time to compute.""",
|
|
501
|
+
ge=1,
|
|
502
|
+
le=10,
|
|
503
|
+
)
|
|
504
|
+
# Here we ingore mypy because the default value is set dynamically in the model_validator
|
|
505
|
+
top_k: int = Field( # type: ignore
|
|
506
|
+
default=None,
|
|
507
|
+
title="Top k",
|
|
508
|
+
description="Number of relationships to keep after each hop after ranking them by relevance to the query. This number correlates to more paragraphs being sent as context. If not set, this number will be set to 30 if `relation_text_as_paragraphs` is set to false or 200 if `relation_text_as_paragraphs` is set to true.",
|
|
509
|
+
ge=1,
|
|
510
|
+
le=300,
|
|
511
|
+
)
|
|
512
|
+
exclude_processor_relations: bool = Field(
|
|
513
|
+
default=True,
|
|
514
|
+
title="Do not use relations extracted by processor.",
|
|
515
|
+
description="If set to true, only relationships extracted from a graph extraction agent are considered for context expansion.",
|
|
516
|
+
validation_alias=AliasChoices(
|
|
517
|
+
"agentic_graph_only", "exclude_processor_relations"
|
|
518
|
+
),
|
|
519
|
+
)
|
|
520
|
+
relation_text_as_paragraphs: bool = Field(
|
|
521
|
+
default=False,
|
|
522
|
+
title="Use relation text as context",
|
|
523
|
+
description="If set to true, the text of the relationships is to create context paragraphs, this enables to use bigger top K values without running into the generative model's context limits. If set to false, the paragraphs that contain the relationships are used as context.",
|
|
524
|
+
)
|
|
525
|
+
relation_ranking: RelationRanking = Field(
|
|
526
|
+
default=RelationRanking.RERANKER,
|
|
527
|
+
title="Method to rank relationships",
|
|
528
|
+
description="""Method to rank relationships.
|
|
529
|
+
- `reranker` uses the reranker model to rank relationships.
|
|
530
|
+
- `generative` uses first the reranker to first lower the amount of relationships and then the generative model to rank relationships.
|
|
531
|
+
The generative model is slower and consumes more tokens, but can provide better results.""",
|
|
532
|
+
)
|
|
533
|
+
query_entity_detection: QueryEntityDetection = Field(
|
|
534
|
+
default=QueryEntityDetection.PREDICT,
|
|
535
|
+
title="Method to detect entities in the query",
|
|
536
|
+
description="""Method to detect entities in the query.
|
|
537
|
+
- `predict` uses NUA to detect entities in the query, slower and more accurate but requires an exact text match between Knowledge Box entities and entities in the query.
|
|
538
|
+
- `suggest` uses fuzzy search to detect entities. It's faster and more flexible but might have trouble matching entities composed of multiple words. It will fallback to Predict if no entities are detected.""",
|
|
539
|
+
)
|
|
540
|
+
weight: float = Field(
|
|
541
|
+
default=3.0,
|
|
542
|
+
title="Weight",
|
|
543
|
+
description=(
|
|
544
|
+
"Weight of the graph strategy in the context. The weight is used to scale the results of the strategy before adding them to the context."
|
|
545
|
+
"The weight should be a positive number."
|
|
546
|
+
),
|
|
547
|
+
ge=0,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
@model_validator(mode="before")
|
|
551
|
+
def set_dynamic_defaults(cls, values):
|
|
552
|
+
if values.get("top_k") is None:
|
|
553
|
+
values["top_k"] = 200 if values.get("relation_text_as_paragraphs") else 30
|
|
554
|
+
return values
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
class TableImageStrategy(ImageRagStrategy):
|
|
558
|
+
name: Literal["tables"] = "tables"
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class PageImageStrategy(ImageRagStrategy):
|
|
562
|
+
name: Literal["page_image"] = "page_image"
|
|
563
|
+
count: int | None = Field(
|
|
564
|
+
default=None,
|
|
565
|
+
title="Count",
|
|
566
|
+
description="Maximum number of page images to retrieve. By default, at most 5 images are retrieved.",
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
class ParagraphImageStrategy(ImageRagStrategy):
|
|
571
|
+
name: Literal["paragraph_image"] = "paragraph_image"
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
RagStrategies = Annotated[
|
|
575
|
+
FieldExtensionStrategy
|
|
576
|
+
| FullResourceStrategy
|
|
577
|
+
| HierarchyResourceStrategy
|
|
578
|
+
| NeighbouringParagraphsStrategy
|
|
579
|
+
| MetadataExtensionStrategy
|
|
580
|
+
| ConversationalStrategy
|
|
581
|
+
| PreQueriesStrategy
|
|
582
|
+
| GraphStrategy,
|
|
583
|
+
Field(discriminator="name"),
|
|
584
|
+
]
|
|
585
|
+
RagImagesStrategies = Annotated[
|
|
586
|
+
PageImageStrategy | ParagraphImageStrategy | TableImageStrategy,
|
|
587
|
+
Field(discriminator="name"),
|
|
588
|
+
]
|
|
589
|
+
PromptContext = dict[str, str]
|
|
590
|
+
PromptContextOrder = dict[str, int]
|
|
591
|
+
PromptContextImages = dict[str, Image]
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
class CustomPrompt(BaseModel):
|
|
595
|
+
system: str | None = Field(
|
|
596
|
+
default=None,
|
|
597
|
+
title="System prompt",
|
|
598
|
+
description="System prompt given to the generative model responsible of generating the answer. This can help customize the behavior of the model when generating the answer. If not specified, the default model provider's prompt is used.",
|
|
599
|
+
min_length=1,
|
|
600
|
+
examples=[
|
|
601
|
+
"You are a medical assistant, use medical terminology",
|
|
602
|
+
"You are an IT expert, express yourself like one",
|
|
603
|
+
"You are a very friendly customer service assistant, be polite",
|
|
604
|
+
"You are a financial expert, use correct terms",
|
|
605
|
+
],
|
|
606
|
+
)
|
|
607
|
+
user: str | None = Field(
|
|
608
|
+
default=None,
|
|
609
|
+
title="User prompt",
|
|
610
|
+
description="User prompt given to the generative model responsible of generating the answer. Use the words {context} and {question} in brackets where you want those fields to be placed, in case you want them in your prompt. Context will be the data returned by the retrieval step and question will be the user's query.",
|
|
611
|
+
min_length=1,
|
|
612
|
+
examples=[
|
|
613
|
+
"Taking into account our previous conversation, and this context: {context} answer this {question}",
|
|
614
|
+
"Give a detailed answer to this {question} in a list format. If you do not find an answer in this context: {context}, say that you don't have enough data.",
|
|
615
|
+
"Given this context: {context}. Answer this {question} in a concise way using the provided context",
|
|
616
|
+
"Given this context: {context}. Answer this {question} using the provided context. Please, answer always in French",
|
|
617
|
+
],
|
|
618
|
+
)
|
|
619
|
+
rephrase: str | None = Field(
|
|
620
|
+
default=None,
|
|
621
|
+
title="Rephrase",
|
|
622
|
+
description=(
|
|
623
|
+
"Rephrase prompt given to the generative model responsible for rephrasing the query for a more effective retrieval step. "
|
|
624
|
+
"This is only used if the `rephrase` flag is set to true in the request.\n"
|
|
625
|
+
"If not specified, Nuclia's default prompt is used. It must include the {question} placeholder. "
|
|
626
|
+
"The placeholder will be replaced with the original question"
|
|
627
|
+
),
|
|
628
|
+
min_length=1,
|
|
629
|
+
examples=[
|
|
630
|
+
"""Rephrase this question so its better for retrieval, and keep the rephrased question in the same language as the original.
|
|
631
|
+
QUESTION: {question}
|
|
632
|
+
Please return ONLY the question without any explanation. Just the rephrased question.""",
|
|
633
|
+
"""Rephrase this question so its better for retrieval, identify any part numbers and append them to the end of the question separated by a commas.
|
|
634
|
+
QUESTION: {question}
|
|
635
|
+
Please return ONLY the question without any explanation.""",
|
|
636
|
+
],
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
class TokensDetail(BaseModel):
|
|
641
|
+
input: float
|
|
642
|
+
output: float
|
|
643
|
+
image: float
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class AskRequest(AuditMetadataBase):
|
|
647
|
+
query: str = SearchParamDefaults.chat_query.to_pydantic_field()
|
|
648
|
+
agentic_config_id: str | None = Field(
|
|
649
|
+
default=None,
|
|
650
|
+
title="Agentic configuration ID",
|
|
651
|
+
description=(
|
|
652
|
+
"The ID of the agentic configuration to use for this request. If not provided, the default retrieval and generation parameters of the Knowledge Box will be used. "
|
|
653
|
+
"If provided, the parameters in the agentic configuration will override the parameters in the request. Note that the `query` field in the agentic configuration will be ignored, and the query in the request will be used instead."
|
|
654
|
+
),
|
|
655
|
+
)
|
|
656
|
+
top_k: int = Field(
|
|
657
|
+
default=20,
|
|
658
|
+
title="Top k",
|
|
659
|
+
ge=1,
|
|
660
|
+
le=200,
|
|
661
|
+
description="The top most relevant results to fetch at the retrieval step. The maximum number of results allowed is 200.",
|
|
662
|
+
)
|
|
663
|
+
filter_expression: FilterExpression | None = (
|
|
664
|
+
SearchParamDefaults.filter_expression.to_pydantic_field()
|
|
665
|
+
)
|
|
666
|
+
fields: list[str] = SearchParamDefaults.fields.to_pydantic_field()
|
|
667
|
+
filters: list[str] | list[Filter] = Field(
|
|
668
|
+
default=[],
|
|
669
|
+
title="Search Filters",
|
|
670
|
+
description="The list of filters to apply. Filtering examples can be found here: https://docs.nuclia.dev/docs/rag/advanced/search-filters",
|
|
671
|
+
)
|
|
672
|
+
keyword_filters: list[str] | list[Filter] = Field(
|
|
673
|
+
default=[],
|
|
674
|
+
title="Keyword filters",
|
|
675
|
+
description=(
|
|
676
|
+
"List of keyword filter expressions to apply to the retrieval step. "
|
|
677
|
+
"The text block search will only be performed on the documents that contain the specified keywords. "
|
|
678
|
+
"The filters are case-insensitive, and only alphanumeric characters and spaces are allowed. "
|
|
679
|
+
"Filtering examples can be found here: https://docs.nuclia.dev/docs/rag/advanced/search-filters"
|
|
680
|
+
),
|
|
681
|
+
examples=[
|
|
682
|
+
["NLP", "BERT"],
|
|
683
|
+
[Filter(all=["NLP", "BERT"])],
|
|
684
|
+
["Friedrich Nietzsche", "Immanuel Kant"],
|
|
685
|
+
],
|
|
686
|
+
)
|
|
687
|
+
vectorset: str | None = SearchParamDefaults.vectorset.to_pydantic_field()
|
|
688
|
+
min_score: float | MinScore | None = Field(
|
|
689
|
+
default=None,
|
|
690
|
+
title="Minimum score",
|
|
691
|
+
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.",
|
|
692
|
+
)
|
|
693
|
+
features: list[ChatOptions] = SearchParamDefaults.chat_features.to_pydantic_field()
|
|
694
|
+
range_creation_start: DateTime | None = (
|
|
695
|
+
SearchParamDefaults.range_creation_start.to_pydantic_field()
|
|
696
|
+
)
|
|
697
|
+
range_creation_end: DateTime | None = (
|
|
698
|
+
SearchParamDefaults.range_creation_end.to_pydantic_field()
|
|
699
|
+
)
|
|
700
|
+
range_modification_start: DateTime | None = (
|
|
701
|
+
SearchParamDefaults.range_modification_start.to_pydantic_field()
|
|
702
|
+
)
|
|
703
|
+
range_modification_end: DateTime | None = (
|
|
704
|
+
SearchParamDefaults.range_modification_end.to_pydantic_field()
|
|
705
|
+
)
|
|
706
|
+
show: list[ResourceProperties] = SearchParamDefaults.show.to_pydantic_field()
|
|
707
|
+
field_type_filter: list[FieldTypeName] = (
|
|
708
|
+
SearchParamDefaults.field_type_filter.to_pydantic_field()
|
|
709
|
+
)
|
|
710
|
+
extracted: list[ExtractedDataTypeName] = (
|
|
711
|
+
SearchParamDefaults.extracted.to_pydantic_field()
|
|
712
|
+
)
|
|
713
|
+
context: list[ChatContextMessage] | None = (
|
|
714
|
+
SearchParamDefaults.chat_context.to_pydantic_field()
|
|
715
|
+
)
|
|
716
|
+
chat_history: list[ChatContextMessage] | None = (
|
|
717
|
+
SearchParamDefaults.chat_history.to_pydantic_field()
|
|
718
|
+
)
|
|
719
|
+
extra_context: list[str] | None = Field(
|
|
720
|
+
default=None,
|
|
721
|
+
title="Extra query context",
|
|
722
|
+
description="""Additional context that is added to the retrieval context sent to the LLM.
|
|
723
|
+
It allows extending the chat feature with content that may not be in the Knowledge Box.""",
|
|
724
|
+
)
|
|
725
|
+
extra_context_images: list[Image] | None = Field(
|
|
726
|
+
default=None,
|
|
727
|
+
title="Extra query context images",
|
|
728
|
+
description="""Additional images added to the retrieval context sent to the LLM."
|
|
729
|
+
It allows extending the chat feature with content that may not be in the Knowledge Box.""",
|
|
730
|
+
)
|
|
731
|
+
query_image: Image | None = Field(
|
|
732
|
+
default=None,
|
|
733
|
+
title="Query image",
|
|
734
|
+
description="Image that will be used together with the query text for retrieval and then sent to the LLM as part of the context. "
|
|
735
|
+
"If a query image is provided, the `extra_context_images` and `rag_images_strategies` will be disabled.",
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
# autofilter is deprecated and its logic was removed. We're just keeping it in the model definition to
|
|
739
|
+
# avoid breaking changes in the python sdks. Please remove on a future major release.
|
|
740
|
+
autofilter: SkipJsonSchema[bool] = False
|
|
741
|
+
|
|
742
|
+
highlight: bool = SearchParamDefaults.highlight.to_pydantic_field()
|
|
743
|
+
resource_filters: list[str] = (
|
|
744
|
+
SearchParamDefaults.resource_filters.to_pydantic_field()
|
|
745
|
+
)
|
|
746
|
+
prompt: str | CustomPrompt | None = Field(
|
|
747
|
+
default=None,
|
|
748
|
+
title="Prompts",
|
|
749
|
+
description="Use to customize the prompts given to the generative model. Both system and user prompts can be customized. If a string is provided, it is interpreted as the user prompt.",
|
|
750
|
+
)
|
|
751
|
+
rank_fusion: RankFusionName | RankFusion = (
|
|
752
|
+
SearchParamDefaults.rank_fusion.to_pydantic_field()
|
|
753
|
+
)
|
|
754
|
+
reranker: RerankerName | Reranker = SearchParamDefaults.reranker.to_pydantic_field()
|
|
755
|
+
citations: bool | None | CitationsType = Field(
|
|
756
|
+
default=None,
|
|
757
|
+
description="Whether to include citations in the response. "
|
|
758
|
+
"If set to None or False, no citations will be computed. "
|
|
759
|
+
"If set to True or 'default', citations will be computed after answer generation and send as a separate `CitationsGenerativeResponse` chunk. "
|
|
760
|
+
"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`.",
|
|
761
|
+
)
|
|
762
|
+
citation_threshold: float | None = Field(
|
|
763
|
+
default=None,
|
|
764
|
+
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.",
|
|
765
|
+
ge=0.0,
|
|
766
|
+
le=1.0,
|
|
767
|
+
)
|
|
768
|
+
security: RequestSecurity | None = SearchParamDefaults.security.to_pydantic_field()
|
|
769
|
+
show_hidden: bool = SearchParamDefaults.show_hidden.to_pydantic_field()
|
|
770
|
+
rag_strategies: list[RagStrategies] = Field(
|
|
771
|
+
default=[],
|
|
772
|
+
title="RAG context building strategies",
|
|
773
|
+
description=(
|
|
774
|
+
"""Options for tweaking how the context for the LLM model is crafted:
|
|
775
|
+
- `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`.
|
|
776
|
+
- `field_extension` will add the text of the matching resource's specified fields to the context.
|
|
777
|
+
- `hierarchy` will add the title and summary text of the parent resource to the context for each matching paragraph.
|
|
778
|
+
- `neighbouring_paragraphs` will add the sorrounding paragraphs to the context for each matching paragraph.
|
|
779
|
+
- `metadata_extension` will add the metadata of the matching paragraphs or its resources to the context.
|
|
780
|
+
- `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.
|
|
781
|
+
|
|
782
|
+
If empty, the default strategy is used, which simply adds the text of the matching paragraphs to the context.
|
|
783
|
+
"""
|
|
784
|
+
),
|
|
785
|
+
examples=[
|
|
786
|
+
[{"name": "full_resource", "count": 2}],
|
|
787
|
+
[
|
|
788
|
+
{"name": "field_extension", "fields": ["t/amend", "a/title"]},
|
|
789
|
+
],
|
|
790
|
+
[{"name": "hierarchy", "count": 2}],
|
|
791
|
+
[{"name": "neighbouring_paragraphs", "before": 2, "after": 2}],
|
|
792
|
+
[
|
|
793
|
+
{
|
|
794
|
+
"name": "metadata_extension",
|
|
795
|
+
"types": ["origin", "classification_labels"],
|
|
796
|
+
}
|
|
797
|
+
],
|
|
798
|
+
[
|
|
799
|
+
{
|
|
800
|
+
"name": "prequeries",
|
|
801
|
+
"queries": [
|
|
802
|
+
{
|
|
803
|
+
"request": {
|
|
804
|
+
"query": "What is the capital of France?",
|
|
805
|
+
"features": ["keyword"],
|
|
806
|
+
},
|
|
807
|
+
"weight": 0.5,
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
"request": {
|
|
811
|
+
"query": "What is the capital of Germany?",
|
|
812
|
+
},
|
|
813
|
+
"weight": 0.5,
|
|
814
|
+
},
|
|
815
|
+
],
|
|
816
|
+
}
|
|
817
|
+
],
|
|
818
|
+
],
|
|
819
|
+
)
|
|
820
|
+
rag_images_strategies: list[RagImagesStrategies] = Field(
|
|
821
|
+
default=[],
|
|
822
|
+
title="RAG image context building strategies",
|
|
823
|
+
description=(
|
|
824
|
+
"Options for tweaking how the image based context for the LLM model is crafted:\n"
|
|
825
|
+
"- `page_image` will add the full page image of the matching resources to the context.\n"
|
|
826
|
+
"- `tables` will send the table images for the paragraphs that contain tables and matched the retrieval query.\n"
|
|
827
|
+
"- `paragraph_image` will add the images of the paragraphs that contain images (images for tables are not included).\n"
|
|
828
|
+
"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."
|
|
829
|
+
),
|
|
830
|
+
)
|
|
831
|
+
debug: bool = SearchParamDefaults.debug.to_pydantic_field()
|
|
832
|
+
|
|
833
|
+
generative_model: str | None = Field(
|
|
834
|
+
default=None,
|
|
835
|
+
title="Generative model",
|
|
836
|
+
description="The generative model to use for the chat endpoint. If not provided, the model configured for the Knowledge Box is used.",
|
|
837
|
+
)
|
|
838
|
+
generative_model_seed: int | None = Field(
|
|
839
|
+
default=None,
|
|
840
|
+
title="Seed for the generative model",
|
|
841
|
+
description="The seed to use for the generative model for deterministic generation. Only supported by some models.",
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
max_tokens: int | MaxTokens | None = Field(
|
|
845
|
+
default=None,
|
|
846
|
+
title="Maximum LLM tokens to use for the request",
|
|
847
|
+
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.",
|
|
848
|
+
)
|
|
849
|
+
|
|
850
|
+
rephrase: bool = Field(
|
|
851
|
+
default=False,
|
|
852
|
+
description=(
|
|
853
|
+
"Rephrase the query for a more efficient retrieval. This will consume LLM tokens and make the request slower."
|
|
854
|
+
),
|
|
855
|
+
)
|
|
856
|
+
chat_history_relevance_threshold: float | None = Field(
|
|
857
|
+
default=None,
|
|
858
|
+
ge=0.0,
|
|
859
|
+
le=1.0,
|
|
860
|
+
description=(
|
|
861
|
+
"Threshold to determine if the past chat history is relevant to rephrase the user's question. "
|
|
862
|
+
"0 - Always treat previous messages as relevant (always rephrase)."
|
|
863
|
+
"1 - Always treat previous messages as irrelevant (never rephrase)."
|
|
864
|
+
"Values in between adjust the sensitivity."
|
|
865
|
+
),
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
prefer_markdown: bool = Field(
|
|
869
|
+
default=False,
|
|
870
|
+
title="Prefer markdown",
|
|
871
|
+
description="If set to true, the response will be in markdown format",
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
answer_json_schema: dict[str, Any] | None = Field(
|
|
875
|
+
default=None,
|
|
876
|
+
title="Answer JSON schema",
|
|
877
|
+
description="""Desired JSON schema for the LLM answer.
|
|
878
|
+
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.
|
|
879
|
+
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.
|
|
880
|
+
Using this feature also disables the `citations` parameter. For maximal accuracy, please include a `description` for each field of the schema.
|
|
881
|
+
""",
|
|
882
|
+
examples=[ANSWER_JSON_SCHEMA_EXAMPLE],
|
|
883
|
+
)
|
|
884
|
+
|
|
885
|
+
generate_answer: bool = Field(
|
|
886
|
+
default=True,
|
|
887
|
+
description="Whether to generate an answer using the generative model. If set to false, the response will only contain the retrieval results.",
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
search_configuration: str | None = Field(
|
|
891
|
+
default=None,
|
|
892
|
+
description="Load ask parameters from this configuration. Parameters in the request override parameters from the configuration.",
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
reasoning: Reasoning | bool = Field(
|
|
896
|
+
default=False,
|
|
897
|
+
title="Reasoning options",
|
|
898
|
+
description=(
|
|
899
|
+
"Reasoning options for the generative model. "
|
|
900
|
+
"Set to True to enable default reasoning, False to disable, or provide a Reasoning object for custom options."
|
|
901
|
+
),
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
@field_validator("rag_strategies", mode="before")
|
|
905
|
+
@classmethod
|
|
906
|
+
def validate_rag_strategies(
|
|
907
|
+
cls, rag_strategies: list[RagStrategies]
|
|
908
|
+
) -> list[RagStrategies]:
|
|
909
|
+
strategy_names: set[str] = set()
|
|
910
|
+
for strategy in rag_strategies or []:
|
|
911
|
+
if isinstance(strategy, dict):
|
|
912
|
+
obj = strategy
|
|
913
|
+
elif isinstance(strategy, BaseModel):
|
|
914
|
+
obj = strategy.model_dump()
|
|
915
|
+
else:
|
|
916
|
+
raise ValueError(
|
|
917
|
+
"RAG strategies must be defined using a valid RagStrategy object or a dictionary"
|
|
918
|
+
)
|
|
919
|
+
strategy_name = obj.get("name")
|
|
920
|
+
if strategy_name is None:
|
|
921
|
+
raise ValueError(f"Invalid strategy '{strategy}'")
|
|
922
|
+
strategy_names.add(strategy_name)
|
|
923
|
+
|
|
924
|
+
if len(strategy_names) != len(rag_strategies):
|
|
925
|
+
raise ValueError("There must be at most one strategy of each type")
|
|
926
|
+
|
|
927
|
+
for not_allowed_combination in (
|
|
928
|
+
{RagStrategyName.FULL_RESOURCE, RagStrategyName.HIERARCHY},
|
|
929
|
+
{RagStrategyName.FULL_RESOURCE, RagStrategyName.NEIGHBOURING_PARAGRAPHS},
|
|
930
|
+
{RagStrategyName.FULL_RESOURCE, RagStrategyName.FIELD_EXTENSION},
|
|
931
|
+
):
|
|
932
|
+
if not_allowed_combination.issubset(strategy_names):
|
|
933
|
+
raise ValueError(
|
|
934
|
+
f"The following strategies cannot be combined in the same request: {', '.join(sorted(not_allowed_combination))}"
|
|
935
|
+
)
|
|
936
|
+
return rag_strategies
|
|
937
|
+
|
|
938
|
+
@model_validator(mode="before")
|
|
939
|
+
@classmethod
|
|
940
|
+
def fix_legacy_rank_fusion(cls, values):
|
|
941
|
+
"""Dirty fix to allow passing "legacy" as rank fusion algorithm but
|
|
942
|
+
convert it to RRF"""
|
|
943
|
+
if isinstance(values, dict):
|
|
944
|
+
rank_fusion = values.get("rank_fusion")
|
|
945
|
+
if isinstance(rank_fusion, str) and rank_fusion == "legacy":
|
|
946
|
+
values["rank_fusion"] = RankFusionName.RECIPROCAL_RANK_FUSION
|
|
947
|
+
return values
|
|
948
|
+
|
|
949
|
+
@model_validator(mode="after")
|
|
950
|
+
def rename_context_to_chat_history(self) -> Self:
|
|
951
|
+
"""Bw/c rename from `context` to `chat_history`"""
|
|
952
|
+
if self.context is not None and self.chat_history is not None:
|
|
953
|
+
raise ValueError(
|
|
954
|
+
"`context` and `chat_history` are the same, please, use the latter"
|
|
955
|
+
)
|
|
956
|
+
elif self.context is not None:
|
|
957
|
+
self.chat_history = self.context
|
|
958
|
+
self.context = None
|
|
959
|
+
return self
|
|
960
|
+
|
|
961
|
+
@field_validator("resource_filters", mode="after")
|
|
962
|
+
def validate_resource_filters(cls, values: list[str]) -> list[str]:
|
|
963
|
+
if values is not None:
|
|
964
|
+
for v in values:
|
|
965
|
+
_validate_resource_filter(v)
|
|
966
|
+
return values
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
class TextBlockAugmentationType(str, Enum):
|
|
970
|
+
NEIGHBOURING_PARAGRAPHS = "neighbouring_paragraphs"
|
|
971
|
+
CONVERSATION = "conversation"
|
|
972
|
+
HIERARCHY = "hierarchy"
|
|
973
|
+
FULL_RESOURCE = "full_resource"
|
|
974
|
+
FIELD_EXTENSION = "field_extension"
|
|
975
|
+
METADATA_EXTENSION = "metadata_extension"
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
class AugmentedTextBlock(BaseModel):
|
|
979
|
+
id: str = Field(
|
|
980
|
+
description="The id of the augmented text bloc. It can be a paragraph id or a field id."
|
|
981
|
+
)
|
|
982
|
+
text: str = Field(
|
|
983
|
+
description="The text of the augmented text block. It may include additional metadata to enrich the context"
|
|
984
|
+
)
|
|
985
|
+
position: TextPosition | None = Field(
|
|
986
|
+
default=None,
|
|
987
|
+
description="Metadata about the position of the text block in the original document.",
|
|
988
|
+
)
|
|
989
|
+
parent: str | None = Field(
|
|
990
|
+
default=None, description="The parent text block that was augmented for."
|
|
991
|
+
)
|
|
992
|
+
augmentation_type: TextBlockAugmentationType = Field(
|
|
993
|
+
description="Type of augmentation."
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
class AugmentedContext(BaseModel):
|
|
998
|
+
paragraphs: dict[str, AugmentedTextBlock] = Field(
|
|
999
|
+
default={},
|
|
1000
|
+
description="Paragraphs added to the context as a result of using the `rag_strategies` parameter, typically the neighbouring_paragraphs or the conversation strategies",
|
|
1001
|
+
)
|
|
1002
|
+
fields: dict[str, AugmentedTextBlock] = Field(
|
|
1003
|
+
default={},
|
|
1004
|
+
description="Field extracted texts added to the context as a result of using the `rag_strategies` parameter, typically the hierarcy or full_resource strategies.",
|
|
1005
|
+
)
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
class AskTokens(BaseModel):
|
|
1009
|
+
input: int = Field(
|
|
1010
|
+
title="Input tokens",
|
|
1011
|
+
description="Number of LLM tokens used for the context in the query",
|
|
1012
|
+
)
|
|
1013
|
+
output: int = Field(
|
|
1014
|
+
title="Output tokens",
|
|
1015
|
+
description="Number of LLM tokens used for the answer",
|
|
1016
|
+
)
|
|
1017
|
+
input_nuclia: float | None = Field(
|
|
1018
|
+
title="Input Nuclia tokens",
|
|
1019
|
+
description="Number of Nuclia LLM tokens used for the context in the query",
|
|
1020
|
+
default=None,
|
|
1021
|
+
)
|
|
1022
|
+
output_nuclia: float | None = Field(
|
|
1023
|
+
title="Output Nuclia tokens",
|
|
1024
|
+
description="Number of Nuclia LLM tokens used for the answer",
|
|
1025
|
+
default=None,
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
class AskTimings(BaseModel):
|
|
1030
|
+
generative_first_chunk: float | None = Field(
|
|
1031
|
+
default=None,
|
|
1032
|
+
title="Generative first chunk",
|
|
1033
|
+
description="Time the LLM took to generate the first chunk of the answer",
|
|
1034
|
+
)
|
|
1035
|
+
generative_total: float | None = Field(
|
|
1036
|
+
default=None,
|
|
1037
|
+
title="Generative total",
|
|
1038
|
+
description="Total time the LLM took to generate the answer",
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
class SyncAskMetadata(BaseModel):
|
|
1043
|
+
tokens: AskTokens | None = Field(
|
|
1044
|
+
default=None,
|
|
1045
|
+
title="Tokens",
|
|
1046
|
+
description="Number of tokens used in the LLM context and answer",
|
|
1047
|
+
)
|
|
1048
|
+
timings: AskTimings | None = Field(
|
|
1049
|
+
default=None,
|
|
1050
|
+
title="Timings",
|
|
1051
|
+
description="Timings of the generative model",
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
class AskRetrievalMatch(BaseModel):
|
|
1056
|
+
id: str = Field(
|
|
1057
|
+
title="Id",
|
|
1058
|
+
description="Id of the matching text block",
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
class SyncAskResponse(BaseModel):
|
|
1063
|
+
answer: str = Field(
|
|
1064
|
+
title="Answer",
|
|
1065
|
+
description="The generative answer to the query",
|
|
1066
|
+
)
|
|
1067
|
+
reasoning: str | None = Field(
|
|
1068
|
+
default=None,
|
|
1069
|
+
title="Reasoning steps",
|
|
1070
|
+
description="The reasoning steps followed by the LLM to generate the answer. This is returned only if the reasoning feature is enabled in the request.",
|
|
1071
|
+
)
|
|
1072
|
+
answer_json: dict[str, Any] | None = Field(
|
|
1073
|
+
default=None,
|
|
1074
|
+
title="Answer JSON",
|
|
1075
|
+
description="The generative JSON answer to the query. This is returned only if the answer_json_schema parameter is provided in the request.",
|
|
1076
|
+
)
|
|
1077
|
+
status: str = Field(
|
|
1078
|
+
title="Status",
|
|
1079
|
+
description="The status of the query execution. It can be 'success', 'error', 'no_context' or 'no_retrieval_data'",
|
|
1080
|
+
)
|
|
1081
|
+
retrieval_results: KnowledgeboxFindResults = Field(
|
|
1082
|
+
title="Retrieval results",
|
|
1083
|
+
description="The retrieval results of the query",
|
|
1084
|
+
)
|
|
1085
|
+
retrieval_best_matches: list[AskRetrievalMatch] = Field(
|
|
1086
|
+
default=[],
|
|
1087
|
+
title="Retrieval best matches",
|
|
1088
|
+
description="Sorted list of best matching text blocks in the retrieval step. This includes the main query and prequeries results, if any.",
|
|
1089
|
+
)
|
|
1090
|
+
prequeries: dict[str, KnowledgeboxFindResults] | None = Field(
|
|
1091
|
+
default=None,
|
|
1092
|
+
title="Prequeries",
|
|
1093
|
+
description="The retrieval results of the prequeries",
|
|
1094
|
+
)
|
|
1095
|
+
learning_id: str = Field(
|
|
1096
|
+
default="",
|
|
1097
|
+
title="Learning id",
|
|
1098
|
+
description="The id of the learning request. This id can be used to provide feedback on the learning process.",
|
|
1099
|
+
)
|
|
1100
|
+
relations: Relations | None = Field(
|
|
1101
|
+
default=None,
|
|
1102
|
+
title="Relations",
|
|
1103
|
+
description="The detected relations of the answer",
|
|
1104
|
+
)
|
|
1105
|
+
citations: dict[str, Any] = Field(
|
|
1106
|
+
default_factory=dict,
|
|
1107
|
+
title="Citations",
|
|
1108
|
+
description="The citations of the answer. List of references to the resources used to generate the answer.",
|
|
1109
|
+
)
|
|
1110
|
+
citation_footnote_to_context: dict[str, str] = Field(
|
|
1111
|
+
default_factory=dict,
|
|
1112
|
+
title="Citation footnote to context",
|
|
1113
|
+
description="""Maps ids in the footnote citations to query_context keys (normally paragraph ids)""",
|
|
1114
|
+
)
|
|
1115
|
+
augmented_context: AugmentedContext | None = Field(
|
|
1116
|
+
default=None,
|
|
1117
|
+
description=(
|
|
1118
|
+
"Augmented text blocks that were sent to the LLM as part of the RAG strategies "
|
|
1119
|
+
"applied on the retrieval results in the request."
|
|
1120
|
+
),
|
|
1121
|
+
)
|
|
1122
|
+
prompt_context: list[str] | None = Field(
|
|
1123
|
+
default=None,
|
|
1124
|
+
title="Prompt context",
|
|
1125
|
+
description="The prompt context used to generate the answer. Returned only if the debug flag is set to true",
|
|
1126
|
+
)
|
|
1127
|
+
predict_request: dict[str, Any] | None = Field(
|
|
1128
|
+
default=None,
|
|
1129
|
+
title="Predict request",
|
|
1130
|
+
description="The internal predict request used to generate the answer. Returned only if the debug flag is set to true",
|
|
1131
|
+
)
|
|
1132
|
+
metadata: SyncAskMetadata | None = Field(
|
|
1133
|
+
default=None,
|
|
1134
|
+
title="Metadata",
|
|
1135
|
+
description="Metadata of the query execution. This includes the number of tokens used in the LLM context and answer, and the timings of the generative model.",
|
|
1136
|
+
)
|
|
1137
|
+
consumption: Consumption | None = Field(
|
|
1138
|
+
default=None,
|
|
1139
|
+
title="Consumption",
|
|
1140
|
+
description=(
|
|
1141
|
+
"The consumption of the query execution. Return only if"
|
|
1142
|
+
" 'X-show-consumption' header is set to true in the request."
|
|
1143
|
+
),
|
|
1144
|
+
)
|
|
1145
|
+
error_details: str | None = Field(
|
|
1146
|
+
default=None,
|
|
1147
|
+
title="Error details",
|
|
1148
|
+
description="Error details message in case there was an error",
|
|
1149
|
+
)
|
|
1150
|
+
debug: dict[str, Any] | None = Field(
|
|
1151
|
+
default=None,
|
|
1152
|
+
title="Debug information",
|
|
1153
|
+
description=(
|
|
1154
|
+
"Debug information about the ask operation. "
|
|
1155
|
+
"The metadata included in this field is subject to change and should not be used in production. "
|
|
1156
|
+
"Note that it is only available if the `debug` parameter is set to true in the request."
|
|
1157
|
+
),
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
class RetrievalAskResponseItem(BaseModel):
|
|
1162
|
+
type: Literal["retrieval"] = "retrieval"
|
|
1163
|
+
results: KnowledgeboxFindResults
|
|
1164
|
+
best_matches: list[AskRetrievalMatch] = Field(
|
|
1165
|
+
default=[],
|
|
1166
|
+
title="Best matches",
|
|
1167
|
+
description="Sorted list of best matching text blocks in the retrieval step. This includes the main query and prequeries results, if any.",
|
|
1168
|
+
)
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
class PrequeriesAskResponseItem(BaseModel):
|
|
1172
|
+
type: Literal["prequeries"] = "prequeries"
|
|
1173
|
+
results: dict[str, KnowledgeboxFindResults] = {}
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
class AnswerAskResponseItem(BaseModel):
|
|
1177
|
+
type: Literal["answer"] = "answer"
|
|
1178
|
+
text: str
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
class ReasoningAskResponseItem(BaseModel):
|
|
1182
|
+
type: Literal["reasoning"] = "reasoning"
|
|
1183
|
+
text: str
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
class JSONAskResponseItem(BaseModel):
|
|
1187
|
+
type: Literal["answer_json"] = "answer_json"
|
|
1188
|
+
object: dict[str, Any]
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
class MetadataAskResponseItem(BaseModel):
|
|
1192
|
+
type: Literal["metadata"] = "metadata"
|
|
1193
|
+
tokens: AskTokens
|
|
1194
|
+
timings: AskTimings
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
class ConsumptionResponseItem(BaseModel):
|
|
1198
|
+
type: Literal["consumption"] = "consumption"
|
|
1199
|
+
normalized_tokens: TokensDetail
|
|
1200
|
+
customer_key_tokens: TokensDetail
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
class AugmentedContextResponseItem(BaseModel):
|
|
1204
|
+
type: Literal["augmented_context"] = "augmented_context"
|
|
1205
|
+
augmented: AugmentedContext = Field(
|
|
1206
|
+
description=(
|
|
1207
|
+
"Augmented text blocks that were sent to the LLM as part of the RAG strategies "
|
|
1208
|
+
"applied on the retrieval results in the request."
|
|
1209
|
+
)
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
class CitationsAskResponseItem(BaseModel):
|
|
1214
|
+
type: Literal["citations"] = "citations"
|
|
1215
|
+
citations: dict[str, Any]
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
class FootnoteCitationsAskResponseItem(BaseModel):
|
|
1219
|
+
type: Literal["footnote_citations"] = "footnote_citations"
|
|
1220
|
+
footnote_to_context: dict[str, str] = Field(
|
|
1221
|
+
description="""Maps ids in the footnote citations to query_context keys (normally paragraph ids)
|
|
1222
|
+
e.g.,
|
|
1223
|
+
{ "block-AA": "f44f4e8acbfb1d48de3fd3c2fb04a885/f/f44f4e8acbfb1d48de3fd3c2fb04a885/73758-73972", ... }
|
|
1224
|
+
If the query_context is a list, it will map to 1-based indices as strings
|
|
1225
|
+
e.g., { "block-AA": "1", "block-AB": "2", ... }
|
|
1226
|
+
"""
|
|
1227
|
+
)
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
class StatusAskResponseItem(BaseModel):
|
|
1231
|
+
type: Literal["status"] = "status"
|
|
1232
|
+
code: str
|
|
1233
|
+
status: str
|
|
1234
|
+
details: str | None = None
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
class ErrorAskResponseItem(BaseModel):
|
|
1238
|
+
type: Literal["error"] = "error"
|
|
1239
|
+
error: str
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
class RelationsAskResponseItem(BaseModel):
|
|
1243
|
+
type: Literal["relations"] = "relations"
|
|
1244
|
+
relations: Relations
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
class DebugAskResponseItem(BaseModel):
|
|
1248
|
+
type: Literal["debug"] = "debug"
|
|
1249
|
+
metadata: dict[str, Any]
|
|
1250
|
+
metrics: dict[str, Any]
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
AskResponseItemType = (
|
|
1254
|
+
AnswerAskResponseItem
|
|
1255
|
+
| ReasoningAskResponseItem
|
|
1256
|
+
| JSONAskResponseItem
|
|
1257
|
+
| MetadataAskResponseItem
|
|
1258
|
+
| AugmentedContextResponseItem
|
|
1259
|
+
| CitationsAskResponseItem
|
|
1260
|
+
| FootnoteCitationsAskResponseItem
|
|
1261
|
+
| StatusAskResponseItem
|
|
1262
|
+
| ErrorAskResponseItem
|
|
1263
|
+
| RetrievalAskResponseItem
|
|
1264
|
+
| RelationsAskResponseItem
|
|
1265
|
+
| DebugAskResponseItem
|
|
1266
|
+
| PrequeriesAskResponseItem
|
|
1267
|
+
| ConsumptionResponseItem
|
|
1268
|
+
)
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
class AskResponseItem(BaseModel):
|
|
1272
|
+
item: AskResponseItemType = Field(..., discriminator="type")
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
def parse_custom_prompt(item: AskRequest) -> CustomPrompt:
|
|
1276
|
+
prompt = CustomPrompt()
|
|
1277
|
+
if item.prompt is not None:
|
|
1278
|
+
if isinstance(item.prompt, str):
|
|
1279
|
+
# If the prompt is a string, it is interpreted as the user prompt
|
|
1280
|
+
prompt.user = item.prompt
|
|
1281
|
+
else:
|
|
1282
|
+
prompt.user = item.prompt.user
|
|
1283
|
+
prompt.system = item.prompt.system
|
|
1284
|
+
prompt.rephrase = item.prompt.rephrase
|
|
1285
|
+
return prompt
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
def parse_rephrase_prompt(item: AskRequest) -> str | None:
|
|
1289
|
+
prompt = parse_custom_prompt(item)
|
|
1290
|
+
return prompt.rephrase
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
def parse_max_tokens(max_tokens: int | MaxTokens | None) -> MaxTokens | None:
|
|
1294
|
+
if isinstance(max_tokens, int):
|
|
1295
|
+
# If the max_tokens is an integer, it is interpreted as the max_tokens value for the generated answer.
|
|
1296
|
+
# The max tokens for the context is set to None to use the default value for the model (comes in the
|
|
1297
|
+
# NUA's query endpoint response).
|
|
1298
|
+
return MaxTokens(answer=max_tokens, context=None)
|
|
1299
|
+
return max_tokens
|