stratyfy-cli 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.
Files changed (66) hide show
  1. stratyfy_cli-0.1.0/PKG-INFO +119 -0
  2. stratyfy_cli-0.1.0/README.md +106 -0
  3. stratyfy_cli-0.1.0/hatch_build.py +20 -0
  4. stratyfy_cli-0.1.0/pre_api_client/__init__.py +8 -0
  5. stratyfy_cli-0.1.0/pre_api_client/api/__init__.py +1 -0
  6. stratyfy_cli-0.1.0/pre_api_client/api/default/__init__.py +1 -0
  7. stratyfy_cli-0.1.0/pre_api_client/api/default/create_dataset.py +184 -0
  8. stratyfy_cli-0.1.0/pre_api_client/api/default/create_model_version.py +197 -0
  9. stratyfy_cli-0.1.0/pre_api_client/api/default/get_dataset.py +166 -0
  10. stratyfy_cli-0.1.0/pre_api_client/api/default/get_device_token.py +178 -0
  11. stratyfy_cli-0.1.0/pre_api_client/api/default/get_model.py +166 -0
  12. stratyfy_cli-0.1.0/pre_api_client/api/default/get_workspace.py +134 -0
  13. stratyfy_cli-0.1.0/pre_api_client/api/default/list_datasets.py +134 -0
  14. stratyfy_cli-0.1.0/pre_api_client/api/default/list_models.py +166 -0
  15. stratyfy_cli-0.1.0/pre_api_client/api/default/list_rules.py +171 -0
  16. stratyfy_cli-0.1.0/pre_api_client/api/default/predict.py +197 -0
  17. stratyfy_cli-0.1.0/pre_api_client/api/default/start_device_login.py +128 -0
  18. stratyfy_cli-0.1.0/pre_api_client/api/default/train_model.py +176 -0
  19. stratyfy_cli-0.1.0/pre_api_client/client.py +280 -0
  20. stratyfy_cli-0.1.0/pre_api_client/errors.py +16 -0
  21. stratyfy_cli-0.1.0/pre_api_client/models/__init__.py +69 -0
  22. stratyfy_cli-0.1.0/pre_api_client/models/clause.py +128 -0
  23. stratyfy_cli-0.1.0/pre_api_client/models/clause_operator.py +18 -0
  24. stratyfy_cli-0.1.0/pre_api_client/models/column.py +87 -0
  25. stratyfy_cli-0.1.0/pre_api_client/models/column_domain.py +10 -0
  26. stratyfy_cli-0.1.0/pre_api_client/models/create_dataset_body.py +85 -0
  27. stratyfy_cli-0.1.0/pre_api_client/models/create_model_version_body.py +75 -0
  28. stratyfy_cli-0.1.0/pre_api_client/models/dataset.py +116 -0
  29. stratyfy_cli-0.1.0/pre_api_client/models/device_code.py +101 -0
  30. stratyfy_cli-0.1.0/pre_api_client/models/device_token.py +93 -0
  31. stratyfy_cli-0.1.0/pre_api_client/models/device_token_token_type.py +8 -0
  32. stratyfy_cli-0.1.0/pre_api_client/models/error.py +81 -0
  33. stratyfy_cli-0.1.0/pre_api_client/models/error_code.py +25 -0
  34. stratyfy_cli-0.1.0/pre_api_client/models/get_device_token_body.py +61 -0
  35. stratyfy_cli-0.1.0/pre_api_client/models/list_datasets_response_200.py +75 -0
  36. stratyfy_cli-0.1.0/pre_api_client/models/list_models_response_200.py +75 -0
  37. stratyfy_cli-0.1.0/pre_api_client/models/list_rules_response_200.py +75 -0
  38. stratyfy_cli-0.1.0/pre_api_client/models/metrics.py +129 -0
  39. stratyfy_cli-0.1.0/pre_api_client/models/metrics_calibration_item.py +77 -0
  40. stratyfy_cli-0.1.0/pre_api_client/models/metrics_lift_item.py +77 -0
  41. stratyfy_cli-0.1.0/pre_api_client/models/model.py +234 -0
  42. stratyfy_cli-0.1.0/pre_api_client/models/model_status.py +11 -0
  43. stratyfy_cli-0.1.0/pre_api_client/models/predict_body.py +79 -0
  44. stratyfy_cli-0.1.0/pre_api_client/models/predict_body_rows_item.py +45 -0
  45. stratyfy_cli-0.1.0/pre_api_client/models/predict_response_200.py +75 -0
  46. stratyfy_cli-0.1.0/pre_api_client/models/prediction.py +112 -0
  47. stratyfy_cli-0.1.0/pre_api_client/models/prediction_fired_rules_item.py +99 -0
  48. stratyfy_cli-0.1.0/pre_api_client/models/rule.py +138 -0
  49. stratyfy_cli-0.1.0/pre_api_client/models/rule_change.py +95 -0
  50. stratyfy_cli-0.1.0/pre_api_client/models/rule_change_action.py +10 -0
  51. stratyfy_cli-0.1.0/pre_api_client/models/rule_input.py +96 -0
  52. stratyfy_cli-0.1.0/pre_api_client/models/train_request.py +94 -0
  53. stratyfy_cli-0.1.0/pre_api_client/models/workspace.py +69 -0
  54. stratyfy_cli-0.1.0/pre_api_client/types.py +54 -0
  55. stratyfy_cli-0.1.0/pre_cli/__init__.py +0 -0
  56. stratyfy_cli-0.1.0/pre_cli/config.py +31 -0
  57. stratyfy_cli-0.1.0/pre_cli/data.py +92 -0
  58. stratyfy_cli-0.1.0/pre_cli/main.py +749 -0
  59. stratyfy_cli-0.1.0/pre_cli/sandbox.py +94 -0
  60. stratyfy_cli-0.1.0/pre_cli/schema.py +72 -0
  61. stratyfy_cli-0.1.0/pre_report/__init__.py +4 -0
  62. stratyfy_cli-0.1.0/pre_report/render.py +124 -0
  63. stratyfy_cli-0.1.0/pre_report/template.html +49 -0
  64. stratyfy_cli-0.1.0/pre_report/types.py +30 -0
  65. stratyfy_cli-0.1.0/pre_words/__init__.py +105 -0
  66. stratyfy_cli-0.1.0/pyproject.toml +47 -0
