nlweb-widget 0.9.12__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 (79) hide show
  1. nlweb_widget-0.9.12/.env.agentic.coding.example +18 -0
  2. nlweb_widget-0.9.12/.env.example +56 -0
  3. nlweb_widget-0.9.12/.github/CODEOWNERS +1 -0
  4. nlweb_widget-0.9.12/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  5. nlweb_widget-0.9.12/.github/dependabot.yml +7 -0
  6. nlweb_widget-0.9.12/.github/workflows/ci.yml +18 -0
  7. nlweb_widget-0.9.12/.github/workflows/release.yml +124 -0
  8. nlweb_widget-0.9.12/.gitignore +16 -0
  9. nlweb_widget-0.9.12/.nlweb-build-manifest.toml +144 -0
  10. nlweb_widget-0.9.12/.python-version +1 -0
  11. nlweb_widget-0.9.12/.vscode/launch.json +37 -0
  12. nlweb_widget-0.9.12/.vscode/tasks.json +93 -0
  13. nlweb_widget-0.9.12/AGENTS.md +34 -0
  14. nlweb_widget-0.9.12/ARCHITECTURE.md +43 -0
  15. nlweb_widget-0.9.12/AUTHORITATIVE-DOMAIN-DATA-CONTRACT.md +55 -0
  16. nlweb_widget-0.9.12/CHANGELOG.md +67 -0
  17. nlweb_widget-0.9.12/CONTRACT-TRACEABILITY.md +16 -0
  18. nlweb_widget-0.9.12/CONTRIBUTING.md +3 -0
  19. nlweb_widget-0.9.12/LICENSE +21 -0
  20. nlweb_widget-0.9.12/PKG-INFO +212 -0
  21. nlweb_widget-0.9.12/README.md +172 -0
  22. nlweb_widget-0.9.12/pyproject.toml +41 -0
  23. nlweb_widget-0.9.12/revision-iteration-manifest-0.9.12.toml +42 -0
  24. nlweb_widget-0.9.12/scripts/bootstrap-win10.ps1 +7 -0
  25. nlweb_widget-0.9.12/skills/nlweb-provider-qa/SKILL.md +16 -0
  26. nlweb_widget-0.9.12/skills/nlweb-release/SKILL.md +18 -0
  27. nlweb_widget-0.9.12/skills/nlweb-release/references/release-gates.md +11 -0
  28. nlweb_widget-0.9.12/skills.md +12 -0
  29. nlweb_widget-0.9.12/src/nlweb_widget/__init__.py +43 -0
  30. nlweb_widget-0.9.12/src/nlweb_widget/cli.py +18 -0
  31. nlweb_widget-0.9.12/src/nlweb_widget/content/__init__.py +52 -0
  32. nlweb_widget-0.9.12/src/nlweb_widget/content/adapters.py +335 -0
  33. nlweb_widget-0.9.12/src/nlweb_widget/content/fingerprint.py +20 -0
  34. nlweb_widget-0.9.12/src/nlweb_widget/content/haystack.py +105 -0
  35. nlweb_widget-0.9.12/src/nlweb_widget/content/models.py +72 -0
  36. nlweb_widget-0.9.12/src/nlweb_widget/content/neo4j.py +236 -0
  37. nlweb_widget-0.9.12/src/nlweb_widget/content/qdrant.py +154 -0
  38. nlweb_widget-0.9.12/src/nlweb_widget/content/qdrant_edge.py +202 -0
  39. nlweb_widget-0.9.12/src/nlweb_widget/content/retrieval.py +31 -0
  40. nlweb_widget-0.9.12/src/nlweb_widget/content/scheduler.py +29 -0
  41. nlweb_widget-0.9.12/src/nlweb_widget/content/service.py +239 -0
  42. nlweb_widget-0.9.12/src/nlweb_widget/content/sqlite.py +404 -0
  43. nlweb_widget-0.9.12/src/nlweb_widget/content/state.py +35 -0
  44. nlweb_widget-0.9.12/src/nlweb_widget/content/store.py +179 -0
  45. nlweb_widget-0.9.12/src/nlweb_widget/embedding_benchmark.py +86 -0
  46. nlweb_widget-0.9.12/src/nlweb_widget/embeddings.py +61 -0
  47. nlweb_widget-0.9.12/src/nlweb_widget/errors.py +25 -0
  48. nlweb_widget-0.9.12/src/nlweb_widget/integrations/__init__.py +13 -0
  49. nlweb_widget-0.9.12/src/nlweb_widget/integrations/_bridge.py +10 -0
  50. nlweb_widget-0.9.12/src/nlweb_widget/integrations/django_integration.py +46 -0
  51. nlweb_widget-0.9.12/src/nlweb_widget/integrations/fastapi_integration.py +143 -0
  52. nlweb_widget-0.9.12/src/nlweb_widget/integrations/flask_integration.py +64 -0
  53. nlweb_widget-0.9.12/src/nlweb_widget/integrations/starlette_integration.py +45 -0
  54. nlweb_widget-0.9.12/src/nlweb_widget/integrations/stdlib_http_integration.py +93 -0
  55. nlweb_widget-0.9.12/src/nlweb_widget/models.py +219 -0
  56. nlweb_widget-0.9.12/src/nlweb_widget/protocol.py +196 -0
  57. nlweb_widget-0.9.12/src/nlweb_widget/retrieval.py +50 -0
  58. nlweb_widget-0.9.12/src/nlweb_widget/settings.py +109 -0
  59. nlweb_widget-0.9.12/src/nlweb_widget/widget.py +460 -0
  60. nlweb_widget-0.9.12/tests/integration/test_fastapi_integration.py +129 -0
  61. nlweb_widget-0.9.12/tests/integration/test_http_error_contract.py +23 -0
  62. nlweb_widget-0.9.12/tests/integration/test_stdlib_contract.py +16 -0
  63. nlweb_widget-0.9.12/tests/packaging/test_manifest.py +72 -0
  64. nlweb_widget-0.9.12/tests/packaging/test_naming.py +19 -0
  65. nlweb_widget-0.9.12/tests/packaging/test_package_ownership.py +13 -0
  66. nlweb_widget-0.9.12/tests/unit/test_cli.py +5 -0
  67. nlweb_widget-0.9.12/tests/unit/test_content_recovery.py +69 -0
  68. nlweb_widget-0.9.12/tests/unit/test_content_sources_config.py +31 -0
  69. nlweb_widget-0.9.12/tests/unit/test_document_adapter.py +38 -0
  70. nlweb_widget-0.9.12/tests/unit/test_embedding_benchmark.py +26 -0
  71. nlweb_widget-0.9.12/tests/unit/test_embeddings.py +22 -0
  72. nlweb_widget-0.9.12/tests/unit/test_operational_state.py +81 -0
  73. nlweb_widget-0.9.12/tests/unit/test_optional_stores.py +203 -0
  74. nlweb_widget-0.9.12/tests/unit/test_protocol.py +47 -0
  75. nlweb_widget-0.9.12/tests/unit/test_protocol_compliance.py +295 -0
  76. nlweb_widget-0.9.12/tests/unit/test_protocol_matrix.py +147 -0
  77. nlweb_widget-0.9.12/tests/unit/test_qdrant_edge.py +34 -0
  78. nlweb_widget-0.9.12/tests/unit/test_settings.py +31 -0
  79. nlweb_widget-0.9.12/uv.lock +4983 -0
