openrunner-sdk 2.16.0__tar.gz → 2.16.2__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.
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/PKG-INFO +1 -1
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/__init__.py +1 -1
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/install_commands.py +52 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/mcp_server.py +157 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/pyproject.toml +1 -1
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/.gitignore +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/=6.0 +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/=8.1 +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/README.md +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/api_client.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/artifact.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/buffer.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cache.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cli.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/config.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cost.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/dataset.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/environment.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/evaluation.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/feedback.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/git_info.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/guardrails.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/__init__.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/accelerate.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/anthropic_tracer.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/catboost.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/diffusers.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/fastai.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/forced_alignment.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/gladia.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/gymnasium.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/huggingface.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/hydra.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/ignite.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/jax.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/keras.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/langchain.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/lightgbm.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/lightning.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/llamaindex.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/openai_finetune.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/openai_tracer.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/optuna.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/pytorch.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/sb3.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/sklearn.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/tensorflow.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/trl.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/tts.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/ultralytics.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/voice_agent.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/whisper.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/xgboost.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/launch.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/media.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/migrate.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/model.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/offline.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/pii.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/plot.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/prompt.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/query_api.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/redact.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/run.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/scorers.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/sender.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/session.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/settings.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/summary.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/sweep.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/system_metrics.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/tensorboard.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/trace.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/transcript_formatter.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wal.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wandb_compat/__init__.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wandb_compat/_shim.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wer.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/__init__.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/conftest.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_alert.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_aliases.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_api_client.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_artifact.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_buffer.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_cache.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_class_scorers.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_cli.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_config.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_evaluation.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_finish.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_git_info.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_init.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_fastai.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_huggingface.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_keras.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_langchain.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_lightning.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_pytorch.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_sklearn.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_xgboost.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_launch.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_log.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_log_code.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_media.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_migrate.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_offline.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_offline_sync.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_pii.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_plot.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_query_api.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_resume.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sdk_features.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sender.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_summary.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sweep.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_system_metrics.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_trace.py +0 -0
- {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_wandb_compat.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrunner-sdk
|
|
3
|
-
Version: 2.16.
|
|
3
|
+
Version: 2.16.2
|
|
4
4
|
Summary: OpenRunner SDK - W&B-compatible ML experiment tracking client
|
|
5
5
|
Project-URL: Homepage, https://github.com/jqueguiner/openrunner
|
|
6
6
|
Project-URL: Repository, https://github.com/jqueguiner/openrunner
|
|
@@ -120,7 +120,7 @@ launch.from_run = _launch_from_run # type: ignore[attr-defined]
|
|
|
120
120
|
# openrunner.trace.patch_openai() syntax
|
|
121
121
|
trace.patch_openai = _patch_openai # type: ignore[attr-defined]
|
|
122
122
|
|
|
123
|
-
__version__ = "2.16.
|
|
123
|
+
__version__ = "2.16.2"
|
|
124
124
|
|
|
125
125
|
logger = logging.getLogger("openrunner")
|
|
126
126
|
|
|
@@ -467,6 +467,56 @@ Report version and installed commands to user. Remind them to enable the MCP plu
|
|
|
467
467
|
"""
|
|
468
468
|
|
|
469
469
|
|
|
470
|
+
MCP_CMD = """---
|
|
471
|
+
name: {prefix}mcp
|
|
472
|
+
description: Install and activate OpenRunner MCP plugin for native tool access
|
|
473
|
+
allowed-tools:
|
|
474
|
+
- Bash
|
|
475
|
+
- AskUserQuestion
|
|
476
|
+
---
|
|
477
|
+
<objective>
|
|
478
|
+
Help user install, activate, and understand the OpenRunner MCP plugin that gives Claude Code native access to search sessions, runs, artifacts, papers, research, metrics, and code.
|
|
479
|
+
</objective>
|
|
480
|
+
|
|
481
|
+
<process>
|
|
482
|
+
1. Install the MCP plugin config file
|
|
483
|
+
2. Explain what tools become available
|
|
484
|
+
3. Tell user to enable plugin and restart session
|
|
485
|
+
</process>
|
|
486
|
+
|
|
487
|
+
Install MCP plugin:
|
|
488
|
+
```bash
|
|
489
|
+
python3 -c "
|
|
490
|
+
import json; from pathlib import Path
|
|
491
|
+
mcp_dir = Path.home() / '.claude' / 'plugins' / 'marketplaces' / 'claude-plugins-official' / 'external_plugins' / 'openrunner'
|
|
492
|
+
mcp_dir.mkdir(parents=True, exist_ok=True)
|
|
493
|
+
mcp_file = mcp_dir / '.mcp.json'
|
|
494
|
+
mcp_file.write_text(json.dumps({{'openrunner': {{'command': 'openrunner', 'args': ['mcp', 'serve']}}}}, indent=2))
|
|
495
|
+
print('MCP plugin installed.')
|
|
496
|
+
print()
|
|
497
|
+
print('10 tools available after activation:')
|
|
498
|
+
print(' openrunner_search_sessions — search AI sessions by keyword')
|
|
499
|
+
print(' openrunner_search_papers — search papers (title, abstract, sections)')
|
|
500
|
+
print(' openrunner_search_research — search research plans (SOTA, gaps, experiments)')
|
|
501
|
+
print(' openrunner_search_runs — search runs by name/tags')
|
|
502
|
+
print(' openrunner_search_code — search code artifacts')
|
|
503
|
+
print(' openrunner_get_session — get full session detail')
|
|
504
|
+
print(' openrunner_list_runs — list recent runs')
|
|
505
|
+
print(' openrunner_get_run_metrics — get metrics for a run')
|
|
506
|
+
print(' openrunner_list_artifacts — list project artifacts')
|
|
507
|
+
print(' openrunner_log_note — log a research note')
|
|
508
|
+
print()
|
|
509
|
+
print('TO ACTIVATE:')
|
|
510
|
+
print(' 1. Run /plugins in Claude Code')
|
|
511
|
+
print(' 2. Enable openrunner')
|
|
512
|
+
print(' 3. Restart this Claude Code session')
|
|
513
|
+
"
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Tell the user the activation steps clearly.
|
|
517
|
+
"""
|
|
518
|
+
|
|
519
|
+
|
|
470
520
|
def install_claude_code() -> list[str]:
|
|
471
521
|
"""Install commands in ~/.claude/commands/openrunner/."""
|
|
472
522
|
cmd_dir = Path.home() / ".claude" / "commands" / "openrunner"
|
|
@@ -481,6 +531,7 @@ def install_claude_code() -> list[str]:
|
|
|
481
531
|
"login.md": LOGIN_CMD,
|
|
482
532
|
"code-upload.md": CODE_UPLOAD_CMD,
|
|
483
533
|
"update.md": UPDATE_CMD,
|
|
534
|
+
"mcp.md": MCP_CMD,
|
|
484
535
|
}
|
|
485
536
|
|
|
486
537
|
files = []
|
|
@@ -505,6 +556,7 @@ def _install_all_commands_to_dir(cmd_dir: Path, prefix: str = "openrunner:") ->
|
|
|
505
556
|
"login.md": LOGIN_CMD,
|
|
506
557
|
"code-upload.md": CODE_UPLOAD_CMD,
|
|
507
558
|
"update.md": UPDATE_CMD,
|
|
559
|
+
"mcp.md": MCP_CMD,
|
|
508
560
|
}
|
|
509
561
|
|
|
510
562
|
files = []
|
|
@@ -259,6 +259,42 @@ def create_server():
|
|
|
259
259
|
"required": [],
|
|
260
260
|
},
|
|
261
261
|
),
|
|
262
|
+
Tool(
|
|
263
|
+
name="openrunner_search_papers",
|
|
264
|
+
description="Search papers by title, abstract, or section content.",
|
|
265
|
+
inputSchema={
|
|
266
|
+
"type": "object",
|
|
267
|
+
"properties": {
|
|
268
|
+
"query": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "Search term (matches title, abstract, section content)",
|
|
271
|
+
},
|
|
272
|
+
"project": {
|
|
273
|
+
"type": "string",
|
|
274
|
+
"description": "Optional: project name",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
"required": ["query"],
|
|
278
|
+
},
|
|
279
|
+
),
|
|
280
|
+
Tool(
|
|
281
|
+
name="openrunner_search_research",
|
|
282
|
+
description="Search research plans (literature, SOTA analysis, gaps, experiments, protocols).",
|
|
283
|
+
inputSchema={
|
|
284
|
+
"type": "object",
|
|
285
|
+
"properties": {
|
|
286
|
+
"query": {
|
|
287
|
+
"type": "string",
|
|
288
|
+
"description": "Search term (matches domain, SOTA, gaps, experiments)",
|
|
289
|
+
},
|
|
290
|
+
"project": {
|
|
291
|
+
"type": "string",
|
|
292
|
+
"description": "Optional: project name",
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
"required": ["query"],
|
|
296
|
+
},
|
|
297
|
+
),
|
|
262
298
|
]
|
|
263
299
|
|
|
264
300
|
@server.call_tool()
|
|
@@ -281,6 +317,10 @@ def create_server():
|
|
|
281
317
|
result = await _tool_list_artifacts(arguments)
|
|
282
318
|
elif name == "openrunner_search_runs":
|
|
283
319
|
result = await _tool_search_runs(arguments)
|
|
320
|
+
elif name == "openrunner_search_papers":
|
|
321
|
+
result = await _tool_search_papers(arguments)
|
|
322
|
+
elif name == "openrunner_search_research":
|
|
323
|
+
result = await _tool_search_research(arguments)
|
|
284
324
|
else:
|
|
285
325
|
result = f"Unknown tool: {name}"
|
|
286
326
|
except RuntimeError as e:
|
|
@@ -606,6 +646,123 @@ async def _tool_search_runs(args: dict) -> str:
|
|
|
606
646
|
return f"Error: {e}"
|
|
607
647
|
|
|
608
648
|
|
|
649
|
+
async def _tool_search_papers(args: dict) -> str:
|
|
650
|
+
"""Search papers by title, abstract, section content."""
|
|
651
|
+
query = args["query"].lower()
|
|
652
|
+
project = args.get("project")
|
|
653
|
+
settings = _load_settings()
|
|
654
|
+
if not project:
|
|
655
|
+
project = settings.get("project")
|
|
656
|
+
if not project:
|
|
657
|
+
return "Error: No project specified."
|
|
658
|
+
|
|
659
|
+
try:
|
|
660
|
+
data = await _api_get("/projects", params={})
|
|
661
|
+
projects = data if isinstance(data, list) else []
|
|
662
|
+
project_id = None
|
|
663
|
+
for p in projects:
|
|
664
|
+
org = p.get("org_name", "")
|
|
665
|
+
name = p.get("name", "")
|
|
666
|
+
slug = p.get("slug", "")
|
|
667
|
+
if f"{org}/{name}" == project or f"{org}/{slug}" == project or name == project:
|
|
668
|
+
project_id = p.get("id")
|
|
669
|
+
break
|
|
670
|
+
if not project_id:
|
|
671
|
+
return f"Project '{project}' not found"
|
|
672
|
+
|
|
673
|
+
papers_data = await _api_get(f"/projects/{project_id}/papers")
|
|
674
|
+
papers = papers_data.get("papers", []) if isinstance(papers_data, dict) else []
|
|
675
|
+
|
|
676
|
+
results = []
|
|
677
|
+
for paper in papers:
|
|
678
|
+
matches = []
|
|
679
|
+
if query in (paper.get("title") or "").lower():
|
|
680
|
+
matches.append("title")
|
|
681
|
+
if query in (paper.get("abstract") or "").lower():
|
|
682
|
+
matches.append("abstract")
|
|
683
|
+
for section in (paper.get("sections") or []):
|
|
684
|
+
if query in (section.get("content") or "").lower():
|
|
685
|
+
matches.append(f"section:{section.get('title', '?')}")
|
|
686
|
+
if matches:
|
|
687
|
+
results.append((paper, matches))
|
|
688
|
+
|
|
689
|
+
if not results:
|
|
690
|
+
return f"No papers matching '{args['query']}'"
|
|
691
|
+
|
|
692
|
+
lines = [f"Found {len(results)} paper(s):\n"]
|
|
693
|
+
for paper, matches in results:
|
|
694
|
+
lines.append(f"- **{paper.get('title', 'Untitled')}** (status: {paper.get('status', '?')})")
|
|
695
|
+
lines.append(f" Matched in: {', '.join(matches)}")
|
|
696
|
+
if paper.get("abstract"):
|
|
697
|
+
lines.append(f" Abstract: {paper['abstract'][:150]}...")
|
|
698
|
+
lines.append(f" ID: {paper.get('id')}")
|
|
699
|
+
lines.append("")
|
|
700
|
+
return "\n".join(lines)
|
|
701
|
+
except Exception as e:
|
|
702
|
+
return f"Error: {e}"
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
async def _tool_search_research(args: dict) -> str:
|
|
706
|
+
"""Search research plans (literature, SOTA, gaps, experiments, protocols)."""
|
|
707
|
+
query = args["query"].lower()
|
|
708
|
+
project = args.get("project")
|
|
709
|
+
settings = _load_settings()
|
|
710
|
+
if not project:
|
|
711
|
+
project = settings.get("project")
|
|
712
|
+
if not project:
|
|
713
|
+
return "Error: No project specified."
|
|
714
|
+
|
|
715
|
+
try:
|
|
716
|
+
data = await _api_get("/projects", params={})
|
|
717
|
+
projects = data if isinstance(data, list) else []
|
|
718
|
+
project_id = None
|
|
719
|
+
for p in projects:
|
|
720
|
+
org = p.get("org_name", "")
|
|
721
|
+
name = p.get("name", "")
|
|
722
|
+
slug = p.get("slug", "")
|
|
723
|
+
if f"{org}/{name}" == project or f"{org}/{slug}" == project or name == project:
|
|
724
|
+
project_id = p.get("id")
|
|
725
|
+
break
|
|
726
|
+
if not project_id:
|
|
727
|
+
return f"Project '{project}' not found"
|
|
728
|
+
|
|
729
|
+
research_data = await _api_get(f"/projects/{project_id}/research")
|
|
730
|
+
plans = research_data.get("plans", []) if isinstance(research_data, dict) else []
|
|
731
|
+
|
|
732
|
+
results = []
|
|
733
|
+
for plan in plans:
|
|
734
|
+
matches = []
|
|
735
|
+
if query in (plan.get("domain") or "").lower():
|
|
736
|
+
matches.append("domain")
|
|
737
|
+
if query in (plan.get("sota_analysis") or "").lower():
|
|
738
|
+
matches.append("sota")
|
|
739
|
+
if query in str(plan.get("gaps") or []).lower():
|
|
740
|
+
matches.append("gaps")
|
|
741
|
+
if query in str(plan.get("proposed_experiments") or []).lower():
|
|
742
|
+
matches.append("experiments")
|
|
743
|
+
if query in (plan.get("protocol") or "").lower():
|
|
744
|
+
matches.append("protocol")
|
|
745
|
+
if query in (plan.get("title") or "").lower():
|
|
746
|
+
matches.append("title")
|
|
747
|
+
if matches:
|
|
748
|
+
results.append((plan, matches))
|
|
749
|
+
|
|
750
|
+
if not results:
|
|
751
|
+
return f"No research plans matching '{args['query']}'"
|
|
752
|
+
|
|
753
|
+
lines = [f"Found {len(results)} research plan(s):\n"]
|
|
754
|
+
for plan, matches in results:
|
|
755
|
+
lines.append(f"- **{plan.get('title', 'Untitled')}** (domain: {plan.get('domain', '?')}, status: {plan.get('status', '?')})")
|
|
756
|
+
lines.append(f" Matched in: {', '.join(matches)}")
|
|
757
|
+
if plan.get("sota_analysis"):
|
|
758
|
+
lines.append(f" SOTA: {plan['sota_analysis'][:150]}...")
|
|
759
|
+
lines.append(f" ID: {plan.get('id')}")
|
|
760
|
+
lines.append("")
|
|
761
|
+
return "\n".join(lines)
|
|
762
|
+
except Exception as e:
|
|
763
|
+
return f"Error: {e}"
|
|
764
|
+
|
|
765
|
+
|
|
609
766
|
# ---------------------------------------------------------------------------
|
|
610
767
|
# Server runner
|
|
611
768
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|