minder-cli 0.6.2__tar.gz → 0.6.4__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.6.2 → minder_cli-0.6.4}/PKG-INFO +1 -1
- {minder_cli-0.6.2 → minder_cli-0.6.4}/pyproject.toml +1 -1
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/bootstrap/transport.py +32 -12
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/config.py +3 -0
- minder_cli-0.6.4/src/minder/graph/concurrency.py +169 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/executor.py +44 -12
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/graph.py +29 -5
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/__init__.py +2 -0
- minder_cli-0.6.4/src/minder/graph/nodes/context_enricher.py +186 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/reasoning.py +83 -9
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/retriever.py +5 -1
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/workflow_planner.py +9 -2
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/repository.py +2 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/skill.py +9 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/runtime.py +12 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/interfaces.py +2 -1
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/operational_store.py +16 -3
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/relational.py +30 -2
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/repo_state.py +23 -12
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/memory.py +39 -4
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/query.py +7 -2
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/workflow.py +16 -9
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/transport/sse.py +6 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/.gitignore +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/LICENSE +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/README-pypi.md +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/README.md +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/api/routers/prompts.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/application/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/application/admin/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/application/admin/dto.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/application/admin/jobs.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/application/admin/use_cases.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/context.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/middleware.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/principal.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/rate_limiter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/rbac.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/auth/service.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/bootstrap/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/bootstrap/agent_seeder.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/bootstrap/providers.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/bootstrap/workflow_seeder.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/cache/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/cache/providers.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/chunking/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/chunking/code_splitter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/chunking/splitter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/cli.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/context_compactor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/continuity.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/dev.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/embedding/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/embedding/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/embedding/local.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/embedding/openai.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/checkpoint.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/edges.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/memory_graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/clarification.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/evaluator.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/guard.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/llm.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/parallel_retriever.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/planning.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/reflection.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/reranker.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/nodes/verification.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/runtime.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/session_graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/state.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/graph/supervisor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/learning/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/learning/error_learner.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/learning/pattern_extractor.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/learning/quality_optimizer.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/learning/skill_synthesizer.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/llm/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/llm/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/llm/factory.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/llm/llama_cpp_llm.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/llm/openai.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/model_bootstrap.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/agent.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/checkpoint.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/client.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/document.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/error.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/history.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/job.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/prompt.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/rule.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/session.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/user.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/models/workflow.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/observability/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/observability/audit.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/observability/logging.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/observability/metrics.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/observability/tracing.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/commands/agent.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/commands/auth.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/commands/mcp.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/commands/sync.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/commands/update.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/main.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/utils/common.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/utils/config.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/utils/git.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/cli/utils/version.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/api.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/context.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/dashboard.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/jobs.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/memories.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/prompts.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/routes.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/search.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/presentation/http/admin/skills.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/prompts/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/prompts/formatter.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/resources/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/retrieval/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/retrieval/hybrid.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/retrieval/mmr.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/retrieval/multi_hop.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/runtime.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/server.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/document.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/error.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/feedback.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/history.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/client.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/crud.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/graph_store.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/qdrant/vector_store.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/rule.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/store/vector.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/auth.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/graph.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/ingest.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/registry.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/repo_scanner.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/seeds/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/seeds/default_agents.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/session.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/tools/skills.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/transport/__init__.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/transport/base.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/transport/stdio.py +0 -0
- {minder_cli-0.6.2 → minder_cli-0.6.4}/src/minder/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: minder-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
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
|
|
@@ -315,12 +315,13 @@ def build_transport(
|
|
|
315
315
|
return await session_tools.minder_session_cleanup(user_id=authenticated_user.id)
|
|
316
316
|
|
|
317
317
|
async def minder_workflow_get(
|
|
318
|
-
*, user=None, repo_id: str, repo_path: str
|
|
318
|
+
*, user=None, repo_id: str, repo_path: str, branch: str = "main"
|
|
319
319
|
) -> dict[str, Any]: # noqa: ANN001
|
|
320
320
|
del user
|
|
321
321
|
return await workflow_tools.minder_workflow_get(
|
|
322
322
|
repo_id=await _resolve_repo_uuid(repo_id),
|
|
323
323
|
repo_path=repo_path,
|
|
324
|
+
branch=branch,
|
|
324
325
|
)
|
|
325
326
|
|
|
326
327
|
async def minder_workflow_step(
|
|
@@ -330,6 +331,7 @@ def build_transport(
|
|
|
330
331
|
repo_path: str | None = None,
|
|
331
332
|
session_id: str | None = None,
|
|
332
333
|
decision: dict[str, Any] | None = None,
|
|
334
|
+
branch: str = "main",
|
|
333
335
|
) -> dict[str, Any]: # noqa: ANN001
|
|
334
336
|
del user
|
|
335
337
|
return await workflow_tools.minder_workflow_step(
|
|
@@ -337,6 +339,7 @@ def build_transport(
|
|
|
337
339
|
repo_path=repo_path,
|
|
338
340
|
session_id=uuid.UUID(session_id) if session_id else None,
|
|
339
341
|
decision=decision,
|
|
342
|
+
branch=branch,
|
|
340
343
|
)
|
|
341
344
|
|
|
342
345
|
async def minder_workflow_update(
|
|
@@ -347,6 +350,7 @@ def build_transport(
|
|
|
347
350
|
completed_step: str,
|
|
348
351
|
artifact_name: str | None = None,
|
|
349
352
|
artifact_content: str | None = None,
|
|
353
|
+
branch: str = "main",
|
|
350
354
|
) -> dict[str, Any]: # noqa: ANN001
|
|
351
355
|
del user
|
|
352
356
|
return await workflow_tools.minder_workflow_update(
|
|
@@ -355,88 +359,104 @@ def build_transport(
|
|
|
355
359
|
completed_step=completed_step,
|
|
356
360
|
artifact_name=artifact_name,
|
|
357
361
|
artifact_content=artifact_content,
|
|
362
|
+
branch=branch,
|
|
358
363
|
)
|
|
359
364
|
|
|
360
365
|
async def minder_workflow_guard(
|
|
361
|
-
*, user=None, repo_id: str, requested_step: str, action: str | None = None
|
|
366
|
+
*, user=None, repo_id: str, requested_step: str, action: str | None = None, branch: str = "main"
|
|
362
367
|
) -> dict[str, Any]: # noqa: ANN001
|
|
363
368
|
del user
|
|
364
369
|
return await workflow_tools.minder_workflow_guard(
|
|
365
370
|
repo_id=await _resolve_repo_uuid(repo_id),
|
|
366
371
|
requested_step=requested_step,
|
|
367
372
|
action=action,
|
|
373
|
+
branch=branch,
|
|
368
374
|
)
|
|
369
375
|
|
|
370
376
|
async def minder_memory_store(
|
|
371
377
|
*,
|
|
372
378
|
user=None,
|
|
379
|
+
principal: Principal | None = None,
|
|
373
380
|
title: str,
|
|
374
381
|
content: str,
|
|
375
382
|
tags: list[str],
|
|
376
383
|
language: str, # noqa: ANN001
|
|
384
|
+
scope: str = "private",
|
|
377
385
|
) -> dict[str, Any]:
|
|
378
|
-
|
|
386
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
379
387
|
return await memory_tools.minder_memory_store(
|
|
380
388
|
title=title,
|
|
381
389
|
content=content,
|
|
382
390
|
tags=tags,
|
|
383
391
|
language=language,
|
|
392
|
+
owner_id=owner_id,
|
|
393
|
+
scope=scope,
|
|
384
394
|
)
|
|
385
395
|
|
|
386
396
|
async def minder_memory_recall(
|
|
387
397
|
*,
|
|
388
398
|
user=None,
|
|
399
|
+
principal: Principal | None = None,
|
|
389
400
|
query: str,
|
|
390
401
|
limit: int = 5,
|
|
391
402
|
current_step: str | None = None,
|
|
392
403
|
artifact_type: str | None = None,
|
|
393
404
|
) -> list[dict[str, Any]]: # noqa: ANN001
|
|
394
|
-
|
|
405
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
395
406
|
return await memory_tools.minder_memory_recall(
|
|
396
407
|
query,
|
|
397
408
|
limit=limit,
|
|
398
409
|
current_step=current_step,
|
|
399
410
|
artifact_type=artifact_type,
|
|
411
|
+
owner_id=owner_id,
|
|
400
412
|
)
|
|
401
413
|
|
|
402
|
-
async def minder_memory_list(
|
|
403
|
-
|
|
404
|
-
|
|
414
|
+
async def minder_memory_list(
|
|
415
|
+
*,
|
|
416
|
+
user=None,
|
|
417
|
+
principal: Principal | None = None,
|
|
418
|
+
) -> list[dict[str, Any]]: # noqa: ANN001
|
|
419
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
420
|
+
return await memory_tools.minder_memory_list(owner_id=owner_id)
|
|
405
421
|
|
|
406
422
|
async def minder_memory_update(
|
|
407
423
|
*,
|
|
408
424
|
user=None,
|
|
425
|
+
principal: Principal | None = None,
|
|
409
426
|
memory_id: str,
|
|
410
427
|
title: str | None = None,
|
|
411
428
|
content: str | None = None,
|
|
412
429
|
tags: list[str] | None = None,
|
|
413
430
|
) -> dict[str, Any]: # noqa: ANN001
|
|
414
|
-
|
|
431
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
415
432
|
return await memory_tools.minder_memory_update(
|
|
416
433
|
memory_id,
|
|
417
434
|
title=title,
|
|
418
435
|
content=content,
|
|
419
436
|
tags=tags,
|
|
437
|
+
owner_id=owner_id,
|
|
420
438
|
)
|
|
421
439
|
|
|
422
440
|
async def minder_memory_delete(
|
|
423
|
-
*, user=None, skill_id: str
|
|
441
|
+
*, user=None, principal: Principal | None = None, skill_id: str
|
|
424
442
|
) -> dict[str, bool]: # noqa: ANN001
|
|
425
|
-
|
|
426
|
-
return await memory_tools.minder_memory_delete(skill_id)
|
|
443
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
444
|
+
return await memory_tools.minder_memory_delete(skill_id, owner_id=owner_id)
|
|
427
445
|
|
|
428
446
|
async def minder_memory_compact(
|
|
429
447
|
*,
|
|
430
448
|
user=None,
|
|
449
|
+
principal: Principal | None = None,
|
|
431
450
|
memory_ids: list[str],
|
|
432
451
|
similarity_threshold: float = 0.92,
|
|
433
452
|
dry_run: bool = True,
|
|
434
453
|
) -> dict[str, Any]: # noqa: ANN001
|
|
435
|
-
|
|
454
|
+
owner_id = principal.principal_id if principal else (user.id if user else None)
|
|
436
455
|
return await memory_tools.minder_memory_compact(
|
|
437
456
|
memory_ids=memory_ids,
|
|
438
457
|
similarity_threshold=similarity_threshold,
|
|
439
458
|
dry_run=dry_run,
|
|
459
|
+
owner_id=owner_id,
|
|
440
460
|
)
|
|
441
461
|
|
|
442
462
|
async def minder_skill_store(
|
|
@@ -11,6 +11,7 @@ class ServerConfig(BaseModel):
|
|
|
11
11
|
host: str = "0.0.0.0"
|
|
12
12
|
port: int = 8800
|
|
13
13
|
log_level: str = "info"
|
|
14
|
+
http_timeout_keep_alive: int = 10 # uvicorn keep-alive timeout (seconds)
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class DashboardConfig(BaseModel):
|
|
@@ -48,6 +49,8 @@ class LLMConfig(BaseModel):
|
|
|
48
49
|
temperature: float = 0.1
|
|
49
50
|
openai_api_key: Optional[str] = None
|
|
50
51
|
openai_model: str = "gpt-4o-mini"
|
|
52
|
+
timeout_seconds: float = 120.0 # wall-clock budget per LLM call
|
|
53
|
+
max_concurrent: int = 1 # max simultaneous LLM inferences
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
class VectorStoreConfig(BaseModel):
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Concurrency utilities for CPU-bound graph inference.
|
|
2
|
+
|
|
3
|
+
LLM inference (llama.cpp) is CPU-bound and can run for 5–30 s. Running it
|
|
4
|
+
directly on the asyncio event loop starves every other in-flight request.
|
|
5
|
+
|
|
6
|
+
This module provides:
|
|
7
|
+
- An asyncio.Semaphore that caps simultaneous LLM inferences.
|
|
8
|
+
- ``run_in_thread`` — wraps a blocking callable in asyncio.to_thread with an
|
|
9
|
+
optional timeout so the event loop stays responsive.
|
|
10
|
+
- ``stream_sync_generator`` — converts a blocking sync generator (e.g.
|
|
11
|
+
LLM token stream) into an async generator via a thread + queue, allowing
|
|
12
|
+
real token-by-token streaming without blocking the event loop.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import logging
|
|
19
|
+
from collections.abc import AsyncGenerator, Generator
|
|
20
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
21
|
+
from typing import Any, Callable, TypeVar
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
_semaphore: asyncio.Semaphore | None = None
|
|
26
|
+
_max_concurrent: int = 1
|
|
27
|
+
_timeout_seconds: float = 120.0
|
|
28
|
+
|
|
29
|
+
# Dedicated thread pool for LLM / embedding inference.
|
|
30
|
+
# Using a bounded pool prevents runaway thread creation under load.
|
|
31
|
+
_INFERENCE_POOL = ThreadPoolExecutor(max_workers=4, thread_name_prefix="minder-inference")
|
|
32
|
+
|
|
33
|
+
T = TypeVar("T")
|
|
34
|
+
|
|
35
|
+
_SENTINEL = object()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def configure(*, max_concurrent: int = 1, timeout_seconds: float = 120.0) -> None:
|
|
39
|
+
"""Call once at startup to set inference concurrency and timeout budgets."""
|
|
40
|
+
global _max_concurrent, _timeout_seconds, _semaphore
|
|
41
|
+
_max_concurrent = max(1, max_concurrent)
|
|
42
|
+
_timeout_seconds = max(10.0, timeout_seconds)
|
|
43
|
+
_semaphore = asyncio.Semaphore(_max_concurrent)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_semaphore() -> asyncio.Semaphore:
|
|
47
|
+
global _semaphore
|
|
48
|
+
if _semaphore is None:
|
|
49
|
+
_semaphore = asyncio.Semaphore(_max_concurrent)
|
|
50
|
+
return _semaphore
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def run_in_thread(
|
|
54
|
+
fn: Callable[..., T],
|
|
55
|
+
/,
|
|
56
|
+
*args: Any,
|
|
57
|
+
timeout: float | None = None,
|
|
58
|
+
use_llm_semaphore: bool = False,
|
|
59
|
+
) -> T:
|
|
60
|
+
"""Run a blocking callable in the inference thread pool.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
fn: Blocking callable.
|
|
64
|
+
*args: Positional arguments forwarded to fn.
|
|
65
|
+
timeout: Maximum seconds to wait. Defaults to the configured global
|
|
66
|
+
timeout when ``use_llm_semaphore`` is True, otherwise no timeout.
|
|
67
|
+
use_llm_semaphore: Acquire the global LLM concurrency semaphore before
|
|
68
|
+
running. Use this for actual LLM inference calls so we never run
|
|
69
|
+
more than ``max_concurrent`` inferences simultaneously.
|
|
70
|
+
"""
|
|
71
|
+
effective_timeout = timeout or (_timeout_seconds if use_llm_semaphore else None)
|
|
72
|
+
|
|
73
|
+
async def _inner() -> T:
|
|
74
|
+
loop = asyncio.get_running_loop()
|
|
75
|
+
return await loop.run_in_executor(_INFERENCE_POOL, fn, *args)
|
|
76
|
+
|
|
77
|
+
if use_llm_semaphore:
|
|
78
|
+
sem = _get_semaphore()
|
|
79
|
+
try:
|
|
80
|
+
async with sem:
|
|
81
|
+
if effective_timeout:
|
|
82
|
+
return await asyncio.wait_for(_inner(), timeout=effective_timeout)
|
|
83
|
+
return await _inner()
|
|
84
|
+
except asyncio.TimeoutError:
|
|
85
|
+
logger.warning("LLM inference timed out after %.0f s", effective_timeout)
|
|
86
|
+
raise
|
|
87
|
+
else:
|
|
88
|
+
if effective_timeout:
|
|
89
|
+
return await asyncio.wait_for(_inner(), timeout=effective_timeout)
|
|
90
|
+
return await _inner()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def stream_sync_generator(
|
|
94
|
+
gen_fn: Callable[..., Generator[Any, None, None]],
|
|
95
|
+
/,
|
|
96
|
+
*args: Any,
|
|
97
|
+
timeout: float | None = None,
|
|
98
|
+
use_llm_semaphore: bool = True,
|
|
99
|
+
) -> AsyncGenerator[Any, None]:
|
|
100
|
+
"""Adapt a blocking sync generator into an async generator.
|
|
101
|
+
|
|
102
|
+
The generator runs inside the inference thread pool so the asyncio event
|
|
103
|
+
loop is never blocked. Items are forwarded through an asyncio.Queue so
|
|
104
|
+
consumers receive them as they are produced.
|
|
105
|
+
|
|
106
|
+
Usage::
|
|
107
|
+
|
|
108
|
+
async for event in stream_sync_generator(llm_node.stream, state):
|
|
109
|
+
yield event
|
|
110
|
+
"""
|
|
111
|
+
loop = asyncio.get_running_loop()
|
|
112
|
+
queue: asyncio.Queue[Any] = asyncio.Queue(maxsize=64)
|
|
113
|
+
effective_timeout = timeout or (_timeout_seconds if use_llm_semaphore else None)
|
|
114
|
+
|
|
115
|
+
def _producer() -> None:
|
|
116
|
+
try:
|
|
117
|
+
for item in gen_fn(*args):
|
|
118
|
+
# Put items synchronously from the thread, waking up the consumer.
|
|
119
|
+
asyncio.run_coroutine_threadsafe(queue.put(item), loop).result()
|
|
120
|
+
except Exception as exc:
|
|
121
|
+
asyncio.run_coroutine_threadsafe(queue.put(exc), loop).result()
|
|
122
|
+
finally:
|
|
123
|
+
asyncio.run_coroutine_threadsafe(queue.put(_SENTINEL), loop).result()
|
|
124
|
+
|
|
125
|
+
async def _generate() -> AsyncGenerator[Any, None]:
|
|
126
|
+
future = loop.run_in_executor(_INFERENCE_POOL, _producer)
|
|
127
|
+
deadline = (
|
|
128
|
+
loop.time() + effective_timeout if effective_timeout else None
|
|
129
|
+
)
|
|
130
|
+
try:
|
|
131
|
+
while True:
|
|
132
|
+
remaining = (
|
|
133
|
+
max(0.1, deadline - loop.time()) if deadline else None
|
|
134
|
+
)
|
|
135
|
+
try:
|
|
136
|
+
item = await asyncio.wait_for(
|
|
137
|
+
queue.get(), timeout=remaining
|
|
138
|
+
)
|
|
139
|
+
except asyncio.TimeoutError:
|
|
140
|
+
logger.warning(
|
|
141
|
+
"LLM stream timed out after %.0f s", effective_timeout
|
|
142
|
+
)
|
|
143
|
+
future.cancel()
|
|
144
|
+
return
|
|
145
|
+
if item is _SENTINEL:
|
|
146
|
+
break
|
|
147
|
+
if isinstance(item, Exception):
|
|
148
|
+
raise item
|
|
149
|
+
yield item
|
|
150
|
+
finally:
|
|
151
|
+
# Drain queue to unblock any waiting producer thread.
|
|
152
|
+
while not queue.empty():
|
|
153
|
+
try:
|
|
154
|
+
queue.get_nowait()
|
|
155
|
+
except asyncio.QueueEmpty:
|
|
156
|
+
break
|
|
157
|
+
try:
|
|
158
|
+
await future
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
sem = _get_semaphore()
|
|
163
|
+
if use_llm_semaphore:
|
|
164
|
+
async with sem:
|
|
165
|
+
async for item in _generate():
|
|
166
|
+
yield item
|
|
167
|
+
else:
|
|
168
|
+
async for item in _generate():
|
|
169
|
+
yield item
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
from dataclasses import dataclass, field
|
|
4
5
|
import inspect
|
|
5
6
|
import uuid
|
|
@@ -7,9 +8,11 @@ from typing import Any
|
|
|
7
8
|
|
|
8
9
|
from minder.config import MinderConfig
|
|
9
10
|
from minder.graph.checkpoint import MinderCheckpointSaver
|
|
11
|
+
from minder.graph.concurrency import run_in_thread
|
|
10
12
|
from minder.graph.edges import determine_next_edge
|
|
11
13
|
from minder.graph.nodes import (
|
|
12
14
|
ClarificationNode,
|
|
15
|
+
ContextEnricherNode,
|
|
13
16
|
EvaluatorNode,
|
|
14
17
|
GuardNode,
|
|
15
18
|
LLMNode,
|
|
@@ -41,6 +44,7 @@ class GraphNodes:
|
|
|
41
44
|
evaluator: EvaluatorNode
|
|
42
45
|
reranker: RerankerNode | None = field(default=None)
|
|
43
46
|
reflection: ReflectionNode | None = field(default=None)
|
|
47
|
+
context_enricher: ContextEnricherNode | None = field(default=None)
|
|
44
48
|
|
|
45
49
|
|
|
46
50
|
class InternalGraphExecutor:
|
|
@@ -52,22 +56,32 @@ class InternalGraphExecutor:
|
|
|
52
56
|
state.metadata.setdefault("attempt_failures", [])
|
|
53
57
|
state.metadata["orchestration_runtime"] = "internal"
|
|
54
58
|
state = await self._nodes.workflow_planner.run(state)
|
|
55
|
-
|
|
56
|
-
state = self._nodes.
|
|
59
|
+
# Fast sync nodes — run in thread to yield control to the event loop
|
|
60
|
+
state = await run_in_thread(self._nodes.planning.run, state)
|
|
61
|
+
state = await run_in_thread(self._nodes.clarification.run, state)
|
|
57
62
|
if state.metadata.get("needs_clarification"):
|
|
58
63
|
return state
|
|
59
64
|
state = await self._nodes.retriever.run(state)
|
|
60
65
|
if self._nodes.reranker is not None:
|
|
61
66
|
state = await self._nodes.reranker.run(state)
|
|
67
|
+
if self._nodes.context_enricher is not None:
|
|
68
|
+
state = await self._nodes.context_enricher.run(state)
|
|
62
69
|
|
|
63
70
|
attempt = 0
|
|
64
71
|
while True:
|
|
65
72
|
attempt += 1
|
|
66
73
|
state.retry_count = attempt - 1
|
|
67
|
-
|
|
68
|
-
state = self._nodes.
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
# reasoning builds the prompt (CPU-bound string work)
|
|
75
|
+
state = await run_in_thread(self._nodes.reasoning.run, state)
|
|
76
|
+
# LLM inference is the main bottleneck — run in dedicated thread
|
|
77
|
+
# with semaphore + timeout so other requests keep moving
|
|
78
|
+
state = await run_in_thread(
|
|
79
|
+
self._nodes.llm.run,
|
|
80
|
+
state,
|
|
81
|
+
use_llm_semaphore=True,
|
|
82
|
+
)
|
|
83
|
+
state = await run_in_thread(self._nodes.guard.run, state)
|
|
84
|
+
state = await run_in_thread(self._nodes.verification.run, state)
|
|
71
85
|
edge = determine_next_edge(state)
|
|
72
86
|
state.transition_log.append(
|
|
73
87
|
{
|
|
@@ -101,7 +115,7 @@ class InternalGraphExecutor:
|
|
|
101
115
|
)
|
|
102
116
|
state.metadata["retry_reason"] = retry_reason
|
|
103
117
|
|
|
104
|
-
state = self._nodes.evaluator.run
|
|
118
|
+
state = await run_in_thread(self._nodes.evaluator.run, state)
|
|
105
119
|
state.metadata["edge"] = determine_next_edge(state)
|
|
106
120
|
|
|
107
121
|
if self._nodes.reflection is not None:
|
|
@@ -228,6 +242,12 @@ class LangGraphExecutorAdapter:
|
|
|
228
242
|
"reranker", self._wrap_state_handler(self._nodes.reranker.run)
|
|
229
243
|
)
|
|
230
244
|
|
|
245
|
+
if self._nodes.context_enricher is not None:
|
|
246
|
+
workflow.add_node(
|
|
247
|
+
"context_enricher",
|
|
248
|
+
self._wrap_state_handler(self._nodes.context_enricher.run),
|
|
249
|
+
)
|
|
250
|
+
|
|
231
251
|
workflow.add_node(
|
|
232
252
|
"reasoning", self._wrap_state_handler(self._node_reasoning_wrapper)
|
|
233
253
|
)
|
|
@@ -295,11 +315,18 @@ class LangGraphExecutorAdapter:
|
|
|
295
315
|
else:
|
|
296
316
|
retrieval_end_node = "retriever"
|
|
297
317
|
|
|
318
|
+
has_enricher = self._nodes.context_enricher is not None
|
|
298
319
|
if self._nodes.reranker is not None:
|
|
299
320
|
workflow.add_edge(retrieval_end_node, "reranker")
|
|
300
|
-
|
|
321
|
+
post_retrieval_node = "reranker"
|
|
301
322
|
else:
|
|
302
|
-
|
|
323
|
+
post_retrieval_node = retrieval_end_node
|
|
324
|
+
|
|
325
|
+
if has_enricher:
|
|
326
|
+
workflow.add_edge(post_retrieval_node, "context_enricher")
|
|
327
|
+
workflow.add_edge("context_enricher", "reasoning")
|
|
328
|
+
else:
|
|
329
|
+
workflow.add_edge(post_retrieval_node, "reasoning")
|
|
303
330
|
|
|
304
331
|
workflow.add_edge("reasoning", "llm")
|
|
305
332
|
workflow.add_edge("llm", "guard")
|
|
@@ -444,11 +471,16 @@ class LangGraphExecutorAdapter:
|
|
|
444
471
|
|
|
445
472
|
@staticmethod
|
|
446
473
|
def _wrap_state_handler(handler): # noqa: ANN001
|
|
474
|
+
is_async = inspect.iscoroutinefunction(handler)
|
|
475
|
+
|
|
447
476
|
async def wrapped(state): # noqa: ANN001
|
|
448
477
|
graph_state = GraphState.model_validate(state)
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
478
|
+
if is_async:
|
|
479
|
+
result = await handler(graph_state)
|
|
480
|
+
else:
|
|
481
|
+
# Run blocking sync handlers in a thread pool to avoid
|
|
482
|
+
# stalling the event loop during CPU-bound LLM inference.
|
|
483
|
+
result = await asyncio.to_thread(handler, graph_state)
|
|
452
484
|
if isinstance(result, GraphState):
|
|
453
485
|
return dict(result)
|
|
454
486
|
return result
|
|
@@ -6,6 +6,8 @@ from time import perf_counter
|
|
|
6
6
|
|
|
7
7
|
from minder.config import MinderConfig
|
|
8
8
|
from minder.embedding.local import LocalEmbeddingProvider
|
|
9
|
+
from minder.graph import concurrency as _concurrency
|
|
10
|
+
from minder.graph.concurrency import run_in_thread, stream_sync_generator
|
|
9
11
|
from minder.graph.edges import determine_next_edge
|
|
10
12
|
from minder.graph.executor import (
|
|
11
13
|
GraphNodes,
|
|
@@ -14,6 +16,7 @@ from minder.graph.executor import (
|
|
|
14
16
|
)
|
|
15
17
|
from minder.graph.nodes import (
|
|
16
18
|
ClarificationNode,
|
|
19
|
+
ContextEnricherNode,
|
|
17
20
|
EvaluatorNode,
|
|
18
21
|
GuardNode,
|
|
19
22
|
LLMNode,
|
|
@@ -45,6 +48,7 @@ class MinderGraph:
|
|
|
45
48
|
clarification: ClarificationNode | None = None,
|
|
46
49
|
retriever: RetrieverNode | None = None,
|
|
47
50
|
reranker: RerankerNode | None = None,
|
|
51
|
+
context_enricher: ContextEnricherNode | None = None,
|
|
48
52
|
reasoning: ReasoningNode | None = None,
|
|
49
53
|
llm: LLMNode | None = None,
|
|
50
54
|
guard: GuardNode | None = None,
|
|
@@ -75,6 +79,7 @@ class MinderGraph:
|
|
|
75
79
|
score_threshold=config.retrieval.similarity_threshold,
|
|
76
80
|
)
|
|
77
81
|
self._reranker = reranker # None by default; pass RerankerNode(...) to activate
|
|
82
|
+
self._context_enricher = context_enricher or ContextEnricherNode(store)
|
|
78
83
|
self._reasoning = reasoning or ReasoningNode()
|
|
79
84
|
self._llm = llm or LLMNode(
|
|
80
85
|
primary=create_llm(config.llm),
|
|
@@ -94,12 +99,18 @@ class MinderGraph:
|
|
|
94
99
|
self._error_store = error_store or store
|
|
95
100
|
self._graph_tools = graph_tools
|
|
96
101
|
self._cached_executor: InternalGraphExecutor | LangGraphExecutorAdapter | None = None
|
|
102
|
+
# Apply LLM concurrency and timeout settings from config
|
|
103
|
+
_concurrency.configure(
|
|
104
|
+
max_concurrent=config.llm.max_concurrent,
|
|
105
|
+
timeout_seconds=config.llm.timeout_seconds,
|
|
106
|
+
)
|
|
97
107
|
self._nodes = GraphNodes(
|
|
98
108
|
workflow_planner=self._workflow_planner,
|
|
99
109
|
planning=self._planning,
|
|
100
110
|
clarification=self._clarification,
|
|
101
111
|
retriever=self._retriever,
|
|
102
112
|
reranker=self._reranker,
|
|
113
|
+
context_enricher=self._context_enricher,
|
|
103
114
|
reasoning=self._reasoning,
|
|
104
115
|
llm=self._llm,
|
|
105
116
|
guard=self._guard,
|
|
@@ -144,19 +155,32 @@ class MinderGraph:
|
|
|
144
155
|
state = await self._nodes.retriever.run(state)
|
|
145
156
|
if self._nodes.reranker is not None:
|
|
146
157
|
state = await self._nodes.reranker.run(state)
|
|
158
|
+
if self._nodes.context_enricher is not None:
|
|
159
|
+
state = await self._nodes.context_enricher.run(state)
|
|
147
160
|
|
|
148
161
|
attempt = 0
|
|
149
162
|
while True:
|
|
150
163
|
attempt += 1
|
|
151
164
|
state.retry_count = attempt - 1
|
|
152
|
-
state = self._nodes.reasoning.run
|
|
165
|
+
state = await run_in_thread(self._nodes.reasoning.run, state)
|
|
153
166
|
yield {"type": "attempt", "attempt": attempt}
|
|
154
|
-
|
|
167
|
+
# Stream LLM tokens without blocking the event loop.
|
|
168
|
+
# stream_sync_generator runs the sync generator in the inference
|
|
169
|
+
# thread pool and forwards items through an asyncio.Queue.
|
|
170
|
+
async for event in stream_sync_generator(
|
|
171
|
+
self._nodes.llm.stream,
|
|
172
|
+
state,
|
|
173
|
+
use_llm_semaphore=True,
|
|
174
|
+
):
|
|
155
175
|
if str(event.get("type")) == "result":
|
|
176
|
+
# Capture the final LLM output written back to state
|
|
177
|
+
result_data = dict(event.get("result", {}) or {})
|
|
178
|
+
if result_data:
|
|
179
|
+
state.llm_output = result_data
|
|
156
180
|
continue
|
|
157
181
|
yield {**event, "attempt": attempt}
|
|
158
|
-
state = self._nodes.guard.run
|
|
159
|
-
state = self._nodes.verification.run
|
|
182
|
+
state = await run_in_thread(self._nodes.guard.run, state)
|
|
183
|
+
state = await run_in_thread(self._nodes.verification.run, state)
|
|
160
184
|
edge = determine_next_edge(state)
|
|
161
185
|
state.transition_log.append(
|
|
162
186
|
{
|
|
@@ -196,7 +220,7 @@ class MinderGraph:
|
|
|
196
220
|
"edge": edge,
|
|
197
221
|
}
|
|
198
222
|
|
|
199
|
-
state = self._nodes.evaluator.run
|
|
223
|
+
state = await run_in_thread(self._nodes.evaluator.run, state)
|
|
200
224
|
state.metadata["edge"] = determine_next_edge(state)
|
|
201
225
|
await self._persist_history(state)
|
|
202
226
|
await self._persist_error_if_needed(state)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .clarification import ClarificationNode
|
|
2
|
+
from .context_enricher import ContextEnricherNode
|
|
2
3
|
from .evaluator import EvaluatorNode
|
|
3
4
|
from .guard import GuardNode
|
|
4
5
|
from .llm import LLMNode
|
|
@@ -17,6 +18,7 @@ from .workflow_planner import WorkflowPlannerNode
|
|
|
17
18
|
|
|
18
19
|
__all__ = [
|
|
19
20
|
"ClarificationNode",
|
|
21
|
+
"ContextEnricherNode",
|
|
20
22
|
"DockerSandboxRunner",
|
|
21
23
|
"EvaluatorNode",
|
|
22
24
|
"GuardNode",
|