mrmd-ai 0.1.0__tar.gz → 0.1.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.
- mrmd_ai-0.1.2/LICENSE +21 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/PKG-INFO +2 -1
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/pyproject.toml +1 -1
- mrmd_ai-0.1.2/src/mrmd_ai/custom_programs.py +215 -0
- mrmd_ai-0.1.2/src/mrmd_ai/juice.py +758 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/__init__.py +11 -0
- mrmd_ai-0.1.2/src/mrmd_ai/modules/edit.py +102 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/server.py +275 -23
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/__init__.py +15 -0
- mrmd_ai-0.1.2/src/mrmd_ai/signatures/edit.py +173 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/uv.lock +2 -7
- mrmd_ai-0.1.0/src/mrmd_ai/juice.py +0 -416
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/.gitignore +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/README.md +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/dspy.config.yaml +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/AddTypeHintsPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/CorrectAndFinishLinePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/DocumentCodePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/ExplainCodePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FinishCodeLinePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FinishCodeSectionPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FinishParagraphPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FinishSentencePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FixGrammarPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FixTranscriptionPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/FormatCodePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/GetSynonymsPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/IdentifyReplacementPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/ImproveNamesPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/RefactorCodePredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/ReformatMarkdownPredict.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/logs/server.log +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/openapi.json +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/__init__.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/metrics/__init__.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/code.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/correct.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/document.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/finish.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/fix.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/notebook.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/modules/text.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/optimizers/__init__.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/code.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/correct.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/document.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/finish.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/fix.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/notebook.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/signatures/text.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/src/mrmd_ai/utils/__init__.py +0 -0
- {mrmd_ai-0.1.0 → mrmd_ai-0.1.2}/tests/__init__.py +0 -0
mrmd_ai-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Maxime Rivest
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom Program Factory - Generate DSPy modules from user-defined templates.
|
|
3
|
+
|
|
4
|
+
This module allows users to create custom AI commands without writing code.
|
|
5
|
+
Users define their commands with:
|
|
6
|
+
- name: Display name for the command
|
|
7
|
+
- inputType: What text to process (selection, cursor, fullDoc)
|
|
8
|
+
- outputType: What to do with result (replace, insert)
|
|
9
|
+
- instructions: Natural language instructions for the AI
|
|
10
|
+
|
|
11
|
+
The factory generates DSPy Signature and Module classes dynamically.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
import dspy
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create_custom_signature(
|
|
19
|
+
name: str,
|
|
20
|
+
instructions: str,
|
|
21
|
+
input_type: str = "selection",
|
|
22
|
+
output_type: str = "replace",
|
|
23
|
+
) -> type:
|
|
24
|
+
"""Create a DSPy Signature class from user configuration.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
name: Command name (used for class naming)
|
|
28
|
+
instructions: User's instructions for the AI
|
|
29
|
+
input_type: "selection" | "cursor" | "fullDoc"
|
|
30
|
+
output_type: "replace" | "insert"
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A DSPy Signature class
|
|
34
|
+
"""
|
|
35
|
+
# Build the docstring from user instructions
|
|
36
|
+
docstring = f"""{instructions}
|
|
37
|
+
|
|
38
|
+
IMPORTANT RULES:
|
|
39
|
+
- Output ONLY the result text, no explanations or meta-commentary
|
|
40
|
+
- Maintain appropriate formatting (markdown, code style, etc.)
|
|
41
|
+
- Be concise and direct
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# Define input fields based on input type
|
|
45
|
+
if input_type == "selection":
|
|
46
|
+
input_fields = {
|
|
47
|
+
"text": dspy.InputField(desc="The selected text to process"),
|
|
48
|
+
"local_context": dspy.InputField(desc="Text surrounding the selection for context"),
|
|
49
|
+
"document_context": dspy.InputField(desc="Broader document context"),
|
|
50
|
+
}
|
|
51
|
+
elif input_type == "cursor":
|
|
52
|
+
input_fields = {
|
|
53
|
+
"text_before_cursor": dspy.InputField(desc="Text before the cursor position"),
|
|
54
|
+
"local_context": dspy.InputField(desc="Text surrounding the cursor for context"),
|
|
55
|
+
"document_context": dspy.InputField(desc="Broader document context"),
|
|
56
|
+
}
|
|
57
|
+
else: # fullDoc
|
|
58
|
+
input_fields = {
|
|
59
|
+
"document_context": dspy.InputField(desc="The full document content"),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Define output field
|
|
63
|
+
output_field_name = "result"
|
|
64
|
+
output_fields = {
|
|
65
|
+
output_field_name: dspy.OutputField(desc="The processed result text. Output ONLY the result.")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Create the Signature class dynamically
|
|
69
|
+
# Clean name for class naming (remove spaces, special chars)
|
|
70
|
+
class_name = "".join(c for c in name if c.isalnum()) + "Signature"
|
|
71
|
+
|
|
72
|
+
signature_class = type(
|
|
73
|
+
class_name,
|
|
74
|
+
(dspy.Signature,),
|
|
75
|
+
{
|
|
76
|
+
"__doc__": docstring,
|
|
77
|
+
"__annotations__": {
|
|
78
|
+
**{k: str for k in input_fields},
|
|
79
|
+
**{k: str for k in output_fields},
|
|
80
|
+
},
|
|
81
|
+
**input_fields,
|
|
82
|
+
**output_fields,
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return signature_class
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def create_custom_module(
|
|
90
|
+
name: str,
|
|
91
|
+
instructions: str,
|
|
92
|
+
input_type: str = "selection",
|
|
93
|
+
output_type: str = "replace",
|
|
94
|
+
) -> type:
|
|
95
|
+
"""Create a DSPy Module class from user configuration.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
name: Command name
|
|
99
|
+
instructions: User's instructions for the AI
|
|
100
|
+
input_type: "selection" | "cursor" | "fullDoc"
|
|
101
|
+
output_type: "replace" | "insert"
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
A DSPy Module class (not instance)
|
|
105
|
+
"""
|
|
106
|
+
signature = create_custom_signature(name, instructions, input_type, output_type)
|
|
107
|
+
|
|
108
|
+
# Clean name for class naming
|
|
109
|
+
class_name = "".join(c for c in name if c.isalnum()) + "Predict"
|
|
110
|
+
|
|
111
|
+
class CustomModule(dspy.Module):
|
|
112
|
+
"""Dynamically generated custom command module."""
|
|
113
|
+
|
|
114
|
+
def __init__(self):
|
|
115
|
+
super().__init__()
|
|
116
|
+
self.predictor = dspy.Predict(signature)
|
|
117
|
+
self._input_type = input_type
|
|
118
|
+
self._output_type = output_type
|
|
119
|
+
|
|
120
|
+
def forward(self, **kwargs) -> Any:
|
|
121
|
+
return self.predictor(**kwargs)
|
|
122
|
+
|
|
123
|
+
# Set the class name
|
|
124
|
+
CustomModule.__name__ = class_name
|
|
125
|
+
CustomModule.__qualname__ = class_name
|
|
126
|
+
|
|
127
|
+
return CustomModule
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class CustomProgramRegistry:
|
|
131
|
+
"""Registry for user-defined custom programs.
|
|
132
|
+
|
|
133
|
+
Manages creation and caching of custom DSPy modules.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
def __init__(self):
|
|
137
|
+
self._modules: dict[str, type] = {}
|
|
138
|
+
self._configs: dict[str, dict] = {}
|
|
139
|
+
|
|
140
|
+
def register(self, program_id: str, config: dict) -> type:
|
|
141
|
+
"""Register a custom program from configuration.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
program_id: Unique identifier for this program
|
|
145
|
+
config: Dict with name, instructions, inputType, outputType
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
The generated Module class
|
|
149
|
+
"""
|
|
150
|
+
module_class = create_custom_module(
|
|
151
|
+
name=config.get("name", program_id),
|
|
152
|
+
instructions=config.get("instructions", "Process this text."),
|
|
153
|
+
input_type=config.get("inputType", "selection"),
|
|
154
|
+
output_type=config.get("outputType", "replace"),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
self._modules[program_id] = module_class
|
|
158
|
+
self._configs[program_id] = config
|
|
159
|
+
|
|
160
|
+
return module_class
|
|
161
|
+
|
|
162
|
+
def get(self, program_id: str) -> type | None:
|
|
163
|
+
"""Get a registered module class by ID."""
|
|
164
|
+
return self._modules.get(program_id)
|
|
165
|
+
|
|
166
|
+
def get_config(self, program_id: str) -> dict | None:
|
|
167
|
+
"""Get the configuration for a registered program."""
|
|
168
|
+
return self._configs.get(program_id)
|
|
169
|
+
|
|
170
|
+
def unregister(self, program_id: str) -> bool:
|
|
171
|
+
"""Remove a program from the registry."""
|
|
172
|
+
if program_id in self._modules:
|
|
173
|
+
del self._modules[program_id]
|
|
174
|
+
del self._configs[program_id]
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
def clear(self):
|
|
179
|
+
"""Clear all registered programs."""
|
|
180
|
+
self._modules.clear()
|
|
181
|
+
self._configs.clear()
|
|
182
|
+
|
|
183
|
+
def list_programs(self) -> list[str]:
|
|
184
|
+
"""List all registered program IDs."""
|
|
185
|
+
return list(self._modules.keys())
|
|
186
|
+
|
|
187
|
+
def is_registered(self, program_id: str) -> bool:
|
|
188
|
+
"""Check if a program is registered."""
|
|
189
|
+
return program_id in self._modules
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# Global registry instance
|
|
193
|
+
custom_registry = CustomProgramRegistry()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def register_custom_programs(commands: list[dict]) -> None:
|
|
197
|
+
"""Register multiple custom programs from a list of command configs.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
commands: List of command configurations, each with:
|
|
201
|
+
- id or program: Unique identifier
|
|
202
|
+
- name: Display name
|
|
203
|
+
- instructions: AI instructions
|
|
204
|
+
- inputType: selection | cursor | fullDoc
|
|
205
|
+
- outputType: replace | insert
|
|
206
|
+
"""
|
|
207
|
+
for cmd in commands:
|
|
208
|
+
program_id = cmd.get("program") or cmd.get("id")
|
|
209
|
+
if program_id:
|
|
210
|
+
custom_registry.register(program_id, cmd)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_custom_program(program_id: str) -> type | None:
|
|
214
|
+
"""Get a custom program module class by ID."""
|
|
215
|
+
return custom_registry.get(program_id)
|