Skip to content

Commit e964d41

Browse files
committed
Add telemetry
1 parent 939a69b commit e964d41

File tree

13 files changed

+1611
-1227
lines changed

13 files changed

+1611
-1227
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,12 @@ For more information, or help for implementing your ideas (or ones from a paper)
399399

400400
Beware that every additional metric/module/optimizer should be approved by the core team, we want to keep the library minimal and clean as possible to avoid an uncontrolled growth leading to bad software practices like in most current leading LM frameworks.
401401

402+
If you have specific feedbacks or features request we invite you to open an [issue](https://github.com/SynaLinks/synalinks/issues).
403+
404+
### Telemetry
405+
406+
We collect usage and software performance information to enhance and catch errors as fast as possible in order to make the best experience for you. No private, personal or confidential information is recorded. To get the complete list of what we collect, and how to disable it, please read the following [document](TELEMETRY.md).
407+
402408
### Community
403409

404410
Join our community to learn more about neuro-symbolic systems and the future of AI. We welcome the participation of people from very different backgrounds or education levels.

TELEMETRY.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Usage telemetry
2+
3+
In order to enhance Synalinks and provide you with the best software possible we collect usage information with [Sentry](https://sentry.io/welcome/).
4+
5+
Here is the list of what we collect, and the reason behind:
6+
7+
- *Modules exceptions*: To solve errors before they can go into production
8+
- *Language & embedding models exceptions*: The monitor integrations stability
9+
- *Knowledge bases exceptions*: To monitor integrations stability
10+
- *Uncatched exceptions*: To enhance your experience by providing you with better error messages
11+
12+
You can disable the telemetry by setting the following evironment variable `export SYNALINKS_TELEMETRY=false` in your `~/.bashrc` (or equivalent).
13+
14+
OR you can use `synalinks.disable_telemetry()` at the beginning of your scripts
15+
16+
```python
17+
import synalinks
18+
19+
synalinks.disable_telemetry()
20+
```
21+
22+
**Note**: We might collect more information in the future, but we will **NEVER** collect private, personal or confidential (like your prompts/data models) informations.
23+
24+
If you have specific feedbacks or feature request we invite you to open an [issue](https://github.com/SynaLinks/synalinks/issues).

coverage-badge.svg

Lines changed: 1 addition & 1 deletion
Loading

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ dependencies = [
3737
"graphviz",
3838
"click",
3939
"inquirer",
40-
"neo4j>=5.28.1",
40+
"neo4j",
41+
"sentry-sdk",
4142
]
4243

4344
[project.urls]

synalinks/cli/magic_cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
from synalinks.cli.utils.setup_utils import secrets_setup_config
1010
from synalinks.cli.utils.setup_utils import setup_project
1111
from synalinks.src.version import version as get_version
12+
from synalinks.src.backend.config import maybe_initialize_telemetry
1213

1314

1415
@click.group()
1516
def magic_cli():
1617
"""Synalinks AI powered command-line interface."""
17-
pass
18+
maybe_initialize_telemetry()
1819

1920

2021
@magic_cli.command()

synalinks/src/backend/config.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import json
66
import os
7-
87
import nest_asyncio
8+
import sentry_sdk
99

1010
from synalinks.src.api_export import synalinks_export
1111

@@ -28,6 +28,78 @@
2828
# Available backends
2929
_AVAILABLE_BACKEND = ["pydantic"]
3030

31+
# Enable/Disable telemetry
32+
_synalinks_telemetry_enabled = False
33+
34+
# Check if sentry have been initialized
35+
_telemetry_initialized = False
36+
37+
# Attemp to know if the telemetry is enabled from env variables
38+
if "SYNALINKS_TELEMETRY" in os.environ:
39+
try:
40+
_synalinks_telemetry_enabled = bool(os.environ.get("SYNALINKS_TELEMETRY"))
41+
except Exception:
42+
_synalinks_telemetry_enabled = True
43+
else:
44+
_synalinks_telemetry_enabled = True
45+
46+
47+
@synalinks_export(
48+
[
49+
"synalinks.config.is_telemetry_enabled",
50+
"synalinks.backend.is_telemetry_enabled",
51+
]
52+
)
53+
def is_telemetry_enabled():
54+
"""Return wether of not the telemetry is enabled
55+
56+
Returns:
57+
(bool): True if the telemetry is enabled False otherwise.
58+
"""
59+
return _synalinks_telemetry_enabled
60+
61+
62+
@synalinks_export(
63+
[
64+
"synalinks.config.disable_telemetry",
65+
"synalinks.disable_telemetry",
66+
]
67+
)
68+
def disable_telemetry():
69+
"""Disable the telemetry
70+
71+
Returns:
72+
(bool): True if the telemetry is enabled False otherwise.
73+
"""
74+
global _synalinks_telemetry_enabled
75+
_synalinks_telemetry_enabled = False
76+
77+
78+
def maybe_initialize_telemetry():
79+
global _telemetry_initialized
80+
if not _telemetry_initialized and _synalinks_telemetry_enabled:
81+
sentry_sdk.init(
82+
dsn="https://b53fc10adc81c3c865734c9b5115082a@o4509456897933312.ingest.de.sentry.io/4509456899637328", # noqa 501
83+
max_breadcrumbs=50,
84+
debug=False,
85+
# we do NOT collect private/personal information
86+
send_default_pii=False,
87+
# Set traces_sample_rate to 1.0 to capture 100%
88+
# of transactions for tracing.
89+
traces_sample_rate=1.0,
90+
)
91+
_telemetry_initialized = True
92+
93+
94+
def capture_exception(error):
95+
if is_telemetry_enabled():
96+
sentry_sdk.capture_exception(error)
97+
98+
99+
def capture_message(msg):
100+
if is_telemetry_enabled():
101+
sentry_sdk.capture_message(msg)
102+
31103

32104
@synalinks_export(["synalinks.config.floatx", "synalinks.backend.floatx"])
33105
def floatx():
@@ -239,6 +311,7 @@ def set_backend(backend):
239311
_BACKEND = backend
240312

241313

314+
242315
# Attemp to get the API key from env variables
243316
_api_key = os.getenv("SYNALINKS_API_KEY", None)
244317
if _api_key:
@@ -284,4 +357,4 @@ def set_backend(backend):
284357
f.write(json.dumps(_config, indent=4))
285358
except IOError:
286359
# Except permission denied.
287-
pass
360+
pass

synalinks/src/embedding_models/embedding_model.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from synalinks.src.api_export import synalinks_export
88
from synalinks.src.saving.synalinks_saveable import SynalinksSaveable
99

10+
from synalinks.src.backend.config import maybe_initialize_telemetry
11+
from synalinks.src.backend.config import capture_exception
12+
1013

1114
@synalinks_export(
1215
["synalinks.EmbeddingModel", "synalinks.embedding_models.EmbeddingModel"]
@@ -82,9 +85,17 @@ class EmbeddingModel(SynalinksSaveable):
8285
model (str): The model to use.
8386
api_base (str): Optional. The endpoint to use.
8487
retry (int): Optional. The number of retry.
88+
fallback (EmbeddingModel): Optional. The embedding model to fallback
89+
if anything is wrong.
8590
"""
8691

87-
def __init__(self, model=None, api_base=None, retry=5):
92+
def __init__(
93+
self,
94+
model=None,
95+
api_base=None,
96+
retry=5,
97+
fallback=None,
98+
):
8899
if model is None:
89100
raise ValueError(
90101
"You need to set the `model` argument for any EmbeddingModel"
@@ -95,6 +106,7 @@ def __init__(self, model=None, api_base=None, retry=5):
95106
else:
96107
self.api_base = api_base
97108
self.retry = retry
109+
self.fallback = fallback
98110

99111
async def __call__(self, texts, **kwargs):
100112
"""
@@ -106,6 +118,8 @@ async def __call__(self, texts, **kwargs):
106118
Returns:
107119
(list): The list of corresponding vectors.
108120
"""
121+
maybe_initialize_telemetry()
122+
109123
for i in range(self.retry):
110124
try:
111125
if self.api_base:
@@ -127,24 +141,44 @@ async def __call__(self, texts, **kwargs):
127141
return {"embeddings": vectors}
128142
except Exception as e:
129143
warnings.warn(f"Error occured while trying to call {self}: " + str(e))
130-
raise RuntimeError(
131-
f"Failed to retrieve embeddings with {self} after {self.retry} attempts."
132-
)
144+
capture_exception(e)
145+
if self.fallback:
146+
return self.fallback(
147+
texts,
148+
**kwargs,
149+
)
150+
else:
151+
return None
133152

134153
def _obj_type(self):
135154
return "EmbeddingModel"
136155

137156
def get_config(self):
138-
return {
157+
config = {
139158
"model": self.model,
140159
"api_base": self.api_base,
141160
"retry": self.retry,
142161
}
162+
if self.fallback:
163+
fallback_config = {
164+
"fallback": serialization_lib.serialize_synalinks_object(
165+
self.fallback,
166+
)
167+
}
168+
return {**fallback_config, **config}
169+
else:
170+
return config
143171

144172
@classmethod
145173
def from_config(cls, config):
146-
return cls(**config)
147-
174+
if "fallback" in config:
175+
fallback = serialization_lib.deserialize_synalinks_object(
176+
config.pop("fallback")
177+
)
178+
return cls(fallback=fallback, **config)
179+
else:
180+
return cls(**config)
181+
148182
def __repr__(self):
149183
api_base = f" api_base={self.api_base}" if self.api_base else ""
150184
return f"<EmbeddingModel model={self.model}{api_base}>"

synalinks/src/knowledge_bases/knowledge_base.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from synalinks.src.saving import serialization_lib
77
from synalinks.src.saving.synalinks_saveable import SynalinksSaveable
88

9+
from synalinks.src.backend.config import maybe_initialize_telemetry
10+
911

1012
@synalinks_export("synalinks.KnowledgeBase")
1113
class KnowledgeBase(SynalinksSaveable):
@@ -45,7 +47,7 @@ class IsPartOf(synalinks.Relation):
4547
)
4648
```
4749
48-
Note: Obviously, use an `.env` file and `.gitignore` to avoid putting your username and password
50+
**Note**: Obviously, use an `.env` file and `.gitignore` to avoid putting your username and password
4951
in the code or a config file that can lead to leackage when pushing it into repositories.
5052
5153
Args:
@@ -102,6 +104,7 @@ async def update(
102104
Entities with similarity above this threshold will be merged.
103105
Should be between 0.0 and 1.0 (Defaults to 0.8).
104106
"""
107+
maybe_initialize_telemetry()
105108
return await self.adapter.update(data_model)
106109

107110
async def query(self, query: str):
@@ -114,6 +117,7 @@ async def query(self, query: str):
114117
Returns:
115118
(GenericResult): the query results
116119
"""
120+
maybe_initialize_telemetry()
117121
return await self.adapter.query(query)
118122

119123
async def similarity_search(
@@ -135,6 +139,7 @@ async def similarity_search(
135139
Entities with similarity below this threshold are excluded.
136140
Should be between 0.0 and 1.0 (Defaults to 0.8).
137141
"""
142+
maybe_initialize_telemetry()
138143
return await self.adapter.similarity_search(
139144
similarity_search,
140145
k=k,
@@ -160,6 +165,7 @@ async def triplet_search(
160165
Triplets with similarity below this threshold are excluded.
161166
Should be between 0.0 and 1.0. (Defaults to 0.8).
162167
"""
168+
maybe_initialize_telemetry()
163169
return await self.adapter.triplet_search(
164170
triplet_search,
165171
k=k,

synalinks/src/language_models/language_model.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
from synalinks.src.saving import serialization_lib
1212
from synalinks.src.saving.synalinks_saveable import SynalinksSaveable
1313

14+
from synalinks.src.backend.config import maybe_initialize_telemetry
15+
from synalinks.src.backend.config import capture_exception
16+
1417

1518
@synalinks_export(
1619
[
@@ -145,7 +148,6 @@ def __init__(
145148
model=None,
146149
api_base=None,
147150
retry=5,
148-
timeout=30,
149151
fallback=None,
150152
):
151153
if model is None:
@@ -162,7 +164,6 @@ def __init__(
162164
else:
163165
self.api_base = api_base
164166
self.retry = retry
165-
self.timeout = timeout
166167

167168
async def __call__(self, messages, schema=None, streaming=False, **kwargs):
168169
"""
@@ -180,6 +181,8 @@ async def __call__(self, messages, schema=None, streaming=False, **kwargs):
180181
Returns:
181182
(dict): The generated structured response.
182183
"""
184+
maybe_initialize_telemetry()
185+
183186
formatted_messages = messages.get_json().get("messages", [])
184187
json_instance = {}
185188
input_kwargs = copy.deepcopy(kwargs)
@@ -293,7 +296,8 @@ async def __call__(self, messages, schema=None, streaming=False, **kwargs):
293296
json_instance = {"role": ChatRole.ASSISTANT, "content": response_str}
294297
return json_instance
295298
except Exception as e:
296-
warnings.warn(str(e))
299+
warnings.warn(f"Error occured while trying to call {self}: " + str(e))
300+
capture_exception(e)
297301
if self.fallback:
298302
return self.fallback(
299303
messages,

synalinks/src/modules/module.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
from synalinks.src.utils import python_utils
2222
from synalinks.src.utils import tracking
2323

24+
from synalinks.src.backend.config import maybe_initialize_telemetry
25+
from synalinks.src.backend.config import capture_exception
26+
2427
if backend.backend() == "pydantic":
2528
from synalinks.src.backend.pydantic.module import PydanticModule as BackendModule
2629
else:
@@ -494,6 +497,8 @@ def get_variable(self, name=None, index=None):
494497
)
495498

496499
async def __call__(self, *args, **kwargs):
500+
maybe_initialize_telemetry()
501+
497502
self._check_super_called()
498503
self._called = True
499504

@@ -572,6 +577,9 @@ async def __call__(self, *args, **kwargs):
572577

573578
if not self.built:
574579
self.built = True
580+
except Exception as e:
581+
capture_exception(e)
582+
raise e
575583
finally:
576584
# Destroy call context if we created it
577585
self._maybe_reset_call_context()

0 commit comments

Comments
 (0)