@@ -0,0 +1,18 @@
1
+ # Agentic coding credential classification template.
2
+ # Copy to .env.agentic.coding locally; never commit the populated file.
3
+
4
+ # OpenAI / Codex-compatible API
5
+ OPENAI_API_KEY=
6
+
7
+ # Anthropic / Claude-compatible API
8
+ ANTHROPIC_API_KEY=
9
+
10
+ # Other coding-agent provider API keys, if used by local tooling
11
+ # CODE_AGENT_API_KEY=
12
+
13
+ # GitHub CLI / API authentication should normally remain in the user's
14
+ # OS credential store or `gh auth`; do not copy a personal token here.
15
+ # GITHUB_TOKEN=
16
+
17
+ # Optional package/release credentials should use CI trusted publishing instead.
18
+ # PYPI_TOKEN=
@@ -0,0 +1,56 @@
1
+ # Core
2
+ # Disabled by default: protects a fresh domain from accidental projection/retrieval.
3
+ NLWEB_ENABLED=false
4
+ NLWEB_PREFIX=/nlweb
5
+ NLWEB_SITE=default
6
+ NLWEB_RETRIEVAL_STORE=memory
7
+ # Optional Haystack InMemory semantic retrieval
8
+ # NLWEB_RETRIEVAL_STORE=haystack-memory
9
+ NLWEB_CONTENT_SYNC_ENABLED=true
10
+ NLWEB_CONTENT_SYNC_ON_STARTUP=false
11
+ NLWEB_CONTENT_SOURCES_FILE=./nlweb-sources.json
12
+ NLWEB_CONTENT_SYNC_STATE_FILE=.nlweb-sync-state.json
13
+ NLWEB_CONTENT_SYNC_INTERVAL_SECONDS=900
14
+
15
+ # Optional PostgreSQL + pgvector
16
+ # NLWEB_RETRIEVAL_STORE=pgvector
17
+ # NLWEB_POSTGRES_DATABASE_URL=postgresql+asyncpg://nlweb:nlweb@localhost:5432/nlweb
18
+ # NLWEB_PGVECTOR_DIMENSIONS=384
19
+
20
+ # Optional Neo4j
21
+ # NLWEB_RETRIEVAL_STORE=neo4j
22
+ # NLWEB_NEO4J_URI=neo4j://localhost:7687
23
+ # NLWEB_NEO4J_USERNAME=neo4j
24
+ # NLWEB_NEO4J_PASSWORD=
25
+ # NLWEB_NEO4J_DATABASE=neo4j
26
+ # NLWEB_NEO4J_VECTOR_DIMENSIONS=384
27
+
28
+ # Optional Qdrant Server / Cloud
29
+ # NLWEB_RETRIEVAL_STORE=qdrant
30
+ # NLWEB_QDRANT_URL=http://localhost:6333
31
+ # NLWEB_QDRANT_API_KEY=
32
+ # NLWEB_QDRANT_COLLECTION=nlweb_documents
33
+ # NLWEB_QDRANT_DIMENSIONS=384
34
+
35
+ # Optional Qdrant Edge (embedded/local, no server required)
36
+ # NLWEB_RETRIEVAL_STORE=qdrant_edge
37
+ # NLWEB_QDRANT_EDGE_PATH=./.nlweb-qdrant-edge
38
+ # NLWEB_QDRANT_EDGE_DIMENSIONS=384
39
+
40
+ # Optional SQLite + sqlite-vec hybrid retrieval (FTS5 + vector KNN)
41
+ # Install with: uv sync --extra sqlite
42
+ # NLWEB_RETRIEVAL_STORE=sqlite
43
+ # NLWEB_SQLITE_PATH=./.nlweb-sqlite/nlweb.db
44
+ # NLWEB_SQLITE_DIMENSIONS=384
45
+
46
+ # Optional local semantic embeddings (used by Haystack/Qdrant/PostgreSQL/Neo4j when
47
+ # an embedding provider is not injected). The default is multilingual and light enough
48
+ # for local development. For E5 models, query/passage prefixes are applied automatically.
49
+ # NLWEB_EMBEDDING_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
50
+ # NLWEB_EMBEDDING_QUERY_PREFIX=
51
+ # NLWEB_EMBEDDING_DOCUMENT_PREFIX=
52
+ # NLWEB_EMBEDDING_DEVICE=cpu
53
+
54
+ # Optional unstructured document ingestion via Docling for ContentSourceKind.CUSTOM.
55
+ # Install with: uv sync --extra documents
56
+ NLWEB_TEST_ENV=dev # Indicates whether tests for governance tests (manifests) needed to be performed
@@ -0,0 +1 @@
1
+ * @maintainers
@@ -0,0 +1,9 @@
1
+ ## Change summary
2
+
3
+ ## Manifest impact
4
+
5
+ ## Validation
6
+ - [ ] pytest
7
+ - [ ] ruff
8
+ - [ ] build
9
+ - [ ] docs updated
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
7
+ open-pull-requests-limit: 0
@@ -0,0 +1,18 @@
1
+ name: CI
2
+ on: [push,pull_request]
3
+ permissions: {contents: read}
4
+ jobs:
5
+ core:
6
+ runs-on: ubuntu-latest
7
+ strategy: {matrix: {python: ["3.10","3.11","3.12","3.13","3.14"]}}
8
+ steps:
9
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
10
+ with:
11
+ persist-credentials: false
12
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
13
+ - run: uv python install ${{ matrix.python }}
14
+ - run: uv sync --dev --all-extras --python ${{ matrix.python }}
15
+ - run: uv run --python ${{ matrix.python }} pytest
16
+ - run: uv run --python ${{ matrix.python }} ruff check .
17
+ - run: uv run --python ${{ matrix.python }} ruff format --check .
18
+ - run: uv build
@@ -0,0 +1,124 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*.*.*"]
6
+
7
+ # Default to read-only; grant write permissions only to jobs that need them.
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: pypi-release
13
+ queue: max
14
+
15
+ jobs:
16
+ build:
17
+ name: Validate, test, and build
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: read
21
+
22
+ steps:
23
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
+ with:
25
+ persist-credentials: false
26
+
27
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
28
+
29
+ - name: Install release Python
30
+ run: uv python install 3.12
31
+
32
+ - name: Install release dependencies
33
+ run: uv sync --dev --all-extras --python 3.12
34
+
35
+ - name: Verify tag matches project version
36
+ shell: bash
37
+ run: |
38
+ VERSION=$(uv run --python 3.12 python - <<'PY'
39
+ import tomllib
40
+ with open("pyproject.toml", "rb") as f:
41
+ print(tomllib.load(f)["project"]["version"])
42
+ PY
43
+ )
44
+ test "v${VERSION}" = "${GITHUB_REF_NAME}"
45
+
46
+ - name: Test
47
+ run: uv run --python 3.12 pytest
48
+
49
+ - name: Lint
50
+ run: uv run --python 3.12 ruff check .
51
+
52
+ - name: Format check
53
+ run: uv run --python 3.12 ruff format --check .
54
+
55
+ - name: Build distributions
56
+ run: uv build --python 3.12
57
+
58
+ - name: Upload distributions
59
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
60
+ with:
61
+ name: distributions
62
+ path: dist/
63
+ if-no-files-found: error
64
+ retention-days: 30
65
+
66
+ attest:
67
+ name: Attest build provenance
68
+ needs: build
69
+ runs-on: ubuntu-latest
70
+ permissions:
71
+ contents: read
72
+ id-token: write
73
+ attestations: write
74
+
75
+ steps:
76
+ - name: Download distributions
77
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
78
+ with:
79
+ name: distributions
80
+ path: dist/
81
+
82
+ - name: Attest build provenance
83
+ uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
84
+ with:
85
+ subject-path: "dist/*"
86
+
87
+ github-release:
88
+ name: Create GitHub release
89
+ needs: [build, attest]
90
+ runs-on: ubuntu-latest
91
+ permissions:
92
+ contents: write
93
+
94
+ steps:
95
+ - name: Download distributions
96
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
97
+ with:
98
+ name: distributions
99
+ path: dist/
100
+
101
+ - name: Create GitHub release
102
+ uses: softprops/action-gh-release@8a0cd651e5b2365bd640854a3fa5dfc70e4875f3 # v3.0.4
103
+ with:
104
+ generate_release_notes: true
105
+ files: "dist/*"
106
+
107
+ publish-pypi:
108
+ name: Publish to PyPI
109
+ needs: [build, attest]
110
+ runs-on: ubuntu-latest
111
+ environment:
112
+ name: pypi
113
+ permissions:
114
+ id-token: write
115
+
116
+ steps:
117
+ - name: Download distributions
118
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
119
+ with:
120
+ name: distributions
121
+ path: dist/
122
+
123
+ - name: Publish to PyPI via trusted publishing
124
+ uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1 v1.14.3
@@ -0,0 +1,16 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ *.py[cod]
6
+ dist/
7
+ build/
8
+ .env
9
+ docs-private/
10
+ governance-artifacts/
11
+
12
+ .nlweb-sync-state.json
13
+
14
+ .env.agentic.coding
15
+ .env.agentic.coding.*
16
+ !.env.agentic.coding.example
@@ -0,0 +1,144 @@
1
+ [manifest]
2
+ kind = "canonical-build-manifest"
3
+ schema_version = "2.0"
4
+ package_version = "0.9.12"
5
+ mutation_round = "release"
6
+ mutation_candidate = "nlweb-widget-0.9.12"
7
+ distribution_name = "nlweb-widget"
8
+ import_namespace = "nlweb_widget"
9
+ build_mode = "forward-only"
10
+ genesis_baseline_immutable = true
11
+
12
+ [governance]
13
+ active_revision_manifest_sha256 = "a6417d4294d7e5d86ae21c7fa890d56d64bc60687625c80892e28379d248e3fb"
14
+ active_revision_manifest = "revision-iteration-manifest-0.9.12.toml"
15
+ active_revision_manifest_required = true
16
+ active_revision_manifest_hash_required = true
17
+ only_declared_iteration_changes_permitted = true
18
+ unapproved_mutation_is_build_blocking = true
19
+ release_approved = true
20
+
21
+ [genesis]
22
+ package_sha256 = "cf22dcb24707a22a2f34a7565b30da73b221bc359e42ad9f6f85a17293f59c02"
23
+ docs_sha256 = "a2cfb30155bb3686ee0116266edeb4770e78fffd3c5718ae9389c9fa9ec53c0a"
24
+ immutable = true
25
+
26
+ [baseline_transition]
27
+ success_action = "promote-successful-result-to-next-baseline"
28
+ failure_action = "retain-current-baseline"
29
+ rollback = "explicit-only"
30
+ rollback_requires_hash_address = true
31
+
32
+ [recovery]
33
+ historical_capability_recovery = true
34
+ protected_capability_inventory_required = true
35
+ omission_point_recorded = true
36
+ reference_implementation_internals_forbidden = true
37
+
38
+ [providers]
39
+ postgres_pgvector_optional = true
40
+ neo4j_optional = true
41
+ haystack_optional = true
42
+ qdrant_optional = true
43
+ qdrant_edge_optional = true
44
+ sqlite_vec_optional = true
45
+ sqlite_vec_pin = "0.1.8"
46
+ docling_optional = true
47
+ sentence_transformers_optional = true
48
+ embedding_provider_injected = true
49
+ provider_tests_conditional = true
50
+
51
+ [protocol]
52
+ ask = "/ask"
53
+ mcp = "/mcp"
54
+ discovery = "/.well-known/nlweb"
55
+ health = "/health"
56
+ ask_is_normative = true
57
+ mcp_is_binding = true
58
+ discovery_is_non_normative_metadata = true
59
+ health_is_operational = true
60
+ root_domain_default = true
61
+ protocol_source_review_required = true
62
+
63
+ [lifecycle]
64
+ enabled_setting = "NLWEB_ENABLED"
65
+ enabled_default = false
66
+ projection_safeguard_only = true
67
+ widget_owned_state = "effective_state"
68
+ admin_lifecycle_extension_external = true
69
+
70
+ [content_projection]
71
+ source_registry_loading = true
72
+ startup_projection = true
73
+ relative_source_paths_resolved_against_registry = true
74
+
75
+ [ecosystem]
76
+ master_manifest = "nlweb-widget-ecosystem-manifest.toml"
77
+ examples_repository = "nlweb-widget-examples"
78
+ examples_are_published_to_pypi = false
79
+ examples_dependencies_must_not_enter_runtime = true
80
+ examples_are_independent_lifecycle = true
81
+
82
+ [structure]
83
+ framework_neutral_core = true
84
+ integrations_are_thin_adapters = true
85
+ adapter_pattern = "*_integration.py"
86
+
87
+ [examples]
88
+ repository = "nlweb-widget-examples"
89
+ kept_out_of_widget_repository = true
90
+ [tests]
91
+ all_test_imports_must_be_satisfied_by_dev_group = true
92
+ fresh_uv_sync_is_a_required_gate = true
93
+ required = ["unit", "integration", "packaging"]
94
+ python_minimum = "3.10"
95
+ live_llm_required = false
96
+ external_network_required = false
97
+ examples_test_suite_is_external = true
98
+
99
+ [docs]
100
+ independent_pyproject_required = true
101
+ static_docs_project_must_not_require_editable_build = true
102
+ zensical_required = true
103
+ role_based = true
104
+ protocol_contract_required = true
105
+
106
+ [vscode]
107
+ package_test_tasks_required = true
108
+ package_build_task_required = true
109
+ example_tasks_forbidden = true
110
+ aggregate_visibility_demo_task_forbidden = true
111
+
112
+ [packaging]
113
+ backend = "hatchling"
114
+ uv_backed = true
115
+ pyproject_required = true
116
+ revision_manifest_shipped = true
117
+ build_manifest_shipped = true
118
+ sync_state_shipped = false
119
+
120
+ [release]
121
+ tag_pattern = "v{version}"
122
+ tag_must_equal_project_version = true
123
+ trusted_publishing = true
124
+ provenance_attestation = true
125
+
126
+ [forward_only]
127
+ rules = [
128
+ "Never modify the genesis baseline.",
129
+ "Never introduce example-only dependencies into the published runtime graph.",
130
+ "Example applications and visibility infrastructure are governed by the examples repository.",
131
+ "Never apply undeclared revision changes.",
132
+ "A failed iteration cannot advance the baseline.",
133
+ "A successful iteration becomes the sole candidate next baseline.",
134
+ "Rollback is explicit, hash-addressed, and reviewable.",
135
+ "Build fails closed when governance metadata is inconsistent.",
136
+ ]
137
+
138
+ [artifact_packaging]
139
+ fail_closed = true
140
+ forbidden_exact_names = [".env", ".nlweb-sync-state.json"]
141
+ manifest = "nlweb-widget-ecosystem-manifest.toml"
142
+ python_version_file = ".python-version"
143
+ python_version = "3.10"
144
+ forbidden_path_fragments = [".venv/", "__pycache__/", ".pytest_cache/", ".ruff_cache/", ".mypy_cache/", "*.pyc", "*.pyo", "dist/", "build/", ".env/", "node_modules/"]
@@ -0,0 +1 @@
1
+ 3.10
@@ -0,0 +1,37 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Python: Current test file",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "module": "pytest",
9
+ "args": [
10
+ "${file}",
11
+ "-q"
12
+ ]
13
+ },
14
+ {
15
+ "name": "Python: Package tests",
16
+ "type": "debugpy",
17
+ "request": "launch",
18
+ "module": "pytest",
19
+ "args": [
20
+ "tests",
21
+ "-q"
22
+ ]
23
+ },
24
+ {
25
+ "name": "Python: SQLite provider test",
26
+ "type": "debugpy",
27
+ "request": "launch",
28
+ "module": "pytest",
29
+ "args": [
30
+ "tests/unit/test_optional_stores.py",
31
+ "-k",
32
+ "sqlite",
33
+ "-q"
34
+ ]
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "test-all",
6
+ "type": "shell",
7
+ "command": "uv run pytest",
8
+ "problemMatcher": [
9
+ "$python"
10
+ ]
11
+ },
12
+ {
13
+ "label": "test-unit",
14
+ "type": "shell",
15
+ "command": "uv run pytest tests/unit",
16
+ "problemMatcher": [
17
+ "$python"
18
+ ]
19
+ },
20
+ {
21
+ "label": "test-integration",
22
+ "type": "shell",
23
+ "command": "uv run pytest tests/integration",
24
+ "problemMatcher": [
25
+ "$python"
26
+ ]
27
+ },
28
+ {
29
+ "label": "test-packaging",
30
+ "type": "shell",
31
+ "command": "uv run pytest tests/packaging",
32
+ "problemMatcher": [
33
+ "$python"
34
+ ]
35
+ },
36
+ {
37
+ "label": "test-optional-haystack-memory",
38
+ "type": "shell",
39
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k haystack",
40
+ "problemMatcher": [
41
+ "$python"
42
+ ]
43
+ },
44
+ {
45
+ "label": "test-optional-qdrant-edge",
46
+ "type": "shell",
47
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k qdrant_edge",
48
+ "problemMatcher": [
49
+ "$python"
50
+ ]
51
+ },
52
+ {
53
+ "label": "test-optional-qdrant-server",
54
+ "type": "shell",
55
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k 'qdrant and not qdrant_edge'",
56
+ "problemMatcher": [
57
+ "$python"
58
+ ]
59
+ },
60
+ {
61
+ "label": "test-optional-postgres-pgvector",
62
+ "type": "shell",
63
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k postgres",
64
+ "problemMatcher": [
65
+ "$python"
66
+ ]
67
+ },
68
+ {
69
+ "label": "test-optional-neo4j",
70
+ "type": "shell",
71
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k neo4j",
72
+ "problemMatcher": [
73
+ "$python"
74
+ ]
75
+ },
76
+ {
77
+ "label": "build-package",
78
+ "type": "shell",
79
+ "command": "uv build",
80
+ "problemMatcher": [
81
+ "$python"
82
+ ]
83
+ },
84
+ {
85
+ "label": "Provider: sqlite \u2014 test",
86
+ "type": "shell",
87
+ "command": "uv run pytest tests/unit/test_optional_stores.py -k sqlite",
88
+ "problemMatcher": [
89
+ "$python"
90
+ ]
91
+ }
92
+ ]
93
+ }
@@ -0,0 +1,34 @@
1
+ # AGENTS.md
2
+
3
+ ## Project
4
+ `nlweb-widget` is a framework-neutral Python library that exposes an authoritative domain-data projection through the NLWeb protocol. The published runtime is intentionally lean; optional providers, framework adapters, and reference examples remain separated.
5
+
6
+ ## Authoritative references
7
+ - Read the active `revision-iteration-manifest-*.toml` and `.nlweb-build-manifest.toml` before mutation work.
8
+ - Treat `ARCHITECTURE.md`, `MUTATION.md`, `docs-provider-evaluations.md`, tests, and public docs as repository evidence; do not invent missing contracts.
9
+ - Preserve the forward-only baseline and hash-addressed rollback policy.
10
+
11
+ ## Change boundaries
12
+ - Do not modify an immutable baseline or historical candidate.
13
+ - Do not introduce example-only dependencies into the runtime package.
14
+ - Do not break public APIs/import paths/endpoints/configuration/provider contracts.
15
+ - Protocol changes are limited to conformance corrections, normative failure semantics, or additive optional capabilities.
16
+ - Keep framework integrations thin and keep authoritative application data separate from AI-facing retrieval projections.
17
+ - Avoid opportunistic refactors, renames, dependency upgrades, or unrelated cleanup.
18
+
19
+ ## Commands
20
+ - Install: `uv sync --dev`
21
+ - Tests: `uv run pytest`
22
+ - Lint: `uv run ruff check .`
23
+ - Format check: `uv run ruff format --check .`
24
+ - Build: `uv build`
25
+ - Optional full dependency QA: `uv sync --dev --all-extras`
26
+
27
+ ## Verification
28
+ Run the smallest relevant checks first, then the full declared gates for release work. Configured optional-provider tests must exercise the real configured backend; unconfigured providers are skipped. A configured-but-unavailable provider is a failure.
29
+
30
+ ## Security
31
+ Never commit credentials, tokens, private keys, `.env`, or `.env.agentic.coding`. Use `.env.agentic.coding.example` only as a classification/template file. Never place production secrets in examples, tests, CI logs, or generated bundles.
32
+
33
+ ## Agent workflow
34
+ For non-trivial work: inspect → plan → smallest coherent edit → synchronize manifests/docs/tests → validate → report hashes and remaining risks. Human approval remains required for release promotion, rollback, credential use, and changes to protected agent/governance files.
@@ -0,0 +1,43 @@
1
+ # Architecture
2
+
3
+ `nlweb-widget` is deliberately split into stable protocol/core abstractions and optional provider implementations.
4
+
5
+ ```text
6
+ web framework adapter
7
+ |
8
+ v
9
+ NLWeb protocol core
10
+ |
11
+ +---- authoritative content ingestion
12
+ |
13
+ +---- RetrievalProvider boundary
14
+ |
15
+ +-- memory
16
+ +-- Haystack InMemory (optional)
17
+ +-- PostgreSQL + pgvector (optional)
18
+ +-- Neo4j (optional)
19
+ +-- Qdrant Server/Cloud (optional)
20
+ +-- Qdrant Edge (optional, separate in-process path)
21
+ ```
22
+
23
+ ## Compatibility boundary
24
+
25
+ The public `NLWebWidget`, request/response models, retrieval protocols, and framework adapters remain the stable consumer-facing surface. Provider libraries are implementation details behind those interfaces.
26
+
27
+ ## Retrieval
28
+
29
+ All semantic providers receive an injected `EmbeddingProvider` or use the optional local sentence-transformers helper. This keeps model choice outside store implementation and prevents incompatible vector dimensions from being silently mixed.
30
+
31
+ ## Neo4j
32
+
33
+ Schema and vector-index DDL remain explicit Cypher. On Neo4j 2026.01+ the store uses the `SEARCH` clause for vector retrieval; older supported servers use the legacy vector procedure as a compatibility path.
34
+
35
+ ## Content ingestion
36
+
37
+ Structured Schema.org/JSON-LD, web-page JSON-LD, RSS/Atom, sitemap, database, and custom callable sources remain supported. `CUSTOM` additionally has an optional Docling-backed document adapter for common unstructured files. Optional parsing libraries are not part of the base runtime dependency set.
38
+
39
+ ## Protocol compliance
40
+
41
+ The `/ask` core owns the complete NLWeb v0.55 request envelope (`query`, `context`, `prefer`, `meta`), applies site/domain filters, emits the `_meta` response envelope, and supports the two normative response formats additively. Streaming remains an explicit transport capability not claimed by this core; a complete response is returned with `_meta.streaming=false` when requested.
42
+
43
+ `/mcp` has an independent MCP binding contract. `tools/list` exposes only capabilities actually implemented by the widget, `tools/call` maps application failures to `isError=true`, and unknown tools/invalid JSON-RPC requests use MCP protocol errors. NLWeb reference implementation internals are never imported.
@@ -0,0 +1,55 @@
1
+ # Shared NLWeb Widget authoritative domain data contract
2
+
3
+ Status: approved planning baseline; implementation candidates are pending local-QA approval. This document is shared by the Widget and Examples mutation iterations. Official wire behavior is governed by the [NLWeb Specification](https://nlweb.ai/docs/specification), currently reviewed at v0.55 for the SSE event contract.
4
+
5
+ ## Authority and precedence
6
+
7
+ 1. Applicable official NLWeb and MCP protocol requirements govern wire behavior.
8
+ 2. The Widget owns correct protocol serialization and transport behavior. An Examples convention cannot force or justify a conflicting Widget behavior.
9
+ 3. The approved source-data contract governs how examples identify artifacts and retain source metadata.
10
+ 4. Consumer-selected manifests and optional metadata may extend behavior only when they do not override standardized field semantics or required response structures.
11
+
12
+ Any suspected conflict must be recorded with the conflicting requirements, affected boundary, evidence, and proposed resolution. It is not silently considered approved.
13
+
14
+ ## Result serialization
15
+
16
+ - For conversational-search responses, a requested `summarize` mode produces a result item with `@type: "SearchSummary"` (or a justified domain-specific type) and `text` containing the summary.
17
+ - A top-level `summary` property must not substitute for that result item.
18
+ - If the mode preference includes `summarize` among multiple comma-separated modes, the Widget must include a summary item.
19
+ - Result objects should expose the relevant authoritative schema properties at the result level rather than a private implementation wrapper such as `schema_object`.
20
+ - A suitable source `@type` is preserved. A manifest-declared type is a fallback only when the structured source has no suitable type.
21
+ - Optional grounding metadata must not be used to replace or redefine protocol fields. Its allowed shape and placement must conform to the applicable official specification, not merely to the examples manifest.
22
+
23
+ ## Artifact identity and URL precedence
24
+
25
+ - Artifact identity is independent from chunk identity. All chunks for one artifact share the same canonical artifact URL.
26
+ - Chunk uniqueness remains in document IDs and internal chunk metadata. Synthetic `#chunk-N` fragments must not be added.
27
+ - A suitable `url` wins over `@id` for the artifact URL when both are present. Preserve a distinct `@id` where it remains useful as schema identity.
28
+ - Converter-provided URLs are retained. Configured registry URL fallbacks are only used when suitable document/converter URLs are absent.
29
+ - A configured fallback that represents a base path receives the artifact filename. A filename alone never proves a public evidence URI exists.
30
+
31
+ ## Optional inbox evidence manifest
32
+
33
+ The Examples candidate defines an optional version-1 `nlweb-evidence-manifest.json` control file. Each `artifacts[]` entry has a safe relative `artifact` key and may contain `type` and `grounding`.
34
+
35
+ - The manifest is not ingested as ordinary prose.
36
+ - Manifest type is a fallback only; suitable source type wins.
37
+ - Grounding supplied by the consumer is persisted as a complete JSON object unchanged at the metadata boundary. If source-schema and metadata grounding are both present and differ, the Widget reports a source-metadata failure instead of silently overwriting either value.
38
+ - Conflicting source and manifest grounding is an explicit ingestion error, not a silent overwrite.
39
+ - No manifest entry means no manifest-declared evidence URI. Other valid source grounding remains possible.
40
+ - A declared URI is a consumer assertion, not proof of public reachability or authenticity. Never expose private paths or fabricate public URLs.
41
+
42
+ ## HTTP streaming contract
43
+
44
+ When `prefer.streaming` is true on HTTP `/ask`, the Widget must return `text/event-stream` using Server-Sent Events. The v0.55 specification defines `start` (with `_meta.streaming: true`), indexed `result` events whose `item` matches the non-streaming result-item shape, optional `error` events using the failure response structure, and one `complete` event. The completion `_meta` must preserve `session_context` when present. Non-streaming requests retain the single JSON response. Framework adapters must not claim streaming support unless they implement the actual SSE transport; MCP remains its own binding.
45
+
46
+ ## Independent mutation ownership
47
+
48
+ Widget iteration owns `/ask`, response formats, result projection, summary item structure, grounding serialization, streaming transport, and MCP protocol behavior. Examples iteration owns URL normalization in example ingestion, optional manifest parsing, source type precedence, metadata persistence, sample data, and documentation. Both use the same contract revision but have independent candidates, QA records, hashes, and promotion gates.
49
+
50
+ ## Known limits requiring explicit QA status
51
+
52
+ - This local sandbox has Python 3.13 but no Python 3.12 installation for the Examples project's declared environment; offline sync cannot provision it.
53
+ - Haystack is declared in the Examples dependencies but is not installed in this sandbox. The actual MultiFileConverter path therefore remains unverified here; installation was attempted but blocked by DNS/network unavailability.
54
+ - Streaming is implemented in the candidate, but only the FastAPI route has a direct end-to-end SSE test in this sandbox; the Starlette, Django, Flask, and stdlib adapters still require framework-specific conformance tests.
55
+ - Before promotion, the maintainer must confirm exact applicable NLWeb/MCP specification revisions for the supported release matrix and rerun QA in the supported clean environment.