agno 1.7.8__py3-none-any.whl → 1.7.9__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.
agno/agent/agent.py CHANGED
@@ -893,7 +893,7 @@ class Agent:
893
893
  ):
894
894
  yield event
895
895
  else:
896
- from agno.utils.events import RunResponseContentEvent
896
+ from agno.run.response import IntermediateRunResponseContentEvent, RunResponseContentEvent
897
897
 
898
898
  for event in self._handle_model_response_stream(
899
899
  run_response=run_response,
@@ -903,7 +903,10 @@ class Agent:
903
903
  ):
904
904
  if isinstance(event, RunResponseContentEvent):
905
905
  if stream_intermediate_steps:
906
- yield event
906
+ yield IntermediateRunResponseContentEvent(
907
+ content=event.content,
908
+ content_type=event.content_type,
909
+ )
907
910
  else:
908
911
  yield event
909
912
 
@@ -1331,7 +1334,7 @@ class Agent:
1331
1334
  ):
1332
1335
  yield event
1333
1336
  else:
1334
- from agno.utils.events import RunResponseContentEvent
1337
+ from agno.run.response import IntermediateRunResponseContentEvent, RunResponseContentEvent
1335
1338
 
1336
1339
  async for event in self._ahandle_model_response_stream(
1337
1340
  run_response=run_response,
@@ -1341,7 +1344,10 @@ class Agent:
1341
1344
  ):
1342
1345
  if isinstance(event, RunResponseContentEvent):
1343
1346
  if stream_intermediate_steps:
1344
- yield event
1347
+ yield IntermediateRunResponseContentEvent(
1348
+ content=event.content,
1349
+ content_type=event.content_type,
1350
+ )
1345
1351
  else:
1346
1352
  yield event
1347
1353
 
@@ -4973,9 +4979,26 @@ class Agent:
4973
4979
 
4974
4980
  run_messages.messages += history_copy
4975
4981
 
4976
- # 4.Add user message to run_messages
4982
+ # 4. Add messages to run_messages if provided
4983
+ if messages is not None and len(messages) > 0:
4984
+ for _m in messages:
4985
+ if isinstance(_m, Message):
4986
+ run_messages.messages.append(_m)
4987
+ if run_messages.extra_messages is None:
4988
+ run_messages.extra_messages = []
4989
+ run_messages.extra_messages.append(_m)
4990
+ elif isinstance(_m, dict):
4991
+ try:
4992
+ run_messages.messages.append(Message.model_validate(_m))
4993
+ if run_messages.extra_messages is None:
4994
+ run_messages.extra_messages = []
4995
+ run_messages.extra_messages.append(Message.model_validate(_m))
4996
+ except Exception as e:
4997
+ log_warning(f"Failed to validate message: {e}")
4998
+
4999
+ # 5. Add user message to run_messages
4977
5000
  user_message: Optional[Message] = None
4978
- # 4.1 Build user message if message is None, str or list
5001
+ # 5.1 Build user message if message is None, str or list
4979
5002
  if message is None or isinstance(message, str) or isinstance(message, list):
4980
5003
  user_message = self.get_user_message(
4981
5004
  message=message,
@@ -4986,16 +5009,16 @@ class Agent:
4986
5009
  knowledge_filters=knowledge_filters,
4987
5010
  **kwargs,
4988
5011
  )
4989
- # 4.2 If message is provided as a Message, use it directly
5012
+ # 5.2 If message is provided as a Message, use it directly
4990
5013
  elif isinstance(message, Message):
4991
5014
  user_message = message
4992
- # 4.3 If message is provided as a dict, try to validate it as a Message
5015
+ # 5.3 If message is provided as a dict, try to validate it as a Message
4993
5016
  elif isinstance(message, dict):
4994
5017
  try:
4995
5018
  user_message = Message.model_validate(message)
4996
5019
  except Exception as e:
4997
5020
  log_warning(f"Failed to validate message: {e}")
4998
- # 4.4 If message is provided as a BaseModel, convert it to a Message
5021
+ # 5.4 If message is provided as a BaseModel, convert it to a Message
4999
5022
  elif isinstance(message, BaseModel):
5000
5023
  try:
5001
5024
  # Create a user message with the BaseModel content
@@ -5008,23 +5031,6 @@ class Agent:
5008
5031
  run_messages.user_message = user_message
5009
5032
  run_messages.messages.append(user_message)
5010
5033
 
5011
- # 5. Add messages to run_messages if provided
5012
- if messages is not None and len(messages) > 0:
5013
- for _m in messages:
5014
- if isinstance(_m, Message):
5015
- run_messages.messages.append(_m)
5016
- if run_messages.extra_messages is None:
5017
- run_messages.extra_messages = []
5018
- run_messages.extra_messages.append(_m)
5019
- elif isinstance(_m, dict):
5020
- try:
5021
- run_messages.messages.append(Message.model_validate(_m))
5022
- if run_messages.extra_messages is None:
5023
- run_messages.extra_messages = []
5024
- run_messages.extra_messages.append(Message.model_validate(_m))
5025
- except Exception as e:
5026
- log_warning(f"Failed to validate message: {e}")
5027
-
5028
5034
  return run_messages
5029
5035
 
5030
5036
  def get_continue_run_messages(
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
+ import re
2
3
  from pathlib import Path
3
- from typing import IO, Any, List, Optional, Union
4
+ from typing import IO, Any, List, Optional, Tuple, Union
4
5
  from uuid import uuid4
5
6
 
6
7
  from agno.document.base import Document
@@ -15,7 +16,13 @@ except ImportError:
15
16
  raise ImportError("`pypdf` not installed. Please install it via `pip install pypdf`.")
16
17
 
17
18
 
18
- def process_image_page(doc_name: str, page_number: int, page: Any) -> Document:
19
+ PAGE_START_NUMBERING_FORMAT_DEFAULT = "<start page {page_nr}>"
20
+ PAGE_END_NUMBERING_FORMAT_DEFAULT = "<end page {page_nr}>"
21
+ PAGE_NUMBERING_CORRECTNESS_RATIO_FOR_REMOVAL = 0.4
22
+
23
+
24
+ def _ocr_reader(page: Any) -> str:
25
+ """A single PDF page object."""
19
26
  try:
20
27
  import rapidocr_onnxruntime as rapidocr
21
28
  except ImportError:
@@ -23,7 +30,6 @@ def process_image_page(doc_name: str, page_number: int, page: Any) -> Document:
23
30
  "`rapidocr_onnxruntime` not installed. Please install it via `pip install rapidocr_onnxruntime`."
24
31
  )
