chumak 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.
@@ -0,0 +1,18 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ commit-message:
8
+ prefix: "fix"
9
+ prefix-development: "chore"
10
+ include: "scope"
11
+
12
+ - package-ecosystem: "github-actions"
13
+ directory: "/"
14
+ schedule:
15
+ interval: "weekly"
16
+ commit-message:
17
+ prefix: "ci"
18
+ include: "scope"
@@ -0,0 +1,81 @@
1
+ name: ๐Ÿ“ฆ๐Ÿš€ CI
2
+
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - '**'
8
+
9
+
10
+ permissions:
11
+ contents: write
12
+ pull-requests: write
13
+
14
+
15
+ env:
16
+ DEFAULT_PYTHON_VERSION: '3.13'
17
+
18
+
19
+ jobs:
20
+ validation:
21
+ name: ๐Ÿงช Validation
22
+ if: ${{ !contains(github.event.head_commit.message, 'chore(main)') }} # don't run on chore(main) commits (e.g. release - it already ran)
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ python-version: ${{ github.ref == 'refs/heads/main' && fromJson('["3.13"]') || fromJson('["default"]') }}
27
+ os: ${{ github.ref == 'refs/heads/main' && fromJson('["ubuntu-latest", "macos-latest", "windows-latest"]') || fromJson('["ubuntu-latest"]') }}
28
+
29
+ runs-on: ${{ matrix.os }}
30
+ permissions:
31
+ contents: read
32
+ issues: none
33
+ pull-requests: none
34
+
35
+
36
+ steps:
37
+ - uses: actions/checkout@v6
38
+
39
+ - uses: corriander/gha/uv/test@main
40
+ env:
41
+ UV_PYTHON: ${{ matrix.python-version == 'default' && env.DEFAULT_PYTHON_VERSION || matrix.python-version }}
42
+
43
+ release:
44
+ name: ๐Ÿš€ Release
45
+ runs-on: ubuntu-latest
46
+ needs: validation
47
+ permissions:
48
+ contents: write
49
+ pull-requests: write
50
+ id-token: write # for PyPI trusted publishing
51
+ if: | # always run on default branch as long as no failures, even if validation is skipped
52
+ always()
53
+ && !contains(needs.*.result, 'failure')
54
+ && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
55
+ steps:
56
+ - uses: googleapis/release-please-action@v5
57
+ id: release
58
+ with:
59
+ release-type: python
60
+
61
+ - uses: actions/checkout@v6
62
+
63
+ - uses: astral-sh/setup-uv@v7
64
+ if: steps.release.outputs.release_created
65
+
66
+ - name: Build
67
+ if: steps.release.outputs.release_created
68
+ run: uv build
69
+
70
+ - uses: svenstaro/upload-release-action@v2
71
+ if: steps.release.outputs.release_created
72
+ with:
73
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
74
+ file: dist/*
75
+ tag: ${{ steps.release.outputs.tag_name }}
76
+ file_glob: true
77
+ make_latest: false
78
+
79
+ - name: Publish to PyPI
80
+ if: steps.release.outputs.release_created
81
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,32 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+
5
+ .venv/
6
+ venv/
7
+ .python-version-local
8
+
9
+ build/
10
+ dist/
11
+ *.egg-info/
12
+ .eggs/
13
+
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .ty_cache/
17
+ .coverage
18
+ .coverage.*
19
+ htmlcov/
20
+ .tox/
21
+
22
+ .env
23
+ .env.local
24
+ .env.*.local
25
+
26
+ .vscode/
27
+ .idea/
28
+ *.swp
29
+ *.swo
30
+ .DS_Store
31
+
32
+ uv.lock.local
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-05-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * scaffold chumak inference substrate ([37187d8](https://github.com/corriander/chumak/commit/37187d84c775f43e1cfe3922769031e265dd169b))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **deps:** bump langchain-openai from 1.2.1 to 1.2.2 ([fc7bb6a](https://github.com/corriander/chumak/commit/fc7bb6a486a049a79a81806c12703ce528443444))
@@ -0,0 +1,78 @@
1
+ # Contributing
2
+
3
+ ## Principles
4
+
5
+ - **Keep the substrate thin.** chumak coordinates handlers, profiles, and meta. It does not own prompts, domain concepts, or secret management โ€” those are consumer concerns.
6
+ - **Handlers are pluggable.** Adding a transport (LangChain, subprocess, โ€ฆ) is dropping a module under `src/chumak/handlers/`, extending `HandlerType`, and registering it in `HANDLER_REGISTRY`. No edits in `surface.py` or `profile.py`.
7
+ - **The library never reads env vars unprompted.** The profile env-overlay is the one exception, gated on the prefix the consumer passes to `ProfileLoader`.
8
+ - **Prefer fixtures over hand-rolled setup.** See `tests/conftest.py` (`write_profile`, `make_loader`, `stub_handler`).
9
+
10
+ ## Dev setup
11
+
12
+ ```bash
13
+ git clone <repo>
14
+ cd chumak
15
+ uv sync # core deps + dev tools (ruff, ty, pytest, pytest-mock, langchain-anthropic)
16
+ uv sync --extra openai # add when running the LangChain live integration test
17
+ ```
18
+
19
+ Python 3.12+ (no 3.13-only syntax is used โ€” keep it that way to stay broadly compatible with consumer projects).
20
+
21
+ ## Tests
22
+
23
+ ### Unit tests (default โ€” fast, hermetic, no network)
24
+
25
+ ```bash
26
+ uv run pytest
27
+ ```
28
+
29
+ These cover every code path. The `stub_handler` fixture swaps the LangChain handler in `HANDLER_REGISTRY` for a deterministic fake so surface/meta tests never touch a real LLM. Use it when writing new tests that need a known `HandlerResult`.
30
+
31
+ Profile-loader tests use `write_profile` to drop TOML files into a per-test tmp dir and `make_loader` to build a `ProfileLoader` pointed at it. Pass `env=` to inject a synthetic environment without mutating `os.environ`.
32
+
33
+ ### Integration test โ€” LangChain handler against an OpenAI-compat backend
34
+
35
+ Marker-gated, off by default. Exercises chumak end-to-end through a real LangChain `init_chat_model` call against any OpenAI-API-compatible server: llama.cpp, vLLM, LiteLLM, real OpenAI, etc. This is the test that proves the `openai:<model>` + `base_url` wiring still works after handler / langchain version bumps.
36
+
37
+ ```bash
38
+ uv sync --extra openai # install langchain-openai
39
+ uv run pytest --integration tests/test_langchain_live.py -v
40
+ ```
41
+
42
+ Defaults: `http://localhost:8080/v1`, model `gpt-3.5-turbo`, dummy API key. Override per-run via env vars:
43
+
44
+ ```bash
45
+ CHUMAK_TEST_OPENAI_URL=http://localhost:8000/v1 \
46
+ CHUMAK_TEST_OPENAI_MODEL=qwen2.5-7b-instruct \
47
+ uv run pytest --integration tests/test_langchain_live.py -v
48
+ ```
49
+
50
+ The test uses a tiny `ColourTag { colour: str, is_warm: bool }` schema โ€” small enough that any reasonable 7B-class instruct model handles it. If you add coverage for a new handler or option, add a sibling test under the same marker and document the env vars it needs here.
51
+
52
+ #### Why no subprocess live test?
53
+
54
+ The subprocess handler is harder to gate (each CLI has its own auth / install requirements). Cover it with unit tests against a temporary script (`tests/test_subprocess_handler.py` does this), and rely on downstream consumers for true end-to-end exercise.
55
+
56
+ ## Quality checks
57
+
58
+ Before opening a PR:
59
+
60
+ ```bash
61
+ uv run ruff check .
62
+ uv run ruff format .
63
+ uv run ty check src tests
64
+ ```
65
+
66
+ All three must pass clean. CI will run the same.
67
+
68
+ ## Adding a handler
69
+
70
+ 1. Module under `src/chumak/handlers/<name>.py` exporting a class with `execute(prompt, output_schema, profile) -> HandlerResult`.
71
+ 2. Add the discriminator to `HandlerType` in `src/chumak/handlers/types.py`.
72
+ 3. Register the class in `HANDLER_REGISTRY` (`src/chumak/handlers/__init__.py`).
73
+ 4. If the handler needs new profile fields, add them to `Profile` with the validator enforcing mutually-exclusive field sets per handler.
74
+ 5. Add unit tests via `stub_handler` for the surface side and direct handler tests for transport-specific quirks.
75
+
76
+ ## Adding a new profile field
77
+
78
+ Profile fields live in `src/chumak/profile.py`. The validator enforces which handler types may use which fields โ€” extend it when you add fields that only make sense for a specific handler. The env-overlay walker in `loader.py` picks them up automatically as long as they're declared on the `Profile` model.
chumak-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: chumak
3
+ Version: 0.1.0
4
+ Summary: Thin inference substrate: user-authored profiles, LangChain as a handler, optional provenance.
5
+ Author-email: Alex Corrie <alex.j.corrie@gmail.com>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: langchain-core>=0.3
8
+ Requires-Dist: langchain>=0.3
9
+ Requires-Dist: pydantic>=2.10
10
+ Provides-Extra: anthropic
11
+ Requires-Dist: langchain-anthropic>=0.3; extra == 'anthropic'
12
+ Provides-Extra: openai
13
+ Requires-Dist: langchain-openai>=1.2.2; extra == 'openai'
14
+ Description-Content-Type: text/markdown
15
+
16
+ # chumak
17
+
18
+ A thin **inference substrate** for Python projects: user-authored profiles, LangChain
19
+ as a handler, optional provenance/meta on every response.
20
+
21
+ > *Chumaks* (ะงัƒะผะฐะบะธ) were wandering Ukrainian salt-traders who traversed the steppe
22
+ > between distant places. They named the Milky Way after themselves โ€”
23
+ > *ะงัƒะผะฐั†ัŒะบะธะน ะจะปัั…*, the Chumaks' Way โ€” because they navigated by it.
24
+
25
+ ## What it is
26
+
27
+ A small library that abstracts away which LLM you're calling and how.
28
+
29
+ 1. Author **profiles** (TOML files) under the app's XDG config dir.
30
+ 2. Load a profile via `ProfileLoader` (with inheritance + env-var overrides)
31
+ 3. Call `infer(prompt=..., output_schema=..., profile=...)` and get back a validated
32
+ pydantic payload, normalised citations, and (optionally) a provenance `Meta` stamp.
33
+
34
+ Two built-in handlers:
35
+
36
+ - **`langchain`** โ€” uses `langchain.chat_models.init_chat_model(profile.model)` so a
37
+ single identifier (`anthropic:claude-opus-4-7`, `openai:gpt-5`, โ€ฆ) routes to the
38
+ right provider. Structured output, citations, and token usage all handled.
39
+ - **`subprocess`** โ€” shells out to a CLI (`claude --print`, `codex exec`, etc.).
40
+ Useful for prompt iteration via an existing, authorised tool.
41
+ Schema is injected into the prompt as JSON Schema; stdout is parsed and validated.
42
+
43
+ ## Profiles
44
+
45
+ Profiles are user-authored. chumak ships at most one generic example
46
+ (`anthropic-claude-opus-4-7` via the LangChain handler). Everything else is yours.
47
+
48
+ Profiles live in consumer app directory, e.g. `~/.config/<your-app>/chumak/profiles/`.
49
+ chumak does not impose a config dir; the app passes `search_paths` to `ProfileLoader`.
50
+
51
+ ### File shape
52
+
53
+ ```toml
54
+ # ~/.config/galops-vision/chumak/profiles/claude.toml
55
+ handler = "langchain"
56
+ model = "anthropic:claude-opus-4-7"
57
+ temperature = 0.0
58
+
59
+ [model_kwargs]
60
+ max_tokens = 4096
61
+ ```
62
+
63
+ ### Inheritance
64
+
65
+ ```toml
66
+ # claude-account-b.toml
67
+ extends = "claude"
68
+
69
+ [model_kwargs]
70
+ # api_key sourced from env โ€” see below
71
+ ```
72
+
73
+ ### Env-var overlay
74
+
75
+ Every field on a profile is overridable from the environment. chumak does not
76
+ provision special fields; the convention is uniform:
77
+
78
+ ```
79
+ {APP_PREFIX}_PROFILE_{PROFILE_NAME}_{FIELD_PATH}
80
+ ```
81
+
82
+ with `__` as the nested-field delimiter (single `_` stays inside field names):
83
+
84
+ ```sh
85
+ # top-level field
86
+ export MYAPP_VISION_PROFILE_CLAUDE_MODEL=anthropic:claude-haiku-4-5
87
+
88
+ # nested into model_kwargs
89
+ export MYAPP_VISION_PROFILE_CLAUDE_ACCOUNT_B_MODEL_KWARGS__API_KEY=sk-ant-...
90
+ ```
91
+
92
+ This means a profile file can be effectively empty on disk (just declaring the
93
+ profile's existence and maybe an `extends`), with all values supplied by the
94
+ environment. You decide which fields are sensitive and never touch disk.
95
+
96
+ ## Usage
97
+
98
+ ```python
99
+ from pathlib import Path
100
+ from chumak import ProfileLoader, infer
101
+
102
+ loader = ProfileLoader(
103
+ search_paths=[Path.home() / ".config/my-app/chumak/profiles"],
104
+ env_prefix="MYAPP",
105
+ )
106
+ loader.names() # -> ["claude", "claude-creative", ...]
107
+ profile = loader.load("claude")
108
+
109
+ from pydantic import BaseModel
110
+
111
+ class AnneSchema(BaseModel):
112
+ title: str
113
+ value: int
114
+
115
+ result = infer(
116
+ prompt="Extract title and value from this text: ...",
117
+ output_schema=AnneSchema,
118
+ profile=profile,
119
+ )
120
+ result.payload # -> MissionTitle(title=..., bounty=...)
121
+ result.citations # -> [Citation, ...] (if the model supplied any)
122
+ result.meta # -> Meta with cost, generated_at, model identity
123
+ ```
124
+
125
+ ### With provenance
126
+
127
+ ```python
128
+ from chumak import Provenance
129
+
130
+ result = infer(
131
+ prompt="...",
132
+ output_schema=AnneSchema,
133
+ profile=profile,
134
+ provenance=Provenance(
135
+ artefact_type="model@v1",
136
+ artefact_id="artifact-type:2026-05-20T12:34:56Z",
137
+ ),
138
+ )
139
+ result.meta.artefact_type # "mission_title@v1"
140
+ result.meta.derived_from # [...]
141
+ ```
142
+
143
+ ## Design notes
144
+
145
+ - **No domain knowledge**: chumak carries no built-in prompts, no role concepts
146
+ (tactical/narrator etc. โ€” that's an app concern; just name your profile).
147
+ - **LangChain is a handler, not the spine**: subprocess CLIs are first-class.
148
+ - **Provenance is opt-in**: omit `provenance=` and `meta.artefact_type` is `None`.
149
+ - **The lib never reads env directly** for its own settings. The env overlay
150
+ for profiles is a deliberate, scoped exception, gated on the prefix the
151
+ consumer passes in.
152
+
153
+ ## Tooling
154
+
155
+ uv, Python 3.12+, ruff, ty, pytest. See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, the integration test, and quality-check commands.
chumak-0.1.0/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # chumak
2
+
3
+ A thin **inference substrate** for Python projects: user-authored profiles, LangChain
4
+ as a handler, optional provenance/meta on every response.
5
+
6
+ > *Chumaks* (ะงัƒะผะฐะบะธ) were wandering Ukrainian salt-traders who traversed the steppe
7
+ > between distant places. They named the Milky Way after themselves โ€”
8
+ > *ะงัƒะผะฐั†ัŒะบะธะน ะจะปัั…*, the Chumaks' Way โ€” because they navigated by it.
9
+
10
+ ## What it is
11
+
12
+ A small library that abstracts away which LLM you're calling and how.
13
+
14
+ 1. Author **profiles** (TOML files) under the app's XDG config dir.
15
+ 2. Load a profile via `ProfileLoader` (with inheritance + env-var overrides)
16
+ 3. Call `infer(prompt=..., output_schema=..., profile=...)` and get back a validated
17
+ pydantic payload, normalised citations, and (optionally) a provenance `Meta` stamp.
18
+
19
+ Two built-in handlers:
20
+
21
+ - **`langchain`** โ€” uses `langchain.chat_models.init_chat_model(profile.model)` so a
22
+ single identifier (`anthropic:claude-opus-4-7`, `openai:gpt-5`, โ€ฆ) routes to the
23
+ right provider. Structured output, citations, and token usage all handled.
24
+ - **`subprocess`** โ€” shells out to a CLI (`claude --print`, `codex exec`, etc.).
25
+ Useful for prompt iteration via an existing, authorised tool.
26
+ Schema is injected into the prompt as JSON Schema; stdout is parsed and validated.
27
+
28
+ ## Profiles
29
+
30
+ Profiles are user-authored. chumak ships at most one generic example
31
+ (`anthropic-claude-opus-4-7` via the LangChain handler). Everything else is yours.
32
+
33
+ Profiles live in consumer app directory, e.g. `~/.config/<your-app>/chumak/profiles/`.
34
+ chumak does not impose a config dir; the app passes `search_paths` to `ProfileLoader`.
35
+
36
+ ### File shape
37
+
38
+ ```toml
39
+ # ~/.config/galops-vision/chumak/profiles/claude.toml
40
+ handler = "langchain"
41
+ model = "anthropic:claude-opus-4-7"
42
+ temperature = 0.0
43
+
44
+ [model_kwargs]
45
+ max_tokens = 4096
46
+ ```
47
+
48
+ ### Inheritance
49
+
50
+ ```toml
51
+ # claude-account-b.toml
52
+ extends = "claude"
53
+
54
+ [model_kwargs]
55
+ # api_key sourced from env โ€” see below
56
+ ```
57
+
58
+ ### Env-var overlay
59
+
60
+ Every field on a profile is overridable from the environment. chumak does not
61
+ provision special fields; the convention is uniform:
62
+
63
+ ```
64
+ {APP_PREFIX}_PROFILE_{PROFILE_NAME}_{FIELD_PATH}
65
+ ```
66
+
67
+ with `__` as the nested-field delimiter (single `_` stays inside field names):
68
+
69
+ ```sh
70
+ # top-level field
71
+ export MYAPP_VISION_PROFILE_CLAUDE_MODEL=anthropic:claude-haiku-4-5
72
+
73
+ # nested into model_kwargs
74
+ export MYAPP_VISION_PROFILE_CLAUDE_ACCOUNT_B_MODEL_KWARGS__API_KEY=sk-ant-...
75
+ ```
76
+
77
+ This means a profile file can be effectively empty on disk (just declaring the
78
+ profile's existence and maybe an `extends`), with all values supplied by the
79
+ environment. You decide which fields are sensitive and never touch disk.
80
+
81
+ ## Usage
82
+
83
+ ```python
84
+ from pathlib import Path
85
+ from chumak import ProfileLoader, infer
86
+
87
+ loader = ProfileLoader(
88
+ search_paths=[Path.home() / ".config/my-app/chumak/profiles"],
89
+ env_prefix="MYAPP",
90
+ )
91
+ loader.names() # -> ["claude", "claude-creative", ...]
92
+ profile = loader.load("claude")
93
+
94
+ from pydantic import BaseModel
95
+
96
+ class AnneSchema(BaseModel):
97
+ title: str
98
+ value: int
99
+
100
+ result = infer(
101
+ prompt="Extract title and value from this text: ...",
102
+ output_schema=AnneSchema,
103
+ profile=profile,
104
+ )
105
+ result.payload # -> MissionTitle(title=..., bounty=...)
106
+ result.citations # -> [Citation, ...] (if the model supplied any)
107
+ result.meta # -> Meta with cost, generated_at, model identity
108
+ ```
109
+
110
+ ### With provenance
111
+
112
+ ```python
113
+ from chumak import Provenance
114
+
115
+ result = infer(
116
+ prompt="...",
117
+ output_schema=AnneSchema,
118
+ profile=profile,
119
+ provenance=Provenance(
120
+ artefact_type="model@v1",
121
+ artefact_id="artifact-type:2026-05-20T12:34:56Z",
122
+ ),
123
+ )
124
+ result.meta.artefact_type # "mission_title@v1"
125
+ result.meta.derived_from # [...]
126
+ ```
127
+
128
+ ## Design notes
129
+
130
+ - **No domain knowledge**: chumak carries no built-in prompts, no role concepts
131
+ (tactical/narrator etc. โ€” that's an app concern; just name your profile).
132
+ - **LangChain is a handler, not the spine**: subprocess CLIs are first-class.
133
+ - **Provenance is opt-in**: omit `provenance=` and `meta.artefact_type` is `None`.
134
+ - **The lib never reads env directly** for its own settings. The env overlay
135
+ for profiles is a deliberate, scoped exception, gated on the prefix the
136
+ consumer passes in.
137
+
138
+ ## Tooling
139
+
140
+ uv, Python 3.12+, ruff, ty, pytest. See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, the integration test, and quality-check commands.
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "chumak"
3
+ version = "0.1.0"
4
+ description = "Thin inference substrate: user-authored profiles, LangChain as a handler, optional provenance."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [
8
+ { name = "Alex Corrie", email = "alex.j.corrie@gmail.com" },
9
+ ]
10
+ dependencies = [
11
+ "pydantic>=2.10",
12
+ "langchain>=0.3",
13
+ "langchain-core>=0.3",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ anthropic = ["langchain-anthropic>=0.3"]
18
+ openai = ["langchain-openai>=1.2.2"]
19
+
20
+ [dependency-groups]
21
+ dev = [
22
+ "pytest>=8",
23
+ "pytest-mock>=3.14",
24
+ "ruff>=0.15.14",
25
+ "ty>=0.0.21",
26
+ "langchain-anthropic>=0.3",
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [tool.hatch.build]
34
+ dev-mode-dirs = ["src"]
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/chumak"]
38
+
39
+ [tool.ruff]
40
+ src = ["src"]
41
+ line-length = 99
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
45
+
46
+ [tool.ty.environment]
47
+ extra-paths = ["src"]
48
+
49
+ [tool.pytest.ini_options]
50
+ testpaths = ["tests"]
51
+ markers = [
52
+ "integration: requires a live inference backend (use --integration to enable)",
53
+ ]
@@ -0,0 +1,48 @@
1
+ """chumak โ€” thin inference substrate.
2
+
3
+ Public surface in `surface.infer`. Profile-loading in `loader.ProfileLoader`.
4
+ Response types in `response`. Handlers are pluggable via `handlers.HANDLER_REGISTRY`.
5
+
6
+ The library is subject-agnostic and carries no domain knowledge: no
7
+ built-in prompts, no role names, no per-domain artefact types. Consumers
8
+ build those on top.
9
+ """
10
+
11
+ from chumak.handlers import HANDLER_REGISTRY, Handler, HandlerType, PromptDelivery
12
+ from chumak.loader import (
13
+ ProfileCycleError,
14
+ ProfileLoader,
15
+ ProfileLoaderError,
16
+ ProfileNotFoundError,
17
+ )
18
+ from chumak.profile import Profile
19
+ from chumak.response import (
20
+ ArtefactRef,
21
+ Citation,
22
+ Cost,
23
+ InferResult,
24
+ Meta,
25
+ ProducedBy,
26
+ Provenance,
27
+ )
28
+ from chumak.surface import infer
29
+
30
+ __all__ = [
31
+ "HANDLER_REGISTRY",
32
+ "ArtefactRef",
33
+ "Citation",
34
+ "Cost",
35
+ "Handler",
36
+ "HandlerType",
37
+ "InferResult",
38
+ "Meta",
39
+ "ProducedBy",
40
+ "Profile",
41
+ "ProfileCycleError",
42
+ "ProfileLoader",
43
+ "ProfileLoaderError",
44
+ "ProfileNotFoundError",
45
+ "PromptDelivery",
46
+ "Provenance",
47
+ "infer",
48
+ ]
@@ -0,0 +1,27 @@
1
+ """Handlers package.
2
+
3
+ Owns the `HandlerType` discriminator and the `HANDLER_REGISTRY` mapping each
4
+ `HandlerType` to its concrete handler class. Adding a new handler type means
5
+ dropping a module here, extending `HandlerType` in `types.py`, and adding
6
+ the class to the registry below โ€” no edits in `surface.py` or `profile.py`.
7
+ """
8
+
9
+ from chumak.handlers.base import Handler, HandlerResult
10
+ from chumak.handlers.langchain import LangChainHandler
11
+ from chumak.handlers.subprocess import SubprocessHandler
12
+ from chumak.handlers.types import HandlerType, PromptDelivery
13
+
14
+ HANDLER_REGISTRY: dict[HandlerType, type[Handler]] = {
15
+ HandlerType.LANGCHAIN: LangChainHandler,
16
+ HandlerType.SUBPROCESS: SubprocessHandler,
17
+ }
18
+
19
+ __all__ = [
20
+ "HANDLER_REGISTRY",
21
+ "Handler",
22
+ "HandlerResult",
23
+ "HandlerType",
24
+ "LangChainHandler",
25
+ "PromptDelivery",
26
+ "SubprocessHandler",
27
+ ]