nikeshn commited on
Commit
547e9da
·
verified ·
1 Parent(s): ecf574b

Update src/agentcore/orchestrator.py

Browse files
Files changed (1) hide show
  1. src/agentcore/orchestrator.py +31 -7
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
- try:
188
- loop = _al.get_event_loop()
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