agno 2.3.25__py3-none-any.whl → 2.4.0__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/__init__.py +4 -0
- agno/agent/agent.py +1428 -558
- agno/agent/remote.py +13 -0
- agno/db/base.py +339 -0
- agno/db/postgres/async_postgres.py +116 -12
- agno/db/postgres/postgres.py +1229 -25
- agno/db/postgres/schemas.py +48 -1
- agno/db/sqlite/async_sqlite.py +119 -4
- agno/db/sqlite/schemas.py +51 -0
- agno/db/sqlite/sqlite.py +1173 -13
- agno/db/utils.py +37 -1
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +1 -1
- agno/knowledge/chunking/semantic.py +1 -1
- agno/knowledge/chunking/strategy.py +4 -0
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +2767 -2254
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +2 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +5 -5
- agno/knowledge/reader/docx_reader.py +2 -2
- agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
- agno/knowledge/reader/firecrawl_reader.py +2 -2
- agno/knowledge/reader/json_reader.py +2 -2
- agno/knowledge/reader/markdown_reader.py +2 -2
- agno/knowledge/reader/pdf_reader.py +5 -4
- agno/knowledge/reader/pptx_reader.py +2 -2
- agno/knowledge/reader/reader_factory.py +110 -0
- agno/knowledge/reader/s3_reader.py +2 -2
- agno/knowledge/reader/tavily_reader.py +2 -2
- agno/knowledge/reader/text_reader.py +2 -2
- agno/knowledge/reader/web_search_reader.py +2 -2
- agno/knowledge/reader/website_reader.py +5 -3
- agno/knowledge/reader/wikipedia_reader.py +2 -2
- agno/knowledge/reader/youtube_reader.py +2 -2
- agno/knowledge/utils.py +37 -29
- agno/learn/__init__.py +6 -0
- agno/learn/machine.py +35 -0
- agno/learn/schemas.py +82 -11
- agno/learn/stores/__init__.py +3 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/learned_knowledge.py +6 -6
- agno/models/anthropic/claude.py +24 -0
- agno/models/aws/bedrock.py +20 -0
- agno/models/base.py +48 -4
- agno/models/cohere/chat.py +25 -0
- agno/models/google/gemini.py +50 -5
- agno/models/litellm/chat.py +38 -0
- agno/models/openai/chat.py +7 -0
- agno/models/openrouter/openrouter.py +46 -0
- agno/models/response.py +16 -0
- agno/os/app.py +83 -44
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +1 -0
- agno/os/routers/agents/router.py +29 -16
- agno/os/routers/agents/schema.py +6 -4
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +466 -0
- agno/os/routers/evals/schemas.py +4 -3
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +3 -3
- agno/os/routers/memory/schemas.py +4 -2
- agno/os/routers/metrics/metrics.py +9 -11
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/teams/router.py +20 -8
- agno/os/routers/teams/schema.py +6 -4
- agno/os/routers/traces/traces.py +5 -5
- agno/os/routers/workflows/router.py +38 -11
- agno/os/routers/workflows/schema.py +1 -1
- agno/os/schema.py +92 -26
- agno/os/utils.py +133 -16
- agno/reasoning/anthropic.py +2 -2
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +2 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +4 -10
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +2 -2
- agno/reasoning/vertexai.py +2 -2
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/run/agent.py +57 -0
- agno/run/base.py +7 -0
- agno/run/team.py +57 -0
- agno/skills/agent_skills.py +10 -3
- agno/team/__init__.py +3 -1
- agno/team/team.py +1276 -326
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/function.py +35 -83
- agno/tools/knowledge.py +9 -4
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/parallel.py +0 -7
- agno/tools/reasoning.py +30 -23
- agno/tools/tavily.py +4 -1
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +48 -47
- agno/utils/agent.py +42 -5
- agno/utils/events.py +160 -2
- agno/utils/print_response/agent.py +0 -31
- agno/utils/print_response/team.py +0 -2
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/team.py +61 -11
- agno/vectordb/lancedb/lance_db.py +4 -1
- agno/vectordb/mongodb/mongodb.py +1 -1
- agno/vectordb/qdrant/qdrant.py +4 -4
- agno/workflow/__init__.py +3 -1
- agno/workflow/condition.py +0 -21
- agno/workflow/loop.py +0 -21
- agno/workflow/parallel.py +0 -21
- agno/workflow/router.py +0 -21
- agno/workflow/step.py +117 -24
- agno/workflow/steps.py +0 -21
- agno/workflow/workflow.py +625 -63
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/knowledge/utils.py
CHANGED
|
@@ -49,26 +49,28 @@ def _import_class(module_name: str, class_name: str):
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def get_reader_info(reader_key: str) -> Dict:
|
|
52
|
-
"""Get information about a reader without instantiating it.
|
|
53
|
-
|
|
52
|
+
"""Get information about a reader without instantiating it.
|
|
53
|
+
|
|
54
|
+
Uses class methods and static metadata from ReaderFactory to avoid
|
|
55
|
+
the overhead of creating reader instances.
|
|
56
|
+
"""
|
|
54
57
|
try:
|
|
55
|
-
|
|
58
|
+
# Get the reader CLASS without instantiation
|
|
59
|
+
reader_class = ReaderFactory.get_reader_class(reader_key)
|
|
56
60
|
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
reader_class = reader_instance.__class__
|
|
61
|
+
# Get metadata from static registry (no instantiation needed)
|
|
62
|
+
metadata = ReaderFactory.READER_METADATA.get(reader_key, {})
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
# Call class methods directly (no instance needed)
|
|
65
|
+
supported_strategies = reader_class.get_supported_chunking_strategies() # type: ignore[attr-defined]
|
|
66
|
+
supported_content_types = reader_class.get_supported_content_types() # type: ignore[attr-defined]
|
|
63
67
|
|
|
64
68
|
return {
|
|
65
69
|
"id": reader_key,
|
|
66
|
-
"name": ""
|
|
67
|
-
"description":
|
|
68
|
-
"chunking_strategies": [
|
|
69
|
-
|
|
70
|
-
], # Convert enums to string values
|
|
71
|
-
"content_types": [ct.value for ct in supported_content_types], # Convert enums to string values
|
|
70
|
+
"name": metadata.get("name", reader_class.__name__),
|
|
71
|
+
"description": metadata.get("description", f"{reader_class.__name__} reader"),
|
|
72
|
+
"chunking_strategies": [strategy.value for strategy in supported_strategies],
|
|
73
|
+
"content_types": [ct.value for ct in supported_content_types],
|
|
72
74
|
}
|
|
73
75
|
except ImportError as e:
|
|
74
76
|
# Skip readers with missing dependencies
|
|
@@ -98,38 +100,44 @@ def get_reader_info_from_instance(reader: Reader, reader_id: str) -> Dict:
|
|
|
98
100
|
def get_all_readers_info(knowledge_instance: Optional[Any] = None) -> List[Dict]:
|
|
99
101
|
"""Get information about all available readers, including custom readers from a Knowledge instance.
|
|
100
102
|
|
|
103
|
+
Custom readers are added first and take precedence over factory readers with the same ID.
|
|
104
|
+
|
|
101
105
|
Args:
|
|
102
106
|
knowledge_instance: Optional Knowledge instance to include custom readers from.
|
|
103
107
|
|
|
104
108
|
Returns:
|
|
105
|
-
List of reader info dictionaries.
|
|
109
|
+
List of reader info dictionaries (custom readers first, then factory readers).
|
|
106
110
|
"""
|
|
107
111
|
readers_info = []
|
|
108
|
-
|
|
109
|
-
for key in keys:
|
|
110
|
-
try:
|
|
111
|
-
reader_info = get_reader_info(key)
|
|
112
|
-
readers_info.append(reader_info)
|
|
113
|
-
except ValueError as e:
|
|
114
|
-
# Skip readers with missing dependencies or other issues
|
|
115
|
-
# Log the error but don't fail the entire request
|
|
116
|
-
log_debug(f"Skipping reader '{key}': {e}")
|
|
117
|
-
continue
|
|
112
|
+
seen_ids: set = set()
|
|
118
113
|
|
|
119
|
-
# Add custom readers
|
|
114
|
+
# 1. Add custom readers FIRST (they take precedence over factory readers)
|
|
120
115
|
if knowledge_instance is not None:
|
|
121
116
|
custom_readers = knowledge_instance.get_readers()
|
|
122
117
|
if isinstance(custom_readers, dict):
|
|
123
118
|
for reader_id, reader in custom_readers.items():
|
|
124
119
|
try:
|
|
125
120
|
reader_info = get_reader_info_from_instance(reader, reader_id)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
readers_info.append(reader_info)
|
|
121
|
+
readers_info.append(reader_info)
|
|
122
|
+
seen_ids.add(reader_id)
|
|
129
123
|
except ValueError as e:
|
|
130
124
|
log_debug(f"Skipping custom reader '{reader_id}': {e}")
|
|
131
125
|
continue
|
|
132
126
|
|
|
127
|
+
# 2. Add factory readers (skip if custom reader with same ID already exists)
|
|
128
|
+
keys = ReaderFactory.get_all_reader_keys()
|
|
129
|
+
for key in keys:
|
|
130
|
+
if key in seen_ids:
|
|
131
|
+
# Custom reader with this ID already added, skip factory version
|
|
132
|
+
continue
|
|
133
|
+
try:
|
|
134
|
+
reader_info = get_reader_info(key)
|
|
135
|
+
readers_info.append(reader_info)
|
|
136
|
+
except ValueError as e:
|
|
137
|
+
# Skip readers with missing dependencies or other issues
|
|
138
|
+
log_debug(f"Skipping reader '{key}': {e}")
|
|
139
|
+
continue
|
|
140
|
+
|
|
133
141
|
return readers_info
|
|
134
142
|
|
|
135
143
|
|
agno/learn/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ Main Components:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from agno.learn.config import (
|
|
14
|
+
DecisionLogConfig,
|
|
14
15
|
EntityMemoryConfig,
|
|
15
16
|
LearnedKnowledgeConfig,
|
|
16
17
|
LearningMode,
|
|
@@ -21,6 +22,7 @@ from agno.learn.config import (
|
|
|
21
22
|
)
|
|
22
23
|
from agno.learn.machine import LearningMachine
|
|
23
24
|
from agno.learn.schemas import (
|
|
25
|
+
DecisionLog,
|
|
24
26
|
EntityMemory,
|
|
25
27
|
LearnedKnowledge,
|
|
26
28
|
Memories,
|
|
@@ -28,6 +30,7 @@ from agno.learn.schemas import (
|
|
|
28
30
|
UserProfile,
|
|
29
31
|
)
|
|
30
32
|
from agno.learn.stores import (
|
|
33
|
+
DecisionLogStore,
|
|
31
34
|
EntityMemoryStore,
|
|
32
35
|
LearnedKnowledgeStore,
|
|
33
36
|
LearningStore,
|
|
@@ -48,12 +51,14 @@ __all__ = [
|
|
|
48
51
|
"EntityMemoryConfig",
|
|
49
52
|
"SessionContextConfig",
|
|
50
53
|
"LearnedKnowledgeConfig",
|
|
54
|
+
"DecisionLogConfig", # Phase 2
|
|
51
55
|
# Schemas
|
|
52
56
|
"UserProfile",
|
|
53
57
|
"Memories",
|
|
54
58
|
"EntityMemory",
|
|
55
59
|
"SessionContext",
|
|
56
60
|
"LearnedKnowledge",
|
|
61
|
+
"DecisionLog", # Phase 2
|
|
57
62
|
# Stores
|
|
58
63
|
"LearningStore",
|
|
59
64
|
"UserProfileStore",
|
|
@@ -62,4 +67,5 @@ __all__ = [
|
|
|
62
67
|
"SessionContextStore",
|
|
63
68
|
"LearnedKnowledgeStore",
|
|
64
69
|
"EntityMemoryStore",
|
|
70
|
+
"DecisionLogStore", # Phase 2
|
|
65
71
|
]
|
agno/learn/machine.py
CHANGED
|
@@ -17,6 +17,7 @@ from os import getenv
|
|
|
17
17
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
18
18
|
|
|
19
19
|
from agno.learn.config import (
|
|
20
|
+
DecisionLogConfig,
|
|
20
21
|
EntityMemoryConfig,
|
|
21
22
|
LearnedKnowledgeConfig,
|
|
22
23
|
LearningMode,
|
|
@@ -45,6 +46,7 @@ UserMemoryInput = Union[bool, UserMemoryConfig, LearningStore, None]
|
|
|
45
46
|
EntityMemoryInput = Union[bool, EntityMemoryConfig, LearningStore, None]
|
|
46
47
|
SessionContextInput = Union[bool, SessionContextConfig, LearningStore, None]
|
|
47
48
|
LearnedKnowledgeInput = Union[bool, LearnedKnowledgeConfig, LearningStore, None]
|
|
49
|
+
DecisionLogInput = Union[bool, DecisionLogConfig, LearningStore, None]
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
@dataclass
|
|
@@ -80,6 +82,7 @@ class LearningMachine:
|
|
|
80
82
|
session_context: SessionContextInput = False
|
|
81
83
|
entity_memory: EntityMemoryInput = False
|
|
82
84
|
learned_knowledge: LearnedKnowledgeInput = False
|
|
85
|
+
decision_log: DecisionLogInput = False # Phase 2
|
|
83
86
|
|
|
84
87
|
# Namespace for entity_memory and learned_knowledge
|
|
85
88
|
namespace: str = "global"
|
|
@@ -144,6 +147,13 @@ class LearningMachine:
|
|
|
144
147
|
store_type="learned_knowledge",
|
|
145
148
|
)
|
|
146
149
|
|
|
150
|
+
# Decision Log (Phase 2)
|
|
151
|
+
if self.decision_log:
|
|
152
|
+
self._stores["decision_log"] = self._resolve_store(
|
|
153
|
+
input_value=self.decision_log,
|
|
154
|
+
store_type="decision_log",
|
|
155
|
+
)
|
|
156
|
+
|
|
147
157
|
# Custom stores
|
|
148
158
|
if self.custom_stores:
|
|
149
159
|
for name, store in self.custom_stores.items():
|
|
@@ -180,6 +190,8 @@ class LearningMachine:
|
|
|
180
190
|
return self._create_entity_memory_store(config=input_value)
|
|
181
191
|
elif store_type == "learned_knowledge":
|
|
182
192
|
return self._create_learned_knowledge_store(config=input_value)
|
|
193
|
+
elif store_type == "decision_log":
|
|
194
|
+
return self._create_decision_log_store(config=input_value)
|
|
183
195
|
else:
|
|
184
196
|
raise ValueError(f"Unknown store type: {store_type}")
|
|
185
197
|
|
|
@@ -274,6 +286,24 @@ class LearningMachine:
|
|
|
274
286
|
|
|
275
287
|
return LearnedKnowledgeStore(config=config, debug_mode=self.debug_mode)
|
|
276
288
|
|
|
289
|
+
def _create_decision_log_store(self, config: Any) -> LearningStore:
|
|
290
|
+
"""Create DecisionLogStore with resolved config."""
|
|
291
|
+
from agno.learn.stores import DecisionLogStore
|
|
292
|
+
|
|
293
|
+
if isinstance(config, DecisionLogConfig):
|
|
294
|
+
if config.db is None:
|
|
295
|
+
config.db = self.db
|
|
296
|
+
if config.model is None:
|
|
297
|
+
config.model = self.model
|
|
298
|
+
else:
|
|
299
|
+
config = DecisionLogConfig(
|
|
300
|
+
db=self.db,
|
|
301
|
+
model=self.model,
|
|
302
|
+
mode=LearningMode.AGENTIC, # Default to AGENTIC for explicit logging
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return DecisionLogStore(config=config, debug_mode=self.debug_mode)
|
|
306
|
+
|
|
277
307
|
# =========================================================================
|
|
278
308
|
# Store Accessors (Type-Safe)
|
|
279
309
|
# =========================================================================
|
|
@@ -303,6 +333,11 @@ class LearningMachine:
|
|
|
303
333
|
"""Get learned knowledge store if enabled."""
|
|
304
334
|
return self.stores.get("learned_knowledge")
|
|
305
335
|
|
|
336
|
+
@property
|
|
337
|
+
def decision_log_store(self) -> Optional[LearningStore]:
|
|
338
|
+
"""Get decision log store if enabled."""
|
|
339
|
+
return self.stores.get("decision_log")
|
|
340
|
+
|
|
306
341
|
@property
|
|
307
342
|
def was_updated(self) -> bool:
|
|
308
343
|
"""True if any store was updated in the last operation."""
|
agno/learn/schemas.py
CHANGED
|
@@ -893,22 +893,72 @@ class SessionPlanningExtractionResponse:
|
|
|
893
893
|
|
|
894
894
|
|
|
895
895
|
@dataclass
|
|
896
|
-
class
|
|
897
|
-
"""Schema for Decision Logs.
|
|
896
|
+
class DecisionLog:
|
|
897
|
+
"""Schema for Decision Logs.
|
|
898
898
|
|
|
899
899
|
Records decisions made by the agent with reasoning and context.
|
|
900
|
+
Useful for:
|
|
901
|
+
- Auditing agent behavior
|
|
902
|
+
- Learning from past decisions
|
|
903
|
+
- Debugging unexpected outcomes
|
|
904
|
+
- Building feedback loops
|
|
905
|
+
|
|
906
|
+
Example:
|
|
907
|
+
DecisionLog(
|
|
908
|
+
id="dec_abc123",
|
|
909
|
+
decision="Used web search instead of knowledge base",
|
|
910
|
+
reasoning="User asked about current events which require fresh data",
|
|
911
|
+
decision_type="tool_selection",
|
|
912
|
+
context="User query: 'What happened in the news today?'",
|
|
913
|
+
alternatives=["search_knowledge_base", "ask_for_clarification"],
|
|
914
|
+
confidence=0.85,
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
Attributes:
|
|
918
|
+
id: Unique identifier for this decision.
|
|
919
|
+
decision: What was decided (the choice made).
|
|
920
|
+
reasoning: Why this decision was made.
|
|
921
|
+
decision_type: Category of decision (tool_selection, response_style, etc).
|
|
922
|
+
context: The situation that required a decision.
|
|
923
|
+
alternatives: Other options that were considered.
|
|
924
|
+
confidence: How confident the agent was (0.0 to 1.0).
|
|
925
|
+
outcome: What happened as a result (can be updated later).
|
|
926
|
+
outcome_quality: Was the outcome good/bad/neutral.
|
|
927
|
+
tags: Categories for organization.
|
|
928
|
+
session_id: Which session this decision was made in.
|
|
929
|
+
user_id: Which user this decision was for.
|
|
930
|
+
agent_id: Which agent made this decision.
|
|
931
|
+
team_id: Which team context.
|
|
932
|
+
created_at: When the decision was made.
|
|
933
|
+
updated_at: When the outcome was recorded.
|
|
900
934
|
"""
|
|
901
935
|
|
|
936
|
+
id: str
|
|
902
937
|
decision: str
|
|
903
|
-
reasoning: Optional[str] = None
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
938
|
+
reasoning: Optional[str] = field(default=None, metadata={"description": "Why this decision was made"})
|
|
939
|
+
decision_type: Optional[str] = field(
|
|
940
|
+
default=None,
|
|
941
|
+
metadata={"description": "Category: tool_selection, response_style, clarification, escalation, etc"},
|
|
942
|
+
)
|
|
943
|
+
context: Optional[str] = field(default=None, metadata={"description": "The situation that required a decision"})
|
|
944
|
+
alternatives: Optional[List[str]] = field(
|
|
945
|
+
default=None, metadata={"description": "Other options that were considered"}
|
|
946
|
+
)
|
|
947
|
+
confidence: Optional[float] = field(default=None, metadata={"description": "Confidence level 0.0 to 1.0"})
|
|
948
|
+
outcome: Optional[str] = field(default=None, metadata={"description": "What happened as a result"})
|
|
949
|
+
outcome_quality: Optional[str] = field(default=None, metadata={"description": "Was outcome good/bad/neutral"})
|
|
950
|
+
tags: Optional[List[str]] = field(default=None, metadata={"description": "Categories for organization"})
|
|
951
|
+
|
|
952
|
+
# Scope
|
|
953
|
+
session_id: Optional[str] = field(default=None, metadata={"internal": True})
|
|
954
|
+
user_id: Optional[str] = field(default=None, metadata={"internal": True})
|
|
955
|
+
agent_id: Optional[str] = field(default=None, metadata={"internal": True})
|
|
956
|
+
team_id: Optional[str] = field(default=None, metadata={"internal": True})
|
|
957
|
+
created_at: Optional[str] = field(default=None, metadata={"internal": True})
|
|
958
|
+
updated_at: Optional[str] = field(default=None, metadata={"internal": True})
|
|
909
959
|
|
|
910
960
|
@classmethod
|
|
911
|
-
def from_dict(cls, data: Any) -> Optional["
|
|
961
|
+
def from_dict(cls, data: Any) -> Optional["DecisionLog"]:
|
|
912
962
|
"""Parse from dict/JSON, returning None on any failure."""
|
|
913
963
|
if data is None:
|
|
914
964
|
return None
|
|
@@ -921,8 +971,9 @@ class Decision:
|
|
|
921
971
|
log_debug(f"{cls.__name__}.from_dict: _parse_json returned None for data={_truncate_for_log(data)}")
|
|
922
972
|
return None
|
|
923
973
|
|
|
924
|
-
|
|
925
|
-
|
|
974
|
+
# id and decision are required
|
|
975
|
+
if not parsed.get("id") or not parsed.get("decision"):
|
|
976
|
+
log_debug(f"{cls.__name__}.from_dict: missing required fields 'id' or 'decision'")
|
|
926
977
|
return None
|
|
927
978
|
|
|
928
979
|
field_names = {f.name for f in fields(cls)}
|
|
@@ -941,6 +992,26 @@ class Decision:
|
|
|
941
992
|
log_debug(f"{self.__class__.__name__}.to_dict failed: {e}")
|
|
942
993
|
return {}
|
|
943
994
|
|
|
995
|
+
def to_text(self) -> str:
|
|
996
|
+
"""Convert to searchable text format."""
|
|
997
|
+
parts = [f"Decision: {self.decision}"]
|
|
998
|
+
if self.reasoning:
|
|
999
|
+
parts.append(f"Reasoning: {self.reasoning}")
|
|
1000
|
+
if self.context:
|
|
1001
|
+
parts.append(f"Context: {self.context}")
|
|
1002
|
+
if self.decision_type:
|
|
1003
|
+
parts.append(f"Type: {self.decision_type}")
|
|
1004
|
+
if self.outcome:
|
|
1005
|
+
parts.append(f"Outcome: {self.outcome}")
|
|
1006
|
+
return "\n".join(parts)
|
|
1007
|
+
|
|
1008
|
+
def __repr__(self) -> str:
|
|
1009
|
+
return f"DecisionLog(id={self.id}, decision={self.decision[:50]}...)"
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
# Backwards compatibility alias
|
|
1013
|
+
Decision = DecisionLog
|
|
1014
|
+
|
|
944
1015
|
|
|
945
1016
|
@dataclass
|
|
946
1017
|
class Feedback:
|
agno/learn/stores/__init__.py
CHANGED
|
@@ -15,8 +15,10 @@ Available Stores:
|
|
|
15
15
|
- SessionContextStore: Current session state
|
|
16
16
|
- LearnedKnowledgeStore: Reusable knowledge/insights
|
|
17
17
|
- EntityMemoryStore: Third-party entity facts
|
|
18
|
+
- DecisionLogStore: Agent decision logging (Phase 2)
|
|
18
19
|
"""
|
|
19
20
|
|
|
21
|
+
from agno.learn.stores.decision_log import DecisionLogStore
|
|
20
22
|
from agno.learn.stores.entity_memory import EntityMemoryStore
|
|
21
23
|
from agno.learn.stores.learned_knowledge import LearnedKnowledgeStore
|
|
22
24
|
from agno.learn.stores.protocol import LearningStore
|
|
@@ -32,4 +34,5 @@ __all__ = [
|
|
|
32
34
|
"SessionContextStore",
|
|
33
35
|
"LearnedKnowledgeStore",
|
|
34
36
|
"EntityMemoryStore",
|
|
37
|
+
"DecisionLogStore",
|
|
35
38
|
]
|