gufomind 1.0.0__tar.gz

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.
Files changed (130) hide show
  1. gufomind-1.0.0/CHANGELOG.md +60 -0
  2. gufomind-1.0.0/LICENSE +9 -0
  3. gufomind-1.0.0/MANIFEST.in +12 -0
  4. gufomind-1.0.0/PKG-INFO +512 -0
  5. gufomind-1.0.0/README.md +456 -0
  6. gufomind-1.0.0/pyproject.toml +118 -0
  7. gufomind-1.0.0/setup.cfg +4 -0
  8. gufomind-1.0.0/src/gufomind/__init__.py +15 -0
  9. gufomind-1.0.0/src/gufomind/categorization/__init__.py +26 -0
  10. gufomind-1.0.0/src/gufomind/categorization/categorizer.py +413 -0
  11. gufomind-1.0.0/src/gufomind/categorization/seeds.py +83 -0
  12. gufomind-1.0.0/src/gufomind/categorization/service.py +238 -0
  13. gufomind-1.0.0/src/gufomind/chunking/__init__.py +25 -0
  14. gufomind-1.0.0/src/gufomind/chunking/splitter.py +252 -0
  15. gufomind-1.0.0/src/gufomind/cli.py +1299 -0
  16. gufomind-1.0.0/src/gufomind/composition.py +469 -0
  17. gufomind-1.0.0/src/gufomind/config.py +485 -0
  18. gufomind-1.0.0/src/gufomind/core/__init__.py +1 -0
  19. gufomind-1.0.0/src/gufomind/core/audit.py +67 -0
  20. gufomind-1.0.0/src/gufomind/core/errors.py +16 -0
  21. gufomind-1.0.0/src/gufomind/core/ids.py +50 -0
  22. gufomind-1.0.0/src/gufomind/core/models.py +404 -0
  23. gufomind-1.0.0/src/gufomind/core/ports.py +545 -0
  24. gufomind-1.0.0/src/gufomind/embeddings/__init__.py +11 -0
  25. gufomind-1.0.0/src/gufomind/embeddings/encoder.py +173 -0
  26. gufomind-1.0.0/src/gufomind/enrichment.py +162 -0
  27. gufomind-1.0.0/src/gufomind/importers/__init__.py +22 -0
  28. gufomind-1.0.0/src/gufomind/importers/base.py +100 -0
  29. gufomind-1.0.0/src/gufomind/importers/deepseek.py +257 -0
  30. gufomind-1.0.0/src/gufomind/importers/personal.py +213 -0
  31. gufomind-1.0.0/src/gufomind/importers/registry.py +49 -0
  32. gufomind-1.0.0/src/gufomind/ingestion.py +197 -0
  33. gufomind-1.0.0/src/gufomind/licensing/__init__.py +51 -0
  34. gufomind-1.0.0/src/gufomind/licensing/codec.py +84 -0
  35. gufomind-1.0.0/src/gufomind/licensing/fingerprint.py +45 -0
  36. gufomind-1.0.0/src/gufomind/licensing/key_format.py +139 -0
  37. gufomind-1.0.0/src/gufomind/licensing/manager.py +444 -0
  38. gufomind-1.0.0/src/gufomind/licensing/paths.py +39 -0
  39. gufomind-1.0.0/src/gufomind/licensing/public_key.py +8 -0
  40. gufomind-1.0.0/src/gufomind/licensing/server_client.py +178 -0
  41. gufomind-1.0.0/src/gufomind/licensing/signing.py +70 -0
  42. gufomind-1.0.0/src/gufomind/llm/__init__.py +33 -0
  43. gufomind-1.0.0/src/gufomind/llm/anthropic_client.py +396 -0
  44. gufomind-1.0.0/src/gufomind/llm/base_client.py +479 -0
  45. gufomind-1.0.0/src/gufomind/llm/deepseek_client.py +75 -0
  46. gufomind-1.0.0/src/gufomind/llm/ollama_client.py +438 -0
  47. gufomind-1.0.0/src/gufomind/llm/openai_client.py +104 -0
  48. gufomind-1.0.0/src/gufomind/llm/openai_compatible_client.py +259 -0
  49. gufomind-1.0.0/src/gufomind/llm/openrouter_client.py +130 -0
  50. gufomind-1.0.0/src/gufomind/py.typed +0 -0
  51. gufomind-1.0.0/src/gufomind/rag/__init__.py +9 -0
  52. gufomind-1.0.0/src/gufomind/rag/chat_service.py +226 -0
  53. gufomind-1.0.0/src/gufomind/rag/pipeline.py +326 -0
  54. gufomind-1.0.0/src/gufomind/rag/retriever.py +64 -0
  55. gufomind-1.0.0/src/gufomind/rag/style_transfer.py +422 -0
  56. gufomind-1.0.0/src/gufomind/settings_meta.py +234 -0
  57. gufomind-1.0.0/src/gufomind/storage/__init__.py +34 -0
  58. gufomind-1.0.0/src/gufomind/storage/jsonl_session_store.py +301 -0
  59. gufomind-1.0.0/src/gufomind/storage/lancedb_store.py +655 -0
  60. gufomind-1.0.0/src/gufomind/user_settings.py +259 -0
  61. gufomind-1.0.0/src/gufomind/web/__init__.py +8 -0
  62. gufomind-1.0.0/src/gufomind/web/app.py +194 -0
  63. gufomind-1.0.0/src/gufomind/web/deps.py +120 -0
  64. gufomind-1.0.0/src/gufomind/web/middleware.py +74 -0
  65. gufomind-1.0.0/src/gufomind/web/routes/__init__.py +22 -0
  66. gufomind-1.0.0/src/gufomind/web/routes/base.py +53 -0
  67. gufomind-1.0.0/src/gufomind/web/routes/chat.py +178 -0
  68. gufomind-1.0.0/src/gufomind/web/routes/health.py +17 -0
  69. gufomind-1.0.0/src/gufomind/web/routes/runtime.py +62 -0
  70. gufomind-1.0.0/src/gufomind/web/routes/sessions.py +82 -0
  71. gufomind-1.0.0/src/gufomind/web/routes/settings.py +574 -0
  72. gufomind-1.0.0/src/gufomind/web/routes/settings_ui.py +175 -0
  73. gufomind-1.0.0/src/gufomind/web/routes/ui.py +106 -0
  74. gufomind-1.0.0/src/gufomind/web/run.py +48 -0
  75. gufomind-1.0.0/src/gufomind/web/sse.py +28 -0
  76. gufomind-1.0.0/src/gufomind/web/static/app.css +462 -0
  77. gufomind-1.0.0/src/gufomind/web/static/app.js +198 -0
  78. gufomind-1.0.0/src/gufomind/web/static/htmx.min.js +1 -0
  79. gufomind-1.0.0/src/gufomind/web/static/marked.min.js +69 -0
  80. gufomind-1.0.0/src/gufomind/web/static/purify.min.js +7 -0
  81. gufomind-1.0.0/src/gufomind/web/templates/base.html +35 -0
  82. gufomind-1.0.0/src/gufomind/web/templates/chat.html +14 -0
  83. gufomind-1.0.0/src/gufomind/web/templates/partials/chat_view.html +21 -0
  84. gufomind-1.0.0/src/gufomind/web/templates/partials/session_list.html +23 -0
  85. gufomind-1.0.0/src/gufomind/web/templates/partials/settings_form.html +58 -0
  86. gufomind-1.0.0/src/gufomind/web/templates/settings.html +8 -0
  87. gufomind-1.0.0/src/gufomind.egg-info/SOURCES.txt +127 -0
  88. gufomind-1.0.0/tests/test_anthropic_client.py +382 -0
  89. gufomind-1.0.0/tests/test_audit.py +40 -0
  90. gufomind-1.0.0/tests/test_base_llm_client.py +169 -0
  91. gufomind-1.0.0/tests/test_categorization_service.py +343 -0
  92. gufomind-1.0.0/tests/test_categorizer.py +585 -0
  93. gufomind-1.0.0/tests/test_chat_service.py +309 -0
  94. gufomind-1.0.0/tests/test_cli.py +1807 -0
  95. gufomind-1.0.0/tests/test_composition.py +414 -0
  96. gufomind-1.0.0/tests/test_config.py +146 -0
  97. gufomind-1.0.0/tests/test_deepseek_client.py +356 -0
  98. gufomind-1.0.0/tests/test_encoder.py +222 -0
  99. gufomind-1.0.0/tests/test_enrichment.py +182 -0
  100. gufomind-1.0.0/tests/test_ids.py +45 -0
  101. gufomind-1.0.0/tests/test_importers.py +606 -0
  102. gufomind-1.0.0/tests/test_ingestion.py +376 -0
  103. gufomind-1.0.0/tests/test_license_manager.py +599 -0
  104. gufomind-1.0.0/tests/test_licensing.py +179 -0
  105. gufomind-1.0.0/tests/test_migration.py +234 -0
  106. gufomind-1.0.0/tests/test_models.py +187 -0
  107. gufomind-1.0.0/tests/test_ollama_client.py +327 -0
  108. gufomind-1.0.0/tests/test_openai_client.py +64 -0
  109. gufomind-1.0.0/tests/test_openai_compatible_client.py +377 -0
  110. gufomind-1.0.0/tests/test_openrouter_client.py +158 -0
  111. gufomind-1.0.0/tests/test_pipeline.py +511 -0
  112. gufomind-1.0.0/tests/test_retriever.py +158 -0
  113. gufomind-1.0.0/tests/test_security.py +340 -0
  114. gufomind-1.0.0/tests/test_server_client.py +168 -0
  115. gufomind-1.0.0/tests/test_session_store.py +244 -0
  116. gufomind-1.0.0/tests/test_settings_api.py +843 -0
  117. gufomind-1.0.0/tests/test_settings_sources.py +151 -0
  118. gufomind-1.0.0/tests/test_settings_ui.py +262 -0
  119. gufomind-1.0.0/tests/test_splitter.py +356 -0
  120. gufomind-1.0.0/tests/test_store.py +899 -0
  121. gufomind-1.0.0/tests/test_style_transfer.py +484 -0
  122. gufomind-1.0.0/tests/test_style_transfer_integration.py +58 -0
  123. gufomind-1.0.0/tests/test_user_settings.py +157 -0
  124. gufomind-1.0.0/tests/test_web_chat.py +208 -0
  125. gufomind-1.0.0/tests/test_web_enrich.py +72 -0
  126. gufomind-1.0.0/tests/test_web_lifespan.py +186 -0
  127. gufomind-1.0.0/tests/test_web_routes.py +87 -0
  128. gufomind-1.0.0/tests/test_web_sessions.py +166 -0
  129. gufomind-1.0.0/tests/test_web_sse.py +219 -0
  130. gufomind-1.0.0/tests/test_web_ui.py +121 -0
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ All notable changes to GufoMind are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] — 2026-10-07
11
+
12
+ First public release.
13
+
14
+ ### Added
15
+
16
+ - **Import** — import AI chat exports (DeepSeek) and personal writing
17
+ (Markdown + YAML frontmatter) into a local LanceDB knowledge base.
18
+ - **Auto-categorization** — an LLM tags every chunk with 36 default topics
19
+ (fully customizable).
20
+ - **RAG** — `gufo ask` answers questions grounded in your own data, with
21
+ source references.
22
+ - **Dialogue memory** — `gufo chat` remembers context within a session.
23
+ - **Web UI** — `gufo web` (FastAPI + HTMX) with a chat, a sessions sidebar,
24
+ a source panel and a settings page.
25
+ - **Style transfer** — `gufo write "topic" -t poem` generates new text in your
26
+ own voice, grounded in your personal writing samples.
27
+ - **Settings page** — edit runtime settings (retrieval, LLM, chunking,
28
+ license) from the web UI without touching `.env`. Layered resolution:
29
+ `env > user settings > .env > defaults`.
30
+ - **Multi-provider LLM support** — Ollama (local, free), DeepSeek, OpenAI,
31
+ Anthropic, OpenRouter.
32
+ - **Streaming** — token-by-token output in the CLI (`--stream`) and the web UI
33
+ (SSE).
34
+ - **Per-file chunking control** — a `chunk: none` frontmatter key keeps
35
+ short-form writing (poems, notes) as a single chunk.
36
+ - **Genre filter** — a repeatable `--type` in `gufo write` narrows the style
37
+ samples by genre (`poem` / `song` / `post` / `article` / `note`).
38
+ - **CLI aliases** — the `gufo` and `gufomind` console commands are equivalent.
39
+ - **Licensing** — Ed25519 offline verification with a periodic remote check.
40
+ - **Web security baseline** — TrustedHost + Origin-check middleware
41
+ (localhost-only by default).
42
+
43
+ ### Changed
44
+
45
+ - **Project renamed to GufoMind.** Python import path `gufo_rag` →
46
+ `gufomind`; environment prefix `CHATMIND_` → `GUFO_`; distribution
47
+ `gufo-rag` → `gufomind`.
48
+ - **`.env` migration required** if you used the old prefix:
49
+ `sed -i '' 's/CHATMIND_/GUFO_/g' .env`
50
+ - Minimum Python is **3.10** (3.12 recommended for the ML stack).
51
+
52
+ ### Fixed
53
+
54
+ - `writing_kind` is no longer dropped by the splitter (before Phase 7.6 every
55
+ chunk carried a NULL genre).
56
+ - Re-ingesting after a chunking change is now idempotent: `replace_document`
57
+ replaces a document's chunks by `document_id` instead of leaving orphan rows.
58
+
59
+ [Unreleased]: https://github.com/chalpanov1/gufomind/compare/v1.0.0...HEAD
60
+ [1.0.0]: https://github.com/chalpanov1/gufomind/releases/tag/v1.0.0
gufomind-1.0.0/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2026 Alexander Chalpanov. All rights reserved.
2
+
3
+ This software and associated documentation files (the "Software")
4
+ are proprietary and confidential. Unauthorized copying,
5
+ modification, distribution, or use of the Software, in whole or
6
+ in part, is strictly prohibited without the express written
7
+ permission of the copyright holder.
8
+
9
+ The Software is provided "AS IS", without warranty of any kind.
@@ -0,0 +1,12 @@
1
+ # Include documentation files that setuptools does not pick up by default.
2
+ include CHANGELOG.md
3
+
4
+ # Exclude build artifacts from sdist.
5
+ prune src/gufomind.egg-info
6
+
7
+ # Exclude local data and secrets (defense in depth; gitignore already covers).
8
+ prune data
9
+ prune dist
10
+ prune build
11
+ exclude .env
12
+ exclude contextforai.md
@@ -0,0 +1,512 @@
1
+ Metadata-Version: 2.4
2
+ Name: gufomind
3
+ Version: 1.0.0
4
+ Summary: Personal RAG over your AI chat history — import, categorize, search, and query your conversations in your own voice.
5
+ Author-email: Chalpanov <chalpanovrich1@yandex.ru>
6
+ License-Expression: LicenseRef-Proprietary
7
+ Project-URL: Homepage, https://github.com/chalpanov1/gufomind
8
+ Project-URL: Repository, https://github.com/chalpanov1/gufomind
9
+ Project-URL: Issues, https://github.com/chalpanov1/gufomind/issues
10
+ Project-URL: Changelog, https://github.com/chalpanov1/gufomind/blob/main/CHANGELOG.md
11
+ Keywords: rag,llm,personal-ai,chat-history,style-transfer,local-first,knowledge-base,lancedb,personal-knowledge-management
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Typing :: Typed
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Text Processing :: Indexing
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: typer>=0.12
26
+ Requires-Dist: rich>=13.0
27
+ Requires-Dist: pydantic>=2.0
28
+ Requires-Dist: pydantic-settings>=2.0
29
+ Requires-Dist: python-dotenv>=1.0
30
+ Requires-Dist: lancedb<0.41,>=0.39
31
+ Requires-Dist: sentence-transformers>=2.7
32
+ Requires-Dist: langchain-text-splitters>=0.2
33
+ Requires-Dist: tiktoken>=0.7
34
+ Requires-Dist: ollama>=0.3
35
+ Requires-Dist: httpx>=0.27
36
+ Requires-Dist: pandas>=2.0
37
+ Requires-Dist: pyarrow>=15.0
38
+ Requires-Dist: tqdm>=4.66
39
+ Requires-Dist: platformdirs>=4.0
40
+ Requires-Dist: python-frontmatter>=1.1
41
+ Requires-Dist: pyyaml>=6.0
42
+ Provides-Extra: dev
43
+ Requires-Dist: pytest>=8.0; extra == "dev"
44
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
45
+ Requires-Dist: ruff>=0.5; extra == "dev"
46
+ Requires-Dist: mypy>=1.11; extra == "dev"
47
+ Provides-Extra: web
48
+ Requires-Dist: fastapi>=0.111; extra == "web"
49
+ Requires-Dist: uvicorn[standard]>=0.30; extra == "web"
50
+ Requires-Dist: jinja2>=3.1; extra == "web"
51
+ Requires-Dist: markdown-it-py>=3.0; extra == "web"
52
+ Requires-Dist: python-multipart>=0.0.9; extra == "web"
53
+ Provides-Extra: licensing
54
+ Requires-Dist: pynacl>=1.5; extra == "licensing"
55
+ Dynamic: license-file
56
+
57
+ # 🦉 GufoMind
58
+
59
+ > **Personal RAG over your AI chat history** — import, categorize, search and
60
+ > query all of your conversations in one place, and generate new content in your
61
+ > own voice.
62
+
63
+ [![CI](https://github.com/chalpanov1/gufomind/actions/workflows/ci.yml/badge.svg)](https://github.com/chalpanov1/gufomind/actions/workflows/ci.yml)
64
+ [![Version: v1.0.0](https://img.shields.io/badge/version-v1.0.0-blue.svg)](CHANGELOG.md)
65
+ [![License: Proprietary](https://img.shields.io/badge/License-Proprietary-red.svg)](LICENSE)
66
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
67
+ [![Status: stable](https://img.shields.io/badge/status-stable-brightgreen.svg)](CHANGELOG.md)
68
+
69
+ GufoMind (Gufo — Italian for *owl*) is a **local personal-RAG system**: it turns
70
+ the scattered history of your AI conversations, your personal writing and your
71
+ own chat sessions into **one local knowledge core** — and then lets you talk to
72
+ it, search it, and write with it. Website: <https://gufomind.ru>.
73
+
74
+ ## Naming
75
+
76
+ - **Product name:** GufoMind
77
+ - **Website:** https://gufomind.ru
78
+ - **CLI command:** `gufo`
79
+ - **Python package / PyPI:** `gufomind`
80
+ - **Env var prefix:** `GUFO_`.
81
+
82
+ The product was previously called *Gufo-RAG*. The rename happened before
83
+ the first public PyPI release, so no migration is required for end users.
84
+
85
+ ---
86
+
87
+ ## ✨ Why Gufo?
88
+
89
+ - Your best ideas are buried in dozens of AI chats across DeepSeek, ChatGPT,
90
+ Claude and Gemini — and they are not searchable.
91
+ - Your personal writing (posts, notes, poems, lyrics) lives somewhere else
92
+ entirely.
93
+ - Asking the same question twice is a waste of time and tokens.
94
+
95
+ Gufo fixes this: everything goes into **one vector database on your machine**,
96
+ and every new conversation makes that base smarter.
97
+
98
+ ```
99
+ Conversation → save → chunk → embed → vector store → search → new conversation
100
+ ▲ │
101
+ └────────────── the loop keeps enriching the base ───────────────┘
102
+ ```
103
+
104
+ ## 🎯 Features
105
+
106
+ | | Feature | Status |
107
+ |---|---|---|
108
+ | 1 | Import chats from DeepSeek / ChatGPT / Claude / Gemini + personal texts | 🚧 Phase 1 |
109
+ | 2 | Automatic categorization (topics tagged by an LLM) | ✅ v0.2.0 |
110
+ | 3 | RAG pipeline — `gufo ask` answers grounded in your archive | ✅ v0.3.0 |
111
+ | 4 | Hybrid search — vectors + BM25 + re-ranker | ⬜ Phase 3.5 |
112
+ | 5 | Dialogue memory — the assistant remembers the context | ✅ v0.4.0 |
113
+ | 6 | Multi-LLM + streaming — Ollama, OpenAI, DeepSeek, Anthropic, OpenRouter | ✅ v0.5.0 |
114
+ | 7 | Web interface with a chat client and auto-saved sessions | ✅ v0.6.0 |
115
+ | 8 | Style transfer — write in the author's own voice | ✅ v0.7.0 |
116
+ | 9 | Write grounded in a chosen genre — repeatable `--type` · per-file `chunk: none` | ✅ v0.7.6 |
117
+ | 10 | Settings page in the web UI — edit runtime settings without touching `.env` | ✅ v0.7.7 |
118
+
119
+ Legend: ✅ done · 🚧 in progress · ⬜ planned
120
+
121
+ **Web interface (v0.6)** — `gufo web`:
122
+
123
+ - ✅ Streamed answers (SSE) rendered as markdown
124
+ - ✅ Multi-turn sessions with memory, listed in a sidebar
125
+ - ✅ Sources panel under each answer
126
+ - ✅ "Enrich the base" — turn a conversation into searchable chunks
127
+ - ✅ Dark/light theme, responsive layout
128
+
129
+ **Current status** (v0.6.0):
130
+
131
+ - **Phase 0 (Foundation)** — ✅ complete.
132
+ - **Phase 1 (Multi-Importer)** — 🚧 in progress. `gufo ingest` works end-to-end
133
+ for DeepSeek exports (verified on a real 64 MB, 236-conversation export →
134
+ 23,966 chunks). The ChatGPT / Claude / Gemini and personal-text importers are
135
+ still to come.
136
+ - **Phase 2 (Auto-categorization)** — ✅ complete. `gufo categorize` tags every
137
+ chunk through the DeepSeek API (verified on the full base: 23,966 chunks →
138
+ 23,618 with topics, 348 unclassified, 0 failures, 41m 40s).
139
+ - **Phase 3 (Vector RAG)** — ✅ complete. `gufo ask` answers questions grounded in
140
+ your archive, with sources (verified live against the real base). Hybrid search
141
+ (BM25 + re-ranker) follows in Phase 3.5.
142
+ - **Phase 4 (Dialogue Memory)** — ✅ complete. `gufo chat` holds a multi-turn
143
+ dialogue, persists each session to JSONL and resumes it with `--session <id>`.
144
+ - **Phase 5 (Multi-LLM + Streaming)** — ✅ complete. Five providers behind one
145
+ `.env` switch (`ollama`, `deepseek`, `openai`, `anthropic`, `openrouter`) and
146
+ streaming answers in the CLI (`gufo ask --stream`, `gufo chat --stream`).
147
+ - **Phase 6 (Web Interface + Chat)** — ✅ complete. `gufo web` serves a local
148
+ FastAPI + HTMX chat at `http://127.0.0.1:8000`: streamed answers over SSE, a
149
+ sessions sidebar, a sources panel, and an "Enrich the base" action that turns a
150
+ conversation into searchable chunks.
151
+ - **Phase 7 (Style Transfer)** — ✅ complete (v0.7.0). `gufo write` drafts new
152
+ texts in your own voice, grounded in personal writing samples.
153
+ - **Phase 7.5 (Style Transfer polish)** — ✅ complete (no version bump).
154
+ Post-audit fixes: a non-blocking license check and prompt hardening.
155
+ - **Phase 7.6 (Writing kind filter + chunking)** — ✅ complete (v0.7.6).
156
+ Optional, repeatable `--type`; per-file `chunk: none`; idempotent re-ingest.
157
+ - **Phase 7.7 (Settings page + security)** — ✅ complete (v0.7.7). A **⚙️ Settings**
158
+ page in the web UI edits runtime settings live (retrieval, LLM, chunking) on
159
+ top of a layered loader (`env > user settings > .env > defaults`), with secrets
160
+ kept in the OS user-data directory; the local server gains a TrustedHost
161
+ allowlist and a cross-origin (CSRF) check.
162
+
163
+ See [ROADMAP.md](ROADMAP.md).
164
+
165
+ ## 📦 Installation
166
+
167
+ > Gufo is not published to PyPI yet. Until then, install it from source.
168
+
169
+ ### From source (development)
170
+
171
+ ```bash
172
+ git clone https://github.com/chalpanov1/gufomind.git
173
+ cd gufomind
174
+
175
+ python3.12 -m venv .venv
176
+ source .venv/bin/activate
177
+
178
+ pip install -e ".[dev]"
179
+ ```
180
+
181
+ > **Optional:** put `HF_TOKEN=...` in `.env` to authenticate Hugging Face model
182
+ > downloads — it is faster and removes the "unauthenticated requests" warning.
183
+ > Create a read-scoped token at
184
+ > [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens).
185
+
186
+ ### Verify the installation
187
+
188
+ ```bash
189
+ gufo --version # prints the installed version
190
+ gufo stats # shows the resolved configuration
191
+ gufo --help # lists all commands
192
+ ```
193
+
194
+ ### Once published (planned)
195
+
196
+ ```bash
197
+ pipx install gufomind # recommended: isolated CLI install
198
+ # or
199
+ pip install gufomind
200
+ ```
201
+
202
+ ## ⚙️ Configuration
203
+
204
+ Copy the example file and adjust it:
205
+
206
+ ```bash
207
+ cp .env.example .env
208
+ ```
209
+
210
+ | Variable | Default | Meaning |
211
+ |---|---|---|
212
+ | `GUFO_DATA_DIR` | `./data` | root directory for local data |
213
+ | `GUFO_DB_PATH` | `./data/vector_db` | LanceDB database directory |
214
+ | `GUFO_TABLE_NAME` | `knowledge_base` | LanceDB table name |
215
+ | `GUFO_SESSIONS_DIR` | `./data/sessions` | directory of `gufo chat` sessions (one JSONL file per session) |
216
+ | `GUFO_EMBEDDING_MODEL` | `paraphrase-multilingual-MiniLM-L12-v2` | sentence-transformers model |
217
+ | `HF_TOKEN` | — | Hugging Face token (faster downloads, gated models) |
218
+ | `GUFO_LLM_PROVIDER` | **required** | `ollama` (local, slow) \| `deepseek` (cloud) \| `openai` \| `anthropic` \| `openrouter` |
219
+ | `GUFO_LLM_MODEL` | per-provider default | optional model override; unset uses the provider default (`gufo stats` shows it) |
220
+ | `GUFO_LLM_TIMEOUT` | `60` | seconds to wait for one LLM request |
221
+ | `OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
222
+ | `GUFO_CHUNK_SIZE` | `512` | target chunk size in tokens |
223
+ | `GUFO_CHUNK_OVERLAP` | `50` | overlap between chunks |
224
+ | `GUFO_TOP_K` | `20` | chunks passed to the LLM |
225
+ | `GUFO_FETCH_K` | `30` | candidates fetched before re-ranking |
226
+ | `GUFO_TEMPERATURE` | `0.3` | generation temperature |
227
+ | `GUFO_HISTORY_WINDOW` | `20` | recent turns of a session passed to the LLM as history |
228
+ | `GUFO_RETRIEVAL_HISTORY_TURNS` | `2` | prior user turns folded into the search query |
229
+ | `GUFO_INCLUDE_THINKING` | `false` | keep the model's chain-of-thought blocks on import |
230
+ | `DEEPSEEK_API_KEY` | — | API key for the `deepseek` provider / `gufo categorize` |
231
+ | `DEEPSEEK_BASE_URL` | `https://api.deepseek.com` | DeepSeek API root |
232
+ | `OPENAI_API_KEY` | — | API key for the `openai` provider |
233
+ | `OPENAI_BASE_URL` | `https://api.openai.com/v1` | OpenAI API root |
234
+ | `ANTHROPIC_API_KEY` | — | API key for the `anthropic` provider |
235
+ | `ANTHROPIC_BASE_URL` | `https://api.anthropic.com/v1` | Anthropic API root |
236
+ | `OPENROUTER_API_KEY` | — | API key for the `openrouter` provider |
237
+ | `OPENROUTER_BASE_URL` | `https://openrouter.ai/api/v1` | OpenRouter API root |
238
+ | `OPENROUTER_REFERER` | — | optional `HTTP-Referer` attribution header |
239
+ | `OPENROUTER_TITLE` | — | optional `X-Title` attribution header |
240
+ | `GUFO_CATEGORIZATION_MODEL` | `deepseek-chat` | model used to tag chunks |
241
+ | `GUFO_CATEGORIZATION_BATCH_SIZE` | `20` | chunks per LLM request |
242
+ | `GUFO_CATEGORIZATION_MAX_TOPICS` | `5` | maximum topics per chunk |
243
+ | `GUFO_CATEGORIZATION_SEED_TOPICS` | built-in list of 36 | comma-separated topic seeds |
244
+ | `GUFO_LICENSE_SERVER_URL` | `https://license.gufomind.ru` | license server base URL |
245
+ | `GUFO_LICENSE_CHECK_INTERVAL_DAYS` | `30` | days a successful remote check is trusted |
246
+ | `GUFO_LICENSE_GRACE_PERIOD_DAYS` | `90` | offline grace period since the last successful check |
247
+ | `GUFO_STYLE_K` | `5` | style samples retrieved for `gufo write` (1–20) |
248
+ | `GUFO_LOG_LEVEL` | `WARNING` | log level: `DEBUG` / `INFO` / `WARNING` / `ERROR` |
249
+ | `GUFO_IGNORE_USER_SETTINGS` | — | set to `1` to ignore settings saved from the web UI (emergency kill-switch) |
250
+
251
+ > Values saved from the web UI's **⚙️ Settings** page live in your OS user-data
252
+ > directory, not in `.env` — see [Settings page](#settings-page). Precedence is
253
+ > **real env vars > user settings > `.env` > defaults**.
254
+
255
+ > Environment variables use the `GUFO_` prefix. The previous prefix
256
+ > (`CHATMIND_`) was renamed in Phase 8 with no backward-compatible alias.
257
+
258
+ ## 💸 Categorization cost
259
+
260
+ Running `gufo categorize` uses an LLM to tag every chunk. The cost depends
261
+ entirely on which model you use — from **$0 (local Ollama)** to **$80+ (OpenAI
262
+ GPT-4o)** for a 24,000-chunk base.
263
+
264
+ | Provider / Model | Cost for 24k chunks | Time | Notes |
265
+ |---|---|---|---|
266
+ | **Ollama (local)** | **$0** | ~hours on M1 | Free, private, slow |
267
+ | **DeepSeek (off-peak)** | **~$1.66** | ~40 min | Cheapest API. Reference run. |
268
+ | DeepSeek (peak hours) | ~$3–4 | ~40 min | 2× off-peak pricing |
269
+ | OpenAI gpt-4o-mini | ~$5–8 | ~15 min | Faster, more expensive |
270
+ | Anthropic Claude Haiku | ~$10–15 | ~20 min | Mid-range |
271
+ | OpenAI gpt-4o | ~$60–80 | ~15 min | Fastest, most expensive |
272
+
273
+ **Incremental runs** (new chunks only) cost proportionally less: 100 new chunks
274
+ ≈ $0.007 with DeepSeek off-peak.
275
+
276
+ **Why categorize?**
277
+
278
+ - **Without it:** `gufo ask` searches by semantic similarity only.
279
+ - **With it:** you can filter by exact topics — `gufo ask "..." --topic databases`
280
+ returns only database-related chunks.
281
+
282
+ > These numbers are indicative and follow the provider's pricing; local Ollama is
283
+ > free but needs a machine that can run the model. A `--force` run asks for
284
+ > confirmation first — pass `--yes` to skip the prompt in scripts and CI.
285
+
286
+ ## 🚀 Usage
287
+
288
+ ```bash
289
+ # Show the resolved configuration and the state of the base
290
+ gufo stats
291
+
292
+ # Import an export into the knowledge base (Phase 1)
293
+ gufo ingest ./exports/deepseek_conversations.json
294
+
295
+ # Tag every chunk with topics through the DeepSeek API (Phase 2)
296
+ gufo categorize # incremental: only untagged chunks
297
+ gufo categorize --force # re-tag everything
298
+
299
+ # Ask a question (Phase 3)
300
+ gufo ask "Что я писал про векторные базы данных?"
301
+
302
+ # Stream the answer as it is generated (Phase 5)
303
+ gufo ask "Что я писал про векторные базы данных?" --stream
304
+
305
+ # Chat with a memory of the dialogue (Phase 4)
306
+ gufo chat # start a new session
307
+ gufo chat --session <id> # resume a saved session
308
+ gufo chat --stream # stream each answer as it arrives
309
+ # inside the REPL: /exit · /new · /clear · /sessions
310
+
311
+ # Chat in the browser, with a sessions sidebar and sources (Phase 6)
312
+ gufo web # serves http://127.0.0.1:8000
313
+ gufo web --open # ...and open the browser
314
+ gufo web --port 9000 # pick another port
315
+
316
+ # Write in your own voice (Phase 7). The first --type is what to write;
317
+ # any further --type values pick the genres to draw samples from.
318
+ gufo write "утро" # a post (default), grounded in everything you wrote
319
+ gufo write "тишина" -t poem # a poem, grounded only in poems
320
+ gufo write "тишина" -t poem -t song # a poem, inspired by poems and songs
321
+ ```
322
+
323
+ ### Settings page
324
+
325
+ Open the **⚙️ Settings** page from the sidebar to change runtime settings
326
+ without editing `.env` or restarting:
327
+
328
+ - **Retrieval** (`top_k`, `history_window`, …) — applied instantly.
329
+ - **LLM** (provider, model, temperature, API keys) — the client is rebuilt on
330
+ save; in-flight streams continue on the old client.
331
+ - **Chunking** (`chunk_size`, `chunk_overlap`) — applied to the next ingest /
332
+ enrich run.
333
+ - **License** intervals — applied on the next restart.
334
+ - **Read-only** fields (`embedding_model`, `db_path`, …) are shown but cannot be
335
+ edited from the UI. To change them, edit `settings.json` and restart the
336
+ process.
337
+
338
+ Your settings are stored in your OS's user-data directory
339
+ (`~/Library/Application Support/Gufo/` on macOS, `~/.local/share/gufo/` on
340
+ Linux, `%APPDATA%\Gufo\` on Windows):
341
+
342
+ - `settings.json` — non-secret values.
343
+ - `secrets.json` — API keys, written with `0600` (owner-only) permissions.
344
+
345
+ **Precedence:** real environment variables > your `settings.json` > `.env` >
346
+ defaults. If a field shows `source: env`, editing it in the UI will have no
347
+ effect until the environment variable is unset.
348
+
349
+ ### Network exposure
350
+
351
+ The web server binds to `127.0.0.1` (loopback) by default and is protected by
352
+ two middlewares:
353
+
354
+ - **TrustedHostMiddleware** — only `localhost`, `127.0.0.1` and `[::1]` are
355
+ accepted; anything else returns `400`.
356
+ - **Origin check** — mutating requests (`POST`/`PUT`/`DELETE`) must carry a
357
+ same-origin `Origin` (or `Referer`) header; cross-origin requests get `403`.
358
+ Non-browser clients (curl, scripts) that send neither header are allowed.
359
+
360
+ Running `gufo web --host 0.0.0.0` (or any non-loopback address) triggers a
361
+ warning and requires explicit confirmation, then opens the allowlist to `["*"]`.
362
+ **GufoMind has no authentication** — never expose it to an untrusted network.
363
+
364
+ ⚠️ Bypassing the CLI (`uvicorn gufomind.web.run:app --host 0.0.0.0`) keeps the
365
+ loopback allowlist and will reject LAN clients. Always use `gufo web`.
366
+
367
+ ### Personal writing (Markdown + YAML frontmatter)
368
+
369
+ Put your own writing (poems, posts, articles, songs, notes) in a folder as
370
+ `.md` files. Each file starts with YAML frontmatter:
371
+
372
+ ```yaml
373
+ ---
374
+ type: poem # poem | post | article | song | note
375
+ title: Morning # optional; falls back to the file name
376
+ topics: [life] # optional
377
+ chunk: none # optional; see below
378
+ date: 2026-10-04 # optional
379
+ ---
380
+
381
+ Your text here.
382
+ ```
383
+
384
+ **`chunk: none`** disables chunk splitting for that file. Use it for short-form
385
+ writing (poems, haiku, notes) so a single work stays one chunk and its voice
386
+ sample is not truncated mid-stanza.
387
+
388
+ **Re-importing is idempotent**: chunks are keyed by a stable document id, and a
389
+ re-import replaces the previous chunks of the same file — no duplicates even if
390
+ you change `chunk`, `type` or `title` between imports.
391
+
392
+ #### Migrating from pre-v0.7.6
393
+
394
+ Personal chunks imported before v0.7.6 have `document_id = NULL`. A re-import
395
+ **with the same chunking structure** works fine — `merge_insert` matches by
396
+ chunk `id` and backfills `document_id`. However, a re-import **after a
397
+ chunking-structure change** (e.g. adding `chunk: none` to a file) will produce
398
+ duplicate chunks: the old NULL rows are not matched by `replace_document` and
399
+ stay behind.
400
+
401
+ To migrate cleanly, run **once**:
402
+
403
+ gufo ingest --reset <path-to-personal-texts>
404
+
405
+ This drops the table and re-imports everything with `document_id` populated. A
406
+ one-shot backfill command is planned for a future release (see ROADMAP backlog).
407
+
408
+ ## 🔎 Retrieval tuning
409
+
410
+ By default `gufo ask` and `gufo chat` retrieve the **20** most relevant chunks
411
+ and pass them to the LLM. That balances coverage (enough chunks to answer) and
412
+ precision — LLM attention degrades beyond roughly 25 chunks ("lost in the
413
+ middle"), so a larger `top_k` can dilute focus on a focused question.
414
+
415
+ Adjust it per call with `--top-k N`:
416
+
417
+ - **Lower (8–10)** — faster and cheaper; good for a focused question about one
418
+ specific thing, but it may miss relevant chunks for questions that span a
419
+ whole project.
420
+ - **Higher (30–50)** — more coverage for broad, cross-cutting questions; may hurt
421
+ precision on focused ones because of attention dilution.
422
+
423
+ Rules of thumb:
424
+
425
+ - Default **20** is tuned for a ~24k-chunk base. On a small base (<5k chunks),
426
+ **10–15** is usually enough.
427
+ - The cost is small: 20 chunks × ~512 tokens ≈ 10k tokens of context — pennies on
428
+ a cloud provider and comfortably inside a 32k local Ollama model.
429
+ - Prefer raising `--top-k` over lowering it when answers feel incomplete: better
430
+ to hand the model a few extra candidates than to miss the one that mattered.
431
+
432
+ ## 🧱 Data model
433
+
434
+ Every chunk carries three independent axes of metadata, so results can be
435
+ filtered in almost any combination:
436
+
437
+ | Axis | Values |
438
+ |---|---|
439
+ | **source** | `deepseek`, `chatgpt`, `claude`, `gemini`, `personal`, `session`, `manual` |
440
+ | **content_type** | `chat`, `writing`, `note`, `article`, `code` |
441
+ | **topics** | free-form tags, e.g. `models`, `databases`, `business` |
442
+
443
+ ```python
444
+ # All my personal writing about business — used to generate a new post
445
+ search(
446
+ query="предпринимательство, рост, стратегия",
447
+ filters={"source": "personal", "content_type": "writing", "topics": ["business"]},
448
+ )
449
+ ```
450
+
451
+ ## 🗂️ Project structure
452
+
453
+ ```
454
+ gufomind/
455
+ ├── .clinerules/ # coding rules for the AI agent
456
+ ├── memory-bank/ # long-term project memory
457
+ ├── src/gufomind/
458
+ │ ├── cli.py # typer CLI
459
+ │ ├── config.py # pydantic-settings (single source of truth)
460
+ │ ├── core/models.py # Message, Chat, Chunk, SearchResult
461
+ │ ├── importers/ # one importer per source
462
+ │ ├── chunking/ # token-aware splitter
463
+ │ ├── embeddings/ # sentence-transformers encoder
464
+ │ ├── storage/ # LanceDB store
465
+ │ ├── categorization/ # LLM topic tagging (port adapter + use case)
466
+ │ ├── llm/ # LLM clients (Ollama, OpenAI, DeepSeek, ...)
467
+ │ └── rag/ # retrieval + generation pipeline
468
+ ├── tests/
469
+ └── data/ # local data (git-ignored)
470
+ ```
471
+
472
+ ## 🗺️ Roadmap
473
+
474
+ See [ROADMAP.md](ROADMAP.md) for the full plan. Short version:
475
+
476
+ | Phase | Name |
477
+ |---|---|
478
+ | 0 | Foundation ✅ v0.1.0 |
479
+ | 1 | Multi-Importer 🚧 |
480
+ | 2 | Auto-categorization ✅ v0.2.0 |
481
+ | 3 | Vector RAG ✅ v0.3.0 |
482
+ | 3.5 | Hybrid Search ⬜ |
483
+ | 4 | Dialogue Memory ✅ v0.4.0 |
484
+ | 5 | Multi-LLM ✅ v0.5.0 |
485
+ | 6 | Web Interface + Chat ✅ v0.6.0 |
486
+ | 7 | Style Transfer ✅ v0.7.0 |
487
+ | 7.5 | Style Transfer polish ✅ |
488
+ | 7.6 | Writing kind filter + chunking ✅ v0.7.6 |
489
+ | 7.7 | Settings page + security ✅ v0.7.7 |
490
+ | 8 | Release ⬜ |
491
+
492
+ ## 🛠️ Development
493
+
494
+ The project follows the coding standards in
495
+ [`.clinerules/01-coding-standards.md`](.clinerules/01-coding-standards.md) and the
496
+ workflow in [`.clinerules/04-workflow.md`](.clinerules/04-workflow.md).
497
+
498
+ ```bash
499
+ ruff check . # lint
500
+ ruff format . # format
501
+ pytest # tests
502
+ ```
503
+
504
+ Commits follow [Conventional Commits](https://www.conventionalcommits.org/).
505
+
506
+ ## 📄 License
507
+
508
+ Copyright © 2026 Alexander Chalpanov. All rights reserved.
509
+
510
+ This is **proprietary, closed-source software** — not open source. See
511
+ [LICENSE](LICENSE) for the full terms. Unauthorized copying, modification,
512
+ distribution or use, in whole or in part, is strictly prohibited.