researchloop 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. researchloop/__init__.py +1 -0
  2. researchloop/__main__.py +3 -0
  3. researchloop/cli.py +1138 -0
  4. researchloop/clusters/__init__.py +4 -0
  5. researchloop/clusters/monitor.py +199 -0
  6. researchloop/clusters/ssh.py +183 -0
  7. researchloop/comms/__init__.py +0 -0
  8. researchloop/comms/base.py +34 -0
  9. researchloop/comms/conversation.py +465 -0
  10. researchloop/comms/ntfy.py +95 -0
  11. researchloop/comms/router.py +71 -0
  12. researchloop/comms/slack.py +188 -0
  13. researchloop/core/__init__.py +0 -0
  14. researchloop/core/auth.py +78 -0
  15. researchloop/core/config.py +328 -0
  16. researchloop/core/credentials.py +38 -0
  17. researchloop/core/models.py +119 -0
  18. researchloop/core/orchestrator.py +910 -0
  19. researchloop/dashboard/__init__.py +0 -0
  20. researchloop/dashboard/app.py +15 -0
  21. researchloop/dashboard/auth.py +60 -0
  22. researchloop/dashboard/routes.py +912 -0
  23. researchloop/dashboard/templates/base.html +84 -0
  24. researchloop/dashboard/templates/login.html +12 -0
  25. researchloop/dashboard/templates/loop_detail.html +58 -0
  26. researchloop/dashboard/templates/loops.html +61 -0
  27. researchloop/dashboard/templates/setup.html +14 -0
  28. researchloop/dashboard/templates/sprint_detail.html +109 -0
  29. researchloop/dashboard/templates/sprints.html +48 -0
  30. researchloop/dashboard/templates/studies.html +18 -0
  31. researchloop/dashboard/templates/study_detail.html +64 -0
  32. researchloop/db/__init__.py +5 -0
  33. researchloop/db/database.py +86 -0
  34. researchloop/db/migrations.py +172 -0
  35. researchloop/db/queries.py +351 -0
  36. researchloop/runner/__init__.py +1 -0
  37. researchloop/runner/claude.py +169 -0
  38. researchloop/runner/job_templates/sge.sh.j2 +319 -0
  39. researchloop/runner/job_templates/slurm.sh.j2 +336 -0
  40. researchloop/runner/main.py +156 -0
  41. researchloop/runner/pipeline.py +272 -0
  42. researchloop/runner/templates/fix_issues.md.j2 +11 -0
  43. researchloop/runner/templates/idea_generator.md.j2 +16 -0
  44. researchloop/runner/templates/red_team.md.j2 +15 -0
  45. researchloop/runner/templates/report.md.j2 +31 -0
  46. researchloop/runner/templates/research_sprint.md.j2 +51 -0
  47. researchloop/runner/templates/summarizer.md.j2 +7 -0
  48. researchloop/runner/upload.py +153 -0
  49. researchloop/schedulers/__init__.py +11 -0
  50. researchloop/schedulers/base.py +43 -0
  51. researchloop/schedulers/local.py +188 -0
  52. researchloop/schedulers/sge.py +163 -0
  53. researchloop/schedulers/slurm.py +179 -0
  54. researchloop/sprints/__init__.py +0 -0
  55. researchloop/sprints/auto_loop.py +458 -0
  56. researchloop/sprints/manager.py +750 -0
  57. researchloop/studies/__init__.py +0 -0
  58. researchloop/studies/manager.py +102 -0
  59. researchloop-0.1.0.dist-info/METADATA +596 -0
  60. researchloop-0.1.0.dist-info/RECORD +63 -0
  61. researchloop-0.1.0.dist-info/WHEEL +4 -0
  62. researchloop-0.1.0.dist-info/entry_points.txt +3 -0
  63. researchloop-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,119 @@
