inputlayer-client-dev 0.1.0.dev919__tar.gz → 0.1.0.dev924__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 (141) hide show
  1. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/PKG-INFO +1 -1
  2. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/pyproject.toml +1 -1
  3. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/compiler.py +11 -6
  4. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/knowledge_graph.py +16 -5
  5. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_compiler.py +19 -0
  6. inputlayer_client_dev-0.1.0.dev924/tests/test_kg_load.py +51 -0
  7. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/.gitignore +0 -0
  8. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/LICENSE +0 -0
  9. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/README.md +0 -0
  10. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/01_quickstart.py +0 -0
  11. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/02_social_network.py +0 -0
  12. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/03_rag_pipeline.py +0 -0
  13. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/04_ecommerce.py +0 -0
  14. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/05_rbac.py +0 -0
  15. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/06_realtime_dashboard.py +0 -0
  16. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/07_dataframe_etl.py +0 -0
  17. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/08_session_rules.py +0 -0
  18. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/09_access_control.py +0 -0
  19. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/10_migrations.py +0 -0
  20. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/__init__.py +0 -0
  21. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/_common.py +0 -0
  22. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex01_retriever.py +0 -0
  23. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex02_vector.py +0 -0
  24. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex03_tool.py +0 -0
  25. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex04_lcel_chain.py +0 -0
  26. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex05_kg_building.py +0 -0
  27. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex06_explainable_rag.py +0 -0
  28. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex07_multihop.py +0 -0
  29. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex08_memory.py +0 -0
  30. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex09_access_control.py +0 -0
  31. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex10_multi_agent.py +0 -0
  32. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex11_anomaly.py +0 -0
  33. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex12_hallucination.py +0 -0
  34. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex13_guardrails.py +0 -0
  35. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex14_graphrag.py +0 -0
  36. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex15_caching.py +0 -0
  37. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex16_recommendation.py +0 -0
  38. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/ex17_lineage.py +0 -0
  39. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langchain/runner.py +0 -0
  40. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/__init__.py +0 -0
  41. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/_common.py +0 -0
  42. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex01_reasoning_loop.py +0 -0
  43. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex02_investigation.py +0 -0
  44. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex03_human_in_loop.py +0 -0
  45. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex04_branching_pipeline.py +0 -0
  46. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex05_self_correcting.py +0 -0
  47. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex06_collaborative_planning.py +0 -0
  48. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex07_event_correlation.py +0 -0
  49. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex08_tool_selection.py +0 -0
  50. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex09_streaming_aggregation.py +0 -0
  51. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex10_resumable_graph.py +0 -0
  52. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex11_memory.py +0 -0
  53. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/ex12_resumable_chat.py +0 -0
  54. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/examples/langgraph/runner.py +0 -0
  55. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/__init__.py +0 -0
  56. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/_ast.py +0 -0
  57. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/_naming.py +0 -0
  58. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/_protocol.py +0 -0
  59. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/_proxy.py +0 -0
  60. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/_sync.py +0 -0
  61. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/aggregations.py +0 -0
  62. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/auth.py +0 -0
  63. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/client.py +0 -0
  64. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/client_sync.py +0 -0
  65. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/connection.py +0 -0
  66. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/derived.py +0 -0
  67. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/exceptions.py +0 -0
  68. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/functions.py +0 -0
  69. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/index.py +0 -0
  70. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/__init__.py +0 -0
  71. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langchain/__init__.py +0 -0
  72. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langchain/params.py +0 -0
  73. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langchain/retriever.py +0 -0
  74. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langchain/tool.py +0 -0
  75. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langchain/vector_store.py +0 -0
  76. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/__init__.py +0 -0
  77. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/_checkpoint_serde.py +0 -0
  78. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/_checkpointer_mixin.py +0 -0
  79. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/_memory_helpers.py +0 -0
  80. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/_memory_mixin.py +0 -0
  81. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/_utils.py +0 -0
  82. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/checkpointer.py +0 -0
  83. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/memory.py +0 -0
  84. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/nodes.py +0 -0
  85. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/router.py +0 -0
  86. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/integrations/langgraph/state.py +0 -0
  87. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/__init__.py +0 -0
  88. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/autodetector.py +0 -0
  89. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/cli.py +0 -0
  90. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/errors.py +0 -0
  91. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/executor.py +0 -0
  92. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/loader.py +0 -0
  93. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/operations.py +0 -0
  94. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/recorder.py +0 -0
  95. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/state.py +0 -0
  96. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/migrations/writer.py +0 -0
  97. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/notifications.py +0 -0
  98. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/py.typed +0 -0
  99. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/relation.py +0 -0
  100. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/result.py +0 -0
  101. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/session.py +0 -0
  102. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/src/inputlayer/types.py +0 -0
  103. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/__init__.py +0 -0
  104. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/_mock_checkpoint_kg.py +0 -0
  105. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/_mock_memory_kg.py +0 -0
  106. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/conftest.py +0 -0
  107. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_aggregations.py +0 -0
  108. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_ast.py +0 -0
  109. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_autodetector.py +0 -0
  110. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_checkpointer_core.py +0 -0
  111. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_checkpointer_lifecycle.py +0 -0
  112. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_checkpointer_list.py +0 -0
  113. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_checkpointer_validation.py +0 -0
  114. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_cli.py +0 -0
  115. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_connection.py +0 -0
  116. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_derived.py +0 -0
  117. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_escape_iql.py +0 -0
  118. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_executor.py +0 -0
  119. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_functions.py +0 -0
  120. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_integration.py +0 -0
  121. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_kg_node.py +0 -0
  122. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_kg_router.py +0 -0
  123. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_langchain.py +0 -0
  124. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_langgraph_integration.py +0 -0
  125. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_loader.py +0 -0
  126. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_memory_core.py +0 -0
  127. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_memory_edge_cases.py +0 -0
  128. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_memory_helpers.py +0 -0
  129. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_migration_safety.py +0 -0
  130. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_naming.py +0 -0
  131. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_operations.py +0 -0
  132. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_protocol.py +0 -0
  133. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_proxy.py +0 -0
  134. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_relation.py +0 -0
  135. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_result.py +0 -0
  136. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_serde.py +0 -0
  137. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_state.py +0 -0
  138. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_sync.py +0 -0
  139. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_types.py +0 -0
  140. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/tests/test_writer.py +0 -0
  141. {inputlayer_client_dev-0.1.0.dev919 → inputlayer_client_dev-0.1.0.dev924}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: inputlayer-client-dev
