deeptutor-knowledge 1.7.0__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.
- deeptutor/api/routers/knowledge.py +5225 -0
- deeptutor/api/routers/marginnote4.py +278 -0
- deeptutor/capabilities/ima/__init__.py +6 -0
- deeptutor/capabilities/ima/binding.py +118 -0
- deeptutor/capabilities/ima/capability.py +92 -0
- deeptutor/capabilities/ima/prompts/en/system.md +33 -0
- deeptutor/capabilities/ima/prompts/zh/system.md +24 -0
- deeptutor/capabilities/ima/tools.py +476 -0
- deeptutor/capabilities/marginnote4/__init__.py +13 -0
- deeptutor/capabilities/marginnote4/binding.py +66 -0
- deeptutor/capabilities/marginnote4/capability.py +80 -0
- deeptutor/capabilities/marginnote4/models.py +173 -0
- deeptutor/capabilities/marginnote4/prompts/en/system.md +54 -0
- deeptutor/capabilities/marginnote4/prompts/zh/system.md +44 -0
- deeptutor/capabilities/marginnote4/store.py +562 -0
- deeptutor/capabilities/marginnote4/tools.py +329 -0
- deeptutor/capabilities/obsidian/__init__.py +6 -0
- deeptutor/capabilities/obsidian/binding.py +75 -0
- deeptutor/capabilities/obsidian/capability.py +82 -0
- deeptutor/capabilities/obsidian/prompts/en/system.md +32 -0
- deeptutor/capabilities/obsidian/prompts/zh/system.md +28 -0
- deeptutor/capabilities/obsidian/tools.py +399 -0
- deeptutor/capabilities/obsidian/vault.py +341 -0
- deeptutor/knowledge/__init__.py +28 -0
- deeptutor/knowledge/add_documents.py +734 -0
- deeptutor/knowledge/indexing_run.py +432 -0
- deeptutor/knowledge/initializer.py +448 -0
- deeptutor/knowledge/manager.py +2570 -0
- deeptutor/knowledge/module_service.py +29 -0
- deeptutor/knowledge/naming.py +40 -0
- deeptutor/knowledge/progress_events.py +45 -0
- deeptutor/knowledge/progress_tracker.py +355 -0
- deeptutor/knowledge/staging.py +69 -0
- deeptutor/services/config/knowledge_base_config.py +239 -0
- deeptutor/services/embedding/__init__.py +46 -0
- deeptutor/services/embedding/adapters/__init__.py +40 -0
- deeptutor/services/embedding/adapters/base.py +196 -0
- deeptutor/services/embedding/adapters/cohere.py +176 -0
- deeptutor/services/embedding/adapters/dashscope_native.py +242 -0
- deeptutor/services/embedding/adapters/gemini.py +462 -0
- deeptutor/services/embedding/adapters/jina.py +161 -0
- deeptutor/services/embedding/adapters/ollama.py +145 -0
- deeptutor/services/embedding/adapters/openai_compatible.py +406 -0
- deeptutor/services/embedding/adapters/openai_sdk.py +150 -0
- deeptutor/services/embedding/client.py +323 -0
- deeptutor/services/embedding/config.py +114 -0
- deeptutor/services/embedding/request_options.py +46 -0
- deeptutor/services/embedding/validation.py +128 -0
- deeptutor/services/github_source/__init__.py +5 -0
- deeptutor/services/github_source/client.py +164 -0
- deeptutor/services/github_source/sync.py +384 -0
- deeptutor/services/github_source/sync_service.py +72 -0
- deeptutor/services/rag/__init__.py +21 -0
- deeptutor/services/rag/embedding_binding.py +331 -0
- deeptutor/services/rag/embedding_signature.py +63 -0
- deeptutor/services/rag/eval/__init__.py +92 -0
- deeptutor/services/rag/eval/dataset.py +244 -0
- deeptutor/services/rag/eval/diff.py +312 -0
- deeptutor/services/rag/eval/matching.py +218 -0
- deeptutor/services/rag/eval/metrics.py +213 -0
- deeptutor/services/rag/eval/report.py +111 -0
- deeptutor/services/rag/eval/runner.py +309 -0
- deeptutor/services/rag/factory.py +369 -0
- deeptutor/services/rag/file_routing.py +5 -0
- deeptutor/services/rag/index_probe.py +327 -0
- deeptutor/services/rag/index_versioning.py +387 -0
- deeptutor/services/rag/kb_paths.py +50 -0
- deeptutor/services/rag/linked_kb.py +237 -0
- deeptutor/services/rag/pipelines/__init__.py +7 -0
- deeptutor/services/rag/pipelines/base.py +41 -0
- deeptutor/services/rag/pipelines/graphrag/__init__.py +18 -0
- deeptutor/services/rag/pipelines/graphrag/compatibility.py +42 -0
- deeptutor/services/rag/pipelines/graphrag/completion_adapter.py +251 -0
- deeptutor/services/rag/pipelines/graphrag/config.py +357 -0
- deeptutor/services/rag/pipelines/graphrag/engine.py +479 -0
- deeptutor/services/rag/pipelines/graphrag/errors.py +328 -0
- deeptutor/services/rag/pipelines/graphrag/ingestion.py +135 -0
- deeptutor/services/rag/pipelines/graphrag/pandas_compat.py +68 -0
- deeptutor/services/rag/pipelines/graphrag/pipeline.py +284 -0
- deeptutor/services/rag/pipelines/graphrag/provider.py +134 -0
- deeptutor/services/rag/pipelines/graphrag/storage.py +127 -0
- deeptutor/services/rag/pipelines/ima/__init__.py +28 -0
- deeptutor/services/rag/pipelines/ima/client.py +364 -0
- deeptutor/services/rag/pipelines/ima/config.py +140 -0
- deeptutor/services/rag/pipelines/ima/envelope.py +109 -0
- deeptutor/services/rag/pipelines/ima/inventory.py +209 -0
- deeptutor/services/rag/pipelines/ima/media.py +190 -0
- deeptutor/services/rag/pipelines/ima/models.py +348 -0
- deeptutor/services/rag/pipelines/ima/notes.py +240 -0
- deeptutor/services/rag/pipelines/ima/pipeline.py +238 -0
- deeptutor/services/rag/pipelines/ima/probe.py +105 -0
- deeptutor/services/rag/pipelines/ima/sources.py +105 -0
- deeptutor/services/rag/pipelines/ima/transport.py +130 -0
- deeptutor/services/rag/pipelines/kiwix/__init__.py +1 -0
- deeptutor/services/rag/pipelines/kiwix/client.py +369 -0
- deeptutor/services/rag/pipelines/kiwix/pipeline.py +75 -0
- deeptutor/services/rag/pipelines/lightrag/README.md +78 -0
- deeptutor/services/rag/pipelines/lightrag/__init__.py +15 -0
- deeptutor/services/rag/pipelines/lightrag/block_policy.py +266 -0
- deeptutor/services/rag/pipelines/lightrag/cache_reuse.py +137 -0
- deeptutor/services/rag/pipelines/lightrag/config.py +453 -0
- deeptutor/services/rag/pipelines/lightrag/engine.py +536 -0
- deeptutor/services/rag/pipelines/lightrag/indexing_policy.py +696 -0
- deeptutor/services/rag/pipelines/lightrag/ingress.py +364 -0
- deeptutor/services/rag/pipelines/lightrag/parser.py +99 -0
- deeptutor/services/rag/pipelines/lightrag/pipeline.py +692 -0
- deeptutor/services/rag/pipelines/lightrag/roles.py +132 -0
- deeptutor/services/rag/pipelines/lightrag/sidecar.py +320 -0
- deeptutor/services/rag/pipelines/lightrag/storage.py +338 -0
- deeptutor/services/rag/pipelines/lightrag/worker.py +259 -0
- deeptutor/services/rag/pipelines/lightrag/write_lock.py +55 -0
- deeptutor/services/rag/pipelines/lightrag_server/__init__.py +15 -0
- deeptutor/services/rag/pipelines/lightrag_server/client.py +149 -0
- deeptutor/services/rag/pipelines/lightrag_server/config.py +68 -0
- deeptutor/services/rag/pipelines/lightrag_server/pipeline.py +120 -0
- deeptutor/services/rag/pipelines/lightrag_server/probe.py +110 -0
- deeptutor/services/rag/pipelines/llamaindex/__init__.py +3 -0
- deeptutor/services/rag/pipelines/llamaindex/config.py +199 -0
- deeptutor/services/rag/pipelines/llamaindex/document_loader.py +648 -0
- deeptutor/services/rag/pipelines/llamaindex/embedding_adapter.py +264 -0
- deeptutor/services/rag/pipelines/llamaindex/errors.py +76 -0
- deeptutor/services/rag/pipelines/llamaindex/exercise_lookup.py +373 -0
- deeptutor/services/rag/pipelines/llamaindex/ingestion.py +129 -0
- deeptutor/services/rag/pipelines/llamaindex/pipeline.py +556 -0
- deeptutor/services/rag/pipelines/llamaindex/rerank.py +130 -0
- deeptutor/services/rag/pipelines/llamaindex/retrievers.py +360 -0
- deeptutor/services/rag/pipelines/llamaindex/storage.py +655 -0
- deeptutor/services/rag/pipelines/llamaindex/vector_store.py +288 -0
- deeptutor/services/rag/pipelines/modes.py +48 -0
- deeptutor/services/rag/pipelines/pageindex/__init__.py +47 -0
- deeptutor/services/rag/pipelines/pageindex/client.py +146 -0
- deeptutor/services/rag/pipelines/pageindex/config.py +49 -0
- deeptutor/services/rag/pipelines/pageindex/pipeline.py +313 -0
- deeptutor/services/rag/pipelines/pageindex/reasoning.py +262 -0
- deeptutor/services/rag/pipelines/pageindex/storage.py +133 -0
- deeptutor/services/rag/pipelines/pageindex/tools.py +221 -0
- deeptutor/services/rag/pipelines/weknora/__init__.py +13 -0
- deeptutor/services/rag/pipelines/weknora/client.py +106 -0
- deeptutor/services/rag/pipelines/weknora/config.py +56 -0
- deeptutor/services/rag/pipelines/weknora/pipeline.py +74 -0
- deeptutor/services/rag/pipelines/weknora/probe.py +80 -0
- deeptutor/services/rag/preflight.py +274 -0
- deeptutor/services/rag/provider_binding.py +70 -0
- deeptutor/services/rag/query_rewrite.py +186 -0
- deeptutor/services/rag/service.py +379 -0
- deeptutor/services/rag/smart_retriever.py +81 -0
- deeptutor/services/rag/source_pages.py +292 -0
- deeptutor/services/rag/source_visuals.py +434 -0
- deeptutor/services/rag/visual_assets.py +430 -0
- deeptutor/services/rag/visual_coverage.py +165 -0
- deeptutor/services/web_source/__init__.py +1 -0
- deeptutor/services/web_source/bilingual.py +255 -0
- deeptutor/services/web_source/crawler.py +681 -0
- deeptutor/services/web_source/html_extractor.py +643 -0
- deeptutor/services/web_source/markdown.py +28 -0
- deeptutor/services/web_source/navigation.py +72 -0
- deeptutor/services/web_source/repository.py +640 -0
- deeptutor/services/web_source/robots.py +182 -0
- deeptutor/services/web_source/scheduler.py +336 -0
- deeptutor/services/web_source/snapshot_assets.py +171 -0
- deeptutor/services/web_source/sync.py +253 -0
- deeptutor/services/workspace/kb_move.py +446 -0
- deeptutor/tools/rag_tool.py +110 -0
- deeptutor_cli/kb.py +756 -0
- deeptutor_knowledge-1.7.0.dist-info/METADATA +15 -0
- deeptutor_knowledge-1.7.0.dist-info/RECORD +168 -0
- deeptutor_knowledge-1.7.0.dist-info/WHEEL +5 -0
- deeptutor_knowledge-1.7.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""Bridge DeepTutor's runtime config into a GraphRAG ``settings.yaml``.
|
|
2
|
+
|
|
3
|
+
GraphRAG (microsoft/graphrag, 3.x) is a config-file-driven engine: it reads a
|
|
4
|
+
``settings.[yaml|json]`` from a project root and wires its own LiteLLM-backed
|
|
5
|
+
model clients from it. Rather than hand-build the deeply nested
|
|
6
|
+
``GraphRagConfig`` pydantic model, we generate a minimal ``settings.yaml`` from
|
|
7
|
+
DeepTutor's already-resolved LLM + embedding runtime config and let
|
|
8
|
+
``graphrag.config.load_config`` validate it.
|
|
9
|
+
|
|
10
|
+
Decoupling notes:
|
|
11
|
+
* The only knobs we set are the two model entries + storage layout. Everything
|
|
12
|
+
else (chunking, graph extraction, community reports, the four search configs)
|
|
13
|
+
defaults correctly because each model entry is named with GraphRAG's default
|
|
14
|
+
model id, so the workflow/search sections pick it up automatically.
|
|
15
|
+
* Built-in prompts are used (every ``prompt`` field defaults to ``None`` in
|
|
16
|
+
GraphRAG), so we never scaffold prompt files.
|
|
17
|
+
* Completion calls use DeepTutor's registered GraphRAG compatibility adapter,
|
|
18
|
+
while embeddings retain GraphRAG's stock LiteLLM path.
|
|
19
|
+
|
|
20
|
+
This is the single spot to touch if GraphRAG's config schema shifts between
|
|
21
|
+
releases; pin the dependency to the 3.x line (see ``pyproject`` extra).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
import logging
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
31
|
+
|
|
32
|
+
from deeptutor.services.config.embedding_endpoint import canonical_embedding_provider_name
|
|
33
|
+
from deeptutor.services.embedding.request_options import should_send_embedding_dimensions
|
|
34
|
+
|
|
35
|
+
from .errors import GraphRagEmbeddingProviderUnsupportedError
|
|
36
|
+
from .provider import (
|
|
37
|
+
COMPLETION_TYPE,
|
|
38
|
+
resolve_completion_call_args,
|
|
39
|
+
resolve_completion_model,
|
|
40
|
+
resolve_completion_provider,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger(__name__)
|
|
44
|
+
|
|
45
|
+
SETTINGS_FILENAME = "settings.yaml"
|
|
46
|
+
|
|
47
|
+
# GraphRAG's default model ids — naming our entries this way means every
|
|
48
|
+
# workflow/search section resolves to them without us spelling each one out.
|
|
49
|
+
COMPLETION_MODEL_ID = "default_completion_model"
|
|
50
|
+
EMBEDDING_MODEL_ID = "default_embedding_model"
|
|
51
|
+
|
|
52
|
+
# The four retrieval methods GraphRAG ships. ``local`` is the safest general
|
|
53
|
+
# default (entity-centric, cheaper than global map-reduce).
|
|
54
|
+
SUPPORTED_MODES = ("local", "global", "drift", "basic")
|
|
55
|
+
DEFAULT_MODE = "local"
|
|
56
|
+
|
|
57
|
+
# GraphRAG's stock LiteLLM embedding client uses an OpenAI-style transport and
|
|
58
|
+
# appends the ``/embeddings`` operation path to ``api_base``. DeepTutor stores a
|
|
59
|
+
# complete operation URL for these bindings, so only this explicitly compatible
|
|
60
|
+
# set can be translated safely. Native Cohere, Ollama, DashScope, and Azure
|
|
61
|
+
# transports require separate provider adapters and must not be guessed here.
|
|
62
|
+
OPENAI_COMPATIBLE_EMBEDDING_BINDINGS = frozenset(
|
|
63
|
+
{
|
|
64
|
+
"custom",
|
|
65
|
+
"custom_openai_sdk",
|
|
66
|
+
"gemini",
|
|
67
|
+
"jina",
|
|
68
|
+
"openai",
|
|
69
|
+
"openrouter",
|
|
70
|
+
"opper",
|
|
71
|
+
"siliconflow",
|
|
72
|
+
"vllm",
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class GraphRagNotAvailableError(RuntimeError):
|
|
78
|
+
"""Raised when the optional ``graphrag`` dependency is not installed."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class GraphRagNotConfiguredError(RuntimeError):
|
|
82
|
+
"""Raised when DeepTutor's LLM / embedding config can't back GraphRAG."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def is_graphrag_available() -> bool:
|
|
86
|
+
"""True when the optional ``graphrag`` package can be imported.
|
|
87
|
+
|
|
88
|
+
GraphRAG is heavy (LiteLLM, lancedb, graspologic, …) and ships as an opt-in
|
|
89
|
+
extra: ``pip install 'deeptutor[graphrag]'``. Until it is installed the
|
|
90
|
+
provider is hidden / blocked in the UI.
|
|
91
|
+
"""
|
|
92
|
+
import importlib.util
|
|
93
|
+
|
|
94
|
+
return importlib.util.find_spec("graphrag") is not None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def normalize_mode(mode: str | None) -> str:
|
|
98
|
+
"""Coerce a stored ``search_mode`` to a valid GraphRAG search method.
|
|
99
|
+
|
|
100
|
+
The per-KB ``search_mode`` field is shared across engines and defaults to
|
|
101
|
+
``"hybrid"`` (a LlamaIndex/LightRAG term). Anything that isn't a GraphRAG
|
|
102
|
+
method falls back to :data:`DEFAULT_MODE`.
|
|
103
|
+
"""
|
|
104
|
+
candidate = (mode or "").strip().lower()
|
|
105
|
+
return candidate if candidate in SUPPORTED_MODES else DEFAULT_MODE
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def graphrag_embedding_api_base(binding: str | None, endpoint: str | None) -> str:
|
|
109
|
+
"""Translate a DeepTutor embedding endpoint into GraphRAG ``api_base``.
|
|
110
|
+
|
|
111
|
+
DeepTutor's public embedding contract stores and calls the complete
|
|
112
|
+
operation URL. GraphRAG's LiteLLM client expects the API root and appends
|
|
113
|
+
``/embeddings`` itself. Strip exactly one terminal path segment only for
|
|
114
|
+
known OpenAI-compatible transports. Query-bearing URLs are left untouched
|
|
115
|
+
because the OpenAI SDK does not preserve operation semantics reliably when
|
|
116
|
+
query parameters are embedded in ``base_url``.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
binding: Active DeepTutor embedding binding.
|
|
120
|
+
endpoint: Fully qualified endpoint saved in the model catalog.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
The API base GraphRAG should pass to LiteLLM.
|
|
124
|
+
"""
|
|
125
|
+
value = str(endpoint or "").strip()
|
|
126
|
+
provider = canonical_embedding_provider_name(binding)
|
|
127
|
+
if not value or provider not in OPENAI_COMPATIBLE_EMBEDDING_BINDINGS:
|
|
128
|
+
return value
|
|
129
|
+
|
|
130
|
+
parsed = urlsplit(value)
|
|
131
|
+
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
|
132
|
+
return value
|
|
133
|
+
if parsed.query or parsed.fragment:
|
|
134
|
+
return value
|
|
135
|
+
|
|
136
|
+
path = parsed.path.rstrip("/")
|
|
137
|
+
if not path.endswith("/embeddings"):
|
|
138
|
+
return value
|
|
139
|
+
|
|
140
|
+
api_path = path[: -len("/embeddings")] or "/"
|
|
141
|
+
return urlunsplit(parsed._replace(path=api_path))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def ensure_graphrag_embedding_transport(
|
|
145
|
+
binding: str | None,
|
|
146
|
+
endpoint: str | None,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Reject native embedding transports that GraphRAG cannot call safely."""
|
|
149
|
+
provider = canonical_embedding_provider_name(binding)
|
|
150
|
+
if provider not in OPENAI_COMPATIBLE_EMBEDDING_BINDINGS:
|
|
151
|
+
raise GraphRagEmbeddingProviderUnsupportedError()
|
|
152
|
+
# Gemini can use either DeepTutor's native ``batchEmbedContents`` adapter
|
|
153
|
+
# or its legacy OpenAI-compatible endpoint. GraphRAG only supports the
|
|
154
|
+
# latter; a provider name alone is no longer enough after Gemini 2 support.
|
|
155
|
+
if provider == "gemini" and not urlsplit(str(endpoint or "")).path.rstrip("/").endswith(
|
|
156
|
+
"/embeddings"
|
|
157
|
+
):
|
|
158
|
+
raise GraphRagEmbeddingProviderUnsupportedError()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass(frozen=True)
|
|
162
|
+
class GraphRagQueryConfig:
|
|
163
|
+
"""Query-time knobs read from the persisted ``graphrag.json`` slice."""
|
|
164
|
+
|
|
165
|
+
response_type: str = "Multiple Paragraphs"
|
|
166
|
+
community_level: int = 2
|
|
167
|
+
dynamic_community_selection: bool = False
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def query_config_from_settings() -> GraphRagQueryConfig:
|
|
171
|
+
"""Load GraphRAG query knobs from runtime settings (defaults on any error)."""
|
|
172
|
+
try:
|
|
173
|
+
from deeptutor.services.config import load_graphrag_settings
|
|
174
|
+
|
|
175
|
+
settings = load_graphrag_settings()
|
|
176
|
+
return GraphRagQueryConfig(
|
|
177
|
+
response_type=str(settings.get("response_type") or "Multiple Paragraphs"),
|
|
178
|
+
community_level=int(settings.get("community_level", 2)),
|
|
179
|
+
dynamic_community_selection=bool(settings.get("dynamic_community_selection", False)),
|
|
180
|
+
)
|
|
181
|
+
except Exception:
|
|
182
|
+
return GraphRagQueryConfig()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _embedding_model_entry(
|
|
186
|
+
*,
|
|
187
|
+
model: str,
|
|
188
|
+
api_base: str | None,
|
|
189
|
+
api_key: str | None,
|
|
190
|
+
binding: str | None,
|
|
191
|
+
dimension: int,
|
|
192
|
+
send_dimensions: bool | None,
|
|
193
|
+
extra_headers: dict[str, str] | None,
|
|
194
|
+
) -> dict[str, Any]:
|
|
195
|
+
entry: dict[str, Any] = {
|
|
196
|
+
"model_provider": "openai",
|
|
197
|
+
"model": model,
|
|
198
|
+
"auth_method": "api_key",
|
|
199
|
+
}
|
|
200
|
+
if api_base:
|
|
201
|
+
entry["api_base"] = api_base
|
|
202
|
+
# GraphRAG validates that a key is present for ``auth_method: api_key``; local
|
|
203
|
+
# OpenAI-compatible servers accept a placeholder.
|
|
204
|
+
entry["api_key"] = api_key or "sk-no-key-required"
|
|
205
|
+
call_args: dict[str, Any] = {}
|
|
206
|
+
if isinstance(extra_headers, dict) and extra_headers:
|
|
207
|
+
call_args["extra_headers"] = dict(extra_headers)
|
|
208
|
+
if should_send_embedding_dimensions(
|
|
209
|
+
binding=binding,
|
|
210
|
+
model=model,
|
|
211
|
+
dimension=dimension,
|
|
212
|
+
send_dimensions=send_dimensions,
|
|
213
|
+
):
|
|
214
|
+
call_args["dimensions"] = dimension
|
|
215
|
+
if call_args:
|
|
216
|
+
entry["call_args"] = call_args
|
|
217
|
+
return entry
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _completion_model_entry(llm_cfg: Any, *, api_base: str | None) -> dict[str, Any]:
|
|
221
|
+
entry: dict[str, Any] = {
|
|
222
|
+
"type": COMPLETION_TYPE,
|
|
223
|
+
"model_provider": resolve_completion_provider(llm_cfg),
|
|
224
|
+
"model": resolve_completion_model(llm_cfg),
|
|
225
|
+
"auth_method": "api_key",
|
|
226
|
+
"api_key": getattr(llm_cfg, "api_key", None) or "sk-no-key-required",
|
|
227
|
+
}
|
|
228
|
+
if api_base:
|
|
229
|
+
entry["api_base"] = api_base
|
|
230
|
+
api_version = getattr(llm_cfg, "api_version", None)
|
|
231
|
+
if api_version:
|
|
232
|
+
entry["api_version"] = api_version
|
|
233
|
+
call_args = resolve_completion_call_args(llm_cfg)
|
|
234
|
+
if call_args:
|
|
235
|
+
entry["call_args"] = call_args
|
|
236
|
+
return entry
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def build_settings(*, llm_cfg: Any = None, embedding_cfg: Any = None) -> dict[str, Any]:
|
|
240
|
+
"""Assemble the GraphRAG ``settings.yaml`` payload from DeepTutor config.
|
|
241
|
+
|
|
242
|
+
``llm_cfg`` / ``embedding_cfg`` are injectable for tests; in production they
|
|
243
|
+
are resolved from DeepTutor's catalog. Raises
|
|
244
|
+
:class:`GraphRagNotConfiguredError` if either side has no usable model.
|
|
245
|
+
"""
|
|
246
|
+
if llm_cfg is None:
|
|
247
|
+
from deeptutor.services.config import resolve_llm_runtime_config
|
|
248
|
+
|
|
249
|
+
llm_cfg = resolve_llm_runtime_config()
|
|
250
|
+
if embedding_cfg is None:
|
|
251
|
+
from deeptutor.services.embedding import get_embedding_config
|
|
252
|
+
|
|
253
|
+
embedding_cfg = get_embedding_config()
|
|
254
|
+
|
|
255
|
+
chat_model = getattr(llm_cfg, "model", None)
|
|
256
|
+
embed_model = getattr(embedding_cfg, "model", None)
|
|
257
|
+
embed_dim = int(getattr(embedding_cfg, "dim", 0) or 0)
|
|
258
|
+
if not chat_model:
|
|
259
|
+
raise GraphRagNotConfiguredError(
|
|
260
|
+
"No active chat model. Configure one under Settings → Catalog before "
|
|
261
|
+
"creating a GraphRAG knowledge base."
|
|
262
|
+
)
|
|
263
|
+
if not embed_model:
|
|
264
|
+
raise GraphRagNotConfiguredError(
|
|
265
|
+
"No active embedding model. Configure one under Settings → Catalog "
|
|
266
|
+
"before creating a GraphRAG knowledge base."
|
|
267
|
+
)
|
|
268
|
+
if not embed_dim:
|
|
269
|
+
raise GraphRagNotConfiguredError(
|
|
270
|
+
"No active embedding model with a known dimension. Configure one under "
|
|
271
|
+
"Settings → Catalog before creating a GraphRAG knowledge base."
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
embedding_binding = str(getattr(embedding_cfg, "binding", "") or "")
|
|
275
|
+
llm_base = getattr(llm_cfg, "effective_url", None) or getattr(llm_cfg, "base_url", None)
|
|
276
|
+
embed_endpoint = getattr(embedding_cfg, "effective_url", None) or getattr(
|
|
277
|
+
embedding_cfg, "base_url", None
|
|
278
|
+
)
|
|
279
|
+
ensure_graphrag_embedding_transport(embedding_binding, embed_endpoint)
|
|
280
|
+
embed_base = graphrag_embedding_api_base(embedding_binding, embed_endpoint)
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
"completion_models": {
|
|
284
|
+
COMPLETION_MODEL_ID: _completion_model_entry(llm_cfg, api_base=llm_base),
|
|
285
|
+
},
|
|
286
|
+
"embedding_models": {
|
|
287
|
+
EMBEDDING_MODEL_ID: _embedding_model_entry(
|
|
288
|
+
model=embed_model,
|
|
289
|
+
api_base=embed_base,
|
|
290
|
+
api_key=getattr(embedding_cfg, "api_key", None),
|
|
291
|
+
binding=embedding_binding,
|
|
292
|
+
dimension=embed_dim,
|
|
293
|
+
send_dimensions=getattr(embedding_cfg, "send_dimensions", None),
|
|
294
|
+
extra_headers=getattr(embedding_cfg, "extra_headers", None),
|
|
295
|
+
),
|
|
296
|
+
},
|
|
297
|
+
# Plain-text input: DeepTutor's ingestion writes parsed ``.txt`` files
|
|
298
|
+
# into ``input/`` (see ``ingestion.py``) so GraphRAG never parses
|
|
299
|
+
# documents itself. The ``$`` is escaped as ``$$`` because GraphRAG's
|
|
300
|
+
# loader treats the whole config text as a ``string.Template`` and does
|
|
301
|
+
# env-var substitution on it before parsing the YAML; a bare ``$`` is
|
|
302
|
+
# an "Invalid placeholder" to that pass.
|
|
303
|
+
"input": {"type": "text", "file_pattern": r".*\.txt$$"},
|
|
304
|
+
"input_storage": {"type": "file", "base_dir": "input"},
|
|
305
|
+
"output_storage": {"type": "file", "base_dir": "output"},
|
|
306
|
+
# "json" is GraphRAG's on-disk cache backend id (registered in
|
|
307
|
+
# graphrag_cache.CacheFactory); "file" is a *storage* type, not a cache
|
|
308
|
+
# type, and is rejected the first time the pipeline builds a cache.
|
|
309
|
+
"cache": {"type": "json", "storage": {"type": "file", "base_dir": "cache"}},
|
|
310
|
+
"reporting": {"type": "file", "base_dir": "logs"},
|
|
311
|
+
# GraphRAG/LanceDB defaults to 3072 dimensions; DeepTutor must stamp the
|
|
312
|
+
# active embedding dimension so Qwen-4096 and other non-default models work.
|
|
313
|
+
"vector_store": {
|
|
314
|
+
"type": "lancedb",
|
|
315
|
+
"db_uri": "output/lancedb",
|
|
316
|
+
"vector_size": embed_dim,
|
|
317
|
+
},
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def write_settings_payload(root_dir: Path, settings: dict[str, Any]) -> Path:
|
|
322
|
+
"""Write a previously-built settings snapshot into ``root_dir``."""
|
|
323
|
+
import yaml
|
|
324
|
+
|
|
325
|
+
root_dir = Path(root_dir)
|
|
326
|
+
root_dir.mkdir(parents=True, exist_ok=True)
|
|
327
|
+
path = root_dir / SETTINGS_FILENAME
|
|
328
|
+
with open(path, "w", encoding="utf-8") as handle:
|
|
329
|
+
yaml.safe_dump(settings, handle, sort_keys=False, allow_unicode=True)
|
|
330
|
+
return path
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def write_settings(root_dir: Path, *, llm_cfg: Any = None, embedding_cfg: Any = None) -> Path:
|
|
334
|
+
"""Build and write ``settings.yaml`` into ``root_dir``."""
|
|
335
|
+
settings = build_settings(llm_cfg=llm_cfg, embedding_cfg=embedding_cfg)
|
|
336
|
+
return write_settings_payload(root_dir, settings)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
__all__ = [
|
|
340
|
+
"SETTINGS_FILENAME",
|
|
341
|
+
"COMPLETION_MODEL_ID",
|
|
342
|
+
"EMBEDDING_MODEL_ID",
|
|
343
|
+
"SUPPORTED_MODES",
|
|
344
|
+
"DEFAULT_MODE",
|
|
345
|
+
"OPENAI_COMPATIBLE_EMBEDDING_BINDINGS",
|
|
346
|
+
"GraphRagNotAvailableError",
|
|
347
|
+
"GraphRagNotConfiguredError",
|
|
348
|
+
"GraphRagQueryConfig",
|
|
349
|
+
"ensure_graphrag_embedding_transport",
|
|
350
|
+
"graphrag_embedding_api_base",
|
|
351
|
+
"is_graphrag_available",
|
|
352
|
+
"normalize_mode",
|
|
353
|
+
"query_config_from_settings",
|
|
354
|
+
"build_settings",
|
|
355
|
+
"write_settings",
|
|
356
|
+
"write_settings_payload",
|
|
357
|
+
]
|