minder-cli 0.2.5__tar.gz → 0.2.7__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.
- {minder_cli-0.2.5 → minder_cli-0.2.7}/PKG-INFO +10 -9
- {minder_cli-0.2.5 → minder_cli-0.2.7}/pyproject.toml +12 -11
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/config.py +6 -6
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/continuity.py +3 -3
- minder_cli-0.2.7/src/minder/embedding/local.py +76 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/graph.py +4 -3
- minder_cli-0.2.7/src/minder/llm/local.py +233 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/memories.py +2 -1
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/search.py +4 -2
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/prompts/formatter.py +3 -3
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/server.py +10 -6
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/milvus/client.py +1 -1
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/milvus/collections.py +1 -1
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/memory.py +2 -1
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/query.py +4 -3
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/skills.py +2 -1
- minder_cli-0.2.5/src/minder/embedding/local.py +0 -65
- minder_cli-0.2.5/src/minder/llm/local.py +0 -381
- {minder_cli-0.2.5 → minder_cli-0.2.7}/.gitignore +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/LICENSE +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/README.md +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/api/routers/prompts.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/application/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/application/admin/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/application/admin/dto.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/application/admin/jobs.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/application/admin/use_cases.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/context.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/middleware.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/principal.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/rate_limiter.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/rbac.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/auth/service.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/bootstrap/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/bootstrap/providers.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/bootstrap/transport.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/cache/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/cache/providers.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/chunking/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/chunking/code_splitter.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/chunking/splitter.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/cli.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/dev.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/embedding/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/embedding/base.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/embedding/openai.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/edges.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/executor.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/evaluator.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/guard.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/llm.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/planning.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/reasoning.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/reranker.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/retriever.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/verification.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/nodes/workflow_planner.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/runtime.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/graph/state.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/llm/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/llm/base.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/llm/openai.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/base.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/client.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/document.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/error.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/graph.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/history.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/job.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/prompt.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/repository.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/rule.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/session.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/skill.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/user.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/models/workflow.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/observability/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/observability/audit.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/observability/logging.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/observability/metrics.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/observability/tracing.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/api.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/context.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/dashboard.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/jobs.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/prompts.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/routes.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/runtime.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/presentation/http/admin/skills.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/prompts/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/resources/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/retrieval/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/retrieval/hybrid.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/retrieval/mmr.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/retrieval/multi_hop.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/runtime.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/document.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/error.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/feedback.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/graph.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/history.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/interfaces.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/milvus/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/milvus/vector_store.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/mongodb/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/mongodb/client.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/mongodb/indexes.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/mongodb/operational_store.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/relational.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/repo_state.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/rule.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/store/vector.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/auth.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/graph.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/ingest.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/registry.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/repo_scanner.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/search.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/session.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/tools/workflow.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/transport/__init__.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/transport/base.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/transport/sse.py +0 -0
- {minder_cli-0.2.5 → minder_cli-0.2.7}/src/minder/transport/stdio.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: minder-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Minder CLI is the command-line interface for the Minder self-hosted MCP platform.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hiimtrung/minder
|
|
6
6
|
Project-URL: Repository, https://github.com/hiimtrung/minder
|
|
@@ -12,16 +12,15 @@ Classifier: Environment :: Console
|
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
-
Requires-Python: >=3.
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
18
|
Requires-Dist: fastapi>=0.136.0
|
|
19
|
-
Requires-Dist: httpx>=0.
|
|
19
|
+
Requires-Dist: httpx>=0.28.0
|
|
20
20
|
Provides-Extra: server
|
|
21
21
|
Requires-Dist: aiosqlite>=0.21.0; extra == 'server'
|
|
22
|
-
Requires-Dist: langgraph>=1.1.
|
|
22
|
+
Requires-Dist: langgraph>=1.1.8; extra == 'server'
|
|
23
23
|
Requires-Dist: litellm>=1.83.1; extra == 'server'
|
|
24
|
-
Requires-Dist: llama-cpp-python>=0.3.20; extra == 'server'
|
|
25
24
|
Requires-Dist: mcp>=1.26.0; extra == 'server'
|
|
26
25
|
Requires-Dist: motor>=3.7.0; extra == 'server'
|
|
27
26
|
Requires-Dist: passlib[bcrypt]>=1.7.4; extra == 'server'
|
|
@@ -29,9 +28,11 @@ Requires-Dist: prometheus-client>=0.24.1; extra == 'server'
|
|
|
29
28
|
Requires-Dist: pydantic-settings[toml]>=2.13.1; extra == 'server'
|
|
30
29
|
Requires-Dist: pydantic>=2.12.5; extra == 'server'
|
|
31
30
|
Requires-Dist: pyjwt>=2.12.1; extra == 'server'
|
|
32
|
-
Requires-Dist: pymilvus>=2.
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: pymilvus>=2.6.12; extra == 'server'
|
|
32
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.49; extra == 'server'
|
|
33
|
+
Requires-Dist: yarl>=1.16.0; extra == 'server'
|
|
34
|
+
Requires-Dist: zipp>=3.21.0; extra == 'server'
|
|
35
|
+
Requires-Dist: zstandard>=0.25.0; extra == 'server'
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
36
37
|
|
|
37
38
|
# Minder CLI
|
|
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "minder-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.7"
|
|
8
8
|
description = "Minder CLI is the command-line interface for the Minder self-hosted MCP platform."
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
11
|
keywords = ["mcp", "cli", "ai", "code-search", "workflow"]
|
|
12
12
|
classifiers = [
|
|
13
13
|
"Development Status :: 3 - Alpha",
|
|
@@ -15,20 +15,19 @@ classifiers = [
|
|
|
15
15
|
"Intended Audience :: Developers",
|
|
16
16
|
"License :: OSI Approved :: Apache Software License",
|
|
17
17
|
"Programming Language :: Python :: 3",
|
|
18
|
-
"Programming Language :: Python :: 3.
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
19
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
22
|
"fastapi>=0.136.0",
|
|
23
|
-
"httpx>=0.
|
|
23
|
+
"httpx>=0.28.0",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[project.optional-dependencies]
|
|
27
27
|
server = [
|
|
28
28
|
"aiosqlite>=0.21.0",
|
|
29
|
-
"langgraph>=1.1.
|
|
29
|
+
"langgraph>=1.1.8",
|
|
30
30
|
"litellm>=1.83.1",
|
|
31
|
-
"llama-cpp-python>=0.3.20",
|
|
32
31
|
"mcp>=1.26.0",
|
|
33
32
|
"motor>=3.7.0",
|
|
34
33
|
"passlib[bcrypt]>=1.7.4",
|
|
@@ -36,9 +35,11 @@ server = [
|
|
|
36
35
|
"pydantic>=2.12.5",
|
|
37
36
|
"pydantic-settings[toml]>=2.13.1",
|
|
38
37
|
"pyjwt>=2.12.1",
|
|
39
|
-
"pymilvus>=2.
|
|
40
|
-
"
|
|
41
|
-
"
|
|
38
|
+
"pymilvus>=2.6.12",
|
|
39
|
+
"sqlalchemy[asyncio]>=2.0.49",
|
|
40
|
+
"yarl>=1.16.0",
|
|
41
|
+
"zipp>=3.21.0",
|
|
42
|
+
"zstandard>=0.25.0",
|
|
42
43
|
]
|
|
43
44
|
|
|
44
45
|
[project.urls]
|
|
@@ -51,8 +52,8 @@ minder = "minder.cli:main"
|
|
|
51
52
|
|
|
52
53
|
[dependency-groups]
|
|
53
54
|
dev = [
|
|
54
|
-
"fakeredis[lua]>=2.
|
|
55
|
-
"mypy>=1.20.
|
|
55
|
+
"fakeredis[lua]>=2.27.0",
|
|
56
|
+
"mypy>=1.20.1",
|
|
56
57
|
"pytest>=9.0.2",
|
|
57
58
|
"pytest-asyncio>=1.3.0",
|
|
58
59
|
"pytest-timeout>=2.3.1",
|
|
@@ -31,18 +31,18 @@ class AuthConfig(BaseModel):
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class EmbeddingConfig(BaseModel):
|
|
34
|
-
provider: str = "
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
provider: str = "ollama"
|
|
35
|
+
ollama_url: str = "http://localhost:11434"
|
|
36
|
+
ollama_model: str = "embeddinggemma"
|
|
37
37
|
dimensions: int = 768
|
|
38
38
|
openai_api_key: Optional[str] = None
|
|
39
39
|
openai_model: str = "text-embedding-3-small"
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class LLMConfig(BaseModel):
|
|
43
|
-
provider: str = "
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
provider: str = "ollama"
|
|
44
|
+
ollama_url: str = "http://localhost:11434"
|
|
45
|
+
ollama_model: str = "gemma3:4b"
|
|
46
46
|
context_length: int = 131072
|
|
47
47
|
temperature: float = 0.1
|
|
48
48
|
openai_api_key: Optional[str] = None
|
|
@@ -272,8 +272,8 @@ class ContinuitySynthesizer:
|
|
|
272
272
|
|
|
273
273
|
self._config = config
|
|
274
274
|
self._llm = LocalModelLLM(
|
|
275
|
-
config.llm.
|
|
276
|
-
|
|
275
|
+
ollama_url=config.llm.ollama_url,
|
|
276
|
+
ollama_model=config.llm.ollama_model,
|
|
277
277
|
context_length=config.llm.context_length,
|
|
278
278
|
)
|
|
279
279
|
|
|
@@ -312,7 +312,7 @@ class ContinuitySynthesizer:
|
|
|
312
312
|
if not parsed:
|
|
313
313
|
return fallback, {
|
|
314
314
|
"provider": "heuristic",
|
|
315
|
-
"model": self._config.llm.
|
|
315
|
+
"model": self._config.llm.ollama_model,
|
|
316
316
|
"runtime": self._llm.runtime,
|
|
317
317
|
}
|
|
318
318
|
return {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Local Embedding provider — delegates to Ollama HTTP API.
|
|
3
|
+
|
|
4
|
+
Falls back to a deterministic hash-based stub when Ollama is unreachable.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from urllib.request import Request, urlopen
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LocalEmbeddingProvider:
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
ollama_url: str = "http://localhost:11434",
|
|
21
|
+
ollama_model: str = "embeddinggemma",
|
|
22
|
+
dimensions: int = 768,
|
|
23
|
+
runtime: str = "auto",
|
|
24
|
+
) -> None:
|
|
25
|
+
self._ollama_url = ollama_url.rstrip("/")
|
|
26
|
+
self._ollama_model = ollama_model
|
|
27
|
+
self._dimensions = dimensions
|
|
28
|
+
self._runtime = runtime
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def runtime(self) -> str:
|
|
32
|
+
runtime = self._runtime
|
|
33
|
+
if runtime == "auto":
|
|
34
|
+
try:
|
|
35
|
+
req = Request(f"{self._ollama_url}/api/tags", method="GET")
|
|
36
|
+
with urlopen(req, timeout=3):
|
|
37
|
+
return "ollama"
|
|
38
|
+
except Exception:
|
|
39
|
+
return "mock"
|
|
40
|
+
return runtime
|
|
41
|
+
|
|
42
|
+
def embed(self, text: str) -> list[float]:
|
|
43
|
+
if self.runtime == "ollama":
|
|
44
|
+
embedded = self._embed_with_ollama(text)
|
|
45
|
+
if embedded is not None:
|
|
46
|
+
return embedded[: self._dimensions]
|
|
47
|
+
# Deterministic hash-based fallback
|
|
48
|
+
digest = hashlib.sha256(text.encode("utf-8")).digest()
|
|
49
|
+
values: list[float] = []
|
|
50
|
+
for index in range(self._dimensions):
|
|
51
|
+
byte = digest[index % len(digest)]
|
|
52
|
+
values.append(round(byte / 255.0, 6))
|
|
53
|
+
return values
|
|
54
|
+
|
|
55
|
+
def _embed_with_ollama(self, text: str) -> list[float] | None:
|
|
56
|
+
payload = {
|
|
57
|
+
"model": self._ollama_model,
|
|
58
|
+
"input": text,
|
|
59
|
+
}
|
|
60
|
+
data = json.dumps(payload).encode()
|
|
61
|
+
req = Request(
|
|
62
|
+
f"{self._ollama_url}/api/embed",
|
|
63
|
+
data=data,
|
|
64
|
+
headers={"Content-Type": "application/json"},
|
|
65
|
+
method="POST",
|
|
66
|
+
)
|
|
67
|
+
try:
|
|
68
|
+
with urlopen(req, timeout=30) as resp:
|
|
69
|
+
body = json.loads(resp.read())
|
|
70
|
+
embeddings = body.get("embeddings", [])
|
|
71
|
+
if embeddings and isinstance(embeddings[0], list):
|
|
72
|
+
return [float(v) for v in embeddings[0]]
|
|
73
|
+
return None
|
|
74
|
+
except Exception:
|
|
75
|
+
logger.warning("Ollama embedding failed, using hash fallback")
|
|
76
|
+
return None
|
|
@@ -58,7 +58,8 @@ class MinderGraph:
|
|
|
58
58
|
self._planning = planning or PlanningNode()
|
|
59
59
|
vector_store = VectorStore(store, store)
|
|
60
60
|
embedder = LocalEmbeddingProvider(
|
|
61
|
-
config.embedding.
|
|
61
|
+
ollama_url=config.embedding.ollama_url,
|
|
62
|
+
ollama_model=config.embedding.ollama_model,
|
|
62
63
|
dimensions=config.embedding.dimensions,
|
|
63
64
|
runtime="auto",
|
|
64
65
|
)
|
|
@@ -72,8 +73,8 @@ class MinderGraph:
|
|
|
72
73
|
self._reasoning = reasoning or ReasoningNode()
|
|
73
74
|
self._llm = llm or LLMNode(
|
|
74
75
|
primary=LocalModelLLM(
|
|
75
|
-
config.llm.
|
|
76
|
-
|
|
76
|
+
ollama_url=config.llm.ollama_url,
|
|
77
|
+
ollama_model=config.llm.ollama_model,
|
|
77
78
|
context_length=config.llm.context_length,
|
|
78
79
|
),
|
|
79
80
|
fallback=OpenAIFallbackLLM(
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Local LLM provider — delegates inference to an Ollama HTTP server.
|
|
3
|
+
|
|
4
|
+
Ollama must be running and accessible at the configured URL (default
|
|
5
|
+
``http://localhost:11434``). The provider gracefully falls back to a
|
|
6
|
+
deterministic text stub when Ollama is unreachable so that the rest of
|
|
7
|
+
the Minder pipeline can still operate.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from collections.abc import Generator
|
|
15
|
+
from urllib.request import Request, urlopen
|
|
16
|
+
|
|
17
|
+
from minder.graph.state import GraphState
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LocalModelLLM:
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
ollama_url: str = "http://localhost:11434",
|
|
27
|
+
ollama_model: str = "gemma3:4b",
|
|
28
|
+
fail: bool = False,
|
|
29
|
+
context_length: int = 131072,
|
|
30
|
+
) -> None:
|
|
31
|
+
self._ollama_url = ollama_url.rstrip("/")
|
|
32
|
+
self._ollama_model = ollama_model
|
|
33
|
+
self._fail = fail
|
|
34
|
+
self._context_length = max(512, context_length)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def runtime(self) -> str:
|
|
38
|
+
if self._fail:
|
|
39
|
+
return "mock"
|
|
40
|
+
try:
|
|
41
|
+
req = Request(f"{self._ollama_url}/api/tags", method="GET")
|
|
42
|
+
with urlopen(req, timeout=3):
|
|
43
|
+
return "ollama"
|
|
44
|
+
except Exception:
|
|
45
|
+
return "mock"
|
|
46
|
+
|
|
47
|
+
# ------------------------------------------------------------------
|
|
48
|
+
# Public API
|
|
49
|
+
# ------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
def generate(self, state: GraphState) -> dict[str, object]:
|
|
52
|
+
if self._fail:
|
|
53
|
+
raise RuntimeError("Local model unavailable")
|
|
54
|
+
|
|
55
|
+
runtime = self.runtime
|
|
56
|
+
source_paths = [doc["path"] for doc in state.reranked_docs[:3]]
|
|
57
|
+
guidance = state.workflow_context.get("guidance", "")
|
|
58
|
+
fallback = (
|
|
59
|
+
f"{guidance}\n"
|
|
60
|
+
f"Plan intent: {state.plan.get('intent', 'unknown')}.\n"
|
|
61
|
+
f"Answer: grounded response for '{state.query}'.\n"
|
|
62
|
+
f"Sources: {', '.join(source_paths) if source_paths else 'none'}."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
text = fallback
|
|
66
|
+
if runtime == "ollama":
|
|
67
|
+
text = self._generate_with_ollama(state, fallback=fallback)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"text": text,
|
|
71
|
+
"sources": source_paths,
|
|
72
|
+
"provider": "local_llm",
|
|
73
|
+
"model": self._ollama_model,
|
|
74
|
+
"runtime": runtime,
|
|
75
|
+
"stream": [line for line in text.splitlines() if line],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def stream_generate(
|
|
79
|
+
self, state: GraphState
|
|
80
|
+
) -> Generator[dict[str, object], None, None]:
|
|
81
|
+
if self._fail:
|
|
82
|
+
raise RuntimeError("Local model unavailable")
|
|
83
|
+
|
|
84
|
+
runtime = self.runtime
|
|
85
|
+
source_paths = [doc["path"] for doc in state.reranked_docs[:3]]
|
|
86
|
+
guidance = state.workflow_context.get("guidance", "")
|
|
87
|
+
fallback = (
|
|
88
|
+
f"{guidance}\n"
|
|
89
|
+
f"Plan intent: {state.plan.get('intent', 'unknown')}.\n"
|
|
90
|
+
f"Answer: grounded response for '{state.query}'.\n"
|
|
91
|
+
f"Sources: {', '.join(source_paths) if source_paths else 'none'}."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if runtime != "ollama":
|
|
95
|
+
if fallback:
|
|
96
|
+
yield {"type": "chunk", "delta": fallback}
|
|
97
|
+
yield {
|
|
98
|
+
"type": "result",
|
|
99
|
+
"result": self._build_result(fallback, source_paths, runtime),
|
|
100
|
+
}
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
deltas: list[str] = []
|
|
104
|
+
try:
|
|
105
|
+
for delta in self._stream_ollama(str(state.query)):
|
|
106
|
+
deltas.append(delta)
|
|
107
|
+
yield {"type": "chunk", "delta": delta}
|
|
108
|
+
except Exception:
|
|
109
|
+
logger.warning("Ollama stream failed, using fallback")
|
|
110
|
+
if fallback:
|
|
111
|
+
yield {"type": "chunk", "delta": fallback}
|
|
112
|
+
deltas = [fallback] if fallback else []
|
|
113
|
+
|
|
114
|
+
text = "".join(deltas).strip() or fallback
|
|
115
|
+
yield {
|
|
116
|
+
"type": "result",
|
|
117
|
+
"result": self._build_result(text, source_paths, runtime, deltas),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def complete_text(
|
|
121
|
+
self,
|
|
122
|
+
prompt: str,
|
|
123
|
+
*,
|
|
124
|
+
max_tokens: int = 512,
|
|
125
|
+
temperature: float = 0.1,
|
|
126
|
+
fallback: str = "",
|
|
127
|
+
) -> str:
|
|
128
|
+
if self._fail:
|
|
129
|
+
raise RuntimeError("Local model unavailable")
|
|
130
|
+
|
|
131
|
+
if self.runtime != "ollama":
|
|
132
|
+
return fallback
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
return self._chat_ollama(prompt, max_tokens=max_tokens, temperature=temperature)
|
|
136
|
+
except Exception:
|
|
137
|
+
logger.warning("Ollama completion failed, using fallback")
|
|
138
|
+
return fallback
|
|
139
|
+
|
|
140
|
+
# ------------------------------------------------------------------
|
|
141
|
+
# Ollama HTTP helpers
|
|
142
|
+
# ------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
def _chat_ollama(
|
|
145
|
+
self,
|
|
146
|
+
prompt: str,
|
|
147
|
+
*,
|
|
148
|
+
max_tokens: int = 512,
|
|
149
|
+
temperature: float = 0.1,
|
|
150
|
+
) -> str:
|
|
151
|
+
payload = {
|
|
152
|
+
"model": self._ollama_model,
|
|
153
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
154
|
+
"stream": False,
|
|
155
|
+
"options": {
|
|
156
|
+
"temperature": temperature,
|
|
157
|
+
"num_predict": max_tokens,
|
|
158
|
+
"num_ctx": self._context_length,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
data = json.dumps(payload).encode()
|
|
162
|
+
req = Request(
|
|
163
|
+
f"{self._ollama_url}/api/chat",
|
|
164
|
+
data=data,
|
|
165
|
+
headers={"Content-Type": "application/json"},
|
|
166
|
+
method="POST",
|
|
167
|
+
)
|
|
168
|
+
with urlopen(req, timeout=120) as resp:
|
|
169
|
+
body = json.loads(resp.read())
|
|
170
|
+
return body.get("message", {}).get("content", "").strip()
|
|
171
|
+
|
|
172
|
+
def _stream_ollama(
|
|
173
|
+
self,
|
|
174
|
+
prompt: str,
|
|
175
|
+
*,
|
|
176
|
+
max_tokens: int = 256,
|
|
177
|
+
temperature: float = 0.1,
|
|
178
|
+
) -> Generator[str, None, None]:
|
|
179
|
+
payload = {
|
|
180
|
+
"model": self._ollama_model,
|
|
181
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
182
|
+
"stream": True,
|
|
183
|
+
"options": {
|
|
184
|
+
"temperature": temperature,
|
|
185
|
+
"num_predict": max_tokens,
|
|
186
|
+
"num_ctx": self._context_length,
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
data = json.dumps(payload).encode()
|
|
190
|
+
req = Request(
|
|
191
|
+
f"{self._ollama_url}/api/chat",
|
|
192
|
+
data=data,
|
|
193
|
+
headers={"Content-Type": "application/json"},
|
|
194
|
+
method="POST",
|
|
195
|
+
)
|
|
196
|
+
with urlopen(req, timeout=120) as resp:
|
|
197
|
+
for line in resp:
|
|
198
|
+
if not line:
|
|
199
|
+
continue
|
|
200
|
+
chunk = json.loads(line)
|
|
201
|
+
content = chunk.get("message", {}).get("content", "")
|
|
202
|
+
if content:
|
|
203
|
+
yield content
|
|
204
|
+
|
|
205
|
+
def _generate_with_ollama(self, state: GraphState, *, fallback: str) -> str:
|
|
206
|
+
reasoning_output = getattr(state, "reasoning_output", {}) or {}
|
|
207
|
+
prompt = reasoning_output.get("prompt") or state.query
|
|
208
|
+
return self.complete_text(
|
|
209
|
+
str(prompt),
|
|
210
|
+
max_tokens=256,
|
|
211
|
+
temperature=0.1,
|
|
212
|
+
fallback=fallback,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# ------------------------------------------------------------------
|
|
216
|
+
# Helpers
|
|
217
|
+
# ------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
def _build_result(
|
|
220
|
+
self,
|
|
221
|
+
text: str,
|
|
222
|
+
source_paths: list[str],
|
|
223
|
+
runtime: str,
|
|
224
|
+
stream: list[str] | None = None,
|
|
225
|
+
) -> dict[str, object]:
|
|
226
|
+
return {
|
|
227
|
+
"text": text,
|
|
228
|
+
"sources": source_paths,
|
|
229
|
+
"provider": "local_llm",
|
|
230
|
+
"model": self._ollama_model,
|
|
231
|
+
"runtime": runtime,
|
|
232
|
+
"stream": stream if stream else ([text] if text else []),
|
|
233
|
+
}
|
|
@@ -140,7 +140,8 @@ def build_memories_routes(context: AdminRouteContext) -> list[BaseRoute]:
|
|
|
140
140
|
if "title" in update_data or "content" in update_data:
|
|
141
141
|
config = _config_from_request(request)
|
|
142
142
|
embedder = LocalEmbeddingProvider(
|
|
143
|
-
config.embedding.
|
|
143
|
+
ollama_url=config.embedding.ollama_url,
|
|
144
|
+
ollama_model=config.embedding.ollama_model,
|
|
144
145
|
dimensions=min(config.embedding.dimensions, 16),
|
|
145
146
|
runtime="auto",
|
|
146
147
|
)
|
|
@@ -73,7 +73,8 @@ def _rank_serialized_items(
|
|
|
73
73
|
return items
|
|
74
74
|
|
|
75
75
|
embedder = LocalEmbeddingProvider(
|
|
76
|
-
config.embedding.
|
|
76
|
+
ollama_url=config.embedding.ollama_url,
|
|
77
|
+
ollama_model=config.embedding.ollama_model,
|
|
77
78
|
dimensions=min(config.embedding.dimensions, 16),
|
|
78
79
|
runtime="auto",
|
|
79
80
|
)
|
|
@@ -108,7 +109,8 @@ def _rank_skill_items(
|
|
|
108
109
|
return items
|
|
109
110
|
|
|
110
111
|
embedder = LocalEmbeddingProvider(
|
|
111
|
-
config.embedding.
|
|
112
|
+
ollama_url=config.embedding.ollama_url,
|
|
113
|
+
ollama_model=config.embedding.ollama_model,
|
|
112
114
|
dimensions=min(config.embedding.dimensions, 16),
|
|
113
115
|
runtime="auto",
|
|
114
116
|
)
|
|
@@ -89,8 +89,8 @@ def polish_prompt_draft(
|
|
|
89
89
|
|
|
90
90
|
polished = _heuristic_polish(draft)
|
|
91
91
|
llm = LocalModelLLM(
|
|
92
|
-
config.llm.
|
|
93
|
-
|
|
92
|
+
ollama_url=config.llm.ollama_url,
|
|
93
|
+
ollama_model=config.llm.ollama_model,
|
|
94
94
|
context_length=config.llm.context_length,
|
|
95
95
|
)
|
|
96
96
|
runtime = llm.runtime
|
|
@@ -118,7 +118,7 @@ Make the prompt direct, structured, and useful for a coding workflow.
|
|
|
118
118
|
if not parsed:
|
|
119
119
|
return polished, {
|
|
120
120
|
"provider": "heuristic",
|
|
121
|
-
"model": config.llm.
|
|
121
|
+
"model": config.llm.ollama_model,
|
|
122
122
|
"runtime": runtime,
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
5
|
|
|
7
6
|
from minder.bootstrap.providers import (
|
|
8
7
|
build_cache,
|
|
@@ -33,12 +32,13 @@ __all__ = [
|
|
|
33
32
|
|
|
34
33
|
def runtime_summary(config: Settings) -> dict[str, object]:
|
|
35
34
|
llm = LocalModelLLM(
|
|
36
|
-
config.llm.
|
|
37
|
-
|
|
35
|
+
ollama_url=config.llm.ollama_url,
|
|
36
|
+
ollama_model=config.llm.ollama_model,
|
|
38
37
|
context_length=config.llm.context_length,
|
|
39
38
|
)
|
|
40
39
|
embedder = LocalEmbeddingProvider(
|
|
41
|
-
config.embedding.
|
|
40
|
+
ollama_url=config.embedding.ollama_url,
|
|
41
|
+
ollama_model=config.embedding.ollama_model,
|
|
42
42
|
dimensions=config.embedding.dimensions,
|
|
43
43
|
runtime="auto",
|
|
44
44
|
)
|
|
@@ -53,10 +53,14 @@ def runtime_summary(config: Settings) -> dict[str, object]:
|
|
|
53
53
|
"orchestration_runtime_effective": graph_runtime_name(
|
|
54
54
|
config.workflow.orchestration_runtime
|
|
55
55
|
),
|
|
56
|
-
"
|
|
56
|
+
"llm_provider": config.llm.provider,
|
|
57
|
+
"llm_ollama_url": config.llm.ollama_url,
|
|
58
|
+
"llm_ollama_model": config.llm.ollama_model,
|
|
57
59
|
"llm_runtime_effective": llm.runtime,
|
|
58
60
|
"llm_context_length": config.llm.context_length,
|
|
59
|
-
"
|
|
61
|
+
"embedding_provider": config.embedding.provider,
|
|
62
|
+
"embedding_ollama_url": config.embedding.ollama_url,
|
|
63
|
+
"embedding_ollama_model": config.embedding.ollama_model,
|
|
60
64
|
"embedding_runtime_effective": embedder.runtime,
|
|
61
65
|
"openai_fallback_configured": fallback.available(),
|
|
62
66
|
"openai_fallback_runtime_effective": fallback.runtime,
|
|
@@ -5,7 +5,7 @@ Milvus Client — connection wrapper.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
|
-
from pymilvus import MilvusClient as PyMilvusClient # type: ignore[import-untyped]
|
|
8
|
+
from pymilvus import MilvusClient as PyMilvusClient # type: ignore[import-not-found, import-untyped]
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class MilvusClient:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Milvus Collections — schema definitions.
|
|
3
3
|
"""
|
|
4
|
-
from pymilvus import CollectionSchema, DataType, FieldSchema # type: ignore[import-untyped]
|
|
4
|
+
from pymilvus import CollectionSchema, DataType, FieldSchema # type: ignore[import-not-found, import-untyped]
|
|
5
5
|
|
|
6
6
|
def get_document_schema(dimensions: int = 768) -> CollectionSchema:
|
|
7
7
|
fields = [
|
|
@@ -19,7 +19,8 @@ class MemoryTools:
|
|
|
19
19
|
self._store = store
|
|
20
20
|
self._config = config
|
|
21
21
|
self._embedder = LocalEmbeddingProvider(
|
|
22
|
-
config.embedding.
|
|
22
|
+
ollama_url=config.embedding.ollama_url,
|
|
23
|
+
ollama_model=config.embedding.ollama_model,
|
|
23
24
|
dimensions=min(config.embedding.dimensions, 16),
|
|
24
25
|
runtime="auto",
|
|
25
26
|
)
|
|
@@ -35,7 +35,8 @@ class QueryTools:
|
|
|
35
35
|
self._graph = graph or MinderGraph(store, config)
|
|
36
36
|
self._vector_store = vector_store or VectorStore(store, store)
|
|
37
37
|
self._embedding_provider = LocalEmbeddingProvider(
|
|
38
|
-
config.embedding.
|
|
38
|
+
ollama_url=config.embedding.ollama_url,
|
|
39
|
+
ollama_model=config.embedding.ollama_model,
|
|
39
40
|
dimensions=config.embedding.dimensions,
|
|
40
41
|
runtime="auto",
|
|
41
42
|
)
|
|
@@ -132,7 +133,7 @@ class QueryTools:
|
|
|
132
133
|
"evaluation": result.evaluation,
|
|
133
134
|
"provider": result.llm_output.get("provider"),
|
|
134
135
|
"model": result.llm_output.get(
|
|
135
|
-
"model", result.llm_output.get("
|
|
136
|
+
"model", result.llm_output.get("ollama_model")
|
|
136
137
|
),
|
|
137
138
|
"runtime": result.llm_output.get("runtime"),
|
|
138
139
|
"orchestration_runtime": result.metadata.get("orchestration_runtime"),
|
|
@@ -237,7 +238,7 @@ class QueryTools:
|
|
|
237
238
|
"evaluation": result.evaluation,
|
|
238
239
|
"provider": result.llm_output.get("provider"),
|
|
239
240
|
"model": result.llm_output.get(
|
|
240
|
-
"model", result.llm_output.get("
|
|
241
|
+
"model", result.llm_output.get("ollama_model")
|
|
241
242
|
),
|
|
242
243
|
"runtime": result.llm_output.get("runtime"),
|
|
243
244
|
"orchestration_runtime": result.metadata.get("orchestration_runtime"),
|
|
@@ -86,7 +86,8 @@ class SkillTools:
|
|
|
86
86
|
def __init__(self, store: IOperationalStore, config: MinderConfig) -> None:
|
|
87
87
|
self._store = store
|
|
88
88
|
self._embedder = LocalEmbeddingProvider(
|
|
89
|
-
config.embedding.
|
|
89
|
+
ollama_url=config.embedding.ollama_url,
|
|
90
|
+
ollama_model=config.embedding.ollama_model,
|
|
90
91
|
dimensions=min(config.embedding.dimensions, 16),
|
|
91
92
|
runtime="auto",
|
|
92
93
|
)
|