agno 2.4.5__py3-none-any.whl → 2.4.7__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
@@ -858,8 +858,9 @@ class Agent:
858
858
  return
859
859
 
860
860
  # Handle learning=True: create default LearningMachine
861
+ # Enables user_profile (structured fields) and user_memory (unstructured observations)
861
862
  if self.learning is True:
862
- self._learning = LearningMachine(db=self.db, model=self.model, user_profile=True)
863
+ self._learning = LearningMachine(db=self.db, model=self.model, user_profile=True, user_memory=True)
863
864
  return
864
865
 
865
866
  # Handle learning=LearningMachine(...): inject dependencies
@@ -173,8 +173,7 @@ class SingleStoreDb(BaseDb):
173
173
  column_kwargs["unique"] = True
174
174
  columns.append(Column(*column_args, **column_kwargs))
175
175
 
176
- # Create the table object without constraints to avoid autoload issues
177
- table = Table(table_name, self.metadata, *columns, schema=self.db_schema)
176
+ table = Table(table_name, self.metadata, *columns, schema=self.db_schema, extend_existing=True)
178
177
 
179
178
  return table
180
179
 
@@ -240,8 +239,7 @@ class SingleStoreDb(BaseDb):
240
239
 
241
240
  columns.append(Column(*column_args, **column_kwargs))
242
241
 
243
- # Create the table object
244
- table = Table(table_name, self.metadata, *columns, schema=self.db_schema)
242
+ table = Table(table_name, self.metadata, *columns, schema=self.db_schema, extend_existing=True)
245
243
 
246
244
  # Add multi-column unique constraints with table-specific names
247
245
  for constraint in schema_unique_constraints:
@@ -396,7 +394,8 @@ class SingleStoreDb(BaseDb):
396
394
 
397
395
  if table_type == "spans":
398
396
  # Ensure traces table exists first (for foreign key)
399
- self._get_table(table_type="traces", create_table_if_not_found=create_table_if_not_found)
397
+ if create_table_if_not_found:
398
+ self._get_table(table_type="traces", create_table_if_not_found=True)
400
399
  self.spans_table = self._get_or_create_table(
401
400
  table_name=self.span_table_name,
402
401
  table_type="spans",
@@ -48,7 +48,7 @@ def surrealize_dates(record: dict) -> dict:
48
48
  if isinstance(value, date):
49
49
  copy[key] = datetime.combine(value, datetime.min.time()).replace(tzinfo=timezone.utc)
50
50
  elif key in ["created_at", "updated_at"] and isinstance(value, (int, float)):
51
- copy[key] = datetime.fromtimestamp(value).replace(tzinfo=timezone.utc)
51
+ copy[key] = datetime.fromtimestamp(value, tz=timezone.utc)
52
52
  elif key in ["created_at", "updated_at"] and isinstance(value, str):
53
53
  # Handle ISO string format - convert back to datetime object for SurrealDB
54
54
  try:
@@ -55,11 +55,7 @@ class AgenticChunking(ChunkingStrategy):
55
55
  chunk = remaining_text[:break_point].strip()
56
56
  meta_data = chunk_meta_data.copy()
57
57
  meta_data["chunk"] = chunk_number
58
- chunk_id = None
59
- if document.id:
60
- chunk_id = f"{document.id}_{chunk_number}"
61
- elif document.name:
62
- chunk_id = f"{document.name}_{chunk_number}"
58
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk)
63
59
  meta_data["chunk_size"] = len(chunk)
64
60
  chunks.append(
65
61
  Document(
@@ -82,7 +82,7 @@ class CodeChunking(ChunkingStrategy):
82
82
  for i, chunk in enumerate(chunks, 1):
83
83
  meta_data = document.meta_data.copy()
84
84
  meta_data["chunk"] = i
85
- chunk_id = f"{document.id}_{i}" if document.id else None
85
+ chunk_id = self._generate_chunk_id(document, i, chunk.text)
86
86
  meta_data["chunk_size"] = len(chunk.text)
87
87
 
88
88
  chunked_documents.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk.text))
