From 82388f0815028ff39609ed8e0e35054d451946ea Mon Sep 17 00:00:00 2001 From: jgjesdal Date: Mon, 28 Sep 2026 12:34:48 +0200 Subject: [PATCH 1/2] docs: Python and Rust cover the rest of the api Follows dataplatform-rust-sdk's feat/cover-remaining-endpoints; merge alongside it. - resources: functions filter/search and a real /functions/byids in Python and Rust (the "catching up" note goes); graph export/import in all three clients, with tabs - timeseries: get_by_id, the value-type hint in all three clients, the live tail in the coverage table; drop Rust `list_with_limit`, which the SDK removed - datasets: get by numeric id - tenant: every call in all three clients, with tabs - subscriptions: document the /timeseries/datapoints/listen tail, subprotocol auth included - client: tenant is in every client; the tail is no longer "unwrapped" nor `?token=` Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: jgjesdal --- docs/reference/client.md | 17 ++--- docs/reference/datasets.md | 7 ++ docs/reference/resources.md | 82 ++++++++++++++++---- docs/reference/subscriptions.md | 55 ++++++++++++++ docs/reference/tenant.md | 128 ++++++++++++++++++++++++++++---- docs/reference/timeseries.md | 40 +++++++++- 6 files changed, 285 insertions(+), 44 deletions(-) diff --git a/docs/reference/client.md b/docs/reference/client.md index 1aff1df..6035ee7 100644 --- a/docs/reference/client.md +++ b/docs/reference/client.md @@ -74,8 +74,9 @@ let api = blocking::create_api_service(); ``` The blocking client covers most of the async one, not all of it. It has no `subscriptions`, and -it lacks `time_series.filter`, `resources.filter`, `resources.get_by_id`, -`resources.fetch_nearest`, `events.get`, `events.update` and `events.count`. `api.async_api()` +it lacks `time_series.filter`, `time_series.listen_datapoints`, `resources.filter`, +`resources.get_by_id`, `resources.fetch_nearest`, `events.get`, `events.update` and +`events.count`. `api.async_api()` hands you the async service for those. @@ -96,26 +97,22 @@ hands you the async service for those. | Subscriptions | `client.subscriptions()` | `client.subscriptions` | `api.subscriptions` | | Edges | `client.edges()` | `client.edges` | `api.edges` | | [Labels](./labels) | `client.labels()` | `client.labels` | `api.labels` | +| [Tenant](./tenant) | `client.tenant()` | `client.tenant` | `api.tenant` | Assets and functions are the `ASSET`- and `FUNCTION`-labelled resources, typed. A service being in a client does not mean every call is: each reference page has a "What each client covers" table. -The Java client adds three more services: +The Java client adds two more services: | Service | Java | Covers | | --- | --- | --- | | [Policies](./policies) | `client.policies()` | `/policies`, including the naming dry-run | | [Governance templates](./policies#governance-templates) | `client.governance()` | `/governance/templates` | -| [Tenant](./tenant) | `client.tenant()` | `/tenant/features` and `/tenant/settings` | -:::note Two endpoints no client wraps, on purpose +:::note One endpoint no client wraps, on purpose `GET /stats` is internal to the console and marked hidden: it is not part of the published -contract, so nothing should call it. The browser datapoint tail -`/timeseries/datapoints/listen` authenticates with a `?token=` query parameter, which is for a -page that cannot set a header. Server-side code wants the durable equivalent instead, -[`subscriptions().listen`](./subscriptions#live-delivery), which authenticates the upgrade with -the usual `Authorization` header and survives a reconnect. +contract, so nothing should call it. ::: ## Authentication diff --git a/docs/reference/datasets.md b/docs/reference/datasets.md index 092f5ab..f39ccde 100644 --- a/docs/reference/datasets.md +++ b/docs/reference/datasets.md @@ -71,12 +71,16 @@ api.datasets.create(&vec![dataset]).await?; ## Look up & delete +`by_ids` takes ids and external ids together and leaves out what it cannot find rather than +failing. `GET /datasets/{id}` reads one data set by numeric id and answers `404` for a miss. + ```java DataWrapper some = client.datasets() .byIds(List.of(IdCollection.createFromExternalId("plant_a"))); +DataWrapper one = client.datasets().getById(5677892L); client.datasets().delete(List.of(IdCollection.createFromExternalId("plant_a"))); ``` @@ -86,6 +90,7 @@ client.datasets().delete(List.of(IdCollection.createFromExternalId("plant_a"))); ```python some = client.datasets.by_ids(["plant_a"]) +one = client.datasets.get_by_id(5677892) client.datasets.delete(["plant_a"]) ``` @@ -96,6 +101,7 @@ client.datasets.delete(["plant_a"]) use intellistream_datahub_sdk::generic::IdAndExtId; let some = api.datasets.by_ids(&vec![IdAndExtId::from_external_id("plant_a")]).await?; +let one = api.datasets.get_by_id(5677892).await?; api.datasets.delete(&vec![IdAndExtId::from_external_id("plant_a")]).await?; ``` @@ -241,6 +247,7 @@ A changed grant therefore takes effect within about a minute, without a new toke | Operation | Java | Python | Rust | | --- | --- | --- | --- | | Create | `datasets().create` | `datasets.create` | `datasets.create` | +| Get by numeric id | `datasets().getById` | `datasets.get_by_id` | `datasets.get_by_id` | | Look up by id / external id | `datasets().byIds` | `datasets.by_ids` | `datasets.by_ids` | | List (`GET /datasets`) | `datasets().list(limit)` | `datasets.list(limit=None)` | `datasets.list(limit)` | | Filter | `datasets().filter` | `datasets.filter` | `datasets.filter` | diff --git a/docs/reference/resources.md b/docs/reference/resources.md index 68546cd..f0cb520 100644 --- a/docs/reference/resources.md +++ b/docs/reference/resources.md @@ -896,9 +896,13 @@ The nodes and relationships it creates count against their own daily quotas thro ordinary create path. A reverse proxy in front of the API has to pass the upload through unbuffered and uncapped, as the shipped nginx examples do for `/resources/import`. -The Java client wraps the pair. `export` hands back the file as bytes and `importGraph` takes -the same bytes, so moving a sub-graph between two tenants is two calls and no temporary file -unless you want one: +Every client wraps the pair. Export hands back the file as bytes and import takes the same +bytes, so moving a sub-graph between two tenants is two calls and no temporary file unless you +want one. Python and Rust also stream either end to or from a path, for a file too large to hold +in memory. + + + ```java import ai.intellistream.datahub.api.graphtransfer.GraphImportResult; @@ -914,7 +918,45 @@ System.out.println(result.nodesCreated() + " created, " `GraphImportResult` is a record, so its fields read as `result.nodesCreated()` and so on, one per key of the JSON above. -Python and Rust call the endpoints over HTTP with the bearer token the client already holds: + + + +```python +graph = source.resources.export_graph(5677892) # bytes + +result = target.resources.import_graph(graph) +print(result.nodes_created, "created,", + result.nodes_skipped_existing, "already there,", + result.nodes_skipped_timeseries, "to create as series first") + +# Or through a file, never held whole in memory: +source.resources.export_graph_to_path(5677892, "plant_oslo.dhgraph") +target.resources.import_graph_from_path("plant_oslo.dhgraph") +``` + + + + +```rust +let graph: Vec = source.resources.export_graph(5677892).await?; + +let result = target.resources.import_graph(graph).await?; +println!("{} created, {} already there, {:?} to create as series first", + result.nodes_created, result.nodes_skipped_existing, result.nodes_skipped_timeseries); + +// Or through a file, never held whole in memory: +source.resources.export_graph_to_path(5677892, "plant_oslo.dhgraph").await?; +target.resources.import_graph_from_path("plant_oslo.dhgraph").await?; +``` + + + + +The export walks the graph, which settles a moment after a write. A component created and +exported straight away can come back short; read it with `fetch_related` first when that +matters. + +Over plain HTTP, send the bearer token the client already holds: ```bash curl -fsS -H "Authorization: Bearer $TOKEN" \ @@ -1056,33 +1098,41 @@ DataWrapper page = client.functions().filter(criteria); ```python # entity objects, external-id strings, or numeric ids some = client.functions.by_ids(["fn_rolling_average"]) + +page = client.functions.filter(labels=["AGGREGATION"]) # a Page; page.next_cursor pages on +hits = client.functions.search("rolling average") ``` +`filter` takes the criteria as keywords or a prepared `FunctionFilter` as `filter=`, never both, +plus `limit`, `sort_by`, `sort_order` and `cursor`. `search` takes `query`, an optional +`FunctionFilter` and `limit`. + ```rust +use intellistream_datahub_sdk::filters::NodeFilter; +use intellistream_datahub_sdk::functions::{FunctionFilter, FunctionFilterForm}; use intellistream_datahub_sdk::generic::IdAndExtId; let some = api.functions.by_ids(&vec![ IdAndExtId::from_external_id("fn_rolling_average"), ]).await?; + +let criteria = FunctionFilter { + node: NodeFilter { labels: Some(vec!["AGGREGATION".into()]), ..Default::default() }, + data_set_id: None, +}; +let page = api.functions.filter(&FunctionFilterForm::new(criteria)).await?; +let hits = api.functions.search_by_query("rolling average").await?; ``` +`FunctionFilterForm` carries `limit` and a `PageRequest` for `sort` and `cursor`; `search` takes a +`SearchAndFilterForm`, and `search_by_query` is the shorthand for a bare phrase. + -:::note Python and Rust are catching up -`filter` and `search` on functions are in the Java client today. Python and Rust will get them -under the names their `assets` service already uses: `functions.filter(...)` taking keyword -criteria in Python and a filter form in Rust, and `functions.search(query, ...)`. Until then, ask -[`resources.filter`](#filter) narrowed to the `function` node type. - -In those two clients `functions.by_ids` still reads the listing and matches on the client, so it -only sees the newest 10 000 functions. It moves to `POST /functions/byids` in the same change; -the call itself stays as it is. -::: - ## What each client covers {#client-coverage} | Operation | Java | Python | Rust | @@ -1097,7 +1147,7 @@ the call itself stays as it is. | Filter | `resources().filter` | `resources.filter` | `resources.filter` | | Traverse (`fetch-related`) | `resources().fetchRelated` | `resources.fetch_related` | `resources.fetch_related` | | Nearest N (`fetch-nearest`) | `resources().fetchNearest` | `resources.fetch_nearest` | `resources.fetch_nearest` | -| [Export / import a graph](#graph-transfer) | `resources().export` / `importGraph` | HTTP only | HTTP only | +| [Export / import a graph](#graph-transfer) | `resources().export` / `importGraph` | `resources.export_graph` / `import_graph`, plus `_to_path` / `_from_path` | `resources.export_graph` / `import_graph`, plus `_to_path` / `_from_path` | Relations have their own client surface in all three clients, `edges()` in Java, `edges` in diff --git a/docs/reference/subscriptions.md b/docs/reference/subscriptions.md index b67d801..1167fb3 100644 --- a/docs/reference/subscriptions.md +++ b/docs/reference/subscriptions.md @@ -254,6 +254,60 @@ Ack a message only after you've durably handled it. If your process dies before the server redelivers it, so make your handler idempotent. ::: +## A live tail without a subscription {#live-tail} + +`/timeseries/datapoints/listen` streams the datapoints written to a set of series with no +subscription behind it: nothing to create first, nothing to ack, nothing left behind when the +socket closes. It is the lighter tool and the weaker one. It starts at the latest point, keeps no +cursor, and does not replay what was written while the socket was down, so a reader that must +see every point uses [live delivery](#live-delivery) instead. + +The token travels as a WebSocket subprotocol, not a header, because a browser cannot set one on +the handshake: offer `datahub.bearer.` and `datahub.v1`, and the server echoes `datahub.v1`. +An `externalIds=a,b,c` query parameter seeds the set, and +`{"action": "set" | "subscribe" | "unsubscribe", "externalIds": [...]}` changes it. Every change +is narrowed to the series whose data set you may read, silently: an id you cannot read, or that +does not exist, is dropped rather than refused. A frame is +`{"datapoints": [{"externalId", "valueType", "timestamp", "value"}, ...]}`, `value` a string. + +A bad token or a missing role is refused after the upgrade, with a **1008** close; a connection +over the [WebSocket cap](./limits#websockets) gets the error frame described there first. + + + + +The Java client does not wrap the tail. Use [live delivery](#live-delivery). + + + + +```python +with client.timeseries.listen_datapoints(["engine_temperature"]) as tail: + for point in tail: + print(point.external_id, point.timestamp, point.value) +``` + +`subscribe`, `unsubscribe` and `set_timeseries` change the set while it runs. On an +`AsyncDataHubClient` it returns a `DatapointListenerAsync`, driven with `async for`. + + + + +```rust +let mut tail = api.time_series.listen_datapoints(&["engine_temperature"]).await?; +while let Some(point) = tail.next().await { + let point = point?; + println!("{} {} {}", point.external_id, point.timestamp, point.value); +} +``` + +`subscribe`, `unsubscribe` and `set_timeseries` change the set while it runs. A dropped +connection is reopened with a fresh token and the current set; a refusal is returned as an +error instead of retried. + + + + ## What each client covers {#client-coverage} | Operation | Java | Python | Rust | @@ -263,5 +317,6 @@ the server redelivers it, so make your handler idempotent. | Filter | `subscriptions().filter` | `subscriptions.filter(form=None, *, timeseries=, limit=, sort=)` | `subscriptions.filter` | | Delete | `subscriptions().delete` | `subscriptions.delete` | `subscriptions.delete` | | Live delivery | `subscriptions().listen` | `subscriptions.listen` (`SubscriptionListenerAsync` with `async for` on the async client) | `subscriptions.listen` | +| [Live tail](#live-tail), no subscription | — | `timeseries.listen_datapoints` | `time_series.listen_datapoints` | diff --git a/docs/reference/tenant.md b/docs/reference/tenant.md index c7f6e42..100c0ac 100644 --- a/docs/reference/tenant.md +++ b/docs/reference/tenant.md @@ -3,16 +3,23 @@ sidebar_position: 12 title: Tenant --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Tenant Which optional features the tenant your token belongs to may use, and the settings your -organization administers for itself. The Java client reaches them through `client.tenant()`. +organization administers for itself: `client.tenant()` in Java, `client.tenant` in Python and +`api.tenant` in Rust. Every answer is a plain object, not an `items` list. ## Feature flags {#features} `GET /tenant/features` says which optional features the tenant may use. A disabled feature's endpoints answer `404` or `403`. + + + ```java import ai.intellistream.datahub.tenant.TenantFeatures; @@ -22,16 +29,38 @@ if (features.isFilesEnabled()) { } ``` -| Wire field | Java | Feature | -| --- | --- | --- | -| `files` | `isFilesEnabled()` | The [file storage](./files) endpoints. | -| `policy` | `isPolicyFeatureEnabled()` | [Policies and governance](./policies). | -| `streaming` | `isStreamingFeatureEnabled()` | Streaming ingest. | -| `chat` | `isChatFeatureEnabled()` | The AI assistant, configured below. | + + + +```python +features = client.tenant.features() +if features.files: + ... # show the file browser +``` + + + + +```rust +let features = api.tenant.features().await?; +if features.files { + // show the file browser +} +``` + + + + +| Wire field | Java | Python and Rust | Feature | +| --- | --- | --- | --- | +| `files` | `isFilesEnabled()` | `files` | The [file storage](./files) endpoints. | +| `policy` | `isPolicyFeatureEnabled()` | `policy` | [Policies and governance](./policies). | +| `streaming` | `isStreamingFeatureEnabled()` | `streaming` | Streaming ingest. | +| `chat` | `isChatFeatureEnabled()` | `chat` | The AI assistant, configured below. | `policy`, `streaming` and `chat` fall back to the deployment default when the tenant's -configuration does not carry them; `files` reads absent as off. The Java accessors return -primitive booleans. +configuration does not carry them; `files` reads absent as off. Every client reads each flag as +a plain boolean. ## What you may change {#settings-permissions} @@ -43,6 +72,9 @@ imply read. The one scope is `llm`. `GET /tenant/settings/permissions` says which grants you hold, with wildcards resolved, so every scope the platform knows is listed by name. The endpoint is ungated. + + + ```java import ai.intellistream.datahub.models.tenant.SettingsPermission; @@ -52,12 +84,36 @@ llm.read(); llm.write(); ``` + + + +```python +grants = client.tenant.settings_permissions() # dict[str, SettingsPermission] +grants["llm"].read +grants["llm"].write +``` + + + + +```rust +let grants = api.tenant.settings_permissions().await?; // HashMap +grants["llm"].read; +grants["llm"].write; +``` + + + + ## Model configuration {#llm-settings} `GET /tenant/settings/llm` and `PUT /tenant/settings/llm` read and replace the model your organization's assistant runs on. The API key is never returned: `apiKeySet` says whether one is stored, and `configured` says whether the settings amount to a model that can be called. + + + ```java import ai.intellistream.datahub.models.tenant.TenantLlmSettings; import ai.intellistream.datahub.models.tenant.TenantLlmSettingsForm; @@ -73,8 +129,50 @@ TenantLlmSettings saved = client.tenant().updateLlmSettings(new TenantLlmSetting ``` `TenantLlmSettings` and `TenantLlmSettingsForm` are records, so the fields read as -`current.provider()` and so on. `apiKey` is write-only: it is carried on the form and never -returned by a read. +`current.provider()` and so on. + + + + +```python +current = client.tenant.llm_settings() +current.api_key_set +current.configured + +# Keywords rather than a form. Every one you leave out is sent unset, so carry the rest over; +# api_key left out keeps the stored credential. +saved = client.tenant.update_llm_settings( + provider=current.provider, model="the-model-id", base_url=current.base_url, + reasoning_effort=current.reasoning_effort, effort="high", + turn_timeout=current.turn_timeout, max_output_tokens=current.max_output_tokens, + max_iterations=current.max_iterations, instructions=current.instructions, +) +``` + + + + +```rust +use intellistream_datahub_sdk::tenant::TenantLlmSettingsForm; + +let current = api.tenant.llm_settings().await?; +current.api_key_set; +current.configured; + +// A form built from what is stored saves it unchanged, `api_key: None` keeping the credential; +// change only what you mean to. +let form = TenantLlmSettingsForm { + model: Some("the-model-id".into()), + effort: Some("high".into()), + ..TenantLlmSettingsForm::from(¤t) +}; +let saved = api.tenant.update_llm_settings(&form).await?; +``` + + + + +`apiKey` is write-only: it is carried on the form and never returned by a read. The `PUT` replaces the configuration with what you send, absent meaning unset, except for `apiKey`: absent or blank leaves the stored credential alone, and only a non-blank value @@ -94,7 +192,7 @@ cache the tenant registry and pick the change up within five minutes. | Operation | Java | Python | Rust | | --- | --- | --- | --- | -| Feature flags (`GET /tenant/features`) | `tenant().features` | — | — | -| Settings permissions (`GET /tenant/settings/permissions`) | `tenant().settingsPermissions` | — | — | -| Read model configuration (`GET /tenant/settings/llm`) | `tenant().llmSettings` | — | — | -| Change model configuration (`PUT /tenant/settings/llm`) | `tenant().updateLlmSettings` | — | — | +| Feature flags (`GET /tenant/features`) | `tenant().features` | `tenant.features` | `tenant.features` | +| Settings permissions (`GET /tenant/settings/permissions`) | `tenant().settingsPermissions` | `tenant.settings_permissions` | `tenant.settings_permissions` | +| Read model configuration (`GET /tenant/settings/llm`) | `tenant().llmSettings` | `tenant.llm_settings` | `tenant.llm_settings` | +| Change model configuration (`PUT /tenant/settings/llm`) | `tenant().updateLlmSettings` | `tenant.update_llm_settings` | `tenant.update_llm_settings` | diff --git a/docs/reference/timeseries.md b/docs/reference/timeseries.md index b916e86..9d2331b 100644 --- a/docs/reference/timeseries.md +++ b/docs/reference/timeseries.md @@ -96,6 +96,9 @@ heuristic: small low-precision ranges map to `DECIMAL32`, wide-magnitude analog `FLOAT32`, and a unit it does not know gets a compact default with `recognized` false. Use it to pre-select the type in a create form; it is advice, not a constraint. + + + ```java import ai.intellistream.datahub.api.responses.ValueTypeRecommendation; @@ -105,6 +108,29 @@ hint.getReason(); // why, in a sentence you can show a user hint.isRecognized(); // false means you got the generic default ``` + + + +```python +hint = client.timeseries.recommend_value_type("temperature_deg_c") +hint.recommended_value_type # the suggested type, upper case +hint.reason # why, in a sentence you can show a user +hint.recognized # False means you got the generic default +``` + + + + +```rust +let hint = api.time_series.recommend_value_type("temperature_deg_c").await?; +hint.recommended_value_type; // the suggested type, upper case +hint.reason; // why, in a sentence you can show a user +hint.recognized; // false means you got the generic default +``` + + + + Every value crosses the wire as a string, whatever the type. For an exact decimal, send the string form rather than a float: `Datapoint.of(ts, "12.34")` in Java, `DatapointString(ts, "12.34")` with `insert_datapoints` in Python (`insert_from_lists` takes floats and converts @@ -361,6 +387,11 @@ loop { ## List and update series {#list-and-update} +`GET /timeseries/{id}` reads one series by numeric id and answers `404` when there is none, or +none you may read; `timeseries().getById` in Java, `timeseries.get_by_id` in Python and +`time_series.get_by_id` in Rust. [Look up](#filter-series) by external id, or several at once, +with `by_ids`, which leaves out what it cannot find rather than failing. + `GET /timeseries?limit=` is the cheap "what have I got" read: the first `limit` series, newest created first, no criteria. Add `dataSetId` (an id or an external id) to restrict it to one data set and everything beneath it in the `BELONGS_TO` hierarchy. Anything narrower belongs in @@ -922,10 +953,12 @@ if (!result.isComplete()) { | Operation | Java | Python | Rust | | --- | --- | --- | --- | | Create | `timeseries().create` | `timeseries.create` | `time_series.create` / `create_one` | +| Get by numeric id | `timeseries().getById` | `timeseries.get_by_id` | `time_series.get_by_id` | | Look up by id / external id | `timeseries().byIds` | `timeseries.by_ids` | `time_series.by_ids` | | Filter | `timeseries().filter` | `timeseries.filter` | `time_series.filter` | | Search | `timeseries().search` | `timeseries.search` | `time_series.search` | -| List | `timeseries().list` | `timeseries.list` | `time_series.list` / `list_with_limit` | +| List | `timeseries().list` | `timeseries.list` | `time_series.list` | +| [Value-type hint](#value-type-hint) | `timeseries().recommendValueType` | `timeseries.recommend_value_type` | `time_series.recommend_value_type` | | Update | `timeseries().update` | `timeseries.update` | `time_series.update` | | Delete | `timeseries().delete` | `timeseries.delete` | `time_series.delete` | | Write datapoints | `insertDatapoints` / `ingest` | `insert_datapoints` / `insert_from_lists` | `insert_datapoint` / `insert_datapoints` | @@ -934,10 +967,11 @@ if (!result.isComplete()) { | Read datapoints (raw and [aggregated](#retrieve-datapoints)) | `retrieve` / `retrieveAggregated` | `retrieve_datapoints` | `retrieve_datapoints` | | [Latest datapoint](#latest-datapoints) | `timeseries().latest` | `retrieve_latest_datapoints` | `retrieve_latest_datapoint` | | Delete datapoints | `deleteDatapoints` | `timeseries.delete_datapoints` | `time_series.delete_datapoints` | +| [Live datapoint tail](./subscriptions#live-tail) | — | `timeseries.listen_datapoints` | `time_series.listen_datapoints` | Java is the one with `ingest`, the chunking, parallelising, retrying path described above, and with `binaryBuffer`. Python and Rust chunk and send concurrently too, and retry through the durable spool when buffering is on; with it off, the first failed request is the call's error. All three have the [binary path](#binary-ingest), Python on its synchronous client only. Python -can create a series of only three [value types](#value-types). Java also has -`timeseries().recommendValueType`, the [value-type hint](#value-type-hint). +can create a series of only three [value types](#value-types). Java has no +[live datapoint tail](./subscriptions#live-tail); use `subscriptions().listen` there. From a59e5f419ff165e6728237f152c94cbe787e4e4f Mon Sep 17 00:00:00 2001 From: jgjesdal Date: Tue, 29 Sep 2026 15:08:48 +0200 Subject: [PATCH 2/2] test(plans): count the Python and Rust blocks this page added The new tabs shift the per-language fence counts the plans pin, so each one is updated with the page it describes: `resources`, `subscriptions` and `timeseries` gain a Python and a Rust block, and `tenant` gains its first of each. The index selections still land where they were written to: the new blocks are appended in every case, so `resources`' `inject before = 8` is still "Traverse the graph" and `subscriptions`' `before = 2` is still "Live delivery". `tenant` states `independent` on its Python section, which is where the Rust and Java sections inherit it from. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: jgjesdal --- doctests/plans/reference__resources.toml | 4 ++-- doctests/plans/reference__subscriptions.toml | 4 ++-- doctests/plans/reference__tenant.toml | 22 ++++++++++++++++---- doctests/plans/reference__timeseries.toml | 4 ++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/doctests/plans/reference__resources.toml b/doctests/plans/reference__resources.toml index 5d50d46..93827b0 100644 --- a/doctests/plans/reference__resources.toml +++ b/doctests/plans/reference__resources.toml @@ -18,9 +18,9 @@ page = "docs/reference/resources.md" # rust #5 L264 Traverse the graph [blocks] -python = 10 +python = 11 java = 11 -rust = 9 +rust = 10 [python] timeout = 600 diff --git a/doctests/plans/reference__subscriptions.toml b/doctests/plans/reference__subscriptions.toml index 8019a09..9a54da7 100644 --- a/doctests/plans/reference__subscriptions.toml +++ b/doctests/plans/reference__subscriptions.toml @@ -10,9 +10,9 @@ page = "docs/reference/subscriptions.md" # rust #2 L130 Live delivery [blocks] -python = 2 +python = 3 java = 3 -rust = 2 +rust = 3 [python] timeout = 600 diff --git a/doctests/plans/reference__tenant.toml b/doctests/plans/reference__tenant.toml index af02469..cb0b3d8 100644 --- a/doctests/plans/reference__tenant.toml +++ b/doctests/plans/reference__tenant.toml @@ -1,16 +1,30 @@ page = "docs/reference/tenant.md" # Blocks on this page: -# java #1 L16 Feature flags {#features} -# java #2 L46 What you may change {#settings-permissions} -# java #3 L61 Model configuration {#llm-settings} +# java #1 L23 Feature flags {#features} +# python #1 L35 Feature flags {#features} +# rust #1 L44 Feature flags {#features} +# java #2 L78 What you may change {#settings-permissions} +# python #2 L90 What you may change {#settings-permissions} +# rust #2 L99 What you may change {#settings-permissions} +# java #3 L117 Model configuration {#llm-settings} +# python #3 L137 Model configuration {#llm-settings} +# rust #3 L155 Model configuration {#llm-settings} # Pinned so an edit to the page fails this plan instead of silently # re-pointing its block selections. Update deliberately, after re-reading. [blocks] +python = 3 java = 3 +rust = 3 + +[python] +# A reference page, not a tutorial: three unrelated reads of tenant settings, each +# self-contained. Every tab is compiled or run one block at a time; the Java and Rust +# sections inherit this when they do not say otherwise. +independent = true +disabled = "Python scenario not written yet." [java] disabled = "Java scenario not written yet; compiled by test_compile.py." -# A reference page, not a tutorial: three unrelated reads of tenant settings. independent = true diff --git a/doctests/plans/reference__timeseries.toml b/doctests/plans/reference__timeseries.toml index 12b8ea6..8e26d45 100644 --- a/doctests/plans/reference__timeseries.toml +++ b/doctests/plans/reference__timeseries.toml @@ -35,9 +35,9 @@ page = "docs/reference/timeseries.md" # java #14 L734 IngestResult [blocks] -python = 10 +python = 11 java = 17 -rust = 10 +rust = 11 [python] timeout = 600