Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.live.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ QODER_REALTIME_GLOBAL_SAT=
# Uses QODER_FORWARD_CN_PAT, QODER_FORWARD_GLOBAL_PAT, QODER_SA_CN_KEY,
# and QODER_SA_GLOBAL_KEY. Never point clear at a shared Identity.
QODER_DRIVE_LIVE_ALLOW_WRITE=false

# Forward Decisions consume model usage and retain records until server expiry.
# Also requires QODER_FORWARD_LIVE_ALLOW_WRITE/EXECUTION=true.
QODER_FORWARD_LIVE_ALLOW_DECISIONS=false
# Optional existing Session for the Python/TypeScript live suite and all examples.
QODER_DECISION_SESSION_ID=
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ existing `0.1.0` release; earlier development prereleases are not listed.

## [Unreleased]

### Added

- Add Forward Decisions for synchronous evaluation of explicit state or accessible Session history, with choice and score questions, idempotent creation, retrieval, cursor pagination, and typed context truncation/token-estimate metadata.

## [0.9.0] - 2026-10-26

### Added
Expand Down
23 changes: 23 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,26 @@ those tests do not replace SDK integration coverage.
## Realtime validation

Realtime REST and local WebSocket protocol/lifecycle tests run offline with the default suite. `uv run make test-live-realtime` is a separate opt-in CN/Global entry, controlled by the dedicated `QODER_REALTIME_*` configuration and both write/execution flags. It creates persistent Conversations using already-enabled Templates; transport close is not resource cleanup. Record the printed Conversation/request IDs and agree on retention with the service owner. Basic ping/history success does not replace text, PCM, playback, and background Work acceptance. Realtime auth headers, SSL context, and proxy settings use the WebSocket adapter; arbitrary httpx middleware is not applied to it.

## Decision live tests

Decision evaluation is opt-in because it consumes model usage and creates
records without a delete endpoint. Configure a dedicated account using
`QODER_FORWARD_PAT` and `QODER_FORWARD_BASE_URL` in `.env.live`, then set
`QODER_FORWARD_LIVE_ALLOW_WRITE=true`,
`QODER_FORWARD_LIVE_ALLOW_EXECUTION=true`, and
`QODER_FORWARD_LIVE_ALLOW_DECISIONS=true`. These live tests use PAT; offline
contracts cover both PAT and SAT behavior. Normal unit/contract runs are offline.

```bash
QODER_RUN_LIVE=1 uv run pytest tests/integration/test_decisions.py -m integration -v
```

This checks state evaluation, idempotent replay, retrieval, and cursor pages.
Set `QODER_DECISION_SESSION_ID` to an accessible Session with existing history
to also test Session input; otherwise that case is explicitly skipped. The
suite does not modify the supplied Session.

Decision records expire according to server retention. Retain only decision
IDs, HTTP status, and request IDs in shared diagnostics; do not log credentials
or unrelated Session contents.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,56 @@ credential = client.vaults.credentials.retrieve("credential-id", vault_id="vault
memory = client.memory_stores.memories.retrieve("memory-id", memory_store_id="store-id")
```

## Forward Decisions (next release)

Evaluate a nonempty `state` object or an accessible `session_id` (exactly one)
with 1–32 choice or score questions. Choice values are strings or booleans;
score questions select from your numeric levels. Creation waits for the result.
The service selects the model; no `model` parameter is accepted. Normal PAT or
SAT authentication determines the caller's access.

```python
from uuid import uuid4
from qca import Forward

with Forward() as client: # Or use the PAT/SA configuration above.
decision = client.decisions.create(
state={"message": "My issue is resolved."},
questions=[{
"type": "choice", "name": "resolved",
"instructions": "Has the user confirmed resolution?",
"choices": [{"value": True}, {"value": False}],
}],
idempotency_key=str(uuid4()),
)
stored = client.decisions.retrieve(decision.id)
print(stored.answers)
page = client.decisions.list(limit=20)
print(page.data) # Iterate over page directly to follow all pages.
```

For Session input, replace `state=...` with `session_id=session_id`.
For scores, use `{"type": "score", "instructions": "Rate risk", "levels":
[{"label": "low", "score": 0}, {"label": "high", "score": 10}]}`.
`AsyncForward` offers the same methods with `await` and asynchronous pagination.

Session context is selected by the server. Inspect `context.truncated`,
`context.truncation_reasons`, and the input-token estimate to understand the
selected history. Explicit state and questions are never shortened; oversized
input returns HTTP 422. Context estimates are separate from `usage`, which can
be null. Answers can also have type `refusal`.

Reuse an idempotency key only for identical request bytes. A changed request or
a still-running request with that key returns HTTP 409; the SDK does not retry
that conflict automatically. Creation errors can contain `decision_id` in the
standard API error body. Retrieve and list can return running or failed records.
Lists preserve server order and support `after_id` / `before_id` cursors (one at
a time), optional `session_id`, and a limit of 1–100. There is no delete endpoint;
Decision records expire according to server retention.

See the [runnable example](examples/forward/decisions.py) and the
[opt-in live test instructions](CONTRIBUTING.md#decision-live-tests).

## Forward Drive (Beta)

Drive is a Beta API. It uses `entries` / `next_page_token`, not the ordinary
Expand Down
Loading
Loading