noukai-sdk 0.2.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.
- noukai_sdk-0.2.0/.env.example +53 -0
- noukai_sdk-0.2.0/.github/CODEOWNERS +6 -0
- noukai_sdk-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- noukai_sdk-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- noukai_sdk-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +25 -0
- noukai_sdk-0.2.0/.github/workflows/ci.yml +61 -0
- noukai_sdk-0.2.0/.github/workflows/integration.yml +44 -0
- noukai_sdk-0.2.0/.github/workflows/release.yml +64 -0
- noukai_sdk-0.2.0/.gitignore +48 -0
- noukai_sdk-0.2.0/.python-version +1 -0
- noukai_sdk-0.2.0/CHANGELOG.md +114 -0
- noukai_sdk-0.2.0/LICENSE +21 -0
- noukai_sdk-0.2.0/PKG-INFO +619 -0
- noukai_sdk-0.2.0/README.md +577 -0
- noukai_sdk-0.2.0/RELEASING.md +119 -0
- noukai_sdk-0.2.0/pyproject.toml +85 -0
- noukai_sdk-0.2.0/src/noukai_sdk/CONTEXT.md +86 -0
- noukai_sdk-0.2.0/src/noukai_sdk/__init__.py +132 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_client.py +394 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_constants.py +92 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_errors.py +149 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_flow.py +991 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_jobs.py +231 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/__init__.py +3 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/_aliases.py +11 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/events.py +115 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/requests.py +69 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/responses.py +166 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/session.py +97 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_models/trace.py +65 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_paths.py +155 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_run.py +240 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_step_iterator.py +679 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_streaming.py +176 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_tool_calls.py +259 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_trace_scope.py +281 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_transport.py +555 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_transport_shared.py +146 -0
- noukai_sdk-0.2.0/src/noukai_sdk/_version.py +1 -0
- noukai_sdk-0.2.0/src/noukai_sdk/adapters/__init__.py +0 -0
- noukai_sdk-0.2.0/src/noukai_sdk/adapters/fastapi.py +155 -0
- noukai_sdk-0.2.0/src/noukai_sdk/adapters/flask.py +124 -0
- noukai_sdk-0.2.0/src/noukai_sdk/py.typed +0 -0
- noukai_sdk-0.2.0/src/noukai_sdk/replay/__init__.py +0 -0
- noukai_sdk-0.2.0/src/noukai_sdk/replay/_state.py +54 -0
- noukai_sdk-0.2.0/src/noukai_sdk/replay/fetcher.py +92 -0
- noukai_sdk-0.2.0/src/noukai_sdk/replay/matcher.py +459 -0
- noukai_sdk-0.2.0/src/noukai_sdk/replay/sse_reconstructor.py +129 -0
- noukai_sdk-0.2.0/tests/__init__.py +0 -0
- noukai_sdk-0.2.0/tests/conftest.py +1 -0
- noukai_sdk-0.2.0/tests/integration/README.md +161 -0
- noukai_sdk-0.2.0/tests/integration/__init__.py +3 -0
- noukai_sdk-0.2.0/tests/integration/conftest.py +125 -0
- noukai_sdk-0.2.0/tests/integration/fixtures/README.md +124 -0
- noukai_sdk-0.2.0/tests/integration/fixtures/hello-world.json +11 -0
- noukai_sdk-0.2.0/tests/integration/fixtures/tools-enabled.json +27 -0
- noukai_sdk-0.2.0/tests/integration/fixtures/two-step.json +12 -0
- noukai_sdk-0.2.0/tests/integration/test_errors.py +133 -0
- noukai_sdk-0.2.0/tests/integration/test_events.py +158 -0
- noukai_sdk-0.2.0/tests/integration/test_execute.py +83 -0
- noukai_sdk-0.2.0/tests/integration/test_execute_async.py +71 -0
- noukai_sdk-0.2.0/tests/integration/test_replay.py +449 -0
- noukai_sdk-0.2.0/tests/integration/test_run_proxy.py +150 -0
- noukai_sdk-0.2.0/tests/integration/test_steps.py +75 -0
- noukai_sdk-0.2.0/tests/integration/test_tool_calls.py +186 -0
- noukai_sdk-0.2.0/tests/unit/__init__.py +0 -0
- noukai_sdk-0.2.0/tests/unit/test_adapters_fastapi.py +251 -0
- noukai_sdk-0.2.0/tests/unit/test_adapters_flask.py +231 -0
- noukai_sdk-0.2.0/tests/unit/test_client_construction.py +193 -0
- noukai_sdk-0.2.0/tests/unit/test_execute.py +207 -0
- noukai_sdk-0.2.0/tests/unit/test_interfaces.py +118 -0
- noukai_sdk-0.2.0/tests/unit/test_jobs.py +332 -0
- noukai_sdk-0.2.0/tests/unit/test_models_roundtrip.py +184 -0
- noukai_sdk-0.2.0/tests/unit/test_parity.py +44 -0
- noukai_sdk-0.2.0/tests/unit/test_replay.py +901 -0
- noukai_sdk-0.2.0/tests/unit/test_replay_sync.py +712 -0
- noukai_sdk-0.2.0/tests/unit/test_run_proxy.py +200 -0
- noukai_sdk-0.2.0/tests/unit/test_sse_parser.py +252 -0
- noukai_sdk-0.2.0/tests/unit/test_step_iterator.py +603 -0
- noukai_sdk-0.2.0/tests/unit/test_sync_client.py +191 -0
- noukai_sdk-0.2.0/tests/unit/test_tool_call_resume.py +289 -0
- noukai_sdk-0.2.0/tests/unit/test_transport.py +483 -0
- noukai_sdk-0.2.0/uv.lock +1116 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# noukai-sdk Python — local env for integration tests
|
|
2
|
+
#
|
|
3
|
+
# Setup:
|
|
4
|
+
# cp .env.example .env
|
|
5
|
+
# # fill in real values below
|
|
6
|
+
# uv run pytest tests/integration -v
|
|
7
|
+
#
|
|
8
|
+
# The .env file is loaded automatically by tests/integration/conftest.py
|
|
9
|
+
# via python-dotenv. It is NOT loaded by the SDK at runtime — only by tests.
|
|
10
|
+
# NEVER commit .env (it's in .gitignore).
|
|
11
|
+
|
|
12
|
+
# ----------------------------------------------------------------------------
|
|
13
|
+
# SDK runtime env (read by the SDK itself at Noukai() construction)
|
|
14
|
+
# ----------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
# Deployment target. "dev" → http://localhost:8080/api/v1
|
|
17
|
+
# unset/"production" → https://api.noukai.xyz/api/v1
|
|
18
|
+
NOUKAI_ENV=dev
|
|
19
|
+
|
|
20
|
+
# ----------------------------------------------------------------------------
|
|
21
|
+
# Integration test env (read only by tests/integration/, not the SDK)
|
|
22
|
+
# ----------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
# Real nk_* API key for the integration test account.
|
|
25
|
+
# For local dev: a key minted against your local Noukai server.
|
|
26
|
+
# For staging/prod: a dedicated test-account key (NOT a production user key).
|
|
27
|
+
NOUKAI_INTEGRATION_KEY=nk_replace_me
|
|
28
|
+
|
|
29
|
+
# "org/project" pair holding the fixture flows.
|
|
30
|
+
# Recommended convention: a dedicated "tests-integration" project.
|
|
31
|
+
NOUKAI_INTEGRATION_PROJECT=acme/tests-integration
|
|
32
|
+
|
|
33
|
+
# Slug of a single-step flow that takes `message` and returns a response.
|
|
34
|
+
# Used by test_execute, test_execute_async, test_run_proxy.
|
|
35
|
+
NOUKAI_INTEGRATION_HELLO_SLUG=hello-world
|
|
36
|
+
|
|
37
|
+
# Slug of a two-block flow used to test steps()/events() iteration.
|
|
38
|
+
# Used by test_steps, test_events, test_run_proxy.
|
|
39
|
+
NOUKAI_INTEGRATION_TWO_STEP_SLUG=two-step
|
|
40
|
+
|
|
41
|
+
# Slug of a flow configured with `tools` for tool-call resume testing.
|
|
42
|
+
# Used by test_tool_calls.
|
|
43
|
+
NOUKAI_INTEGRATION_TOOLS_SLUG=tools-enabled
|
|
44
|
+
|
|
45
|
+
# ----------------------------------------------------------------------------
|
|
46
|
+
# Optional: zero-credit org for the InsufficientCreditsError test
|
|
47
|
+
# ----------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
# If you have a separate test account with 0 credits, fill these in to enable
|
|
50
|
+
# the test_zero_credits_raises_insufficient_credits test. Otherwise it skips.
|
|
51
|
+
# NOUKAI_INTEGRATION_ZERO_CREDIT_KEY=nk_zero_credit_account
|
|
52
|
+
# NOUKAI_INTEGRATION_ZERO_CREDIT_PROJECT=acme/integration-zero
|
|
53
|
+
# NOUKAI_INTEGRATION_ZERO_CREDIT_SLUG=hello-world
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something is not working as expected
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Description
|
|
8
|
+
|
|
9
|
+
<!-- A clear, concise description of what the bug is. -->
|
|
10
|
+
|
|
11
|
+
## Minimal reproducer
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from noukai_sdk import Noukai
|
|
15
|
+
|
|
16
|
+
# Paste the smallest code snippet that triggers the problem.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Expected behaviour
|
|
20
|
+
|
|
21
|
+
<!-- What you expected to happen. -->
|
|
22
|
+
|
|
23
|
+
## Actual behaviour
|
|
24
|
+
|
|
25
|
+
<!-- What actually happened. Include any error messages or tracebacks. -->
|
|
26
|
+
|
|
27
|
+
## Environment
|
|
28
|
+
|
|
29
|
+
| Field | Value |
|
|
30
|
+
|-------|-------|
|
|
31
|
+
| SDK version | <!-- e.g. 0.1.0 — run: python -c "import noukai_sdk; print(noukai_sdk.__version__)" --> |
|
|
32
|
+
| Python version | <!-- e.g. 3.11.9 --> |
|
|
33
|
+
| OS | <!-- e.g. macOS 14.5, Ubuntu 22.04, Windows 11 --> |
|
|
34
|
+
| Install method | <!-- pip / uv / conda / other --> |
|
|
35
|
+
|
|
36
|
+
## Additional context
|
|
37
|
+
|
|
38
|
+
<!-- Anything else that might help — logs, related issues, etc. -->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an improvement or new capability
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
<!-- One sentence: what would you like the SDK to do? -->
|
|
10
|
+
|
|
11
|
+
## Motivation
|
|
12
|
+
|
|
13
|
+
<!-- Why do you need this? What use case does it unlock? -->
|
|
14
|
+
|
|
15
|
+
## Proposed API
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
# Optional: sketch what the API might look like.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Alternatives considered
|
|
22
|
+
|
|
23
|
+
<!-- Have you worked around this another way? Are there existing methods that partially cover the need? -->
|
|
24
|
+
|
|
25
|
+
## Additional context
|
|
26
|
+
|
|
27
|
+
<!-- Anything else — links to Noukai docs, related issues, etc. -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR do? One to three bullet points. -->
|
|
4
|
+
|
|
5
|
+
-
|
|
6
|
+
|
|
7
|
+
## Related issues
|
|
8
|
+
|
|
9
|
+
<!-- Link any issues this PR addresses: "Closes #123" or "Relates to #456". -->
|
|
10
|
+
|
|
11
|
+
## Test plan
|
|
12
|
+
|
|
13
|
+
- [ ] Existing tests pass (`uv run pytest tests/unit/ -v`)
|
|
14
|
+
- [ ] New tests added for new behaviour (if applicable)
|
|
15
|
+
- [ ] Type-check passes (`uv run mypy`)
|
|
16
|
+
- [ ] Linting passes (`uv run ruff check && uv run ruff format --check`)
|
|
17
|
+
- [ ] Manual verification steps:
|
|
18
|
+
|
|
19
|
+
<!-- Describe any manual steps needed to verify this PR. -->
|
|
20
|
+
|
|
21
|
+
## Checklist
|
|
22
|
+
|
|
23
|
+
- [ ] CHANGELOG.md updated under `[Unreleased]`
|
|
24
|
+
- [ ] `_version.py` and `pyproject.toml` bumped (if this is a release PR)
|
|
25
|
+
- [ ] Docs updated (if public API changed)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-type:
|
|
11
|
+
name: Lint & type-check
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up uv
|
|
17
|
+
uses: astral-sh/setup-uv@v3
|
|
18
|
+
|
|
19
|
+
- name: Install dev dependencies
|
|
20
|
+
run: uv sync --extra dev
|
|
21
|
+
|
|
22
|
+
- name: Check formatting (ruff)
|
|
23
|
+
run: uv run ruff format --check
|
|
24
|
+
|
|
25
|
+
- name: Lint (ruff)
|
|
26
|
+
run: uv run ruff check
|
|
27
|
+
|
|
28
|
+
- name: Type-check (mypy)
|
|
29
|
+
run: uv run mypy
|
|
30
|
+
|
|
31
|
+
test:
|
|
32
|
+
name: Tests (${{ matrix.os }} / Python ${{ matrix.python-version }})
|
|
33
|
+
runs-on: ${{ matrix.os }}
|
|
34
|
+
strategy:
|
|
35
|
+
fail-fast: false
|
|
36
|
+
matrix:
|
|
37
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
38
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
|
|
42
|
+
- name: Set up uv
|
|
43
|
+
uses: astral-sh/setup-uv@v3
|
|
44
|
+
|
|
45
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
46
|
+
uses: actions/setup-python@v5
|
|
47
|
+
with:
|
|
48
|
+
python-version: ${{ matrix.python-version }}
|
|
49
|
+
|
|
50
|
+
- name: Install dev dependencies
|
|
51
|
+
run: uv sync --extra dev
|
|
52
|
+
|
|
53
|
+
# Run unit tests with coverage reporting and enforce the floor in
|
|
54
|
+
# the same invocation (one pytest run is sufficient — the second
|
|
55
|
+
# invocation was producing duplicate output and re-running tests).
|
|
56
|
+
- name: Run unit tests with coverage
|
|
57
|
+
run: >
|
|
58
|
+
uv run pytest tests/unit/ -v
|
|
59
|
+
--cov=noukai_sdk
|
|
60
|
+
--cov-report=term-missing
|
|
61
|
+
--cov-fail-under=85
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Integration (Python SDK)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: "0 6 * * *" # daily 06:00 UTC
|
|
6
|
+
push:
|
|
7
|
+
tags:
|
|
8
|
+
- "v*.*.*"
|
|
9
|
+
workflow_dispatch: # manual trigger
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
integration:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
timeout-minutes: 30
|
|
15
|
+
env:
|
|
16
|
+
NOUKAI_INTEGRATION_KEY: ${{ secrets.NOUKAI_INTEGRATION_KEY }}
|
|
17
|
+
NOUKAI_INTEGRATION_PROJECT: ${{ vars.NOUKAI_INTEGRATION_PROJECT }}
|
|
18
|
+
NOUKAI_INTEGRATION_HELLO_SLUG: ${{ vars.NOUKAI_INTEGRATION_HELLO_SLUG }}
|
|
19
|
+
NOUKAI_INTEGRATION_TWO_STEP_SLUG: ${{ vars.NOUKAI_INTEGRATION_TWO_STEP_SLUG }}
|
|
20
|
+
NOUKAI_INTEGRATION_TOOLS_SLUG: ${{ vars.NOUKAI_INTEGRATION_TOOLS_SLUG }}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v3
|
|
25
|
+
with:
|
|
26
|
+
version: "latest"
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: |
|
|
30
|
+
cd development/noukai/sdk/python
|
|
31
|
+
uv sync --extra dev
|
|
32
|
+
|
|
33
|
+
- name: Run integration tests
|
|
34
|
+
run: |
|
|
35
|
+
cd development/noukai/sdk/python
|
|
36
|
+
uv run pytest tests/integration/ -v --tb=short
|
|
37
|
+
|
|
38
|
+
- name: Upload test results
|
|
39
|
+
if: always()
|
|
40
|
+
uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: integration-test-results
|
|
43
|
+
path: development/noukai/sdk/python/.pytest_cache/
|
|
44
|
+
retention-days: 7
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Triggers on semver tags like v0.1.0, v1.2.3, etc.
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v*.*.*"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build-and-publish:
|
|
11
|
+
name: Build, publish to PyPI & create GitHub release
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
# The "pypi" environment must be created in the GitHub repo settings
|
|
14
|
+
# (Settings → Environments → New environment: pypi) and the PyPI trusted
|
|
15
|
+
# publisher must be configured at:
|
|
16
|
+
# https://pypi.org/manage/account/publishing/
|
|
17
|
+
# pointing at this workflow file path:
|
|
18
|
+
# Workflow: .github/workflows/release.yml
|
|
19
|
+
# Environment: pypi
|
|
20
|
+
environment: pypi
|
|
21
|
+
permissions:
|
|
22
|
+
id-token: write # Required for OIDC trusted publisher (no API token needed)
|
|
23
|
+
contents: write # Required to create the GitHub release
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
|
|
28
|
+
- name: Set up uv
|
|
29
|
+
uses: astral-sh/setup-uv@v3
|
|
30
|
+
|
|
31
|
+
- name: Install package (no extras — just needs to be importable for version check)
|
|
32
|
+
run: uv sync
|
|
33
|
+
|
|
34
|
+
# Safety guard: the pushed tag (e.g. v0.1.0) must match the version
|
|
35
|
+
# declared in noukai_sdk._version. Catches "forgot to bump _version.py"
|
|
36
|
+
# before we reach PyPI (uploads there are permanent).
|
|
37
|
+
- name: Verify tag matches package version
|
|
38
|
+
shell: bash
|
|
39
|
+
run: |
|
|
40
|
+
TAG_VER="${GITHUB_REF_NAME#v}"
|
|
41
|
+
FILE_VER=$(uv run python -c "import noukai_sdk; print(noukai_sdk.__version__)")
|
|
42
|
+
echo "Tag version : $TAG_VER"
|
|
43
|
+
echo "Package version: $FILE_VER"
|
|
44
|
+
if [ "$TAG_VER" != "$FILE_VER" ]; then
|
|
45
|
+
echo "ERROR: tag $TAG_VER does not match package version $FILE_VER"
|
|
46
|
+
echo "Update src/noukai_sdk/_version.py and pyproject.toml, then retag."
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
- name: Build wheel and sdist
|
|
51
|
+
run: uv build
|
|
52
|
+
|
|
53
|
+
# Uses PyPI's OIDC trusted publisher — no API token stored in GitHub secrets.
|
|
54
|
+
# See: https://docs.pypi.org/trusted-publishers/
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
57
|
+
|
|
58
|
+
# Creates a GitHub release from the tag with auto-generated release notes
|
|
59
|
+
# and attaches the CHANGELOG as the body.
|
|
60
|
+
- name: Create GitHub release
|
|
61
|
+
uses: softprops/action-gh-release@v2
|
|
62
|
+
with:
|
|
63
|
+
generate_release_notes: true
|
|
64
|
+
body_path: CHANGELOG.md
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
*.egg
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
.eggs/
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# UV
|
|
19
|
+
.uv/
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# Type checking
|
|
28
|
+
.mypy_cache/
|
|
29
|
+
|
|
30
|
+
# Linting
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
|
|
33
|
+
# Documentation build output (mkdocs)
|
|
34
|
+
site/
|
|
35
|
+
|
|
36
|
+
# Local env (integration test secrets — never commit)
|
|
37
|
+
.env
|
|
38
|
+
.env.local
|
|
39
|
+
.env.*.local
|
|
40
|
+
|
|
41
|
+
# IDE
|
|
42
|
+
.idea/
|
|
43
|
+
.vscode/
|
|
44
|
+
*.swp
|
|
45
|
+
*.swo
|
|
46
|
+
|
|
47
|
+
# macOS
|
|
48
|
+
.DS_Store
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. The
|
|
4
|
+
format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] — 2026-06-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `@noukai_sdk.trace` decorator and `trace_scope` / `trace_scope_sync` context
|
|
14
|
+
managers. Wrapping a route (or any callable) in `@trace` groups every Noukai
|
|
15
|
+
SDK call it makes under a single session id so the full execution can be
|
|
16
|
+
replayed later.
|
|
17
|
+
- `current_session_id() -> str | None` — returns the session id of the active
|
|
18
|
+
scope, or `None` when called outside a scope.
|
|
19
|
+
- `session_id=` optional kwarg on `Flow.execute()`, `Flow.steps()`,
|
|
20
|
+
`Flow.events()`, `Flow.execute_async()`, and their `AsyncFlow` counterparts.
|
|
21
|
+
When passed outside a scope the SDK sends `X-Session-Id` on the wire;
|
|
22
|
+
when passed *inside* an active replay scope it triggers a one-shot fetch
|
|
23
|
+
of that explicit session instead of drawing from the scope cassette (see
|
|
24
|
+
Caveats in README).
|
|
25
|
+
- `session_id=` optional kwarg on `Noukai(...)` / `AsyncNoukai(...)` — sets a
|
|
26
|
+
default session id for every call made through that client.
|
|
27
|
+
- `ExecuteResult.session_id` property surfaces the captured or replayed session
|
|
28
|
+
id returned in the `X-Noukai-Session` response header.
|
|
29
|
+
- **Replay mode.** When `NOUKAI_REPLAY_ENABLED=true` and the caller passes
|
|
30
|
+
`X-Noukai-Replay: <session_id>` to the adapter, the SDK fetches the recorded
|
|
31
|
+
session via `GET /seq/sessions/{id}` (idempotent; retried by default transport
|
|
32
|
+
retry logic — see R3 note in README) and serves each `Flow.execute()` /
|
|
33
|
+
`steps()` / `events()` call from the cassette instead of making live calls.
|
|
34
|
+
- **Framework adapters:**
|
|
35
|
+
- `noukai_sdk.adapters.fastapi.NoukaiTraceMiddleware` — ASGI middleware for
|
|
36
|
+
FastAPI / Starlette; reads `X-Noukai-Replay`, opens/closes a trace scope
|
|
37
|
+
around the request, and writes `X-Noukai-Session` to the response.
|
|
38
|
+
- `noukai_sdk.adapters.flask.init_noukai_trace` — Flask before/after-request
|
|
39
|
+
hooks equivalent.
|
|
40
|
+
- **9 replay error classes** (all extend `ReplayError` which extends
|
|
41
|
+
`NoukaiError`):
|
|
42
|
+
- `ReplayError` — base class for all replay errors.
|
|
43
|
+
- `ReplayMissError` — no matching execution found in the cassette for a
|
|
44
|
+
`(slug, position)` lookup.
|
|
45
|
+
- `ReplayLeftoverError` — scope exited with unconsumed executions remaining
|
|
46
|
+
in the cassette.
|
|
47
|
+
- `ReplayForbiddenError` — replay attempted against a session that belongs to
|
|
48
|
+
a different org/project.
|
|
49
|
+
- `ReplaySessionNotFoundError` — the requested session id does not exist on
|
|
50
|
+
the backend.
|
|
51
|
+
- `ReplaySessionExpiredError` — the session exists but its TTL has elapsed and
|
|
52
|
+
snapshots are no longer available.
|
|
53
|
+
- `ReplayInvalidSessionError` — the session payload is structurally invalid or
|
|
54
|
+
cannot be parsed.
|
|
55
|
+
- `ReplayNoSnapshotsError` — the session exists but `trace_capture_mode` was
|
|
56
|
+
`off` so no snapshot data was recorded.
|
|
57
|
+
- `ReplayDisabledError` — replay was requested but `NOUKAI_REPLAY_ENABLED` is
|
|
58
|
+
not set to `true` (raised only when the adapter is used without the guard).
|
|
59
|
+
- Optional package extras: `noukai-sdk[fastapi]` (pulls FastAPI + Starlette)
|
|
60
|
+
and `noukai-sdk[flask]` (pulls `flask>=3.0`).
|
|
61
|
+
- Centralized URL audit registry at `noukai_sdk/_paths.py` — single file
|
|
62
|
+
lists every backend route the SDK calls. Auditing the wire surface is one
|
|
63
|
+
file read.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- Replay session fetch now hits `GET /seq/sessions/{id}` (was incorrectly
|
|
68
|
+
`GET /sessions/{id}` — 404 in production).
|
|
69
|
+
- Replay matcher now compares against the BE's bare `flow.slug` (e.g.
|
|
70
|
+
`"grade-3"`), not the synthesized `org/project/slug` that fixtures
|
|
71
|
+
previously used. Replay against the real backend now actually matches
|
|
72
|
+
recorded executions.
|
|
73
|
+
- `SessionExecution` model aligned with BE schema: `status` includes
|
|
74
|
+
`"pending"` and `"cancelled"`; `flow_id`, `slug`, `trigger_type`,
|
|
75
|
+
`trace_capture_mode`, and `error_at_step` are now Optional so the SDK
|
|
76
|
+
does not crash when the underlying flow has been deleted.
|
|
77
|
+
- Reserved-header guard on `extra_headers=`: a misconfigured caller cannot
|
|
78
|
+
overwrite `Authorization`, `X-Noukai-API-Version`, `User-Agent`,
|
|
79
|
+
`X-Request-ID`, `Content-Type`, or `Cookie` via per-request headers.
|
|
80
|
+
- Unified `Flow.execute() / steps() / events()` REPLAY-mode dispatch — all
|
|
81
|
+
three now apply the same rule when an explicit `session_id` matches or
|
|
82
|
+
differs from the scope. Previously `events()` and `steps()` made a live
|
|
83
|
+
call for explicit-sid-matching-scope, asymmetric with `execute()`.
|
|
84
|
+
- Session-id precedence chain uses `is not None` instead of `or`, so an
|
|
85
|
+
explicit empty-string `session_id=""` is no longer silently overridden
|
|
86
|
+
by the next tier.
|
|
87
|
+
|
|
88
|
+
### Internal
|
|
89
|
+
|
|
90
|
+
- Transport `request()` and `stream()` accept an `extra_headers=` kwarg; used
|
|
91
|
+
by the replay subsystem to inject `X-Session-Id` / `X-Noukai-Replay` without
|
|
92
|
+
touching the public client surface.
|
|
93
|
+
- Log handler receives `scope_open` and `scope_close` events with `mode`
|
|
94
|
+
(`"normal"` | `"capture"` | `"replay"`) and `session_id` fields.
|
|
95
|
+
|
|
96
|
+
### Requires
|
|
97
|
+
|
|
98
|
+
- Backend session-grouping endpoint per BE design
|
|
99
|
+
`20260605-BE-execution-session-grouping` (`GET /seq/sessions/{id}`,
|
|
100
|
+
`X-Session-Id` header on `/execute` + `/step` routes).
|
|
101
|
+
|
|
102
|
+
## [0.1.0] — 2026-05-31
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
- Initial release.
|
|
106
|
+
- `Noukai` and `AsyncNoukai` clients (sync + async).
|
|
107
|
+
- `flow.execute()`, `flow.execute_async()`, `flow.steps()`, `flow.events()`.
|
|
108
|
+
- `flow.run(id).trace()`, `step_trace()`, `live_trace()`.
|
|
109
|
+
- Tool-call auto-resume via `tool_handler=`; manual mode via `PausedResult.resume()`.
|
|
110
|
+
- Typed Pydantic event hierarchy for SSE streams.
|
|
111
|
+
- Exception hierarchy mapped to HTTP status; server error codes on `.code`.
|
|
112
|
+
|
|
113
|
+
[Unreleased]: https://github.com/noukai/noukai-python/compare/v0.1.0...HEAD
|
|
114
|
+
[0.1.0]: https://github.com/noukai/noukai-python/releases/tag/v0.1.0
|
noukai_sdk-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 abigcircle
|
|
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.
|