25
32
  ocr = rapidocr.RapidOCR()
26
- page_text = page.extract_text() or ""
27
33
  images_text_list = []
28
34
 
29
35
  # Extract and process images
@@ -34,22 +40,13 @@ def process_image_page(doc_name: str, page_number: int, page: Any) -> Document:
34
40
  ocr_result, elapse = ocr(image_data)
35
41
 
36
42
  # Extract text from OCR result
37
- if ocr_result:
38
- images_text_list += [item[1] for item in ocr_result]
43
+ images_text_list += [item[1] for item in ocr_result] if ocr_result else []
39
44
 
40
- images_text = "\n".join(images_text_list)
41
- content = page_text + "\n" + images_text
42
-
43
- # Append the document
44
- return Document(
45
- name=doc_name,
46
- id=str(uuid4()),
47
- meta_data={"page": page_number},
48
- content=content,
49
- )
45
+ return "\n".join(images_text_list)
50
46
 
51
47
 
52
- async def async_process_image_page(doc_name: str, page_number: int, page: Any) -> Document:
48
+ async def _async_ocr_reader(page: Any) -> str:
49
+ """page: A single PDF page object."""
53
50
  try:
54
51
  import rapidocr_onnxruntime as rapidocr
55
52
  except ImportError:
@@ -58,9 +55,6 @@ async def async_process_image_page(doc_name: str, page_number: int, page: Any) -
58
55
  )
59
56
  ocr = rapidocr.RapidOCR()
60
57
 
61
- page_text = page.extract_text() or ""
62
- images_text_list: List = []
63
-
64
58
  # Process images in parallel
65
59
  async def process_image(image_data: bytes) -> List[str]:
66
60
  ocr_result, _ = ocr(image_data)
@@ -69,27 +63,221 @@ async def async_process_image_page(doc_name: str, page_number: int, page: Any) -
69
63
  image_tasks = [process_image(image.data) for image in page.images]
70
64
  images_results = await asyncio.gather(*image_tasks)
71
65
 
66
+ images_text_list: List = []
72
67
  for result in images_results:
73
68
  images_text_list.extend(result)
74
69
 
75
70
  images_text = "\n".join(images_text_list)
