cortexdb-connectors 0.2.5__tar.gz → 0.2.7__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.
Files changed (46) hide show
  1. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/PKG-INFO +1 -1
  2. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/__init__.py +40 -40
  3. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/base.py +39 -10
  4. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/cli.py +584 -583
  5. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/confluence/__init__.py +359 -359
  6. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/discord/__init__.py +646 -646
  7. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/gitlab/__init__.py +749 -749
  8. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/hubspot/__init__.py +489 -489
  9. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/insights/detectors.py +56 -28
  10. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/intercom/__init__.py +631 -631
  11. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/linear/__init__.py +705 -705
  12. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/pagerduty/__init__.py +298 -298
  13. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/salesforce/__init__.py +399 -399
  14. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/servicenow/__init__.py +460 -460
  15. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/slack/__init__.py +103 -11
  16. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/state.py +145 -145
  17. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/teams/__init__.py +656 -656
  18. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/webhooks.py +940 -933
  19. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/worker.py +2 -2
  20. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/zendesk/__init__.py +496 -496
  21. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/PKG-INFO +1 -1
  22. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/pyproject.toml +87 -87
  23. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_base.py +367 -257
  24. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_slack.py +294 -197
  25. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/README.md +0 -0
  26. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/freshdesk/__init__.py +0 -0
  27. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/github/__init__.py +0 -0
  28. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/google_workspace/__init__.py +0 -0
  29. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/insights/__init__.py +0 -0
  30. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/insights/api.py +0 -0
  31. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/insights/engine.py +0 -0
  32. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/jira/__init__.py +0 -0
  33. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/notion/__init__.py +0 -0
  34. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors/tldv/__init__.py +0 -0
  35. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/SOURCES.txt +0 -0
  36. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/dependency_links.txt +0 -0
  37. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/entry_points.txt +0 -0
  38. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/requires.txt +0 -0
  39. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/cortexdb_connectors.egg-info/top_level.txt +0 -0
  40. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/setup.cfg +0 -0
  41. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_connector_coverage.py +0 -0
  42. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_github.py +0 -0
  43. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_ingest_e2e.py +0 -0
  44. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_streaming_cursor.py +0 -0
  45. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/tests/test_webhooks.py +0 -0
  46. {cortexdb_connectors-0.2.5 → cortexdb_connectors-0.2.7}/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.5
3
+ Version: 0.2.7
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.1"
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.7"
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
+ ]
@@ -158,6 +158,13 @@ class Episode:
158
158
  class SyncResult:
159
159
  episodes_fetched: int = 0
160
160
  episodes_ingested: int = 0
161
+ # Items the connector's content policy deliberately dropped (`normalize`
162
+ # returned None: empty/attachment-only text, emoji-only, noise subtypes).
163
+ # Counted separately so operators can tell "filtered by design" from
164
+ # "failed"; skipped items ADVANCE the cursor like successes — a skip is
165
+ # a decision, not an error, and treating it as one would stall the sync
166
+ # at the first attachment-only message.
167
+ episodes_skipped: int = 0
161
168
  errors: list[str] = field(default_factory=list)
162
169
  duration_seconds: float = 0.0
163
170
  # Watermarks for cursor-safe syncing: the latest source timestamp we
@@ -237,7 +244,13 @@ class CortexConnector(ABC):
237
244
  ...
238
245
 
239
246
  @abstractmethod
240
- def normalize(self, raw: RawEvent) -> Episode:
247
+ def normalize(self, raw: RawEvent) -> Episode | None:
248
+ """Convert a RawEvent into an Episode, or return None to SKIP it.
249
+
250
+ None means the item is dropped by content policy (nothing sent to
251
+ the server); the sync loop counts it in `episodes_skipped` and the
252
+ cursor advances past it.
253
+ """
241
254
  ...
242
255
 
243
256
  @abstractmethod
@@ -257,6 +270,13 @@ class CortexConnector(ABC):
257
270
  for raw in raw_events:
258
271
  try:
259
272
  episode = self.normalize(raw)
273
+ if episode is None:
274
+ # Content-policy skip: advance the cursor exactly like a
275
+ # success so the next incremental run never re-fetches it.
276
+ result.episodes_skipped += 1
277
+ if result.latest_success is None or raw.timestamp > result.latest_success:
278
+ result.latest_success = raw.timestamp
279
+ continue
260
280
  episode.visibility = self.map_permissions(raw)
261
281
  await self.ingest_episode(episode)
262
282
  result.episodes_ingested += 1
@@ -322,19 +342,28 @@ class CortexConnector(ABC):
322
342
  "role": "user" if modality == "conversation" else "system",
323
343
  "text": self._guard_content(episode.content, episode),
324
344
  }
345
+ # The v1 write envelope is STRICT (deny_unknown_fields) as of v0.8.0:
346
+ # thread_id / meta / preceded_by are NOT envelope context fields, and
347
+ # sending them returns 422 INVALID_BODY (which stalls the sync cursor
348
+ # forever). Everything routable rides `context.labels` as key=value
349
+ # strings — the queryable partition recall metadata filters target.
350
+ # `context.preceded_by` is a list of CortexDB event ids (evt_…), not
351
+ # source-side parent ids, so the connector's parent_id goes to a
352
+ # label instead.
353
+ labels = list(labels)
354
+ if episode.thread_id:
355
+ labels.append(f"thread={episode.thread_id}")
356
+ if episode.parent_id:
357
+ labels.append(f"parent={episode.parent_id}")
358
+ if episode.metadata:
359
+ for k, v in episode.metadata.items():
360
+ if k == "modality":
361
+ continue
362
+ labels.append(f"{k}={v}")
325
363
  context: dict[str, Any] = {
326
364
  "observed_at": episode.occurred_at.isoformat(),
327
365
  "labels": labels,
328
366
  }
329
- if episode.thread_id:
330
- context["thread_id"] = episode.thread_id
331
- if episode.parent_id:
332
- context["preceded_by"] = [episode.parent_id]
333
- if episode.metadata:
334
- # Forward non-reserved metadata under a single key — the v1
335
- # surface doesn't reject unknown context keys but we tuck
336
- # everything under `meta` to keep the envelope tidy.
337
- context["meta"] = {k: v for k, v in episode.metadata.items() if k != "modality"}
338
367
 
339
368
  envelope: dict[str, Any] = {
340
369
  "scope": scope,