evalforge-runtime 0.2.1__tar.gz → 0.2.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.
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/PKG-INFO +1 -1
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/pyproject.toml +1 -1
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/gmail.py +100 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/.github/workflows/workflow.yml +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/.gitignore +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/LICENSE +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/README.md +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/docs/decisions/001-file-forwarding-in-process-chains.md +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/fixtures/evalforge.config.yaml +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/__init__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/__main__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/__init__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/base.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/__init__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/email_forward.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/email_mark_read.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/email_move.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/email_reply.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/email_send.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/file_save_output.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/gdrive_upload.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/process_call.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/sharepoint_upload.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/builtins/webhook_post.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/runner.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/auth.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/condition.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/config.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/__init__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/base.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/exchange.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/slack.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/webhook.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/db.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/executor.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/files.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/observability.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/pipeline.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/scheduler.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/server.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/storage.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/types.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/ui.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/__init__.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/conftest.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_config.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_connectors.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_db.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_executor.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_langfuse.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_pipeline.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_reviews.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_server.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/tests/test_ui.py +0 -0
- {evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evalforge-runtime
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Runtime engine for EvalForge generated applications
|
|
5
5
|
Project-URL: Homepage, https://github.com/JannisConen/evalforge-runtime
|
|
6
6
|
Project-URL: Repository, https://github.com/JannisConen/evalforge-runtime
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/gmail.py
RENAMED
|
@@ -447,6 +447,106 @@ class GmailConnector(Connector):
|
|
|
447
447
|
resp.raise_for_status()
|
|
448
448
|
logger.info("Reply sent via Gmail API (thread=%s)", thread_id)
|
|
449
449
|
|
|
450
|
+
# -----------------------------------------------------------------------
|
|
451
|
+
# Forward
|
|
452
|
+
# -----------------------------------------------------------------------
|
|
453
|
+
|
|
454
|
+
async def forward(self, message_id: str, to: str, body: str = "") -> None:
|
|
455
|
+
"""Forward a message to another recipient."""
|
|
456
|
+
logger.info("Forwarding message %s to %s", message_id, to)
|
|
457
|
+
if self._auth_method != "service_account":
|
|
458
|
+
await self._forward_smtp(message_id, to, body)
|
|
459
|
+
else:
|
|
460
|
+
await self._forward_api(message_id, to, body)
|
|
461
|
+
|
|
462
|
+
async def _forward_smtp(self, message_id: str, to: str, body: str) -> None:
|
|
463
|
+
"""Forward via SMTP — fetches original via IMAP and re-sends."""
|
|
464
|
+
def _do_forward() -> None:
|
|
465
|
+
mail = imaplib.IMAP4_SSL("imap.gmail.com")
|
|
466
|
+
mail.login(self._mailbox, self.secrets["GMAIL_APP_PASSWORD"])
|
|
467
|
+
mail.select("INBOX")
|
|
468
|
+
|
|
469
|
+
_, msg_data = mail.fetch(message_id.encode(), "(RFC822)")
|
|
470
|
+
if not msg_data or not msg_data[0] or not isinstance(msg_data[0], tuple):
|
|
471
|
+
raise ValueError(f"Could not fetch original message {message_id}")
|
|
472
|
+
|
|
473
|
+
original = email_lib.message_from_bytes(msg_data[0][1])
|
|
474
|
+
subject = str(original.get("Subject", ""))
|
|
475
|
+
fwd_subject = subject if subject.lower().startswith("fwd:") else f"Fwd: {subject}"
|
|
476
|
+
|
|
477
|
+
import email.mime.multipart
|
|
478
|
+
fwd = email.mime.multipart.MIMEMultipart()
|
|
479
|
+
fwd["From"] = self._mailbox
|
|
480
|
+
fwd["To"] = to
|
|
481
|
+
fwd["Subject"] = fwd_subject
|
|
482
|
+
|
|
483
|
+
if body:
|
|
484
|
+
fwd.attach(email.mime.text.MIMEText(body, "html"))
|
|
485
|
+
|
|
486
|
+
# Attach original as message/rfc822
|
|
487
|
+
import email.mime.message
|
|
488
|
+
attached = email.mime.message.MIMEMessage(original)
|
|
489
|
+
fwd.attach(attached)
|
|
490
|
+
|
|
491
|
+
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
|
|
492
|
+
server.login(self._mailbox, self.secrets["GMAIL_APP_PASSWORD"])
|
|
493
|
+
server.send_message(fwd)
|
|
494
|
+
logger.info("Message %s forwarded via SMTP to %s", message_id, to)
|
|
495
|
+
mail.logout()
|
|
496
|
+
|
|
497
|
+
await asyncio.to_thread(_do_forward)
|
|
498
|
+
|
|
499
|
+
async def _forward_api(self, message_id: str, to: str, body: str) -> None:
|
|
500
|
+
"""Forward via Gmail API — fetches raw message and re-sends."""
|
|
501
|
+
token = await self._acquire_token()
|
|
502
|
+
|
|
503
|
+
async with httpx.AsyncClient() as client:
|
|
504
|
+
# Fetch original metadata for subject
|
|
505
|
+
resp = await client.get(
|
|
506
|
+
f"https://gmail.googleapis.com/gmail/v1/users/{self._mailbox}"
|
|
507
|
+
f"/messages/{message_id}?format=metadata",
|
|
508
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
509
|
+
)
|
|
510
|
+
resp.raise_for_status()
|
|
511
|
+
msg = resp.json()
|
|
512
|
+
headers_map = {
|
|
513
|
+
h["name"].lower(): h["value"]
|
|
514
|
+
for h in msg.get("payload", {}).get("headers", [])
|
|
515
|
+
}
|
|
516
|
+
subject = headers_map.get("subject", "")
|
|
517
|
+
fwd_subject = subject if subject.lower().startswith("fwd:") else f"Fwd: {subject}"
|
|
518
|
+
|
|
519
|
+
# Fetch raw RFC822
|
|
520
|
+
raw_resp = await client.get(
|
|
521
|
+
f"https://gmail.googleapis.com/gmail/v1/users/{self._mailbox}"
|
|
522
|
+
f"/messages/{message_id}?format=raw",
|
|
523
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
524
|
+
)
|
|
525
|
+
raw_resp.raise_for_status()
|
|
526
|
+
raw_bytes = base64.urlsafe_b64decode(raw_resp.json().get("raw", ""))
|
|
527
|
+
|
|
528
|
+
original = email_lib.message_from_bytes(raw_bytes)
|
|
529
|
+
|
|
530
|
+
import email.mime.multipart
|
|
531
|
+
import email.mime.message
|
|
532
|
+
fwd = email.mime.multipart.MIMEMultipart()
|
|
533
|
+
fwd["to"] = to
|
|
534
|
+
fwd["subject"] = fwd_subject
|
|
535
|
+
if body:
|
|
536
|
+
fwd.attach(email.mime.text.MIMEText(body, "html"))
|
|
537
|
+
fwd.attach(email.mime.message.MIMEMessage(original))
|
|
538
|
+
|
|
539
|
+
raw_fwd = base64.urlsafe_b64encode(fwd.as_bytes()).decode("utf-8")
|
|
540
|
+
|
|
541
|
+
async with httpx.AsyncClient() as client:
|
|
542
|
+
resp = await client.post(
|
|
543
|
+
f"https://gmail.googleapis.com/gmail/v1/users/{self._mailbox}/messages/send",
|
|
544
|
+
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
|
|
545
|
+
json={"raw": raw_fwd},
|
|
546
|
+
)
|
|
547
|
+
resp.raise_for_status()
|
|
548
|
+
logger.info("Message %s forwarded via Gmail API to %s", message_id, to)
|
|
549
|
+
|
|
450
550
|
# -----------------------------------------------------------------------
|
|
451
551
|
# Move / Mark read
|
|
452
552
|
# -----------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/actions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/__init__.py
RENAMED
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/base.py
RENAMED
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/exchange.py
RENAMED
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/slack.py
RENAMED
|
File without changes
|
{evalforge_runtime-0.2.1 → evalforge_runtime-0.2.2}/src/evalforge_runtime/connectors/webhook.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|