@@ -38,17 +38,10 @@ class DocumentChunking(ChunkingStrategy):
38
38
  if current_chunk:
39
39
  meta_data = chunk_meta_data.copy()
40
40
  meta_data["chunk"] = chunk_number
41
- chunk_id = None
42
- if document.id:
43
- chunk_id = f"{document.id}_{chunk_number}"
44
- elif document.name:
45
- chunk_id = f"{document.name}_{chunk_number}"
46
- meta_data["chunk_size"] = len("\n\n".join(current_chunk))
47
- chunks.append(
48
- Document(
49
- id=chunk_id, name=document.name, meta_data=meta_data, content="\n\n".join(current_chunk)
50
- )
51
- )
41
+ chunk_content = "\n\n".join(current_chunk)
42
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
43
+ meta_data["chunk_size"] = len(chunk_content)
44
+ chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
52
45
  chunk_number += 1
53
46
  current_chunk = []
54
47
  current_size = 0
@@ -70,18 +63,15 @@ class DocumentChunking(ChunkingStrategy):
70
63
  if current_chunk:
71
64
  meta_data = chunk_meta_data.copy()
72
65
  meta_data["chunk"] = chunk_number
73
- chunk_id = None
74
- if document.id:
75
- chunk_id = f"{document.id}_{chunk_number}"
76
- elif document.name:
77
- chunk_id = f"{document.name}_{chunk_number}"
78
- meta_data["chunk_size"] = len(" ".join(current_chunk))
66
+ chunk_content = " ".join(current_chunk)
67
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
68
+ meta_data["chunk_size"] = len(chunk_content)
79
69
  chunks.append(
80
70
  Document(
81
71
  id=chunk_id,
82
72
  name=document.name,
83
73
  meta_data=meta_data,
84
- content=" ".join(current_chunk),
74
+ content=chunk_content,
85
75
  )
86
76
  )
87
77
  chunk_number += 1
@@ -94,18 +84,11 @@ class DocumentChunking(ChunkingStrategy):
94
84
  else:
95
85
  meta_data = chunk_meta_data.copy()
96
86
  meta_data["chunk"] = chunk_number
97
- chunk_id = None
98
- if document.id:
99
- chunk_id = f"{document.id}_{chunk_number}"
100
- elif document.name:
101
- chunk_id = f"{document.name}_{chunk_number}"
102
- meta_data["chunk_size"] = len("\n\n".join(current_chunk))
87
+ chunk_content = "\n\n".join(current_chunk)
88
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
89
+ meta_data["chunk_size"] = len(chunk_content)
103
90
  if current_chunk:
104
- chunks.append(
105
- Document(
106
- id=chunk_id, name=document.name, meta_data=meta_data, content="\n\n".join(current_chunk)
107
- )
108
- )
91
+ chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
109
92
  chunk_number += 1
110
93
  current_chunk = [para]
111
94
  current_size = para_size
@@ -113,15 +96,10 @@ class DocumentChunking(ChunkingStrategy):
113
96
  if current_chunk:
114
97
  meta_data = chunk_meta_data.copy()
115
98
  meta_data["chunk"] = chunk_number
116
- chunk_id = None
117
- if document.id:
118
- chunk_id = f"{document.id}_{chunk_number}"
119
- elif document.name:
120
- chunk_id = f"{document.name}_{chunk_number}"
121
- meta_data["chunk_size"] = len("\n\n".join(current_chunk))
122
- chunks.append(
123
- Document(id=chunk_id, name=document.name, meta_data=meta_data, content="\n\n".join(current_chunk))
124
- )
99
+ chunk_content = "\n\n".join(current_chunk)
100
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
101
+ meta_data["chunk_size"] = len(chunk_content)
102
+ chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
125
103
 
