concierge-graph 3.8.2__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.
main.py ADDED
@@ -0,0 +1,281 @@
1
+ """
2
+ main.py — Grafo Concierge v3.8.0 (Absolute Solidity)
3
+
4
+ System entry point. Initializes all components and
5
+ runs the MCP server via stdio transport.
6
+
7
+ Initialization flow:
8
+ 1. Logging configured (console + timestamps)
9
+ 2. Storage: SqliteStore + EmbeddingManager + ChromaVectorStore
10
+ 3. Ingestion: ZoomSummarizer + IngestionManager
11
+ 4. Services: JanitorService (background thread)
12
+ 5. Server: GrafoConciergeServer (FastMCP)
13
+ 6. Graceful shutdown: Janitor stop + DB close
14
+
15
+ Environment variables:
16
+ GRAFO_DB_PATH → SQLite path (default: ./data/concierge.db)
17
+ GRAFO_CHROMA_PATH → ChromaDB path (default: ./data/chroma)
18
+ GRAFO_CHROMA_COLLECTION → Collection name (default: grafo_concierge)
19
+ GRAFO_EMBEDDING_MODEL → Embedding model (default: all-MiniLM-L6-v2)
20
+ GRAFO_LLM_MODEL → LLM model for summarization (default: gemini-2.0-flash)
21
+ GRAFO_LLM_API_KEY → LLM API Key (optional)
22
+ GRAFO_JANITOR_INTERVAL → Janitor interval in seconds (default: 300)
23
+ GRAFO_LOG_LEVEL → Log level (default: INFO)
24
+ GRAFO_TRANSPORT → MCP transport: stdio or sse (default: stdio)
25
+
26
+ Usage:
27
+ python main.py
28
+ """
29
+
30
+ from __future__ import annotations
31
+ from dotenv import load_dotenv
32
+ load_dotenv()
33
+
34
+ import logging
35
+ import os
36
+ import signal
37
+ import sys
38
+ from pathlib import Path
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # 1. CONFIGURATION — Constants and environment variables
42
+ # ---------------------------------------------------------------------------
43
+
44
+ # Paths — anchored at the project root, never relative to the process CWD
45
+ PROJECT_ROOT = Path(__file__).parent.resolve()
46
+
47
+ def resolve_project_path(env_value: str, default_rel: str) -> str:
48
+ val = env_value or default_rel
49
+ path = Path(val)
50
+ if path.is_absolute():
51
+ return str(path)
52
+ return str((PROJECT_ROOT / path).resolve())
53
+
54
+ DB_PATH = resolve_project_path(os.environ.get("GRAFO_DB_PATH", ""), "data/concierge.db")
55
+ CHROMA_PATH = resolve_project_path(os.environ.get("GRAFO_CHROMA_PATH", ""), "data/chroma")
56
+ CHROMA_COLLECTION = os.environ.get("GRAFO_CHROMA_COLLECTION", "grafo_concierge")
57
+
58
+ # Models
59
+ EMBEDDING_MODEL = os.environ.get("GRAFO_EMBEDDING_MODEL", "all-MiniLM-L6-v2")
60
+ LLM_MODEL = os.environ.get("GRAFO_LLM_MODEL", "gemini-2.0-flash")
61
+ LLM_API_KEY = os.environ.get("GRAFO_LLM_API_KEY", "")
62
+ LLM_BASE_URL = os.environ.get("GRAFO_LLM_BASE_URL", "")
63
+
64
+ # Janitor
65
+ JANITOR_INTERVAL = int(os.environ.get("GRAFO_JANITOR_INTERVAL", "300"))
66
+ JANITOR_PROJECT_UUID = os.environ.get("GRAFO_JANITOR_PROJECT", "")
67
+
68
+ # Runtime
69
+ LOG_LEVEL = os.environ.get("GRAFO_LOG_LEVEL", "INFO").upper()
70
+ TRANSPORT = os.environ.get("GRAFO_TRANSPORT", "stdio")
71
+
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # 2. LOGGING — Console with timestamps and levels
75
+ # ---------------------------------------------------------------------------
76
+
77
+ def setup_logging(level: str = "INFO") -> None:
78
+ """Configures global logging for Grafo Concierge."""
79
+ numeric_level = getattr(logging, level, logging.INFO)
80
+
81
+ formatter = logging.Formatter(
82
+ fmt="%(asctime)s │ %(levelname)-7s │ %(name)-28s │ %(message)s",
83
+ datefmt="%Y-%m-%d %H:%M:%S",
84
+ )
85
+
86
+ # Handler: stderr (so it does not interfere with MCP stdio)
87
+ handler = logging.StreamHandler(sys.stderr)
88
+ handler.setLevel(numeric_level)
89
+ handler.setFormatter(formatter)
90
+
91
+ # Root logger
92
+ root = logging.getLogger()
93
+ root.setLevel(numeric_level)
94
+ root.addHandler(handler)
95
+
96
+ # Silence noisy third-party loggers
97
+ logging.getLogger("chromadb").setLevel(logging.WARNING)
98
+ logging.getLogger("httpx").setLevel(logging.WARNING)
99
+ logging.getLogger("httpcore").setLevel(logging.WARNING)
100
+ logging.getLogger("sentence_transformers").setLevel(logging.WARNING)
101
+
102
+
103
+ # ---------------------------------------------------------------------------
104
+ # 3. BOOTSTRAP — Initialization of all components
105
+ # ---------------------------------------------------------------------------
106
+
107
+ logger = logging.getLogger("grafo-concierge.main")
108
+
109
+
110
+ def bootstrap():
111
+ """Initializes and returns all system components.
112
+
113
+ Returns:
114
+ Tuple containing (server, janitor, store) for use in the main loop and shutdown.
115
+ """
116
+ # --- Ensures the data directory exists ---
117
+ os.makedirs(os.path.dirname(DB_PATH) or ".", exist_ok=True)
118
+ os.makedirs(CHROMA_PATH, exist_ok=True)
119
+
120
+ # ── STORAGE ──────────────────────────────────────────────────────
121
+ logger.info("Initializing SqliteStore: %s", DB_PATH)
122
+ from storage import SqliteStore
123
+ store = SqliteStore(DB_PATH)
124
+
125
+ logger.info("Initializing EmbeddingManager: tier=FLASH")
126
+ from storage import EmbeddingManager, EmbeddingTier
127
+ embedder = EmbeddingManager(tier=EmbeddingTier.FLASH)
128
+
129
+ vector_backend = os.environ.get("GRAFO_VECTOR_BACKEND", "chroma").lower()
130
+ if vector_backend == "qdrant":
131
+ qdrant_url = os.environ.get("GRAFO_QDRANT_URL", "http://localhost:6333")
132
+ qdrant_key = os.environ.get("GRAFO_QDRANT_API_KEY", "") or None
133
+ logger.info("Initializing QdrantVectorStore: url=%s, collection=%s", qdrant_url, CHROMA_COLLECTION)
134
+ from core.vector_backend import QdrantVectorStore
135
+ vector_store = QdrantVectorStore(
136
+ url=qdrant_url,
137
+ api_key=qdrant_key,
138
+ collection_name=CHROMA_COLLECTION,
139
+ embedding_dimensions=embedder.dimensions,
140
+ )
141
+ else:
142
+ logger.info("Initializing ChromaVectorStore: path=%s, collection=%s", CHROMA_PATH, CHROMA_COLLECTION)
143
+ from storage import ChromaVectorStore
144
+ vector_store = ChromaVectorStore(
145
+ persist_dir=CHROMA_PATH,
146
+ collection_name=CHROMA_COLLECTION,
147
+ embedding_manager=embedder,
148
+ )
149
+
150
+ # ── INGESTION ────────────────────────────────────────────────────
151
+ logger.info("Initializing ZoomSummarizer: model=%s", LLM_MODEL)
152
+ from ingestion import ZoomSummarizer
153
+ from ingestion.summarizer import LLMAdapter
154
+
155
+ llm_adapter = LLMAdapter(
156
+ model_name=LLM_MODEL,
157
+ api_key=LLM_API_KEY or None,
158
+ base_url=LLM_BASE_URL or None,
159
+ )
160
+ summarizer = ZoomSummarizer(
161
+ llm_adapter=llm_adapter,
162
+ sqlite_store=store,
163
+ )
164
+
165
+ logger.info("Initializing IngestionManager")
166
+ from ingestion import IngestionManager
167
+ ingestion_manager = IngestionManager(
168
+ sqlite_store=store,
169
+ vector_store=vector_store,
170
+ embedding_manager=embedder,
171
+ summarizer=summarizer,
172
+ )
173
+
174
+ # ── SERVICES ─────────────────────────────────────────────────────
175
+ logger.info("Initializing JanitorService: interval=%ds", JANITOR_INTERVAL)
176
+ from services import JanitorService
177
+ janitor = JanitorService(
178
+ sqlite_store=store,
179
+ vector_store=vector_store,
180
+ ingestion_manager=ingestion_manager,
181
+ )
182
+
183
+ # ── CENTRAL FACADE ───────────────────────────────────────────────
184
+ logger.info("Initializing GrafoConcierge Central Facade")
185
+ from core.middleware import GrafoConcierge
186
+ concierge = GrafoConcierge(
187
+ sqlite_store=store,
188
+ vector_store=vector_store,
189
+ embedding_manager=embedder,
190
+ ingestion_manager=ingestion_manager,
191
+ )
192
+
193
+ # ── MCP SERVER ───────────────────────────────────────────────────
194
+ logger.info("Initializing GrafoConciergeServer")
195
+ from interface.mcp_server import GrafoConciergeServer
196
+ server = GrafoConciergeServer(
197
+ concierge=concierge,
198
+ janitor=janitor,
199
+ )
200
+
201
+ logger.info("Bootstrap complete — all components initialized.")
202
+ return server, janitor, store
203
+
204
+
205
+ # ---------------------------------------------------------------------------
206
+ # 4. MAIN — Main loop with graceful shutdown
207
+ # ---------------------------------------------------------------------------
208
+
209
+ def main() -> None:
210
+ """Main entry point of Grafo Concierge."""
211
+
212
+ # ── Logging ──
213
+ setup_logging(LOG_LEVEL)
214
+
215
+ logger.info("=" * 60)
216
+ logger.info(" Grafo Concierge v3.8.0 — Absolute Solidity")
217
+ logger.info("=" * 60)
218
+ logger.info(" DB: %s", DB_PATH)
219
+ logger.info(" ChromaDB: %s (%s)", CHROMA_PATH, CHROMA_COLLECTION)
220
+ logger.info(" Embedding: %s", EMBEDDING_MODEL)
221
+ logger.info(" LLM: %s", LLM_MODEL)
222
+ logger.info(" Janitor: %ds", JANITOR_INTERVAL)
223
+ logger.info(" Transport: %s", TRANSPORT)
224
+ logger.info(" Log Level: %s", LOG_LEVEL)
225
+ logger.info("=" * 60)
226
+
227
+ server = None
228
+ janitor = None
229
+ store = None
230
+
231
+ try:
232
+ # ── Bootstrap ──
233
+ server, janitor, store = bootstrap()
234
+
235
+ # ── Background Janitor (if project is configured) ──
236
+ if JANITOR_PROJECT_UUID:
237
+ logger.info("Starting background Janitor for project: %s", JANITOR_PROJECT_UUID)
238
+ janitor.start_background(JANITOR_PROJECT_UUID, interval=JANITOR_INTERVAL)
239
+ else:
240
+ logger.info("Janitor in idle mode (no GRAFO_JANITOR_PROJECT defined).")
241
+
242
+ # ── MCP Server ──
243
+ logger.info("Starting MCP server (transport=%s)...", TRANSPORT)
244
+ server.run(transport=TRANSPORT)
245
+
246
+ except KeyboardInterrupt:
247
+ logger.info("Interrupt received (Ctrl+C) — starting graceful shutdown...")
248
+
249
+ except Exception as e:
250
+ logger.critical("Erro fatal durante execução: %s", e, exc_info=True)
251
+ sys.exit(1)
252
+
253
+ finally:
254
+ # ── Graceful Shutdown ──
255
+ logger.info("Shutdown gracioso iniciado...")
256
+
257
+ if janitor is not None:
258
+ try:
259
+ janitor.stop_background(timeout=5.0)
260
+ logger.info("Janitor encerrado.")
261
+ except Exception as e:
262
+ logger.warning("Erro ao parar Janitor: %s", e)
263
+
264
+ if store is not None:
265
+ try:
266
+ store.close()
267
+ logger.info("SqliteStore encerrado.")
268
+ except Exception as e:
269
+ logger.warning("Erro ao fechar SqliteStore: %s", e)
270
+
271
+ logger.info("=" * 60)
272
+ logger.info(" Grafo Concierge encerrado com sucesso.")
273
+ logger.info("=" * 60)
274
+
275
+
276
+ # ---------------------------------------------------------------------------
277
+ # ENTRY POINT
278
+ # ---------------------------------------------------------------------------
279
+
280
+ if __name__ == "__main__":
281
+ main()
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ scripts/bootstrap_core_memory.py — Grafo Concierge v3.8.0
4
+
5
+ Populate the user_core_memory table with persona information and default
6
+ context rules so that the agent starts operational and aligned with project guidelines.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+ from dotenv import load_dotenv
13
+
14
+ # Ensures loading of Grafo Concierge modules by inserting the root directory path into sys.path
15
+ ROOT_DIR = Path(__file__).parent.parent.resolve()
16
+ sys.path.insert(0, str(ROOT_DIR))
17
+
18
+ load_dotenv(str(ROOT_DIR / ".env"))
19
+
20
+ from storage import SqliteStore
21
+
22
+ def resolve_project_path(env_value: str, default_rel: str) -> str:
23
+ val = env_value or default_rel
24
+ path = Path(val)
25
+ if path.is_absolute():
26
+ return str(path)
27
+ return str((ROOT_DIR / path).resolve())
28
+
29
+ DB_PATH = resolve_project_path(os.environ.get("GRAFO_DB_PATH", ""), "data/concierge.db")
30
+
31
+ DEFAULT_MEMORIES = [
32
+ {
33
+ "scope_type": "agent",
34
+ "scope_id": "concierge",
35
+ "block_label": "persona",
36
+ "content": (
37
+ "Você é o Grafo Concierge, um agente inteligente de engenharia de software "
38
+ "e memória arquitetural de longo prazo. Você pair-programa com o usuário "
39
+ "e gerencia grafos de código-fonte de sistemas complexos."
40
+ )
41
+ },
42
+ {
43
+ "scope_type": "agent",
44
+ "scope_id": "concierge",
45
+ "block_label": "context_rules",
46
+ "content": (
47
+ "Regra 1: Dívida Técnica Zero Absoluta. Nunca introduzir hacks ou violar encapsulamentos.\n"
48
+ "Regra 2: Blindagem de Segredos. Nunca deixar chaves de API em texto plano em arquivos JSON ou versionados.\n"
49
+ "Regra 3: Validação Fail-Fast nas bordas da API MCP.\n"
50
+ "Regra 4: Resiliência em conexões de banco de dados (retry com backoff)."
51
+ )
52
+ }
53
+ ]
54
+
55
+ def main() -> None:
56
+ print("=" * 60)
57
+ print(" Grafo Concierge — Bootstrap da Core Memory")
58
+ print("=" * 60)
59
+ print(f"Banco de dados: {DB_PATH}")
60
+
61
+ # Ensures the data directory exists
62
+ db_dir = os.path.dirname(DB_PATH)
63
+ if db_dir:
64
+ os.makedirs(db_dir, exist_ok=True)
65
+
66
+ try:
67
+ store = SqliteStore(DB_PATH)
68
+ print("SqliteStore inicializado com sucesso.")
69
+
70
+ inserted_count = 0
71
+ for mem in DEFAULT_MEMORIES:
72
+ # Inserts/updates default blocks in the table
73
+ store.set_core_memory(
74
+ scope_type=mem["scope_type"],
75
+ scope_id=mem["scope_id"],
76
+ block_label=mem["block_label"],
77
+ content=mem["content"]
78
+ )
79
+ print(f" [+] Bloco '{mem['block_label']}' registrado para '{mem['scope_type']}/{mem['scope_id']}'.")
80
+ inserted_count += 1
81
+
82
+ store.close()
83
+ print("=" * 60)
84
+ print(f"Sucesso: {inserted_count} blocos de memória padrão registrados.")
85
+ print("=" * 60)
86
+ sys.exit(0)
87
+
88
+ except Exception as e:
89
+ print(f"[-] Erro crítico durante o bootstrap: {e}", file=sys.stderr)
90
+ sys.exit(1)
91
+
92
+ if __name__ == "__main__":
93
+ main()
services/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """
2
+ services/ - Autonomous services of Grafo Concierge v3.8.0
3
+
4
+ Modules:
5
+ janitor.py → Background Janitor — autonomous maintenance of the graph
6
+ """
7
+
8
+ from services.janitor import JanitorService, MaintenanceReport
9
+
10
+ __all__ = [
11
+ "JanitorService",
12
+ "MaintenanceReport",
13
+ ]