@@ -0,0 +1,119 @@
1
+ Metadata-Version: 2.5
2
+ Name: stratyfy-cli
3
+ Version: 0.1.0
4
+ Summary: The pre command: describe a database, train a PRE model, read its rules
5
+ Requires-Python: <4,>=3.11
6
+ Requires-Dist: attrs>=23
7
+ Requires-Dist: httpx<1,>=0.27
8
+ Requires-Dist: psycopg[binary]<4,>=3.1
9
+ Requires-Dist: python-dateutil>=2.8
10
+ Requires-Dist: sqlalchemy<3,>=2.0
11
+ Requires-Dist: typer>=0.12
12
+ Description-Content-Type: text/markdown
13
+
14
+ # pre-cli
15
+
16
+ The `pre` command. A coding agent runs it to describe the user's database, train a PRE model, read its rules and
17
+ score cases.
18
+ It talks to the PRE API through `packages/pre-api-client`. It never imports PRE. Its sentences come from
19
+ `packages/pre-words`, so they match the report.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pipx install stratyfy-cli # or: uv tool install stratyfy-cli
25
+ ```
26
+
27
+ For development, run `poetry install` in this folder, then `poetry run pre --help`. `pre --version` prints the
28
+ version.
29
+
30
+ The wheel and the sdist bundle `pre_api_client`, `pre_words` and `pre_report` from `packages/`, so the install
31
+ needs nothing else from this repo. `hatch_build.py` copies them in at build time. The CLI uses hatchling to build,
32
+ because poetry-core cannot include files from outside this folder. Poetry still manages the development env.
33
+
34
+ ## Commands
35
+
36
+ Each command prints a short text result and a next step. Add `--json` to get one JSON object instead. On
37
+ failure the command exits with 1 and prints `code`, `message` and `next_step`.
38
+
39
+ | Command | What it does |
40
+ | --- | --- |
41
+ | `pre login [--no-wait] [--api-url <url>]` | Signs in as `gh auth login` does. It prints a link and a code. The user signs in with an email code and approves the device. Then it writes a new key for this machine to `.pre/config.json`. With `--no-wait` it prints the link and exits; the next `pre login` waits for that same sign-in. That way an agent can show the user the link first. The default API is `https://api.stratyfy.ai/v1`. |
42
+ | `pre logout` | Removes the key from `.pre/config.json`. The key keeps working until an admin revokes it, so the output names its id. |
43
+ | `pre init --api-key <key> [--api-url <url>]` | Writes a key you already have to `.pre/config.json`. The key can also come from `PRE_API_KEY`. |
44
+ | `pre connect <db>` | Describes a SQLite or Postgres database: tables, row counts, columns, types, null share, sample values and foreign keys. It opens the database read-only. |
45
+ | `pre setup claude [--yes] [--dry-run] [--local]` | For Claude Code. Run it in your own terminal. It adds the API host and a remote Postgres host to Claude Code's sandbox network allowlist. It shows the change and asks first. It refuses to write when an agent runs it. See [Sandboxes](#sandboxes). |
46
+ | `pre status` | Checks that the API answers and accepts the key, and names the workspace. Lists the datasets and models this folder made. |
47
+ | `pre outcome define --name <slug> --sql <query> [--case account --cases accounts --event churn --event-past churned]` | Runs the outcome query and stores it. The query returns one row per case: the case id, then a 0/1 label. It refuses no rows, a label that is not 0 or 1, a repeated case id and a label that is always the same. The words go into every sentence the CLI prints. It guesses each word you leave out from the column names and says so. |
48
+ | `pre outcome show` | Lists the stored outcomes and their queries. |
49
+ | `pre extract --outcome <slug> --features <query>` | Runs the features query (the case id, then the features), joins it to the label and writes `.pre/data/<slug>.csv`. Nothing is sent. |
50
+ | `pre extract --outcome <slug> --upload [--refresh]` | Sends the CSV the dry run wrote, unchanged. With `--refresh`, or with no CSV yet, it runs the queries again first. |
51
+ | `pre train --outcome <slug> [--max-rules N] [--max-conditions N] [--no-wait] [--timeout S]` | Trains on the outcome's uploaded dataset. Every column but `id` is an input. It waits until the model is ready or failed and prints progress to stderr. When ready, it says how well the model predicts on held-out cases: the AUC and how many times the average rate the top 10% hold. |
52
+ | `pre rules --outcome <slug>` or `pre rules --model <id>` | Prints the rules as sentences, strongest first, with points and direction. The baseline shows as the starting rate. `--json` gives the API's rules. |
53
+ | `pre predict --outcome <slug> [--features <query>]` | Scores cases with the outcome's model. The query returns the case id, then the model inputs as of today, for example for the accounts still active. Without `--features` it scores the training cases as of their snapshot date and says so. It writes `.pre/data/<slug>.scores.csv`: the case id, the score, the points and the rules that fired as sentences, highest score first. It also writes `.pre/data/<slug>.scores.json` for `pre report`: the same scores with the rule ids that fired. It refuses a query that lacks an input or repeats a case id. |
54
+ | `pre report --outcome <slug> [--actions <file.json>] [--labels <file.json>] [--top N]` | Writes `.pre/reports/<slug>.html` and `.pre/reports/<slug>.csv` from the scores `pre predict` saved. It sends nothing. The page says how well the model predicts, lists the top rules as sentences and the `N` cases most likely to have the outcome, with their reasons. The CSV holds the same cases. `--actions` maps a rule id to one suggested action. `--labels` maps an input column to a plain label. It refuses a rule id or a column the model lacks. When the scores came from the training cases, the page says so. |
55
+
56
+ `<db>` is a SQLite file path, `sqlite:///path`, or `postgresql://user:password@host:5432/db`. Use `--schema` for
57
+ a Postgres schema other than `public`.
58
+
59
+ A `<query>` is the SQL itself or a path that ends in `.sql`. It must be one SELECT or WITH statement. The CLI
60
+ opens SQLite with `mode=ro` and runs Postgres in a read-only transaction, so a query cannot change data. For
61
+ Postgres, a database user with read access only is safer still.
62
+
63
+ ## What leaves the machine
64
+
65
+ The CSV that `pre extract --upload` sends, and the rows that `pre predict` sends. Before it writes the CSV, `pre extract`:
66
+
67
+ 1. Drops a column when every word of its name is an identifier word and one of them is a core word. Core words
68
+ are `email`, `phone`, `mobile`, `ip`, `address`, `street`, `ssn`, `name` and `username`. The other identifier
69
+ words are `mail`, `number`, `first`, `last`, `full`, `user`, `contact`, `home` and `work`. So `work_email` and
70
+ `first_name` go, and `plan_name` and `address_verified` stay.
71
+ 2. Drops a column when a text value holds an email address or looks like a phone number.
72
+ 3. Replaces the case id with an HMAC of it in a column named `id`. The key is in `.pre/secret` (mode 600).
73
+ `.pre/data/<slug>.ids.csv` maps each hash back to the case id. It stays on this machine.
74
+
75
+ The output names each dropped column and the reason.
76
+
77
+ `pre predict` sends each case as the same HMAC of its id and the model's inputs, in batches of 1,000. It sends no
78
+ other column. It maps the hashes back to the case ids in memory.
79
+
80
+ ## Sandboxes
81
+
82
+ `pre setup` writes the smallest change it can. It merges into the file, so a second run changes nothing.
83
+
84
+ It writes `.claude/settings.json`, key `sandbox.network.allowedDomains`. For a Postgres on this machine, `--local`
85
+ also sets `sandbox.network.allowLocalBinding`, which opens every local port. It never sets `allowAllUnixSockets`.
86
+ Claude Code is the only agent at launch; others are on the roadmap. The onboard skill comes with the plugin
87
+ (`tools/agent-plugin`), not with `pre setup`.
88
+
89
+ These key names come from secondary sources. They live in `pre_cli/sandbox.py`.
90
+
91
+ ## Local config
92
+
93
+ `.pre/config.json` holds the API URL, the key and its id, the database URL, the outcome queries and the dataset
94
+ and model ids that later commands create. While a `pre login --no-wait` waits, it also holds that sign-in. The API
95
+ can also list every dataset and model in the workspace. Each command that writes the config also writes
96
+ `.pre/.gitignore`, so git ignores the folder. Set `PRE_DIR` to use another folder.
97
+
98
+ ## Tests
99
+
100
+ `scripts/check pre-cli` runs ruff and pytest. The `pre status` tests start a Prism mock of
101
+ `services/pre-api/openapi.yaml` with `npx`. They skip when `npx` is missing.
102
+
103
+ `tests/test_package.py` builds the sdist, a wheel from that sdist and a wheel from this folder. It fails if one
104
+ holds a file outside `pre_cli` and the three bundled packages, a `services`, `apps` or `ground_truth` path, a
105
+ `seed.py` or `check.py`, or the name of the PRE library. PRE must never reach PyPI.
106
+
107
+ ## Release
108
+
109
+ The CLI is on PyPI as `stratyfy-cli`. The command it installs is `pre`. `.github/workflows/release-cli.yml`
110
+ publishes it with trusted publishing, so no PyPI token is stored.
111
+
112
+ 1. Set the new `version` in `pyproject.toml`. It is the only place that holds the version.
113
+ 2. Merge that change to `master`. CI runs `scripts/check pre-cli`, which builds the package and checks its files.
114
+ 3. Tag the merge commit and push the tag: `git tag stratyfy-cli-v<version>`, then
115
+ `git push origin stratyfy-cli-v<version>`.
116
+ 4. The workflow checks that the tag matches the version, builds, installs the wheel and runs `pre --version`. Then
117
+ it publishes in the GitHub environment `pypi`.
118
+
119
+ Users install it with Python 3.11 or later: `pipx install stratyfy-cli`.
@@ -0,0 +1,106 @@
1
+ # pre-cli
2
+
3
+ The `pre` command. A coding agent runs it to describe the user's database, train a PRE model, read its rules and
4
+ score cases.
5
+ It talks to the PRE API through `packages/pre-api-client`. It never imports PRE. Its sentences come from
6
+ `packages/pre-words`, so they match the report.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pipx install stratyfy-cli # or: uv tool install stratyfy-cli
12
+ ```
13
+
14
+ For development, run `poetry install` in this folder, then `poetry run pre --help`. `pre --version` prints the
15
+ version.
16
+
17
+ The wheel and the sdist bundle `pre_api_client`, `pre_words` and `pre_report` from `packages/`, so the install
18
+ needs nothing else from this repo. `hatch_build.py` copies them in at build time. The CLI uses hatchling to build,
19
+ because poetry-core cannot include files from outside this folder. Poetry still manages the development env.
20
+
21
+ ## Commands
22
+
23
+ Each command prints a short text result and a next step. Add `--json` to get one JSON object instead. On
24
+ failure the command exits with 1 and prints `code`, `message` and `next_step`.
25
+
26
+ | Command | What it does |
27
+ | --- | --- |
28
+ | `pre login [--no-wait] [--api-url <url>]` | Signs in as `gh auth login` does. It prints a link and a code. The user signs in with an email code and approves the device. Then it writes a new key for this machine to `.pre/config.json`. With `--no-wait` it prints the link and exits; the next `pre login` waits for that same sign-in. That way an agent can show the user the link first. The default API is `https://api.stratyfy.ai/v1`. |
29
+ | `pre logout` | Removes the key from `.pre/config.json`. The key keeps working until an admin revokes it, so the output names its id. |
30
+ | `pre init --api-key <key> [--api-url <url>]` | Writes a key you already have to `.pre/config.json`. The key can also come from `PRE_API_KEY`. |
31
+ | `pre connect <db>` | Describes a SQLite or Postgres database: tables, row counts, columns, types, null share, sample values and foreign keys. It opens the database read-only. |
32
+ | `pre setup claude [--yes] [--dry-run] [--local]` | For Claude Code. Run it in your own terminal. It adds the API host and a remote Postgres host to Claude Code's sandbox network allowlist. It shows the change and asks first. It refuses to write when an agent runs it. See [Sandboxes](#sandboxes). |
33
+ | `pre status` | Checks that the API answers and accepts the key, and names the workspace. Lists the datasets and models this folder made. |
34
+ | `pre outcome define --name <slug> --sql <query> [--case account --cases accounts --event churn --event-past churned]` | Runs the outcome query and stores it. The query returns one row per case: the case id, then a 0/1 label. It refuses no rows, a label that is not 0 or 1, a repeated case id and a label that is always the same. The words go into every sentence the CLI prints. It guesses each word you leave out from the column names and says so. |
35
+ | `pre outcome show` | Lists the stored outcomes and their queries. |
36
+ | `pre extract --outcome <slug> --features <query>` | Runs the features query (the case id, then the features), joins it to the label and writes `.pre/data/<slug>.csv`. Nothing is sent. |
37
+ | `pre extract --outcome <slug> --upload [--refresh]` | Sends the CSV the dry run wrote, unchanged. With `--refresh`, or with no CSV yet, it runs the queries again first. |
38
+ | `pre train --outcome <slug> [--max-rules N] [--max-conditions N] [--no-wait] [--timeout S]` | Trains on the outcome's uploaded dataset. Every column but `id` is an input. It waits until the model is ready or failed and prints progress to stderr. When ready, it says how well the model predicts on held-out cases: the AUC and how many times the average rate the top 10% hold. |
39
+ | `pre rules --outcome <slug>` or `pre rules --model <id>` | Prints the rules as sentences, strongest first, with points and direction. The baseline shows as the starting rate. `--json` gives the API's rules. |
40
+ | `pre predict --outcome <slug> [--features <query>]` | Scores cases with the outcome's model. The query returns the case id, then the model inputs as of today, for example for the accounts still active. Without `--features` it scores the training cases as of their snapshot date and says so. It writes `.pre/data/<slug>.scores.csv`: the case id, the score, the points and the rules that fired as sentences, highest score first. It also writes `.pre/data/<slug>.scores.json` for `pre report`: the same scores with the rule ids that fired. It refuses a query that lacks an input or repeats a case id. |
41
+ | `pre report --outcome <slug> [--actions <file.json>] [--labels <file.json>] [--top N]` | Writes `.pre/reports/<slug>.html` and `.pre/reports/<slug>.csv` from the scores `pre predict` saved. It sends nothing. The page says how well the model predicts, lists the top rules as sentences and the `N` cases most likely to have the outcome, with their reasons. The CSV holds the same cases. `--actions` maps a rule id to one suggested action. `--labels` maps an input column to a plain label. It refuses a rule id or a column the model lacks. When the scores came from the training cases, the page says so. |
42
+
43
+ `<db>` is a SQLite file path, `sqlite:///path`, or `postgresql://user:password@host:5432/db`. Use `--schema` for
44
+ a Postgres schema other than `public`.
45
+
46
+ A `<query>` is the SQL itself or a path that ends in `.sql`. It must be one SELECT or WITH statement. The CLI
47
+ opens SQLite with `mode=ro` and runs Postgres in a read-only transaction, so a query cannot change data. For
48
+ Postgres, a database user with read access only is safer still.
49
+
50
+ ## What leaves the machine
51
+
52
+ The CSV that `pre extract --upload` sends, and the rows that `pre predict` sends. Before it writes the CSV, `pre extract`:
53
+
54
+ 1. Drops a column when every word of its name is an identifier word and one of them is a core word. Core words
55
+ are `email`, `phone`, `mobile`, `ip`, `address`, `street`, `ssn`, `name` and `username`. The other identifier
56
+ words are `mail`, `number`, `first`, `last`, `full`, `user`, `contact`, `home` and `work`. So `work_email` and
57
+ `first_name` go, and `plan_name` and `address_verified` stay.
58
+ 2. Drops a column when a text value holds an email address or looks like a phone number.
59
+ 3. Replaces the case id with an HMAC of it in a column named `id`. The key is in `.pre/secret` (mode 600).
60
+ `.pre/data/<slug>.ids.csv` maps each hash back to the case id. It stays on this machine.
61
+
62
+ The output names each dropped column and the reason.
63
+
64
+ `pre predict` sends each case as the same HMAC of its id and the model's inputs, in batches of 1,000. It sends no
65
+ other column. It maps the hashes back to the case ids in memory.
66
+
67
+ ## Sandboxes
68
+
69
+ `pre setup` writes the smallest change it can. It merges into the file, so a second run changes nothing.
70
+
71
+ It writes `.claude/settings.json`, key `sandbox.network.allowedDomains`. For a Postgres on this machine, `--local`
72
+ also sets `sandbox.network.allowLocalBinding`, which opens every local port. It never sets `allowAllUnixSockets`.
73
+ Claude Code is the only agent at launch; others are on the roadmap. The onboard skill comes with the plugin
74
+ (`tools/agent-plugin`), not with `pre setup`.
75
+
76
+ These key names come from secondary sources. They live in `pre_cli/sandbox.py`.
77
+
78
+ ## Local config
79
+
80
+ `.pre/config.json` holds the API URL, the key and its id, the database URL, the outcome queries and the dataset
81
+ and model ids that later commands create. While a `pre login --no-wait` waits, it also holds that sign-in. The API
82
+ can also list every dataset and model in the workspace. Each command that writes the config also writes
83
+ `.pre/.gitignore`, so git ignores the folder. Set `PRE_DIR` to use another folder.
84
+
85
+ ## Tests
86
+
87
+ `scripts/check pre-cli` runs ruff and pytest. The `pre status` tests start a Prism mock of
88
+ `services/pre-api/openapi.yaml` with `npx`. They skip when `npx` is missing.
89
+
90
+ `tests/test_package.py` builds the sdist, a wheel from that sdist and a wheel from this folder. It fails if one
91
+ holds a file outside `pre_cli` and the three bundled packages, a `services`, `apps` or `ground_truth` path, a
92
+ `seed.py` or `check.py`, or the name of the PRE library. PRE must never reach PyPI.
93
+
94
+ ## Release
95
+
96
+ The CLI is on PyPI as `stratyfy-cli`. The command it installs is `pre`. `.github/workflows/release-cli.yml`
97
+ publishes it with trusted publishing, so no PyPI token is stored.
98
+
99
+ 1. Set the new `version` in `pyproject.toml`. It is the only place that holds the version.
100
+ 2. Merge that change to `master`. CI runs `scripts/check pre-cli`, which builds the package and checks its files.
101
+ 3. Tag the merge commit and push the tag: `git tag stratyfy-cli-v<version>`, then
102
+ `git push origin stratyfy-cli-v<version>`.
103
+ 4. The workflow checks that the tag matches the version, builds, installs the wheel and runs `pre --version`. Then
104
+ it publishes in the GitHub environment `pypi`.
105
+
106
+ Users install it with Python 3.11 or later: `pipx install stratyfy-cli`.
@@ -0,0 +1,20 @@
1
+ """Bundle the three sibling packages into the sdist and the wheel, so `pipx install` needs nothing from this repo.
2
+
3
+ In the repo they live in `packages/`. An sdist holds them next to `pre_cli`, so a wheel built from it finds them.
4
+ """
5
+
6
+ from pathlib import Path
7
+
8
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
9
+
10
+ PACKAGES = Path(__file__).resolve().parents[2] / "packages"
11
+ BUNDLED = {"pre_api_client": "pre-api-client", "pre_words": "pre-words", "pre_report": "pre-report"}
12
+
13
+
14
+ class CustomBuildHook(BuildHookInterface):
15
+ def initialize(self, version, build_data):
16
+ # hatchling puts the repo's .gitignore in the sdist. It is not part of the CLI.
17
+ build_data["force_include"] = {k: v for k, v in build_data["force_include"].items() if v != ".gitignore"}
18
+ for name, folder in BUNDLED.items():
19
+ if not (Path(self.root) / name).is_dir():
20
+ build_data["force_include"][str(PACKAGES / folder / name)] = name
@@ -0,0 +1,8 @@
1
+ """A client library for accessing PRE API"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -0,0 +1 @@
1
+ """Contains methods for accessing the API"""
@@ -0,0 +1 @@
1
+ """Contains endpoint functions for accessing the API"""
@@ -0,0 +1,184 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+
4
+ import httpx
5
+
6
+ from ... import errors
7
+ from ...client import AuthenticatedClient, Client
8
+ from ...models.create_dataset_body import CreateDatasetBody
9
+ from ...models.dataset import Dataset
10
+ from ...models.error import Error
11
+ from ...types import Response
12
+
13
+
14
+ def _get_kwargs(
15
+ *,
16
+ body: CreateDatasetBody,
17
+ ) -> dict[str, Any]:
18
+ headers: dict[str, Any] = {}
19
+
20
+ _kwargs: dict[str, Any] = {
21
+ "method": "post",
22
+ "url": "/datasets",
23
+ }
24
+
25
+ _kwargs["files"] = body.to_multipart()
26
+
27
+ headers["Content-Type"] = "multipart/form-data; boundary=+++"
28
+
29
+ _kwargs["headers"] = headers
30
+ return _kwargs
31
+
32
+
33
+ def _parse_response(
34
+ *, client: AuthenticatedClient | Client, response: httpx.Response
35
+ ) -> Dataset | Error | None:
36
+ if response.status_code == 201:
37
+ response_201 = Dataset.from_dict(response.json())
38
+
39
+ return response_201
40
+
41
+ if response.status_code == 400:
42
+ response_400 = Error.from_dict(response.json())
43
+
44
+ return response_400
45
+
46
+ if response.status_code == 401:
47
+ response_401 = Error.from_dict(response.json())
48
+
49
+ return response_401
50
+
51
+ if response.status_code == 413:
52
+ response_413 = Error.from_dict(response.json())
53
+
54
+ return response_413
55
+
56
+ if client.raise_on_unexpected_status:
57
+ raise errors.UnexpectedStatus(response.status_code, response.content)
58
+ else:
59
+ return None
60
+
61
+
62
+ def _build_response(
63
+ *, client: AuthenticatedClient | Client, response: httpx.Response
64
+ ) -> Response[Dataset | Error]:
65
+ return Response(
66
+ status_code=HTTPStatus(response.status_code),
67
+ content=response.content,
68
+ headers=response.headers,
69
+ parsed=_parse_response(client=client, response=response),
70
+ )
71
+
72
+
73
+ def sync_detailed(
74
+ *,
75
+ client: AuthenticatedClient,
76
+ body: CreateDatasetBody,
77
+ ) -> Response[Dataset | Error]:
78
+ """Upload labelled training data as a CSV or parquet file. The response holds the profile.
79
+
80
+ The API takes a file only. It does not take rows as JSON.
81
+
82
+ Args:
83
+ body (CreateDatasetBody):
84
+
85
+ Raises:
86
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
87
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
88
+
89
+ Returns:
90
+ Response[Dataset | Error]
91
+ """
92
+
93
+ kwargs = _get_kwargs(
94
+ body=body,
95
+ )
96
+
97
+ response = client.get_httpx_client().request(
98
+ **kwargs,
99
+ )
100
+
101
+ return _build_response(client=client, response=response)
102
+
103
+
104
+ def sync(
105
+ *,
106
+ client: AuthenticatedClient,
107
+ body: CreateDatasetBody,
108
+ ) -> Dataset | Error | None:
109
+ """Upload labelled training data as a CSV or parquet file. The response holds the profile.
110
+
111
+ The API takes a file only. It does not take rows as JSON.
112
+
113
+ Args:
114
+ body (CreateDatasetBody):
115
+
116
+ Raises:
117
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
118
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
119
+
120
+ Returns:
121
+ Dataset | Error
122
+ """
123
+
124
+ return sync_detailed(
125
+ client=client,
126
+ body=body,
127
+ ).parsed
128
+
129
+
130
+ async def asyncio_detailed(
131
+ *,
132
+ client: AuthenticatedClient,
133
+ body: CreateDatasetBody,
134
+ ) -> Response[Dataset | Error]:
135
+ """Upload labelled training data as a CSV or parquet file. The response holds the profile.
136
+
137
+ The API takes a file only. It does not take rows as JSON.
138
+
139
+ Args:
140
+ body (CreateDatasetBody):
141
+
142
+ Raises:
143
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
144
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
145
+
146
+ Returns:
147
+ Response[Dataset | Error]
148
+ """
149
+
150
+ kwargs = _get_kwargs(
151
+ body=body,
152
+ )
153
+
154
+ response = await client.get_async_httpx_client().request(**kwargs)
155
+
156
+ return _build_response(client=client, response=response)
157
+
158
+
159
+ async def asyncio(
160
+ *,
161
+ client: AuthenticatedClient,
162
+ body: CreateDatasetBody,
163
+ ) -> Dataset | Error | None:
164
+ """Upload labelled training data as a CSV or parquet file. The response holds the profile.
165
+
166
+ The API takes a file only. It does not take rows as JSON.
167
+
168
+ Args:
169
+ body (CreateDatasetBody):
170
+
171
+ Raises:
172
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
173
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
174
+
175
+ Returns:
176
+ Dataset | Error
177
+ """
178
+
179
+ return (
180
+ await asyncio_detailed(
181
+ client=client,
182
+ body=body,
183
+ )
184
+ ).parsed
@@ -0,0 +1,197 @@
1
+ from http import HTTPStatus
2
+ from typing import Any
3
+ from urllib.parse import quote
4
+
5
+ import httpx
6
+
7
+ from ... import errors
8
+ from ...client import AuthenticatedClient, Client
9
+ from ...models.create_model_version_body import CreateModelVersionBody
10
+ from ...models.error import Error
11
+ from ...models.model import Model
12
+ from ...types import Response
13
+
14
+
15
+ def _get_kwargs(
16
+ model_id: str,
17
+ *,
18
+ body: CreateModelVersionBody,
19
+ ) -> dict[str, Any]:
20
+ headers: dict[str, Any] = {}
21
+
22
+ _kwargs: dict[str, Any] = {
23
+ "method": "post",
24
+ "url": "/models/{model_id}/versions".format(
25
+ model_id=quote(str(model_id), safe=""),
26
+ ),
27
+ }
28
+
29
+ _kwargs["json"] = body.to_dict()
30
+
31
+ headers["Content-Type"] = "application/json"
32
+
33
+ _kwargs["headers"] = headers
34
+ return _kwargs
35
+
36
+
37
+ def _parse_response(
38
+ *, client: AuthenticatedClient | Client, response: httpx.Response
39
+ ) -> Error | Model | None:
40
+ if response.status_code == 202:
41
+ response_202 = Model.from_dict(response.json())
42
+
43
+ return response_202
44
+
45
+ if response.status_code == 400:
46
+ response_400 = Error.from_dict(response.json())
47
+
48
+ return response_400
49
+
50
+ if response.status_code == 401:
51
+ response_401 = Error.from_dict(response.json())
52
+
53
+ return response_401
54
+
55
+ if response.status_code == 404:
56
+ response_404 = Error.from_dict(response.json())
57
+
58
+ return response_404
59
+
60
+ if response.status_code == 409:
61
+ response_409 = Error.from_dict(response.json())
62
+
63
+ return response_409
64
+
65
+ if client.raise_on_unexpected_status:
66
+ raise errors.UnexpectedStatus(response.status_code, response.content)
67
+ else:
68
+ return None
69
+
70
+
71
+ def _build_response(
72
+ *, client: AuthenticatedClient | Client, response: httpx.Response
73
+ ) -> Response[Error | Model]:
74
+ return Response(
75
+ status_code=HTTPStatus(response.status_code),
76
+ content=response.content,
77
+ headers=response.headers,
78
+ parsed=_parse_response(client=client, response=response),
79
+ )
80
+
81
+
82
+ def sync_detailed(
83
+ model_id: str,
84
+ *,
85
+ client: AuthenticatedClient,
86
+ body: CreateModelVersionBody,
87
+ ) -> Response[Error | Model]:
88
+ """Add, edit or remove rules. This makes a new version and refits it. The old version does not change.
89
+
90
+ Args:
91
+ model_id (str):
92
+ body (CreateModelVersionBody):
93
+
94
+ Raises:
95
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
96
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
97
+
98
+ Returns:
99
+ Response[Error | Model]
100
+ """
101
+
102
+ kwargs = _get_kwargs(
103
+ model_id=model_id,
104
+ body=body,
105
+ )
106
+
107
+ response = client.get_httpx_client().request(
108
+ **kwargs,
109
+ )
110
+
111
+ return _build_response(client=client, response=response)
112
+
113
+
114
+ def sync(
115
+ model_id: str,
116
+ *,
117
+ client: AuthenticatedClient,
118
+ body: CreateModelVersionBody,
119
+ ) -> Error | Model | None:
120
+ """Add, edit or remove rules. This makes a new version and refits it. The old version does not change.
121
+
122
+ Args:
123
+ model_id (str):
124
+ body (CreateModelVersionBody):
125
+
126
+ Raises:
127
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
128
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
129
+
130
+ Returns:
131
+ Error | Model
132
+ """
133
+
134
+ return sync_detailed(
135
+ model_id=model_id,
136
+ client=client,
137
+ body=body,
138
+ ).parsed
139
+
140
+
141
+ async def asyncio_detailed(
142
+ model_id: str,
143
+ *,
144
+ client: AuthenticatedClient,
145
+ body: CreateModelVersionBody,
146
+ ) -> Response[Error | Model]:
147
+ """Add, edit or remove rules. This makes a new version and refits it. The old version does not change.
148
+
149
+ Args:
150
+ model_id (str):
151
+ body (CreateModelVersionBody):
152
+
153
+ Raises:
154
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
155
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
156
+
157
+ Returns:
158
+ Response[Error | Model]
159
+ """
160
+
161
+ kwargs = _get_kwargs(
162
+ model_id=model_id,
163
+ body=body,
164
+ )
165
+
166
+ response = await client.get_async_httpx_client().request(**kwargs)
167
+
168
+ return _build_response(client=client, response=response)
169
+
170
+
171
+ async def asyncio(
172
+ model_id: str,
173
+ *,
174
+ client: AuthenticatedClient,
175
+ body: CreateModelVersionBody,
176
+ ) -> Error | Model | None:
177
+ """Add, edit or remove rules. This makes a new version and refits it. The old version does not change.
178
+
179
+ Args:
180
+ model_id (str):
181
+ body (CreateModelVersionBody):
182
+
183
+ Raises:
184
+ errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
185
+ httpx.TimeoutException: If the request takes longer than Client.timeout.
186
+
187
+ Returns:
188
+ Error | Model
189
+ """
190
+
191
+ return (
192
+ await asyncio_detailed(
193
+ model_id=model_id,
194
+ client=client,
195
+ body=body,
196
+ )
197
+ ).parsed