-
Notifications
You must be signed in to change notification settings - Fork 1
fix: correct Strix model to github_models/gpt-4o #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
f9cf929
ebb45e0
d59d14f
04a4264
64eba33
c1ec3a1
2e3ed7d
8b148e3
9f18e94
911a69e
7e44cf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| from fastapi import APIRouter, Depends, HTTPException | ||
| from typing import Dict, Any | ||
|
|
||
| router = APIRouter() | ||
|
|
||
| @router.post("/writeback-intent") | ||
| async def register_writeback_intent(payload: Dict[str, Any]): | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| # In a real scenario, this would use the auth context dependency. | ||
| # Currently just a stub for Phase 10 API wiring. | ||
| account_map = payload.get("calendar_account_map", {}) | ||
| if not account_map: | ||
| raise HTTPException(status_code=400, detail="Missing account mapping") | ||
|
|
||
| return {"status": "success", "message": "Writeback intent registered", "target": account_map} | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| @router.post("/sync") | ||
| async def sync_caldav_accounts(): | ||
| # Deprecated legacy endpoint path, should fail or require strict auth | ||
| raise HTTPException(status_code=403, detail="Use specific writeback intent and verified sources instead.") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import asyncio | ||
| import logging | ||
| from typing import Dict, Any | ||
|
|
||
| try: | ||
| import websockets | ||
| except ImportError: | ||
| # Optional dependency for the runner | ||
| websockets = None | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| class SelfHostedConnector: | ||
| def __init__(self, target_ws_url: str, token: str): | ||
| self.target_ws_url = target_ws_url | ||
| self.token = token | ||
| self.connection = None | ||
| self.is_connected = False | ||
|
|
||
| async def connect(self): | ||
| if websockets is None: | ||
| logger.error("websockets library is not installed. Runner cannot start.") | ||
| return | ||
|
|
||
| headers = {"Authorization": f"Bearer {self.token}"} | ||
| try: | ||
| self.connection = await websockets.connect(self.target_ws_url, extra_headers=headers) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| self.is_connected = True | ||
| logger.info(f"Connected to Naruon Gateway at {self.target_ws_url}") | ||
| await self._listen_loop() | ||
| except Exception as e: | ||
| logger.error(f"Failed to connect to Naruon Gateway: {e}") | ||
| self.is_connected = False | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| async def _listen_loop(self): | ||
| try: | ||
| while self.is_connected: | ||
| message = await self.connection.recv() | ||
| await self.handle_message(message) | ||
| except websockets.exceptions.ConnectionClosed: | ||
| logger.warning("Connection closed by remote gateway.") | ||
| self.is_connected = False | ||
|
Comment on lines
+54
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Inspect broad catches in connector:"
rg -nP --type=py -C2 '\bexcept\s+Exception\b' backend/runner/connector.py
echo
echo "Inspect listen loop region:"
cat -n backend/runner/connector.py | sed -n '47,70p'Repository: Seongho-Bae/naruon Length of output: 1890 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Top of backend/runner/connector.py (imports + class header):"
cat -n backend/runner/connector.py | sed -n '1,120p'
echo
echo "Confirm whether asyncio is imported:"
rg -n --type=py '\bimport\s+asyncio\b|\bfrom\s+asyncio\s+import\b' backend/runner/connector.py
echo
echo "Confirm websockets import:"
rg -n --type=py '\bimport\s+websockets\b|\bfrom\s+websockets\s+import\b' backend/runner/connector.pyRepository: Seongho-Bae/naruon Length of output: 3720 Narrow In ♻️ Proposed fix async def _listen_loop(self):
if not self.connection:
return
try:
while self.is_connected:
message = await self.connection.recv()
await self.handle_message(message)
- except Exception as e:
- if websockets and isinstance(e, websockets.exceptions.ConnectionClosed):
+ except asyncio.CancelledError:
+ raise
+ except (OSError, asyncio.TimeoutError) as e:
+ logger.warning(f"Connection loop ended: {e}")
+ self.is_connected = False
+ except Exception as e:
+ if websockets and isinstance(e, websockets.exceptions.ConnectionClosed):
logger.warning("Connection closed by remote gateway.")
+ self.is_connected = False
else:
- logger.warning(f"Connection loop ended: {e}")
- self.is_connected = False
+ raise🧰 Tools🪛 Ruff (0.15.13)[warning] 54-54: Do not catch blind exception: (BLE001) 🤖 Prompt for AI Agents |
||
|
|
||
| async def handle_message(self, message: str): | ||
| # Dispatch message to internal SMTP/IMAP proxy handlers | ||
| logger.debug(f"Received instruction from gateway: {message}") | ||
| pass | ||
|
github-code-quality[bot] marked this conversation as resolved.
Fixed
|
||
|
|
||
| async def send_response(self, response: Dict[str, Any]): | ||
| if self.is_connected and self.connection: | ||
| import json | ||
| await self.connection.send(json.dumps(response)) | ||
|
|
||
| if __name__ == "__main__": | ||
| # Example usage for local bootstrap | ||
| connector = SelfHostedConnector("ws://localhost:8080/api/runner/ws", "sample-token") | ||
| asyncio.run(connector.connect()) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import logging | ||
| from typing import Dict, Any | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| class CalDavService: | ||
| def __init__(self): | ||
| pass | ||
|
|
||
| def determine_writeback_target(self, task_context: Dict[str, Any], connected_accounts: list) -> str: | ||
| """ | ||
| Determines the most appropriate CalDav account to write back to, | ||
| based on the context of the task (e.g., if it originated from a company email). | ||
| """ | ||
| # Basic ontology/context mock logic | ||
| source_email = task_context.get("source_email", "") | ||
| for account in connected_accounts: | ||
| if account.get("domain") in source_email: | ||
| return account.get("account_id") | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Fallback to the primary account | ||
| if connected_accounts: | ||
| return connected_accounts[0].get("account_id") | ||
| return "default_system_caldav" | ||
|
|
||
| caldav_service = CalDavService() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import hashlib | ||
| import logging | ||
| from typing import Dict, Any | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| def generate_email_fingerprint(email_data: Dict[str, Any]) -> str: | ||
| """ | ||
| Generates a unique fingerprint for an email based on its sender, subject, date, and body content. | ||
| Used to de-duplicate emails from ZIP imports or forwarding loops. | ||
| """ | ||
| sender = email_data.get("sender", "") | ||
| subject = email_data.get("subject", "") | ||
| date = str(email_data.get("date", "")) | ||
| body_snippet = email_data.get("body", "")[:500] # First 500 chars | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| raw_str = f"{sender}|{subject}|{date}|{body_snippet}" | ||
| return hashlib.sha256(raw_str.encode("utf-8")).hexdigest() | ||
|
|
||
| def detect_reply_tracking(email_data: Dict[str, Any]) -> bool: | ||
| """ | ||
| Detects if the user sent an email that expects a reply. | ||
| """ | ||
| body = email_data.get("body", "").lower() | ||
| return "please reply" in body or "?" in body | ||
|
|
||
| def process_self_to_self(email_data: Dict[str, Any], user_email: str) -> bool: | ||
| """ | ||
| Detects if an email is sent from the user to themselves, turning it into a knowledge node. | ||
| """ | ||
| sender = email_data.get("sender", "") | ||
| recipients = email_data.get("recipients", "") | ||
|
|
||
| if user_email in sender and user_email in recipients: | ||
| logger.info(f"Self-to-self email detected. Organizing as knowledge node.") | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| return True | ||
| return False | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import logging | ||
| from typing import Dict, Any | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| class OntologyService: | ||
| def __init__(self): | ||
| self.relationships = {} | ||
|
|
||
| def analyze_sender_relationship(self, user_email: str, sender_email: str, email_content: str) -> Dict[str, Any]: | ||
| """ | ||
| Analyzes the email content to build a relationship graph (DAG) between the user and the sender. | ||
| Returns attributes like the relationship type (e.g., Colleague, Client, Newsletter, Unknown) | ||
| and confidence score. | ||
| """ | ||
| # A simple stub logic for Phase 10 implementation | ||
| relationship_type = "Unknown" | ||
| confidence = 0.5 | ||
|
|
||
| if "unsubscribe" in email_content.lower(): | ||
| relationship_type = "Newsletter" | ||
| confidence = 0.9 | ||
| elif "@" in user_email and "@" in sender_email: | ||
| user_domain = user_email.split("@")[1] | ||
| sender_domain = sender_email.split("@")[1] | ||
| if user_domain == sender_domain: | ||
| relationship_type = "Colleague" | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| confidence = 0.85 | ||
|
|
||
| logger.info(f"Analyzed relationship: {sender_email} -> {relationship_type} (conf: {confidence})") | ||
| return { | ||
| "type": relationship_type, | ||
| "confidence": confidence | ||
| } | ||
|
|
||
| ontology_service = OntologyService() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import pytest | ||
|
|
||
| from services.caldav_service import caldav_service | ||
|
|
||
| def test_determine_writeback_target(): | ||
| connected_accounts = [ | ||
| {"account_id": "account1", "domain": "company.com"}, | ||
| {"account_id": "account2", "domain": "personal.com"} | ||
| ] | ||
|
|
||
| # Should match company.com | ||
| task_context_1 = {"source_email": "boss@company.com"} | ||
| target_1 = caldav_service.determine_writeback_target(task_context_1, connected_accounts) | ||
| assert target_1 == "account1" | ||
|
|
||
| # Should fallback to primary | ||
| task_context_2 = {"source_email": "friend@other.com"} | ||
| target_2 = caldav_service.determine_writeback_target(task_context_2, connected_accounts) | ||
| assert target_2 == "account1" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import pytest | ||
|
|
||
| from services.ontology_service import ontology_service | ||
|
|
||
| def test_analyze_sender_relationship(): | ||
| result1 = ontology_service.analyze_sender_relationship("seongho@company.com", "newsletter@marketing.com", "Please unsubscribe here") | ||
| assert result1["type"] == "Newsletter" | ||
| assert result1["confidence"] == 0.9 | ||
|
|
||
| result2 = ontology_service.analyze_sender_relationship("seongho@company.com", "boss@company.com", "Hello") | ||
| assert result2["type"] == "Colleague" | ||
| assert result2["confidence"] == 0.85 | ||
Uh oh!
There was an error while loading. Please reload this page.