hyperforge-summarize 1.0.0.post21__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_summarize/__init__.py +5 -0
- hyperforge_summarize/agent.py +377 -0
- hyperforge_summarize/config.py +52 -0
- hyperforge_summarize/prompts.py +39 -0
- hyperforge_summarize-1.0.0.post21.dist-info/METADATA +19 -0
- hyperforge_summarize-1.0.0.post21.dist-info/RECORD +8 -0
- hyperforge_summarize-1.0.0.post21.dist-info/WHEEL +5 -0
- hyperforge_summarize-1.0.0.post21.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
from typing import List, Optional, overload
|
|
3
|
+
|
|
4
|
+
from hyperforge import PROMPT_ENVIRONMENT
|
|
5
|
+
from hyperforge.agent import Agent
|
|
6
|
+
from hyperforge.configure import agent
|
|
7
|
+
from hyperforge.context.agent import generate_ctx_block_id
|
|
8
|
+
from hyperforge.manager import Manager
|
|
9
|
+
from hyperforge.memory import QuestionMemory
|
|
10
|
+
from hyperforge.models import AnswerCitations, CitationMetadata, Context
|
|
11
|
+
from hyperforge.trace import trace_agent
|
|
12
|
+
from nuclia.lib.nua_responses import ChatModel, Tool, ToolChoiceAuto, UserPrompt
|
|
13
|
+
from nuclia_models.predict.generative_responses import ToolCall
|
|
14
|
+
|
|
15
|
+
from hyperforge_summarize.config import SummarizeAgentConfig
|
|
16
|
+
from hyperforge_summarize.prompts import MARKDOWN_TWO_LEVELS_CITATIONS_PROMPT_ADJUSTMENT
|
|
17
|
+
|
|
18
|
+
DEFAULT_SYSTEM_PROMPT = """You are a helpful AI assistant. Your role is to provide accurate, clear, and well-structured answers based strictly on the information provided to you.
|
|
19
|
+
Key principles:
|
|
20
|
+
- Answer only using the information in the provided context
|
|
21
|
+
- Do not use external knowledge, assumptions, or prior experience
|
|
22
|
+
- Maintain a professional and informative tone
|
|
23
|
+
- Be concise yet thorough
|
|
24
|
+
- If information is insufficient, acknowledge this clearly
|
|
25
|
+
|
|
26
|
+
Always follow any additional instructions provided about format, style, or domain-specific behavior."""
|
|
27
|
+
|
|
28
|
+
SUMMARIZE_PROMPT_CONVERSATIONAL = """
|
|
29
|
+
{% if rules -%}
|
|
30
|
+
# Generation Rules
|
|
31
|
+
{% for rule in rules -%}
|
|
32
|
+
- {{ rule }}
|
|
33
|
+
{% endfor -%}
|
|
34
|
+
{% endif -%}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Question
|
|
38
|
+
{{ question }}
|
|
39
|
+
|
|
40
|
+
## Provided Context
|
|
41
|
+
[START OF CONTEXT]
|
|
42
|
+
{{ context }}
|
|
43
|
+
[END OF CONTEXT]
|
|
44
|
+
|
|
45
|
+
## Answering Guidelines
|
|
46
|
+
- Carefully read all context; it may be lengthy or detailed
|
|
47
|
+
- Do not omit or overlook any relevant information
|
|
48
|
+
- If the context is incomplete or insufficient, try to provide a partial answer and encourage the user to clarify their question
|
|
49
|
+
- Read carefully any extra instructions below if provided and use them to answer
|
|
50
|
+
|
|
51
|
+
{% if prompt -%}
|
|
52
|
+
## Additional Instructions for answering
|
|
53
|
+
{{ prompt }}
|
|
54
|
+
{% endif -%}
|
|
55
|
+
|
|
56
|
+
{% if chat_history -%}
|
|
57
|
+
## Previous conversation history
|
|
58
|
+
- {{ chat_history }}
|
|
59
|
+
{% endif -%}
|
|
60
|
+
|
|
61
|
+
{% if extra_prompts -%}
|
|
62
|
+
## Extra Prompts to consider for generating the answer
|
|
63
|
+
This information was used to generate the context.
|
|
64
|
+
Use it to help generate a better answer and follow any specific instructions it may contain about the format or style of the answer.
|
|
65
|
+
{% for extra in extra_prompts -%}
|
|
66
|
+
- {{ extra }}
|
|
67
|
+
{% endfor -%}
|
|
68
|
+
{% endif -%}
|
|
69
|
+
|
|
70
|
+
Now provide your answer to the question: {{ question }}
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
SUMMARIZE_PROMPT = """
|
|
74
|
+
{% if rules -%}
|
|
75
|
+
# Generation Rules
|
|
76
|
+
{% for rule in rules -%}
|
|
77
|
+
- {{ rule }}
|
|
78
|
+
{% endfor -%}
|
|
79
|
+
{% endif -%}
|
|
80
|
+
|
|
81
|
+
## Question
|
|
82
|
+
{{ question }}
|
|
83
|
+
|
|
84
|
+
## Provided Context
|
|
85
|
+
[START OF CONTEXT]
|
|
86
|
+
{{ context }}
|
|
87
|
+
[END OF CONTEXT]
|
|
88
|
+
|
|
89
|
+
## Answering Guidelines
|
|
90
|
+
- Carefully read all context; it may be lengthy or detailed
|
|
91
|
+
- Do not omit or overlook any relevant information
|
|
92
|
+
- If the context is incomplete or insufficient, state: "Not enough data to answer this."
|
|
93
|
+
- Read carefully any extra instructions below if provided and use them to answer
|
|
94
|
+
|
|
95
|
+
{% if prompt -%}
|
|
96
|
+
## Additional Instructions for answering
|
|
97
|
+
- {{ prompt }}
|
|
98
|
+
{% endif -%}
|
|
99
|
+
|
|
100
|
+
{% if chat_history -%}
|
|
101
|
+
## Previous conversation history
|
|
102
|
+
- {{ chat_history }}
|
|
103
|
+
{% endif -%}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
{% if extra_prompts -%}
|
|
107
|
+
## Extra Prompts to consider for generating the answer
|
|
108
|
+
This information was used to generate the context.
|
|
109
|
+
Use it to help generate a better answer and follow any specific instructions it may contain about the format or style of the answer.
|
|
110
|
+
{% for extra in extra_prompts -%}
|
|
111
|
+
- {{ extra }}
|
|
112
|
+
{% endfor -%}
|
|
113
|
+
{% endif -%}
|
|
114
|
+
|
|
115
|
+
Now provide your answer to the question: {{ question }}
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
SUMMARIZE_PROMPT_TEMPLATE = PROMPT_ENVIRONMENT.from_string(SUMMARIZE_PROMPT)
|
|
119
|
+
SUMMARIZE_PROMPT_CONVERSATIONAL_TEMPLATE = PROMPT_ENVIRONMENT.from_string(
|
|
120
|
+
SUMMARIZE_PROMPT_CONVERSATIONAL
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@agent(
|
|
125
|
+
id="summarize",
|
|
126
|
+
agent_type="generation",
|
|
127
|
+
title="Summarize",
|
|
128
|
+
description="Summarize the provided context.",
|
|
129
|
+
config_schema=SummarizeAgentConfig,
|
|
130
|
+
)
|
|
131
|
+
class SummarizeAgent(Agent[SummarizeAgentConfig]):
|
|
132
|
+
__root_agent__ = True
|
|
133
|
+
config: SummarizeAgentConfig
|
|
134
|
+
|
|
135
|
+
@overload
|
|
136
|
+
async def __call__(
|
|
137
|
+
self,
|
|
138
|
+
memory: QuestionMemory,
|
|
139
|
+
manager: Manager,
|
|
140
|
+
tools: None = None,
|
|
141
|
+
) -> None: ...
|
|
142
|
+
|
|
143
|
+
@overload
|
|
144
|
+
async def __call__(
|
|
145
|
+
self,
|
|
146
|
+
memory: QuestionMemory,
|
|
147
|
+
manager: Manager,
|
|
148
|
+
tools: list[Tool],
|
|
149
|
+
) -> None | dict[str, list[ToolCall]]: ...
|
|
150
|
+
|
|
151
|
+
@trace_agent
|
|
152
|
+
async def __call__(
|
|
153
|
+
self,
|
|
154
|
+
memory: QuestionMemory,
|
|
155
|
+
manager: Manager,
|
|
156
|
+
tools: list[Tool] | None = None,
|
|
157
|
+
) -> None | dict[str, list[ToolCall]]:
|
|
158
|
+
citations_enabled = self.config.citations
|
|
159
|
+
|
|
160
|
+
# For each context in memory add context query, summary and answer onto a text and the initial question
|
|
161
|
+
# In cases when the original question has been rephrased, use the rephrased question
|
|
162
|
+
questions = memory.get_questions()
|
|
163
|
+
if len(questions) == 1:
|
|
164
|
+
question = questions[0][1]
|
|
165
|
+
else:
|
|
166
|
+
question = memory.original_question
|
|
167
|
+
|
|
168
|
+
session_context_parts: List[str] = []
|
|
169
|
+
|
|
170
|
+
if self.config.history:
|
|
171
|
+
qa_history, interactions = await memory.context_history()
|
|
172
|
+
await memory.add_step(
|
|
173
|
+
step_module=self.config.module,
|
|
174
|
+
step_title=self.step_title("History check"),
|
|
175
|
+
step_value="Included {} interactions of Q&A history".format(
|
|
176
|
+
interactions
|
|
177
|
+
),
|
|
178
|
+
step_reason="",
|
|
179
|
+
timeit=0,
|
|
180
|
+
step_agent_path=f"/context/{self.config.id if self.config.id else 'default'}",
|
|
181
|
+
input_nuclia_tokens=0.0,
|
|
182
|
+
output_nuclia_tokens=0.0,
|
|
183
|
+
)
|
|
184
|
+
session_context_parts.append(
|
|
185
|
+
f"## Previous questions and answers in this session:\n{qa_history}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
session_context = "\n\n".join(session_context_parts)
|
|
189
|
+
|
|
190
|
+
PROMPT_TEMPLATE = (
|
|
191
|
+
SUMMARIZE_PROMPT_CONVERSATIONAL_TEMPLATE
|
|
192
|
+
if self.config.conversational
|
|
193
|
+
else SUMMARIZE_PROMPT_TEMPLATE
|
|
194
|
+
)
|
|
195
|
+
prompt = self.config.prompt
|
|
196
|
+
extra_prompts: List[str] = []
|
|
197
|
+
if self.config.include_mcp_prompts:
|
|
198
|
+
extra_prompts = memory.get_prompt_texts()
|
|
199
|
+
|
|
200
|
+
if citations_enabled:
|
|
201
|
+
# Add citation ids to each context so they can be referenced in the answer
|
|
202
|
+
for index, context in enumerate(memory.contexts):
|
|
203
|
+
context.citations_id = generate_ctx_block_id(index)
|
|
204
|
+
|
|
205
|
+
prompt = PROMPT_TEMPLATE.render(
|
|
206
|
+
question=question,
|
|
207
|
+
context=memory.contexts_markdown()
|
|
208
|
+
if citations_enabled and self.config.force_chunk_level_citations
|
|
209
|
+
else memory.contexts_minimal(),
|
|
210
|
+
prompt=prompt,
|
|
211
|
+
extra_prompts=extra_prompts,
|
|
212
|
+
rules=self.config.rules,
|
|
213
|
+
chat_history=session_context,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if citations_enabled:
|
|
217
|
+
# Adjust the prompt so that the model returns citations
|
|
218
|
+
prompt += MARKDOWN_TWO_LEVELS_CITATIONS_PROMPT_ADJUSTMENT
|
|
219
|
+
|
|
220
|
+
t0 = time()
|
|
221
|
+
images = {}
|
|
222
|
+
for memory_context in memory.contexts:
|
|
223
|
+
if memory_context.images:
|
|
224
|
+
images.update(memory_context.images)
|
|
225
|
+
chat_model = ChatModel(
|
|
226
|
+
user_id="summarize",
|
|
227
|
+
question="",
|
|
228
|
+
user_prompt=UserPrompt(prompt=prompt),
|
|
229
|
+
system=self.config.system_prompt
|
|
230
|
+
if self.config.system_prompt
|
|
231
|
+
else DEFAULT_SYSTEM_PROMPT,
|
|
232
|
+
format_prompt=False,
|
|
233
|
+
generative_model=self.config.model,
|
|
234
|
+
query_context_images=images,
|
|
235
|
+
max_tokens=5000,
|
|
236
|
+
chat_history=await memory.get_chat_history(),
|
|
237
|
+
tools=tools if tools else [],
|
|
238
|
+
tool_choice=ToolChoiceAuto(),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
agent_path = f"/generation/{self.config.id if self.config.id else 'default'}"
|
|
242
|
+
# Pass memory so execute_raw streams automatically when memory.streaming is True.
|
|
243
|
+
# Streaming is skipped when tools are active (tool calls are not streamable).
|
|
244
|
+
streaming_memory = memory if not tools else None
|
|
245
|
+
resp, input_tokens, output_tokens = await manager.execute_raw(
|
|
246
|
+
chat_model,
|
|
247
|
+
memory=streaming_memory,
|
|
248
|
+
module="summarize",
|
|
249
|
+
agent_path=agent_path,
|
|
250
|
+
tracking=memory.get_tracking_info(),
|
|
251
|
+
)
|
|
252
|
+
answer = resp.answer
|
|
253
|
+
end_code = resp.code
|
|
254
|
+
|
|
255
|
+
# Fall back to original question and full contexts
|
|
256
|
+
if end_code == "-2": # indicates not enough data
|
|
257
|
+
prompt = PROMPT_TEMPLATE.render(
|
|
258
|
+
question=memory.original_question,
|
|
259
|
+
context=memory.contexts_markdown(),
|
|
260
|
+
prompt=prompt,
|
|
261
|
+
extra_prompts=extra_prompts,
|
|
262
|
+
rules=self.config.rules,
|
|
263
|
+
)
|
|
264
|
+
if citations_enabled:
|
|
265
|
+
# Adjust the prompt so that the model returns citations
|
|
266
|
+
prompt += MARKDOWN_TWO_LEVELS_CITATIONS_PROMPT_ADJUSTMENT
|
|
267
|
+
|
|
268
|
+
chat_model.user_prompt = UserPrompt(prompt=prompt)
|
|
269
|
+
resp, input_tokens, output_tokens = await manager.execute_raw(
|
|
270
|
+
chat_model,
|
|
271
|
+
memory=streaming_memory,
|
|
272
|
+
module="summarize",
|
|
273
|
+
agent_path=agent_path,
|
|
274
|
+
tracking=memory.get_tracking_info(),
|
|
275
|
+
)
|
|
276
|
+
answer = resp.answer
|
|
277
|
+
end_code = resp.code
|
|
278
|
+
|
|
279
|
+
if not resp.tools or answer:
|
|
280
|
+
# Only add answer if not a tool call or if answer is present (maybe some models return both)
|
|
281
|
+
await memory.add_answer(
|
|
282
|
+
answer,
|
|
283
|
+
module="summarize",
|
|
284
|
+
agent_path=f"/generation/{self.config.id if self.config.id else 'default'}",
|
|
285
|
+
citations=build_answer_citations(answer, memory.contexts)
|
|
286
|
+
if citations_enabled
|
|
287
|
+
else None,
|
|
288
|
+
)
|
|
289
|
+
memory.is_answered = end_code != "-2"
|
|
290
|
+
await memory.add_step(
|
|
291
|
+
step_module=self.config.module,
|
|
292
|
+
step_title=self.step_title("Summarize"),
|
|
293
|
+
step_value=str(answer),
|
|
294
|
+
step_reason="Summarized",
|
|
295
|
+
step_agent_path=f"/generation/{self.config.id if self.config.id else 'default'}",
|
|
296
|
+
timeit=time() - t0,
|
|
297
|
+
input_nuclia_tokens=input_tokens,
|
|
298
|
+
output_nuclia_tokens=output_tokens,
|
|
299
|
+
)
|
|
300
|
+
return resp.tools
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def build_answer_citations(answer: str, contexts: list[Context]) -> AnswerCitations:
|
|
304
|
+
result = AnswerCitations()
|
|
305
|
+
# Build a map of citation_id to context
|
|
306
|
+
citation_map = {
|
|
307
|
+
context.citations_id: context
|
|
308
|
+
for context in contexts
|
|
309
|
+
if context.citations_id is not None
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# Parse citations in the answer
|
|
313
|
+
citations_in_answer: set[str] = set()
|
|
314
|
+
for line in answer.splitlines():
|
|
315
|
+
if line.startswith("[") and "]: block-" in line:
|
|
316
|
+
citation_id = line.split("]: ")[1].strip()
|
|
317
|
+
citations_in_answer.add(citation_id)
|
|
318
|
+
|
|
319
|
+
for citation_id in citations_in_answer:
|
|
320
|
+
try:
|
|
321
|
+
context_citation_id, chunk_index = _parse_citation_id(citation_id)
|
|
322
|
+
except ValueError:
|
|
323
|
+
# Unknown format, skip
|
|
324
|
+
continue
|
|
325
|
+
|
|
326
|
+
if context_citation_id not in citation_map:
|
|
327
|
+
# Unknown citation, skip
|
|
328
|
+
continue
|
|
329
|
+
|
|
330
|
+
context: Context = citation_map[context_citation_id]
|
|
331
|
+
origin_urls: list[str] = []
|
|
332
|
+
|
|
333
|
+
if chunk_index is not None:
|
|
334
|
+
# This is a citation to a specific chunk of a context
|
|
335
|
+
try:
|
|
336
|
+
chunk = context.chunks[chunk_index]
|
|
337
|
+
if chunk.origin_url:
|
|
338
|
+
origin_urls.append(chunk.origin_url)
|
|
339
|
+
except IndexError:
|
|
340
|
+
# Chunk index is out of range, skip
|
|
341
|
+
pass
|
|
342
|
+
|
|
343
|
+
else:
|
|
344
|
+
# This is a citation to a summarized context
|
|
345
|
+
for chunk in context.chunks:
|
|
346
|
+
if (
|
|
347
|
+
context.citations is None or chunk.chunk_id in context.citations
|
|
348
|
+
) and chunk.origin_url:
|
|
349
|
+
origin_urls.append(chunk.origin_url)
|
|
350
|
+
|
|
351
|
+
result.metadata[citation_id] = CitationMetadata(
|
|
352
|
+
context_id=context.id,
|
|
353
|
+
origin_urls=origin_urls,
|
|
354
|
+
chunk_index=chunk_index,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
return result
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _parse_citation_id(citation_id: str) -> tuple[str, Optional[int]]:
|
|
361
|
+
"""Parse a citation id into context citation id and optional chunk index.
|
|
362
|
+
|
|
363
|
+
Examples:
|
|
364
|
+
- "block-abc123" -> ("block-abc123", None)
|
|
365
|
+
- "block-abc123-0" -> ("block-abc123", 0)
|
|
366
|
+
"""
|
|
367
|
+
if citation_id.count("-") >= 2:
|
|
368
|
+
# Assume the last part is the chunk index
|
|
369
|
+
parts = citation_id.rsplit("-", 1)
|
|
370
|
+
context_citation_id = parts[0]
|
|
371
|
+
try:
|
|
372
|
+
chunk_index = int(parts[1])
|
|
373
|
+
return context_citation_id, chunk_index
|
|
374
|
+
except ValueError:
|
|
375
|
+
raise ValueError("Invalid citation id format")
|
|
376
|
+
else:
|
|
377
|
+
return citation_id, None
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import 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 SummarizeAgentConfig(AgentConfig):
|
|
10
|
+
model_config = ConfigDict(title="Summarize")
|
|
11
|
+
module: Literal["summarize"] = "summarize"
|
|
12
|
+
system_prompt: Optional[str] = Field(
|
|
13
|
+
default=None,
|
|
14
|
+
title="System prompt",
|
|
15
|
+
description="System prompt to guide the model's behavior and response style",
|
|
16
|
+
json_schema_extra={
|
|
17
|
+
"show_in_node": True,
|
|
18
|
+
"widget": WidgetType.EXPANDABLE_TEXTAREA,
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
prompt: Optional[str] = Field(
|
|
22
|
+
default=None,
|
|
23
|
+
json_schema_extra={
|
|
24
|
+
"show_in_node": True,
|
|
25
|
+
"widget": WidgetType.EXPANDABLE_TEXTAREA,
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
model: str = Field(
|
|
29
|
+
default="chatgpt-azure-4o-mini",
|
|
30
|
+
title="Generative model",
|
|
31
|
+
description="Model used to generate the response",
|
|
32
|
+
json_schema_extra={"widget": WidgetType.MODEL_SELECT},
|
|
33
|
+
)
|
|
34
|
+
images: bool = False
|
|
35
|
+
conversational: bool = False
|
|
36
|
+
include_mcp_prompts: bool = Field(
|
|
37
|
+
default=False,
|
|
38
|
+
title="If MCP prompts were used during the context steps, include them in the prompt to generate the final answer",
|
|
39
|
+
)
|
|
40
|
+
citations: bool = Field(
|
|
41
|
+
default=False,
|
|
42
|
+
title="Whether to include markdown citations in the generated answer.",
|
|
43
|
+
)
|
|
44
|
+
force_chunk_level_citations: bool = Field(
|
|
45
|
+
default=False,
|
|
46
|
+
title="Whether to always use chunk-level citations instead of context-level citations.",
|
|
47
|
+
)
|
|
48
|
+
history: bool = Field(
|
|
49
|
+
default=False,
|
|
50
|
+
title="Session history",
|
|
51
|
+
description="Include previous Q&A history from the current session in the context provided to the summarize agent",
|
|
52
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
MARKDOWN_TWO_LEVELS_CITATIONS_PROMPT_ADJUSTMENT = """
|
|
2
|
+
You are given source blocks with IDs like: block-AB, block-BA, block-CD, etc. or block-AB-1, block-BA-2, block-CD-3, etc.
|
|
3
|
+
When producing an answer, cite these sources precisely using markdown footnotes.
|
|
4
|
+
|
|
5
|
+
CITATION RULES
|
|
6
|
+
|
|
7
|
+
1. In the main body, cite sources only with bracketed Arabic numerals: [1], [2], [3], etc.
|
|
8
|
+
- Never put a block ID directly in brackets (e.g. NO: [AB], [block-AB], [BA]).
|
|
9
|
+
- Never mix styles (no superscripts, no inline block names, no [Ref 1], etc.).
|
|
10
|
+
2. Numbering is assigned in order of FIRST USE of a unique block ID.
|
|
11
|
+
- The first time you need info from a block, assign it [1].
|
|
12
|
+
- The first time you need info from a never-before-used block, assign it the next unused number (e.g. [2]).
|
|
13
|
+
- If you later cite the SAME block again, REUSE its existing number (do NOT create a new one).
|
|
14
|
+
- This guarantees there are no duplicate footnote definitions and no gaps.
|
|
15
|
+
3. If facts in a sentence come from different blocks, you may concatenate citations WITH spaces: like [1] [3]. Do NOT merge them (no ranges like [1-3]) or output them without spaces (no [1][3]).
|
|
16
|
+
4. At the end, output section consisting ONLY of the unique citation mappings, one per line, in ascending numeric order
|
|
17
|
+
- Don't title this section or add any extra text, just the mappings.
|
|
18
|
+
- No duplicates.
|
|
19
|
+
- No skipped numbers.
|
|
20
|
+
- ONLY include blocks actually cited in the body.
|
|
21
|
+
5. Do NOT hallucinate block IDs. Only use those provided in the context.
|
|
22
|
+
|
|
23
|
+
FORMATTING CONTRACT
|
|
24
|
+
|
|
25
|
+
* Body: free text with numeric citations as specified.
|
|
26
|
+
* A blank line.
|
|
27
|
+
* References section (if any) exactly as described, no heading.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Example format:
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
"The OP-1 has a built-in tape feature with 6 minutes of recording time [1] [2]. You can record to any of the 4 individual tracks [1]."
|
|
34
|
+
|
|
35
|
+
[1]: block-AB
|
|
36
|
+
[2]: block-FZ-2
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
"""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hyperforge_summarize
|
|
3
|
+
Version: 1.0.0.post21
|
|
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
|
+
|
|
19
|
+
# Summarization Hyperforge agents
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
hyperforge_summarize/__init__.py,sha256=eqPf63ok3aXty9p_llAM9hfITEsCQ4s2h4iPPITrfQs,71
|
|
2
|
+
hyperforge_summarize/agent.py,sha256=2F7q-aWj5uk5GVcVr-wALpuXHUa0T7ZMe05iMW54Qxc,12987
|
|
3
|
+
hyperforge_summarize/config.py,sha256=hUt7SLarq3Qmpc5ryUMKCV1t68IAA6aeNzuPUAu63TA,1829
|
|
4
|
+
hyperforge_summarize/prompts.py,sha256=4Rz5WySdV_TPIxLPDMA7HljeePnVlCJK2YYoQ2u-OgU,1851
|
|
5
|
+
hyperforge_summarize-1.0.0.post21.dist-info/METADATA,sha256=fCwFVq-5E3kuD2vJb7E2f5kPscEomdc9UNJparB4vPM,734
|
|
6
|
+
hyperforge_summarize-1.0.0.post21.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
hyperforge_summarize-1.0.0.post21.dist-info/top_level.txt,sha256=8hkP4ie0zM_l2ulyGmPUCGDsjMyC2kM7en28kU2oUvg,21
|
|
8
|
+
hyperforge_summarize-1.0.0.post21.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hyperforge_summarize
|