hyperforge-rephrase 1.0.0.post20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperforge_rephrase
3
+ Version: 1.0.0.post20
4
+ Summary: Rephrase Hyperforge agent
5
+ Author-email: Nuclia <nucliadb@nuclia.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://progress.com
8
+ Project-URL: Repository, https://github.com/nuclia/forge
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: <4,>=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: hyperforge
18
+
19
+ # Rephrase Hyperforge agents
@@ -0,0 +1 @@
1
+ # Rephrase Hyperforge agents
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hyperforge_rephrase"
7
+ version = "1.0.0.post20"
8
+ license = "Apache-2.0"
9
+ description = "Rephrase Hyperforge agent"
10
+ authors = [{ name = "Nuclia", email = "nucliadb@nuclia.com" }]
11
+ readme = "README.md"
12
+ classifiers = [
13
+ "Programming Language :: Python",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Topic :: Software Development :: Libraries :: Python Modules",
19
+ ]
20
+ requires-python = ">=3.10, <4"
21
+ dependencies = ["hyperforge"]
22
+
23
+ [dependency-groups]
24
+ # 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_nucliadb",
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,5 @@
1
+ from .agent import RephraseAgent
2
+
3
+ __all__ = [
4
+ "RephraseAgent",
5
+ ]
@@ -0,0 +1,398 @@
1
+ from time import time
2
+ from typing import Any, Dict, Optional, cast
3
+
4
+ from hyperforge.agent import Agent
5
+ from hyperforge.configure import agent
6
+ from hyperforge.manager import Manager
7
+ from hyperforge.memory.memory import QuestionMemory
8
+ from hyperforge.models import TrackingInfo
9
+ from hyperforge.trace import trace_agent
10
+ from hyperforge_nucliadb.driver import NucliaDBDriver
11
+
12
+ from hyperforge import PROMPT_ENVIRONMENT
13
+ from hyperforge_rephrase.config import RephraseAgentConfig
14
+
15
+ REPHRASE_PROMPT = """
16
+ You are an expert at rephrasing complex questions for an agentic RAG system.
17
+
18
+ Your task is to review the main question and any provided context, then rephrase the question to maximize clarity and focus. Follow these steps:
19
+
20
+ 1. Carefully analyze the main question and all context provided, including sources, previous questions and answers, and any other relevant information.
21
+ 2. I present, assess whether previous questions and answers (history) are necessary for rephrasing. Only use history if it is relevant and improves the clarity or specificity of the main question; otherwise, ignore it.
22
+ 3. If the question can be made clearer or more specific, rephrase it accordingly. If it is already clear and focused, return it unchanged.
23
+ 4. Only use information present in the provided context. Do not introduce external knowledge or assumptions.
24
+ 5. Return a JSON object with the following fields:
25
+ - "rephrased_question": The rephrased version of the main question, keep the same question if no rephrasing is needed or possible.
26
+ - "rules": Any rules or guidelines that should be followed when generating the answer.
27
+ - "reason": Explain why the rephrasing was necessary or beneficial.
28
+ {% if rules -%}
29
+ # IMPORTANT CONSIDERATIONS FOR REPHRASING
30
+ {% for rule in rules -%}
31
+ - {{ rule }}
32
+ {% endfor -%}
33
+ {% endif -%}
34
+ Return only the JSON object as your response.
35
+ {% if context %}
36
+ Additional context to assist with rephrasing:
37
+ # CONTEXT
38
+ {% for info in context %}
39
+ - {{ info }}
40
+ {% endfor %}
41
+ {% endif %}
42
+
43
+ {% if arguments %}
44
+ Arguments that might be useful for rephrasing or splitting the question:
45
+ # ARGUMENTS
46
+ {% for key, value in arguments.items() %}
47
+ - {{ key }}: {{ value }}
48
+ {% endfor %}
49
+ {% endif %}
50
+
51
+ # MAIN QUESTION:
52
+ {{question}}
53
+ """
54
+
55
+ MULTI_REPHRASE_PROMPT = """
56
+ You are an expert at clarifying and rephrasing questions for an agentic RAG system. Your goal is to identify wheteher a question needs rephrasing or breaking down into sub-questions to ensure it can be answered effectively.
57
+
58
+ Given the main question and any provided context, your task is to identify whether splitting into sub-questions would improve the answer quality.
59
+
60
+ Guidelines for splitting:
61
+ - Split when the question asks about multiple distinct topics or entities that would benefit from separate retrieval
62
+ - Split when there are multiple independent questions that could be answered more thoroughly separately
63
+ - A good sub-question should be atomic. A good rule of thumb is that if the question contains a logical operator, it probably should be split further.
64
+
65
+ Keep as a single question when:
66
+ - It asks about a single topic, even if complex
67
+ - The question is already clear and focused
68
+
69
+ Steps:
70
+ 1. Analyze the main question and all context provided, including sources, previous questions and answers, and any other relevant information.
71
+ 2. Identify if answering just that could provide a valid answer.
72
+ 3. If it can't and the question can be broken down into smaller, more specific sub-questions, do so. Each sub-question should be clear, focused, and do not depend on the other sub-questions to be answered.
73
+ 4. If the main question is already clear and does not require further breakdown, return it as is.
74
+ 5. Provide a reason for each sub-question explaining why it is necessary to answer the main question.
75
+ 6. If there are any rules or guidelines that should be followed when generating the answer, include them in the response.
76
+ 7. Keep key information such as names, titles, dates, and specific terms unchanged to preserve the original intent.
77
+ 8. Return a JSON object with the following fields:
78
+ - "questions": A list of the minimum set of sub-questions needed to answer the main question. If no sub-questions are needed, return just the main question.
79
+ - "rules": A list of rules or guidelines to follow when generating the answer.
80
+ - "reason": A reason for each sub-question explaining why it is necessary to answer the main question.
81
+
82
+ {% if rules -%}
83
+ # IMPORTANT CONSIDERATIONS FOR REPHRASING
84
+ {% for rule in rules -%}
85
+ - {{ rule }}
86
+ {% endfor -%}
87
+ {% endif -%}
88
+
89
+ {% if context %}
90
+ Additional context to assist with rephrasing:
91
+ # CONTEXT
92
+ {% for info in context %}
93
+ - {{ info }}
94
+ {% endfor %}
95
+ {% endif %}
96
+
97
+ {% if arguments %}
98
+ Arguments that might be useful for rephrasing or splitting the question:
99
+ # ARGUMENTS
100
+ {% for key, value in arguments.items() %}
101
+ - {{ key }}: {{ value }}
102
+ {% endfor %}
103
+ {% endif %}
104
+
105
+ # MAIN QUESTION:
106
+ {{question}}
107
+
108
+
109
+ """
110
+ MULTI_REPHRASE_JSON_SCHEMA = {
111
+ "title": "questions",
112
+ "description": "",
113
+ "type": "object",
114
+ "properties": {
115
+ "questions": {
116
+ "type": "array",
117
+ "items": {"type": "string"},
118
+ "description": "List of strictly minimum set of sub-questions needed to answer the main question. If not needed, return just the main question - rephrased if necessary.",
119
+ },
120
+ "rules": {
121
+ "type": "array",
122
+ "items": {"type": "string"},
123
+ "description": "Rules or guidelines to follow when generating the answer.",
124
+ },
125
+ "reason": {
126
+ "type": "string",
127
+ "description": "Reason for each sub-question explaining why it is necessary.",
128
+ },
129
+ },
130
+ "required": ["questions"],
131
+ }
132
+ REPHRASE_JSON_SCHEMA = {
133
+ "title": "rephrase",
134
+ "description": "",
135
+ "type": "object",
136
+ "properties": {
137
+ "rephrased_question": {
138
+ "type": "string",
139
+ "description": "Rephrased version of the main question.",
140
+ },
141
+ "rules": {
142
+ "type": "array",
143
+ "items": {"type": "string"},
144
+ "description": "Rules or guidelines to follow when generating the answer.",
145
+ },
146
+ "reason": {
147
+ "type": "string",
148
+ "description": "Reason for rephrasing explaining why it is necessary.",
149
+ },
150
+ },
151
+ "required": ["rephrased_question"],
152
+ }
153
+ REPHRASE_PROMPT_TEMPLATE = PROMPT_ENVIRONMENT.from_string(REPHRASE_PROMPT)
154
+ MULTI_REPHRASE_PROMPT_TEMPLATE = PROMPT_ENVIRONMENT.from_string(MULTI_REPHRASE_PROMPT)
155
+
156
+ DEFINE_ACTIONS_PROMPT = """
157
+ Define the actions needed to transform the retrieved information to user needs
158
+
159
+ {{context}}
160
+ """
161
+ DEFINE_ACTIONS_PROMPT_TEMPLATE = PROMPT_ENVIRONMENT.from_string(DEFINE_ACTIONS_PROMPT)
162
+
163
+
164
+ @agent(
165
+ id="rephrase",
166
+ agent_type="preprocess",
167
+ title="Rephrase Query",
168
+ description="Agent that rephrases a given question.",
169
+ config_schema=RephraseAgentConfig,
170
+ )
171
+ class RephraseAgent(Agent[RephraseAgentConfig]):
172
+ async def multi_rephrase(
173
+ self,
174
+ question: str,
175
+ context: list[str],
176
+ manager: Manager,
177
+ extra_rule: Optional[str] = None,
178
+ arguments: Optional[Dict[str, Any]] = None,
179
+ tracking: TrackingInfo | None = None,
180
+ ) -> tuple[list[str], list[str], str, str, float, float]:
181
+ if extra_rule is not None and self.config.rules is not None:
182
+ rules = self.config.rules.copy() + [extra_rule]
183
+ elif extra_rule is not None:
184
+ rules = [extra_rule]
185
+ else:
186
+ rules = self.config.rules if self.config.rules is not None else []
187
+ prompt = MULTI_REPHRASE_PROMPT_TEMPLATE.render(
188
+ context=context,
189
+ question=question,
190
+ rules=rules,
191
+ arguments=arguments,
192
+ )
193
+ # Ask the LLM to define which information is needed to answer.
194
+ information, input_tokens, output_tokens = await manager.execute_json(
195
+ model=self.config.model,
196
+ user_id="rephrase",
197
+ prompt=prompt,
198
+ schema=MULTI_REPHRASE_JSON_SCHEMA,
199
+ tracking=tracking,
200
+ )
201
+
202
+ # retrieve all paragraphs and ask to rephrase on the NUA API
203
+ new_questions = information.get("questions", [])
204
+ list_rules = information.get("rules", [])
205
+ reason = information.get("reason", [])
206
+ step_value = (
207
+ f"{len(new_questions)} questions.\nQuestions: {', '.join(new_questions)}"
208
+ )
209
+
210
+ return (
211
+ new_questions,
212
+ list_rules,
213
+ reason,
214
+ step_value,
215
+ input_tokens,
216
+ output_tokens,
217
+ )
218
+
219
+ async def rephrase(
220
+ self,
221
+ question: str,
222
+ context: list[str],
223
+ manager: Manager,
224
+ extra_rule: Optional[str] = None,
225
+ arguments: Optional[Dict[str, Any]] = None,
226
+ tracking: TrackingInfo | None = None,
227
+ ) -> tuple[list[str], list[str], str, str, float, float]:
228
+ if extra_rule is not None and self.config.rules is not None:
229
+ rules = self.config.rules.copy() + [extra_rule]
230
+ elif extra_rule is not None:
231
+ rules = [extra_rule]
232
+ else:
233
+ rules = self.config.rules if self.config.rules is not None else []
234
+
235
+ prompt = REPHRASE_PROMPT_TEMPLATE.render(
236
+ context=context,
237
+ question=question,
238
+ rules=rules,
239
+ arguments=arguments,
240
+ )
241
+ # Ask the LLM to define which information is needed to answer.
242
+ information, input_tokens, output_tokens = await manager.execute_json(
243
+ model=self.config.model,
244
+ user_id="rephrase",
245
+ prompt=prompt,
246
+ schema=REPHRASE_JSON_SCHEMA,
247
+ tracking=tracking,
248
+ )
249
+ # retrieve all paragraphs and ask to rephrase on the NUA API
250
+ rephrased_question = [information.get("rephrased_question", question)]
251
+ list_rules = information.get("rules", [])
252
+ reason = information.get("reason", [])
253
+ step_value = (
254
+ (
255
+ f"Rephrased question: {rephrased_question[0]}\nRules: {', '.join(list_rules)}\n"
256
+ )
257
+ if rephrased_question[0] != question
258
+ else "No rephrasing considered necessary"
259
+ )
260
+
261
+ return (
262
+ rephrased_question,
263
+ list_rules,
264
+ reason,
265
+ step_value,
266
+ input_tokens,
267
+ output_tokens,
268
+ )
269
+
270
+ async def inner_rephrase(
271
+ self,
272
+ question: str,
273
+ memory: QuestionMemory,
274
+ manager: Manager,
275
+ extra_rule: Optional[str] = None,
276
+ ) -> list[str]:
277
+ context = []
278
+ if self.config.kb is not None:
279
+ # Search by BM25 of the words on the actual query (Synonyms)
280
+ nucliadb_driver: Optional[NucliaDBDriver] = cast(
281
+ NucliaDBDriver, manager.drivers.get(self.config.kb)
282
+ )
283
+ if nucliadb_driver is None:
284
+ raise Exception(f"No NucliaDB Driver {self.config.kb} found")
285
+
286
+ if self.config.synonyms:
287
+ question = await nucliadb_driver.synonyms(question)
288
+
289
+ if self.config.provided_synonyms:
290
+ for key in self.config.provided_synonyms:
291
+ if key.lower() in question.lower():
292
+ question += " ".join(self.config.provided_synonyms[key])
293
+
294
+ if self.config.extend:
295
+ extra = []
296
+ # Only BM25
297
+ find_result = await nucliadb_driver.find(
298
+ question, filters=self.config.labels, rids=self.config.rids
299
+ )
300
+ for resource in find_result.resources.values():
301
+ for field in resource.fields.values():
302
+ for paragraph in field.paragraphs.values():
303
+ extra.append(paragraph.text)
304
+ if extra:
305
+ extra_paragraph = "\n".join(extra)
306
+ if extra_paragraph:
307
+ context.append(
308
+ f"## Extra information to append to the question:\n{extra_paragraph}"
309
+ )
310
+
311
+ if self.config.session_info:
312
+ # Add session information
313
+ context.append(f"## Session information:\n{memory.context_user_info()}")
314
+
315
+ if self.config.history:
316
+ qa_history, interactions = await memory.context_history()
317
+ await memory.add_step(
318
+ step_module="rephrase",
319
+ step_title=self.step_title("History check"),
320
+ step_value="Included {} interactions of Q&A history".format(
321
+ interactions,
322
+ ),
323
+ step_reason="",
324
+ timeit=0,
325
+ step_agent_path=f"/preprocess/{self.config.id if self.config.id else 'default'}",
326
+ input_nuclia_tokens=0.0,
327
+ output_nuclia_tokens=0.0,
328
+ )
329
+
330
+ context.append(
331
+ f"## Previous questions and answers in this session:\n{qa_history}"
332
+ )
333
+
334
+ t0 = time()
335
+ if self.config.split_question:
336
+ (
337
+ new_questions,
338
+ list_rules,
339
+ reason,
340
+ step_value,
341
+ input_tokens,
342
+ output_tokens,
343
+ ) = await self.multi_rephrase(
344
+ question,
345
+ context,
346
+ manager,
347
+ extra_rule,
348
+ memory.arguments,
349
+ tracking=memory.get_tracking_info(),
350
+ )
351
+ step_title = self.step_title("Sub-questions")
352
+ else:
353
+ (
354
+ new_questions,
355
+ list_rules,
356
+ reason,
357
+ step_value,
358
+ input_tokens,
359
+ output_tokens,
360
+ ) = await self.rephrase(
361
+ question,
362
+ context,
363
+ manager,
364
+ extra_rule,
365
+ memory.arguments,
366
+ tracking=memory.get_tracking_info(),
367
+ )
368
+ step_title = self.step_title("Rephrase")
369
+
370
+ await memory.add_step(
371
+ step_module="rephrase",
372
+ step_title=step_title,
373
+ step_value=step_value,
374
+ step_reason=reason,
375
+ timeit=time() - t0,
376
+ step_agent_path=f"/preprocess/{self.config.id if self.config.id else 'default'}",
377
+ input_nuclia_tokens=input_tokens,
378
+ output_nuclia_tokens=output_tokens,
379
+ )
380
+ return new_questions
381
+
382
+ @trace_agent
383
+ async def __call__(
384
+ self,
385
+ memory: QuestionMemory,
386
+ manager: Manager,
387
+ ):
388
+ question = memory.original_question
389
+ if question is None:
390
+ raise Exception("No question")
391
+ new_questions = await self.inner_rephrase(
392
+ question=question,
393
+ memory=memory,
394
+ manager=manager,
395
+ )
396
+ memory.add_context_questions(new_questions)
397
+ # Disabling rules since they do not seem to be helping atm
398
+ # memory.add_generation_rules(list_rules)
@@ -0,0 +1,29 @@
1
+ from typing import Dict, List, Literal, Optional
2
+
3
+ from hyperforge.agent import AgentConfig
4
+ from hyperforge.utils import WidgetType
5
+ from pydantic import Field
6
+ from pydantic.config import ConfigDict
7
+
8
+
9
+ class RephraseAgentConfig(AgentConfig):
10
+ model_config = ConfigDict(title="Rephrase")
11
+ kb: Optional[str] = None
12
+ rids: List[str] = Field(default_factory=list)
13
+ labels: List[str] = Field(default_factory=list)
14
+ synonyms: bool = True
15
+ provided_synonyms: Dict[str, List[str]] = Field(
16
+ default_factory=dict,
17
+ )
18
+ extend: bool = True
19
+ session_info: bool = False
20
+ history: bool = False
21
+ model: str = Field(
22
+ default="gemini-2.5-flash-lite",
23
+ title="Generative model",
24
+ description="Model used to generate the rephrased question",
25
+ json_schema_extra={"widget": WidgetType.MODEL_SELECT},
26
+ )
27
+ module: Literal["rephrase"] = "rephrase"
28
+ split_question: bool = False
29
+ rules: Optional[List[str]] = None
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperforge_rephrase
3
+ Version: 1.0.0.post20
4
+ Summary: Rephrase Hyperforge agent
5
+ Author-email: Nuclia <nucliadb@nuclia.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://progress.com
8
+ Project-URL: Repository, https://github.com/nuclia/forge
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: <4,>=3.10
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: hyperforge
18
+
19
+ # Rephrase Hyperforge agents
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/hyperforge_rephrase/__init__.py
4
+ src/hyperforge_rephrase/agent.py
5
+ src/hyperforge_rephrase/config.py
6
+ src/hyperforge_rephrase.egg-info/PKG-INFO
7
+ src/hyperforge_rephrase.egg-info/SOURCES.txt
8
+ src/hyperforge_rephrase.egg-info/dependency_links.txt
9
+ src/hyperforge_rephrase.egg-info/requires.txt
10
+ src/hyperforge_rephrase.egg-info/top_level.txt
11
+ tests/test_rephrase.py
@@ -0,0 +1,139 @@
1
+ import os
2
+ from copy import deepcopy
3
+
4
+ import pytest
5
+ from hyperforge.engine import main as arag_main
6
+ from hyperforge.minimal_fixtures import cassette_nua_key
7
+
8
+ NUA_KEY = os.environ.get(
9
+ "NUA_KEY",
10
+ ) or cassette_nua_key("https://europe-1.nuclia.cloud/")
11
+
12
+
13
+ DE48CFAA_3209_4041_BB64_8604AFF061FB = os.environ.get(
14
+ "KB_DE48CFAA_3209_4041_BB64_8604AFF061FB"
15
+ ) or cassette_nua_key("https://europe-1.nuclia.cloud/")
16
+
17
+ DF8B4C24_2807_4888_AD6C_AE97357A638B = os.environ.get(
18
+ "KB_DF8B4C24_2807_4888_AD6C_AE97357A638B"
19
+ ) or cassette_nua_key("https://europe-1.nuclia.cloud/")
20
+ pytestmark = [
21
+ pytest.mark.vcr(
22
+ ignore_localhost=True, ignore_hosts=["test", "europe-1.nuclia.cloud"]
23
+ ),
24
+ pytest.mark.asyncio,
25
+ ]
26
+
27
+
28
+ CONFIG = {
29
+ "drivers": [
30
+ {
31
+ "name": "nuclia-conversation",
32
+ "provider": "nucliadb",
33
+ "identifier": "nucliadb-1",
34
+ "config": {
35
+ "url": "https://europe-1.stashify.cloud/api",
36
+ "manager": "https://europe-1.stashify.cloud/api",
37
+ "kbid": "de48cfaa-3209-4041-bb64-8604aff061fb",
38
+ "key": DE48CFAA_3209_4041_BB64_8604AFF061FB,
39
+ "filters": [],
40
+ "description": "Make Discourse Conversation",
41
+ },
42
+ },
43
+ {
44
+ "name": "nuclia-docs",
45
+ "provider": "nucliadb",
46
+ "identifier": "nucliadb-2",
47
+ "config": {
48
+ "identifier": "nucliadb-2",
49
+ "url": "https://europe-1.nuclia.cloud/api",
50
+ "manager": "https://europe-1.nuclia.cloud/api",
51
+ "kbid": "df8b4c24-2807-4888-ad6c-ae97357a638b",
52
+ "key": DF8B4C24_2807_4888_AD6C_AE97357A638B,
53
+ "filters": [],
54
+ "description": "Documentation of the Nuclia API, recipies, reference",
55
+ },
56
+ },
57
+ ],
58
+ "rules": {
59
+ "rules": [
60
+ {"prompt": "Be polite"},
61
+ {
62
+ "prompt": "The documentation of Nuclia is hosted at https://docs.nuclia.dev"
63
+ },
64
+ ]
65
+ },
66
+ "memory": {},
67
+ "workflow": {
68
+ "id": "default",
69
+ "name": "Default workflow",
70
+ "description": "Default workflow for testing",
71
+ "parameters": {},
72
+ },
73
+ "preprocess": [
74
+ {
75
+ "module": "rephrase",
76
+ "kb": "nucliadb-2",
77
+ "rids": [],
78
+ "labels": [],
79
+ "synonyms": False,
80
+ "extend": True,
81
+ "history": False, # Requires NucliaDB to be running to retrieve memory
82
+ "session_info": True,
83
+ }
84
+ ],
85
+ "context": [
86
+ {
87
+ "module": "ask",
88
+ "title": "",
89
+ "sources": ["nucliadb-2"],
90
+ "ai_parameter_search": False,
91
+ },
92
+ ],
93
+ "generation": [
94
+ {"module": "summarize"},
95
+ ],
96
+ "postprocess": [],
97
+ }
98
+
99
+
100
+ async def test_rephrase_agent():
101
+ config = deepcopy(CONFIG)
102
+ config["preprocess"][0]["split_question"] = True
103
+ question_memory = await arag_main(
104
+ agent_id="default",
105
+ internal_nua=False,
106
+ external_nua_api_key=NUA_KEY,
107
+ question="Como usar max_tokens.answer. En español y dame link a la doucmentación",
108
+ config=config,
109
+ loaded_modules=[
110
+ "hyperforge_rephrase",
111
+ "hyperforge_nucliadb",
112
+ "hyperforge_summarize",
113
+ ],
114
+ )
115
+ assert (
116
+ len(question_memory.context_questions) <= 5
117
+ ) # Rephrase should not make more than this number of questions
118
+ assert question_memory.final_answer and "max_tokens" in question_memory.final_answer
119
+
120
+
121
+ async def test_rephrase_agent_only_rephrase():
122
+ config = deepcopy(CONFIG)
123
+ config["preprocess"][0]["split_question"] = False
124
+ question_memory = await arag_main(
125
+ agent_id="default",
126
+ internal_nua=False,
127
+ external_nua_api_key=NUA_KEY,
128
+ question="Como usar max_tokens.answer. En español y dame link a la doucmentación",
129
+ config=config,
130
+ loaded_modules=[
131
+ "hyperforge_rephrase",
132
+ "hyperforge_nucliadb",
133
+ "hyperforge_summarize",
134
+ ],
135
+ )
136
+
137
+ assert (
138
+ len(question_memory.context_questions) == 1
139
+ ) # Rephrase should not make more than this number of questions