welt-io-langgraph 0.3.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,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.so
4
+ dist/
5
+ .venv*/
6
+ .coverage
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ src/welt_io_langgraph/_version.py
10
+ .claude/
@@ -0,0 +1,48 @@
1
+ # Contributing
2
+
3
+ We welcome contributions! Please follow these guidelines when submitting pull requests.
4
+
5
+ ## Recommended setup
6
+
7
+ After cloning, enable the git hooks:
8
+
9
+ ```bash
10
+ mise run enable-git-hooks
11
+ ```
12
+
13
+ This wires `./validate.sh` to `pre-commit`, plus DCO and Conventional Commits checks to `commit-msg`, so the requirements below run automatically on each commit.
14
+
15
+ ## Before Submitting a PR
16
+
17
+ ### 1. Run Validation
18
+
19
+ ```bash
20
+ ./validate.sh
21
+ ```
22
+
23
+ Runs the project's validation pipeline.
24
+
25
+ ### 2. Sign Commits with DCO
26
+
27
+ All commits must be signed with DCO. Use `git commit -s` to sign your commits.
28
+
29
+ - Use your real name (no pseudonyms)
30
+ - All commits in a PR must be signed
31
+ - If the DCO check fails, see the check details page for instructions on fixing unsigned commits
32
+
33
+ For more about DCO: https://developercertificate.org/
34
+
35
+ ### 3. Follow Conventional Commits
36
+
37
+ Commit subjects (first line) must follow `<type>(<scope>)?!?: <description>`. Allowed types: `feat`, `fix`, `chore`, `docs`. Append `!` for breaking changes.
38
+
39
+ ```
40
+ feat: add dark-mode toggle
41
+ fix(auth): handle expired token
42
+ chore: bump dependencies
43
+ feat!: drop legacy API
44
+ ```
45
+
46
+ Other types (`refactor`, `ci`, `test`, etc.) are intentionally rejected — use `chore:` instead.
47
+
48
+ For more about Conventional Commits: https://www.conventionalcommits.org/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Takashi Iwamoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: welt-io-langgraph
3
+ Version: 0.3.0
4
+ Summary: The LangGraph (Python) adapter for Welt's wire contract
5
+ Project-URL: Repository, https://github.com/iwamot/welt-io-langgraph
6
+ Project-URL: Issues, https://github.com/iwamot/welt-io-langgraph/issues
7
+ Author: Takashi Iwamoto
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agentcore,bedrock,langgraph,slack,welt
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+
24
+ # welt-io-langgraph
25
+
26
+ [![pypi](https://img.shields.io/pypi/v/welt-io-langgraph.svg)](https://pypi.org/project/welt-io-langgraph/)
27
+ [![python](https://img.shields.io/pypi/pyversions/welt-io-langgraph.svg)](https://pypi.org/project/welt-io-langgraph/)
28
+
29
+ The [LangGraph](https://docs.langchain.com/oss/python/langgraph/overview) (Python) adapter for [Welt](https://github.com/iwamot/welt)'s wire contract.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ uv add welt-io-langgraph
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ See [`examples/agent`](examples/agent) — the smallest complete agent built on this package (text streaming, tool use, file output, file input, and human-approval tools). The sections below explain the adapters it wires in.
40
+
41
+ ## API
42
+
43
+ The wire between Welt and the agent is JSON, specified by [Welt's wire contract](https://github.com/iwamot/welt/blob/main/docs/wire.md) — plain LangGraph values do not fit it in either direction. Two functions adapt the inbound payload, three the outbound stream. The adapters target LangGraph 1.x and LangChain 1.x, whose messages carry [standard content blocks](https://docs.langchain.com/oss/python/langchain/messages).
44
+
45
+ ### Inbound
46
+
47
+ #### `decode_messages(messages)`
48
+
49
+ Turns Welt's Converse-shaped messages — built from the Slack thread, file bytes base64-encoded — into role/content message dicts that feed the graph input (`{"messages": decoded}`) as-is:
50
+
51
+ | Converse block | Standard content block |
52
+ |---|---|
53
+ | Text | Text |
54
+ | Image | Image |
55
+ | Document | File (the document's name carried as `filename`) |
56
+ | Video | Video |
57
+
58
+ Each file-carrying block gets the media type LangChain models expect in place of the Converse format token, and the base64 data stays base64 — standard content blocks need no decoding. Malformed entries are skipped.
59
+
60
+ #### `decode_interrupt_responses(responses)`
61
+
62
+ Turns Welt's resume payload — a mapping of interrupt id to the answer a human chose — into the mapping `Command(resume=...)` takes, answering every pending interrupt at once:
63
+
64
+ ```python
65
+ agent.astream(
66
+ Command(resume=decode_interrupt_responses(payload["interrupt_responses"])),
67
+ config,
68
+ stream_mode=["messages", "updates", "custom"],
69
+ )
70
+ ```
71
+
72
+ The interrupt ids are LangGraph's own, as emitted by `renderable_events`; the config must point at the interrupted thread, which the host app stashes when an interrupt event goes by (see the [example agent](examples/agent)).
73
+
74
+ ### Outbound
75
+
76
+ #### `renderable_events(stream)`
77
+
78
+ Reduces the `(mode, payload)` items of `astream(..., stream_mode=["messages", "updates", "custom"])` — whose values Welt does not render — to the events Welt renders:
79
+
80
+ | LangGraph emits | On the wire | In the Slack thread |
81
+ |---|---|---|
82
+ | Token deltas | `data` | The streamed reply |
83
+ | Tool calls and tool messages | `current_tool_use` / `tool_result` | "Using tool" indicators (tool output stays off the wire) |
84
+ | Image / file / video content blocks a tool or the model returns | `file` | An uploaded file ([size limits](https://github.com/iwamot/welt/blob/main/docs/wire.md#limits)) |
85
+ | Pending [interrupts](https://docs.langchain.com/oss/python/langgraph/interrupts) | `interrupt` | Buttons and/or a text field |
86
+
87
+ A run that stops for human input ends its stream with one `interrupt` event per pending interrupt; agents that do not use interrupts see no change.
88
+
89
+ #### `file_event(name, data)`
90
+
91
+ Builds the same `file` event from a filename and raw bytes, for attaching arbitrary files of your own. Yield it from the host app alongside the reduced stream, or pass it to LangGraph's custom stream writer to attach a file from inside a tool — `renderable_events` passes it through by itself:
92
+
93
+ ```python
94
+ writer = get_stream_writer()
95
+ writer(file_event("report.csv", csv_bytes))
96
+ ```
97
+
98
+ #### `interrupt_reason(message, options=..., input=...)`
99
+
100
+ Builds the structured reason Welt renders as a message with the specified widgets — choice buttons (`options`), a free-text field (`input`), or both. The specs are [the wire's own shapes](https://github.com/iwamot/welt/blob/main/docs/wire.md#interrupt); omitted fields keep Welt's defaults, and a typo becomes an immediate `ValueError` instead of a silent fallback to Welt's default rendering:
101
+
102
+ ```python
103
+ answer = interrupt(
104
+ interrupt_reason(
105
+ "Deploy to prod?",
106
+ [
107
+ {"value": "y", "label": "Deploy", "style": "primary"},
108
+ {"value": "n", "label": "Cancel"},
109
+ ],
110
+ input={"label": "Or tell me what to do instead"},
111
+ )
112
+ )
113
+ ```
114
+
115
+ ## Working with interrupts
116
+
117
+ [Welt's Interrupts doc](https://github.com/iwamot/welt/blob/main/docs/interrupts.md) covers the Slack side: how each reason renders, who can answer, multiple questions, and expiry. On the LangGraph side:
118
+
119
+ - **`interrupt` needs a checkpointer**, even though the conversation history lives in Slack — pausing and resuming run through checkpoints. An in-memory checkpointer works on AgentCore Runtime, where each session keeps its own microVM.
120
+ - **Start each conversation turn on a fresh thread.** Welt sends the whole Slack thread every turn by default, so letting the checkpointer stack turns into its own history would double the conversation. Resume alone reuses the interrupted thread's config. (An agent that keeps its own history instead sets `AGENT_MANAGES_HISTORY` on the Welt side.)
121
+ - **A plain interrupt value renders too.** Any non-structured value — `interrupt("Deploy to prod?")` — becomes a question with Welt's default **Approve** / **Deny** buttons, whose answers arrive as `y` / `n`.
122
+ - **Code before `interrupt` runs again on resume.** LangGraph re-executes the interrupted node (or tool) from its start, so wrap whatever precedes an interrupt and must not run twice — side effects, or work that must match what the human approved — in a [LangGraph task](https://docs.langchain.com/oss/python/langgraph/durable-execution): a completed task is not re-executed on resume; its saved result is reused. The [example agent](examples/agent)'s `sample_draft_report` shows the pattern.
123
+
124
+ ## Supported Versions
125
+
126
+ Welt releases first; welt-io-langgraph follows, mirroring the minor version. While both are 0.x, a welt-io-langgraph 0.Y release supports Welt v0.Y — other combinations may work, but come with no guarantee.
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,107 @@
1
+ # welt-io-langgraph
2
+
3
+ [![pypi](https://img.shields.io/pypi/v/welt-io-langgraph.svg)](https://pypi.org/project/welt-io-langgraph/)
4
+ [![python](https://img.shields.io/pypi/pyversions/welt-io-langgraph.svg)](https://pypi.org/project/welt-io-langgraph/)
5
+
6
+ The [LangGraph](https://docs.langchain.com/oss/python/langgraph/overview) (Python) adapter for [Welt](https://github.com/iwamot/welt)'s wire contract.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ uv add welt-io-langgraph
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ See [`examples/agent`](examples/agent) — the smallest complete agent built on this package (text streaming, tool use, file output, file input, and human-approval tools). The sections below explain the adapters it wires in.
17
+
18
+ ## API
19
+
20
+ The wire between Welt and the agent is JSON, specified by [Welt's wire contract](https://github.com/iwamot/welt/blob/main/docs/wire.md) — plain LangGraph values do not fit it in either direction. Two functions adapt the inbound payload, three the outbound stream. The adapters target LangGraph 1.x and LangChain 1.x, whose messages carry [standard content blocks](https://docs.langchain.com/oss/python/langchain/messages).
21
+
22
+ ### Inbound
23
+
24
+ #### `decode_messages(messages)`
25
+
26
+ Turns Welt's Converse-shaped messages — built from the Slack thread, file bytes base64-encoded — into role/content message dicts that feed the graph input (`{"messages": decoded}`) as-is:
27
+
28
+ | Converse block | Standard content block |
29
+ |---|---|
30
+ | Text | Text |
31
+ | Image | Image |
32
+ | Document | File (the document's name carried as `filename`) |
33
+ | Video | Video |
34
+
35
+ Each file-carrying block gets the media type LangChain models expect in place of the Converse format token, and the base64 data stays base64 — standard content blocks need no decoding. Malformed entries are skipped.
36
+
37
+ #### `decode_interrupt_responses(responses)`
38
+
39
+ Turns Welt's resume payload — a mapping of interrupt id to the answer a human chose — into the mapping `Command(resume=...)` takes, answering every pending interrupt at once:
40
+
41
+ ```python
42
+ agent.astream(
43
+ Command(resume=decode_interrupt_responses(payload["interrupt_responses"])),
44
+ config,
45
+ stream_mode=["messages", "updates", "custom"],
46
+ )
47
+ ```
48
+
49
+ The interrupt ids are LangGraph's own, as emitted by `renderable_events`; the config must point at the interrupted thread, which the host app stashes when an interrupt event goes by (see the [example agent](examples/agent)).
50
+
51
+ ### Outbound
52
+
53
+ #### `renderable_events(stream)`
54
+
55
+ Reduces the `(mode, payload)` items of `astream(..., stream_mode=["messages", "updates", "custom"])` — whose values Welt does not render — to the events Welt renders:
56
+
57
+ | LangGraph emits | On the wire | In the Slack thread |
58
+ |---|---|---|
59
+ | Token deltas | `data` | The streamed reply |
60
+ | Tool calls and tool messages | `current_tool_use` / `tool_result` | "Using tool" indicators (tool output stays off the wire) |
61
+ | Image / file / video content blocks a tool or the model returns | `file` | An uploaded file ([size limits](https://github.com/iwamot/welt/blob/main/docs/wire.md#limits)) |
62
+ | Pending [interrupts](https://docs.langchain.com/oss/python/langgraph/interrupts) | `interrupt` | Buttons and/or a text field |
63
+
64
+ A run that stops for human input ends its stream with one `interrupt` event per pending interrupt; agents that do not use interrupts see no change.
65
+
66
+ #### `file_event(name, data)`
67
+
68
+ Builds the same `file` event from a filename and raw bytes, for attaching arbitrary files of your own. Yield it from the host app alongside the reduced stream, or pass it to LangGraph's custom stream writer to attach a file from inside a tool — `renderable_events` passes it through by itself:
69
+
70
+ ```python
71
+ writer = get_stream_writer()
72
+ writer(file_event("report.csv", csv_bytes))
73
+ ```
74
+
75
+ #### `interrupt_reason(message, options=..., input=...)`
76
+
77
+ Builds the structured reason Welt renders as a message with the specified widgets — choice buttons (`options`), a free-text field (`input`), or both. The specs are [the wire's own shapes](https://github.com/iwamot/welt/blob/main/docs/wire.md#interrupt); omitted fields keep Welt's defaults, and a typo becomes an immediate `ValueError` instead of a silent fallback to Welt's default rendering:
78
+
79
+ ```python
80
+ answer = interrupt(
81
+ interrupt_reason(
82
+ "Deploy to prod?",
83
+ [
84
+ {"value": "y", "label": "Deploy", "style": "primary"},
85
+ {"value": "n", "label": "Cancel"},
86
+ ],
87
+ input={"label": "Or tell me what to do instead"},
88
+ )
89
+ )
90
+ ```
91
+
92
+ ## Working with interrupts
93
+
94
+ [Welt's Interrupts doc](https://github.com/iwamot/welt/blob/main/docs/interrupts.md) covers the Slack side: how each reason renders, who can answer, multiple questions, and expiry. On the LangGraph side:
95
+
96
+ - **`interrupt` needs a checkpointer**, even though the conversation history lives in Slack — pausing and resuming run through checkpoints. An in-memory checkpointer works on AgentCore Runtime, where each session keeps its own microVM.
97
+ - **Start each conversation turn on a fresh thread.** Welt sends the whole Slack thread every turn by default, so letting the checkpointer stack turns into its own history would double the conversation. Resume alone reuses the interrupted thread's config. (An agent that keeps its own history instead sets `AGENT_MANAGES_HISTORY` on the Welt side.)
98
+ - **A plain interrupt value renders too.** Any non-structured value — `interrupt("Deploy to prod?")` — becomes a question with Welt's default **Approve** / **Deny** buttons, whose answers arrive as `y` / `n`.
99
+ - **Code before `interrupt` runs again on resume.** LangGraph re-executes the interrupted node (or tool) from its start, so wrap whatever precedes an interrupt and must not run twice — side effects, or work that must match what the human approved — in a [LangGraph task](https://docs.langchain.com/oss/python/langgraph/durable-execution): a completed task is not re-executed on resume; its saved result is reused. The [example agent](examples/agent)'s `sample_draft_report` shows the pattern.
100
+
101
+ ## Supported Versions
102
+
103
+ Welt releases first; welt-io-langgraph follows, mirroring the minor version. While both are 0.x, a welt-io-langgraph 0.Y release supports Welt v0.Y — other combinations may work, but come with no guarantee.
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,9 @@
1
+ # Security Policy
2
+
3
+ ## Disclaimer
4
+
5
+ This software is provided "as is" without warranty. Security issues will be addressed on a best-effort basis.
6
+
7
+ ## Reporting a Vulnerability
8
+
9
+ Please report security vulnerabilities through GitHub's Security Advisory "Report a Vulnerability" feature.
@@ -0,0 +1,178 @@
1
+ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html
2
+
3
+ [[tools."aqua:astral-sh/ruff"]]
4
+ version = "0.15.22"
5
+ backend = "aqua:astral-sh/ruff"
6
+
7
+ [tools."aqua:astral-sh/ruff"."platforms.linux-arm64"]
8
+ checksum = "sha256:88feb2dc2fafb92482185201f4d483134fbaab10370c79512d3dd372f1d3f28b"
9
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-aarch64-unknown-linux-musl.tar.gz"
10
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479313917"
11
+ provenance = "github-attestations"
12
+
13
+ [tools."aqua:astral-sh/ruff"."platforms.linux-arm64-musl"]
14
+ checksum = "sha256:88feb2dc2fafb92482185201f4d483134fbaab10370c79512d3dd372f1d3f28b"
15
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-aarch64-unknown-linux-musl.tar.gz"
16
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479313917"
17
+ provenance = "github-attestations"
18
+
19
+ [tools."aqua:astral-sh/ruff"."platforms.linux-x64"]
20
+ checksum = "sha256:853462df618fb9f156a9f04d9d899d9e5a9e1a43fbffaec72df4bb159348e71b"
21
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-x86_64-unknown-linux-musl.tar.gz"
22
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479314045"
23
+ provenance = "github-attestations"
24
+
25
+ [tools."aqua:astral-sh/ruff"."platforms.linux-x64-musl"]
26
+ checksum = "sha256:853462df618fb9f156a9f04d9d899d9e5a9e1a43fbffaec72df4bb159348e71b"
27
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-x86_64-unknown-linux-musl.tar.gz"
28
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479314045"
29
+ provenance = "github-attestations"
30
+
31
+ [tools."aqua:astral-sh/ruff"."platforms.macos-arm64"]
32
+ checksum = "sha256:a2881af26fd1d19f4932c4ddf1e70b4e0efcf48513c5dae082564e03f0b467a3"
33
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-aarch64-apple-darwin.tar.gz"
34
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479313898"
35
+ provenance = "github-attestations"
36
+
37
+ [tools."aqua:astral-sh/ruff"."platforms.macos-x64"]
38
+ checksum = "sha256:687a9ceb88ab85dab061026d5017218225a481121b1a40862cc8f92b56f18090"
39
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-x86_64-apple-darwin.tar.gz"
40
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479314007"
41
+ provenance = "github-attestations"
42
+
43
+ [tools."aqua:astral-sh/ruff"."platforms.windows-x64"]
44
+ checksum = "sha256:6e5419593984941405e9add902e89c6ea4af87d97919ac5ef82e1bc4e43bbd8d"
45
+ url = "https://github.com/astral-sh/ruff/releases/download/0.15.22/ruff-x86_64-pc-windows-msvc.zip"
46
+ url_api = "https://api.github.com/repos/astral-sh/ruff/releases/assets/479314019"
47
+ provenance = "github-attestations"
48
+
49
+ [[tools."aqua:astral-sh/ty"]]
50
+ version = "0.0.63"
51
+ backend = "aqua:astral-sh/ty"
52
+
53
+ [tools."aqua:astral-sh/ty"."platforms.linux-arm64"]
54
+ checksum = "sha256:e44b9da55ec237a8e8c53991c9df56603d2948d721c9710ec430e4112e2fbe6e"
55
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-aarch64-unknown-linux-musl.tar.gz"
56
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124536"
57
+ provenance = "github-attestations"
58
+
59
+ [tools."aqua:astral-sh/ty"."platforms.linux-arm64-musl"]
60
+ checksum = "sha256:e44b9da55ec237a8e8c53991c9df56603d2948d721c9710ec430e4112e2fbe6e"
61
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-aarch64-unknown-linux-musl.tar.gz"
62
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124536"
63
+ provenance = "github-attestations"
64
+
65
+ [tools."aqua:astral-sh/ty"."platforms.linux-x64"]
66
+ checksum = "sha256:363c340abc2490f3cfd7d9b829ff89cd8e79a41f8d9db911bf78dae48c36c436"
67
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-x86_64-unknown-linux-musl.tar.gz"
68
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124639"
69
+ provenance = "github-attestations"
70
+
71
+ [tools."aqua:astral-sh/ty"."platforms.linux-x64-musl"]
72
+ checksum = "sha256:363c340abc2490f3cfd7d9b829ff89cd8e79a41f8d9db911bf78dae48c36c436"
73
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-x86_64-unknown-linux-musl.tar.gz"
74
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124639"
75
+ provenance = "github-attestations"
76
+
77
+ [tools."aqua:astral-sh/ty"."platforms.macos-arm64"]
78
+ checksum = "sha256:30d09000d5f2524769816c57f85e19505676d23618aad6a43e046d5c50510988"
79
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-aarch64-apple-darwin.tar.gz"
80
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124513"
81
+ provenance = "github-attestations"
82
+
83
+ [tools."aqua:astral-sh/ty"."platforms.macos-x64"]
84
+ checksum = "sha256:5a18373c8bd6894be5f595a2cbefa4e72b7d5b95cfe2c2f075024a949064bba9"
85
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-x86_64-apple-darwin.tar.gz"
86
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124618"
87
+ provenance = "github-attestations"
88
+
89
+ [tools."aqua:astral-sh/ty"."platforms.windows-x64"]
90
+ checksum = "sha256:f9476788c8c27c328bff1b4691dbb1aa6f1591e103e630caffb9ccb70648a1a0"
91
+ url = "https://github.com/astral-sh/ty/releases/download/0.0.63/ty-x86_64-pc-windows-msvc.zip"
92
+ url_api = "https://api.github.com/repos/astral-sh/ty/releases/assets/487124623"
93
+ provenance = "github-attestations"
94
+
95
+ [[tools."aqua:astral-sh/uv"]]
96
+ version = "0.11.31"
97
+ backend = "aqua:astral-sh/uv"
98
+
99
+ [tools."aqua:astral-sh/uv"."platforms.linux-arm64"]
100
+ checksum = "sha256:49cb5ffce40cc9c85355caa8104f7b61c40a8daac7334f4bc841cad1a7bb359e"
101
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-aarch64-unknown-linux-musl.tar.gz"
102
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371019"
103
+ provenance = "github-attestations"
104
+
105
+ [tools."aqua:astral-sh/uv"."platforms.linux-arm64-musl"]
106
+ checksum = "sha256:49cb5ffce40cc9c85355caa8104f7b61c40a8daac7334f4bc841cad1a7bb359e"
107
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-aarch64-unknown-linux-musl.tar.gz"
108
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371019"
109
+ provenance = "github-attestations"
110
+
111
+ [tools."aqua:astral-sh/uv"."platforms.linux-x64"]
112
+ checksum = "sha256:89048b7e30a6c459fa7e8f2e91cfdc413dc004dcbddc6c2af5e09df123e3246d"
113
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-x86_64-unknown-linux-musl.tar.gz"
114
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371115"
115
+ provenance = "github-attestations"
116
+
117
+ [tools."aqua:astral-sh/uv"."platforms.linux-x64-musl"]
118
+ checksum = "sha256:89048b7e30a6c459fa7e8f2e91cfdc413dc004dcbddc6c2af5e09df123e3246d"
119
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-x86_64-unknown-linux-musl.tar.gz"
120
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371115"
121
+ provenance = "github-attestations"
122
+
123
+ [tools."aqua:astral-sh/uv"."platforms.macos-arm64"]
124
+ checksum = "sha256:b2b93e82a6786f9c7cb89fd4ca0e859a147b292ae8f6f95784f9742f0efec39e"
125
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-aarch64-apple-darwin.tar.gz"
126
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371001"
127
+ provenance = "github-attestations"
128
+
129
+ [tools."aqua:astral-sh/uv"."platforms.macos-x64"]
130
+ checksum = "sha256:33ee6bd62b57fcd77a499deb54e4432dc1e1a2f3d34930ba987ad8b43f9c7bc7"
131
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-x86_64-apple-darwin.tar.gz"
132
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371098"
133
+ provenance = "github-attestations"
134
+
135
+ [tools."aqua:astral-sh/uv"."platforms.windows-x64"]
136
+ checksum = "sha256:410c2fd3126ff621c9450a21cfc200002c7540dc48d130069a8f619cdb0a811b"
137
+ url = "https://github.com/astral-sh/uv/releases/download/0.11.31/uv-x86_64-pc-windows-msvc.zip"
138
+ url_api = "https://api.github.com/repos/astral-sh/uv/releases/assets/485371099"
139
+ provenance = "github-attestations"
140
+
141
+ [[tools.python]]
142
+ version = "3.14.6"
143
+ backend = "core:python"
144
+
145
+ [tools.python."platforms.linux-arm64"]
146
+ checksum = "sha256:c2a2caa03aa9beb77a5bbb5a009c8fe77b96a56925da2dba18095237e30faf04"
147
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz"
148
+ provenance = "github-attestations"
149
+
150
+ [tools.python."platforms.linux-arm64-musl"]
151
+ checksum = "sha256:ff7da86be2a3d7fdf0124da7b408ad2de3c7ca8d6c19e6c449ec2cddedd01a8b"
152
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-aarch64-unknown-linux-musl-install_only_stripped.tar.gz"
153
+ provenance = "github-attestations"
154
+
155
+ [tools.python."platforms.linux-x64"]
156
+ checksum = "sha256:86bf107f65fc30b56f2b263b26797fcbb1661f5315910cdbf27f733eb8738b74"
157
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz"
158
+ provenance = "github-attestations"
159
+
160
+ [tools.python."platforms.linux-x64-musl"]
161
+ checksum = "sha256:2c27fc15bde497fbd78a8d5d38559ee1a8c93110952d5c2b390bc6481e2be415"
162
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-x86_64-unknown-linux-musl-install_only_stripped.tar.gz"
163
+ provenance = "github-attestations"
164
+
165
+ [tools.python."platforms.macos-arm64"]
166
+ checksum = "sha256:5e8e0746cb0108d138f510cfbb28c4b031b9ed63bbbe3e43a34c77d4663c4fa1"
167
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-aarch64-apple-darwin-install_only_stripped.tar.gz"
168
+ provenance = "github-attestations"
169
+
170
+ [tools.python."platforms.macos-x64"]
171
+ checksum = "sha256:2d03154bc83c6d2d048bba23bb808b020e352028f5bd541a740e54467fc9cc75"
172
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-x86_64-apple-darwin-install_only_stripped.tar.gz"
173
+ provenance = "github-attestations"
174
+
175
+ [tools.python."platforms.windows-x64"]
176
+ checksum = "sha256:0c5c9f231704fb49149bc8f2b9d9dbe43eeaf6c54f362ef0fc2066ce64b4d10f"
177
+ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260718/cpython-3.14.6+20260718-x86_64-pc-windows-msvc-install_only_stripped.tar.gz"
178
+ provenance = "github-attestations"
@@ -0,0 +1,113 @@
1
+ [project]
2
+ name = "welt-io-langgraph"
3
+ dynamic = ["version"]
4
+ description = "The LangGraph (Python) adapter for Welt's wire contract"
5
+ readme = "README.md"
6
+ # Floor matches the AgentCore CLI's generated agent projects (requires-python
7
+ # >= 3.10), so `uv add welt-io-langgraph` resolves there without edits.
8
+ requires-python = ">=3.10"
9
+ license = "MIT"
10
+ authors = [{ name = "Takashi Iwamoto" }]
11
+ keywords = ["welt", "slack", "agentcore", "bedrock", "langgraph"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Topic :: Software Development",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/iwamot/welt-io-langgraph"
28
+ Issues = "https://github.com/iwamot/welt-io-langgraph/issues"
29
+
30
+ # The dev group is included by `uv sync` and `uv run` by default, and —
31
+ # unlike a dev extra — is not published in the wheel metadata.
32
+ [dependency-groups]
33
+ dev = [
34
+ # examples/agent is a standalone deployable; its dependencies are
35
+ # installed here so lint and type checks cover it against the local
36
+ # library.
37
+ "bedrock-agentcore==1.18.1",
38
+ "langchain==1.3.14",
39
+ "langchain-aws==1.6.3",
40
+ "langgraph==1.2.9",
41
+ "pip-licenses==5.5.5",
42
+ "pytest==9.1.1",
43
+ "pytest-cov==7.1.0",
44
+ ]
45
+
46
+ [build-system]
47
+ requires = ["hatchling", "hatch-vcs"]
48
+ build-backend = "hatchling.build"
49
+
50
+ [tool.hatch.version]
51
+ source = "vcs"
52
+
53
+ [tool.hatch.build.hooks.vcs]
54
+ version-file = "src/welt_io_langgraph/_version.py"
55
+
56
+ [tool.hatch.build.targets.wheel]
57
+ packages = ["src/welt_io_langgraph"]
58
+
59
+ [tool.hatch.build.targets.sdist]
60
+ exclude = [
61
+ "/.codecov.yml",
62
+ "/.github",
63
+ "/compatibility.sh",
64
+ "/examples",
65
+ "/mise.toml",
66
+ "/uv.lock",
67
+ "/validate.sh",
68
+ ]
69
+
70
+ [tool.uv]
71
+ exclude-newer = "1 day"
72
+
73
+ [tool.ruff]
74
+ include = ["*.py", "examples/**/*.py", "src/**/*.py", "tests/**/*.py"]
75
+
76
+ [tool.ruff.lint]
77
+ # Reference: https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults/
78
+ extend-select = [
79
+ "F", # Pyflakes rules
80
+ "W", # PyCodeStyle warnings
81
+ # "E", # PyCodeStyle errors
82
+ "I", # Sort imports properly
83
+ "UP", # Warn if certain things can changed due to newer Python versions
84
+ "C4", # Catch incorrect use of comprehensions, dict, list, etc
85
+ "FA", # Enforce from __future__ import annotations
86
+ "ISC", # Good use of string concatenation
87
+ "ICN", # Use common import conventions
88
+ "RET", # Good return practices
89
+ # "SIM", # Common simplification rules
90
+ "TID", # Some good import practices
91
+ # "TC", # Enforce importing certain types in a TYPE_CHECKING block
92
+ "PTH", # Use pathlib instead of os.path
93
+ "TD", # Be diligent with TODO comments
94
+ # "NPY", # Some numpy-specific things
95
+ # Not in the reference URL
96
+ "S", # flake8-bandit (security)
97
+ ]
98
+ ignore = [
99
+ "S101", # Allow assert in tests (pytest standard practice)
100
+ ]
101
+
102
+ [tool.ty.src]
103
+ include = ["examples", "src", "tests"]
104
+
105
+ [tool.pytest.ini_options]
106
+ testpaths = ["tests"]
107
+
108
+ [tool.coverage.run]
109
+ source = ["welt_io_langgraph"]
110
+ branch = true
111
+
112
+ [tool.coverage.report]
113
+ show_missing = true