126
104
  # Handle overlap if specified
127
105
  if self.overlap > 0:
@@ -131,11 +109,11 @@ class DocumentChunking(ChunkingStrategy):
131
109
  # Add overlap from previous chunk
132
110
  prev_text = chunks[i - 1].content[-self.overlap :]
133
111
  meta_data = chunk_meta_data.copy()
134
- meta_data["chunk"] = chunk_number
135
- chunk_id = None
136
- if document.id:
137
- chunk_id = f"{document.id}_{chunk_number}"
112
+ # Use the chunk's existing metadata and ID instead of stale chunk_number
113
+ meta_data["chunk"] = chunks[i].meta_data["chunk"]
114
+ chunk_id = chunks[i].id
138
115
  meta_data["chunk_size"] = len(prev_text + chunks[i].content)
116
+
139
117
  if prev_text:
140
118
  overlapped_chunks.append(
141
119
  Document(
@@ -145,6 +123,8 @@ class DocumentChunking(ChunkingStrategy):
145
123
  content=prev_text + chunks[i].content,
146
124
  )
147
125
  )
126
+ else:
127
+ overlapped_chunks.append(chunks[i])
148
128
  else:
149
129
  overlapped_chunks.append(chunks[i])
150
130
  chunks = overlapped_chunks
@@ -38,11 +38,7 @@ class FixedSizeChunking(ChunkingStrategy):
38
38
  chunk = content[start:end]
39
39
  meta_data = chunk_meta_data.copy()
40
40
  meta_data["chunk"] = chunk_number
41
- chunk_id = None
42
- if document.id:
43
- chunk_id = f"{document.id}_{chunk_number}"
44
- elif document.name:
45
- chunk_id = f"{document.name}_{chunk_number}"
41
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk)
46
42
  meta_data["chunk_size"] = len(chunk)
47
43
  chunked_documents.append(
48
44
  Document(
@@ -267,11 +267,7 @@ class MarkdownChunking(ChunkingStrategy):
267
267
  for sub_chunk in sub_chunks:
268
268
  meta_data = chunk_meta_data.copy()
269
269
  meta_data["chunk"] = chunk_number
270
- chunk_id = None
271
- if document.id:
272
- chunk_id = f"{document.id}_{chunk_number}"
273
- elif document.name:
274
- chunk_id = f"{document.name}_{chunk_number}"
270
+ chunk_id = self._generate_chunk_id(document, chunk_number, sub_chunk)
275
271
  meta_data["chunk_size"] = len(sub_chunk)
276
272
 
277
273
  chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=sub_chunk))
@@ -282,19 +278,12 @@ class MarkdownChunking(ChunkingStrategy):
282
278
  else:
283
279
  meta_data = chunk_meta_data.copy()
284
280
  meta_data["chunk"] = chunk_number
285
- chunk_id = None
286
- if document.id:
287
- chunk_id = f"{document.id}_{chunk_number}"
288
- elif document.name:
289
- chunk_id = f"{document.name}_{chunk_number}"
290
- meta_data["chunk_size"] = len("\n\n".join(current_chunk))
281
+ chunk_content = "\n\n".join(current_chunk)
282
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
283
+ meta_data["chunk_size"] = len(chunk_content)
291
284
 
292
285
  if current_chunk:
293
- chunks.append(
294
- Document(
295
- id=chunk_id, name=document.name, meta_data=meta_data, content="\n\n".join(current_chunk)
296
- )
297
- )
286
+ chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
298
287
  chunk_number += 1
299
288
 
300
289
  current_chunk = [section]
@@ -304,15 +293,10 @@ class MarkdownChunking(ChunkingStrategy):
304
293
  if current_chunk and not self.split_on_headings:
305
294
  meta_data = chunk_meta_data.copy()
306
295
  meta_data["chunk"] = chunk_number