1
+ """Core domain models for researchloop."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import secrets
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime, timezone
9
+ from enum import Enum
10
+
11
+
12
+ class SprintStatus(str, Enum):
13
+ """Lifecycle states for a research sprint."""
14
+
15
+ PENDING = "pending"
16
+ SUBMITTED = "submitted"
17
+ RUNNING = "running"
18
+ RESEARCH = "research"
19
+ RED_TEAM = "red_team"
20
+ FIXING = "fixing"
21
+ VALIDATING = "validating"
22
+ REPORTING = "reporting"
23
+ SUMMARIZING = "summarizing"
24
+ UPLOADING = "uploading"
25
+ COMPLETED = "completed"
26
+ FAILED = "failed"
27
+ CANCELLED = "cancelled"
28
+
29
+
30
+ def generate_sprint_id() -> str:
31
+ """Generate a short hex sprint ID like ``sp-a3f7b2``."""
32
+ return f"sp-{secrets.token_hex(3)}"
33
+
34
+
35
+ def format_sprint_dirname(sprint_id: str, idea: str | None) -> str:
36
+ """Create a directory name for a sprint.
37
+
38
+ Format: ``2026-03-15--19-50--sp-a3f7b2--feature-absorption``
39
+
40
+ Date comes first so ``ls`` shows sprints in chronological order.
41
+ """
42
+ now = datetime.now(timezone.utc)
43
+ date_part = now.strftime("%Y-%m-%d")
44
+ time_part = now.strftime("%H-%M")
45
+ # Slugify the idea: lowercase, replace non-alnum with hyphens, collapse
46
+ slug = re.sub(r"[^a-z0-9]+", "-", (idea or "auto").lower()).strip("-")
47
+ slug = re.sub(r"-{2,}", "-", slug)
48
+ # Truncate slug to keep directory names reasonable
49
+ slug = slug[:60]
50
+ return f"{date_part}--{time_part}--{sprint_id}--{slug}"
51
+
52
+
53
+ def _utcnow() -> datetime:
54
+ return datetime.now(timezone.utc)
55
+
56
+
57
+ @dataclass
58
+ class Sprint:
59
+ """A single research sprint."""
60
+
61
+ id: str
62
+ study_name: str
63
+ idea: str | None
64
+ status: SprintStatus = SprintStatus.PENDING
65
+ job_id: str | None = None
66
+ directory: str | None = None
67
+ created_at: datetime = field(default_factory=_utcnow)
68
+ started_at: datetime | None = None
69
+ completed_at: datetime | None = None
70
+ error: str | None = None
71
+ summary: str | None = None
72
+ artifacts: list[str] = field(default_factory=list)
73
+
74
+
75
+ @dataclass
76
+ class Study:
77
+ """A research study (read-only view)."""
78
+
79
+ name: str
80
+ cluster: str
81
+ description: str = ""
82
+ sprint_count: int = 0
83
+ active_sprints: int = 0
84
+
85
+
86
+ @dataclass
87
+ class AutoLoop:
88
+ """An automated multi-sprint loop."""
89
+
90
+ id: str
91
+ study_name: str
92
+ total_count: int
93
+ completed_count: int = 0
94
+ current_sprint_id: str | None = None
95
+ status: SprintStatus = SprintStatus.PENDING
96
+ created_at: datetime = field(default_factory=_utcnow)
97
+
98
+
99
+ @dataclass
100
+ class Artifact:
101
+ """A file artifact produced by a sprint."""
102
+
103
+ id: str
104
+ sprint_id: str
105
+ filename: str
106
+ path: str
107
+ size: int = 0
108
+ uploaded_at: datetime = field(default_factory=_utcnow)
109
+
110
+
111
+ @dataclass
112
+ class Event:
113
+ """An event recorded during sprint execution."""
114
+
115
+ id: str
116
+ sprint_id: str
117
+ event_type: str
118
+ data: dict = field(default_factory=dict)
119
+ created_at: datetime = field(default_factory=_utcnow)