ralphx 0.3.4__py3-none-any.whl → 0.4.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.
- ralphx/__init__.py +1 -1
- ralphx/adapters/base.py +10 -2
- ralphx/adapters/claude_cli.py +222 -82
- ralphx/api/routes/auth.py +780 -98
- ralphx/api/routes/config.py +3 -56
- ralphx/api/routes/export_import.py +6 -9
- ralphx/api/routes/loops.py +4 -4
- ralphx/api/routes/planning.py +882 -19
- ralphx/api/routes/resources.py +528 -6
- ralphx/api/routes/stream.py +58 -56
- ralphx/api/routes/templates.py +2 -2
- ralphx/api/routes/workflows.py +258 -47
- ralphx/cli.py +4 -1
- ralphx/core/auth.py +372 -172
- ralphx/core/database.py +588 -164
- ralphx/core/executor.py +170 -19
- ralphx/core/loop.py +15 -2
- ralphx/core/loop_templates.py +29 -3
- ralphx/core/planning_iteration_executor.py +633 -0
- ralphx/core/planning_service.py +119 -24
- ralphx/core/preview.py +9 -25
- ralphx/core/project_db.py +864 -121
- ralphx/core/project_export.py +1 -5
- ralphx/core/project_import.py +14 -29
- ralphx/core/resources.py +28 -2
- ralphx/core/sample_project.py +1 -5
- ralphx/core/templates.py +9 -9
- ralphx/core/workflow_executor.py +32 -3
- ralphx/core/workflow_export.py +4 -7
- ralphx/core/workflow_import.py +3 -27
- ralphx/mcp/__init__.py +6 -2
- ralphx/mcp/registry.py +3 -3
- ralphx/mcp/tools/diagnostics.py +1 -1
- ralphx/mcp/tools/monitoring.py +10 -16
- ralphx/mcp/tools/workflows.py +115 -33
- ralphx/mcp_server.py +6 -2
- ralphx/static/assets/index-BuLI7ffn.css +1 -0
- ralphx/static/assets/index-DWvlqOTb.js +264 -0
- ralphx/static/assets/index-DWvlqOTb.js.map +1 -0
- ralphx/static/index.html +2 -2
- ralphx/templates/loop_templates/consumer.md +2 -2
- {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/METADATA +33 -12
- {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/RECORD +45 -44
- ralphx/static/assets/index-CcRDyY3b.css +0 -1
- ralphx/static/assets/index-CcxfTosc.js +0 -251
- ralphx/static/assets/index-CcxfTosc.js.map +0 -1
- {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/WHEEL +0 -0
- {ralphx-0.3.4.dist-info → ralphx-0.4.0.dist-info}/entry_points.txt +0 -0
ralphx/core/planning_service.py
CHANGED
|
@@ -64,25 +64,109 @@ This format is machine-parsed. Non-compliance breaks the system.
|
|
|
64
64
|
# =============================================================================
|
|
65
65
|
|
|
66
66
|
PLANNING_BEHAVIOR = '''<behavior>
|
|
67
|
-
You are
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
|
|
67
|
+
You are an expert product architect and technical consultant helping users design
|
|
68
|
+
software products. You combine deep technical knowledge with strong product sense.
|
|
69
|
+
|
|
70
|
+
## Your Core Mission
|
|
71
|
+
|
|
72
|
+
Transform vague ideas into comprehensive, implementable design documents through
|
|
73
|
+
collaborative discovery. You're not just a Q&A bot—you're a thinking partner who
|
|
74
|
+
challenges assumptions, identifies blind spots, and brings industry expertise.
|
|
75
|
+
|
|
76
|
+
## Discovery Phases
|
|
77
|
+
|
|
78
|
+
Work through these phases naturally (you don't need to announce them):
|
|
79
|
+
|
|
80
|
+
### Phase 1: Problem Space Understanding
|
|
81
|
+
- What problem are we solving? Why does it matter?
|
|
82
|
+
- Who experiences this problem? (specific personas, not generic "users")
|
|
83
|
+
- What do they currently do? What's broken about that?
|
|
84
|
+
- What would success look like for them?
|
|
85
|
+
|
|
86
|
+
### Phase 2: Solution Requirements
|
|
87
|
+
- Core features (MVP vs nice-to-have)
|
|
88
|
+
- User workflows and key interactions
|
|
89
|
+
- Data the system needs to handle
|
|
90
|
+
- Integration points with other systems
|
|
91
|
+
- Constraints: budget, timeline, team skills, existing infrastructure
|
|
92
|
+
|
|
93
|
+
### Phase 3: Technical Architecture
|
|
94
|
+
- System components and how they communicate
|
|
95
|
+
- Data model and storage choices
|
|
96
|
+
- API design (if applicable)
|
|
97
|
+
- Authentication and authorization approach
|
|
98
|
+
- Third-party services and dependencies
|
|
99
|
+
|
|
100
|
+
### Phase 4: Infrastructure & Operations
|
|
101
|
+
- Hosting environment (cloud provider, on-prem, hybrid)
|
|
102
|
+
- Deployment strategy (containers, serverless, VMs)
|
|
103
|
+
- Scaling considerations
|
|
104
|
+
- Monitoring and observability
|
|
105
|
+
- Backup and disaster recovery
|
|
106
|
+
|
|
107
|
+
### Phase 5: Security & Compliance
|
|
108
|
+
- Data sensitivity and protection requirements
|
|
109
|
+
- Authentication mechanisms
|
|
110
|
+
- Compliance requirements (GDPR, HIPAA, SOC2, etc.)
|
|
111
|
+
- Threat model considerations
|
|
112
|
+
|
|
113
|
+
## How to Ask Questions
|
|
114
|
+
|
|
115
|
+
Ask 2-4 focused questions at a time. For each question:
|
|
116
|
+
- Explain WHY you're asking (what decision it informs)
|
|
117
|
+
- Offer concrete options when helpful, not just open-ended questions
|
|
118
|
+
- Share your initial thinking or recommendation if you have one
|
|
119
|
+
|
|
120
|
+
Good: "For authentication, are you thinking OAuth (Google/GitHub login) for simplicity,
|
|
121
|
+
or do you need custom username/password? OAuth is faster to implement and more secure,
|
|
122
|
+
but custom auth gives you more control over the user experience."
|
|
123
|
+
|
|
124
|
+
Bad: "How do you want to handle authentication?"
|
|
125
|
+
|
|
126
|
+
## Using Web Search
|
|
127
|
+
|
|
128
|
+
When you have web search available, use it strategically:
|
|
129
|
+
- Research industry best practices for the specific domain
|
|
130
|
+
- Look up current pricing/capabilities of services you might recommend
|
|
131
|
+
- Find examples of similar products for inspiration
|
|
132
|
+
- Verify technical approaches are current (technologies evolve fast)
|
|
133
|
+
- Look for common pitfalls in this type of application
|
|
134
|
+
|
|
135
|
+
IMPORTANT: Always tell the user when you're searching and summarize what you learned.
|
|
136
|
+
This builds trust and shows you're doing real research, not just making things up.
|
|
137
|
+
|
|
138
|
+
## Progressive Refinement
|
|
139
|
+
|
|
140
|
+
As you learn more:
|
|
141
|
+
- Periodically summarize your understanding ("Here's what I have so far...")
|
|
142
|
+
- Explicitly call out assumptions you're making
|
|
143
|
+
- Revisit earlier decisions if new information changes things
|
|
144
|
+
- Be willing to push back if something doesn't make sense
|
|
145
|
+
|
|
146
|
+
## Re-engagement Support
|
|
147
|
+
|
|
148
|
+
If the user is returning to continue or update an existing design doc:
|
|
149
|
+
- Acknowledge what exists and ask what they want to change
|
|
150
|
+
- Don't re-ask questions that are already answered in the doc
|
|
151
|
+
- Focus on the delta—what's new, changed, or needs refinement
|
|
152
|
+
|
|
153
|
+
## Offering to Generate
|
|
154
|
+
|
|
155
|
+
When you have enough information for a solid design document:
|
|
156
|
+
- Summarize the key decisions that have been made
|
|
157
|
+
- List any important questions that remain unanswered
|
|
158
|
+
- Offer to generate the design doc (the user will click a button)
|
|
159
|
+
|
|
160
|
+
The user can generate the document at ANY time—it doesn't have to be "complete."
|
|
161
|
+
Better to generate something and iterate than to wait forever for perfection.
|
|
162
|
+
|
|
163
|
+
## What NOT To Do
|
|
164
|
+
|
|
165
|
+
- Don't write code or implementation details (that's for later steps)
|
|
166
|
+
- Don't make major assumptions without confirming
|
|
167
|
+
- Don't be a yes-person—challenge ideas that seem problematic
|
|
168
|
+
- Don't overwhelm with too many questions
|
|
169
|
+
- Don't be generic—tailor advice to their specific situation
|
|
86
170
|
</behavior>'''
|
|
87
171
|
|
|
88
172
|
ARTIFACT_BEHAVIOR = '''<behavior>
|
|
@@ -153,10 +237,17 @@ class PlanningService:
|
|
|
153
237
|
"format_rules", "request", "design_doc", "guardrails",
|
|
154
238
|
]
|
|
155
239
|
for tag in dangerous_tags:
|
|
156
|
-
# Escape opening tags
|
|
240
|
+
# Escape opening tags with attributes: <tag ...>
|
|
241
|
+
sanitized = re.sub(
|
|
242
|
+
rf'<\s*{tag}\s([^>]*)>',
|
|
243
|
+
rf'<{tag} \1>',
|
|
244
|
+
sanitized,
|
|
245
|
+
flags=re.IGNORECASE,
|
|
246
|
+
)
|
|
247
|
+
# Escape opening tags without attributes: <tag>
|
|
157
248
|
sanitized = re.sub(
|
|
158
|
-
rf'<\s*{tag}
|
|
159
|
-
rf'<{tag}
|
|
249
|
+
rf'<\s*{tag}\s*>',
|
|
250
|
+
rf'<{tag}>',
|
|
160
251
|
sanitized,
|
|
161
252
|
flags=re.IGNORECASE,
|
|
162
253
|
)
|
|
@@ -264,6 +355,8 @@ Start your response with <design_doc> immediately.
|
|
|
264
355
|
self,
|
|
265
356
|
messages: list[dict],
|
|
266
357
|
model: str = "sonnet",
|
|
358
|
+
tools: Optional[list[str]] = None,
|
|
359
|
+
timeout: int = 180,
|
|
267
360
|
) -> AsyncIterator[StreamEvent]:
|
|
268
361
|
"""Stream Claude's response to the conversation.
|
|
269
362
|
|
|
@@ -272,6 +365,8 @@ Start your response with <design_doc> immediately.
|
|
|
272
365
|
Args:
|
|
273
366
|
messages: Conversation history.
|
|
274
367
|
model: Model to use (sonnet, opus, haiku).
|
|
368
|
+
tools: Optional list of tools to enable (e.g., ['WebSearch', 'WebFetch']).
|
|
369
|
+
timeout: Timeout in seconds (default 180 for web search).
|
|
275
370
|
|
|
276
371
|
Yields:
|
|
277
372
|
StreamEvent objects as Claude responds.
|
|
@@ -286,8 +381,8 @@ Start your response with <design_doc> immediately.
|
|
|
286
381
|
async for event in adapter.stream(
|
|
287
382
|
prompt=prompt,
|
|
288
383
|
model=model,
|
|
289
|
-
tools=
|
|
290
|
-
timeout=
|
|
384
|
+
tools=tools,
|
|
385
|
+
timeout=timeout,
|
|
291
386
|
):
|
|
292
387
|
yield event
|
|
293
388
|
|
ralphx/core/preview.py
CHANGED
|
@@ -337,16 +337,14 @@ class PromptPreviewEngine:
|
|
|
337
337
|
title = item.get("title") or ""
|
|
338
338
|
metadata = item.get("metadata")
|
|
339
339
|
metadata_json = json.dumps(metadata) if metadata else "{}"
|
|
340
|
-
|
|
340
|
+
workflow_id = item.get("workflow_id", "unknown")
|
|
341
341
|
|
|
342
342
|
# Substitution order: most specific first
|
|
343
343
|
result = template.replace("{{input_item.metadata}}", metadata_json)
|
|
344
344
|
result = result.replace("{{input_item.content}}", content)
|
|
345
345
|
result = result.replace("{{input_item.title}}", title)
|
|
346
346
|
result = result.replace("{{input_item}}", content)
|
|
347
|
-
result = result.replace("{{
|
|
348
|
-
# Backward compatibility
|
|
349
|
-
result = result.replace("{{source_loop}}", namespace)
|
|
347
|
+
result = result.replace("{{workflow_id}}", workflow_id)
|
|
350
348
|
|
|
351
349
|
return result
|
|
352
350
|
|
|
@@ -372,28 +370,17 @@ class PromptPreviewEngine:
|
|
|
372
370
|
return False
|
|
373
371
|
return bool(self.config.item_types.input.source)
|
|
374
372
|
|
|
375
|
-
def _get_source_namespace(self) -> Optional[str]:
|
|
376
|
-
"""Get the source namespace for consumer loops.
|
|
377
|
-
|
|
378
|
-
For consumer loops, this is the source loop name used as the namespace.
|
|
379
|
-
"""
|
|
380
|
-
if not self._is_consumer_loop():
|
|
381
|
-
return None
|
|
382
|
-
return self.config.item_types.input.source
|
|
383
|
-
|
|
384
373
|
def _get_sample_item(self) -> Optional[dict]:
|
|
385
|
-
"""Get a sample item
|
|
374
|
+
"""Get a sample item for preview (for consumer loops).
|
|
386
375
|
|
|
387
376
|
Returns:
|
|
388
377
|
Sample item dict or None.
|
|
389
378
|
"""
|
|
390
|
-
|
|
391
|
-
if not namespace:
|
|
379
|
+
if not self._is_consumer_loop():
|
|
392
380
|
return None
|
|
393
381
|
|
|
394
|
-
# Get first completed item
|
|
382
|
+
# Get first completed item
|
|
395
383
|
items, _ = self.db.list_work_items(
|
|
396
|
-
namespace=namespace,
|
|
397
384
|
status="completed",
|
|
398
385
|
limit=1,
|
|
399
386
|
)
|
|
@@ -403,7 +390,6 @@ class PromptPreviewEngine:
|
|
|
403
390
|
|
|
404
391
|
# Fallback: get any pending item
|
|
405
392
|
items, _ = self.db.list_work_items(
|
|
406
|
-
namespace=namespace,
|
|
407
393
|
status="pending",
|
|
408
394
|
limit=1,
|
|
409
395
|
)
|
|
@@ -414,8 +400,8 @@ class PromptPreviewEngine:
|
|
|
414
400
|
# Return a placeholder
|
|
415
401
|
return {
|
|
416
402
|
"id": "sample-001",
|
|
417
|
-
"content": "[Sample item content - no items
|
|
418
|
-
"
|
|
403
|
+
"content": "[Sample item content - no items available]",
|
|
404
|
+
"source": source_name,
|
|
419
405
|
}
|
|
420
406
|
|
|
421
407
|
def _explain_strategy(self) -> str:
|
|
@@ -472,15 +458,13 @@ class PromptPreviewEngine:
|
|
|
472
458
|
variables["{{input_item.content}}"] = sample_item.get("content", "")[:50] + "..."
|
|
473
459
|
variables["{{input_item.title}}"] = sample_item.get("title", "")
|
|
474
460
|
variables["{{input_item.metadata}}"] = "{...}"
|
|
475
|
-
variables["{{
|
|
476
|
-
variables["{{source_loop}}"] = sample_item.get("namespace", "") # Backward compat
|
|
461
|
+
variables["{{workflow_id}}"] = sample_item.get("workflow_id", "")
|
|
477
462
|
else:
|
|
478
463
|
variables["{{input_item}}"] = "[Claimed item content]"
|
|
479
464
|
variables["{{input_item.content}}"] = "[Claimed item content]"
|
|
480
465
|
variables["{{input_item.title}}"] = "[Claimed item title]"
|
|
481
466
|
variables["{{input_item.metadata}}"] = "[Claimed item metadata as JSON]"
|
|
482
|
-
variables["{{
|
|
483
|
-
variables["{{source_loop}}"] = self._get_source_namespace() or "" # Backward compat
|
|
467
|
+
variables["{{workflow_id}}"] = "[Workflow ID]"
|
|
484
468
|
|
|
485
469
|
# Design doc variable (if configured)
|
|
486
470
|
if self.config.context and self.config.context.design_doc:
|