307
- chunk_id = None
308
- if document.id:
309
- chunk_id = f"{document.id}_{chunk_number}"
310
- elif document.name:
311
- chunk_id = f"{document.name}_{chunk_number}"
312
- meta_data["chunk_size"] = len("\n\n".join(current_chunk))
313
- chunks.append(
314
- Document(id=chunk_id, name=document.name, meta_data=meta_data, content="\n\n".join(current_chunk))
315
- )
296
+ chunk_content = "\n\n".join(current_chunk)
297
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk_content)
298
+ meta_data["chunk_size"] = len(chunk_content)
299
+ chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
316
300
 
317
301
  # Handle overlap if specified
318
302
  if self.overlap > 0:
@@ -46,9 +46,7 @@ class RecursiveChunking(ChunkingStrategy):
46
46
  chunk = self.clean_text(content[start:end])
47
47
  meta_data = chunk_meta_data.copy()
48
48
  meta_data["chunk"] = chunk_number
49
- chunk_id = None
50
- if document.id:
51
- chunk_id = f"{document.id}_{chunk_number}"
49
+ chunk_id = self._generate_chunk_id(document, chunk_number, chunk)
52
50
  chunk_number += 1
53
51
  meta_data["chunk_size"] = len(chunk)
54
52
  chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk))
@@ -33,7 +33,8 @@ class RowChunking(ChunkingStrategy):
33
33
 
34
34
  if chunk_content: # Skip empty rows
35
35
  meta_data = document.meta_data.copy()
36
- meta_data["row_number"] = start_index + i # Preserve logical row numbering
37
- chunk_id = f"{document.id}_row_{start_index + i}" if document.id else None
36
+ row_number = start_index + i
37
+ meta_data["row_number"] = row_number # Preserve logical row numbering
38
+ chunk_id = self._generate_chunk_id(document, row_number, chunk_content, prefix="row")
38
39
  chunks.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk_content))
39
40
  return chunks
@@ -160,7 +160,7 @@ class SemanticChunking(ChunkingStrategy):
160
160
  for i, chunk in enumerate(chunks, 1):
161
161
  meta_data = document.meta_data.copy()
162
162
  meta_data["chunk"] = i
163
- chunk_id = f"{document.id}_{i}" if document.id else None
163
+ chunk_id = self._generate_chunk_id(document, i, chunk.text)
164
164
  meta_data["chunk_size"] = len(chunk.text)
165
165
 
166
166
  chunked_documents.append(Document(id=chunk_id, name=document.name, meta_data=meta_data, content=chunk.text))
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  from abc import ABC, abstractmethod
2
3
  from enum import Enum
3
4
  from typing import List, Optional
@@ -12,6 +13,24 @@ class ChunkingStrategy(ABC):
12
13
  def chunk(self, document: Document) -> List[Document]:
13
14
  raise NotImplementedError
14
15
 
16
+ def _generate_chunk_id(
17
+ self, document: Document, chunk_number: int, content: Optional[str] = None, prefix: Optional[str] = None
18
+ ) -> Optional[str]:
19
+ """Generate a deterministic ID for the chunk."""
20
+ suffix = f"_{prefix}_{chunk_number}" if prefix else f"_{chunk_number}"
21
+
22
+ if document.id:
23
+ return f"{document.id}{suffix}"
24
+ elif document.name:
25
+ return f"{document.name}{suffix}"
26
+ else:
27
+ # Hash the chunk content for a deterministic ID when no identifier exists
28
+ hash_source = content if content else document.content
29
+ if hash_source:
30
+ content_hash = hashlib.md5(hash_source.encode("utf-8")).hexdigest()[:12] # nosec B324
31
+ return f"chunk_{content_hash}{suffix}"
32
+ return None
33
+
15
34
  async def achunk(self, document: Document) -> List[Document]:
16
35
  """Async version of chunk. Override for truly async implementations."""
17
36
  return self.chunk(document)