Update src/agentcore/orchestrator.py
Browse files
src/agentcore/orchestrator.py
CHANGED
|
@@ -108,6 +108,33 @@ _LONG_DIGITS_RE = re.compile(r"\d{6,}")
|
|
| 108 |
_TAG_RE = re.compile(r"<[^>]+>")
|
| 109 |
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
def _redact_for_analytics(text: str, max_len: int = 200) -> str:
|
| 112 |
"""Privacy pass before anything reaches the analytics store: e-mail
|
| 113 |
addresses and long digit runs (IDs, phone numbers) are masked and the
|
|
@@ -147,7 +174,6 @@ def _log_agent_query(question: str, response: AgentResponse, start_time: float)
|
|
| 147 |
settings = get_settings()
|
| 148 |
worker_url = getattr(settings, "cloudflare_worker_url", "") or ""
|
| 149 |
if worker_url:
|
| 150 |
-
import asyncio as _al
|
| 151 |
async def _post_to_worker():
|
| 152 |
try:
|
| 153 |
from src.services.http_client import make_client as _mk
|
|
@@ -182,14 +208,12 @@ def _log_agent_query(question: str, response: AgentResponse, start_time: float)
|
|
| 182 |
)
|
| 183 |
elif _resp.status_code >= 400:
|
| 184 |
logger.warning(f"D1 logging failed: HTTP {_resp.status_code}")
|
|
|
|
|
|
|
| 185 |
except Exception as _exc:
|
| 186 |
logger.warning(f"D1 logging unreachable: {_exc}")
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
if loop.is_running():
|
| 190 |
-
loop.create_task(_post_to_worker())
|
| 191 |
-
except Exception:
|
| 192 |
-
pass
|
| 193 |
except Exception:
|
| 194 |
pass
|
| 195 |
|
|
|
|
| 108 |
_TAG_RE = re.compile(r"<[^>]+>")
|
| 109 |
|
| 110 |
|
| 111 |
+
# Strong references to in-flight analytics POSTs.
|
| 112 |
+
#
|
| 113 |
+
# asyncio keeps only WEAK references to tasks: a task created with
|
| 114 |
+
# create_task() and then dropped can be garbage-collected before it finishes,
|
| 115 |
+
# silently cancelling the request. That is invisible when the network is fast
|
| 116 |
+
# and drops writes unpredictably when the handshake is slow. Holding a
|
| 117 |
+
# reference until the task completes is the documented fix.
|
| 118 |
+
_ANALYTICS_TASKS = set()
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def _spawn_background(coro) -> bool:
|
| 122 |
+
"""Run coro in the background, keeping a strong reference. True if scheduled."""
|
| 123 |
+
try:
|
| 124 |
+
loop = asyncio.get_running_loop()
|
| 125 |
+
except RuntimeError:
|
| 126 |
+
try:
|
| 127 |
+
loop = asyncio.get_event_loop()
|
| 128 |
+
except Exception:
|
| 129 |
+
return False
|
| 130 |
+
if not loop.is_running():
|
| 131 |
+
return False
|
| 132 |
+
task = loop.create_task(coro)
|
| 133 |
+
_ANALYTICS_TASKS.add(task)
|
| 134 |
+
task.add_done_callback(_ANALYTICS_TASKS.discard)
|
| 135 |
+
return True
|
| 136 |
+
|
| 137 |
+
|
| 138 |
def _redact_for_analytics(text: str, max_len: int = 200) -> str:
|
| 139 |
"""Privacy pass before anything reaches the analytics store: e-mail
|
| 140 |
addresses and long digit runs (IDs, phone numbers) are masked and the
|
|
|
|
| 174 |
settings = get_settings()
|
| 175 |
worker_url = getattr(settings, "cloudflare_worker_url", "") or ""
|
| 176 |
if worker_url:
|
|
|
|
| 177 |
async def _post_to_worker():
|
| 178 |
try:
|
| 179 |
from src.services.http_client import make_client as _mk
|
|
|
|
| 208 |
)
|
| 209 |
elif _resp.status_code >= 400:
|
| 210 |
logger.warning(f"D1 logging failed: HTTP {_resp.status_code}")
|
| 211 |
+
else:
|
| 212 |
+
logger.debug("D1 logged OK")
|
| 213 |
except Exception as _exc:
|
| 214 |
logger.warning(f"D1 logging unreachable: {_exc}")
|
| 215 |
+
if not _spawn_background(_post_to_worker()):
|
| 216 |
+
logger.warning("D1 logging skipped: no running event loop")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
except Exception:
|
| 218 |
pass
|
| 219 |
|