|
5 | 5 | import json |
6 | 6 | from difflib import SequenceMatcher |
7 | 7 | from time import time |
| 8 | +from typing import TYPE_CHECKING, Literal |
8 | 9 |
|
9 | 10 | from synaptic.agent_search import AgentSearch, SearchIntent, suggest_intent |
10 | 11 | from synaptic.cache import NodeCache |
|
24 | 25 | NodeKind, |
25 | 26 | SearchResult, |
26 | 27 | ) |
27 | | -from synaptic.ontology import OntologyRegistry |
| 28 | +from synaptic.ontology import OntologyRegistry, build_agent_ontology |
28 | 29 | from synaptic.protocols import ( |
29 | 30 | Digester, |
30 | 31 | KindClassifier, |
|
36 | 37 | from synaptic.search import HybridSearch |
37 | 38 | from synaptic.store import Store |
38 | 39 |
|
| 40 | +if TYPE_CHECKING: |
| 41 | + from synaptic.extensions.llm_provider import LLMProvider |
| 42 | + |
39 | 43 |
|
40 | 44 | class SynapticGraph: |
41 | | - """Facade over the synaptic memory system.""" |
| 45 | + """Facade over the synaptic memory system. |
| 46 | +
|
| 47 | + Quick Start:: |
| 48 | +
|
| 49 | + # 1. In-memory (zero-dep, 테스트/프로토타이핑) |
| 50 | + graph = SynapticGraph.memory() |
| 51 | +
|
| 52 | + # 2. SQLite (경량 프로덕션) |
| 53 | + graph = SynapticGraph.sqlite("knowledge.db") |
| 54 | +
|
| 55 | + # 3. Full preset with custom backend |
| 56 | + graph = SynapticGraph(backend, classifier=..., embedder=...) |
| 57 | + """ |
42 | 58 |
|
43 | 59 | __slots__ = ( |
44 | 60 | "_agent_search", |
@@ -85,6 +101,117 @@ def __init__( |
85 | 101 | self._phrase_extractor = phrase_extractor |
86 | 102 | self._agent_search = AgentSearch(hybrid=self._search) |
87 | 103 |
|
| 104 | + # --- Factory methods --- |
| 105 | + |
| 106 | + @classmethod |
| 107 | + def memory(cls, *, cache_size: int = 256) -> SynapticGraph: |
| 108 | + """In-memory backend — zero dependencies, 테스트/프로토타이핑용. |
| 109 | +
|
| 110 | + Example:: |
| 111 | +
|
| 112 | + graph = SynapticGraph.memory() |
| 113 | + await graph.add("Hello", "World") |
| 114 | + """ |
| 115 | + from synaptic.backends.memory import MemoryBackend # noqa: PLC0415 |
| 116 | + from synaptic.extensions.classifier_rules import RuleBasedClassifier # noqa: PLC0415 |
| 117 | + |
| 118 | + return cls( |
| 119 | + MemoryBackend(), |
| 120 | + classifier=RuleBasedClassifier(), |
| 121 | + cache_size=cache_size, |
| 122 | + ) |
| 123 | + |
| 124 | + @classmethod |
| 125 | + def sqlite( |
| 126 | + cls, |
| 127 | + db_path: str = "synaptic.db", |
| 128 | + *, |
| 129 | + cache_size: int = 256, |
| 130 | + ) -> SynapticGraph: |
| 131 | + """SQLite backend — 경량 프로덕션, FTS5 검색 지원. |
| 132 | +
|
| 133 | + Example:: |
| 134 | +
|
| 135 | + graph = SynapticGraph.sqlite("knowledge.db") |
| 136 | + await graph.backend.connect() |
| 137 | + await graph.add("Hello", "World") |
| 138 | + """ |
| 139 | + from synaptic.backends.sqlite import SQLiteBackend # noqa: PLC0415 |
| 140 | + from synaptic.extensions.classifier_rules import RuleBasedClassifier # noqa: PLC0415 |
| 141 | + from synaptic.extensions.relation_detector import RuleBasedRelationDetector # noqa: PLC0415 |
| 142 | + |
| 143 | + return cls( |
| 144 | + SQLiteBackend(db_path), |
| 145 | + classifier=RuleBasedClassifier(), |
| 146 | + relation_detector=RuleBasedRelationDetector(), |
| 147 | + ontology=build_agent_ontology(), |
| 148 | + cache_size=cache_size, |
| 149 | + ) |
| 150 | + |
| 151 | + @classmethod |
| 152 | + def full( |
| 153 | + cls, |
| 154 | + backend: StorageBackend, |
| 155 | + *, |
| 156 | + llm: LLMProvider | None = None, |
| 157 | + embed_api_base: str = "", |
| 158 | + embed_model: str = "default", |
| 159 | + embed_api_key: str = "", |
| 160 | + cache_size: int = 512, |
| 161 | + ) -> SynapticGraph: |
| 162 | + """Full-featured setup — LLM 분류, 임베딩, 관계 탐지, 온톨로지. |
| 163 | +
|
| 164 | + Example:: |
| 165 | +
|
| 166 | + from synaptic.backends.sqlite import SQLiteBackend |
| 167 | + from synaptic.extensions.llm_provider import OllamaLLMProvider |
| 168 | +
|
| 169 | + graph = SynapticGraph.full( |
| 170 | + SQLiteBackend("knowledge.db"), |
| 171 | + llm=OllamaLLMProvider(model="gemma3:4b"), |
| 172 | + embed_api_base="http://localhost:8080/v1", |
| 173 | + ) |
| 174 | + """ |
| 175 | + from synaptic.extensions.classifier_rules import RuleBasedClassifier # noqa: PLC0415 |
| 176 | + from synaptic.extensions.relation_detector import RuleBasedRelationDetector # noqa: PLC0415 |
| 177 | + |
| 178 | + classifier: KindClassifier |
| 179 | + relation_detector: RelationDetector |
| 180 | + embedder: EmbeddingProvider | None = None |
| 181 | + |
| 182 | + if llm is not None: |
| 183 | + from synaptic.extensions.classifier_hybrid import HybridClassifier # noqa: PLC0415 |
| 184 | + from synaptic.extensions.classifier_llm import LLMClassifier # noqa: PLC0415 |
| 185 | + from synaptic.extensions.relation_detector_llm import LLMRelationDetector # noqa: PLC0415 |
| 186 | + |
| 187 | + classifier = HybridClassifier( |
| 188 | + llm=LLMClassifier(llm, fallback=RuleBasedClassifier()), |
| 189 | + rule=RuleBasedClassifier(), |
| 190 | + ) |
| 191 | + relation_detector = LLMRelationDetector(llm, fallback=RuleBasedRelationDetector()) |
| 192 | + else: |
| 193 | + classifier = RuleBasedClassifier() |
| 194 | + relation_detector = RuleBasedRelationDetector() |
| 195 | + |
| 196 | + if embed_api_base: |
| 197 | + from synaptic.extensions.embedder import OpenAIEmbeddingProvider # noqa: PLC0415 |
| 198 | + |
| 199 | + embedder = OpenAIEmbeddingProvider( |
| 200 | + api_base=embed_api_base, |
| 201 | + model=embed_model, |
| 202 | + api_key=embed_api_key, |
| 203 | + ) |
| 204 | + |
| 205 | + return cls( |
| 206 | + backend, |
| 207 | + classifier=classifier, |
| 208 | + relation_detector=relation_detector, |
| 209 | + embedder=embedder, |
| 210 | + ontology=build_agent_ontology(), |
| 211 | + phrase_extractor=PhraseExtractor(), |
| 212 | + cache_size=cache_size, |
| 213 | + ) |
| 214 | + |
88 | 215 | @property |
89 | 216 | def backend(self) -> StorageBackend: |
90 | 217 | return self._backend |
|
0 commit comments