3
- Version: 0.1.0.dev919
3
+ Version: 0.1.0.dev924
4
4
  Summary: Python Object-Logic Mapper for InputLayer knowledge graph engine
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "inputlayer-client-dev"
7
- version = "0.1.0.dev919"
7
+ version = "0.1.0.dev924"
8
8
  description = "Python Object-Logic Mapper for InputLayer knowledge graph engine"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -666,6 +666,17 @@ def compile_rule(
666
666
  else:
667
667
  head_parts.append(column_to_variable(col))
668
668
 
669
+ # Compile filter conditions BEFORE building body atoms, so every column
670
+ # the condition references is registered in the env and gets bound to a
671
+ # variable in its atom. Compiling them after produced atoms with `_` for
672
+ # condition-only columns while the condition referenced an unbound
673
+ # variable - which the engine accepts and silently satisfies, deriving
674
+ # wrong results (e.g. a tier == "gold" filter matching every row).
675
+ cond_parts: list[str] = []
676
+ if condition:
677
+ cond_parts = compile_bool_expr(condition, env)
678
+ cond_parts = [p for p in cond_parts if p]
679
+
669
680
  # Build body atoms
670
681
  body_atoms: list[str] = []
671
682
  for rn, cls, alias in body_relations:
@@ -680,12 +691,6 @@ def compile_rule(
680
691
  atom_parts.append("_")
681
692
  body_atoms.append(f"{rn}({', '.join(atom_parts)})")
682
693
 
683
- # Compile filter conditions
684
- cond_parts: list[str] = []
685
- if condition:
686
- cond_parts = compile_bool_expr(condition, env)
687
- cond_parts = [p for p in cond_parts if p]
688
-
689
694
  all_body = body_atoms + cond_parts
690
695
  prefix = "+" if persistent else ""
691
696
  head_str = f"{prefix}{head_name}({', '.join(head_parts)})"
@@ -963,11 +963,22 @@ class KnowledgeGraph:
963
963
  )
964
964
 
965
965
  async def load(self, path: str, *, mode: str | None = None) -> None:
966
- """Load data from a file."""
967
- cmd = f".load {path}"
968
- if mode:
969
- cmd += f" {mode}"
970
- await self._execute(cmd)
966
+ """Load an IQL file into this knowledge graph.
967
+
968
+ The file is read locally and sent to the server as a single
969
+ multi-statement program, so the load is atomic: the server parses
970
+ every statement before executing any, and rejects the whole
971
+ program on the first error.
972
+
973
+ (Sending ``.load`` over the wire does not work: the server treats
974
+ it as a client-only REPL command and silently ignores it.)
975
+ """
976
+ if mode is not None:
977
+ msg = "load(mode=...) is not supported; --replace/--merge are unimplemented server-side"
978
+ raise NotImplementedError(msg)
979
+ with open(path, encoding="utf-8") as f:
980
+ program = f.read()
981
+ await self._execute(program)
971
982
 
972
983
  async def clear_prefix(self, prefix: str) -> ClearResult:
973
984
  """Clear all relations matching a prefix."""
@@ -494,6 +494,25 @@ class TestCompileRule:
494
494
  )
