mirror of
https://github.com/Aider-AI/aider.git
synced 2025-05-30 01:04:59 +00:00
feat: add opt-in analytics collection with privacy notice
This commit is contained in:
parent
bd59a8debf
commit
267872b7e4
4 changed files with 64 additions and 21 deletions
|
@ -17,32 +17,53 @@ posthog_host = "https://us.i.posthog.com"
|
||||||
|
|
||||||
|
|
||||||
class Analytics:
|
class Analytics:
|
||||||
|
# providers
|
||||||
mp = None
|
mp = None
|
||||||
ph = None
|
ph = None
|
||||||
|
|
||||||
|
# saved
|
||||||
user_id = None
|
user_id = None
|
||||||
permanently_disable = None
|
permanently_disable = None
|
||||||
|
asked_opt_in = None
|
||||||
|
|
||||||
|
# ephemeral
|
||||||
logfile = None
|
logfile = None
|
||||||
|
|
||||||
def __init__(self, enable=False, logfile=None, permanently_disable=False):
|
def __init__(self, logfile=None, permanently_disable=False):
|
||||||
self.logfile = logfile
|
self.logfile = logfile
|
||||||
self.asked_opt_in = False
|
|
||||||
self.get_or_create_uuid()
|
self.get_or_create_uuid()
|
||||||
|
|
||||||
if not enable or self.permanently_disable or permanently_disable:
|
if self.permanently_disable or permanently_disable or not self.asked_opt_in:
|
||||||
self.disable(permanently_disable)
|
self.disable(permanently_disable)
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
if not self.user_id:
|
||||||
|
self.disable(False)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.user_id and not self.permanently_disable:
|
if self.permanently_disable:
|
||||||
self.mp = Mixpanel(mixpanel_project_token)
|
self.disable(False)
|
||||||
self.ph = Posthog(project_api_key=posthog_project_api_key, host=posthog_host)
|
return
|
||||||
|
|
||||||
def disable(self, permanently_disable):
|
if not self.asked_opt_in:
|
||||||
|
self.disable(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.mp = Mixpanel(mixpanel_project_token)
|
||||||
|
self.ph = Posthog(project_api_key=posthog_project_api_key, host=posthog_host)
|
||||||
|
|
||||||
|
def disable(self, permanently):
|
||||||
self.mp = None
|
self.mp = None
|
||||||
self.ph = None
|
self.ph = None
|
||||||
if permanently_disable and not self.permanently_disable:
|
|
||||||
|
if permanently:
|
||||||
|
self.asked_opt_in = True
|
||||||
self.permanently_disable = True
|
self.permanently_disable = True
|
||||||
self.save_data()
|
self.save_data()
|
||||||
|
|
||||||
|
def need_to_ask(self):
|
||||||
|
return not self.asked_opt_in and not self.permanently_disable
|
||||||
|
|
||||||
def get_data_file_path(self):
|
def get_data_file_path(self):
|
||||||
data_file = Path.home() / ".aider" / "analytics.json"
|
data_file = Path.home() / ".aider" / "analytics.json"
|
||||||
data_file.parent.mkdir(parents=True, exist_ok=True)
|
data_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
@ -64,8 +85,8 @@ class Analytics:
|
||||||
self.permanently_disable = data.get("permanently_disable")
|
self.permanently_disable = data.get("permanently_disable")
|
||||||
self.user_id = data.get("uuid")
|
self.user_id = data.get("uuid")
|
||||||
self.asked_opt_in = data.get("asked_opt_in", False)
|
self.asked_opt_in = data.get("asked_opt_in", False)
|
||||||
except json.decoder.JSONDecodeError:
|
except (json.decoder.JSONDecodeError, OSError):
|
||||||
pass
|
self.disable(permanently=False)
|
||||||
|
|
||||||
def save_data(self):
|
def save_data(self):
|
||||||
data_file = self.get_data_file_path()
|
data_file = self.get_data_file_path()
|
||||||
|
@ -75,6 +96,7 @@ class Analytics:
|
||||||
asked_opt_in=self.asked_opt_in,
|
asked_opt_in=self.asked_opt_in,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Allow exceptions; crash if we can't record permanently_disabled=True, etc
|
||||||
data_file.write_text(json.dumps(data, indent=4))
|
data_file.write_text(json.dumps(data, indent=4))
|
||||||
|
|
||||||
def get_system_info(self):
|
def get_system_info(self):
|
||||||
|
@ -110,10 +132,10 @@ class Analytics:
|
||||||
properties["aider_version"] = __version__
|
properties["aider_version"] = __version__
|
||||||
|
|
||||||
if self.mp:
|
if self.mp:
|
||||||
self.mp.track(self.user_id, event_name, properties)
|
self.mp.track(self.user_id, event_name, dict(properties))
|
||||||
|
|
||||||
if self.ph:
|
if self.ph:
|
||||||
self.ph.capture(self.user_id, event_name, properties)
|
self.ph.capture(self.user_id, event_name, dict(properties))
|
||||||
|
|
||||||
if self.logfile:
|
if self.logfile:
|
||||||
log_entry = {
|
log_entry = {
|
||||||
|
|
|
@ -430,7 +430,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
if args.analytics_disable:
|
if args.analytics_disable:
|
||||||
analytics = Analytics(enable=False, permanently_disable=True)
|
analytics = Analytics(permanently_disable=True)
|
||||||
print("Analytics have been permanently disabled.")
|
print("Analytics have been permanently disabled.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -495,9 +495,28 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||||
io = get_io(False)
|
io = get_io(False)
|
||||||
io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)")
|
io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)")
|
||||||
|
|
||||||
analytics = Analytics(
|
analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable)
|
||||||
args.analytics, logfile=args.analytics_log, permanently_disable=args.analytics_disable
|
if args.analytics:
|
||||||
)
|
if analytics.need_to_ask():
|
||||||
|
io.tool_output(
|
||||||
|
"Aider respects your privacy and never collects your code, prompts, chats, keys or"
|
||||||
|
" any personal info."
|
||||||
|
)
|
||||||
|
io.tool_output(f"For more info: {urls.analytics}")
|
||||||
|
disable = not io.confirm_ask(
|
||||||
|
"Allow collection of anonymous analytics to help improve aider?"
|
||||||
|
)
|
||||||
|
|
||||||
|
analytics.asked_opt_in = True
|
||||||
|
if disable:
|
||||||
|
analytics.disable(permanently=True)
|
||||||
|
io.tool_output("Analytics have been permanently disabled.")
|
||||||
|
|
||||||
|
analytics.save_data()
|
||||||
|
io.tool_output()
|
||||||
|
|
||||||
|
analytics.enable()
|
||||||
|
|
||||||
analytics.event("launched")
|
analytics.event("launched")
|
||||||
|
|
||||||
if args.gui and not return_coder:
|
if args.gui and not return_coder:
|
||||||
|
@ -796,7 +815,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||||
if args.exit:
|
if args.exit:
|
||||||
return
|
return
|
||||||
|
|
||||||
analytics.event("cli session", main_model=main_model)
|
analytics.event("cli session", main_model=main_model, edit_format=main_model.edit_format)
|
||||||
|
|
||||||
thread = threading.Thread(target=load_slow_imports)
|
thread = threading.Thread(target=load_slow_imports)
|
||||||
thread.daemon = True
|
thread.daemon = True
|
||||||
|
|
|
@ -11,3 +11,4 @@ large_repos = "https://aider.chat/docs/faq.html#can-i-use-aider-in-a-large-mono-
|
||||||
github_issues = "https://github.com/Aider-AI/aider/issues/new"
|
github_issues = "https://github.com/Aider-AI/aider/issues/new"
|
||||||
git_index_version = "https://github.com/Aider-AI/aider/issues/211"
|
git_index_version = "https://github.com/Aider-AI/aider/issues/211"
|
||||||
install_properly = "https://aider.chat/docs/troubleshooting/imports.html"
|
install_properly = "https://aider.chat/docs/troubleshooting/imports.html"
|
||||||
|
analytics = "https://aider.chat/docs/more/analytics.html"
|
||||||
|
|
|
@ -5,11 +5,12 @@ nav_order: 500
|
||||||
|
|
||||||
# Analytics
|
# Analytics
|
||||||
|
|
||||||
Aider collects anonymous analytics that are used to help
|
Aider can collect anonymous analytics to help
|
||||||
improve aider's ability to work with LLMs, edit code and complete user requests.
|
improve aider's ability to work with LLMs, edit code and complete user requests.
|
||||||
**Analytics are currently turned off by default**, but are
|
|
||||||
expected to be turned on by default in
|
## Opt-in
|
||||||
a future release.
|
|
||||||
|
Analytics are only collected if you agree and opt-in.
|
||||||
|
|
||||||
## Anonymous, no personal info
|
## Anonymous, no personal info
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue