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.
Files changed (119) hide show
  1. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/PKG-INFO +1 -1
  2. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/__init__.py +1 -1
  3. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/install_commands.py +52 -0
  4. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/mcp_server.py +157 -0
  5. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/pyproject.toml +1 -1
  6. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/.gitignore +0 -0
  7. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/=6.0 +0 -0
  8. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/=8.1 +0 -0
  9. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/README.md +0 -0
  10. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/api_client.py +0 -0
  11. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/artifact.py +0 -0
  12. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/buffer.py +0 -0
  13. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cache.py +0 -0
  14. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cli.py +0 -0
  15. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/config.py +0 -0
  16. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/cost.py +0 -0
  17. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/dataset.py +0 -0
  18. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/environment.py +0 -0
  19. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/evaluation.py +0 -0
  20. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/feedback.py +0 -0
  21. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/git_info.py +0 -0
  22. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/guardrails.py +0 -0
  23. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/__init__.py +0 -0
  24. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/accelerate.py +0 -0
  25. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/anthropic_tracer.py +0 -0
  26. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/catboost.py +0 -0
  27. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/diffusers.py +0 -0
  28. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/fastai.py +0 -0
  29. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/forced_alignment.py +0 -0
  30. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/gladia.py +0 -0
  31. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/gymnasium.py +0 -0
  32. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/huggingface.py +0 -0
  33. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/hydra.py +0 -0
  34. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/ignite.py +0 -0
  35. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/jax.py +0 -0
  36. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/keras.py +0 -0
  37. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/langchain.py +0 -0
  38. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/lightgbm.py +0 -0
  39. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/lightning.py +0 -0
  40. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/llamaindex.py +0 -0
  41. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/openai_finetune.py +0 -0
  42. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/openai_tracer.py +0 -0
  43. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/optuna.py +0 -0
  44. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/pytorch.py +0 -0
  45. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/sb3.py +0 -0
  46. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/sklearn.py +0 -0
  47. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/tensorflow.py +0 -0
  48. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/trl.py +0 -0
  49. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/tts.py +0 -0
  50. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/ultralytics.py +0 -0
  51. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/voice_agent.py +0 -0
  52. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/whisper.py +0 -0
  53. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/integration/xgboost.py +0 -0
  54. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/launch.py +0 -0
  55. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/media.py +0 -0
  56. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/migrate.py +0 -0
  57. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/model.py +0 -0
  58. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/offline.py +0 -0
  59. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/pii.py +0 -0
  60. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/plot.py +0 -0
  61. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/prompt.py +0 -0
  62. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/query_api.py +0 -0
  63. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/redact.py +0 -0
  64. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/run.py +0 -0
  65. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/scorers.py +0 -0
  66. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/sender.py +0 -0
  67. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/session.py +0 -0
  68. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/settings.py +0 -0
  69. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/summary.py +0 -0
  70. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/sweep.py +0 -0
  71. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/system_metrics.py +0 -0
  72. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/tensorboard.py +0 -0
  73. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/trace.py +0 -0
  74. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/transcript_formatter.py +0 -0
  75. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wal.py +0 -0
  76. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wandb_compat/__init__.py +0 -0
  77. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wandb_compat/_shim.py +0 -0
  78. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/openrunner/wer.py +0 -0
  79. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/__init__.py +0 -0
  80. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/conftest.py +0 -0
  81. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_alert.py +0 -0
  82. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_aliases.py +0 -0
  83. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_api_client.py +0 -0
  84. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_artifact.py +0 -0
  85. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_buffer.py +0 -0
  86. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_cache.py +0 -0
  87. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_class_scorers.py +0 -0
  88. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_cli.py +0 -0
  89. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_config.py +0 -0
  90. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_evaluation.py +0 -0
  91. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_finish.py +0 -0
  92. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_git_info.py +0 -0
  93. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_init.py +0 -0
  94. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_fastai.py +0 -0
  95. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_huggingface.py +0 -0
  96. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_keras.py +0 -0
  97. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_langchain.py +0 -0
  98. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_lightning.py +0 -0
  99. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_pytorch.py +0 -0
  100. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_sklearn.py +0 -0
  101. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_integration_xgboost.py +0 -0
  102. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_launch.py +0 -0
  103. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_log.py +0 -0
  104. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_log_code.py +0 -0
  105. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_media.py +0 -0
  106. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_migrate.py +0 -0
  107. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_offline.py +0 -0
  108. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_offline_sync.py +0 -0
  109. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_pii.py +0 -0
  110. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_plot.py +0 -0
  111. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_query_api.py +0 -0
  112. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_resume.py +0 -0
  113. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sdk_features.py +0 -0
  114. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sender.py +0 -0
  115. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_summary.py +0 -0
  116. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_sweep.py +0 -0
  117. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_system_metrics.py +0 -0
  118. {openrunner_sdk-2.16.0 → openrunner_sdk-2.16.2}/tests/test_trace.py +0 -0
  119. {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.0
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.0"
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
  # ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openrunner-sdk"
3
- version = "2.16.0"
3
+ version = "2.16.2"
4
4
  description = "OpenRunner SDK - W&B-compatible ML experiment tracking client"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
File without changes
File without changes