495
495
  assert "+high_earner(Id, Name)" in result
496
496
  assert "Salary > 100000" in result
497
+ # The condition column must be BOUND in the body atom. Emitting
498
+ # employee(Id, Name, _, _, _) alongside `Salary > 100000` leaves
499
+ # Salary unbound - the engine accepts that and silently satisfies
500
+ # the comparison, deriving wrong results.
501
+ assert "employee(Id, Name, _, Salary, _)" in result
502
+
503
+ def test_condition_only_column_is_bound(self):
504
+ # Regression: a column referenced ONLY by the where-condition (not
505
+ # selected into the head) must still be bound in its atom.
506
+ cond = Comparison("=", AstColumn("employee", "department"), Literal("eng"))
507
+ result = compile_rule(
508
+ "eng_member",
509
+ ["name"],
510
+ {"name": AstColumn("employee", "name")},
511
+ [(Employee._resolve_name(), Employee, None)],
512
+ condition=cond,
513
+ persistent=True,
514
+ )
515
+ assert result == '+eng_member(Name) <- employee(_, Name, Department, _, _), Department = "eng"'
497
516
 
498
517
  def test_recursive(self):
499
518
  # reachable(Src, Dst) <- reachable(Src, Mid), edge(Mid, Dst)
@@ -0,0 +1,51 @@
1
+ """Tests for KnowledgeGraph.load: local file read, sent as one atomic program."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import AsyncMock
6
+
7
+ import pytest
8
+
9
+ from inputlayer.knowledge_graph import KnowledgeGraph
10
+
11
+
12
+ def _kg_with_mocked_execute() -> KnowledgeGraph:
13
+ kg = KnowledgeGraph.__new__(KnowledgeGraph)
14
+ kg._execute = AsyncMock() # type: ignore[method-assign]
15
+ return kg
16
+
17
+
18
+ class TestLoad:
19
+ async def test_sends_file_contents_as_one_program(self, tmp_path) -> None:
20
+ pack = tmp_path / "pack.iql"
21
+ pack.write_text(
22
+ "// a comment the server strips\n"
23
+ '+claim(id: string, val: string)\n'
24
+ '+claim[("c1", "v1")]\n'
25
+ )
26
+
27
+ kg = _kg_with_mocked_execute()
28
+ await kg.load(str(pack))
29
+
30
+ kg._execute.assert_awaited_once()
31
+ program = kg._execute.await_args.args[0]
32
+ # The whole file goes over in a single execute, comments included
33
+ # (the server strips them), never as a `.load` meta command.
34
+ assert '+claim[("c1", "v1")]' in program
35
+ assert "// a comment" in program
36
+ assert not program.lstrip().startswith(".load")
37
+
38
+ async def test_mode_is_rejected(self, tmp_path) -> None:
39
+ pack = tmp_path / "pack.iql"
40
+ pack.write_text("+r(x: string)\n")
41
+
42
+ kg = _kg_with_mocked_execute()
43
+ with pytest.raises(NotImplementedError):
44
+ await kg.load(str(pack), mode="--replace")
45
+ kg._execute.assert_not_awaited()
46
+
47
+ async def test_missing_file_raises(self) -> None:
48
+ kg = _kg_with_mocked_execute()
49
+ with pytest.raises(FileNotFoundError):
50
+ await kg.load("/nonexistent/pack.iql")
51
+ kg._execute.assert_not_awaited()