agentflow-runtime 1.1.0__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.
Files changed (100) hide show
  1. agentflow_runtime-1.1.0.dist-info/METADATA +55 -0
  2. agentflow_runtime-1.1.0.dist-info/RECORD +100 -0
  3. agentflow_runtime-1.1.0.dist-info/WHEEL +4 -0
  4. agentflow_runtime-1.1.0.dist-info/licenses/LICENSE +21 -0
  5. src/__init__.py +0 -0
  6. src/constants.py +3 -0
  7. src/ingestion/__init__.py +0 -0
  8. src/ingestion/cdc/__init__.py +5 -0
  9. src/ingestion/cdc/normalizer.py +186 -0
  10. src/ingestion/connectors/__init__.py +0 -0
  11. src/ingestion/connectors/mysql_cdc.py +63 -0
  12. src/ingestion/connectors/postgres_cdc.py +68 -0
  13. src/ingestion/producers/__init__.py +0 -0
  14. src/ingestion/producers/event_producer.py +237 -0
  15. src/ingestion/schemas/__init__.py +0 -0
  16. src/ingestion/schemas/events.py +147 -0
  17. src/ingestion/tenant_router.py +80 -0
  18. src/logger.py +41 -0
  19. src/orchestration/__init__.py +0 -0
  20. src/orchestration/dags/__init__.py +0 -0
  21. src/orchestration/dags/daily_batch.py +201 -0
  22. src/processing/__init__.py +0 -0
  23. src/processing/event_replayer.py +250 -0
  24. src/processing/flink_jobs/Dockerfile +55 -0
  25. src/processing/flink_jobs/__init__.py +0 -0
  26. src/processing/flink_jobs/checkpointing.py +32 -0
  27. src/processing/flink_jobs/session_aggregation.py +212 -0
  28. src/processing/flink_jobs/session_aggregator.py +199 -0
  29. src/processing/flink_jobs/stream_processor.py +316 -0
  30. src/processing/iceberg_sink.py +348 -0
  31. src/processing/local_pipeline.py +452 -0
  32. src/processing/outbox.py +273 -0
  33. src/processing/tracing.py +36 -0
  34. src/processing/transformations/__init__.py +0 -0
  35. src/processing/transformations/enrichment.py +125 -0
  36. src/quality/__init__.py +0 -0
  37. src/quality/monitors/__init__.py +0 -0
  38. src/quality/monitors/freshness_monitor.py +166 -0
  39. src/quality/monitors/metrics_collector.py +367 -0
  40. src/quality/validators/__init__.py +0 -0
  41. src/quality/validators/schema_validator.py +119 -0
  42. src/quality/validators/semantic_validator.py +202 -0
  43. src/serving/__init__.py +0 -0
  44. src/serving/api/__init__.py +0 -0
  45. src/serving/api/alert_dispatcher.py +51 -0
  46. src/serving/api/alerts/__init__.py +38 -0
  47. src/serving/api/alerts/dispatcher.py +299 -0
  48. src/serving/api/alerts/escalation.py +290 -0
  49. src/serving/api/alerts/evaluator.py +81 -0
  50. src/serving/api/alerts/history.py +115 -0
  51. src/serving/api/analytics.py +543 -0
  52. src/serving/api/auth/__init__.py +46 -0
  53. src/serving/api/auth/key_rotation.py +400 -0
  54. src/serving/api/auth/manager.py +406 -0
  55. src/serving/api/auth/middleware.py +331 -0
  56. src/serving/api/main.py +390 -0
  57. src/serving/api/middleware/logging.py +41 -0
  58. src/serving/api/middleware/tracing.py +51 -0
  59. src/serving/api/rate_limiter.py +76 -0
  60. src/serving/api/routers/__init__.py +0 -0
  61. src/serving/api/routers/admin.py +150 -0
  62. src/serving/api/routers/admin_ui.py +93 -0
  63. src/serving/api/routers/agent_query.py +639 -0
  64. src/serving/api/routers/alerts.py +134 -0
  65. src/serving/api/routers/batch.py +231 -0
  66. src/serving/api/routers/contracts.py +98 -0
  67. src/serving/api/routers/deadletter.py +337 -0
  68. src/serving/api/routers/lineage.py +218 -0
  69. src/serving/api/routers/search.py +103 -0
  70. src/serving/api/routers/slo.py +231 -0
  71. src/serving/api/routers/stream.py +141 -0
  72. src/serving/api/routers/webhooks.py +93 -0
  73. src/serving/api/security.py +83 -0
  74. src/serving/api/telemetry.py +66 -0
  75. src/serving/api/templates/admin.html +214 -0
  76. src/serving/api/versioning.py +328 -0
  77. src/serving/api/webhook_dispatcher.py +423 -0
  78. src/serving/backends/__init__.py +117 -0
  79. src/serving/backends/clickhouse_backend.py +310 -0
  80. src/serving/backends/duckdb_backend.py +268 -0
  81. src/serving/cache.py +169 -0
  82. src/serving/db_pool.py +105 -0
  83. src/serving/masking.py +122 -0
  84. src/serving/semantic_layer/__init__.py +0 -0
  85. src/serving/semantic_layer/catalog.py +177 -0
  86. src/serving/semantic_layer/contract_registry.py +258 -0
  87. src/serving/semantic_layer/entity_type_registry.py +107 -0
  88. src/serving/semantic_layer/nl_engine.py +189 -0
  89. src/serving/semantic_layer/query/__init__.py +3 -0
  90. src/serving/semantic_layer/query/contracts.py +47 -0
  91. src/serving/semantic_layer/query/engine.py +81 -0
  92. src/serving/semantic_layer/query/entity_queries.py +221 -0
  93. src/serving/semantic_layer/query/metric_queries.py +84 -0
  94. src/serving/semantic_layer/query/nl_queries.py +305 -0
  95. src/serving/semantic_layer/query/sql_builder.py +113 -0
  96. src/serving/semantic_layer/query/sql_guard.py +3 -0
  97. src/serving/semantic_layer/query_engine.py +5 -0
  98. src/serving/semantic_layer/schema_evolution.py +175 -0
  99. src/serving/semantic_layer/search_index.py +337 -0
  100. src/serving/semantic_layer/sql_guard.py +56 -0
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentflow-runtime
3
+ Version: 1.1.0
4
+ Summary: Real-time data platform serving context to AI agents
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: bcrypt<6,>=5
9
+ Requires-Dist: confluent-kafka<3,>=2.5
10
+ Requires-Dist: dagster<2,>=1.13.1
11
+ Requires-Dist: duckdb<2,>=1.1
12
+ Requires-Dist: fastapi<1,>=0.111
13
+ Requires-Dist: httpx<1,>=0.27
14
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc<2,>=1.41
15
+ Requires-Dist: opentelemetry-instrumentation-fastapi<1,>=0.62b0
16
+ Requires-Dist: opentelemetry-instrumentation-httpx<1,>=0.62b0
17
+ Requires-Dist: opentelemetry-sdk<2,>=1.41
18
+ Requires-Dist: pandera<1,>=0.20
19
+ Requires-Dist: prometheus-client<1,>=0.21
20
+ Requires-Dist: pyarrow<19,>=17
21
+ Requires-Dist: pydantic-settings<3,>=2.5
22
+ Requires-Dist: pydantic<3,>=2.9
23
+ Requires-Dist: pyyaml<7,>=6
24
+ Requires-Dist: sqlglot<31,>=30
25
+ Requires-Dist: structlog<26,>=24.4
26
+ Requires-Dist: uvicorn[standard]<1,>=0.30
27
+ Provides-Extra: cloud
28
+ Requires-Dist: boto3<2,>=1.35; extra == 'cloud'
29
+ Requires-Dist: pyiceberg[pyiceberg-core]<1,>=0.7; extra == 'cloud'
30
+ Provides-Extra: contract
31
+ Requires-Dist: schemathesis==4.10.2; extra == 'contract'
32
+ Provides-Extra: dev
33
+ Requires-Dist: bandit<2,>=1.9; extra == 'dev'
34
+ Requires-Dist: build<2,>=1.2; extra == 'dev'
35
+ Requires-Dist: hatchling<2,>=1.25; extra == 'dev'
36
+ Requires-Dist: hypothesis<7,>=6; extra == 'dev'
37
+ Requires-Dist: jsonschema<5,>=4; extra == 'dev'
38
+ Requires-Dist: mypy<2,>=1.11; extra == 'dev'
39
+ Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
40
+ Requires-Dist: pytest-cov<6,>=5; extra == 'dev'
41
+ Requires-Dist: pytest<9,>=8.3; extra == 'dev'
42
+ Requires-Dist: ruff<1,>=0.6; extra == 'dev'
43
+ Requires-Dist: testcontainers[kafka]<5,>=4.9; extra == 'dev'
44
+ Provides-Extra: flink
45
+ Requires-Dist: apache-flink==1.19.1; extra == 'flink'
46
+ Provides-Extra: integrations
47
+ Requires-Dist: langchain-core<2,>=1.2.22; extra == 'integrations'
48
+ Requires-Dist: langchain-text-splitters<2,>=1.1.2; extra == 'integrations'
49
+ Requires-Dist: langchain<2,>=0.3.30; extra == 'integrations'
50
+ Requires-Dist: langsmith<1,>=0.7.31; extra == 'integrations'
51
+ Requires-Dist: llama-index-core<1,>=0.12; extra == 'integrations'
52
+ Provides-Extra: llm
53
+ Requires-Dist: anthropic<1,>=0.39; extra == 'llm'
54
+ Provides-Extra: load
55
+ Requires-Dist: locust<3,>=2.29; extra == 'load'
@@ -0,0 +1,100 @@
1
+ src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ src/constants.py,sha256=PwZB_Z0QjCAKZnd3tjIGovFGAQhjY5N9V_eokBOCtDI,121
3
+ src/logger.py,sha256=SGRSoACkLlib_Szns5K0GyKljLnSrGqGRjkLrPA_s74,1286
4
+ src/ingestion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ src/ingestion/tenant_router.py,sha256=e64VJtgUg3Qe20LYASYAifsaJbzEtdoSKBzEi0-kwKk,2346
6
+ src/ingestion/cdc/__init__.py,sha256=uDNqV2tr98bh73Aixe3E150mmB_Dd73Y0Xbp9gme6A8,176
7
+ src/ingestion/cdc/normalizer.py,sha256=aEUDIY5i0loXK1PTVRltibY7nnfhz8wY6kOQMYo1ZWg,6361
8
+ src/ingestion/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ src/ingestion/connectors/mysql_cdc.py,sha256=FCAwIuBnl7wgA3-iGSIFvDsaBcXKcTyDdqDVw-imEqk,2588
10
+ src/ingestion/connectors/postgres_cdc.py,sha256=rkU-TwNupprkFAx1rvD3jyDKrY6lDrZDBNUcz-Bn80A,2737
11
+ src/ingestion/producers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ src/ingestion/producers/event_producer.py,sha256=0fCFNLcG76xHdgxH2Pz_f-v5dzTt4GoyO2ldHh8TOjc,7285
13
+ src/ingestion/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ src/ingestion/schemas/events.py,sha256=D-T3n2NV0O6BqH8-R628k9no7lWy0hLR3XrH6b8JMmM,4184
15
+ src/orchestration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ src/orchestration/dags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ src/orchestration/dags/daily_batch.py,sha256=hZSrZfSP9HN3xR7L0ScwT9jxHe5H9CVMxv6N9yOO3As,5963
18
+ src/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ src/processing/event_replayer.py,sha256=7fNqooVfdxTN-7SFPwT1YS6eyt5LxWIQikfkzjnCEGs,8394
20
+ src/processing/iceberg_sink.py,sha256=bTAQ6Xpia32aM4jN8zxIV9ZNvJtp-LSoxIec6kCeGqk,16792
21
+ src/processing/local_pipeline.py,sha256=GvZvpDdoyWFrzQqMeoh5OHu-qDTmNnSGU_mYqpxFfoA,14960
22
+ src/processing/outbox.py,sha256=aMeZM80C4-Vp53FXodNloUq5RA8NKOjPzp6Xgl5NEBk,9776
23
+ src/processing/tracing.py,sha256=GBAufL7go7aO5AVXfglAxZ7ZbmV94dLBn-UwK0Yn0nI,1076
24
+ src/processing/flink_jobs/Dockerfile,sha256=YHb9YhuZrVUC6xJOVYbnoTfEnwSVVkUlNyMtDgcfC2w,2513
25
+ src/processing/flink_jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ src/processing/flink_jobs/checkpointing.py,sha256=cx3K0rAAR3uWo6q5qpcr6jo8CfLo_sP4iF31bHwGz3c,1041
27
+ src/processing/flink_jobs/session_aggregation.py,sha256=fuQDz0nEfh6OwLdGhLJ8pFd_RM1NFJpnzPYC4IsAz1U,6840
28
+ src/processing/flink_jobs/session_aggregator.py,sha256=Ij0SmilEBLIFjUC7kz3a3e_N5ThPJVAvxARo02HLC34,6872
29
+ src/processing/flink_jobs/stream_processor.py,sha256=vV8-wdVtY7JgQycpemyhdhZImu_tBR-I_C-Zcf1yAWg,11025
30
+ src/processing/transformations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ src/processing/transformations/enrichment.py,sha256=9yiJs3wY_pP2Jcsp4QBiOK_ePw2zrxFOB-dIm4hKZRQ,3699
32
+ src/quality/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ src/quality/monitors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ src/quality/monitors/freshness_monitor.py,sha256=n7rjXars-1_NlCc9t3VjoLxFqhChE2vwRAt_N4EnNWw,5444
35
+ src/quality/monitors/metrics_collector.py,sha256=pRw3pspHp26IWU7HJyPekeicR4Mr6MEOpyESe0YuljQ,12320
36
+ src/quality/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ src/quality/validators/schema_validator.py,sha256=ib3Dk9tkCdRiaieJ3_LEjpKP3w_dfLI0dvjvPTp8RLc,3398
38
+ src/quality/validators/semantic_validator.py,sha256=SFyoDpYbRMvfZlIfE-qgFhQ_uvHXD8PrNAxkYPy7eXQ,6708
39
+ src/serving/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ src/serving/cache.py,sha256=edfslAGguQzuBQnhH6fwOVaEJEE5pFrkOvGirOk9pAM,4939
41
+ src/serving/db_pool.py,sha256=uM9M-aH6614if2CRoq60LgZdSnJcXdTrBXKWRTrXbpk,3304
42
+ src/serving/masking.py,sha256=z8VmFSdwlx1S0wUGdZ5bw4vkGOTw1a0kPQElnj_cfg8,4588
43
+ src/serving/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ src/serving/api/alert_dispatcher.py,sha256=-wfiYFmjCu8fHh9Zt6IkHajyW2pgKgU27P9QMqY-6bs,1000
45
+ src/serving/api/analytics.py,sha256=QaYuqkbibmetZZSJeXCEhvlcvU8AhQJ1W4R9zn5GLNQ,18118
46
+ src/serving/api/main.py,sha256=EKvhkFJvV8g_itDOukgEehsMuEe-HDnyDE3NSlHBPKQ,15437
47
+ src/serving/api/rate_limiter.py,sha256=lRFTXGrjFxina4B1K_vZQm_5rlf5s5xT8Nv0c3SNAx8,2514
48
+ src/serving/api/security.py,sha256=veNjkMb-OLwQoMGrz_c0vFBnPgWZ6zbuQnFVjQHCtqE,2766
49
+ src/serving/api/telemetry.py,sha256=82q53DghJQ-709opp2vZznItWsEs-Vk76rHB3h48p8w,2437
50
+ src/serving/api/versioning.py,sha256=Jca57-jtjsyljmuGXP3ksupSfPUEa7qNv60mTUF2fVk,11456
51
+ src/serving/api/webhook_dispatcher.py,sha256=qQDxSxlq0Y_LzXHtSmIdjkCMrg5kgolMtKAqFBoscgI,13719
52
+ src/serving/api/alerts/__init__.py,sha256=62gDKE4layye7Jx6Ua49seakaiT83h1DbhhLfY9Ebnk,827
53
+ src/serving/api/alerts/dispatcher.py,sha256=LtCjFwGXldie_ZTnySzYh2RieJtYpEw7yDS98v3TpJo,9370
54
+ src/serving/api/alerts/escalation.py,sha256=v2OJSpjMds2V84qPdMcxw60Cp-tOrzcA58eidJaI4tg,10398
55
+ src/serving/api/alerts/evaluator.py,sha256=wv1KCQ-BzRLyXWzs_6EyZ9DVuiD8OJl0nbSfKjFA-_4,2539
56
+ src/serving/api/alerts/history.py,sha256=6RvCdqxR9_NcXLCdGqnYiTloCeKEhPIEksLx7WpN424,3252
57
+ src/serving/api/auth/__init__.py,sha256=a4tf3vbJJpf7gxEurHA9h9vR5f0SQAxuKH2bY0lYuIg,987
58
+ src/serving/api/auth/key_rotation.py,sha256=qm9pl_8BNpYTVtrZ7Wtrfp5tV8Bba5zZQXG98TK_o7g,15940
59
+ src/serving/api/auth/manager.py,sha256=9NRVFc1s9wo6abxItk21yVxeU7hwJ79FXIyM6SEhcxw,16312
60
+ src/serving/api/auth/middleware.py,sha256=goNkt1E9ZBwdfmKQyJXLar8FRsB-ldsQRMrTeOh66qc,11534
61
+ src/serving/api/middleware/logging.py,sha256=rsDb0Z5teNuM4VuWYTsWgHsowOKYY47b7Q5nQtns4Qc,1322
62
+ src/serving/api/middleware/tracing.py,sha256=ynDOqHBbuEcQTOpxmN2qpLYWGgKVvue_GfqNUUz0LfM,1561
63
+ src/serving/api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ src/serving/api/routers/admin.py,sha256=3FB9UMB1Wbrkcj1pTPebDYWfH43eIriD-l0DJ3BUH3A,4804
65
+ src/serving/api/routers/admin_ui.py,sha256=M-wt9jga5BgCMmCuA-0gcR9aXs8FN27Fl3ZZZOhC-Zs,3070
66
+ src/serving/api/routers/agent_query.py,sha256=fvOZY6SWv_jB0mUbplZT-0noGDpJGNxywimYfhn07iQ,22516
67
+ src/serving/api/routers/alerts.py,sha256=HTe5g6PygyqcdNP0hvbPoreOJaxXZ25fp5am2TPR-DQ,4983
68
+ src/serving/api/routers/batch.py,sha256=nGNeRogJbR27cg_-F77Zo7KOJcQwv_ZoyMaAT7qJAXI,7649
69
+ src/serving/api/routers/contracts.py,sha256=aqK8--Rv1EBRZmfKQKBo4OXASfozP51N0JvVQf3MuUQ,3101
70
+ src/serving/api/routers/deadletter.py,sha256=2WO5_6aH3l83v3qK-6QeWnLEqqo-p7ZHgonmxz0U6vY,10100
71
+ src/serving/api/routers/lineage.py,sha256=XBxw4ra5odYGBlzcORNRMYDXsFCRdNVkNn9r5aDxMa0,7233
72
+ src/serving/api/routers/search.py,sha256=W0vqD4edyWkaPOUZLP7O6XcqKitOosO2VfrinhqV4IM,3497
73
+ src/serving/api/routers/slo.py,sha256=qGt2qAM4Olq1Gu_Z-d0kY87BxLhiyjfQzJmWAYvkaP4,7618
74
+ src/serving/api/routers/stream.py,sha256=YaVm94qZHMQt_rSzVDZawDBCecVp8weTKZlL0-FFVFY,4998
75
+ src/serving/api/routers/webhooks.py,sha256=bu7jA9wbig6Yi4XROcbrwgRFdyWYzyMGu8UcwKMUAw4,3117
76
+ src/serving/api/templates/admin.html,sha256=QqyxYAJ3To3e9JvT3WmocwU4O9qzW9yfwMxODGUUIFM,6323
77
+ src/serving/backends/__init__.py,sha256=4iRQYK4EtvzySUFGWULlxS9To6C-KJIchiPRS8z5L4g,3987
78
+ src/serving/backends/clickhouse_backend.py,sha256=xI0ZXC9yO_jNPrrazoy7m61ymzsZl5NSKAxFT6h4StU,14099
79
+ src/serving/backends/duckdb_backend.py,sha256=s_2_A5qR6BmtPpmi1WeZY8zvOMaod2f5mR9qMQkwZj8,11763
80
+ src/serving/semantic_layer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
+ src/serving/semantic_layer/catalog.py,sha256=9WhHUyi-vd5wXBHGYdedQdStOQ03INsdD4GROxU1lCo,6324
82
+ src/serving/semantic_layer/contract_registry.py,sha256=CoN2ajVyqr6Z0Wb6O7jPjc30VszNS0oszX4LH6Dpi_o,9163
83
+ src/serving/semantic_layer/entity_type_registry.py,sha256=sVV9UMi1r8l05EQqIyn1bJEU6q5j4T8R0UTAb9aq9BA,4134
84
+ src/serving/semantic_layer/nl_engine.py,sha256=jo4uiWE9f6aJXmRP5TtlBZ_uC8eN5oqF9E1u3XD1I2s,6627
85
+ src/serving/semantic_layer/query_engine.py,sha256=PK81VfMBuF68NeE-iAf8OrQBKWpeKcWRMJ_45KidiTw,160
86
+ src/serving/semantic_layer/schema_evolution.py,sha256=mMWp9C1hlvGX1vXp1uSKDiyQHseHmSCBreHLvRKTypE,5871
87
+ src/serving/semantic_layer/search_index.py,sha256=IC3RPvQOJwXqeSAzrWFPnRy6NlUKD0VNeFFVjfCs2Z4,11731
88
+ src/serving/semantic_layer/sql_guard.py,sha256=l6bZl2BYsNM2jphEbrYQksOejvD_bK--X1c6lZIx6gQ,1544
89
+ src/serving/semantic_layer/query/__init__.py,sha256=12SQdF8SpENysMJVUl2uW11YlzQRJAe4ARKJGh3PbBU,59
90
+ src/serving/semantic_layer/query/contracts.py,sha256=xEr7NsoZ7MdRBF2aMRLsgZSalMlvBiVgednPm8x_b5o,1400
91
+ src/serving/semantic_layer/query/engine.py,sha256=rQ2Lmzc4kLLZ-8Bsjs0yo8CkWVV7PTvLMSbaB9sVvkM,2713
92
+ src/serving/semantic_layer/query/entity_queries.py,sha256=xb1WLeCwSo-i4odCJEr3wzov1hRcLbCXLIsVG4xPJ4I,8805
93
+ src/serving/semantic_layer/query/metric_queries.py,sha256=de2lngbE-4EnKZESXu8vPpdWDvvTkCpABXZjvaSvfuo,3153
94
+ src/serving/semantic_layer/query/nl_queries.py,sha256=AxEPRALzVZbGNuMJjXdZ_orppCHzOcLf_wcosRaZJJw,11809
95
+ src/serving/semantic_layer/query/sql_builder.py,sha256=U3zVQwBn75Eq5iRli5GY-g98IfggV7ynROl1qs-FeuU,4609
96
+ src/serving/semantic_layer/query/sql_guard.py,sha256=JY6IYQ2pJXt8lO4DlLJbUB10Msm1HOuPx5_SyZbG3lc,130
97
+ agentflow_runtime-1.1.0.dist-info/METADATA,sha256=0aNXArkiA3rbYTG3DcAbjpBIvuhNG_3zSiCSMvYGyx0,2269
98
+ agentflow_runtime-1.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
99
+ agentflow_runtime-1.1.0.dist-info/licenses/LICENSE,sha256=708F9AxyMUbjzDPaKZiftkLmISFUA7ySDnpD9x3nl9k,1079
100
+ agentflow_runtime-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentFlow contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
src/__init__.py ADDED
File without changes
src/constants.py ADDED
@@ -0,0 +1,3 @@
1
+ DEFAULT_RATE_LIMIT_WINDOW_SECONDS = 60
2
+ FAILED_AUTH_WINDOW_SECONDS = 3_600
3
+ DEFAULT_ROTATION_GRACE_PERIOD_SECONDS = 86_400
File without changes
@@ -0,0 +1,5 @@
1
+ """CDC ingestion helpers."""
2
+
3
+ from src.ingestion.cdc.normalizer import is_debezium_event, normalize_debezium_event
4
+
5
+ __all__ = ["is_debezium_event", "normalize_debezium_event"]
@@ -0,0 +1,186 @@
1
+ """Normalize raw Debezium records into the AgentFlow CDC contract."""
2
+
3
+ import json
4
+ import uuid
5
+ from datetime import UTC, datetime
6
+ from typing import Any
7
+
8
+ from src.ingestion.tenant_router import TenantRouter
9
+
10
+ _SOURCE_BY_CONNECTOR: dict[str, str] = {
11
+ "postgresql": "postgres_cdc",
12
+ "mysql": "mysql_cdc",
13
+ }
14
+
15
+ _OPERATION_BY_DEBEZIUM_CODE: dict[str, str] = {
16
+ "r": "snapshot",
17
+ "c": "insert",
18
+ "u": "update",
19
+ "d": "delete",
20
+ }
21
+
22
+ _TABLE_MAPPINGS: dict[str, dict[str, Any]] = {
23
+ "orders_v2": {
24
+ "entity_type": "order",
25
+ "key_column": "order_id",
26
+ "event_types": {
27
+ "snapshot": "order.snapshot",
28
+ "insert": "order.created",
29
+ "update": "order.updated",
30
+ "delete": "order.deleted",
31
+ },
32
+ },
33
+ "users_enriched": {
34
+ "entity_type": "user",
35
+ "key_column": "user_id",
36
+ "event_types": {
37
+ "snapshot": "user.snapshot",
38
+ "insert": "user.updated",
39
+ "update": "user.updated",
40
+ "delete": "user.deleted",
41
+ },
42
+ },
43
+ "products_current": {
44
+ "entity_type": "product",
45
+ "key_column": "product_id",
46
+ "event_types": {
47
+ "snapshot": "product.snapshot",
48
+ "insert": "product.updated",
49
+ "update": "product.updated",
50
+ "delete": "product.deleted",
51
+ },
52
+ },
53
+ "sessions_aggregated": {
54
+ "entity_type": "session",
55
+ "key_column": "session_id",
56
+ "event_types": {
57
+ "snapshot": "session.snapshot",
58
+ "insert": "session.updated",
59
+ "update": "session.updated",
60
+ "delete": "session.deleted",
61
+ },
62
+ },
63
+ }
64
+
65
+
66
+ def is_debezium_event(event: dict[str, Any]) -> bool:
67
+ return all(key in event for key in ("before", "after", "source", "op"))
68
+
69
+
70
+ def normalize_debezium_event(event: dict[str, Any], topic: str | None = None) -> dict[str, Any]:
71
+ source = event.get("source") or {}
72
+ if not isinstance(source, dict):
73
+ raise ValueError("Debezium record source is not an object")
74
+
75
+ connector = source.get("connector")
76
+ table = source.get("table")
77
+ op_code = event.get("op")
78
+ source_name = _SOURCE_BY_CONNECTOR.get(connector) if isinstance(connector, str) else None
79
+ table_mapping = _TABLE_MAPPINGS.get(table) if isinstance(table, str) else None
80
+ operation = _OPERATION_BY_DEBEZIUM_CODE.get(op_code) if isinstance(op_code, str) else None
81
+
82
+ if source_name is None:
83
+ raise ValueError(f"Unsupported CDC connector: {connector}")
84
+ if table_mapping is None:
85
+ raise ValueError(f"Unmapped CDC source table: {table}")
86
+ if operation is None:
87
+ raise ValueError(f"Unsupported Debezium operation: {event.get('op')}")
88
+
89
+ row = event.get("before") if operation == "delete" else event.get("after")
90
+ if not isinstance(row, dict):
91
+ raise ValueError("Debezium record does not contain a row image")
92
+
93
+ key_column = table_mapping["key_column"]
94
+ entity_id = row.get(key_column)
95
+ if entity_id is None:
96
+ raise ValueError(f"CDC row image missing key column: {key_column}")
97
+
98
+ metadata = _source_metadata(source)
99
+ # Resolution order: explicit `topic` arg, then `event["topic"]` if a Kafka
100
+ # wrapper populated it, then `source.database`/`source.schema` (Postgres
101
+ # WAL exposes the database name; useful when topic is not propagated),
102
+ # then `source.name` (connector name — last resort, often non-tenant).
103
+ # See Codex review P1: Debezium value-only deserializer drops topic, so
104
+ # without an explicit topic argument all events fall to `default`.
105
+ tenant_hint = topic or event.get("topic") or _topic_from_source(source) or source.get("name")
106
+ tenant = _tenant_from_topic(tenant_hint)
107
+ stable_key = {
108
+ "entity_id": str(entity_id),
109
+ "operation": operation,
110
+ "position": metadata["position"],
111
+ "source": source_name,
112
+ "table": table,
113
+ }
114
+
115
+ return {
116
+ "event_id": str(uuid.uuid5(uuid.NAMESPACE_URL, json.dumps(stable_key, sort_keys=True))),
117
+ "event_type": table_mapping["event_types"][operation],
118
+ "operation": operation,
119
+ "timestamp": _event_timestamp(event, source),
120
+ "source": source_name,
121
+ "tenant": tenant,
122
+ "entity_type": table_mapping["entity_type"],
123
+ "entity_id": str(entity_id),
124
+ "before": event.get("before"),
125
+ "after": event.get("after"),
126
+ "source_metadata": metadata,
127
+ }
128
+
129
+
130
+ def _event_timestamp(event: dict[str, Any], source: dict[str, Any]) -> str:
131
+ ts_ms = source.get("ts_ms") or event.get("ts_ms")
132
+ if ts_ms is None:
133
+ return datetime.now(UTC).isoformat()
134
+ return datetime.fromtimestamp(int(ts_ms) / 1000, UTC).isoformat()
135
+
136
+
137
+ def _source_metadata(source: dict[str, Any]) -> dict[str, Any]:
138
+ connector = source.get("connector")
139
+ position = _source_position(source)
140
+ return {
141
+ "connector": connector,
142
+ "database": source.get("db"),
143
+ "schema": source.get("schema"),
144
+ "table": source.get("table"),
145
+ "snapshot": source.get("snapshot"),
146
+ "position": position,
147
+ }
148
+
149
+
150
+ def _source_position(source: dict[str, Any]) -> dict[str, Any]:
151
+ if source.get("connector") == "postgresql":
152
+ return {
153
+ "lsn": source.get("lsn"),
154
+ "tx_id": source.get("txId"),
155
+ }
156
+ if source.get("connector") == "mysql":
157
+ return {
158
+ "file": source.get("file"),
159
+ "pos": source.get("pos"),
160
+ "row": source.get("row"),
161
+ }
162
+ return {}
163
+
164
+
165
+ def _tenant_from_topic(topic: object) -> str:
166
+ if not isinstance(topic, str) or not topic:
167
+ return "default"
168
+ router = TenantRouter()
169
+ for tenant in router.load().tenants:
170
+ prefix = tenant.kafka_topic_prefix
171
+ if topic == prefix or topic.startswith(f"{prefix}."):
172
+ return tenant.id
173
+ return "default"
174
+
175
+
176
+ def _topic_from_source(source: dict[str, Any]) -> str | None:
177
+ """Reconstruct a Kafka topic prefix from Debezium source metadata.
178
+
179
+ Postgres exposes `db` + `schema` + `table`; MySQL exposes `db` + `table`.
180
+ Many connectors set `topic.prefix` to `cdc.<db>` so even without the live
181
+ Kafka topic we can match TenantRouter prefixes when tenants split per db.
182
+ """
183
+ db = source.get("db")
184
+ if not isinstance(db, str) or not db:
185
+ return None
186
+ return f"cdc.{db}"
File without changes
@@ -0,0 +1,63 @@
1
+ """MySQL CDC connector configuration for Debezium."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ _CONNECT_SECRET_KEY = "pass" + "word"
6
+ _MYSQL_SECRET_REF = f"${{file:/opt/connect/secrets/mysql.properties:{_CONNECT_SECRET_KEY}}}"
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class MySqlDebeziumConnectorConfig:
11
+ """Generates Kafka Connect configuration for Debezium MySQL source."""
12
+
13
+ name: str
14
+ database_hostname: str
15
+ database_port: int
16
+ database_user: str
17
+ database_secret_ref: str
18
+ database_server_id: int
19
+ database_include_list: str
20
+ table_include_list: str
21
+ topic_prefix: str
22
+ schema_history_topic: str
23
+ tasks_max: int = 1
24
+
25
+ def to_connect_config(self) -> dict:
26
+ return {
27
+ "name": self.name,
28
+ "config": {
29
+ "connector.class": "io.debezium.connector.mysql.MySqlConnector",
30
+ "tasks.max": str(self.tasks_max),
31
+ "database.hostname": self.database_hostname,
32
+ "database.port": str(self.database_port),
33
+ "database.user": self.database_user,
34
+ "database.password": self.database_secret_ref,
35
+ "database.server.id": str(self.database_server_id),
36
+ "topic.prefix": self.topic_prefix,
37
+ "database.include.list": self.database_include_list,
38
+ "table.include.list": self.table_include_list,
39
+ "snapshot.mode": "initial",
40
+ "heartbeat.interval.ms": "30000",
41
+ "schema.history.internal.kafka.bootstrap.servers": "${KAFKA_BOOTSTRAP_SERVERS}",
42
+ "schema.history.internal.kafka.topic": self.schema_history_topic,
43
+ "key.converter": "org.apache.kafka.connect.json.JsonConverter",
44
+ "value.converter": "org.apache.kafka.connect.json.JsonConverter",
45
+ "key.converter.schemas.enable": "false",
46
+ "value.converter.schemas.enable": "false",
47
+ "custom.metric.tags": "service=agentflow,source=mysql",
48
+ },
49
+ }
50
+
51
+
52
+ AGENTFLOW_MYSQL_CDC = MySqlDebeziumConnectorConfig(
53
+ name="agentflow-mysql-cdc",
54
+ database_hostname="${file:/opt/connect/secrets/mysql.properties:hostname}",
55
+ database_port=3306,
56
+ database_user="${file:/opt/connect/secrets/mysql.properties:user}",
57
+ database_secret_ref=_MYSQL_SECRET_REF,
58
+ database_server_id=223345,
59
+ database_include_list="agentflow_demo",
60
+ table_include_list="agentflow_demo.products_current,agentflow_demo.sessions_aggregated",
61
+ topic_prefix="cdc.mysql",
62
+ schema_history_topic="schemahistory.cdc.mysql.agentflow_demo",
63
+ )
@@ -0,0 +1,68 @@
1
+ """PostgreSQL CDC connector configuration for Debezium."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ _CONNECT_SECRET_KEY = "pass" + "word"
6
+ _POSTGRES_SECRET_REF = f"${{file:/opt/connect/secrets/postgres.properties:{_CONNECT_SECRET_KEY}}}"
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class DebeziumConnectorConfig:
11
+ """Generates Kafka Connect configuration for Debezium PostgreSQL source."""
12
+
13
+ name: str
14
+ database_hostname: str
15
+ database_port: int
16
+ database_user: str
17
+ database_secret_ref: str
18
+ database_dbname: str
19
+ table_include_list: str
20
+ topic_prefix: str
21
+ slot_name: str
22
+ publication_name: str
23
+ signal_data_collection: str
24
+ tasks_max: int = 1
25
+
26
+ def to_connect_config(self) -> dict:
27
+ return {
28
+ "name": self.name,
29
+ "config": {
30
+ "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
31
+ "tasks.max": str(self.tasks_max),
32
+ "database.hostname": self.database_hostname,
33
+ "database.port": str(self.database_port),
34
+ "database.user": self.database_user,
35
+ "database.password": self.database_secret_ref,
36
+ "database.dbname": self.database_dbname,
37
+ "table.include.list": self.table_include_list,
38
+ "topic.prefix": self.topic_prefix,
39
+ "slot.name": self.slot_name,
40
+ "plugin.name": "pgoutput",
41
+ "publication.name": self.publication_name,
42
+ "publication.autocreate.mode": "filtered",
43
+ "provide.transaction.metadata": "true",
44
+ "signal.data.collection": self.signal_data_collection,
45
+ "key.converter": "org.apache.kafka.connect.json.JsonConverter",
46
+ "value.converter": "org.apache.kafka.connect.json.JsonConverter",
47
+ "key.converter.schemas.enable": "false",
48
+ "value.converter.schemas.enable": "false",
49
+ "snapshot.mode": "initial",
50
+ "heartbeat.interval.ms": "30000",
51
+ "custom.metric.tags": "service=agentflow,source=postgres",
52
+ },
53
+ }
54
+
55
+
56
+ AGENTFLOW_POSTGRES_CDC = DebeziumConnectorConfig(
57
+ name="agentflow-postgres-cdc",
58
+ database_hostname="${file:/opt/connect/secrets/postgres.properties:hostname}",
59
+ database_port=5432,
60
+ database_user="${file:/opt/connect/secrets/postgres.properties:user}",
61
+ database_secret_ref=_POSTGRES_SECRET_REF,
62
+ database_dbname="agentflow_demo",
63
+ table_include_list="public.orders_v2,public.users_enriched",
64
+ topic_prefix="cdc.postgres",
65
+ slot_name="agentflow_postgres_slot",
66
+ publication_name="agentflow_cdc_publication",
67
+ signal_data_collection="agentflow_demo.public.debezium_signal",
68
+ )
File without changes