Coverage for astrocyte/integrations/semantic_kernel.py: 86%
36 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-04 05:24 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-04 05:24 +0000
1"""Microsoft Semantic Kernel integration — Astrocyte as kernel plugins.
3Usage:
4 from astrocyte import Astrocyte
5 from astrocyte.integrations.semantic_kernel import AstrocytePlugin
7 brain = Astrocyte.from_config("astrocyte.yaml")
8 plugin = AstrocytePlugin(brain, bank_id="user-123")
10 # Register with Semantic Kernel
11 kernel = Kernel()
12 kernel.add_plugin(plugin, plugin_name="memory")
14Semantic Kernel uses a plugin/function pattern. Each plugin method decorated
15with @kernel_function becomes a callable function. We emulate this pattern
16with plain methods that follow the same signature conventions.
17"""
19from __future__ import annotations
21from typing import TYPE_CHECKING, Any
23if TYPE_CHECKING:
24 from astrocyte._astrocyte import Astrocyte
26from astrocyte.types import AstrocyteContext
29class AstrocytePlugin:
30 """Astrocyte memory plugin for Microsoft Semantic Kernel.
32 Methods follow the Semantic Kernel @kernel_function pattern:
33 - Named methods with docstrings (used as function descriptions)
34 - Type-annotated parameters (used for schema generation)
35 - Return strings (Semantic Kernel standard for text results)
37 Register with: kernel.add_plugin(plugin, plugin_name="memory")
38 """
40 def __init__(
41 self,
42 brain: Astrocyte,
43 bank_id: str,
44 *,
45 context: AstrocyteContext | None = None,
46 include_reflect: bool = True,
47 include_forget: bool = False,
48 ) -> None:
49 self.brain = brain
50 self.bank_id = bank_id
51 self._context = context
52 self._include_reflect = include_reflect
53 self._include_forget = include_forget
55 async def retain(self, content: str, tags: str = "") -> str:
56 """Store content into long-term memory for future recall.
58 Args:
59 content: The text to memorize.
60 tags: Comma-separated tags for filtering (optional).
61 """
62 tag_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else None
63 result = await self.brain.retain(content, bank_id=self.bank_id, tags=tag_list, context=self._context)
64 if result.stored:
65 return f"Stored memory (id: {result.memory_id})"
66 return f"Failed to store: {result.error}"
68 async def recall(self, query: str, max_results: int = 5) -> str:
69 """Search long-term memory for information relevant to a query.
71 Args:
72 query: Natural language search query.
73 max_results: Maximum number of results to return.
74 """
75 result = await self.brain.recall(query, bank_id=self.bank_id, max_results=max_results, context=self._context)
76 if not result.hits:
77 return "No relevant memories found."
78 lines = [f"- [{h.score:.2f}] {h.text}" for h in result.hits]
79 return f"Found {len(result.hits)} memories:\n" + "\n".join(lines)
81 async def reflect(self, query: str) -> str:
82 """Synthesize a comprehensive answer from long-term memory.
84 Args:
85 query: The question to answer from memory.
86 """
87 result = await self.brain.reflect(query, bank_id=self.bank_id, context=self._context)
88 return result.answer
90 async def forget(self, memory_ids: str) -> str:
91 """Remove specific memories by their IDs.
93 Args:
94 memory_ids: Comma-separated memory IDs to delete.
95 """
96 ids = [mid.strip() for mid in memory_ids.split(",")]
97 result = await self.brain.forget(self.bank_id, memory_ids=ids, context=self._context)
98 return f"Deleted {result.deleted_count} memories."
100 def get_functions(self) -> list[dict[str, Any]]:
101 """Return the list of available functions for registration.
103 Each dict has 'name', 'description', 'function' for Semantic Kernel.
104 """
105 fns: list[dict[str, Any]] = [
106 {"name": "retain", "description": self.retain.__doc__ or "", "function": self.retain},
107 {"name": "recall", "description": self.recall.__doc__ or "", "function": self.recall},
108 ]
109 if self._include_reflect:
110 fns.append({"name": "reflect", "description": self.reflect.__doc__ or "", "function": self.reflect})
111 if self._include_forget:
112 fns.append({"name": "forget", "description": self.forget.__doc__ or "", "function": self.forget})
113 return fns