cognitive-fabric 0.1.0__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.
- cognitive_fabric-0.1.0/.dockerignore +69 -0
- cognitive_fabric-0.1.0/.env.example +47 -0
- cognitive_fabric-0.1.0/.github/dependabot.yml +76 -0
- cognitive_fabric-0.1.0/.github/workflows/ci.yml +285 -0
- cognitive_fabric-0.1.0/.github/workflows/live-provider-smoke.yml +73 -0
- cognitive_fabric-0.1.0/.github/workflows/release.yml +140 -0
- cognitive_fabric-0.1.0/.gitignore +78 -0
- cognitive_fabric-0.1.0/Dockerfile +54 -0
- cognitive_fabric-0.1.0/Dockerfile.dev +35 -0
- cognitive_fabric-0.1.0/LICENSE +201 -0
- cognitive_fabric-0.1.0/NOTICE +29 -0
- cognitive_fabric-0.1.0/PKG-INFO +274 -0
- cognitive_fabric-0.1.0/README.md +233 -0
- cognitive_fabric-0.1.0/docker-compose.yml +118 -0
- cognitive_fabric-0.1.0/docs/SECURITY.md +50 -0
- cognitive_fabric-0.1.0/docs/api/cli.md +455 -0
- cognitive_fabric-0.1.0/docs/api/schemas.md +372 -0
- cognitive_fabric-0.1.0/docs/api/tools.md +980 -0
- cognitive_fabric-0.1.0/docs/architecture.md +286 -0
- cognitive_fabric-0.1.0/docs/examples/basic-usage.md +440 -0
- cognitive_fabric-0.1.0/docs/fabric.md +75 -0
- cognitive_fabric-0.1.0/docs/guides/configuration.md +306 -0
- cognitive_fabric-0.1.0/docs/guides/development.md +502 -0
- cognitive_fabric-0.1.0/docs/guides/getting-started.md +217 -0
- cognitive_fabric-0.1.0/docs/guides/memory-optimizer.md +403 -0
- cognitive_fabric-0.1.0/docs/index.md +77 -0
- cognitive_fabric-0.1.0/pyproject.toml +149 -0
- cognitive_fabric-0.1.0/scripts/live_provider_smoke.py +133 -0
- cognitive_fabric-0.1.0/scripts/start_server.py +23 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/__init__.py +7 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/_version.py +20 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/__init__.py +7 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/__init__.py +19 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/agent.py +367 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/base.py +129 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/context_builder.py +319 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/mcp_sampling.py +407 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/prompt_manager.py +298 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/services/__init__.py +17 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/services/analysis.py +290 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/services/execution.py +416 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/agents/memory_optimizer/services/planning.py +303 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/cli/__init__.py +5 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/cli/main.py +463 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/config.py +95 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/__init__.py +11 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/connection_manager.py +126 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/kuzu_client.py +269 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/query_executor.py +208 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/repository_factory.py +244 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/repository_provider.py +138 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/db/schema_manager.py +425 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/fabric/__init__.py +6 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/fabric/ingestor.py +126 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/fabric/vector_store.py +239 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/llm.py +239 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/main.py +35 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/__init__.py +12 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/__init__.py +48 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/analyze.py +165 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/associate.py +169 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/bulk_import.py +137 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/context.py +113 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/delete.py +477 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/detect.py +145 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/entity.py +195 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/fabric.py +213 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/introspect.py +224 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/memory_bank.py +90 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/memory_optimizer.py +577 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/query.py +159 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/handlers/search.py +416 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/server.py +319 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/tool_context.py +91 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/tool_registry.py +131 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/tools/__init__.py +13 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/tools/base.py +85 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/mcp/tools/definitions.py +929 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/__init__.py +29 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/base.py +216 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/component/__init__.py +11 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/component/algorithms.py +224 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/component/crud.py +302 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/component/graph.py +257 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/component_repo.py +30 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/context_repo.py +483 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/decision_repo.py +375 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/file_repo.py +509 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/graph_projection.py +116 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/metadata_repo.py +364 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/repository_repo.py +241 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/requirement_repo.py +183 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/rule_repo.py +449 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/symbol_repo.py +171 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/tag_repo.py +518 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/repositories/trace_repo.py +140 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/__init__.py +11 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/core/__init__.py +19 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/core/interfaces.py +316 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/__init__.py +21 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/context.py +252 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/data_fabric.py +159 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/dream.py +193 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/entity.py +743 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/graph_analysis.py +437 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/graph_query.py +553 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/memory_bank.py +337 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/domain/metadata.py +177 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/memory_service.py +280 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/service_container.py +357 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/services/snapshot_service.py +463 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/types/__init__.py +81 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/types/entities.py +386 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/types/mcp_types.py +202 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/types/optimization.py +355 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/__init__.py +18 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/id_utils.py +103 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/logger.py +81 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/mutex.py +75 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/path_utils.py +151 -0
- cognitive_fabric-0.1.0/src/cognitive_fabric/utils/security.py +183 -0
- cognitive_fabric-0.1.0/tests/__init__.py +1 -0
- cognitive_fabric-0.1.0/tests/conftest.py +192 -0
- cognitive_fabric-0.1.0/tests/e2e/__init__.py +1 -0
- cognitive_fabric-0.1.0/tests/e2e/test_http_transport.py +132 -0
- cognitive_fabric-0.1.0/tests/e2e/test_mcp_server.py +226 -0
- cognitive_fabric-0.1.0/tests/e2e/tools_snapshot.json +1012 -0
- cognitive_fabric-0.1.0/tests/integration/__init__.py +1 -0
- cognitive_fabric-0.1.0/tests/integration/test_fabric_handler.py +196 -0
- cognitive_fabric-0.1.0/tests/integration/test_fabric_repos.py +188 -0
- cognitive_fabric-0.1.0/tests/integration/test_fabric_schema.py +24 -0
- cognitive_fabric-0.1.0/tests/integration/test_fabric_services.py +334 -0
- cognitive_fabric-0.1.0/tests/integration/test_kuzu_operations.py +289 -0
- cognitive_fabric-0.1.0/tests/integration/test_optimizer_parity.py +483 -0
- cognitive_fabric-0.1.0/tests/integration/test_repos_extra.py +154 -0
- cognitive_fabric-0.1.0/tests/integration/test_services.py +117 -0
- cognitive_fabric-0.1.0/tests/integration/test_snapshot_service.py +385 -0
- cognitive_fabric-0.1.0/tests/integration/test_vector_store.py +98 -0
- cognitive_fabric-0.1.0/tests/integration/test_wire_contract.py +246 -0
- cognitive_fabric-0.1.0/tests/unit/__init__.py +1 -0
- cognitive_fabric-0.1.0/tests/unit/test_db_path_env.py +97 -0
- cognitive_fabric-0.1.0/tests/unit/test_docs_consistency.py +755 -0
- cognitive_fabric-0.1.0/tests/unit/test_logging.py +120 -0
- cognitive_fabric-0.1.0/tests/unit/test_no_model_ids_in_source.py +176 -0
- cognitive_fabric-0.1.0/tests/unit/test_optimizer_limits.py +219 -0
- cognitive_fabric-0.1.0/tests/unit/test_path_confinement.py +97 -0
- cognitive_fabric-0.1.0/tests/unit/test_types.py +245 -0
- cognitive_fabric-0.1.0/tests/unit/test_version.py +75 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Docker matches these against the path relative to the context root, so a bare
|
|
2
|
+
# name covers only the root -- `__pycache__` does not match `src/x/__pycache__`.
|
|
3
|
+
# Depth-capable patterns therefore carry `**/`. Patterns that are meaningfully
|
|
4
|
+
# root-only (.git, .venv, data) stay anchored.
|
|
5
|
+
|
|
6
|
+
# Git
|
|
7
|
+
.git
|
|
8
|
+
.gitignore
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
**/__pycache__
|
|
12
|
+
**/*.py[cod]
|
|
13
|
+
**/*$py.class
|
|
14
|
+
**/*.so
|
|
15
|
+
.Python
|
|
16
|
+
.venv
|
|
17
|
+
venv
|
|
18
|
+
ENV
|
|
19
|
+
env
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
**/.vscode
|
|
23
|
+
**/.idea
|
|
24
|
+
**/*.swp
|
|
25
|
+
**/*.swo
|
|
26
|
+
|
|
27
|
+
# Testing
|
|
28
|
+
**/.pytest_cache
|
|
29
|
+
**/.coverage
|
|
30
|
+
**/htmlcov
|
|
31
|
+
**/.tox
|
|
32
|
+
**/.nox
|
|
33
|
+
|
|
34
|
+
# Type-checker and linter caches (61 MB of .mypy_cache here)
|
|
35
|
+
**/.mypy_cache
|
|
36
|
+
**/.ruff_cache
|
|
37
|
+
**/.cache
|
|
38
|
+
|
|
39
|
+
# Build
|
|
40
|
+
build
|
|
41
|
+
dist
|
|
42
|
+
**/*.egg-info
|
|
43
|
+
**/*.egg
|
|
44
|
+
|
|
45
|
+
# Documentation
|
|
46
|
+
docs/_build
|
|
47
|
+
|
|
48
|
+
# Local data
|
|
49
|
+
data/
|
|
50
|
+
**/*.db
|
|
51
|
+
**/*.kuzu
|
|
52
|
+
**/*.wal
|
|
53
|
+
.kuzumem/
|
|
54
|
+
.fabric_data/
|
|
55
|
+
|
|
56
|
+
# Misc
|
|
57
|
+
**/.DS_Store
|
|
58
|
+
**/*.log
|
|
59
|
+
|
|
60
|
+
# Secrets. .gitignore already covers `.env`, but this file did not -- and
|
|
61
|
+
# .dockerignore is what governs the build context, not .gitignore. An
|
|
62
|
+
# untracked .env is exactly what a broad COPY would pick up. Nested too, which
|
|
63
|
+
# is why this is `**/` rather than a bare name.
|
|
64
|
+
**/.env
|
|
65
|
+
**/.env.*
|
|
66
|
+
**/*.pem
|
|
67
|
+
**/*.key
|
|
68
|
+
**/*.p12
|
|
69
|
+
**/id_rsa*
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Cognitive-Fabric Configuration
|
|
2
|
+
#
|
|
3
|
+
# Every model id in this file is an example. No model is compiled into the
|
|
4
|
+
# package: a model id is a vendor, a price and a capability envelope chosen at
|
|
5
|
+
# install time, and it goes stale only when the provider retires the name. A
|
|
6
|
+
# provider is usable once you name a model for it, and the server says which
|
|
7
|
+
# setting is missing when you have not.
|
|
8
|
+
|
|
9
|
+
# Logging
|
|
10
|
+
COGNITIVE_FABRIC_LOG_LEVEL=INFO
|
|
11
|
+
COGNITIVE_FABRIC_LOG_JSON=true
|
|
12
|
+
|
|
13
|
+
# Database (required: the server has no default path)
|
|
14
|
+
# COGNITIVE_FABRIC_DB_PATH=/path/to/custom/db
|
|
15
|
+
|
|
16
|
+
# LLM Provider (openai or anthropic)
|
|
17
|
+
COGNITIVE_FABRIC_LLM_PROVIDER=openai
|
|
18
|
+
|
|
19
|
+
# OpenAI Configuration
|
|
20
|
+
OPENAI_API_KEY=sk-your-api-key-here
|
|
21
|
+
# Required. Must be a model the provider still lists -- it is checked against
|
|
22
|
+
# https://api.openai.com/v1/models before the first call.
|
|
23
|
+
COGNITIVE_FABRIC_OPENAI_MODEL=<a model id from https://platform.openai.com/docs/models>
|
|
24
|
+
|
|
25
|
+
# Anthropic Configuration (alternative)
|
|
26
|
+
# ANTHROPIC_API_KEY=sk-ant-your-api-key-here
|
|
27
|
+
# COGNITIVE_FABRIC_ANTHROPIC_MODEL=<a model id from https://docs.anthropic.com/en/docs/about-claude/models>
|
|
28
|
+
|
|
29
|
+
# Embeddings for the semantic layer. There is no default cloud embedding model:
|
|
30
|
+
# an embedding model fixes both the price per million tokens and the width of
|
|
31
|
+
# every vector already in the table, and vectors from two models are not
|
|
32
|
+
# comparable. The local backends keep their own defaults.
|
|
33
|
+
#
|
|
34
|
+
# fastembed local, ONNX, no torch (default)
|
|
35
|
+
# sentence-transformers local, needs torch
|
|
36
|
+
# openai cloud, model required
|
|
37
|
+
# COGNITIVE_FABRIC_FABRIC_EMBEDDING_PROVIDER=fastembed
|
|
38
|
+
# COGNITIVE_FABRIC_FABRIC_EMBEDDING_MODEL=<required when the provider is openai>
|
|
39
|
+
|
|
40
|
+
# Memory Optimizer
|
|
41
|
+
COGNITIVE_FABRIC_OPTIMIZER_DEFAULT_STRATEGY=conservative
|
|
42
|
+
COGNITIVE_FABRIC_OPTIMIZER_ENABLE_MCP_SAMPLING=true
|
|
43
|
+
COGNITIVE_FABRIC_OPTIMIZER_SNAPSHOT_FAILURE_POLICY=warn
|
|
44
|
+
|
|
45
|
+
# Cognitive Fabric (semantic layer)
|
|
46
|
+
# Where the LanceDB vector store is stored (defaults to ./.fabric_data)
|
|
47
|
+
# COGNITIVE_FABRIC_FABRIC_DATA_DIR=/path/to/fabric_data
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Dependabot is configured rather than switched off.
|
|
2
|
+
#
|
|
3
|
+
# Left at the default, this opens a PR per dependency per week, which in practice
|
|
4
|
+
# is a queue nobody can merge. This repository holds two dependencies deliberately
|
|
5
|
+
# (kuzu, mcp) and caps two more (the cloud SDKs), so an ungrouped config would
|
|
6
|
+
# spend most of its PRs proposing to lift exactly the pins that are there on
|
|
7
|
+
# purpose. One grouped PR per ecosystem per month is a size a person can review in
|
|
8
|
+
# one sitting.
|
|
9
|
+
#
|
|
10
|
+
# The `ignore` entries below restate decisions already written into
|
|
11
|
+
# pyproject.toml. If a pin is lifted there, lift it here too -- otherwise this file
|
|
12
|
+
# goes on suppressing updates nobody still means to suppress. Note that Dependabot
|
|
13
|
+
# reads this file only from the default branch, so changes here take effect at the
|
|
14
|
+
# next merge to `main`, not at the push.
|
|
15
|
+
#
|
|
16
|
+
# There are deliberately no `labels:` entries: requesting a label that does not
|
|
17
|
+
# exist in the repository gives you a PR that carries a "could not be found"
|
|
18
|
+
# warning and no labels at all. Create them first if you want them.
|
|
19
|
+
|
|
20
|
+
version: 2
|
|
21
|
+
|
|
22
|
+
updates:
|
|
23
|
+
# ------------------------------------------------------------------ Python deps
|
|
24
|
+
- package-ecosystem: "pip"
|
|
25
|
+
directory: "/"
|
|
26
|
+
schedule:
|
|
27
|
+
interval: "monthly"
|
|
28
|
+
commit-message:
|
|
29
|
+
prefix: "deps"
|
|
30
|
+
groups:
|
|
31
|
+
pip:
|
|
32
|
+
patterns: ["*"]
|
|
33
|
+
ignore:
|
|
34
|
+
# Pinned exactly in pyproject.toml. kuzu is archived upstream -- 0.11.3 is the
|
|
35
|
+
# final release -- and its file format is not stable before 1.0, so a bump can
|
|
36
|
+
# leave an existing graph unreadable. Lifting this pin needs a migration step,
|
|
37
|
+
# not a dependency bump.
|
|
38
|
+
- dependency-name: "kuzu"
|
|
39
|
+
|
|
40
|
+
# Capped below 2 in pyproject.toml: the 2.x SDK removed the low-level Server
|
|
41
|
+
# list_tools/call_tool decorators that the server is built on.
|
|
42
|
+
- dependency-name: "mcp"
|
|
43
|
+
versions: [">= 2.0.0"]
|
|
44
|
+
|
|
45
|
+
# The two cloud SDKs in the `[cloud]` extra are deliberately *not* ignored,
|
|
46
|
+
# though they are capped in pyproject.toml. An ignore would suppress the one
|
|
47
|
+
# PR that would show you a break: an SDK major rewrote the request types under
|
|
48
|
+
# its callers once already, and the failure arrived as a runtime TypeError on a
|
|
49
|
+
# user's machine. A major bump now opens a PR proposing to lift the cap, and
|
|
50
|
+
# the live-provider smoke workflow (Actions -> Live provider smoke -> Run
|
|
51
|
+
# workflow) is what says whether the new SDK still accepts the request this
|
|
52
|
+
# package builds. A green install proves nothing about the request shape.
|
|
53
|
+
|
|
54
|
+
# ----------------------------------------------------------------- GitHub Actions
|
|
55
|
+
- package-ecosystem: "github-actions"
|
|
56
|
+
directory: "/"
|
|
57
|
+
schedule:
|
|
58
|
+
interval: "monthly"
|
|
59
|
+
commit-message:
|
|
60
|
+
prefix: "ci"
|
|
61
|
+
groups:
|
|
62
|
+
github-actions:
|
|
63
|
+
patterns: ["*"]
|
|
64
|
+
|
|
65
|
+
# ------------------------------------------------------------------ Docker images
|
|
66
|
+
# The Dockerfile's base image is the only docker dependency here; nothing is held,
|
|
67
|
+
# so this is a plain grouped update.
|
|
68
|
+
- package-ecosystem: "docker"
|
|
69
|
+
directory: "/"
|
|
70
|
+
schedule:
|
|
71
|
+
interval: "monthly"
|
|
72
|
+
commit-message:
|
|
73
|
+
prefix: "docker"
|
|
74
|
+
groups:
|
|
75
|
+
docker:
|
|
76
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Continuous Integration - Run tests and linting
|
|
2
|
+
name: CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
- master
|
|
9
|
+
# Deliberately no branch filter. With `branches: [main, master]` here, a pull
|
|
10
|
+
# request whose base is any other branch -- which is how a change is stacked on
|
|
11
|
+
# another unmerged change -- got no CI at all, and "no checks reported" in the
|
|
12
|
+
# PR list reads much like "checks passed". Every pull request runs CI.
|
|
13
|
+
pull_request:
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
# The workflow files are the one thing here with no other gate, and release.yml
|
|
17
|
+
# is the part that runs rarely and matters most -- so a mistake in it is found
|
|
18
|
+
# at release time, by everyone. actionlint reads the workflows themselves:
|
|
19
|
+
# expression syntax, job-graph references, and the shell and Python inside each
|
|
20
|
+
# `run:` block, through shellcheck and pyflakes, both of which the pinned image
|
|
21
|
+
# below ships. Run against this tree it reported two SC2086 findings in
|
|
22
|
+
# release.yml (`>> $GITHUB_OUTPUT` unquoted); both are fixed.
|
|
23
|
+
actionlint:
|
|
24
|
+
name: Lint workflows
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- name: Checkout repository
|
|
28
|
+
uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Run actionlint
|
|
31
|
+
uses: docker://rhysd/actionlint:1.7.12
|
|
32
|
+
with:
|
|
33
|
+
args: -color
|
|
34
|
+
|
|
35
|
+
lint:
|
|
36
|
+
name: Lint
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- name: Checkout repository
|
|
40
|
+
uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Set up Python
|
|
43
|
+
uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: '3.11'
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: pip install -e '.[dev]'
|
|
49
|
+
|
|
50
|
+
# `scripts/` is included, not just `src/` and `tests/`. The smoke script is
|
|
51
|
+
# not part of the package but it is part of this repository, and code no
|
|
52
|
+
# gate reads is code that rots. This used to name that one file because
|
|
53
|
+
# scripts/start_server.py did not pass -- it imported `kuzumempy.main`,
|
|
54
|
+
# which does not exist; the tool-surface freeze fixed it to
|
|
55
|
+
# `cognitive_fabric.main`. Its remaining E402 is a deliberate path insert,
|
|
56
|
+
# marked as such.
|
|
57
|
+
- name: Run Ruff linter
|
|
58
|
+
run: ruff check src tests scripts
|
|
59
|
+
|
|
60
|
+
# mypy is deliberately not a gate here yet. It is configured
|
|
61
|
+
# (`strict = true` in pyproject.toml) but the tree does not satisfy it:
|
|
62
|
+
# 199 errors across 39 files as of this commit. A job that fails on every
|
|
63
|
+
# run says less than no job at all, so wire this in once the tree is
|
|
64
|
+
# clean — the configuration is already in place and only the step is
|
|
65
|
+
# missing. The command will be `mypy src`.
|
|
66
|
+
|
|
67
|
+
test:
|
|
68
|
+
name: Test (py${{ matrix.python-version }}, ${{ matrix.profile.name }})
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
needs: lint
|
|
71
|
+
strategy:
|
|
72
|
+
# Report every combination, rather than cancelling the others when one
|
|
73
|
+
# fails.
|
|
74
|
+
fail-fast: false
|
|
75
|
+
matrix:
|
|
76
|
+
# Every version the package installs on, and nothing beyond them. 3.14 is
|
|
77
|
+
# excluded by `requires-python`, because kuzu 0.11.3 ships no cp314 wheels
|
|
78
|
+
# for macOS or Windows; the comment there has the detail.
|
|
79
|
+
python-version: ['3.11', '3.12', '3.13']
|
|
80
|
+
# Two installs, because the failure worth catching here is not a red
|
|
81
|
+
# test -- it is a green run in which the optional half never ran. The
|
|
82
|
+
# vector-store suite is `pytest.importorskip("lancedb")` at module level,
|
|
83
|
+
# so under the minimal install it collapses to a single module skip and
|
|
84
|
+
# its four tests do not exist; the full install is where they run. The
|
|
85
|
+
# minimal install is where the *degraded* path -- semantic search
|
|
86
|
+
# reporting itself unavailable rather than raising -- is the only thing
|
|
87
|
+
# that can run, and so must. That pairing has to hold on both Python
|
|
88
|
+
# versions, because a green minimal run is exactly what a broken install
|
|
89
|
+
# of the full one looks like.
|
|
90
|
+
profile:
|
|
91
|
+
- name: minimal
|
|
92
|
+
extras: dev
|
|
93
|
+
- name: full
|
|
94
|
+
extras: dev,cloud,semantic
|
|
95
|
+
|
|
96
|
+
steps:
|
|
97
|
+
- name: Checkout repository
|
|
98
|
+
uses: actions/checkout@v4
|
|
99
|
+
|
|
100
|
+
- name: Set up Python
|
|
101
|
+
uses: actions/setup-python@v5
|
|
102
|
+
with:
|
|
103
|
+
python-version: ${{ matrix.python-version }}
|
|
104
|
+
|
|
105
|
+
# Every extra is installed somewhere here. An extra that does not resolve
|
|
106
|
+
# is a broken install for every user who asks for it, and nothing else in
|
|
107
|
+
# CI would notice. `cloud` is in the full profile only; no test imports
|
|
108
|
+
# either SDK (the LLM tests use duck-typed fakes), so it is installed to
|
|
109
|
+
# prove it installs, not to cover a hidden skip.
|
|
110
|
+
#
|
|
111
|
+
# `full` downloads fastembed's ONNX model on the first vector-store test,
|
|
112
|
+
# which is the price of the four tests actually running rather than
|
|
113
|
+
# skipping. It happens once per job.
|
|
114
|
+
- name: Install dependencies
|
|
115
|
+
run: pip install -e '.[${{ matrix.profile.extras }}]'
|
|
116
|
+
|
|
117
|
+
# `-rs` is not decoration. Without it a module-level skip leaves no line at
|
|
118
|
+
# all in the `-v` output, so "the vector-store tests skipped" and "the
|
|
119
|
+
# vector-store tests were never mentioned" read identically -- which is the
|
|
120
|
+
# whole defect this job pair exists for. The short summary is what names the
|
|
121
|
+
# reason, and the check below reads it.
|
|
122
|
+
- name: Run tests
|
|
123
|
+
env:
|
|
124
|
+
PROFILE: ${{ matrix.profile.name }}
|
|
125
|
+
run: |
|
|
126
|
+
set +e
|
|
127
|
+
pytest tests/unit/ tests/integration/ tests/e2e/ -v --tb=short -rs \
|
|
128
|
+
> /tmp/pytest.log 2>&1
|
|
129
|
+
PYTEST_RC=$?
|
|
130
|
+
set -e
|
|
131
|
+
cat /tmp/pytest.log
|
|
132
|
+
export PYTEST_RC
|
|
133
|
+
python3 - <<'PY'
|
|
134
|
+
import os
|
|
135
|
+
import pathlib
|
|
136
|
+
import re
|
|
137
|
+
import sys
|
|
138
|
+
|
|
139
|
+
rc = int(os.environ["PYTEST_RC"])
|
|
140
|
+
log = pathlib.Path("/tmp/pytest.log").read_text()
|
|
141
|
+
|
|
142
|
+
# A red run is its own message, and it is printed above.
|
|
143
|
+
if rc != 0:
|
|
144
|
+
sys.exit(rc)
|
|
145
|
+
|
|
146
|
+
profile = os.environ["PROFILE"]
|
|
147
|
+
|
|
148
|
+
# One line per test that actually ran, so this counts tests executed --
|
|
149
|
+
# not tests collected, and not "no failures".
|
|
150
|
+
ran = re.findall(
|
|
151
|
+
r"^tests/integration/test_vector_store\.py::\S+ PASSED", log, re.M
|
|
152
|
+
)
|
|
153
|
+
degraded = (
|
|
154
|
+
"TestTheDegradedSemanticPath::"
|
|
155
|
+
"test_semantic_search_reports_itself_unavailable PASSED"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if profile == "full":
|
|
159
|
+
# `full` installs the [semantic] extra, so the four tests are the
|
|
160
|
+
# point of the profile; anything fewer means part of them skipped.
|
|
161
|
+
if len(ran) != 4:
|
|
162
|
+
sys.exit(
|
|
163
|
+
f"the full profile installs [semantic], so all four "
|
|
164
|
+
f"vector-store tests must run; {len(ran)} did: {ran}"
|
|
165
|
+
)
|
|
166
|
+
print(f"full profile: {len(ran)}/4 vector-store tests ran")
|
|
167
|
+
else:
|
|
168
|
+
# `minimal` has no [semantic] extra: the module must have skipped,
|
|
169
|
+
# and the degraded path must have been asserted. A profile that
|
|
170
|
+
# merely skipped things has proved nothing.
|
|
171
|
+
if ran:
|
|
172
|
+
sys.exit(
|
|
173
|
+
f"the minimal profile does not install [semantic], so "
|
|
174
|
+
f"test_vector_store.py must not run, but {len(ran)} of its "
|
|
175
|
+
f"tests did: {ran}"
|
|
176
|
+
)
|
|
177
|
+
if "SKIPPED [1] tests/integration/test_vector_store.py:" not in log:
|
|
178
|
+
sys.exit(
|
|
179
|
+
"the vector-store module did not report itself skipped, so "
|
|
180
|
+
"nothing here says the missing extra is what is missing"
|
|
181
|
+
)
|
|
182
|
+
if degraded not in log:
|
|
183
|
+
sys.exit(
|
|
184
|
+
"semantic search was never asserted to degrade, in the one "
|
|
185
|
+
"profile where it is unavailable"
|
|
186
|
+
)
|
|
187
|
+
print("minimal profile: vector store skipped; degraded path asserted")
|
|
188
|
+
PY
|
|
189
|
+
|
|
190
|
+
docker:
|
|
191
|
+
# Builds the image but never publishes it. CF ships a Dockerfile and a
|
|
192
|
+
# docker-compose.yml, and until this job existed nothing had ever built
|
|
193
|
+
# either -- so a broken image would have been found by whoever tried it
|
|
194
|
+
# first, after release, rather than here.
|
|
195
|
+
name: Docker (production image)
|
|
196
|
+
runs-on: ubuntu-latest
|
|
197
|
+
needs: lint
|
|
198
|
+
|
|
199
|
+
steps:
|
|
200
|
+
- name: Checkout repository
|
|
201
|
+
uses: actions/checkout@v4
|
|
202
|
+
|
|
203
|
+
- name: Build the production image
|
|
204
|
+
run: docker build --file Dockerfile --tag cognitive-fabric:ci .
|
|
205
|
+
|
|
206
|
+
- name: Handshake over stdio
|
|
207
|
+
# Initialise, then let stdin close. This does need the database path the
|
|
208
|
+
# Dockerfile sets: the server builds its MemoryService during startup and
|
|
209
|
+
# exits without one, so it cannot answer `initialize` pathless. No key
|
|
210
|
+
# and no network are needed, which is still the point of checking here.
|
|
211
|
+
#
|
|
212
|
+
# stdout carries the JSON-RPC stream, so anything the server logs has to
|
|
213
|
+
# go to stderr. It does -- PrintLoggerFactory(file=sys.stderr) in
|
|
214
|
+
# utils/logger.py -- and the assertion below fails rather than skips if
|
|
215
|
+
# that ever stops being true.
|
|
216
|
+
run: |
|
|
217
|
+
printf '%s\n' \
|
|
218
|
+
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci-handshake","version":"0"}}}' \
|
|
219
|
+
| timeout 300 docker run -i --rm cognitive-fabric:ci \
|
|
220
|
+
> /tmp/handshake.jsonl 2> /tmp/handshake.err \
|
|
221
|
+
|| echo "docker run exit: $?"
|
|
222
|
+
echo "--- container stderr (tail) ---"
|
|
223
|
+
tail -20 /tmp/handshake.err || true
|
|
224
|
+
|
|
225
|
+
- name: Assert the handshake
|
|
226
|
+
run: |
|
|
227
|
+
python3 - <<'PY'
|
|
228
|
+
import json, pathlib, sys
|
|
229
|
+
|
|
230
|
+
# Both pinned deliberately: the version this repo publishes, and the
|
|
231
|
+
# name a client sees. Without an explicit `version=` the MCP SDK falls
|
|
232
|
+
# back to the *mcp package's* version, which is how the handshake once
|
|
233
|
+
# advertised 1.30.0 for a 0.1.0 server.
|
|
234
|
+
EXPECTED_VERSION = "0.1.0"
|
|
235
|
+
EXPECTED_NAME = "cognitive-fabric"
|
|
236
|
+
|
|
237
|
+
raw = pathlib.Path("/tmp/handshake.jsonl").read_text()
|
|
238
|
+
|
|
239
|
+
# stdout carries the JSON-RPC stream and nothing else. A stray log line
|
|
240
|
+
# here corrupts the protocol, so it fails rather than being skipped.
|
|
241
|
+
messages = []
|
|
242
|
+
for n, line in enumerate(raw.splitlines(), 1):
|
|
243
|
+
if not line.strip():
|
|
244
|
+
continue
|
|
245
|
+
try:
|
|
246
|
+
messages.append(json.loads(line))
|
|
247
|
+
except json.JSONDecodeError as e:
|
|
248
|
+
sys.exit(f"stdout line {n} is not JSON: {line[:160]!r} ({e})")
|
|
249
|
+
|
|
250
|
+
infos = [
|
|
251
|
+
m["result"]["serverInfo"]
|
|
252
|
+
for m in messages
|
|
253
|
+
if isinstance(m.get("result"), dict) and "serverInfo" in m["result"]
|
|
254
|
+
]
|
|
255
|
+
if not infos:
|
|
256
|
+
sys.exit(f"no serverInfo in {len(messages)} message(s): {messages!r}")
|
|
257
|
+
|
|
258
|
+
info = infos[0]
|
|
259
|
+
if info.get("name") != EXPECTED_NAME:
|
|
260
|
+
sys.exit(f"serverInfo.name is {info.get('name')!r}, expected {EXPECTED_NAME!r}")
|
|
261
|
+
if info.get("version") != EXPECTED_VERSION:
|
|
262
|
+
sys.exit(
|
|
263
|
+
f"serverInfo.version is {info.get('version')!r}, "
|
|
264
|
+
f"expected {EXPECTED_VERSION!r}"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
print(f"stdout: {len(messages)} JSON-RPC message(s), 0 stray lines")
|
|
268
|
+
print(f"serverInfo: {info}")
|
|
269
|
+
PY
|
|
270
|
+
|
|
271
|
+
test-summary:
|
|
272
|
+
name: Test Summary
|
|
273
|
+
runs-on: ubuntu-latest
|
|
274
|
+
needs: test
|
|
275
|
+
if: always()
|
|
276
|
+
|
|
277
|
+
steps:
|
|
278
|
+
- name: Check test results
|
|
279
|
+
run: |
|
|
280
|
+
if [ "${{ needs.test.result }}" == "success" ]; then
|
|
281
|
+
echo "All tests passed!"
|
|
282
|
+
else
|
|
283
|
+
echo "Some tests failed"
|
|
284
|
+
exit 1
|
|
285
|
+
fi
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# One real call per cloud provider, weekly and on demand.
|
|
2
|
+
#
|
|
3
|
+
# This is the only job in this repository that talks to a provider for real.
|
|
4
|
+
# Everything else runs offline, and the tests that cover request shape use fakes
|
|
5
|
+
# -- deliberately, because that is what makes them fast and deterministic, and
|
|
6
|
+
# also what makes them unable to answer the one question only a live call can:
|
|
7
|
+
# does the model the operator configured still exist, and does the provider still
|
|
8
|
+
# accept the request this package builds for it? Vendors retire model ids on
|
|
9
|
+
# their own schedule, and no amount of local testing notices.
|
|
10
|
+
#
|
|
11
|
+
# So it is deliberately not in the push and PR gate. It needs secrets, it spends a
|
|
12
|
+
# fraction of a cent, and it fails for reasons that have nothing to do with the
|
|
13
|
+
# commit under test -- a red X on every PR would train people to ignore it. It
|
|
14
|
+
# runs on `main` only, weekly and by hand, and its failures are a signal about the
|
|
15
|
+
# world rather than about a diff.
|
|
16
|
+
#
|
|
17
|
+
# Models come from repository *variables* (Settings -> Secrets and variables
|
|
18
|
+
# -> Actions -> Variables): OPENAI_MODEL, ANTHROPIC_MODEL. A model id is
|
|
19
|
+
# not a credential, and being able to read it in the settings page is
|
|
20
|
+
# what makes a failure diagnosable.
|
|
21
|
+
# Keys come from repository *secrets* (OPENAI_API_KEY, ANTHROPIC_API_KEY).
|
|
22
|
+
#
|
|
23
|
+
# A provider with no key or no model is reported as skipped, and the job stays
|
|
24
|
+
# green -- but a run where *nothing* was configured fails, because a smoke test
|
|
25
|
+
# that passes by testing nothing is worse than one that never ran.
|
|
26
|
+
name: Live provider smoke
|
|
27
|
+
|
|
28
|
+
on:
|
|
29
|
+
# Manual: Actions -> Live provider smoke -> Run workflow.
|
|
30
|
+
workflow_dispatch:
|
|
31
|
+
schedule:
|
|
32
|
+
# Mondays at 07:19 UTC. Off the hour on purpose: the top of the hour is when
|
|
33
|
+
# most of the world's scheduled jobs fire, and GitHub queues behind them.
|
|
34
|
+
- cron: '19 7 * * 1'
|
|
35
|
+
|
|
36
|
+
# Read-only: this job runs provider SDKs against a checked-out tree and writes
|
|
37
|
+
# nothing back.
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
smoke:
|
|
43
|
+
name: One call per provider
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
# `main` only, including for a manual dispatch, which can otherwise be
|
|
46
|
+
# started from any branch. The point is to check what users have: a branch may
|
|
47
|
+
# name a model that is not the released default, and a green run there would
|
|
48
|
+
# say nothing about the published package. Scheduled runs are already pinned
|
|
49
|
+
# to the default branch.
|
|
50
|
+
if: github.ref == 'refs/heads/main'
|
|
51
|
+
steps:
|
|
52
|
+
- name: Checkout repository
|
|
53
|
+
uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Set up Python
|
|
56
|
+
uses: actions/setup-python@v5
|
|
57
|
+
with:
|
|
58
|
+
python-version: '3.11'
|
|
59
|
+
|
|
60
|
+
# `.[cloud]`: the two provider SDKs live in an extra, not in the core
|
|
61
|
+
# dependencies, so a local-first install does not download them. This job
|
|
62
|
+
# is the one that speaks to a provider for real, so it is the one that has
|
|
63
|
+
# to ask for them.
|
|
64
|
+
- name: Install dependencies
|
|
65
|
+
run: pip install -e '.[cloud]'
|
|
66
|
+
|
|
67
|
+
- name: One tiny call per provider
|
|
68
|
+
env:
|
|
69
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
70
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
71
|
+
OPENAI_MODEL: ${{ vars.OPENAI_MODEL }}
|
|
72
|
+
ANTHROPIC_MODEL: ${{ vars.ANTHROPIC_MODEL }}
|
|
73
|
+
run: python scripts/live_provider_smoke.py
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Release workflow - Publish to PyPI on release
|
|
2
|
+
#
|
|
3
|
+
# Mirrors docugraph's, with three things that are CF's rather than docugraph's:
|
|
4
|
+
# the project name, the Dockerfile at the repository root (docugraph's lives in
|
|
5
|
+
# docker/), and no `target:` on the image build -- CF's Dockerfile has a single
|
|
6
|
+
# stage, so naming one would fail.
|
|
7
|
+
#
|
|
8
|
+
# This runs when a *GitHub Release* is published, or on manual dispatch. Pushing
|
|
9
|
+
# a bare `git tag v0.1.0` does NOT trigger it: draft a release from the tag, or
|
|
10
|
+
# use the manual run below.
|
|
11
|
+
name: Release
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
release:
|
|
15
|
+
types: [published]
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
inputs:
|
|
18
|
+
version:
|
|
19
|
+
description: 'Version to release (e.g., 0.1.0)'
|
|
20
|
+
required: true
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
name: Build Distribution
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: '3.11'
|
|
35
|
+
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v4
|
|
38
|
+
|
|
39
|
+
- name: Install build dependencies
|
|
40
|
+
run: |
|
|
41
|
+
uv pip install --system build twine
|
|
42
|
+
|
|
43
|
+
- name: Build package
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Check distribution
|
|
47
|
+
run: twine check dist/*
|
|
48
|
+
|
|
49
|
+
- name: Upload artifacts
|
|
50
|
+
uses: actions/upload-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
publish-pypi:
|
|
56
|
+
name: Publish to PyPI
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
needs: build
|
|
59
|
+
environment:
|
|
60
|
+
name: pypi
|
|
61
|
+
url: https://pypi.org/project/cognitive-fabric/
|
|
62
|
+
|
|
63
|
+
permissions:
|
|
64
|
+
id-token: write # Required for trusted publishing
|
|
65
|
+
|
|
66
|
+
steps:
|
|
67
|
+
- name: Download artifacts
|
|
68
|
+
uses: actions/download-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
- name: Publish to PyPI
|
|
74
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
75
|
+
# Uses trusted publishing - no API token needed
|
|
76
|
+
# Configure at: https://pypi.org/manage/project/cognitive-fabric/settings/publishing/
|
|
77
|
+
|
|
78
|
+
publish-docker:
|
|
79
|
+
name: Publish Release Docker Image
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
# Not `needs: build`. `publish-pypi` is gated on the `pypi` environment's
|
|
82
|
+
# required reviewers, so waiting on it means nothing reaches the registry
|
|
83
|
+
# before the release has been approved. Running straight off `build` would
|
|
84
|
+
# put an image on ghcr.io for a version that may never be published.
|
|
85
|
+
needs: [build, publish-pypi]
|
|
86
|
+
permissions:
|
|
87
|
+
contents: read
|
|
88
|
+
packages: write
|
|
89
|
+
|
|
90
|
+
env:
|
|
91
|
+
REGISTRY: ghcr.io
|
|
92
|
+
|
|
93
|
+
steps:
|
|
94
|
+
- name: Checkout repository
|
|
95
|
+
uses: actions/checkout@v4
|
|
96
|
+
|
|
97
|
+
# ghcr.io requires a lowercase repository path, and `github.repository`
|
|
98
|
+
# keeps the owner's case. Expressions have no lowercase function, so it
|
|
99
|
+
# is done in the shell.
|
|
100
|
+
- name: Resolve image name
|
|
101
|
+
id: image
|
|
102
|
+
run: echo "name=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
|
|
103
|
+
|
|
104
|
+
# linux/amd64 only. Building arm64 means running it under emulation, which
|
|
105
|
+
# needs a QEMU step registered with binfmt -- the step that used to sit
|
|
106
|
+
# here. It is a post-release item: add both back once an arm64 image has
|
|
107
|
+
# been built and verified by hand. kuzu 0.11.3 does publish manylinux
|
|
108
|
+
# aarch64 wheels for cp311, so that build would install from a wheel
|
|
109
|
+
# rather than compiling the extension.
|
|
110
|
+
- name: Set up Docker Buildx
|
|
111
|
+
uses: docker/setup-buildx-action@v3
|
|
112
|
+
|
|
113
|
+
- name: Log in to Container Registry
|
|
114
|
+
uses: docker/login-action@v3
|
|
115
|
+
with:
|
|
116
|
+
registry: ${{ env.REGISTRY }}
|
|
117
|
+
username: ${{ github.actor }}
|
|
118
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
119
|
+
|
|
120
|
+
- name: Extract version
|
|
121
|
+
id: version
|
|
122
|
+
run: |
|
|
123
|
+
if [ "${{ github.event_name }}" == "release" ]; then
|
|
124
|
+
echo "version=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
|
125
|
+
else
|
|
126
|
+
echo "version=v${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
- name: Build and push Docker image
|
|
130
|
+
uses: docker/build-push-action@v5
|
|
131
|
+
with:
|
|
132
|
+
context: .
|
|
133
|
+
file: ./Dockerfile
|
|
134
|
+
push: true
|
|
135
|
+
tags: |
|
|
136
|
+
${{ steps.image.outputs.name }}:${{ steps.version.outputs.version }}
|
|
137
|
+
${{ steps.image.outputs.name }}:latest
|
|
138
|
+
cache-from: type=gha
|
|
139
|
+
cache-to: type=gha,mode=max
|
|
140
|
+
platforms: linux/amd64
|