lm-deluge 0.0.67__py3-none-any.whl → 0.0.90__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.

Potentially problematic release.


This version of lm-deluge might be problematic. Click here for more details.

Files changed (108) hide show
  1. lm_deluge/__init__.py +1 -2
  2. lm_deluge/api_requests/anthropic.py +117 -22
  3. lm_deluge/api_requests/base.py +84 -11
  4. lm_deluge/api_requests/bedrock.py +30 -6
  5. lm_deluge/api_requests/chat_reasoning.py +4 -0
  6. lm_deluge/api_requests/gemini.py +166 -20
  7. lm_deluge/api_requests/openai.py +145 -25
  8. lm_deluge/batches.py +15 -45
  9. lm_deluge/client.py +309 -50
  10. lm_deluge/config.py +15 -3
  11. lm_deluge/models/__init__.py +14 -1
  12. lm_deluge/models/anthropic.py +29 -14
  13. lm_deluge/models/arcee.py +16 -0
  14. lm_deluge/models/deepseek.py +36 -4
  15. lm_deluge/models/google.py +42 -0
  16. lm_deluge/models/grok.py +24 -0
  17. lm_deluge/models/kimi.py +36 -0
  18. lm_deluge/models/minimax.py +18 -0
  19. lm_deluge/models/openai.py +100 -0
  20. lm_deluge/models/openrouter.py +133 -7
  21. lm_deluge/models/together.py +11 -0
  22. lm_deluge/models/zai.py +50 -0
  23. lm_deluge/pipelines/gepa/__init__.py +95 -0
  24. lm_deluge/pipelines/gepa/core.py +354 -0
  25. lm_deluge/pipelines/gepa/docs/samples.py +705 -0
  26. lm_deluge/pipelines/gepa/examples/01_synthetic_keywords.py +140 -0
  27. lm_deluge/pipelines/gepa/examples/02_gsm8k_math.py +261 -0
  28. lm_deluge/pipelines/gepa/examples/03_hotpotqa_multihop.py +300 -0
  29. lm_deluge/pipelines/gepa/examples/04_batch_classification.py +271 -0
  30. lm_deluge/pipelines/gepa/examples/simple_qa.py +129 -0
  31. lm_deluge/pipelines/gepa/optimizer.py +435 -0
  32. lm_deluge/pipelines/gepa/proposer.py +235 -0
  33. lm_deluge/pipelines/gepa/util.py +165 -0
  34. lm_deluge/{llm_tools → pipelines}/score.py +2 -2
  35. lm_deluge/{llm_tools → pipelines}/translate.py +5 -3
  36. lm_deluge/prompt.py +537 -88
  37. lm_deluge/request_context.py +7 -2
  38. lm_deluge/server/__init__.py +24 -0
  39. lm_deluge/server/__main__.py +144 -0
  40. lm_deluge/server/adapters.py +369 -0
  41. lm_deluge/server/app.py +388 -0
  42. lm_deluge/server/auth.py +71 -0
  43. lm_deluge/server/model_policy.py +215 -0
  44. lm_deluge/server/models_anthropic.py +172 -0
  45. lm_deluge/server/models_openai.py +175 -0
  46. lm_deluge/tool/__init__.py +1130 -0
  47. lm_deluge/tool/builtin/anthropic/__init__.py +300 -0
  48. lm_deluge/tool/builtin/anthropic/bash.py +0 -0
  49. lm_deluge/tool/builtin/anthropic/computer_use.py +0 -0
  50. lm_deluge/tool/builtin/gemini.py +59 -0
  51. lm_deluge/tool/builtin/openai.py +74 -0
  52. lm_deluge/tool/cua/__init__.py +173 -0
  53. lm_deluge/tool/cua/actions.py +148 -0
  54. lm_deluge/tool/cua/base.py +27 -0
  55. lm_deluge/tool/cua/batch.py +215 -0
  56. lm_deluge/tool/cua/converters.py +466 -0
  57. lm_deluge/tool/cua/kernel.py +702 -0
  58. lm_deluge/tool/cua/trycua.py +989 -0
  59. lm_deluge/tool/prefab/__init__.py +45 -0
  60. lm_deluge/tool/prefab/batch_tool.py +156 -0
  61. lm_deluge/tool/prefab/docs.py +1119 -0
  62. lm_deluge/tool/prefab/email.py +294 -0
  63. lm_deluge/tool/prefab/filesystem.py +1711 -0
  64. lm_deluge/tool/prefab/full_text_search/__init__.py +285 -0
  65. lm_deluge/tool/prefab/full_text_search/tantivy_index.py +396 -0
  66. lm_deluge/tool/prefab/memory.py +458 -0
  67. lm_deluge/tool/prefab/otc/__init__.py +165 -0
  68. lm_deluge/tool/prefab/otc/executor.py +281 -0
  69. lm_deluge/tool/prefab/otc/parse.py +188 -0
  70. lm_deluge/tool/prefab/random.py +212 -0
  71. lm_deluge/tool/prefab/rlm/__init__.py +296 -0
  72. lm_deluge/tool/prefab/rlm/executor.py +349 -0
  73. lm_deluge/tool/prefab/rlm/parse.py +144 -0
  74. lm_deluge/tool/prefab/sandbox/__init__.py +19 -0
  75. lm_deluge/tool/prefab/sandbox/daytona_sandbox.py +483 -0
  76. lm_deluge/tool/prefab/sandbox/docker_sandbox.py +609 -0
  77. lm_deluge/tool/prefab/sandbox/fargate_sandbox.py +546 -0
  78. lm_deluge/tool/prefab/sandbox/modal_sandbox.py +469 -0
  79. lm_deluge/tool/prefab/sandbox/seatbelt_sandbox.py +827 -0
  80. lm_deluge/tool/prefab/sheets.py +385 -0
  81. lm_deluge/tool/prefab/skills.py +0 -0
  82. lm_deluge/tool/prefab/subagents.py +233 -0
  83. lm_deluge/tool/prefab/todos.py +342 -0
  84. lm_deluge/tool/prefab/tool_search.py +169 -0
  85. lm_deluge/tool/prefab/web_search.py +199 -0
  86. lm_deluge/tracker.py +16 -13
  87. lm_deluge/util/schema.py +412 -0
  88. lm_deluge/warnings.py +8 -0
  89. {lm_deluge-0.0.67.dist-info → lm_deluge-0.0.90.dist-info}/METADATA +23 -9
  90. lm_deluge-0.0.90.dist-info/RECORD +132 -0
  91. lm_deluge/built_in_tools/anthropic/__init__.py +0 -128
  92. lm_deluge/built_in_tools/openai.py +0 -28
  93. lm_deluge/presets/cerebras.py +0 -17
  94. lm_deluge/presets/meta.py +0 -13
  95. lm_deluge/tool.py +0 -849
  96. lm_deluge-0.0.67.dist-info/RECORD +0 -72
  97. lm_deluge/{llm_tools → pipelines}/__init__.py +1 -1
  98. /lm_deluge/{llm_tools → pipelines}/classify.py +0 -0
  99. /lm_deluge/{llm_tools → pipelines}/extract.py +0 -0
  100. /lm_deluge/{llm_tools → pipelines}/locate.py +0 -0
  101. /lm_deluge/{llm_tools → pipelines}/ocr.py +0 -0
  102. /lm_deluge/{built_in_tools/anthropic/bash.py → skills/anthropic.py} +0 -0
  103. /lm_deluge/{built_in_tools/anthropic/computer_use.py → skills/compat.py} +0 -0
  104. /lm_deluge/{built_in_tools → tool/builtin}/anthropic/editor.py +0 -0
  105. /lm_deluge/{built_in_tools → tool/builtin}/base.py +0 -0
  106. {lm_deluge-0.0.67.dist-info → lm_deluge-0.0.90.dist-info}/WHEEL +0 -0
  107. {lm_deluge-0.0.67.dist-info → lm_deluge-0.0.90.dist-info}/licenses/LICENSE +0 -0
  108. {lm_deluge-0.0.67.dist-info → lm_deluge-0.0.90.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,294 @@
1
+ """AWS SES email sending prefab tool."""
2
+
3
+ import json
4
+ import os
5
+ import re
6
+ from typing import Any
7
+
8
+ from .. import Tool
9
+
10
+
11
+ class EmailManager:
12
+ """
13
+ A prefab tool for sending emails via AWS SES.
14
+
15
+ Provides a tool to send emails with enforced recipient restrictions,
16
+ preventing agents from emailing arbitrary addresses.
17
+
18
+ Args:
19
+ allowed_recipients: List of allowed email addresses or domain patterns.
20
+ Patterns can be exact emails ("user@example.com") or
21
+ domain wildcards ("*@example.com").
22
+ sender_email: The verified SES sender email address.
23
+ If not provided, uses SES_SENDER_EMAIL env variable.
24
+ sender_name: Display name for the sender.
25
+ If not provided, uses SES_SENDER_NAME env variable.
26
+ region: AWS region for SES. If not provided, uses SES_REGION or defaults to "us-east-1".
27
+ send_tool_name: Name for the send email tool (default: "send_email")
28
+
29
+ Example:
30
+ ```python
31
+ # Only allow sending to specific addresses
32
+ manager = EmailManager(
33
+ allowed_recipients=["support@mycompany.com", "*@internal.mycompany.com"],
34
+ sender_email="noreply@mycompany.com",
35
+ sender_name="My App"
36
+ )
37
+
38
+ tools = manager.get_tools()
39
+ ```
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ allowed_recipients: list[str],
45
+ *,
46
+ sender_email: str | None = None,
47
+ sender_name: str | None = None,
48
+ region: str | None = None,
49
+ send_tool_name: str = "send_email",
50
+ ):
51
+ if not allowed_recipients:
52
+ raise ValueError(
53
+ "allowed_recipients must be provided and non-empty. "
54
+ "This is required to prevent agents from emailing arbitrary addresses."
55
+ )
56
+
57
+ self.allowed_recipients = allowed_recipients
58
+ self.send_tool_name = send_tool_name
59
+
60
+ # Handle sender email
61
+ if sender_email is not None:
62
+ self.sender_email = sender_email
63
+ else:
64
+ env_sender = os.environ.get("SES_SENDER_EMAIL")
65
+ if env_sender:
66
+ self.sender_email = env_sender
67
+ else:
68
+ raise ValueError(
69
+ "No sender email provided. Set sender_email parameter or "
70
+ "SES_SENDER_EMAIL environment variable."
71
+ )
72
+
73
+ # Handle sender name (optional)
74
+ if sender_name is not None:
75
+ self.sender_name = sender_name
76
+ else:
77
+ self.sender_name = os.environ.get("SES_SENDER_NAME")
78
+
79
+ # Handle region
80
+ if region is not None:
81
+ self.region = region
82
+ else:
83
+ self.region = os.environ.get("SES_REGION", "us-east-1")
84
+
85
+ self._tools: list[Tool] | None = None
86
+
87
+ def _get_sender(self) -> str:
88
+ """Get formatted sender with optional display name."""
89
+ if self.sender_name:
90
+ return f"{self.sender_name} <{self.sender_email}>"
91
+ return self.sender_email
92
+
93
+ def _is_recipient_allowed(self, email: str) -> bool:
94
+ """Check if an email address matches any allowed recipient pattern."""
95
+ email_lower = email.lower().strip()
96
+
97
+ for pattern in self.allowed_recipients:
98
+ pattern_lower = pattern.lower().strip()
99
+
100
+ if pattern_lower.startswith("*@"):
101
+ # Domain wildcard pattern
102
+ domain = pattern_lower[2:]
103
+ if email_lower.endswith(f"@{domain}"):
104
+ return True
105
+ else:
106
+ # Exact match
107
+ if email_lower == pattern_lower:
108
+ return True
109
+
110
+ return False
111
+
112
+ def _validate_email(self, email: str) -> bool:
113
+ """Basic email format validation."""
114
+ pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
115
+ return bool(re.match(pattern, email.strip()))
116
+
117
+ async def _send_email(
118
+ self,
119
+ to: str,
120
+ subject: str,
121
+ body: str,
122
+ html_body: str | None = None,
123
+ cc: list[str] | None = None,
124
+ bcc: list[str] | None = None,
125
+ ) -> str:
126
+ """
127
+ Send an email via AWS SES.
128
+
129
+ Args:
130
+ to: Recipient email address
131
+ subject: Email subject line
132
+ body: Plain text body of the email
133
+ html_body: Optional HTML body (if not provided, only plain text is sent)
134
+ cc: Optional list of CC recipients
135
+ bcc: Optional list of BCC recipients
136
+
137
+ Returns:
138
+ JSON string with status and result
139
+ """
140
+ try:
141
+ import aioboto3
142
+ except ImportError:
143
+ return json.dumps(
144
+ {
145
+ "status": "error",
146
+ "error": "aioboto3 not installed. Install with: pip install aioboto3",
147
+ }
148
+ )
149
+
150
+ # Validate primary recipient
151
+ to = to.strip()
152
+ if not self._validate_email(to):
153
+ return json.dumps(
154
+ {"status": "error", "error": f"Invalid email format: {to}"}
155
+ )
156
+
157
+ if not self._is_recipient_allowed(to):
158
+ return json.dumps(
159
+ {
160
+ "status": "error",
161
+ "error": f"Recipient not allowed: {to}. Allowed patterns: {self.allowed_recipients}",
162
+ }
163
+ )
164
+
165
+ # Validate and filter CC recipients
166
+ validated_cc: list[str] = []
167
+ if cc:
168
+ for addr in cc:
169
+ addr = addr.strip()
170
+ if not self._validate_email(addr):
171
+ return json.dumps(
172
+ {"status": "error", "error": f"Invalid CC email format: {addr}"}
173
+ )
174
+ if not self._is_recipient_allowed(addr):
175
+ return json.dumps(
176
+ {
177
+ "status": "error",
178
+ "error": f"CC recipient not allowed: {addr}. Allowed patterns: {self.allowed_recipients}",
179
+ }
180
+ )
181
+ validated_cc.append(addr)
182
+
183
+ # Validate and filter BCC recipients
184
+ validated_bcc: list[str] = []
185
+ if bcc:
186
+ for addr in bcc:
187
+ addr = addr.strip()
188
+ if not self._validate_email(addr):
189
+ return json.dumps(
190
+ {
191
+ "status": "error",
192
+ "error": f"Invalid BCC email format: {addr}",
193
+ }
194
+ )
195
+ if not self._is_recipient_allowed(addr):
196
+ return json.dumps(
197
+ {
198
+ "status": "error",
199
+ "error": f"BCC recipient not allowed: {addr}. Allowed patterns: {self.allowed_recipients}",
200
+ }
201
+ )
202
+ validated_bcc.append(addr)
203
+
204
+ try:
205
+ # Build destination
206
+ destination: dict[str, Any] = {"ToAddresses": [to]}
207
+ if validated_cc:
208
+ destination["CcAddresses"] = validated_cc
209
+ if validated_bcc:
210
+ destination["BccAddresses"] = validated_bcc
211
+
212
+ # Build message body
213
+ message_body: dict[str, Any] = {"Text": {"Data": body, "Charset": "UTF-8"}}
214
+ if html_body:
215
+ message_body["Html"] = {"Data": html_body, "Charset": "UTF-8"}
216
+
217
+ session = aioboto3.Session()
218
+ async with session.client("ses", region_name=self.region) as ses: # type: ignore[reportGeneralTypeIssues]
219
+ response = await ses.send_email(
220
+ Source=self._get_sender(),
221
+ Destination=destination,
222
+ Message={
223
+ "Subject": {"Data": subject, "Charset": "UTF-8"},
224
+ "Body": message_body,
225
+ },
226
+ )
227
+
228
+ message_id = response.get("MessageId", "")
229
+
230
+ return json.dumps(
231
+ {
232
+ "status": "success",
233
+ "message_id": message_id,
234
+ "message": f"Email sent successfully to {to}",
235
+ "recipients": {
236
+ "to": to,
237
+ "cc": validated_cc if validated_cc else None,
238
+ "bcc": validated_bcc if validated_bcc else None,
239
+ },
240
+ }
241
+ )
242
+
243
+ except Exception as e:
244
+ return json.dumps({"status": "error", "error": str(e)})
245
+
246
+ def get_tools(self) -> list[Tool]:
247
+ """Return the email tools."""
248
+ if self._tools is not None:
249
+ return self._tools
250
+
251
+ # Build description with allowed recipients info
252
+ allowed_desc = ", ".join(self.allowed_recipients[:5])
253
+ if len(self.allowed_recipients) > 5:
254
+ allowed_desc += f", ... ({len(self.allowed_recipients)} total)"
255
+
256
+ self._tools = [
257
+ Tool(
258
+ name=self.send_tool_name,
259
+ description=(
260
+ f"Send an email via AWS SES. "
261
+ f"Recipients are restricted to: {allowed_desc}. "
262
+ f"Supports plain text and HTML content, with optional CC and BCC."
263
+ ),
264
+ run=self._send_email,
265
+ parameters={
266
+ "to": {"type": "string", "description": "Recipient email address"},
267
+ "subject": {"type": "string", "description": "Email subject line"},
268
+ "body": {
269
+ "type": "string",
270
+ "description": "Plain text body of the email",
271
+ },
272
+ "html_body": {
273
+ "type": "string",
274
+ "description": "Optional HTML body. If provided, email will be sent as multipart with both plain text and HTML.",
275
+ },
276
+ "cc": {
277
+ "type": "array",
278
+ "items": {"type": "string"},
279
+ "description": "Optional list of CC recipient email addresses",
280
+ },
281
+ "bcc": {
282
+ "type": "array",
283
+ "items": {"type": "string"},
284
+ "description": "Optional list of BCC recipient email addresses",
285
+ },
286
+ },
287
+ required=["to", "subject", "body"],
288
+ )
289
+ ]
290
+
291
+ return self._tools
292
+
293
+
294
+ __all__ = ["EmailManager"]