codebuff-agent 2.1.74__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.
- codebuff_agent-2.1.74.data/data/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/package.json +157 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/150.39fa98117cc8ca590465.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/166.386ff9b2c37ac865fdee.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/216.25cf81131bf7274dabf4.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/353.581bd1cb00746399b62e.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/367.575f9621b63c9a641aa9.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/421.11dac223a2c2da1b09f5.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/481.cc2dec9fa869d66f1407.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/487.0796e931d6e651482c14.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/49.d326f554b6f9ccde6580.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/541.26897ea32845659e36be.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/579.e569d3edd8391fd8fbb3.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/666.d9bfdb23f98489d0a188.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/68.26b6f59c8454adb17f20.js +204 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/694.4ac443814260057e68f7.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/791.61affb8d3480d3d60e0d.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js +2 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/97.ea559a5b8a9a09f17c6b.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/style.js +4 -0
- codebuff_agent-2.1.74.data/data/share/jupyter/labextensions/codebuff/static/third-party-licenses.json +724 -0
- codebuff_agent-2.1.74.dist-info/METADATA +142 -0
- codebuff_agent-2.1.74.dist-info/RECORD +217 -0
- codebuff_agent-2.1.74.dist-info/WHEEL +4 -0
- codebuff_agent-2.1.74.dist-info/licenses/LICENSE +21 -0
- codebuff_core/__init__.py +153 -0
- codebuff_core/codebuff_config_defaults.json +64 -0
- codebuff_core/core/__init__.py +35 -0
- codebuff_core/core/code_validator.py +1238 -0
- codebuff_core/core/error_classifier.py +579 -0
- codebuff_core/core/exceptions.py +49 -0
- codebuff_core/data/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4 +100256 -0
- codebuff_core/directory_policy.py +32 -0
- codebuff_core/factory.py +155 -0
- codebuff_core/interfaces.py +197 -0
- codebuff_core/knowledge/__init__.py +31 -0
- codebuff_core/knowledge/chunking.py +375 -0
- codebuff_core/knowledge/loader.py +339 -0
- codebuff_core/knowledge/watchdog_service.py +354 -0
- codebuff_core/legacy_paths.py +69 -0
- codebuff_core/llm/__init__.py +13 -0
- codebuff_core/llm/service.py +407 -0
- codebuff_core/managers/__init__.py +22 -0
- codebuff_core/managers/config_manager.py +690 -0
- codebuff_core/managers/remote_config.py +673 -0
- codebuff_core/managers/remote_skills.py +539 -0
- codebuff_core/managers/session_manager.py +255 -0
- codebuff_core/middleware/__init__.py +3 -0
- codebuff_core/middleware/error_recovery.py +394 -0
- codebuff_core/middleware/logging_utils.py +33 -0
- codebuff_core/models/__init__.py +111 -0
- codebuff_core/models/agent.py +306 -0
- codebuff_core/models/chat.py +57 -0
- codebuff_core/models/common.py +82 -0
- codebuff_core/models/rag.py +300 -0
- codebuff_core/platform_rules.py +127 -0
- codebuff_core/prompts/__init__.py +44 -0
- codebuff_core/prompts/auto_agent_prompts.py +1278 -0
- codebuff_core/services/__init__.py +30 -0
- codebuff_core/services/agent_service.py +345 -0
- codebuff_core/services/analytics_aggregate.py +837 -0
- codebuff_core/services/chat_service.py +898 -0
- codebuff_core/services/context_condenser.py +306 -0
- codebuff_core/services/pii_guardrail.py +393 -0
- codebuff_core/services/rag_chat_agent.py +2993 -0
- codebuff_core/services/rag_collections.py +2740 -0
- codebuff_core/services/rag_manager.py +890 -0
- codebuff_core/services/rag_service.py +145 -0
- codebuff_core/services/retriever.py +260 -0
- codebuff_core/services/vllm_embedding_service.py +290 -0
- codebuff_core/tests/__init__.py +1 -0
- codebuff_core/tests/conftest.py +81 -0
- codebuff_core/tests/test_error_recovery.py +647 -0
- codebuff_core/tests/test_factory.py +135 -0
- codebuff_core/tests/test_services.py +150 -0
- jupyter_ext/__init__.py +266 -0
- jupyter_ext/_version.py +4 -0
- jupyter_ext/agent/__init__.py +49 -0
- jupyter_ext/agent/agent_factory.py +2270 -0
- jupyter_ext/agent/ast_tools.py +451 -0
- jupyter_ext/agent/autofix_middleware.py +481 -0
- jupyter_ext/agent/background_procs.py +372 -0
- jupyter_ext/agent/code_history_middleware.py +211 -0
- jupyter_ext/agent/codex_autofix.py +160 -0
- jupyter_ext/agent/codex_bridge.py +3339 -0
- jupyter_ext/agent/codex_hooks.py +320 -0
- jupyter_ext/agent/codex_slash.py +685 -0
- jupyter_ext/agent/codex_threads.py +114 -0
- jupyter_ext/agent/content_injection.py +377 -0
- jupyter_ext/agent/continuation_middleware.py +548 -0
- jupyter_ext/agent/custom_skills.py +335 -0
- jupyter_ext/agent/file_diffs.py +99 -0
- jupyter_ext/agent/git_history.py +188 -0
- jupyter_ext/agent/guardrail_middleware.py +421 -0
- jupyter_ext/agent/json_repair_utils.py +54 -0
- jupyter_ext/agent/langchain_tools.py +2673 -0
- jupyter_ext/agent/mermaid_fixer.py +289 -0
- jupyter_ext/agent/mermaid_middleware.py +135 -0
- jupyter_ext/agent/middleware.py +359 -0
- jupyter_ext/agent/middleware_prompts.py +53 -0
- jupyter_ext/agent/observability.py +334 -0
- jupyter_ext/agent/patch_tool_calls.py +57 -0
- jupyter_ext/agent/prompts.py +112 -0
- jupyter_ext/agent/skill_middleware.py +257 -0
- jupyter_ext/agent/skills/airflow_dag_development.md +36 -0
- jupyter_ext/agent/skills/anomaly_detection.md +12 -0
- jupyter_ext/agent/skills/athena_sql.md +156 -0
- jupyter_ext/agent/skills/bdp_table_search.md +33 -0
- jupyter_ext/agent/skills/bigquery.md +65 -0
- jupyter_ext/agent/skills/bigquery_oracle.md +231 -0
- jupyter_ext/agent/skills/bigquery_tuning.md +307 -0
- jupyter_ext/agent/skills/classification_evaluation.md +11 -0
- jupyter_ext/agent/skills/clustering_analysis.md +12 -0
- jupyter_ext/agent/skills/comprehensive_eda.md +11 -0
- jupyter_ext/agent/skills/data_analysis.md +236 -0
- jupyter_ext/agent/skills/data_cleaning_pipeline.md +12 -0
- jupyter_ext/agent/skills/data_loading.md +158 -0
- jupyter_ext/agent/skills/data_quality_report.md +13 -0
- jupyter_ext/agent/skills/dimensionality_reduction.md +12 -0
- jupyter_ext/agent/skills/emr_script_development.md +17 -0
- jupyter_ext/agent/skills/experiment_tracking.md +13 -0
- jupyter_ext/agent/skills/feature_importance_analysis.md +10 -0
- jupyter_ext/agent/skills/hyperparameter_tuning.md +12 -0
- jupyter_ext/agent/skills/hypothesis_testing.md +12 -0
- jupyter_ext/agent/skills/imbalanced_data_handling.md +12 -0
- jupyter_ext/agent/skills/inference.md +392 -0
- jupyter_ext/agent/skills/lambda_function_development.md +19 -0
- jupyter_ext/agent/skills/model_comparison.md +11 -0
- jupyter_ext/agent/skills/model_explainability.md +12 -0
- jupyter_ext/agent/skills/model_training.md +318 -0
- jupyter_ext/agent/skills/pyspark.md +352 -0
- jupyter_ext/agent/skills/python_code_quality.md +35 -0
- jupyter_ext/agent/skills/regression_evaluation.md +12 -0
- jupyter_ext/agent/skills/spring_java_development.md +35 -0
- jupyter_ext/agent/skills/text_preprocessing.md +12 -0
- jupyter_ext/agent/skills/time_series_analysis.md +13 -0
- jupyter_ext/agent/skills/unit_testing.md +203 -0
- jupyter_ext/agent/todo_prompts.py +57 -0
- jupyter_ext/agent/tool_report.py +45 -0
- jupyter_ext/analytics_ingest.py +599 -0
- jupyter_ext/etc/jupyter/jupyter_server_config.d/codebuff_agent.json +7 -0
- jupyter_ext/handlers/__init__.py +8 -0
- jupyter_ext/handlers/analytics_handler.py +170 -0
- jupyter_ext/handlers/benchmark_handler.py +794 -0
- jupyter_ext/handlers/eda_benchmark_handler.py +1328 -0
- jupyter_ext/handlers/rag_admin_handler.py +689 -0
- jupyter_ext/handlers/websocket.py +497 -0
- jupyter_ext/http_handlers.py +8744 -0
- jupyter_ext/idle_monitor.py +346 -0
- jupyter_ext/labextension/package.json +157 -0
- jupyter_ext/labextension/static/104.2b8f20f9d1ac2c9db01b.js +1 -0
- jupyter_ext/labextension/static/150.39fa98117cc8ca590465.js +1 -0
- jupyter_ext/labextension/static/166.386ff9b2c37ac865fdee.js +1 -0
- jupyter_ext/labextension/static/216.25cf81131bf7274dabf4.js +1 -0
- jupyter_ext/labextension/static/353.581bd1cb00746399b62e.js +1 -0
- jupyter_ext/labextension/static/367.575f9621b63c9a641aa9.js +1 -0
- jupyter_ext/labextension/static/421.11dac223a2c2da1b09f5.js +1 -0
- jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js +2 -0
- jupyter_ext/labextension/static/432.8513b4c6cc1d1232d165.js.LICENSE.txt +19 -0
- jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js +2 -0
- jupyter_ext/labextension/static/439.ba5b5ef70ec6de214b8c.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/481.cc2dec9fa869d66f1407.js +1 -0
- jupyter_ext/labextension/static/487.0796e931d6e651482c14.js +1 -0
- jupyter_ext/labextension/static/49.d326f554b6f9ccde6580.js +1 -0
- jupyter_ext/labextension/static/541.26897ea32845659e36be.js +1 -0
- jupyter_ext/labextension/static/579.e569d3edd8391fd8fbb3.js +1 -0
- jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js +2 -0
- jupyter_ext/labextension/static/58.eb7cc679f7535cc8a2f1.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/666.d9bfdb23f98489d0a188.js +1 -0
- jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js +2 -0
- jupyter_ext/labextension/static/677.d266c39dbed33bc388b3.js.LICENSE.txt +8 -0
- jupyter_ext/labextension/static/68.26b6f59c8454adb17f20.js +204 -0
- jupyter_ext/labextension/static/694.4ac443814260057e68f7.js +1 -0
- jupyter_ext/labextension/static/791.61affb8d3480d3d60e0d.js +1 -0
- jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js +2 -0
- jupyter_ext/labextension/static/848.4d2db09cfbb09374b299.js.LICENSE.txt +9 -0
- jupyter_ext/labextension/static/868.5cfe7e1a348ec5af1b8c.js +1 -0
- jupyter_ext/labextension/static/97.ea559a5b8a9a09f17c6b.js +1 -0
- jupyter_ext/labextension/static/remoteEntry.5292fc9c6df7e7920cc8.js +1 -0
- jupyter_ext/labextension/static/style.js +4 -0
- jupyter_ext/labextension/static/third-party-licenses.json +724 -0
- jupyter_ext/log_paths.py +113 -0
- jupyter_ext/resource_usage.py +519 -0
- jupyter_ext/secrets_bootstrap.py +116 -0
- jupyter_ext/team_access.py +105 -0
- jupyter_ext/tests/__init__.py +1 -0
- jupyter_ext/tests/test_context.py +234 -0
- jupyter_ext/tests/test_factory.py +563 -0
- jupyter_ext/tests/test_file_finder.py +139 -0
- jupyter_ext/tests/test_handlers.py +58 -0
- jupyter_ext/tests/test_mcp_selftest.py +204 -0
- jupyter_ext/tests/test_mermaid_fixer.py +272 -0
- jupyter_ext/tests/test_resource_tiers.py +298 -0
- jupyter_ext/tools/__init__.py +15 -0
- jupyter_ext/tools/base.py +90 -0
- jupyter_ext/tools/context.py +268 -0
- jupyter_ext/tools/dir_complete.py +86 -0
- jupyter_ext/tools/file_finder.py +303 -0
- jupyter_ext/tools/file_tools.py +244 -0
- jupyter_ext/tools/jupyter_tools.py +296 -0
- jupyter_ext/tools/registry.py +159 -0
- jupyter_ext/tools/search_tools.py +230 -0
- jupyter_ext/tools/security_tools.py +657 -0
- jupyter_ext/tools/shell_tools.py +109 -0
- jupyter_ext/tools/user_tools.py +132 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Analytics events handler - append JSONL to file."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import datetime
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from jupyter_server.base.handlers import APIHandler
|
|
14
|
+
|
|
15
|
+
from .._version import __version__ as _EXT_VERSION
|
|
16
|
+
from ..analytics_ingest import enqueue_ux_event
|
|
17
|
+
from ..log_paths import get_analytics_log_path
|
|
18
|
+
from ..resource_usage import get_integrated_resources
|
|
19
|
+
from ..team_access import resolve_team
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
VALID_EVENT_TYPES = {"impression", "click", "error"}
|
|
24
|
+
|
|
25
|
+
# Environment-variable sourced identity fields.
|
|
26
|
+
# If any is missing, the handler is disabled (silent 204 for every request).
|
|
27
|
+
_ENV_TEAM = resolve_team()
|
|
28
|
+
_ENV_PROJECT = os.environ.get("HDSP_PRJ_ID", "")
|
|
29
|
+
_ENV_USER = os.environ.get("JUPYTERHUB_USER", "") or os.environ.get("HDSP_BDP_ID", "")
|
|
30
|
+
print(
|
|
31
|
+
f"[Analytics] env check: HDSP_USER_TEAM={_ENV_TEAM!r}, "
|
|
32
|
+
f"HDSP_PRJ_ID={_ENV_PROJECT!r}, "
|
|
33
|
+
f"JUPYTERHUB_USER={os.environ.get('JUPYTERHUB_USER', '')!r}, "
|
|
34
|
+
f"HDSP_BDP_ID={os.environ.get('HDSP_BDP_ID', '')!r}, "
|
|
35
|
+
f"_ENV_USER={_ENV_USER!r}",
|
|
36
|
+
flush=True,
|
|
37
|
+
)
|
|
38
|
+
_DISABLED = not ((_ENV_TEAM or _ENV_PROJECT) and _ENV_USER)
|
|
39
|
+
print(f"[Analytics] _DISABLED={_DISABLED}", flush=True)
|
|
40
|
+
if _DISABLED:
|
|
41
|
+
logger.warning(
|
|
42
|
+
"Analytics disabled: missing identity (team=%r, HDSP_PRJ_ID=%r, "
|
|
43
|
+
"JUPYTERHUB_USER/HDSP_BDP_ID=%r)",
|
|
44
|
+
_ENV_TEAM,
|
|
45
|
+
_ENV_PROJECT,
|
|
46
|
+
_ENV_USER,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _get_analytics_log_path_for_type(event_type: str) -> Optional[Path]:
|
|
51
|
+
"""Return today's log file path for a given event type.
|
|
52
|
+
|
|
53
|
+
File pattern: ``analytics_{event_type}_{project}_{user}_{YYYYMMDD}.jsonl``
|
|
54
|
+
"""
|
|
55
|
+
today = datetime.date.today().isoformat()
|
|
56
|
+
day_cache = _DAILY_PATH_CACHE.get(today)
|
|
57
|
+
if day_cache is not None:
|
|
58
|
+
cached = day_cache.get(event_type)
|
|
59
|
+
if cached is not None:
|
|
60
|
+
return cached
|
|
61
|
+
else:
|
|
62
|
+
day_cache = {}
|
|
63
|
+
_DAILY_PATH_CACHE[today] = day_cache
|
|
64
|
+
path = get_analytics_log_path(event_type, today.replace("-", ""))
|
|
65
|
+
day_cache[event_type] = path
|
|
66
|
+
return path
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
_DAILY_PATH_CACHE: dict[str, dict[str, Path]] = {}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _collect_hardware_attrs() -> dict[str, object]:
|
|
73
|
+
try:
|
|
74
|
+
res = get_integrated_resources()
|
|
75
|
+
cpu = res.get("cpu", {})
|
|
76
|
+
mem = res.get("memory", {})
|
|
77
|
+
gpus = res.get("gpus", [])
|
|
78
|
+
return {
|
|
79
|
+
"cpu_cores": cpu.get("cores"),
|
|
80
|
+
"memory_total_gb": mem.get("total_gb"),
|
|
81
|
+
"gpu_name": gpus[0]["name"] if gpus else None,
|
|
82
|
+
"gpu_memory_total_mb": gpus[0]["memory_total_mb"] if gpus else None,
|
|
83
|
+
}
|
|
84
|
+
except Exception:
|
|
85
|
+
return {
|
|
86
|
+
"cpu_cores": None,
|
|
87
|
+
"memory_total_gb": None,
|
|
88
|
+
"gpu_name": None,
|
|
89
|
+
"gpu_memory_total_mb": None,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_HW_ATTRS: dict[str, object] = _collect_hardware_attrs()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _enqueue_events(events: list) -> None:
|
|
97
|
+
"""RDS 적재 큐에 넣는다. 워커 스레드에서 돈다 — 빌더가 JSON 직렬화를 하므로
|
|
98
|
+
이벤트루프 위에서 돌리면 큰 context 에서 응답이 밀린다."""
|
|
99
|
+
for event in events:
|
|
100
|
+
enqueue_ux_event(event)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _write_events(path: Path, lines: list[str]) -> None:
|
|
104
|
+
try:
|
|
105
|
+
with open(path, "a", encoding="utf-8") as f:
|
|
106
|
+
for line in lines:
|
|
107
|
+
f.write(line + "\n")
|
|
108
|
+
except OSError:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class AnalyticsEventsHandler(APIHandler):
|
|
113
|
+
"""POST /codebuff/analytics/events"""
|
|
114
|
+
|
|
115
|
+
def check_xsrf_cookie(self):
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
async def post(self):
|
|
119
|
+
try:
|
|
120
|
+
if _DISABLED:
|
|
121
|
+
self.set_status(204)
|
|
122
|
+
self.finish()
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
body = json.loads(self.request.body.decode("utf-8"))
|
|
126
|
+
if not isinstance(body, list):
|
|
127
|
+
self.set_status(400)
|
|
128
|
+
self.finish()
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
lines_by_type: dict[str, list[str]] = {}
|
|
132
|
+
accepted: list[dict] = []
|
|
133
|
+
for event in body:
|
|
134
|
+
if not isinstance(event, dict):
|
|
135
|
+
continue
|
|
136
|
+
if event.get("event_type") not in VALID_EVENT_TYPES:
|
|
137
|
+
continue
|
|
138
|
+
if not event.get("feature"):
|
|
139
|
+
continue
|
|
140
|
+
# Server-side injection
|
|
141
|
+
event["datetime"] = datetime.datetime.now().strftime(
|
|
142
|
+
"%Y-%m-%d %H:%M:%S"
|
|
143
|
+
)
|
|
144
|
+
event["team"] = _ENV_TEAM
|
|
145
|
+
event["project"] = _ENV_PROJECT
|
|
146
|
+
event["user_id"] = _ENV_USER
|
|
147
|
+
event["extension_version"] = _EXT_VERSION
|
|
148
|
+
event.update(_HW_ATTRS)
|
|
149
|
+
et = event["event_type"]
|
|
150
|
+
lines_by_type.setdefault(et, []).append(
|
|
151
|
+
json.dumps(event, ensure_ascii=False)
|
|
152
|
+
)
|
|
153
|
+
accepted.append(event)
|
|
154
|
+
|
|
155
|
+
for et, lines in lines_by_type.items():
|
|
156
|
+
log_path = _get_analytics_log_path_for_type(et)
|
|
157
|
+
if log_path is not None:
|
|
158
|
+
await asyncio.to_thread(_write_events, log_path, lines)
|
|
159
|
+
|
|
160
|
+
# 파일 기록 뒤에 RDS 로 한 벌 더 보낸다 (이슈 #30). enqueue 는 예외를
|
|
161
|
+
# 내지 않고 URL 이 없으면 no-op 이라 여기서 응답이 바뀌지 않는다.
|
|
162
|
+
if accepted:
|
|
163
|
+
await asyncio.to_thread(_enqueue_events, accepted)
|
|
164
|
+
|
|
165
|
+
self.set_status(204)
|
|
166
|
+
self.finish()
|
|
167
|
+
except Exception:
|
|
168
|
+
logger.exception("Analytics write failed")
|
|
169
|
+
self.set_status(204)
|
|
170
|
+
self.finish()
|