cortexdb-connectors 0.2.6__tar.gz → 0.2.8__tar.gz
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.
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/PKG-INFO +1 -1
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/__init__.py +40 -40
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/base.py +38 -9
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/cli.py +2 -1
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/confluence/__init__.py +359 -359
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/discord/__init__.py +646 -646
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/gitlab/__init__.py +749 -749
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/hubspot/__init__.py +489 -489
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/intercom/__init__.py +631 -631
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/linear/__init__.py +705 -705
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/pagerduty/__init__.py +298 -298
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/salesforce/__init__.py +399 -399
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/servicenow/__init__.py +460 -460
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/slack/__init__.py +103 -11
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/state.py +145 -145
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/teams/__init__.py +656 -656
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/webhooks.py +7 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/zendesk/__init__.py +496 -496
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/PKG-INFO +1 -1
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/pyproject.toml +87 -87
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_base.py +114 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_slack.py +104 -7
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/README.md +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/freshdesk/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/github/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/google_workspace/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/insights/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/insights/api.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/insights/detectors.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/insights/engine.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/jira/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/notion/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/tldv/__init__.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors/worker.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/SOURCES.txt +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/dependency_links.txt +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/entry_points.txt +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/requires.txt +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/cortexdb_connectors.egg-info/top_level.txt +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/setup.cfg +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_connector_coverage.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_github.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_ingest_e2e.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_streaming_cursor.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_webhooks.py +0 -0
- {cortexdb_connectors-0.2.6 → cortexdb_connectors-0.2.8}/tests/test_webhooks_replay.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cortexdb-connectors
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Data connectors for CortexDB — ingest from Slack, GitHub, GitLab, Jira, Linear, Confluence, Notion, PagerDuty, Discord, Teams, Google Workspace, Salesforce, HubSpot, Zendesk, Intercom, and ServiceNow into the v1 memory API.
|
|
5
5
|
Author-email: CortexDB Team <team@cortexdb.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
"""cortexdb_connectors — data connector SDK for the CortexDB v1 API.
|
|
2
|
-
|
|
3
|
-
Each connector subclass implements `fetch_events`, `normalize`, and
|
|
4
|
-
`map_permissions`; the base class POSTs every normalised Episode to
|
|
5
|
-
`/v1/experience` with the required Authorization + X-Cortex-Actor
|
|
6
|
-
headers. Use the `cortexdb-sync` CLI to run a connector once or in a
|
|
7
|
-
poll loop; or `from cortexdb_connectors.slack import SlackConnector`
|
|
8
|
-
to drive it programmatically.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
__version__ = "0.2.
|
|
12
|
-
|
|
13
|
-
from cortexdb_connectors.base import (
|
|
14
|
-
Actor,
|
|
15
|
-
CortexConnector,
|
|
16
|
-
EntityRef,
|
|
17
|
-
Episode,
|
|
18
|
-
EpisodeType,
|
|
19
|
-
RawEvent,
|
|
20
|
-
Source,
|
|
21
|
-
SyncResult,
|
|
22
|
-
SyncState,
|
|
23
|
-
Visibility,
|
|
24
|
-
VisibilityLevel,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
__all__ = [
|
|
28
|
-
"__version__",
|
|
29
|
-
"Actor",
|
|
30
|
-
"CortexConnector",
|
|
31
|
-
"EntityRef",
|
|
32
|
-
"Episode",
|
|
33
|
-
"EpisodeType",
|
|
34
|
-
"RawEvent",
|
|
35
|
-
"Source",
|
|
36
|
-
"SyncResult",
|
|
37
|
-
"SyncState",
|
|
38
|
-
"Visibility",
|
|
39
|
-
"VisibilityLevel",
|
|
40
|
-
]
|
|
1
|
+
"""cortexdb_connectors — data connector SDK for the CortexDB v1 API.
|
|
2
|
+
|
|
3
|
+
Each connector subclass implements `fetch_events`, `normalize`, and
|
|
4
|
+
`map_permissions`; the base class POSTs every normalised Episode to
|
|
5
|
+
`/v1/experience` with the required Authorization + X-Cortex-Actor
|
|
6
|
+
headers. Use the `cortexdb-sync` CLI to run a connector once or in a
|
|
7
|
+
poll loop; or `from cortexdb_connectors.slack import SlackConnector`
|
|
8
|
+
to drive it programmatically.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "0.2.8"
|
|
12
|
+
|
|
13
|
+
from cortexdb_connectors.base import (
|
|
14
|
+
Actor,
|
|
15
|
+
CortexConnector,
|
|
16
|
+
EntityRef,
|
|
17
|
+
Episode,
|
|
18
|
+
EpisodeType,
|
|
19
|
+
RawEvent,
|
|
20
|
+
Source,
|
|
21
|
+
SyncResult,
|
|
22
|
+
SyncState,
|
|
23
|
+
Visibility,
|
|
24
|
+
VisibilityLevel,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"__version__",
|
|
29
|
+
"Actor",
|
|
30
|
+
"CortexConnector",
|
|
31
|
+
"EntityRef",
|
|
32
|
+
"Episode",
|
|
33
|
+
"EpisodeType",
|
|
34
|
+
"RawEvent",
|
|
35
|
+
"Source",
|
|
36
|
+
"SyncResult",
|
|
37
|
+
"SyncState",
|
|
38
|
+
"Visibility",
|
|
39
|
+
"VisibilityLevel",
|
|
40
|
+
]
|
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
import hashlib
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
+
import re
|
|
16
17
|
import uuid
|
|
17
18
|
from abc import ABC, abstractmethod
|
|
18
19
|
from dataclasses import dataclass, field
|
|
@@ -24,6 +25,11 @@ DEFAULT_API_URL = "https://api-v1.cortexdb.ai"
|
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger("cortexdb_connectors")
|
|
26
27
|
|
|
28
|
+
# v1 ActorRef grammar (server: cortex-types identity.rs): `kind:local` where
|
|
29
|
+
# kind ∈ _ACTOR_KINDS and local matches [A-Za-z0-9_.@-]+ (no colons!).
|
|
30
|
+
_ACTOR_KINDS = frozenset({"user", "agent", "service", "system"})
|
|
31
|
+
_ACTOR_LOCAL_ILLEGAL = re.compile(r"[^A-Za-z0-9_.@-]")
|
|
32
|
+
|
|
27
33
|
# Hard ceiling on content size sent to the server. Real items never approach
|
|
28
34
|
# this; it exists only to stop a pathological payload from exceeding the request
|
|
29
35
|
# body limit. When it trips we truncate on a UTF-8 boundary and log LOUDLY — we
|
|
@@ -158,6 +164,13 @@ class Episode:
|
|
|
158
164
|
class SyncResult:
|
|
159
165
|
episodes_fetched: int = 0
|
|
160
166
|
episodes_ingested: int = 0
|
|
167
|
+
# Items the connector's content policy deliberately dropped (`normalize`
|
|
168
|
+
# returned None: empty/attachment-only text, emoji-only, noise subtypes).
|
|
169
|
+
# Counted separately so operators can tell "filtered by design" from
|
|
170
|
+
# "failed"; skipped items ADVANCE the cursor like successes — a skip is
|
|
171
|
+
# a decision, not an error, and treating it as one would stall the sync
|
|
172
|
+
# at the first attachment-only message.
|
|
173
|
+
episodes_skipped: int = 0
|
|
161
174
|
errors: list[str] = field(default_factory=list)
|
|
162
175
|
duration_seconds: float = 0.0
|
|
163
176
|
# Watermarks for cursor-safe syncing: the latest source timestamp we
|
|
@@ -237,7 +250,13 @@ class CortexConnector(ABC):
|
|
|
237
250
|
...
|
|
238
251
|
|
|
239
252
|
@abstractmethod
|
|
240
|
-
def normalize(self, raw: RawEvent) -> Episode:
|
|
253
|
+
def normalize(self, raw: RawEvent) -> Episode | None:
|
|
254
|
+
"""Convert a RawEvent into an Episode, or return None to SKIP it.
|
|
255
|
+
|
|
256
|
+
None means the item is dropped by content policy (nothing sent to
|
|
257
|
+
the server); the sync loop counts it in `episodes_skipped` and the
|
|
258
|
+
cursor advances past it.
|
|
259
|
+
"""
|
|
241
260
|
...
|
|
242
261
|
|
|
243
262
|
@abstractmethod
|
|
@@ -257,6 +276,13 @@ class CortexConnector(ABC):
|
|
|
257
276
|
for raw in raw_events:
|
|
258
277
|
try:
|
|
259
278
|
episode = self.normalize(raw)
|
|
279
|
+
if episode is None:
|
|
280
|
+
# Content-policy skip: advance the cursor exactly like a
|
|
281
|
+
# success so the next incremental run never re-fetches it.
|
|
282
|
+
result.episodes_skipped += 1
|
|
283
|
+
if result.latest_success is None or raw.timestamp > result.latest_success:
|
|
284
|
+
result.latest_success = raw.timestamp
|
|
285
|
+
continue
|
|
260
286
|
episode.visibility = self.map_permissions(raw)
|
|
261
287
|
await self.ingest_episode(episode)
|
|
262
288
|
result.episodes_ingested += 1
|
|
@@ -356,14 +382,17 @@ class CortexConnector(ABC):
|
|
|
356
382
|
),
|
|
357
383
|
}
|
|
358
384
|
if episode.actor and episode.actor.id:
|
|
359
|
-
#
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
385
|
+
# Source actor ids are not guaranteed bare: Jira Cloud accountIds
|
|
386
|
+
# are `712020:<uuid>`, so a colon does NOT mean the connector
|
|
387
|
+
# already supplied a kind. Treat the prefix as a kind only when it
|
|
388
|
+
# is one the server accepts; otherwise the whole source id is the
|
|
389
|
+
# local part under `user:`, sanitized to the server's charset
|
|
390
|
+
# (which forbids ':' anywhere in the local part).
|
|
391
|
+
kind, sep, local = episode.actor.id.partition(":")
|
|
392
|
+
if not sep or kind not in _ACTOR_KINDS or not local:
|
|
393
|
+
kind, local = "user", episode.actor.id
|
|
394
|
+
local = _ACTOR_LOCAL_ILLEGAL.sub("_", local)
|
|
395
|
+
envelope["observed_actor"] = {"id": f"{kind}:{local}"}
|
|
367
396
|
|
|
368
397
|
return envelope
|
|
369
398
|
|
|
@@ -414,9 +414,10 @@ def _cmd_sync(args: argparse.Namespace) -> None:
|
|
|
414
414
|
state_store.update_last_synced(args.connector, args.scope, timestamp=new_cursor)
|
|
415
415
|
|
|
416
416
|
logger.info(
|
|
417
|
-
"sync done fetched=%d ingested=%d errors=%d duration=%.1fs",
|
|
417
|
+
"sync done fetched=%d ingested=%d skipped=%d errors=%d duration=%.1fs",
|
|
418
418
|
result.episodes_fetched,
|
|
419
419
|
result.episodes_ingested,
|
|
420
|
+
result.episodes_skipped,
|
|
420
421
|
len(result.errors),
|
|
421
422
|
result.duration_seconds,
|
|
422
423
|
)
|