mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Merge pull request #434 from Hawksight-AI/utils
Utilsfix: add_decision kwargs support and quickstart VectorStore backend
This commit is contained in:
@@ -354,7 +354,7 @@ from semantica.context import AgentContext, AgentMemory
|
||||
from semantica.vector_store import VectorStore
|
||||
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
vector_store=VectorStore(backend="inmemory"),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
decision_tracking=True,
|
||||
graph_expansion=True,
|
||||
|
||||
@@ -44,7 +44,7 @@ from semantica.context import AgentContext, ContextGraph
|
||||
from semantica.vector_store import VectorStore
|
||||
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
vector_store=VectorStore(backend="inmemory"),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
decision_tracking=True,
|
||||
)
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ from semantica.context import AgentContext, ContextGraph
|
||||
from semantica.vector_store import VectorStore
|
||||
|
||||
context = AgentContext(
|
||||
vector_store=VectorStore(backend="faiss", dimension=768),
|
||||
vector_store=VectorStore(backend="inmemory"),
|
||||
knowledge_graph=ContextGraph(advanced_analytics=True),
|
||||
decision_tracking=True,
|
||||
)
|
||||
|
||||
@@ -275,7 +275,7 @@ print(f"Python importance score: {importance.get('degree', 0)}")
|
||||
|--------|-------------|------------|
|
||||
| `add_node(node_id, node_type, properties)` | Add concepts to remember | Build knowledge base |
|
||||
| `add_edge(source, target, relation)` | Connect related concepts | Show relationships |
|
||||
| `add_decision(category, scenario, reasoning, outcome, confidence, ...)` | Record decisions | Track choices and learn |
|
||||
| `add_decision(decision)` or `add_decision(category, scenario, reasoning, outcome, ...)` | Record decisions | Track choices and learn |
|
||||
| `add_decision_simple(category, scenario, reasoning, outcome, confidence, ...)` | Easy decision recording | Quick decision tracking |
|
||||
| `find_precedents(decision_id, limit)` | Find precedents by ID | Get connected decisions |
|
||||
| `find_precedents_by_scenario(scenario, category, ...)` | Find similar decisions | Make consistent choices |
|
||||
|
||||
@@ -1478,25 +1478,85 @@ class ContextGraph:
|
||||
}
|
||||
|
||||
# Decision Support Methods
|
||||
def add_decision(self, decision: "Decision") -> None:
|
||||
def add_decision(
|
||||
self,
|
||||
decision: "Decision" = None,
|
||||
*,
|
||||
category: str = None,
|
||||
scenario: str = None,
|
||||
reasoning: str = None,
|
||||
outcome: str = None,
|
||||
confidence: float = 0.5,
|
||||
entities: Optional[List[str]] = None,
|
||||
decision_maker: Optional[str] = "system",
|
||||
valid_from=None,
|
||||
valid_until=None,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""
|
||||
Add decision node to graph.
|
||||
|
||||
|
||||
Accepts either a Decision object or keyword arguments:
|
||||
|
||||
# From a Decision object
|
||||
graph.add_decision(Decision(category="x", scenario="y", ...))
|
||||
|
||||
# From keyword arguments (convenience form)
|
||||
graph.add_decision(category="x", scenario="y", reasoning="z",
|
||||
outcome="o", confidence=0.9)
|
||||
|
||||
Args:
|
||||
decision: Decision object to add
|
||||
decision: Decision object to add (mutually exclusive with kwargs)
|
||||
category: Decision category
|
||||
scenario: Decision scenario description
|
||||
reasoning: Reasoning behind the decision
|
||||
outcome: Decision outcome
|
||||
confidence: Confidence score (0.0–1.0)
|
||||
entities: Related entity labels
|
||||
decision_maker: Who made the decision
|
||||
valid_from: Start of validity window (ISO string or datetime)
|
||||
valid_until: End of validity window (ISO string or datetime)
|
||||
**kwargs: Extra metadata stored on the decision node
|
||||
|
||||
Returns:
|
||||
Decision ID
|
||||
"""
|
||||
from .decision_models import Decision
|
||||
|
||||
|
||||
if decision is not None and (
|
||||
any(v is not None for v in (
|
||||
category, scenario, reasoning, outcome, entities, valid_from, valid_until,
|
||||
)) or kwargs
|
||||
):
|
||||
raise ValueError(
|
||||
"Pass either a Decision object or keyword arguments, not both."
|
||||
)
|
||||
|
||||
if decision is None:
|
||||
# Build from kwargs — delegate to record_decision which handles ID gen
|
||||
return self.record_decision(
|
||||
category=category,
|
||||
scenario=scenario,
|
||||
reasoning=reasoning,
|
||||
outcome=outcome,
|
||||
confidence=confidence,
|
||||
entities=entities,
|
||||
decision_maker=decision_maker,
|
||||
valid_from=valid_from,
|
||||
valid_until=valid_until,
|
||||
metadata=kwargs,
|
||||
)
|
||||
|
||||
# Handle empty decision ID by generating UUID for both None and empty string
|
||||
# This ensures consistent behavior with Decision model's __post_init__ method
|
||||
node_id = decision.decision_id if decision.decision_id else str(uuid.uuid4())
|
||||
|
||||
|
||||
# Handle None metadata
|
||||
metadata = decision.metadata or {}
|
||||
|
||||
|
||||
# Normalize timestamp to ensure consistent storage format
|
||||
normalized_timestamp = self._normalize_timestamp(decision.timestamp)
|
||||
|
||||
|
||||
node = ContextNode(
|
||||
node_id=node_id,
|
||||
node_type="Decision",
|
||||
@@ -1516,6 +1576,7 @@ class ContextGraph:
|
||||
valid_until=decision.valid_until,
|
||||
)
|
||||
self._add_internal_node(node)
|
||||
return node_id
|
||||
|
||||
def add_causal_relationship(
|
||||
self,
|
||||
|
||||
@@ -39,6 +39,24 @@ def test_agent_context_minimal_decisions_and_chain():
|
||||
assert len(chain) >= 1
|
||||
|
||||
|
||||
def test_agent_context_inmemory_store_and_retrieve():
|
||||
"""VectorStore(backend="inmemory") stores memories without faiss-cpu."""
|
||||
vs = VectorStore(backend="inmemory")
|
||||
ctx = AgentContext(
|
||||
vector_store=vs,
|
||||
knowledge_graph=ContextGraph(),
|
||||
decision_tracking=True,
|
||||
kg_algorithms=False,
|
||||
vector_store_features=False,
|
||||
)
|
||||
memory_id = ctx.store(
|
||||
"GPT-4 outperforms GPT-3.5 on reasoning benchmarks by 40%",
|
||||
conversation_id="test_session",
|
||||
)
|
||||
assert isinstance(memory_id, str)
|
||||
assert len(memory_id) > 0
|
||||
|
||||
|
||||
def test_agent_context_policy_engine_with_graph_backend():
|
||||
vs = VectorStore(backend="inmemory", dimension=64)
|
||||
graph = ContextGraph()
|
||||
|
||||
@@ -49,6 +49,38 @@ class TestContextGraphDecisions:
|
||||
assert node.properties["confidence"] == sample_decision.confidence
|
||||
assert node.properties["decision_maker"] == sample_decision.decision_maker
|
||||
|
||||
def test_add_decision_kwargs_form(self, context_graph):
|
||||
"""add_decision() accepts kwargs directly (no Decision object required)."""
|
||||
decision_id = context_graph.add_decision(
|
||||
category="loan_approval",
|
||||
scenario="Mortgage application — 780 credit score",
|
||||
reasoning="Strong credit history, low DTI",
|
||||
outcome="approved",
|
||||
confidence=0.95,
|
||||
)
|
||||
|
||||
assert isinstance(decision_id, str)
|
||||
assert len(decision_id) > 0
|
||||
node = context_graph.nodes[decision_id]
|
||||
assert node.node_type in ("Decision", "decision")
|
||||
assert node.properties["category"] == "loan_approval"
|
||||
assert node.properties["outcome"] == "approved"
|
||||
assert node.properties["confidence"] == 0.95
|
||||
|
||||
def test_add_decision_kwargs_and_object_both_return_id(self, context_graph, sample_decision):
|
||||
"""Both call forms return a non-empty decision ID string."""
|
||||
id_from_object = context_graph.add_decision(sample_decision)
|
||||
id_from_kwargs = context_graph.add_decision(
|
||||
category="test",
|
||||
scenario="test scenario",
|
||||
reasoning="test reasoning",
|
||||
outcome="approved",
|
||||
confidence=0.8,
|
||||
)
|
||||
|
||||
assert isinstance(id_from_object, str) and len(id_from_object) > 0
|
||||
assert isinstance(id_from_kwargs, str) and len(id_from_kwargs) > 0
|
||||
|
||||
def test_add_decision_with_embeddings(self, context_graph):
|
||||
"""Test adding decision with embeddings."""
|
||||
decision = Decision(
|
||||
|
||||
@@ -1062,6 +1062,8 @@ class TestFindPrecedentsAsOf:
|
||||
# Bob's decision should be reachable; Alice's should not appear
|
||||
# (implementation may not filter on valid_from, just check it doesn't crash)
|
||||
assert isinstance(precedents, list)
|
||||
assert "approve loan for Bob" in scenarios
|
||||
assert "approve loan for Alice" not in scenarios
|
||||
|
||||
def test_find_precedents_no_as_of_returns_list(self):
|
||||
self.graph.record_decision(
|
||||
|
||||
@@ -873,6 +873,7 @@ class TestOllamaProviderBaseURLGap:
|
||||
ollama_mock.Client = MagicMock(return_value=MagicMock())
|
||||
with patch.dict("sys.modules", {"ollama": ollama_mock}):
|
||||
from semantica.semantic_extract.providers import OllamaProvider
|
||||
OllamaProvider(
|
||||
provider = OllamaProvider(
|
||||
model_name="llama3",
|
||||
base_url="http://192.168.1.10:11434",
|
||||
|
||||
Reference in New Issue
Block a user