76
- content = page_text + "\n" + images_text
77
-
78
- return Document(
79
- name=doc_name,
80
- id=str(uuid4()),
81
- meta_data={"page": page_number},
82
- content=content,
71
+ return images_text
72
+
73
+
74
+ def _clean_page_numbers(
75
+ page_content_list: List[str],
76
+ extra_content: List[str] = [],
77
+ page_start_numbering_format: str = PAGE_START_NUMBERING_FORMAT_DEFAULT,
78
+ page_end_numbering_format: str = PAGE_END_NUMBERING_FORMAT_DEFAULT,
79
+ ) -> Tuple[List[str], Optional[int]]:
80
+ f"""
81
+ Identifies and removes or reformats page numbers from a list of PDF page contents, based on the most consistent sequential numbering.
82
+
83
+ Args:
84
+ page_content_list (List[str]): A list of strings where each string represents the content of a PDF page.
85
+ extra_content (List[str]): A list of strings where each string will be appended after the main content. Can be used for appending image information.
86
+ page_start_numbering_format (str): A format string to prepend to the page content, with `{{page_nr}}` as a placeholder for the page number.
87
+ Defaults to {PAGE_START_NUMBERING_FORMAT_DEFAULT}. Make it an empty string to remove the page number.
88
+ page_end_numbering_format (str): A format string to append to the page content, with `{{page_nr}}` as a placeholder for the page number.
89
+ Defaults to {PAGE_END_NUMBERING_FORMAT_DEFAULT}. Make it an empty string to remove the page number.
90
+
91
+ Returns:
92
+ List[str]: The list of page contents with page numbers removed or reformatted based on the detected sequence.
93
+ Optional[Int]: The shift for the page numbering. Can be (-2, -1, 0, 1, 2).
94
+
95
+ Notes:
96
+ - The function scans for page numbers using a regular expression that matches digits at the start or end of a string.
97
+ - It evaluates several potential starting points for numbering (-2, -1, 0, 1, 2 shifts) to determine the most consistent sequence.
98
+ - If at least a specified ratio of pages (defined by `PAGE_NUMBERING_CORRECTNESS_RATIO_FOR_REMOVAL`) has correct sequential numbering,
99
+ the page numbers are processed.
100
+ - If page numbers are found, the function will add formatted page numbers to each page's content if `page_start_numbering_format` or
101
+ `page_end_numbering_format` is provided.
102
+ """
103
+ assert len(extra_content) == 0 or len(extra_content) == len(page_content_list), (
104
+ "Please provide an equally sized list of extra content if provided."
83
105
  )
84
106
 
107
+ # Regex to match potential page numbers at the start or end of a string
108
+ page_number_regex = re.compile(r"^\s*(\d+)\s*|\s*(\d+)\s*$")
109
+
110
+ def find_page_number(content):
111
+ match = page_number_regex.search(content)
112
+ if match:
113
+ return int(match.group(1) or match.group(2))
114
+ return None
115
+
116
+ page_numbers = [find_page_number(content) for content in page_content_list]
117
+ if all(x is None or x > 5 for x in page_numbers):
118
+ # This approach won't work reliably for higher page numbers.
119
+ return page_content_list, None
120
+
121
+ # Possible range shifts to detect page numbering
122
+ range_shifts = [-2, -1, 0, 1, 2]
123
+ best_match, best_correct_count, best_shift = _identify_best_page_sequence(page_numbers, range_shifts)
124
+
125
+ # Check if at least ..% of the pages have correct sequential numbering
126
+ if best_match and best_correct_count / len(page_numbers) >= PAGE_NUMBERING_CORRECTNESS_RATIO_FOR_REMOVAL:
127
+ # Remove the page numbers from the content
128
+ for i, expected_number in enumerate(best_match):
129
+ page_content_list[i] = re.sub(
130
+ rf"^\s*{expected_number}\s*|\s*{expected_number}\s*$", "", page_content_list[i]
131
+ )
132
+
133
+ page_start = (
134
+ page_start_numbering_format.format(page_nr=expected_number) + "\n"
135
+ if page_start_numbering_format
136
+ else ""
137
+ )
138
+ page_end = (
139
+ "\n" + page_end_numbering_format.format(page_nr=expected_number) if page_end_numbering_format else ""
140
+ )
141
+ extra_info = "\n" + extra_content[i] if extra_content else ""
142
+
143
+ # Add formatted page numbering if configured.
144
+ page_content_list[i] = page_start + page_content_list[i] + extra_info + page_end
145
+ else:
146
+ best_shift = None
147
+
148
+ return page_content_list, best_shift
149
+
150
+
151
+ def _identify_best_page_sequence(page_numbers, range_shifts):
152
+ best_match = None
153
+ best_shift: Optional[int] = None
154
+ best_correct_count = 0
155
+
156
+ for shift in range_shifts:
157
+ expected_numbers = [i + shift for i in range(len(page_numbers))]
158
+ # Check if expected number occurs (or that the expected "2" occurs in an incorrectly merged number like 25,
159
+ # where 2 is the page number and 5 is part of the PDF content).
160
+ correct_count = sum(
161
+ 1
162
+ for actual, expected in zip(page_numbers, expected_numbers)
163
+ if actual == expected or str(actual).startswith(str(expected)) or str(actual).endswith(str(expected))
164
+ )
165
+
166
+ if correct_count > best_correct_count:
167
+ best_correct_count = correct_count
168
+ best_match = expected_numbers
169
+ best_shift = shift
170
+
171
+ return best_match, best_correct_count, best_shift
172
+
85
173
 
86
174
  class BasePDFReader(Reader):
175
+ def __init__(
176
+ self,
177
+ split_on_pages: bool = True,
178
+ page_start_numbering_format: Optional[str] = None,
179
+ page_end_numbering_format: Optional[str] = None,
180
+ **kwargs,
181
+ ):
182
+ if page_start_numbering_format is None:
183
+ page_start_numbering_format = PAGE_START_NUMBERING_FORMAT_DEFAULT
184
+ if page_end_numbering_format is None:
185
+ page_end_numbering_format = PAGE_END_NUMBERING_FORMAT_DEFAULT
186
+
187
+ self.split_on_pages = split_on_pages
188
+ self.page_start_numbering_format = page_start_numbering_format
189
+ self.page_end_numbering_format = page_end_numbering_format
190
+
191
+ super().__init__(**kwargs)
192
+
87
193
  def _build_chunked_documents(self, documents: List[Document]) -> List[Document]:
88
194
  chunked_documents: List[Document] = []
89
195
  for document in documents:
90
196
  chunked_documents.extend(self.chunk_document(document))
91
197
  return chunked_documents
92
198
 
199
+ def _create_documents(self, pdf_content: List[str], doc_name: str, use_uuid_for_id: bool, page_number_shift):
200
+ if self.split_on_pages:
201
+ shift = page_number_shift if page_number_shift is not None else 1
202
+ documents: List[Document] = []
203
+ for page_number, page_content in enumerate(pdf_content, start=shift):
204
+ documents.append(
205
+ Document(
206
+ name=doc_name,
207
+ id=(str(uuid4()) if use_uuid_for_id else f"{doc_name}_{page_number}"),
208
+ meta_data={"page": page_number},
209
+ content=page_content,
210
+ )
211
+ )
212
+ else:
213
+ pdf_content_str = "\n".join(pdf_content)
214
+ document = Document(
215
+ name=doc_name,
216
+ id=str(uuid4()) if use_uuid_for_id else doc_name,
217
+ meta_data={},
218
+ content=pdf_content_str,
219
+ )
220
+ documents = [document]
221
+
222
+ if self.chunk:
223
+ return self._build_chunked_documents(documents)
224
+
225
+ return documents
226
+
227
+ def _pdf_reader_to_documents(
228
+ self,
229
+ doc_reader: DocumentReader,
230
+ doc_name,
231
+ read_images=False,
232
+ use_uuid_for_id=False,
233
+ ):
234
+ pdf_content = []
235
+ pdf_images_text = []
236
+ for page in doc_reader.pages:
237
+ pdf_content.append(page.extract_text())
238
+ if read_images:
239
+ pdf_images_text.append(_ocr_reader(page))
240
+
241
+ pdf_content, shift = _clean_page_numbers(
242
+ page_content_list=pdf_content,
243
+ extra_content=pdf_images_text,
244
+ page_start_numbering_format=self.page_start_numbering_format,
245
+ page_end_numbering_format=self.page_end_numbering_format,
246
+ )
247
+ return self._create_documents(pdf_content, doc_name, use_uuid_for_id, shift)
248
+
249
+ async def _async_pdf_reader_to_documents(
250
+ self,
251
+ doc_reader: DocumentReader,
252
+ doc_name: str,
253
+ read_images=False,
254
+ use_uuid_for_id=False,
255
+ ):
256
+ async def _read_pdf_page(page, read_images) -> Tuple[str, str]:
257
+ # We tried "asyncio.to_thread(page.extract_text)", but it maintains state internally, which leads to issues.
258
+ page_text = page.extract_text()
259
+
260
+ if read_images:
261
+ pdf_images_text = await _async_ocr_reader(page)
262
+ else:
263
+ pdf_images_text = ""
264
+
265
+ return page_text, pdf_images_text
266
+
267
+ # Process pages in parallel using asyncio.gather
268
+ pdf_content: List[Tuple[str, str]] = await asyncio.gather(
269
+ *[_read_pdf_page(page, read_images) for page in doc_reader.pages]
270
+ )
271
+
272
+ pdf_content_clean, shift = _clean_page_numbers(
273
+ page_content_list=[x[0] for x in pdf_content],
274
+ extra_content=[x[1] for x in pdf_content],
275
+ page_start_numbering_format=self.page_start_numbering_format,
276
+ page_end_numbering_format=self.page_end_numbering_format,
277
+ )
278
+
279
+ return self._create_documents(pdf_content_clean, doc_name, use_uuid_for_id, shift)
280
+
93
281
 
94
282
  class PDFReader(BasePDFReader):
95
283
  """Reader for PDF files"""
@@ -106,24 +294,13 @@ class PDFReader(BasePDFReader):
106
294
  log_info(f"Reading: {doc_name}")
107
295
 
108
296
  try:
109
- doc_reader = DocumentReader(pdf)
297
+ pdf_reader = DocumentReader(pdf)
110
298
  except PdfStreamError as e:
111
299
  logger.error(f"Error reading PDF: {e}")
112
300
  return []
113
301
 
114
- documents = []
115
- for page_number, page in enumerate(doc_reader.pages, start=1):
116
- documents.append(
117
- Document(
118
- name=doc_name,
119
- id=str(uuid4()),
120
- meta_data={"page": page_number},
121
- content=page.extract_text(),
122
- )
123
- )
124
- if self.chunk:
125
- return self._build_chunked_documents(documents)
126
- return documents
302
+ # Read and chunk.
303
+ return self._pdf_reader_to_documents(pdf_reader, doc_name, use_uuid_for_id=True)
127
304
 
128
305
  async def async_read(self, pdf: Union[str, Path, IO[Any]]) -> List[Document]:
129
306
  try:
@@ -137,30 +314,13 @@ class PDFReader(BasePDFReader):
137
314
  log_info(f"Reading: {doc_name}")
138
315
 
139
316
  try:
140
- doc_reader = DocumentReader(pdf)
317
+ pdf_reader = DocumentReader(pdf)
141
318
  except PdfStreamError as e:
142
319
  logger.error(f"Error reading PDF: {e}")
143
320
  return []
144
321
 
145
- async def _process_document(doc_name: str, page_number: int, page: Any) -> Document:
146
- return Document(
147
- name=doc_name,
148
- id=str(uuid4()),
149
- meta_data={"page": page_number},
150
- content=page.extract_text(),
151
- )
152
-
153
- # Process pages in parallel using asyncio.gather
154
- documents = await asyncio.gather(
155
- *[
156
- _process_document(doc_name, page_number, page)
157
- for page_number, page in enumerate(doc_reader.pages, start=1)
158
- ]
159
- )
160
-
161
- if self.chunk:
162
- return self._build_chunked_documents(documents)
163
- return documents
322
+ # Read and chunk.
323
+ return await self._async_pdf_reader_to_documents(pdf_reader, doc_name, use_uuid_for_id=True)
164
324
 
165
325
 
166
326
  class PDFUrlReader(BasePDFReader):
@@ -182,21 +342,10 @@ class PDFUrlReader(BasePDFReader):
182
342
  response = fetch_with_retry(url, proxy=self.proxy)
183
343
 
184
344
  doc_name = url.split("/")[-1].split(".")[0].replace("/", "_").replace(" ", "_")
185
- doc_reader = DocumentReader(BytesIO(response.content))
186
-
187
- documents = []
188
- for page_number, page in enumerate(doc_reader.pages, start=1):
189
- documents.append(
190
- Document(
191
- name=doc_name,
192
- id=f"{doc_name}_{page_number}",
193
- meta_data={"page": page_number},
194
- content=page.extract_text(),
195
- )
196
- )
197
- if self.chunk:
198
- return self._build_chunked_documents(documents)
199
- return documents
345
+ pdf_reader = DocumentReader(BytesIO(response.content))
346
+
347
+ # Read and chunk.
348
+ return self._pdf_reader_to_documents(pdf_reader, doc_name, use_uuid_for_id=False)
200
349
 
201
350
  async def async_read(self, url: str) -> List[Document]:
202
351
  if not url:
@@ -213,27 +362,10 @@ class PDFUrlReader(BasePDFReader):
213
362
  response = await async_fetch_with_retry(url, client=client)
214
363
 
215
364
  doc_name = url.split("/")[-1].split(".")[0].replace("/", "_").replace(" ", "_")
216
- doc_reader = DocumentReader(BytesIO(response.content))
217
-
218
- async def _process_document(doc_name: str, page_number: int, page: Any) -> Document:
219
- return Document(
220
- name=doc_name,
221
- id=f"{doc_name}_{page_number}",
222
- meta_data={"page": page_number},
223
- content=page.extract_text(),
224
- )
225
-
226
- # Process pages in parallel using asyncio.gather
227
- documents = await asyncio.gather(
228
- *[
229
- _process_document(doc_name, page_number, page)
230
- for page_number, page in enumerate(doc_reader.pages, start=1)
231
- ]
232
- )
365
+ pdf_reader = DocumentReader(BytesIO(response.content))
233
366
 
234
- if self.chunk:
235
- return self._build_chunked_documents(documents)
236
- return documents
367
+ # Read and chunk.
368
+ return await self._async_pdf_reader_to_documents(pdf_reader, doc_name, use_uuid_for_id=False)
237
369
 
238
370
 
239
371
  class PDFImageReader(BasePDFReader):
@@ -252,16 +384,10 @@ class PDFImageReader(BasePDFReader):
252
384
  doc_name = "pdf"
253
385
 
254
386
  log_info(f"Reading: {doc_name}")
255
- doc_reader = DocumentReader(pdf)
256
-
257
- documents = []
258
- for page_number, page in enumerate(doc_reader.pages, start=1):
259
- documents.append(process_image_page(doc_name, page_number, page))
260
-
261
- if self.chunk:
262
- return self._build_chunked_documents(documents)
387
+ pdf_reader = DocumentReader(pdf)
263
388
 
264
- return documents
389
+ # Read and chunk.
390
+ return self._pdf_reader_to_documents(pdf_reader, doc_name, read_images=True, use_uuid_for_id=False)
265
391
 
266
392
  async def async_read(self, pdf: Union[str, Path, IO[Any]]) -> List[Document]:
267
393
  if not pdf:
@@ -276,18 +402,10 @@ class PDFImageReader(BasePDFReader):
276
402
  doc_name = "pdf"
277
403
 
278
404
  log_info(f"Reading: {doc_name}")
279
- doc_reader = DocumentReader(pdf)
405
+ pdf_reader = DocumentReader(pdf)
280
406
 
281
- documents = await asyncio.gather(
282
- *[
283
- async_process_image_page(doc_name, page_number, page)
284
- for page_number, page in enumerate(doc_reader.pages, start=1)
285
- ]
286
- )
287
-
288
- if self.chunk:
289
- return self._build_chunked_documents(documents)
290
- return documents
407
+ # Read and chunk.
408
+ return await self._async_pdf_reader_to_documents(pdf_reader, doc_name, read_images=True, use_uuid_for_id=False)
291
409
 
292
410
 
293
411
  class PDFUrlImageReader(BasePDFReader):
@@ -310,17 +428,10 @@ class PDFUrlImageReader(BasePDFReader):
310
428
  response = httpx.get(url, proxy=self.proxy) if self.proxy else httpx.get(url)
311
429
 
312
430
  doc_name = url.split("/")[-1].split(".")[0].replace(" ", "_")
313
- doc_reader = DocumentReader(BytesIO(response.content))
314
-
315
- documents = []
316
- for page_number, page in enumerate(doc_reader.pages, start=1):
317
- documents.append(process_image_page(doc_name, page_number, page))
318
-
319
- # Optionally chunk documents
320
- if self.chunk:
321
- return self._build_chunked_documents(documents)
431
+ pdf_reader = DocumentReader(BytesIO(response.content))
322
432
 
323
- return documents
433
+ # Read and chunk.
434
+ return self._pdf_reader_to_documents(pdf_reader, doc_name, read_images=True, use_uuid_for_id=False)
324
435
 
325
436
  async def async_read(self, url: str) -> List[Document]:
326
437
  if not url:
@@ -338,15 +449,7 @@ class PDFUrlImageReader(BasePDFReader):
338
449
  response.raise_for_status()
339
450
 
340
451
  doc_name = url.split("/")[-1].split(".")[0].replace(" ", "_")
341
- doc_reader = DocumentReader(BytesIO(response.content))
452
+ pdf_reader = DocumentReader(BytesIO(response.content))
342
453
 
343
- documents = await asyncio.gather(
344
- *[
345
- async_process_image_page(doc_name, page_number, page)
346
- for page_number, page in enumerate(doc_reader.pages, start=1)
347
- ]
348
- )
349
-
350
- if self.chunk:
351
- return self._build_chunked_documents(documents)
352
- return documents
454
+ # Read and chunk.
455
+ return await self._async_pdf_reader_to_documents(pdf_reader, doc_name, read_images=True, use_uuid_for_id=False)
agno/run/response.py CHANGED
@@ -17,6 +17,7 @@ class RunEvent(str, Enum):
17
17
 
18
18
  run_started = "RunStarted"
19
19
  run_response_content = "RunResponseContent"
20
+ run_intermediate_response_content = "RunIntermediateResponseContent"
20
21
  run_completed = "RunCompleted"
21
22
  run_error = "RunError"
22
23
  run_cancelled = "RunCancelled"
@@ -92,6 +93,13 @@ class RunResponseContentEvent(BaseAgentRunResponseEvent):
92
93
  extra_data: Optional[RunResponseExtraData] = None
93
94
 
94
95
 
96
+ @dataclass
97
+ class IntermediateRunResponseContentEvent(BaseAgentRunResponseEvent):
98
+ event: str = RunEvent.run_intermediate_response_content.value
99
+ content: Optional[Any] = None
100
+ content_type: str = "str"
101
+
102
+
95
103
  @dataclass
96
104
  class RunResponseCompletedEvent(BaseAgentRunResponseEvent):
97
105
  event: str = RunEvent.run_completed.value
@@ -207,6 +215,7 @@ class OutputModelResponseCompletedEvent(BaseAgentRunResponseEvent):
207
215
  RunResponseEvent = Union[
208
216
  RunResponseStartedEvent,
209
217
  RunResponseContentEvent,
218
+ IntermediateRunResponseContentEvent,
210
219
  RunResponseCompletedEvent,
211
220
  RunResponseErrorEvent,
212
221
  RunResponseCancelledEvent,
@@ -230,6 +239,7 @@ RunResponseEvent = Union[
230
239
  RUN_EVENT_TYPE_REGISTRY = {
231
240
  RunEvent.run_started.value: RunResponseStartedEvent,
232
241
  RunEvent.run_response_content.value: RunResponseContentEvent,
242
+ RunEvent.run_intermediate_response_content.value: IntermediateRunResponseContentEvent,
233
243
  RunEvent.run_completed.value: RunResponseCompletedEvent,
234
244
  RunEvent.run_error.value: RunResponseErrorEvent,
235
245
  RunEvent.run_cancelled.value: RunResponseCancelledEvent,
agno/run/team.py CHANGED
@@ -17,6 +17,7 @@ class TeamRunEvent(str, Enum):
17
17
 
18
18
  run_started = "TeamRunStarted"
19
19
  run_response_content = "TeamRunResponseContent"
20
+ run_intermediate_response_content = "TeamRunIntermediateResponseContent"
20
21
  run_completed = "TeamRunCompleted"
21
22
  run_error = "TeamRunError"
22
23
  run_cancelled = "TeamRunCancelled"
@@ -94,6 +95,13 @@ class RunResponseContentEvent(BaseTeamRunResponseEvent):
94
95
  extra_data: Optional[RunResponseExtraData] = None
95
96
 
96
97
 
98
+ @dataclass
99
+ class IntermediateRunResponseContentEvent(BaseTeamRunResponseEvent):
100
+ event: str = TeamRunEvent.run_intermediate_response_content.value
101
+ content: Optional[Any] = None
102
+ content_type: str = "str"
103
+
104
+
97
105
  @dataclass
98
106
  class RunResponseCompletedEvent(BaseTeamRunResponseEvent):
99
107
  event: str = TeamRunEvent.run_completed.value
@@ -191,6 +199,7 @@ class OutputModelResponseCompletedEvent(BaseTeamRunResponseEvent):
191
199
  TeamRunResponseEvent = Union[
192
200
  RunResponseStartedEvent,
193
201
  RunResponseContentEvent,
202
+ IntermediateRunResponseContentEvent,
194
203
  RunResponseCompletedEvent,
195
204
  RunResponseErrorEvent,
196
205
  RunResponseCancelledEvent,
@@ -211,6 +220,7 @@ TeamRunResponseEvent = Union[
211
220
  TEAM_RUN_EVENT_TYPE_REGISTRY = {
212
221
  TeamRunEvent.run_started.value: RunResponseStartedEvent,
213
222
  TeamRunEvent.run_response_content.value: RunResponseContentEvent,
223
+ TeamRunEvent.run_intermediate_response_content.value: IntermediateRunResponseContentEvent,
214
224
  TeamRunEvent.run_completed.value: RunResponseCompletedEvent,
215
225
  TeamRunEvent.run_error.value: RunResponseErrorEvent,
216
226
  TeamRunEvent.run_cancelled.value: RunResponseCancelledEvent,
agno/team/team.py CHANGED
@@ -1110,16 +1110,21 @@ class Team:
1110
1110
  response_format=response_format,
1111
1111
  stream_intermediate_steps=stream_intermediate_steps,
1112
1112
  ):
1113
- from agno.run.team import RunResponseContentEvent
1113
+ from agno.run.team import IntermediateRunResponseContentEvent, RunResponseContentEvent
1114
1114
 
1115
1115
  if isinstance(event, RunResponseContentEvent):
1116
1116
  if stream_intermediate_steps:
1117
- yield event
1117
+ yield IntermediateRunResponseContentEvent(
1118
+ content=event.content,
1119
+ content_type=event.content_type,
1120
+ )
1118
1121
  else:
1119
1122
  yield event
1120
1123
 
1121
1124
  yield from self._generate_response_with_output_model_stream(
1122
- run_response=run_response, run_messages=run_messages
1125
+ run_response=run_response,
1126
+ run_messages=run_messages,
1127
+ stream_intermediate_steps=stream_intermediate_steps,
1123
1128
  )
1124
1129
 
1125
1130
  # If a parser model is provided, structure the response separately
@@ -1519,19 +1524,28 @@ class Team:
1519
1524
  ):
1520
1525
  yield event
1521
1526
  else:
1522
- from agno.run.team import RunResponseContentEvent
1523
-
1524
- async for event in self._agenerate_response_with_output_model_stream(
1527
+ async for event in self._ahandle_model_response_stream(
1525
1528
  run_response=run_response,
1526
1529
  run_messages=run_messages,
1530
+ response_format=response_format,
1527
1531
  stream_intermediate_steps=stream_intermediate_steps,
1528
1532
  ):
1533
+ from agno.run.team import IntermediateRunResponseContentEvent, RunResponseContentEvent
1534
+
1529
1535
  if isinstance(event, RunResponseContentEvent):
1530
1536
  if stream_intermediate_steps:
1531
- yield event
1537
+ yield IntermediateRunResponseContentEvent(
1538
+ content=event.content,
1539
+ content_type=event.content_type,
1540
+ )
1532
1541
  else:
1533
1542
  yield event
1534
1543
 
1544
+ async for event in self._agenerate_response_with_output_model_stream(
1545
+ run_response=run_response,
1546
+ run_messages=run_messages,
1547
+ stream_intermediate_steps=stream_intermediate_steps,
1548
+ ):
1535
1549
  yield event
1536
1550
 
1537
1551
  # If a parser model is provided, structure the response separately
@@ -2446,10 +2460,9 @@ class Team:
2446
2460
  model_response.content = output_model_response.content
2447
2461
 
2448
2462
  def _generate_response_with_output_model_stream(
2449
- self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = True
2463
+ self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = False
2450
2464
  ):
2451
2465
  """Parse the model response using the output model stream."""
2452
-
2453
2466
  from agno.utils.events import (
2454
2467
  create_team_output_model_response_completed_event,
2455
2468
  create_team_output_model_response_started_event,
@@ -2497,7 +2510,7 @@ class Team:
2497
2510
  model_response.content = output_model_response.content
2498
2511
 
2499
2512
  async def _agenerate_response_with_output_model_stream(
2500
- self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = True
2513
+ self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = False
2501
2514
  ):
2502
2515
  """Parse the model response using the output model stream."""
2503
2516
  from agno.utils.events import (
@@ -2514,9 +2527,7 @@ class Team:
2514
2527
  messages_for_output_model = self.get_messages_for_output_model(run_messages.messages)
2515
2528
  model_response = ModelResponse(content="")
2516
2529
 
2517
- model_response_stream = self.output_model.aresponse_stream(messages=messages_for_output_model)
2518
-
2519
- async for model_response_event in model_response_stream:
2530
+ async for model_response_event in self.output_model.aresponse_stream(messages=messages_for_output_model):
2520
2531
  for event in self._handle_model_response_chunk(
2521
2532
  run_response=run_response,
2522
2533
  full_model_response=model_response,
agno/tools/aws_lambda.py CHANGED
@@ -26,6 +26,9 @@ class AWSLambdaTools(Toolkit):
26
26
  super().__init__(name="aws-lambda", tools=tools, **kwargs)
27
27
 
28
28
  def list_functions(self) -> str:
29
+ """
30
+ List all AWS Lambda functions in the configured AWS account.
31
+ """
29
32
  try:
30
33
  response = self.client.list_functions()
31
34
  functions = [func["FunctionName"] for func in response["Functions"]]
@@ -34,6 +37,13 @@ class AWSLambdaTools(Toolkit):
34
37
  return f"Error listing functions: {str(e)}"
35
38
 
36
39
  def invoke_function(self, function_name: str, payload: str = "{}") -> str:
40
+ """
41
+ Invoke a specific AWS Lambda function with an optional JSON payload.
42
+
43
+ Args:
44
+ function_name (str): The name of the Lambda function to invoke.
45
+ payload (str): The JSON payload to send to the function. Defaults to "{}".
46
+ """
37
47
  try:
38
48
  response = self.client.invoke(FunctionName=function_name, Payload=payload)
39
49
  return f"Function invoked successfully. Status code: {response['StatusCode']}, Payload: {response['Payload'].read().decode('utf-8')}"
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  from hashlib import md5
3
+ from os import getenv
3
4
  from typing import Any, Dict, List, Optional
4
5
 
5
6
  try:
@@ -74,9 +75,12 @@ class LanceDb(VectorDb):
74
75
  # Distance metric
75
76
  self.distance: Distance = distance
76
77
 
78
+ # Remote LanceDB connection details
79
+ self.api_key: Optional[str] = api_key
80
+
77
81
  # LanceDB connection details
78
82
  self.uri: lancedb.URI = uri
79
- self.connection: lancedb.LanceDBConnection = connection or lancedb.connect(uri=self.uri, api_key=api_key)
83
+ self.connection: lancedb.DBConnection = connection or lancedb.connect(uri=self.uri, api_key=api_key)
80
84
  self.table: Optional[lancedb.db.LanceTable] = table
81
85
 
82
86
  self.async_connection: Optional[lancedb.AsyncConnection] = async_connection
@@ -168,7 +172,11 @@ class LanceDb(VectorDb):
168
172
  schema = self._base_schema()
169
173
 
170
174
  log_info(f"Creating table: {self.table_name}")
171
- tbl = self.connection.create_table(self.table_name, schema=schema, mode="overwrite", exist_ok=True) # type: ignore
175
+ if self.api_key or getenv("LANCEDB_API_KEY"):
176
+ log_info("API key found, creating table in remote LanceDB")
177
+ tbl = self.connection.create_table(name=self.table_name, schema=schema, mode="overwrite") # type: ignore
178
+ else:
179
+ tbl = self.connection.create_table(name=self.table_name, schema=schema, mode="overwrite", exist_ok=True) # type: ignore
172
180
  return tbl # type: ignore
173
181
 
174
182
  def doc_exists(self, document: Document) -> bool:
@@ -766,6 +766,9 @@ class PgVector(VectorDb):
766
766
  )
767
767
  )
768
768
 
769
+ if self.reranker:
770
+ search_results = self.reranker.rerank(query=query, documents=search_results)
771
+
769
772
  log_info(f"Found {len(search_results)} documents")
770
773
  return search_results
771
774
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agno
3
- Version: 1.7.8
3
+ Version: 1.7.9
4
4
  Summary: Agno: a lightweight library for building Multi-Agent Systems
5
5
  Author-email: Ashpreet Bedi <ashpreet@agno.com>
6
6
  License: Copyright (c) Agno, Inc.
@@ -5,7 +5,7 @@ agno/exceptions.py,sha256=HWuuNFS5J0l1RYJsdUrSx51M22aFEoh9ltoeonXBoBw,2891
5
5
  agno/media.py,sha256=lXJuylmhuIEWThKZkQ9pUZPp8Kms7EdT4N_U4YN9I00,12656
6
6
  agno/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  agno/agent/__init__.py,sha256=Ai6GVyw-0rkA2eYAfoEQIvbi_mrWQUxuPFaFbSDJYCQ,1306
8
- agno/agent/agent.py,sha256=pvXeME41lUvYBALizxWnawLcIhBwKG33fYJbw-MNY8s,381013
8
+ agno/agent/agent.py,sha256=jNCP1lMtzKmDliOgPhItQTrx6HJG5ZHNo3P4g41Jy9k,381426
9
9
  agno/agent/metrics.py,sha256=Lf7JYgPPdqRCyPfCDVUjnmUZ1SkWXrJClL80aW2ffEw,4379
10
10
  agno/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  agno/api/agent.py,sha256=J-Y4HI-J0Bu6r9gxRYCM3U7SnVBGwLIouDy806KSIIw,2821
@@ -92,7 +92,7 @@ agno/document/reader/docx_reader.py,sha256=fNSZNzBlROQow7nagouEfN8E4KgVp3hTcSj3d
92
92
  agno/document/reader/firecrawl_reader.py,sha256=4CFACPA22t0gI1YgYL1mZLZQN_T6LrLj8V9mAmU2In8,5748
93
93
  agno/document/reader/json_reader.py,sha256=TrE14YAPkEd3q1e1dFf1ZX-GJPlXadsbeCzNh6EGpgg,2189
94
94
  agno/document/reader/markdown_reader.py,sha256=SX0Zydj_0AmBr8Grk8z9jDiNmebU7rnPg2aAJoFEHAk,3546
95
- agno/document/reader/pdf_reader.py,sha256=UQpKO41JtmA1lZaVe4gyZERsxglcZWK2qlnpYNp1_fI,11253
95
+ agno/document/reader/pdf_reader.py,sha256=KgY_V7dI0XC-hoYIKYg_nGszzsthWF58ejocegkY-1I,17269
96
96
  agno/document/reader/text_reader.py,sha256=jtCuhWHkC5QNPmRF3DyXcXVKHM1jnqqxXVxHKZIpkfQ,3315
97
97
  agno/document/reader/url_reader.py,sha256=dQmuO1NkJg9A9m2YeiVMuR7GikLl_K1FudE0UO62Um4,1988
98
98
  agno/document/reader/website_reader.py,sha256=Ei-FIsdw0FWhGOBVge24JcNMfiAHL8nUA1tawrfBZPM,17307
@@ -291,8 +291,8 @@ agno/reranker/sentence_transformer.py,sha256=Pqv38uZ5MhwYPQkei8ixs3jANoBOIKqVwI3
291
291
  agno/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
292
292
  agno/run/base.py,sha256=cueGFKz_TOCNO9aMe8IBFwohax4z6IgL0e3HAK7XqRQ,7546
293
293
  agno/run/messages.py,sha256=rAC4CLW-xBA6qFS1BOvcjJ9j_qYf0a7sX1mcdY04zMU,1126
294
- agno/run/response.py,sha256=4Y1VPfRVIQdhacqW_N41RaM_y0RoDvJ-D32cACv2Tvo,15348
295
- agno/run/team.py,sha256=3VSdOrlGhRu_zJ9pE623eSqb0MDZiYSZ43GBAl0juQw,16075
294
+ agno/run/response.py,sha256=CafLgeLszVbdre7jm1A5TNG4WMpN0OiPMPk6ioeN-cg,15766
295
+ agno/run/team.py,sha256=WT5HbqVA1fvKPQg8gr2tpdeaNq2aM3WoWpas93P07ck,16504
296
296
  agno/run/workflow.py,sha256=2eBvNXbGini3-cWwl3dOf-o-VgGq51ZEl4GCbQFblaw,1581
297
297
  agno/run/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
298
298
  agno/run/v2/workflow.py,sha256=FiZY5eWl6oEJCEfBUIX2q1WDHLI8mqVmaWWwKBA1PL8,18903
@@ -328,14 +328,14 @@ agno/storage/workflow/mongodb.py,sha256=x-0Jl2WovupTfwuVNOSndE9-7V4U7BBIjejtJ1Wa
328
328
  agno/storage/workflow/postgres.py,sha256=66bvx6eT7PtFvd4EtTCfI2smynAyvpjvAPYtPo-PCNg,91
329
329
  agno/storage/workflow/sqlite.py,sha256=PLqEA1YC8AtIklINr6wy8lzK6KABEqvlJW-nz5KacWM,85
330
330
  agno/team/__init__.py,sha256=OSkwJhm4uSoOwpHLeDdcH4q2R_BmfS-7a9_aPxB-Skw,967
331
- agno/team/team.py,sha256=XmA2r9LAqgYVGINIj0hanfJH-iFtfY4Bn4pRl7TK82s,376693
331
+ agno/team/team.py,sha256=9rZuwihREck6SU6-IZQMOOjZfzxGeQVulsYTMGtb4Rw,377428
332
332
  agno/tools/__init__.py,sha256=jNll2sELhPPbqm5nPeT4_uyzRO2_KRTW-8Or60kioS0,210
333
333
  agno/tools/agentql.py,sha256=w6FlCfhuS0cc2BHa9K6dZjqO1ycA66fSZbR_nvXiVSo,3813
334
334
  agno/tools/airflow.py,sha256=2ZCwx65w_tSXm4xEzZQR_teOiXJlnEgIqU9AgQTQemI,2493
335
335
  agno/tools/api.py,sha256=gd86Fvk_3kHcaNWepTQ_Bmzs9RwajwiwcsEHJKdngB8,4238
336
336
  agno/tools/apify.py,sha256=WWs9NWiiyMW5jKRRYLDHBoPPX4ScGMMt87VCXHowTy0,13555
337
337
  agno/tools/arxiv.py,sha256=xnfbzOBDuHRj5SHImlz_q-pdRT9o_Vnna-VtDt1JuDU,5202
338
- agno/tools/aws_lambda.py,sha256=GL4McjdbzveteVzeYHKHcwTCLA_B6DJmK4J5tLSNfFk,1459
338
+ agno/tools/aws_lambda.py,sha256=CbVQpoLudpmle1KkgecuVDqE_JdapBBf26OdIj8fUsY,1831
339
339
  agno/tools/aws_ses.py,sha256=fl5NTRWcljzxp4WxTg2gIAjLMNcuEWs_vnjeRtEKRHY,2090
340
340
  agno/tools/baidusearch.py,sha256=HBdhLz1HUtKXJjIQru21jKiSonG9jEjNB_W6FPjklew,2883
341
341
  agno/tools/bitbucket.py,sha256=CLhYdB_HTriy44VHtsRtqCY-BhnHpZMBHQ9vgL4vpWg,11207
@@ -511,14 +511,14 @@ agno/vectordb/clickhouse/index.py,sha256=_YW-8AuEYy5kzOHi0zIzjngpQPgJOBdSrn9BfEL
511
511
  agno/vectordb/couchbase/__init__.py,sha256=dKZkcQLFN4r2_NIdXby4inzAAn4BDMlb9T2BW_i0_gQ,93
512
512
  agno/vectordb/couchbase/couchbase.py,sha256=I-nWRPAEyEoww-1FpQJLM6CyxWXWPtR9dRjpAyzLWyo,48814
513
513
  agno/vectordb/lancedb/__init__.py,sha256=tb9qvinKyWMTLjJYMwW_lhYHFvrfWTfHODtBfMj-NLE,111
514
- agno/vectordb/lancedb/lance_db.py,sha256=0zKFBJ-RNx-BBxmHfKxzeird6JrYxM2beJd5ySOqmpg,22511
514
+ agno/vectordb/lancedb/lance_db.py,sha256=PBLtn0AkAVEEYJywYKzdf5gWbQWvka6yJXeAfsJQdtI,22886
515
515
  agno/vectordb/milvus/__init__.py,sha256=I9V-Rm-rIYxWdRVIs6bKI-6JSJsyOd1-vvasvVpYHuE,127
516
516
  agno/vectordb/milvus/milvus.py,sha256=SwLPmgP80dPWFeN9ehj3D1yseyVXbHJDIBNCu-NPs9s,30002
517
517
  agno/vectordb/mongodb/__init__.py,sha256=yYwaWdxZRnFTd87Hfgs8_DO4QxcJxy1iL3__bnxP71I,73
518
518
  agno/vectordb/mongodb/mongodb.py,sha256=yG_bs7iJrXatrmI2BMLALxkVT1qVx4BYoiUcXWXlSsg,48537
519
519
  agno/vectordb/pgvector/__init__.py,sha256=Lui0HBzoHPIsKh5QuiT0eyTvYW88nQPfd_723jjHFCk,288
520
520
  agno/vectordb/pgvector/index.py,sha256=qfGgPP33SwZkXLfUcAC_XgQsyZIyggpGS2bfIkjjs-E,495
521
- agno/vectordb/pgvector/pgvector.py,sha256=IRtsuHknq5OyHK81QRXl_LMJsBmfbPS2cWob6jRkvrU,44181
521
+ agno/vectordb/pgvector/pgvector.py,sha256=JE52MgXSXRqbySPMzgy1DcvJxZhabiw1ufFHf6QImWA,44305
522
522
  agno/vectordb/pineconedb/__init__.py,sha256=D7iThXtUCxNO0Nyjunv5Z91Jc1vHG1pgAFXthqD1I_w,92
523
523
  agno/vectordb/pineconedb/pineconedb.py,sha256=NaYmkskY1eLAWIM-HSBa-DVHEMQ6ZuZDqqUmmHZhlro,18399
524
524
  agno/vectordb/qdrant/__init__.py,sha256=x1ReQt79f9aI_T4JUWb36KNFnvdd-kVwZ1sLsU4sW7Q,76
@@ -550,9 +550,9 @@ agno/workspace/enums.py,sha256=MxF1CUMXBaZMTKLEfiR-7kEhTki2Gfz6W7u49RdYYaE,123
550
550
  agno/workspace/helpers.py,sha256=Mp-VlRsPVhW10CfDWYVhc9ANLk9RjNurDfCgXmycZCg,2066
551
551
  agno/workspace/operator.py,sha256=CNLwVR45eE5dSRjto2o0c9NgCi2xD-JZR5uLt9kfIt8,30758
552
552
  agno/workspace/settings.py,sha256=bcyHHN7lH1LPSMt4i_20XpTjZLoNXdzwyW-G9nHYV40,5703
553
- agno-1.7.8.dist-info/licenses/LICENSE,sha256=m2rfTWFUfIwCaQqgT2WeBjuKzMKEJRwnaiofg9n8MsQ,16751
554
- agno-1.7.8.dist-info/METADATA,sha256=Y5HQs_UEHkulA08VeiMi7w6cTig4B-C2DykiMQETwb4,44277
555
- agno-1.7.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
556
- agno-1.7.8.dist-info/entry_points.txt,sha256=Be-iPnPVabMohESsuUdV5w6IAYEIlpc2emJZbyNnfGI,88
557
- agno-1.7.8.dist-info/top_level.txt,sha256=MKyeuVesTyOKIXUhc-d_tPa2Hrh0oTA4LM0izowpx70,5
558
- agno-1.7.8.dist-info/RECORD,,
553
+ agno-1.7.9.dist-info/licenses/LICENSE,sha256=m2rfTWFUfIwCaQqgT2WeBjuKzMKEJRwnaiofg9n8MsQ,16751
554
+ agno-1.7.9.dist-info/METADATA,sha256=F9YGHpMsxVonyK77LMJ7vPXygE1klxiUtekDCh58O6g,44277
555
+ agno-1.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
556
+ agno-1.7.9.dist-info/entry_points.txt,sha256=Be-iPnPVabMohESsuUdV5w6IAYEIlpc2emJZbyNnfGI,88
557
+ agno-1.7.9.dist-info/top_level.txt,sha256=MKyeuVesTyOKIXUhc-d_tPa2Hrh0oTA4LM0izowpx70,5
558
+ agno-1.7.9.dist-info/RECORD,,
File without changes