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.
- agentflow_runtime-1.1.0.dist-info/METADATA +55 -0
- agentflow_runtime-1.1.0.dist-info/RECORD +100 -0
- agentflow_runtime-1.1.0.dist-info/WHEEL +4 -0
- agentflow_runtime-1.1.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +0 -0
- src/constants.py +3 -0
- src/ingestion/__init__.py +0 -0
- src/ingestion/cdc/__init__.py +5 -0
- src/ingestion/cdc/normalizer.py +186 -0
- src/ingestion/connectors/__init__.py +0 -0
- src/ingestion/connectors/mysql_cdc.py +63 -0
- src/ingestion/connectors/postgres_cdc.py +68 -0
- src/ingestion/producers/__init__.py +0 -0
- src/ingestion/producers/event_producer.py +237 -0
- src/ingestion/schemas/__init__.py +0 -0
- src/ingestion/schemas/events.py +147 -0
- src/ingestion/tenant_router.py +80 -0
- src/logger.py +41 -0
- src/orchestration/__init__.py +0 -0
- src/orchestration/dags/__init__.py +0 -0
- src/orchestration/dags/daily_batch.py +201 -0
- src/processing/__init__.py +0 -0
- src/processing/event_replayer.py +250 -0
- src/processing/flink_jobs/Dockerfile +55 -0
- src/processing/flink_jobs/__init__.py +0 -0
- src/processing/flink_jobs/checkpointing.py +32 -0
- src/processing/flink_jobs/session_aggregation.py +212 -0
- src/processing/flink_jobs/session_aggregator.py +199 -0
- src/processing/flink_jobs/stream_processor.py +316 -0
- src/processing/iceberg_sink.py +348 -0
- src/processing/local_pipeline.py +452 -0
- src/processing/outbox.py +273 -0
- src/processing/tracing.py +36 -0
- src/processing/transformations/__init__.py +0 -0
- src/processing/transformations/enrichment.py +125 -0
- src/quality/__init__.py +0 -0
- src/quality/monitors/__init__.py +0 -0
- src/quality/monitors/freshness_monitor.py +166 -0
- src/quality/monitors/metrics_collector.py +367 -0
- src/quality/validators/__init__.py +0 -0
- src/quality/validators/schema_validator.py +119 -0
- src/quality/validators/semantic_validator.py +202 -0
- src/serving/__init__.py +0 -0
- src/serving/api/__init__.py +0 -0
- src/serving/api/alert_dispatcher.py +51 -0
- src/serving/api/alerts/__init__.py +38 -0
- src/serving/api/alerts/dispatcher.py +299 -0
- src/serving/api/alerts/escalation.py +290 -0
- src/serving/api/alerts/evaluator.py +81 -0
- src/serving/api/alerts/history.py +115 -0
- src/serving/api/analytics.py +543 -0
- src/serving/api/auth/__init__.py +46 -0
- src/serving/api/auth/key_rotation.py +400 -0
- src/serving/api/auth/manager.py +406 -0
- src/serving/api/auth/middleware.py +331 -0
- src/serving/api/main.py +390 -0
- src/serving/api/middleware/logging.py +41 -0
- src/serving/api/middleware/tracing.py +51 -0
- src/serving/api/rate_limiter.py +76 -0
- src/serving/api/routers/__init__.py +0 -0
- src/serving/api/routers/admin.py +150 -0
- src/serving/api/routers/admin_ui.py +93 -0
- src/serving/api/routers/agent_query.py +639 -0
- src/serving/api/routers/alerts.py +134 -0
- src/serving/api/routers/batch.py +231 -0
- src/serving/api/routers/contracts.py +98 -0
- src/serving/api/routers/deadletter.py +337 -0
- src/serving/api/routers/lineage.py +218 -0
- src/serving/api/routers/search.py +103 -0
- src/serving/api/routers/slo.py +231 -0
- src/serving/api/routers/stream.py +141 -0
- src/serving/api/routers/webhooks.py +93 -0
- src/serving/api/security.py +83 -0
- src/serving/api/telemetry.py +66 -0
- src/serving/api/templates/admin.html +214 -0
- src/serving/api/versioning.py +328 -0
- src/serving/api/webhook_dispatcher.py +423 -0
- src/serving/backends/__init__.py +117 -0
- src/serving/backends/clickhouse_backend.py +310 -0
- src/serving/backends/duckdb_backend.py +268 -0
- src/serving/cache.py +169 -0
- src/serving/db_pool.py +105 -0
- src/serving/masking.py +122 -0
- src/serving/semantic_layer/__init__.py +0 -0
- src/serving/semantic_layer/catalog.py +177 -0
- src/serving/semantic_layer/contract_registry.py +258 -0
- src/serving/semantic_layer/entity_type_registry.py +107 -0
- src/serving/semantic_layer/nl_engine.py +189 -0
- src/serving/semantic_layer/query/__init__.py +3 -0
- src/serving/semantic_layer/query/contracts.py +47 -0
- src/serving/semantic_layer/query/engine.py +81 -0
- src/serving/semantic_layer/query/entity_queries.py +221 -0
- src/serving/semantic_layer/query/metric_queries.py +84 -0
- src/serving/semantic_layer/query/nl_queries.py +305 -0
- src/serving/semantic_layer/query/sql_builder.py +113 -0
- src/serving/semantic_layer/query/sql_guard.py +3 -0
- src/serving/semantic_layer/query_engine.py +5 -0
- src/serving/semantic_layer/schema_evolution.py +175 -0
- src/serving/semantic_layer/search_index.py +337 -0
- 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,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
|
File without changes
|
|
@@ -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
|