Metadata-Version: 2.5
Name: diteko
Version: 0.5.1
Summary: Generate, export and run tests for Amazon Connect contact flows with Diteko (Grammatical Evolution).
Project-URL: Homepage, https://github.com/Lehlohonolo-Sehako/diteko-selfhost
Project-URL: Documentation, https://github.com/Lehlohonolo-Sehako/diteko-selfhost#readme
Project-URL: Run a server, https://github.com/Lehlohonolo-Sehako/diteko-selfhost
Project-URL: Issues, https://github.com/Lehlohonolo-Sehako/diteko-selfhost/issues
Project-URL: Funding, https://github.com/sponsors/Lehlohonolo-Sehako
Author: Lehlohonolo Sehako
License-Expression: MIT
License-File: LICENSE
Keywords: amazon-connect,contact-flow,cxblueprint,grammatical-evolution,ivr,mcp,testing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Telephony
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Provides-Extra: cxblueprint
Requires-Dist: cxblueprint>=0.1.0.post15; extra == 'cxblueprint'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: twine>=6; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp<3,>=1.26.0; extra == 'mcp'
Description-Content-Type: text/markdown

# diteko (Python)

Generate, export and run tests for Amazon Connect contact flows, from Python. It is the
testing counterpart to [cxblueprint](https://pypi.org/project/cxblueprint/): build or
export a flow, then let Diteko derive its grammar, evolve a test suite with Grammatical
Evolution, export it, and run it on Connect's native testing.

> **This package is a client: it needs a Diteko server.** It does nothing on its own. Point it
> at your team's Diteko, or start one on this machine with `diteko server up` (it needs Docker;
> see <https://github.com/Lehlohonolo-Sehako/diteko-selfhost>).

The SDK talks to a running Diteko deployment over its REST API. If that deployment requires API
keys, set `DITEKO_API_KEY` (or pass `api_key=`); otherwise point the SDK only at a deployment
inside your network or behind your VPN.

```bash
pip install diteko               # SDK
pip install "diteko[mcp]"        # + MCP server for Claude Desktop / Cursor / VS Code
pip install "diteko[cxblueprint]"
```

## Getting started

1. **Find your Diteko.** Diteko is self-hosted: you need the address of a running deployment. If you run your own ([diteko-selfhost](https://github.com/Lehlohonolo-Sehako/diteko-selfhost)), that is `http://localhost:8080`, which is also the SDK's default. To start one on this machine (with Docker installed):

   ```bash
   diteko server up        # sets it up in ~/.diteko/server and starts it with Docker
   ```

   It needs only Docker (Docker Desktop on macOS and Windows): the self-hosting files travel in
   this package, and the first start downloads the server image. The first time, it creates a
   `WRITE` key and prints the two settings to export (PowerShell syntax on Windows), plus the
   bootstrap ADMIN key for the console at the same address. `diteko server status` and
   `diteko server down` do what they say; data is kept between runs, and upgrading this package
   upgrades the server's files on the next `up` without touching your settings. `--port` picks
   another port, `--latest` uses the newest diteko-selfhost release instead of the files in this
   package, and `--bundle path` a self-hosting folder you already have.

2. **Get a key, if it asks for one.** When a deployment requires API keys, whoever runs it creates one for you under Settings › API keys. `WRITE` is enough for everything here except live runs on Connect. Put it in the environment:

   ```bash
   export DITEKO_URL=https://diteko.example.internal
   export DITEKO_API_KEY=dk_...     # only if the deployment requires keys
   ```

3. **Check the connection:**

   ```python
   from diteko import Diteko

   dk = Diteko()            # DITEKO_URL and DITEKO_API_KEY from the environment
   print(dk.version())      # {'api': '1.1', 'server': '…', 'auth': 'keys'}
   print(dk.whoami())       # the key in use, or None when keys are off
   ```

4. **Run the example.** `examples/cxblueprint_to_excel.py`, also as a notebook, builds a flow with cxblueprint, evolves a suite and writes the Excel test script, in about a minute. Both ship in the source distribution: `pip download --no-binary :all: --no-deps diteko`, then unpack it.

## Watching a run, and typed results

```python
run = project.evolve(preset="standard").wait(progress=True)       # one line on stderr, kept up to date
run = project.evolve(preset="standard").wait(progress=print)      # or any callable, given a RunProgress
run.info.best_fitness, run.info.generation, run.info.generations

project.refresh().info            # ProjectInfo: grammar, latest run, suite, last execution, typed
execution = suite.publish_to_connect(dry_run=True)
execution.results                 # [Verdict(test_id, status, message), ...]
execution.passed, execution.failed, execution.inconclusive   # inconclusive: Connect could not start it, etc.
```

## The pipeline

```python
from diteko import Diteko

dk = Diteko("http://localhost:8080")

project = dk.projects.from_json("claims.json")          # Connect export or cxblueprint output
project.grammar.generate()                              # derive, store and validate the grammar
run = project.evolve(preset="standard", focus="balanced").wait()
suite = run.suite(top=30, selection="coverage")         # or selection="fitness"

for test in suite:
    print(test.test_id, test.keys, test.fitness)

suite.export_xlsx("claims-tests.xlsx")                  # manual test script (ISO/IEC/IEEE 29119-3)
suite.export_native("claims-native.json")               # Amazon Connect native test cases
result = suite.publish_to_connect(dry_run=True)         # dry run unless you say otherwise
print(result.passed, result.failed, result.verdicts)
```

### Straight from cxblueprint

```python
from cxblueprint import Flow
from diteko import Diteko

flow = Flow.build("Burger Order")
menu = flow.get_input("Press 1 for Classic or 2 for Veggie", timeout=10)
# ... build the flow as usual ...

project = Diteko().projects.from_cxblueprint(flow)      # compiles locally, uploads the JSON
```

### Other ways in

```python
dk.projects.import_from_connect("08e145dc-d959-4094-9021-6f10d8590be2")   # from the instance
dk.projects.list()                                      # every project and its next step
dk.projects.get(21601).suite(top=10).export_xlsx()
```

### Grammars as BNF or JSON

The two forms are exact reflections of each other. Both are validated by the same parser,
and both keep comments.

```python
g = project.grammar.latest()
spec = g.to_json()                    # {"format": "diteko-grammar/v1", "start": ..., "rules": [...]}
g.replace(spec, format="json").save() # edit as JSON, store as canonical BNF
dk.convert_grammar(bnf_text, "bnf", "json")
```

### Presets

| `preset`   | calls × generations |
| ---------- | ------------------- |
| `quick`    | 40 × 15             |
| `standard` | 100 × 50            |
| `thorough` | 200 × 120           |

| `focus`     | favours                                              |
| ----------- | ---------------------------------------------------- |
| `balanced`  | blocks and branches, plus happy paths and recoveries |
| `coverage`  | visiting as much of the flow as possible             |
| `errors`    | timeouts and wrong keys that still end properly      |
| `transfers` | calls that reach an agent queue                      |

Pass `population=`, `generations=`, `weights={"state_coverage": 1.0}` or `seed=` to go beyond them.

### API keys

```python
dk = Diteko("https://diteko.internal", api_key="dk_...")   # or set DITEKO_API_KEY
dk.whoami()   # {"name": "nightly CI", "prefix": "dk_...", "scope": "WRITE", ...}; None if keys are off
```

A key carries one scope: `READ` (see and export), `WRITE` (also create, run and dry-run),
`PUBLISH_LIVE` (also run on Connect for real, which is billed) or `ADMIN` (also manage keys).
Keys are created in the console under Settings, API keys.

## MCP server

```bash
DITEKO_URL=http://localhost:8080 DITEKO_API_KEY=dk_... diteko-mcp   # the key only if the server requires one
```

```json
{ "mcpServers": { "diteko": { "command": "diteko-mcp", "env": { "DITEKO_URL": "http://localhost:8080", "DITEKO_API_KEY": "dk_..." } } } }
```

| Tool               | Does                                                                                                              |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `list_projects`    | every flow under test and its next step                                                                           |
| `import_flow`      | register a flow from JSON text, a file path, or a Connect flow id                                                 |
| `generate_grammar` | derive, store and validate a flow's grammar                                                                       |
| `convert_grammar`  | BNF ⇄ JSON                                                                                                        |
| `start_evolution`  | start a run (`size`, `focus`, `seed`)                                                                             |
| `get_run`          | a run's progress                                                                                                  |
| `export_suite`     | `xlsx`, `json` or `native` to a file                                                                              |
| `publish_suite`    | start a run on Connect and return at once; **`dry_run` defaults to true**, since a live run bills the AWS account |
| `get_execution`    | a Connect run's status and verdicts; poll it until the status is no longer `RUNNING`                              |

Each tool carries MCP annotations: read-only tools say so, and `publish_suite` is marked as reaching an external system. Works with `mcp` 1.x and 2.x.

## Support Diteko

Diteko is free to run for your own organisation. If it saves your team time, you can
[sponsor it on GitHub](https://github.com/sponsors/Lehlohonolo-Sehako); the
[docs](https://lehlohonolo-sehako.github.io/diteko-selfhost/support/) list other ways to help.

## Versioning

The package follows [Semantic Versioning](https://semver.org/). See the [changelog](#changelog) for what each release changed.

The server reports an **API version**, `MAJOR.MINOR`, at `GET /api/version`. A minor bump only adds endpoints or fields. A major bump changes something an existing client relies on. Each SDK release speaks one API major version, currently **1**. On its first call the SDK checks the server's version:

| Server                      | What happens                                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------- |
| API 1.x                     | Works.                                                                                      |
| Another major               | `DitekoError`, saying whether to upgrade the SDK or the server                              |
| Too old to report a version | A warning, then it carries on                                                               |
| Not a Diteko server         | `DitekoError`: check `DITEKO_URL`                                                           |
| Nothing answering           | `DitekoError` saying a server is needed and how to point at one; the next call checks again |

`Diteko(url, check_version=False)` skips the check, and `dk.version()` returns what the server reports.

## Development

```bash
cd clients/python
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev,mcp]"
pytest                                   # against an in-memory fake of the API
DITEKO_URL=http://localhost:8888 pytest  # also runs the live and contract tests against a local stack
```


# Changelog

All notable changes to the `diteko` Python package. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
[Semantic Versioning](https://semver.org/) as described under “Versioning” in the README.

## [Unreleased]

## [0.5.1] - 2026-11-01

### Added

- A `Funding` link on PyPI, and a "Support Diteko" section in this README.

## [0.5.0] - 2026-11-01

### Added

- **`diteko server up`**, `status` and `down`: start a Diteko server on this machine with only
  Docker installed. The self-hosting files ship inside the package (a copy of `self-hosting/`,
  kept identical by a test), the settings are written in Python as `setup.sh` writes them (so it
  works on Windows too), and the first start creates a `WRITE` key and prints what to set.
  A later `up` refreshes the server's files and keeps your settings. `--latest` uses the newest
  diteko-selfhost release instead (checked against its SHA256SUMS), `--bundle` a local folder,
  `--port` another port. When Docker is missing, stopped or may not pull the image, it says so.
- The "no server answered" error mentions `diteko server up`.
- A server started with `diteko server up` serves the API reference and links its console to the
  docs site (`DITEKO_DOCS_URL` in its `.env`; empty hides the links).
- MCP tool `get_execution(upload_id)`: a Connect run's status, verdicts, inconclusive count and
  per-test results. `Diteko.execution(upload_id)` does the same from Python.
- `[project.urls]`, so PyPI links to where you can run a server.

### Changed

- **The default server address is `http://localhost:8080`**, where a self-hosted Diteko
  listens, instead of `:8888`, which only Diteko's own development setup uses. `DITEKO_URL` and
  `url=` work as before.
- **MCP `publish_suite` returns at once** with the upload id and status, instead of holding one
  tool call open for up to 30 minutes. Poll `get_execution` for the verdicts.
- When no server answers, the error says the SDK needs a running Diteko server, how to point
  at one (`DITEKO_URL`) and where to get one. Connecting gives up after 10 seconds.
- An address that is not a Diteko server (a 404 without a problem body on `/api/version`, or
  a page that is not JSON) is reported as such, instead of a warning followed by a page of HTML.
  HTML in any error is reduced to its text and cut to 300 characters.
- The version check runs again on the next call when the server could not be reached, instead
  of being skipped for the rest of the session.
- `evolve(preset="thorough")` uses a per-bit mutation rate of 0.05 instead of 0.1, like the
  console's Thorough size: on the largest corpus flow it did better and was never worse
  elsewhere. Quick and standard runs are unchanged.

### Fixed

- `Execution.wait()` returned only on COMPLETED, so a run a server restart cut off
  (INTERRUPTED) blocked for the full 30-minute timeout. Every status but RUNNING now ends the
  wait, and INTERRUPTED raises `DitekoError` asking you to publish again.

## [0.4.0] - 2026-10-11

### Added

- `Verdict.attempts` and `Verdict.passed_on_rerun`. A server on API 1.2 re-runs a test
  that failed, once, because Connect's prompt recognition sometimes misses a prompt the
  flow played; a pass on the re-run is reported as a pass with `attempts == 2`. Older
  servers leave `attempts` as `None`.

## [0.3.0] - 2026-10-11

### Added

- `Run.wait(progress=True)` keeps a line on stderr up to date while a run evolves; pass
  a callable instead to receive a `RunProgress` whenever the generation or status changes.
- Typed views alongside the raw responses: `Run.info` (`RunProgress`), `Project.info`
  (`ProjectInfo`: grammar, latest run, suite, last execution), `Execution.results`
  (`Verdict`s with Connect's message) and `Execution.inconclusive`.
- `Diteko()` reads `DITEKO_URL` when no address is given, as `diteko-mcp` already did.
- A getting-started guide in this README, and `examples/cxblueprint_to_excel.py` (and the
  same as a notebook): from a cxblueprint flow to an Excel test script.

### Fixed

- A project's last execution no longer reads as RUNNING for a moment after the execution
  reported COMPLETED (fixed server-side).

## [0.2.0] - 2026-10-11

### Added

- The first call checks that the server speaks this SDK's API major version
  (`GET /api/version`, API 1.x). A server on another major is refused with an
  error that says which side to upgrade; a server that predates version reporting
  gets a warning, not a failure. `Diteko(..., check_version=False)` skips the check.
- `Diteko.version()` returns the server's API version and build.
- API keys: the SDK and `diteko-mcp` send `DITEKO_API_KEY` (or `Diteko(api_key=...)`)
  to servers that require keys (API 1.1). A missing key, a refused key and a key
  without permission each raise a `DitekoError` that says which. `Diteko.whoami()`
  returns the key in use.

### Fixed

- Moving a record to another parent with a partial update (for example a grammar to
  another flow) no longer fails on the server (fixed server-side, Diteko API 1.0).

## [0.1.0] - 2026-10-10

### Added

- First release: projects from Connect exports, cxblueprint flows or a Connect
  instance; grammars as BNF or JSON; evolution with size and focus presets; suites
  exported as an Excel test script (ISO/IEC/IEEE 29119-3), JSON or Connect native
  tests; publishing to Connect, dry run by default.
- `diteko-mcp`, an MCP server exposing the same pipeline as eight tools, on `mcp`
  1.x or 2.x.

[0.4.0]: https://pypi.org/project/diteko/0.4.0/
[0.3.0]: https://pypi.org/project/diteko/0.3.0/
[0.2.0]: https://pypi.org/project/diteko/0.2.0/
[0.1.0]: https://pypi.org/project/diteko/0.1.0/
